Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / DataOracleClient / System / Data / OracleClient / OracleLob.cs / 1 / OracleLob.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //----------------------------------------------------------------------------- namespace System.Data.OracleClient { using System; using System.Data.Common; using System.Data.SqlTypes; using System.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; //--------------------------------------------------------------------- // OracleLob // // This class implements support for Oracle's BLOB, CLOB, and NCLOB // internal data types. This is primarily a stream object, to allow // it to be used by the StreamReader/StreamWriter and BinaryReader/ // BinaryWriter objects. // sealed public class OracleLob : Stream, ICloneable, IDisposable, INullable { private bool _isNull; // true when the object is a Null lob. private OciLobLocator _lobLocator; private OracleType _lobType; // the underlying data type of the LOB locator, cached, because after close/dispose we still need the information private OCI.CHARSETFORM _charsetForm; // the character set form (char/varchar/clob vs nchar/nvarchar/nclob) private long _currentPosition; // the current read/write position, in BYTES (NOTE: Oracle is 1 based, but this is zero based) private byte _isTemporaryState; // see x_IsTemporary.... constants below. private const byte x_IsTemporaryUnknown = 0; // don't know the temporary status private const byte x_IsTemporary = 1; // know the temporary status, and it's temporary private const byte x_IsNotTemporary = 2; // know the temporary status, and it's not temporary static public new readonly OracleLob Null = new OracleLob(); // (internal) Construct a null lob internal OracleLob() { _isNull = true; _lobType = OracleType.Blob; // pick something... } // (internal) Construct from a buffer internal OracleLob(OciLobLocator lobLocator) { _lobLocator = lobLocator.Clone(); _lobType = _lobLocator.LobType; _charsetForm = (OracleType.NClob == _lobType) ? OCI.CHARSETFORM.SQLCS_NCHAR : OCI.CHARSETFORM.SQLCS_IMPLICIT; } // (internal) Construct from an existing Lob object (copy constructor) internal OracleLob(OracleLob lob) { this._lobLocator = lob._lobLocator.Clone(); this._lobType = lob._lobLocator.LobType; this._charsetForm = lob._charsetForm; this._currentPosition = lob._currentPosition; this._isTemporaryState = lob._isTemporaryState; } // (internal) Construct a temporary Lob object internal OracleLob(OracleConnection connection, OracleType oracleType) { Debug.Assert(ConnectionState.Open == connection.State, "attempting to construct a tempory LOB on a closed connection?"); Debug.Assert((OracleType.Blob == oracleType || OracleType.Clob == oracleType || OracleType.NClob == oracleType), "invalid OracleType for temporary LOB"); _lobLocator = new OciLobLocator(connection, oracleType); _lobType = oracleType; _charsetForm = (OracleType.NClob == _lobType) ? OCI.CHARSETFORM.SQLCS_NCHAR : OCI.CHARSETFORM.SQLCS_IMPLICIT; _isTemporaryState = x_IsTemporary; OCI.LOB_TYPE tempLobType = (OracleType.Blob == oracleType) ? OCI.LOB_TYPE.OCI_TEMP_BLOB : OCI.LOB_TYPE.OCI_TEMP_CLOB; // NOTE: If we do not have some mechanism to ensure that these // LOBs are cleaned up, we will appear to leak them until the internal // connection is disposed. This is bad. Fortunately, we will cause // them to be free'd as part of a try..finally block in the execute // method where they're allocated, so we don't really need to store // them in the reference collection right now. // connection.AddTemporaryLob(this); // don't forget to release it when the connection is closed! int rc = TracedNativeMethods.OCILobCreateTemporary( connection.ServiceContextHandle, connection.ErrorHandle, this._lobLocator.Descriptor, 0, //csid, _charsetForm, tempLobType, 0, // cache OCI.DURATION.OCI_DURATION_SESSION ); if (0 != rc) { connection.CheckError(ErrorHandle, rc); } } public override bool CanRead { get { if (IsNull) { return true; } return !IsDisposed; } } public override bool CanSeek { get { if (IsNull) { return true; } return !IsDisposed; } } public override bool CanWrite { get { bool value = (OracleType.BFile != _lobType); if (!IsNull) { value = !IsDisposed; } return value; } } public int ChunkSize { get { AssertObjectNotDisposed(); if (IsNull) { return 0; } AssertConnectionIsOpen(); UInt32 chunkSize = 0; int rc = TracedNativeMethods.OCILobGetChunkSize( ServiceContextHandle, ErrorHandle, Descriptor, out chunkSize ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } return (int)chunkSize; } } public OracleConnection Connection { get { AssertObjectNotDisposed(); OciLobLocator lobLocator = LobLocator; if (null == lobLocator) { return null; } return lobLocator.Connection; } } private bool ConnectionIsClosed { // returns TRUE when the parent connection object has been closed get { return (null == LobLocator) || LobLocator.ConnectionIsClosed; } } private UInt32 CurrentOraclePosition { // Oracle's LOBs can be between 0 and 4GB-1 in size, but the frameworks // Stream class presumes a Int64. We use we have to pass the offset // to several OCI calls as an UInt32 because that's what Oracle // expects. // // We solve this dilemma by verifying that the currentPosition is in // the valid range before we use it. get { Debug.Assert (_currentPosition <= (long)UInt32.MaxValue, "Position is beyond the maximum LOB length"); return (UInt32)AdjustOffsetToOracle(_currentPosition) + 1; } } internal OciHandle Descriptor { get { return LobLocator.Descriptor; } } internal OciErrorHandle ErrorHandle { // Every OCI call needs an error handle, so make it available // internally. get { return LobLocator.ErrorHandle; } } public bool IsBatched { get { if (IsNull || IsDisposed || ConnectionIsClosed) { return false; } int flag; int rc = TracedNativeMethods.OCILobIsOpen( ServiceContextHandle, ErrorHandle, Descriptor, out flag ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } return (flag != 0); } } private bool IsCharacterLob { get { return (OracleType.Clob == _lobType || OracleType.NClob == _lobType); } } private bool IsDisposed { get { return _isNull ? false : (null == LobLocator); } } public bool IsNull { get { return _isNull; } } public bool IsTemporary { get { AssertObjectNotDisposed(); if (IsNull) { return false; } AssertConnectionIsOpen(); // Don't bother asking if we already know. if (x_IsTemporaryUnknown == _isTemporaryState) { int flag; int rc = TracedNativeMethods.OCILobIsTemporary( Connection.EnvironmentHandle, ErrorHandle, Descriptor, out flag ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } _isTemporaryState = (flag != 0) ? x_IsTemporary : x_IsNotTemporary; } return (x_IsTemporary == _isTemporaryState); } } internal OciLobLocator LobLocator { get { return _lobLocator; } } public OracleType LobType { get { return _lobType; } } public override long Length { get { AssertObjectNotDisposed(); if (IsNull) { return 0; } AssertConnectionIsOpen(); UInt32 len; int rc = TracedNativeMethods.OCILobGetLength( ServiceContextHandle, ErrorHandle, Descriptor, out len ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } return AdjustOracleToOffset(len); } } public override long Position { get { AssertObjectNotDisposed(); if (IsNull) { return 0; } AssertConnectionIsOpen(); return _currentPosition; } set { if (!IsNull) { Seek(value, SeekOrigin.Begin); } } } internal OciServiceContextHandle ServiceContextHandle { // You need to provide the service context handle to things like the // OCI execute call so a statement handle can be associated with a // connection. Better make it available internally, then. get { return LobLocator.ServiceContextHandle; } } public object Value { // We need to return a CLS object from the Data Reader and exec // scalar; that means we have to get the contents of the lob, not // a stream. It's a real bummer. get { AssertObjectNotDisposed(); if (IsNull) { return DBNull.Value; } long savedPosition = _currentPosition; int length = (int)this.Length; bool isBinary = (OracleType.Blob == _lobType || OracleType.BFile == _lobType); string result; // If the LOB is empty, return the appropriate empty object; if (0 == length) { if (isBinary) { return new byte[0]; } return String.Empty; } try { // It's not empty, so we have to read the whole thing. Bummer. Seek(0,SeekOrigin.Begin); if (isBinary) { byte[] blobResult = new byte[length]; Read(blobResult, 0, length); return blobResult; } StreamReader sr; try { sr = new StreamReader((Stream)this, System.Text.Encoding.Unicode); result = sr.ReadToEnd(); } finally { sr = null; } } finally { // Make sure we reset the position back to the start. _currentPosition = savedPosition; } return result; } } internal int AdjustOffsetToOracle (int amount) { int result = IsCharacterLob ? amount / 2 : amount; return result; } internal long AdjustOffsetToOracle (long amount) { long result = IsCharacterLob ? amount / 2 : amount; return result; } internal int AdjustOracleToOffset (int amount) { int result = IsCharacterLob ? checked((int)amount * 2) : amount; return result; } internal long AdjustOracleToOffset (long amount) { long result = IsCharacterLob ? checked((long)amount * 2) : amount; return result; } internal void AssertAmountIsEven (long amount, string argName) { if (IsCharacterLob && 1 == (amount & 0x1)) { throw ADP.LobAmountMustBeEven(argName); } } internal void AssertAmountIsValidOddOK (long amount, string argName) { if (amount < 0 || amount >= (long)UInt32.MaxValue) { throw ADP.LobAmountExceeded(argName); } } internal void AssertAmountIsValid (long amount, string argName) { AssertAmountIsValidOddOK(amount, argName); AssertAmountIsEven(amount, argName); } internal void AssertConnectionIsOpen() { if (ConnectionIsClosed) { throw ADP.ClosedConnectionError(); } } internal void AssertObjectNotDisposed() { if (IsDisposed) { throw ADP.ObjectDisposed("OracleLob"); } } internal void AssertPositionIsValid() { if (IsCharacterLob && 1 == (_currentPosition & 0x1)) { throw ADP.LobPositionMustBeEven(); } } internal void AssertTransactionExists() { if (!Connection.HasTransaction) { throw ADP.LobWriteRequiresTransaction(); } } public void Append (OracleLob source) { if (null == source) { throw ADP.ArgumentNull("source"); } AssertObjectNotDisposed(); source.AssertObjectNotDisposed(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } if (!source.IsNull) { AssertConnectionIsOpen(); int rc = TracedNativeMethods.OCILobAppend( ServiceContextHandle, ErrorHandle, Descriptor, source.Descriptor ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } } } public void BeginBatch () { BeginBatch(OracleLobOpenMode.ReadOnly); } public void BeginBatch (OracleLobOpenMode mode) { AssertObjectNotDisposed(); if (!IsNull) { AssertConnectionIsOpen(); LobLocator.Open(mode); } } public object Clone() { AssertObjectNotDisposed(); if (IsNull) { return Null; } AssertConnectionIsOpen(); OracleLob clone = new OracleLob(this); return clone; } protected override void Dispose (bool disposing) { // If we're not disposing, it's because we went out of scope, and we're // being garbage collected. We shouldn't touch any managed objects // or bad things can happen. try { if (disposing) { if (!IsNull && !ConnectionIsClosed) { Flush(); OciLobLocator.SafeDispose(ref _lobLocator); _lobLocator = null; } } } finally { base.Dispose(disposing); } } public long CopyTo (OracleLob destination) { // Copies the entire lob to a compatible lob, starting at the beginning of the target array. return CopyTo (0, destination, 0, Length); } public long CopyTo (OracleLob destination, long destinationOffset) { // Copies the entire lob to a compatible lob, starting at the specified offset of the target array. return CopyTo (0, destination, destinationOffset, Length); } public long CopyTo (long sourceOffset, OracleLob destination, long destinationOffset, long amount) { // Copies a range of elements from the lob to a compatible lob, starting at the specified index of the target array. if (null == destination) { throw ADP.ArgumentNull("destination"); } AssertObjectNotDisposed(); destination.AssertObjectNotDisposed(); AssertAmountIsValid(amount, "amount"); AssertAmountIsValid(sourceOffset, "sourceOffset"); AssertAmountIsValid(destinationOffset, "destinationOffset"); if (destination.IsNull) { throw ADP.LobWriteInvalidOnNull(); } if (IsNull) { return 0; } AssertConnectionIsOpen(); AssertTransactionExists(); int rc; long dataCount = AdjustOffsetToOracle(Math.Min(Length - sourceOffset, amount)); long dstOffset = AdjustOffsetToOracle(destinationOffset) + 1; // Oracle is 1 based, we are zero based. long srcOffset = AdjustOffsetToOracle(sourceOffset) + 1; // Oracle is 1 based, we are zero based. if (0 >= dataCount) { return 0; } rc = TracedNativeMethods.OCILobCopy( ServiceContextHandle, ErrorHandle, destination.Descriptor, Descriptor, (UInt32)dataCount, (UInt32)dstOffset, (UInt32)srcOffset ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } // DEVNOTE: Oracle must not do partial copies, because their API doesn't tell you how many bytes were copied. long byteCount = AdjustOracleToOffset(dataCount); return byteCount; } public void EndBatch () { AssertObjectNotDisposed(); if (!IsNull) { AssertConnectionIsOpen(); LobLocator.ForceClose(); } } public long Erase () { // Erase (zero or space fill) the entire LOB return Erase (0, Length); } public long Erase (long offset, long amount) { AssertObjectNotDisposed(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } AssertAmountIsValid(amount, "amount"); AssertAmountIsEven(offset, "offset"); AssertPositionIsValid(); AssertConnectionIsOpen(); AssertTransactionExists(); if (offset < 0 || offset >= (long)UInt32.MaxValue) { // MDAC 82575 return 0; } UInt32 eraseAmount = (UInt32)AdjustOffsetToOracle(amount); UInt32 eraseOffset = (UInt32)AdjustOffsetToOracle(offset) + 1; // Oracle is 1 based, we are zero based. // Erase (zero or space fill) bytes from the specified offset int rc = TracedNativeMethods.OCILobErase( ServiceContextHandle, ErrorHandle, Descriptor, ref eraseAmount, eraseOffset ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } long bytesErased = AdjustOracleToOffset(eraseAmount); return bytesErased; } internal void Free() { int rc = TracedNativeMethods.OCILobFreeTemporary(_lobLocator.ServiceContextHandle, _lobLocator.ErrorHandle, _lobLocator.Descriptor); if (0 != rc) { _lobLocator.Connection.CheckError(ErrorHandle, rc); } } public override void Flush () { } public override int Read (byte[] buffer, int offset, int count) { AssertObjectNotDisposed(); if (count < 0) { throw ADP.MustBePositive("count"); } if (offset < 0) { throw ADP.MustBePositive("offset"); } if (null == buffer) { throw ADP.ArgumentNull("buffer"); } if ((long)buffer.Length < ((long)offset + (long)count)) { throw ADP.BufferExceeded("count"); } if (IsNull || 0 == count) { return 0; } AssertConnectionIsOpen(); AssertAmountIsValidOddOK(offset, "offset"); AssertAmountIsValidOddOK(count, "count"); int amount; uint readPosition = (uint)_currentPosition; // Bless their hearts: Oracle won't let us use odd addresses to read // character data, nor will they let us read a single byte from a // character lob. Instead, we allocate our own buffer and copy the // value to the caller's buffer. int oddPosition = 0; int oddOffset = 0; int oddCount = 0; byte[] readBuffer = buffer; int readOffset = offset; int readCount = count; if (IsCharacterLob) { oddPosition = (int)(readPosition & 0x1); oddOffset = offset & 0x1; oddCount = count & 0x1; readPosition /= 2; if (1 == oddOffset || 1 == oddPosition || 1 == oddCount) { readOffset = 0; readCount = count + oddCount + (2 * oddPosition); readBuffer = new byte[readCount]; } } ushort charsetId = unchecked(IsCharacterLob ? (ushort)OCI.CHARSETID.OCI_UTF16ID : (ushort)0); int rc = 0; amount = AdjustOffsetToOracle(readCount); // We need to pin the buffer and get the address of the offset that // the user requested; Oracle doesn't allow us to specify an offset // into the buffer. GCHandle handle = new GCHandle(); try { handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned); IntPtr bufferPtr = new IntPtr((long)handle.AddrOfPinnedObject() + readOffset); rc = TracedNativeMethods.OCILobRead( ServiceContextHandle, ErrorHandle, Descriptor, ref amount, readPosition + 1, bufferPtr, checked((uint)readCount), charsetId, _charsetForm ); } finally { if (handle.IsAllocated) { handle.Free(); // Unpin the buffer } } if ((int)OCI.RETURNCODE.OCI_NEED_DATA == rc) { rc = 0; } if ((int)OCI.RETURNCODE.OCI_NO_DATA == rc) { return 0; } if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } amount = AdjustOracleToOffset(amount); if (readBuffer as object != buffer as object) { if (amount >= count) { amount = count; } else { amount -= oddPosition; } Buffer.BlockCopy(readBuffer, oddPosition, buffer, offset, amount); readBuffer = null; } _currentPosition += amount; return amount; } public override long Seek (long offset, SeekOrigin origin) { AssertObjectNotDisposed(); if (IsNull) { return 0; } long newPosition = offset; // SeekOrigin.Begin is default case long length = Length; switch (origin) { case SeekOrigin.Begin: newPosition = offset; break; case SeekOrigin.End: newPosition = length + offset; break; case SeekOrigin.Current: newPosition = _currentPosition + offset; break; default: throw ADP.InvalidSeekOrigin(origin); } if (newPosition < 0 || newPosition > length) { throw ADP.SeekBeyondEnd("offset"); } _currentPosition = newPosition; return _currentPosition; } public override void SetLength (long value) { AssertObjectNotDisposed(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } AssertConnectionIsOpen(); AssertAmountIsValid(value, "value"); AssertTransactionExists(); UInt32 newlength = (UInt32)AdjustOffsetToOracle(value); int rc = TracedNativeMethods.OCILobTrim( ServiceContextHandle, ErrorHandle, Descriptor, newlength ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } // Adjust the current position to be within the length of the lob, if // we just truncated it before the current position. _currentPosition = Math.Min(_currentPosition, value); } public override void Write (byte[] buffer, int offset, int count) { AssertObjectNotDisposed(); AssertConnectionIsOpen(); if (count < 0) { throw ADP.MustBePositive("count"); } if (offset < 0) { throw ADP.MustBePositive("offset"); } if (null == buffer) { throw ADP.ArgumentNull("buffer"); } if ((long)buffer.Length < ((long)offset + (long)count)) { throw ADP.BufferExceeded("count"); } AssertTransactionExists(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } AssertAmountIsValid(offset, "offset"); AssertAmountIsValid(count, "count"); AssertPositionIsValid(); OCI.CHARSETFORM charsetForm = _charsetForm; ushort charsetId = unchecked(IsCharacterLob ? (ushort)OCI.CHARSETID.OCI_UTF16ID : (ushort)0); int amount = AdjustOffsetToOracle(count); int rc = 0; if (0 == amount) { return; } // We need to pin the buffer and get the address of the offset that // the user requested; Oracle doesn't allow us to specify an offset // into the buffer. GCHandle handle = new GCHandle(); try { handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); IntPtr bufferPtr = new IntPtr((long)handle.AddrOfPinnedObject() + offset); rc = TracedNativeMethods.OCILobWrite( ServiceContextHandle, ErrorHandle, Descriptor, ref amount, CurrentOraclePosition, bufferPtr, unchecked((uint)count), // verified above (byte)OCI.PIECE.OCI_ONE_PIECE, charsetId, charsetForm ); } finally { if (handle.IsAllocated) { handle.Free(); // Unpin the buffer } } if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } amount = AdjustOracleToOffset(amount); _currentPosition += amount; } public override void WriteByte (byte value) { if (OracleType.Clob == _lobType || OracleType.NClob == _lobType) { throw ADP.WriteByteForBinaryLobsOnly(); } base.WriteByte(value); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //----------------------------------------------------------------------------- namespace System.Data.OracleClient { using System; using System.Data.Common; using System.Data.SqlTypes; using System.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; //--------------------------------------------------------------------- // OracleLob // // This class implements support for Oracle's BLOB, CLOB, and NCLOB // internal data types. This is primarily a stream object, to allow // it to be used by the StreamReader/StreamWriter and BinaryReader/ // BinaryWriter objects. // sealed public class OracleLob : Stream, ICloneable, IDisposable, INullable { private bool _isNull; // true when the object is a Null lob. private OciLobLocator _lobLocator; private OracleType _lobType; // the underlying data type of the LOB locator, cached, because after close/dispose we still need the information private OCI.CHARSETFORM _charsetForm; // the character set form (char/varchar/clob vs nchar/nvarchar/nclob) private long _currentPosition; // the current read/write position, in BYTES (NOTE: Oracle is 1 based, but this is zero based) private byte _isTemporaryState; // see x_IsTemporary.... constants below. private const byte x_IsTemporaryUnknown = 0; // don't know the temporary status private const byte x_IsTemporary = 1; // know the temporary status, and it's temporary private const byte x_IsNotTemporary = 2; // know the temporary status, and it's not temporary static public new readonly OracleLob Null = new OracleLob(); // (internal) Construct a null lob internal OracleLob() { _isNull = true; _lobType = OracleType.Blob; // pick something... } // (internal) Construct from a buffer internal OracleLob(OciLobLocator lobLocator) { _lobLocator = lobLocator.Clone(); _lobType = _lobLocator.LobType; _charsetForm = (OracleType.NClob == _lobType) ? OCI.CHARSETFORM.SQLCS_NCHAR : OCI.CHARSETFORM.SQLCS_IMPLICIT; } // (internal) Construct from an existing Lob object (copy constructor) internal OracleLob(OracleLob lob) { this._lobLocator = lob._lobLocator.Clone(); this._lobType = lob._lobLocator.LobType; this._charsetForm = lob._charsetForm; this._currentPosition = lob._currentPosition; this._isTemporaryState = lob._isTemporaryState; } // (internal) Construct a temporary Lob object internal OracleLob(OracleConnection connection, OracleType oracleType) { Debug.Assert(ConnectionState.Open == connection.State, "attempting to construct a tempory LOB on a closed connection?"); Debug.Assert((OracleType.Blob == oracleType || OracleType.Clob == oracleType || OracleType.NClob == oracleType), "invalid OracleType for temporary LOB"); _lobLocator = new OciLobLocator(connection, oracleType); _lobType = oracleType; _charsetForm = (OracleType.NClob == _lobType) ? OCI.CHARSETFORM.SQLCS_NCHAR : OCI.CHARSETFORM.SQLCS_IMPLICIT; _isTemporaryState = x_IsTemporary; OCI.LOB_TYPE tempLobType = (OracleType.Blob == oracleType) ? OCI.LOB_TYPE.OCI_TEMP_BLOB : OCI.LOB_TYPE.OCI_TEMP_CLOB; // NOTE: If we do not have some mechanism to ensure that these // LOBs are cleaned up, we will appear to leak them until the internal // connection is disposed. This is bad. Fortunately, we will cause // them to be free'd as part of a try..finally block in the execute // method where they're allocated, so we don't really need to store // them in the reference collection right now. // connection.AddTemporaryLob(this); // don't forget to release it when the connection is closed! int rc = TracedNativeMethods.OCILobCreateTemporary( connection.ServiceContextHandle, connection.ErrorHandle, this._lobLocator.Descriptor, 0, //csid, _charsetForm, tempLobType, 0, // cache OCI.DURATION.OCI_DURATION_SESSION ); if (0 != rc) { connection.CheckError(ErrorHandle, rc); } } public override bool CanRead { get { if (IsNull) { return true; } return !IsDisposed; } } public override bool CanSeek { get { if (IsNull) { return true; } return !IsDisposed; } } public override bool CanWrite { get { bool value = (OracleType.BFile != _lobType); if (!IsNull) { value = !IsDisposed; } return value; } } public int ChunkSize { get { AssertObjectNotDisposed(); if (IsNull) { return 0; } AssertConnectionIsOpen(); UInt32 chunkSize = 0; int rc = TracedNativeMethods.OCILobGetChunkSize( ServiceContextHandle, ErrorHandle, Descriptor, out chunkSize ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } return (int)chunkSize; } } public OracleConnection Connection { get { AssertObjectNotDisposed(); OciLobLocator lobLocator = LobLocator; if (null == lobLocator) { return null; } return lobLocator.Connection; } } private bool ConnectionIsClosed { // returns TRUE when the parent connection object has been closed get { return (null == LobLocator) || LobLocator.ConnectionIsClosed; } } private UInt32 CurrentOraclePosition { // Oracle's LOBs can be between 0 and 4GB-1 in size, but the frameworks // Stream class presumes a Int64. We use we have to pass the offset // to several OCI calls as an UInt32 because that's what Oracle // expects. // // We solve this dilemma by verifying that the currentPosition is in // the valid range before we use it. get { Debug.Assert (_currentPosition <= (long)UInt32.MaxValue, "Position is beyond the maximum LOB length"); return (UInt32)AdjustOffsetToOracle(_currentPosition) + 1; } } internal OciHandle Descriptor { get { return LobLocator.Descriptor; } } internal OciErrorHandle ErrorHandle { // Every OCI call needs an error handle, so make it available // internally. get { return LobLocator.ErrorHandle; } } public bool IsBatched { get { if (IsNull || IsDisposed || ConnectionIsClosed) { return false; } int flag; int rc = TracedNativeMethods.OCILobIsOpen( ServiceContextHandle, ErrorHandle, Descriptor, out flag ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } return (flag != 0); } } private bool IsCharacterLob { get { return (OracleType.Clob == _lobType || OracleType.NClob == _lobType); } } private bool IsDisposed { get { return _isNull ? false : (null == LobLocator); } } public bool IsNull { get { return _isNull; } } public bool IsTemporary { get { AssertObjectNotDisposed(); if (IsNull) { return false; } AssertConnectionIsOpen(); // Don't bother asking if we already know. if (x_IsTemporaryUnknown == _isTemporaryState) { int flag; int rc = TracedNativeMethods.OCILobIsTemporary( Connection.EnvironmentHandle, ErrorHandle, Descriptor, out flag ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } _isTemporaryState = (flag != 0) ? x_IsTemporary : x_IsNotTemporary; } return (x_IsTemporary == _isTemporaryState); } } internal OciLobLocator LobLocator { get { return _lobLocator; } } public OracleType LobType { get { return _lobType; } } public override long Length { get { AssertObjectNotDisposed(); if (IsNull) { return 0; } AssertConnectionIsOpen(); UInt32 len; int rc = TracedNativeMethods.OCILobGetLength( ServiceContextHandle, ErrorHandle, Descriptor, out len ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } return AdjustOracleToOffset(len); } } public override long Position { get { AssertObjectNotDisposed(); if (IsNull) { return 0; } AssertConnectionIsOpen(); return _currentPosition; } set { if (!IsNull) { Seek(value, SeekOrigin.Begin); } } } internal OciServiceContextHandle ServiceContextHandle { // You need to provide the service context handle to things like the // OCI execute call so a statement handle can be associated with a // connection. Better make it available internally, then. get { return LobLocator.ServiceContextHandle; } } public object Value { // We need to return a CLS object from the Data Reader and exec // scalar; that means we have to get the contents of the lob, not // a stream. It's a real bummer. get { AssertObjectNotDisposed(); if (IsNull) { return DBNull.Value; } long savedPosition = _currentPosition; int length = (int)this.Length; bool isBinary = (OracleType.Blob == _lobType || OracleType.BFile == _lobType); string result; // If the LOB is empty, return the appropriate empty object; if (0 == length) { if (isBinary) { return new byte[0]; } return String.Empty; } try { // It's not empty, so we have to read the whole thing. Bummer. Seek(0,SeekOrigin.Begin); if (isBinary) { byte[] blobResult = new byte[length]; Read(blobResult, 0, length); return blobResult; } StreamReader sr; try { sr = new StreamReader((Stream)this, System.Text.Encoding.Unicode); result = sr.ReadToEnd(); } finally { sr = null; } } finally { // Make sure we reset the position back to the start. _currentPosition = savedPosition; } return result; } } internal int AdjustOffsetToOracle (int amount) { int result = IsCharacterLob ? amount / 2 : amount; return result; } internal long AdjustOffsetToOracle (long amount) { long result = IsCharacterLob ? amount / 2 : amount; return result; } internal int AdjustOracleToOffset (int amount) { int result = IsCharacterLob ? checked((int)amount * 2) : amount; return result; } internal long AdjustOracleToOffset (long amount) { long result = IsCharacterLob ? checked((long)amount * 2) : amount; return result; } internal void AssertAmountIsEven (long amount, string argName) { if (IsCharacterLob && 1 == (amount & 0x1)) { throw ADP.LobAmountMustBeEven(argName); } } internal void AssertAmountIsValidOddOK (long amount, string argName) { if (amount < 0 || amount >= (long)UInt32.MaxValue) { throw ADP.LobAmountExceeded(argName); } } internal void AssertAmountIsValid (long amount, string argName) { AssertAmountIsValidOddOK(amount, argName); AssertAmountIsEven(amount, argName); } internal void AssertConnectionIsOpen() { if (ConnectionIsClosed) { throw ADP.ClosedConnectionError(); } } internal void AssertObjectNotDisposed() { if (IsDisposed) { throw ADP.ObjectDisposed("OracleLob"); } } internal void AssertPositionIsValid() { if (IsCharacterLob && 1 == (_currentPosition & 0x1)) { throw ADP.LobPositionMustBeEven(); } } internal void AssertTransactionExists() { if (!Connection.HasTransaction) { throw ADP.LobWriteRequiresTransaction(); } } public void Append (OracleLob source) { if (null == source) { throw ADP.ArgumentNull("source"); } AssertObjectNotDisposed(); source.AssertObjectNotDisposed(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } if (!source.IsNull) { AssertConnectionIsOpen(); int rc = TracedNativeMethods.OCILobAppend( ServiceContextHandle, ErrorHandle, Descriptor, source.Descriptor ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } } } public void BeginBatch () { BeginBatch(OracleLobOpenMode.ReadOnly); } public void BeginBatch (OracleLobOpenMode mode) { AssertObjectNotDisposed(); if (!IsNull) { AssertConnectionIsOpen(); LobLocator.Open(mode); } } public object Clone() { AssertObjectNotDisposed(); if (IsNull) { return Null; } AssertConnectionIsOpen(); OracleLob clone = new OracleLob(this); return clone; } protected override void Dispose (bool disposing) { // If we're not disposing, it's because we went out of scope, and we're // being garbage collected. We shouldn't touch any managed objects // or bad things can happen. try { if (disposing) { if (!IsNull && !ConnectionIsClosed) { Flush(); OciLobLocator.SafeDispose(ref _lobLocator); _lobLocator = null; } } } finally { base.Dispose(disposing); } } public long CopyTo (OracleLob destination) { // Copies the entire lob to a compatible lob, starting at the beginning of the target array. return CopyTo (0, destination, 0, Length); } public long CopyTo (OracleLob destination, long destinationOffset) { // Copies the entire lob to a compatible lob, starting at the specified offset of the target array. return CopyTo (0, destination, destinationOffset, Length); } public long CopyTo (long sourceOffset, OracleLob destination, long destinationOffset, long amount) { // Copies a range of elements from the lob to a compatible lob, starting at the specified index of the target array. if (null == destination) { throw ADP.ArgumentNull("destination"); } AssertObjectNotDisposed(); destination.AssertObjectNotDisposed(); AssertAmountIsValid(amount, "amount"); AssertAmountIsValid(sourceOffset, "sourceOffset"); AssertAmountIsValid(destinationOffset, "destinationOffset"); if (destination.IsNull) { throw ADP.LobWriteInvalidOnNull(); } if (IsNull) { return 0; } AssertConnectionIsOpen(); AssertTransactionExists(); int rc; long dataCount = AdjustOffsetToOracle(Math.Min(Length - sourceOffset, amount)); long dstOffset = AdjustOffsetToOracle(destinationOffset) + 1; // Oracle is 1 based, we are zero based. long srcOffset = AdjustOffsetToOracle(sourceOffset) + 1; // Oracle is 1 based, we are zero based. if (0 >= dataCount) { return 0; } rc = TracedNativeMethods.OCILobCopy( ServiceContextHandle, ErrorHandle, destination.Descriptor, Descriptor, (UInt32)dataCount, (UInt32)dstOffset, (UInt32)srcOffset ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } // DEVNOTE: Oracle must not do partial copies, because their API doesn't tell you how many bytes were copied. long byteCount = AdjustOracleToOffset(dataCount); return byteCount; } public void EndBatch () { AssertObjectNotDisposed(); if (!IsNull) { AssertConnectionIsOpen(); LobLocator.ForceClose(); } } public long Erase () { // Erase (zero or space fill) the entire LOB return Erase (0, Length); } public long Erase (long offset, long amount) { AssertObjectNotDisposed(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } AssertAmountIsValid(amount, "amount"); AssertAmountIsEven(offset, "offset"); AssertPositionIsValid(); AssertConnectionIsOpen(); AssertTransactionExists(); if (offset < 0 || offset >= (long)UInt32.MaxValue) { // MDAC 82575 return 0; } UInt32 eraseAmount = (UInt32)AdjustOffsetToOracle(amount); UInt32 eraseOffset = (UInt32)AdjustOffsetToOracle(offset) + 1; // Oracle is 1 based, we are zero based. // Erase (zero or space fill) bytes from the specified offset int rc = TracedNativeMethods.OCILobErase( ServiceContextHandle, ErrorHandle, Descriptor, ref eraseAmount, eraseOffset ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } long bytesErased = AdjustOracleToOffset(eraseAmount); return bytesErased; } internal void Free() { int rc = TracedNativeMethods.OCILobFreeTemporary(_lobLocator.ServiceContextHandle, _lobLocator.ErrorHandle, _lobLocator.Descriptor); if (0 != rc) { _lobLocator.Connection.CheckError(ErrorHandle, rc); } } public override void Flush () { } public override int Read (byte[] buffer, int offset, int count) { AssertObjectNotDisposed(); if (count < 0) { throw ADP.MustBePositive("count"); } if (offset < 0) { throw ADP.MustBePositive("offset"); } if (null == buffer) { throw ADP.ArgumentNull("buffer"); } if ((long)buffer.Length < ((long)offset + (long)count)) { throw ADP.BufferExceeded("count"); } if (IsNull || 0 == count) { return 0; } AssertConnectionIsOpen(); AssertAmountIsValidOddOK(offset, "offset"); AssertAmountIsValidOddOK(count, "count"); int amount; uint readPosition = (uint)_currentPosition; // Bless their hearts: Oracle won't let us use odd addresses to read // character data, nor will they let us read a single byte from a // character lob. Instead, we allocate our own buffer and copy the // value to the caller's buffer. int oddPosition = 0; int oddOffset = 0; int oddCount = 0; byte[] readBuffer = buffer; int readOffset = offset; int readCount = count; if (IsCharacterLob) { oddPosition = (int)(readPosition & 0x1); oddOffset = offset & 0x1; oddCount = count & 0x1; readPosition /= 2; if (1 == oddOffset || 1 == oddPosition || 1 == oddCount) { readOffset = 0; readCount = count + oddCount + (2 * oddPosition); readBuffer = new byte[readCount]; } } ushort charsetId = unchecked(IsCharacterLob ? (ushort)OCI.CHARSETID.OCI_UTF16ID : (ushort)0); int rc = 0; amount = AdjustOffsetToOracle(readCount); // We need to pin the buffer and get the address of the offset that // the user requested; Oracle doesn't allow us to specify an offset // into the buffer. GCHandle handle = new GCHandle(); try { handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned); IntPtr bufferPtr = new IntPtr((long)handle.AddrOfPinnedObject() + readOffset); rc = TracedNativeMethods.OCILobRead( ServiceContextHandle, ErrorHandle, Descriptor, ref amount, readPosition + 1, bufferPtr, checked((uint)readCount), charsetId, _charsetForm ); } finally { if (handle.IsAllocated) { handle.Free(); // Unpin the buffer } } if ((int)OCI.RETURNCODE.OCI_NEED_DATA == rc) { rc = 0; } if ((int)OCI.RETURNCODE.OCI_NO_DATA == rc) { return 0; } if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } amount = AdjustOracleToOffset(amount); if (readBuffer as object != buffer as object) { if (amount >= count) { amount = count; } else { amount -= oddPosition; } Buffer.BlockCopy(readBuffer, oddPosition, buffer, offset, amount); readBuffer = null; } _currentPosition += amount; return amount; } public override long Seek (long offset, SeekOrigin origin) { AssertObjectNotDisposed(); if (IsNull) { return 0; } long newPosition = offset; // SeekOrigin.Begin is default case long length = Length; switch (origin) { case SeekOrigin.Begin: newPosition = offset; break; case SeekOrigin.End: newPosition = length + offset; break; case SeekOrigin.Current: newPosition = _currentPosition + offset; break; default: throw ADP.InvalidSeekOrigin(origin); } if (newPosition < 0 || newPosition > length) { throw ADP.SeekBeyondEnd("offset"); } _currentPosition = newPosition; return _currentPosition; } public override void SetLength (long value) { AssertObjectNotDisposed(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } AssertConnectionIsOpen(); AssertAmountIsValid(value, "value"); AssertTransactionExists(); UInt32 newlength = (UInt32)AdjustOffsetToOracle(value); int rc = TracedNativeMethods.OCILobTrim( ServiceContextHandle, ErrorHandle, Descriptor, newlength ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } // Adjust the current position to be within the length of the lob, if // we just truncated it before the current position. _currentPosition = Math.Min(_currentPosition, value); } public override void Write (byte[] buffer, int offset, int count) { AssertObjectNotDisposed(); AssertConnectionIsOpen(); if (count < 0) { throw ADP.MustBePositive("count"); } if (offset < 0) { throw ADP.MustBePositive("offset"); } if (null == buffer) { throw ADP.ArgumentNull("buffer"); } if ((long)buffer.Length < ((long)offset + (long)count)) { throw ADP.BufferExceeded("count"); } AssertTransactionExists(); if (IsNull) { throw ADP.LobWriteInvalidOnNull(); } AssertAmountIsValid(offset, "offset"); AssertAmountIsValid(count, "count"); AssertPositionIsValid(); OCI.CHARSETFORM charsetForm = _charsetForm; ushort charsetId = unchecked(IsCharacterLob ? (ushort)OCI.CHARSETID.OCI_UTF16ID : (ushort)0); int amount = AdjustOffsetToOracle(count); int rc = 0; if (0 == amount) { return; } // We need to pin the buffer and get the address of the offset that // the user requested; Oracle doesn't allow us to specify an offset // into the buffer. GCHandle handle = new GCHandle(); try { handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); IntPtr bufferPtr = new IntPtr((long)handle.AddrOfPinnedObject() + offset); rc = TracedNativeMethods.OCILobWrite( ServiceContextHandle, ErrorHandle, Descriptor, ref amount, CurrentOraclePosition, bufferPtr, unchecked((uint)count), // verified above (byte)OCI.PIECE.OCI_ONE_PIECE, charsetId, charsetForm ); } finally { if (handle.IsAllocated) { handle.Free(); // Unpin the buffer } } if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } amount = AdjustOracleToOffset(amount); _currentPosition += amount; } public override void WriteByte (byte value) { if (OracleType.Clob == _lobType || OracleType.NClob == _lobType) { throw ADP.WriteByteForBinaryLobsOnly(); } base.WriteByte(value); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- GroupQuery.cs
- ActivityMarkupSerializer.cs
- ComponentChangingEvent.cs
- OdbcError.cs
- TextTreeInsertElementUndoUnit.cs
- HttpValueCollection.cs
- RowParagraph.cs
- RelationshipEnd.cs
- ExclusiveNamedPipeTransportManager.cs
- GC.cs
- shaperfactory.cs
- Compensate.cs
- MouseEventArgs.cs
- PropertyDescriptorCollection.cs
- OdbcConnectionFactory.cs
- TypeResolver.cs
- ToolStripItemClickedEventArgs.cs
- TitleStyle.cs
- Operators.cs
- IgnoreFlushAndCloseStream.cs
- CompensationHandlingFilter.cs
- CodeExporter.cs
- FilteredAttributeCollection.cs
- ArrangedElementCollection.cs
- TemplateControl.cs
- StylusEventArgs.cs
- HttpWebResponse.cs
- DataGridColumnHeaderItemAutomationPeer.cs
- NoneExcludedImageIndexConverter.cs
- DataRowCollection.cs
- DetailsViewPagerRow.cs
- SemanticKeyElement.cs
- EntityDataSourceEntitySetNameItem.cs
- MorphHelpers.cs
- nulltextcontainer.cs
- ResourceDictionary.cs
- CapabilitiesSection.cs
- CellCreator.cs
- ToolStripSystemRenderer.cs
- BufferedGraphicsContext.cs
- FontCacheLogic.cs
- VirtualPath.cs
- ServiceContractViewControl.Designer.cs
- CompiledQuery.cs
- securitycriticaldata.cs
- BuildResultCache.cs
- DigestComparer.cs
- BatchParser.cs
- StrongNamePublicKeyBlob.cs
- TimeSpanParse.cs
- RemoteHelper.cs
- XPathEmptyIterator.cs
- TargetFrameworkUtil.cs
- WorkflowMarkupSerializerMapping.cs
- CodeSubDirectory.cs
- DocumentViewerConstants.cs
- ImageAnimator.cs
- BamlCollectionHolder.cs
- mediaclock.cs
- SecurityTraceRecordHelper.cs
- SafeCryptHandles.cs
- ServiceDescriptions.cs
- BooleanKeyFrameCollection.cs
- DataGridSortCommandEventArgs.cs
- ProcessProtocolHandler.cs
- TCPListener.cs
- InstalledFontCollection.cs
- SchemaSetCompiler.cs
- CodeDomDesignerLoader.cs
- ServicePointManager.cs
- WindowClosedEventArgs.cs
- ToolboxCategory.cs
- Compiler.cs
- updateconfighost.cs
- AccessibleObject.cs
- ScriptHandlerFactory.cs
- securitycriticaldata.cs
- ToolBarTray.cs
- ProcessStartInfo.cs
- SamlSecurityToken.cs
- DirtyTextRange.cs
- ImageListStreamer.cs
- RegexInterpreter.cs
- InternalsVisibleToAttribute.cs
- CryptoApi.cs
- Label.cs
- CompositeFontInfo.cs
- RTTrackingProfile.cs
- PeerApplication.cs
- SqlCacheDependencySection.cs
- EdmTypeAttribute.cs
- SoapTypeAttribute.cs
- EditingCoordinator.cs
- XhtmlBasicLabelAdapter.cs
- BitmapEffectInputConnector.cs
- OdbcConnectionHandle.cs
- InternalCache.cs
- SafeHandles.cs
- TabletDeviceInfo.cs
- SqlDataSourceView.cs