static internal int GetChars( NativeBuffer buffer, int valueOffset, int lengthOffset, MetaType metaType, OracleConnection connection, // See MDAC #78258 for reason. bool boundAsUCS2, // See MDAC #78258 for reason. int sourceOffset, char[] destinationBuffer, int destinationOffset, int charCount ) { // This static method allows the GetChars type getter to do it's job // without having to marshal the entire value into managed space. if (boundAsUCS2) { HandleRef sourceBuffer; if (!metaType.IsLong) { sourceBuffer = buffer.PtrOffset(valueOffset + (ADP.CharSize * sourceOffset)); } else { // Long values are bound out-of-line, which means we have // to do this the hard way... sourceBuffer = buffer.PtrOffset(valueOffset); IntPtr longBuffer = Marshal.ReadIntPtr((IntPtr)sourceBuffer); if (0 != sourceOffset) { longBuffer = new IntPtr(longBuffer.ToInt64() + (long)(ADP.CharSize * sourceOffset)); } HandleRef newSourceBuffer = new HandleRef(sourceBuffer.Wrapper, longBuffer); sourceBuffer = newSourceBuffer; } Marshal.Copy((IntPtr)sourceBuffer, destinationBuffer, destinationOffset, charCount); } else { // In the odd case that we don't have a Unicode value (see MDAC #78258 // for the reason) we have to do this the hard way -- get the full value, // then copy the data... string value = MarshalToString(buffer, valueOffset, lengthOffset, metaType, connection, boundAsUCS2, false); int valueLength = value.Length; int resultLength = (sourceOffset + charCount) > valueLength ? valueLength - sourceOffset : charCount; char[] result = value.ToCharArray(sourceOffset, resultLength); Buffer.BlockCopy(result, 0, destinationBuffer, (destinationOffset * ADP.CharSize), (resultLength * ADP.CharSize)); charCount = resultLength; } GC.KeepAlive(buffer); return(charCount); }
internal void GetNames() { _lob.AssertConnectionIsOpen(); int charSize = (Connection.EnvironmentHandle.IsUnicode) ? 2 : 1; short directoryAliasLength = (short)(charSize * 30); short fileAliasLength = (short)(charSize * 255); NativeBuffer buffer = Connection.ScratchBuffer; Debug.Assert(buffer.Length > (directoryAliasLength + fileAliasLength), "connection's scratch buffer is too small"); HandleRef directoryAlias = buffer.Ptr; HandleRef fileAlias = buffer.PtrOffset(directoryAliasLength); int rc = TracedNativeMethods.OCILobFileGetName( Connection.EnvironmentHandle, ErrorHandle, Descriptor, directoryAlias, ref directoryAliasLength, fileAlias, ref fileAliasLength ); if (0 != rc) { Connection.CheckError(ErrorHandle, rc); } _directoryAlias = Connection.GetString((IntPtr)directoryAlias, directoryAliasLength, false); _fileName = Connection.GetString((IntPtr)fileAlias, fileAliasLength, false); GC.KeepAlive(buffer); }
static internal int GetLength( NativeBuffer buffer, int lengthOffset, MetaType metaType ) { // Get the length of the data bound int length; HandleRef lengthBuffer = buffer.PtrOffset(lengthOffset); // Oracle only will write two bytes of length, but LONG data types // can exceed that amount; our piecewise callbacks will write a // full DWORD of length, so we need to get the full length for them, // but if we do that for all the other types, we'll be reading // un-initialized memory and bad things happen. if (metaType.IsLong) { length = Marshal.ReadInt32((IntPtr)lengthBuffer); } else { length = (int)Marshal.ReadInt16((IntPtr)lengthBuffer); } return(length); }
// (internal) construct from a row/parameter binding internal OracleTimeSpan( NativeBuffer buffer, int valueOffset) : this(true) { _value = new byte[11]; Marshal.Copy((IntPtr)buffer.PtrOffset(valueOffset), _value, 0, 11); }
static internal TimeSpan MarshalToTimeSpan( NativeBuffer buffer, int valueOffset) { byte[] rawValue = new byte[11]; Marshal.Copy((IntPtr)buffer.PtrOffset(valueOffset), rawValue, 0, 11); TimeSpan result = ToTimeSpan(rawValue); return(result); }
static internal int GetBytes( NativeBuffer buffer, int valueOffset, MetaType metaType, int sourceOffset, byte[] destinationBuffer, int destinationOffset, int byteCount ) { // This static method allows the GetBytes type getter to do it's job // without having to marshal the entire value into managed space. HandleRef sourceBuffer; if (!metaType.IsLong) { sourceBuffer = buffer.PtrOffset(valueOffset + sourceOffset); } else { // Long values are bound out-of-line, which means we have // to do this the hard way... sourceBuffer = buffer.PtrOffset(valueOffset); IntPtr longBuffer = Marshal.ReadIntPtr((IntPtr)sourceBuffer); if (0 != sourceOffset) { longBuffer = new IntPtr(longBuffer.ToInt64() + (long)sourceOffset); } HandleRef newSourceBuffer = new HandleRef(sourceBuffer.Wrapper, longBuffer); sourceBuffer = newSourceBuffer; } Marshal.Copy((IntPtr)sourceBuffer, destinationBuffer, destinationOffset, byteCount); GC.KeepAlive(sourceBuffer); return(byteCount); }
static internal byte[] GetBytes( NativeBuffer buffer, int valueOffset, MetaType metaType, OracleConnection connection) { // Static method to return the raw data bytes from the row/parameter // buffer, taking the binding type into account and adjusting it for // the server time zones, as appropriate. int ociBytes; OCI.DATATYPE ociType = metaType.OciType; switch (ociType) { case OCI.DATATYPE.DATE: ociBytes = x_DATE_Length; break; case OCI.DATATYPE.INT_TIMESTAMP: ociBytes = x_TIMESTAMP_Length; break; case OCI.DATATYPE.INT_TIMESTAMP_LTZ: ociBytes = x_TIMESTAMP_WITH_TIMEZONE_Length; break; default: Debug.Assert(OCI.DATATYPE.INT_TIMESTAMP_TZ == ociType, "unrecognized type"); ociBytes = x_TIMESTAMP_WITH_TIMEZONE_Length; break; } byte[] result = new byte[ociBytes]; Marshal.Copy((IntPtr)buffer.PtrOffset(valueOffset), result, 0, ociBytes); if (OCI.DATATYPE.INT_TIMESTAMP_LTZ == ociType) { TimeSpan tzadjust = connection.ServerTimeZoneAdjustmentToUTC; result[11] = (byte)(tzadjust.Hours + 20); result[12] = (byte)(tzadjust.Minutes + 60); } return(result); }
static internal int MarshalToInt32( NativeBuffer buffer, int valueOffset) { byte[] ociValue = new byte[5]; Marshal.Copy((IntPtr)buffer.PtrOffset(valueOffset), ociValue, 0, 5); int years = (int)((long)((int)ociValue[0] << 24 | (int)ociValue[1] << 16 | (int)ociValue[2] << 8 | (int)ociValue[3] ) - 0x80000000); int months = (int)ociValue[4] - 60; int result = (years * 12) + months; AssertValid(result); return(result); }
static internal string MarshalToString( NativeBuffer buffer, int valueOffset, int lengthOffset, MetaType metaType, OracleConnection connection, bool boundAsUCS2, bool outputParameterBinding ) { int valueLength = GetLength(buffer, lengthOffset, metaType); IntPtr valueBuffer = (IntPtr)buffer.PtrOffset(valueOffset); // Long values are bound out-of-line if (metaType.IsLong && !outputParameterBinding) { valueBuffer = (IntPtr)Marshal.ReadIntPtr(valueBuffer); } if (boundAsUCS2 && outputParameterBinding) { valueLength /= 2; } string result; if (boundAsUCS2) { result = Marshal.PtrToStringUni(valueBuffer, valueLength); } else { result = connection.GetString(valueBuffer, valueLength, metaType.UsesNationalCharacterSet); } GC.KeepAlive(buffer); return(result); }
internal void Bind( OciHandle statementHandle, NativeBuffer buffer, OciHandle errorHandle, int rowBufferLength ) { // Binds the buffer for the column to the statement handle specified. OciHandle defineHandle = null; IntPtr h; OCI.MODE mode = OCI.MODE.OCI_DEFAULT; int bindSize; OCI.DATATYPE ociType = _metaType.OciType; _rowBuffer = buffer; if (_metaType.IsLong) { mode = OCI.MODE.OCI_DYNAMIC_FETCH; bindSize = Int32.MaxValue; } else { bindSize = _size; } HandleRef indicatorLocation = ADP.NullHandleRef; HandleRef lengthLocation = ADP.NullHandleRef; HandleRef valueLocation = _rowBuffer.PtrOffset(_valueOffset); if (-1 != _indicatorOffset) { indicatorLocation = _rowBuffer.PtrOffset(_indicatorOffset); } if (-1 != _lengthOffset && !_metaType.IsLong) { lengthLocation = _rowBuffer.PtrOffset(_lengthOffset); } try { try { int rc = TracedNativeMethods.OCIDefineByPos( statementHandle, // hndlp out h, // defnpp errorHandle, // errhp _ordinal + 1, // position valueLocation, // valuep bindSize, // value_sz ociType, // htype indicatorLocation, // indp, lengthLocation, // rlenp, ADP.NullHandleRef, // rcodep, mode // mode ); if (rc != 0) { _connection.CheckError(errorHandle, rc); } defineHandle = new OciDefineHandle(statementHandle, h); if (0 != rowBufferLength) { int valOffset = rowBufferLength; int indOffset = (-1 != _indicatorOffset) ? rowBufferLength : 0; int lenOffset = (-1 != _lengthOffset && !_metaType.IsLong) ? rowBufferLength : 0; rc = TracedNativeMethods.OCIDefineArrayOfStruct( defineHandle, errorHandle, valOffset, indOffset, lenOffset, 0 // never use rcodep above... ); if (rc != 0) { _connection.CheckError(errorHandle, rc); } } if (!_connection.UnicodeEnabled) { if (_metaType.UsesNationalCharacterSet) { Debug.Assert(!_metaType.IsLong, "LONG data may never be bound as NCHAR"); // NOTE: the order is important here; setting charsetForm will // reset charsetId (I found this out the hard way...) defineHandle.SetAttribute(OCI.ATTR.OCI_ATTR_CHARSET_FORM, (int)OCI.CHARSETFORM.SQLCS_NCHAR, errorHandle); } if (_bindAsUCS2) { // NOTE: the order is important here; setting charsetForm will // reset charsetId (I found this out the hard way...) defineHandle.SetAttribute(OCI.ATTR.OCI_ATTR_CHARSET_ID, OCI.OCI_UCS2ID, errorHandle); } } if (_metaType.IsLong) { // Initialize the longBuffer in the rowBuffer to null Marshal.WriteIntPtr((IntPtr)_rowBuffer.PtrOffset(_valueOffset), IntPtr.Zero); if (null != _longBuffer) { _longBuffer.Dispose(); _longBuffer = null; } // We require MTxOCI8 to be in the path somewhere for us to handle LONG data if (!OCI.IsNewMtxOci8Installed) #if EVERETT { throw ADP.MustInstallNewMtxOciLONG(); } #else //!EVERETT { throw ADP.MustInstallNewMtxOci(); } #endif //!EVERETT _callback = new OCI.Callback.OCICallbackDefine(_callback_GetColumnPiecewise); rc = TracedNativeMethods.MTxOciDefineDynamic( defineHandle, // defnp errorHandle, // errhp ADP.NullHandleRef, // dvoid *octxp, _callback // OCICallbackDefine ocbfp ); if (rc != 0) { _connection.CheckError(errorHandle, rc); } } } finally { // We don't need this any longer, get rid of it. OciHandle.SafeDispose(ref defineHandle); } }
} // This is the value used for the SchemaTable, which must be Chars... //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // Methods // //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// private int _callback_GetColumnPiecewise( IntPtr octxp, IntPtr defnp, int iter, IntPtr bufpp, // dvoid** IntPtr alenp, // ub4** IntPtr piecep, // ub1* IntPtr indpp, // dvoid** IntPtr rcodep // ub2** ) { // Callback routine for Dynamic Binding column values from Oracle: tell // Oracle where to stuff the data. int thisChunkSize; // TODO: Consider creating a StringBuilder-like class (BlobBuilder?) that can store chunks of allocations, instead of a single buffer being re-allocated. if (null == _longBuffer) { _longBuffer = new NativeBuffer_LongColumnData(ChunkSize); _longCurrentOffset = 0; } if (0 == _longNextOffset) { thisChunkSize = _longBuffer.Length; // first read: fill the existing buffer } else { thisChunkSize = ChunkSize; } _longCurrentOffset = _longNextOffset; _longNextOffset = _longCurrentOffset + thisChunkSize; _longBuffer.Length = _longNextOffset; HandleRef buffer = _longBuffer.Ptr; //Debug.WriteLine(((IntPtr)buffer).ToInt32()); // Stuff the longBuffer into the rowBuffer so we can get use it // in the type getters later (this also verifies that the allocation // has occured) Marshal.WriteIntPtr((IntPtr)_rowBuffer.PtrOffset(_valueOffset), (IntPtr)buffer); Marshal.WriteIntPtr(alenp, (IntPtr)_rowBuffer.PtrOffset(_lengthOffset)); // *alenp if (-1 != _indicatorOffset) { Marshal.WriteIntPtr(indpp, (IntPtr)_rowBuffer.PtrOffset(_indicatorOffset)); // *indpp } else { Marshal.WriteIntPtr(indpp, IntPtr.Zero); // *indpp } Marshal.WriteIntPtr(bufpp, (IntPtr)_longBuffer.PtrOffset(_longCurrentOffset)); // *bufpp Marshal.WriteInt32((IntPtr)_rowBuffer.PtrOffset(_lengthOffset), thisChunkSize); // **alenp GC.KeepAlive(this); return((int)OCI.RETURNCODE.OCI_CONTINUE); }
//////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // Methods // //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// internal void Bind( OciHandle statementHandle, NativeBuffer parameterBuffer, OracleConnection connection ) { IntPtr h; // Don't bother with parameters where the user asks for the default value. if (!IsDirection(Parameter, ParameterDirection.Output) && null == Parameter.Value) { return; } string parameterName = Parameter.ParameterName; OciHandle errorHandle = connection.ErrorHandle; OciHandle environmentHandle = connection.EnvironmentHandle; int valueLength = 0; OCI.INDICATOR indicatorValue = OCI.INDICATOR.OK; int bufferLength; OCI.DATATYPE ociType = _bindingMetaType.OciType; HandleRef indicatorLocation = parameterBuffer.PtrOffset(_indicatorOffset); HandleRef lengthLocation = parameterBuffer.PtrOffset(_lengthOffset); HandleRef valueLocation = parameterBuffer.PtrOffset(_valueOffset); if (IsDirection(Parameter, ParameterDirection.Input)) { if (ADP.IsNull(_coercedValue)) { indicatorValue = OCI.INDICATOR.ISNULL; } else { valueLength = PutOracleValue( _coercedValue, valueLocation, _bindingMetaType, connection); } } else { Debug.Assert(IsDirection(Parameter, ParameterDirection.Output), "non-output output parameter?"); if (_bindingMetaType.IsVariableLength) { valueLength = 0; // Output-only values never have an input length... } else { valueLength = _bufferLengthInBytes; // ...except when they're fixed length, to avoid ORA-01459 errors } OciLobLocator.SafeDispose(ref _locator); OciHandle.SafeDispose(ref _descriptor); switch (ociType) { case OCI.DATATYPE.BFILE: case OCI.DATATYPE.BLOB: case OCI.DATATYPE.CLOB: _locator = new OciLobLocator(connection, _bindingMetaType.OracleType); break; case OCI.DATATYPE.RSET: _descriptor = new OciStatementHandle(environmentHandle); break; } if (null != _locator) { Marshal.WriteIntPtr((IntPtr)valueLocation, (IntPtr)_locator.Handle); } else if (null != _descriptor) { Marshal.WriteIntPtr((IntPtr)valueLocation, (IntPtr)_descriptor.Handle); } } Marshal.WriteInt16((IntPtr)indicatorLocation, (Int16)indicatorValue); // Don't bind a length value for LONGVARCHAR or LONGVARRAW data, or you'll end // up with ORA-01098: program Interface error during Long Insert\nORA-01458: invalid length inside variable character string // errors. if (OCI.DATATYPE.LONGVARCHAR == ociType || OCI.DATATYPE.LONGVARRAW == ociType) { lengthLocation = ADP.NullHandleRef; } else { // When we're binding this parameter as UCS2, the length we specify // must be in characters, not in bytes. if (_bindAsUCS2) { Marshal.WriteInt32((IntPtr)lengthLocation, (Int32)(valueLength / ADP.CharSize)); } else { Marshal.WriteInt32((IntPtr)lengthLocation, (Int32)valueLength); } } if (IsDirection(Parameter, ParameterDirection.Output)) { bufferLength = _bufferLengthInBytes; } else { bufferLength = valueLength; } // Finally, tell Oracle about our parameter. int rc = TracedNativeMethods.OCIBindByName( statementHandle, out h, errorHandle, parameterName, parameterName.Length, valueLocation, bufferLength, ociType, indicatorLocation, lengthLocation, ADP.NullHandleRef, 0, ADP.NullHandleRef, OCI.MODE.OCI_DEFAULT ); if (rc != 0) { _command.Connection.CheckError(errorHandle, rc); } _bindHandle = new OciBindHandle(statementHandle, h); #if TRACEPARAMETERVALUES if (null != _coercedValue) { SafeNativeMethods.OutputDebugStringW("Value = '" + _coercedValue.ToString() + "'\n"); } #endif //TRACEPARAMETERVALUES // OK, character bindings have a few extra things we need to do to // deal with character sizes and alternate character sets. if (_bindingMetaType.IsCharacterType) { // To avoid problems when our buffer is larger than the maximum number // of characters, we use OCI_ATTR_MAXCHAR_SIZE to limit the number of // characters that will be used. (Except on Oracle8i clients where it // isn't available) if (OCI.ClientVersionAtLeastOracle9i && IsDirection(Parameter, ParameterDirection.Output)) { _bindHandle.SetAttribute(OCI.ATTR.OCI_ATTR_MAXCHAR_SIZE, (int)_bufferLength, errorHandle); } if ((bufferLength > _bindingMetaType.MaxBindSize / ADP.CharSize) || (!OCI.ClientVersionAtLeastOracle9i && _bindingMetaType.UsesNationalCharacterSet)) // need to specify MAXDATA_SIZE for OCI8 UCS2 bindings to work { _bindHandle.SetAttribute(OCI.ATTR.OCI_ATTR_MAXDATA_SIZE, (int)_bindingMetaType.MaxBindSize, errorHandle); } // NOTE: the order is important here; setting charsetForm will // reset charsetId (I found this out the hard way...) if (_bindingMetaType.UsesNationalCharacterSet) { _bindHandle.SetAttribute(OCI.ATTR.OCI_ATTR_CHARSET_FORM, (int)OCI.CHARSETFORM.SQLCS_NCHAR, errorHandle); } // NOTE: the order is important here; setting charsetForm will // reset charsetId (I found this out the hard way...) if (_bindAsUCS2) { _bindHandle.SetAttribute(OCI.ATTR.OCI_ATTR_CHARSET_ID, OCI.OCI_UCS2ID, errorHandle); } } }
internal object GetOutputValue( NativeBuffer parameterBuffer, OracleConnection connection, // connection, so we can create LOB values bool needCLSType ) { object result; // Returns an object that contains the value of the column in the // specified row buffer. This method returns Oracle-typed objects. if (Marshal.ReadInt16((IntPtr)parameterBuffer.Ptr, _indicatorOffset) == (Int16)OCI.INDICATOR.ISNULL) { return(DBNull.Value); } switch (_bindingMetaType.OciType) { case OCI.DATATYPE.FLOAT: case OCI.DATATYPE.INTEGER: case OCI.DATATYPE.UNSIGNEDINT: result = Marshal.PtrToStructure((IntPtr)parameterBuffer.PtrOffset(_valueOffset), _bindingMetaType.BaseType); return(result); case OCI.DATATYPE.BFILE: result = new OracleBFile(_locator); return(result); case OCI.DATATYPE.RAW: case OCI.DATATYPE.LONGRAW: case OCI.DATATYPE.LONGVARRAW: result = new OracleBinary(parameterBuffer, _valueOffset, _lengthOffset, _bindingMetaType); if (needCLSType) { object newresult = ((OracleBinary)result).Value; result = newresult; } return(result); case OCI.DATATYPE.RSET: result = new OracleDataReader(connection, _descriptor); return(result); case OCI.DATATYPE.DATE: case OCI.DATATYPE.INT_TIMESTAMP: case OCI.DATATYPE.INT_TIMESTAMP_TZ: case OCI.DATATYPE.INT_TIMESTAMP_LTZ: result = new OracleDateTime(parameterBuffer, _valueOffset, _bindingMetaType, connection); if (needCLSType) { object newresult = ((OracleDateTime)result).Value; result = newresult; } return(result); case OCI.DATATYPE.BLOB: case OCI.DATATYPE.CLOB: result = new OracleLob(_locator); return(result); case OCI.DATATYPE.INT_INTERVAL_YM: result = new OracleMonthSpan(parameterBuffer, _valueOffset); if (needCLSType) { object newresult = ((OracleMonthSpan)result).Value; result = newresult; } return(result); case OCI.DATATYPE.VARNUM: result = new OracleNumber(parameterBuffer, _valueOffset); if (needCLSType) { object newresult = ((OracleNumber)result).Value; result = newresult; } return(result); case OCI.DATATYPE.CHAR: case OCI.DATATYPE.VARCHAR2: case OCI.DATATYPE.LONG: case OCI.DATATYPE.LONGVARCHAR: result = new OracleString(parameterBuffer, _valueOffset, _lengthOffset, _bindingMetaType, connection, _bindAsUCS2, true ); int size = _parameter.Size; if (0 != size && size < ((OracleString)result).Length) { string truncatedResult = ((OracleString)result).Value.Substring(0, size); if (needCLSType) { result = truncatedResult; } else { result = new OracleString(truncatedResult); } } else if (needCLSType) { object newresult = ((OracleString)result).Value; result = newresult; } return(result); case OCI.DATATYPE.INT_INTERVAL_DS: result = new OracleTimeSpan(parameterBuffer, _valueOffset); if (needCLSType) { object newresult = ((OracleTimeSpan)result).Value; result = newresult; } return(result); } throw ADP.TypeNotSupported(_bindingMetaType.OciType); }