//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] // [....] //------------------------------------------------------------------------------ using System; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Data.SqlTypes; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; using System.Text; using System.Threading; namespace System.Data.Odbc { [ TypeConverterAttribute(typeof(System.Data.Odbc.OdbcParameter.OdbcParameterConverter)) ] public sealed partial class OdbcParameter : DbParameter, ICloneable, IDbDataParameter { private bool _hasChanged; private bool _userSpecifiedType; // _typemap User explicit set type or default parameter type // _infertpe _typemap if the user explicitly sets type // otherwise it is infered from the value // _bindtype The actual type used for binding. E.g. string substitutes numeric // // set_DbType: _bindtype = _infertype = _typemap = TypeMap.FromDbType(value) // set_OdbcType: _bindtype = _infertype = _typemap = TypeMap.FromOdbcType(value) // // GetParameterType: If _typemap != _infertype AND value != 0 // _bindtype = _infertype = TypeMap.FromSystemType(value.GetType()); // otherwise // _bindtype = _infertype // // Bind: Bind may change _bindtype if the type is not supported through the driver // private TypeMap _typemap; private TypeMap _bindtype; private string _parameterName; private byte _precision; private byte _scale; private bool _hasScale; private ODBC32.SQL_C _boundSqlCType; private ODBC32.SQL_TYPE _boundParameterType; // if we bound already that is the type we used private int _boundSize; private int _boundScale; private IntPtr _boundBuffer; private IntPtr _boundIntbuffer; private TypeMap _originalbindtype; // the original type in case we had to change the bindtype // (e.g. decimal to string) private byte _internalPrecision; private bool _internalShouldSerializeSize; private int _internalSize; private ParameterDirection _internalDirection; private byte _internalScale; private int _internalOffset; internal bool _internalUserSpecifiedType; private object _internalValue; private int _preparedOffset; private int _preparedSize; private int _preparedBufferSize; private object _preparedValue; private int _preparedIntOffset; private int _preparedValueOffset; private ODBC32.SQL_C _prepared_Sql_C_Type; public OdbcParameter() : base() { // uses System.Threading! } public OdbcParameter(string name, object value) : this() { ParameterName = name; Value = value; } public OdbcParameter(string name, OdbcType type) : this() { ParameterName = name; OdbcType = type; } public OdbcParameter(string name, OdbcType type, int size) : this() { ParameterName = name; OdbcType = type; Size = size; } public OdbcParameter(string name, OdbcType type, int size, string sourcecolumn) : this() { ParameterName = name; OdbcType = type; Size = size; SourceColumn = sourcecolumn; } [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508 public OdbcParameter(string parameterName, OdbcType odbcType, int size, ParameterDirection parameterDirection, Boolean isNullable, Byte precision, Byte scale, string srcColumn, DataRowVersion srcVersion, object value ) : this() { // V1.0 everything this.ParameterName = parameterName; this.OdbcType = odbcType; this.Size = size; this.Direction = parameterDirection; this.IsNullable = isNullable; PrecisionInternal = precision; ScaleInternal = scale; this.SourceColumn = srcColumn; this.SourceVersion = srcVersion; this.Value = value; } [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508 public OdbcParameter(string parameterName, OdbcType odbcType, int size, ParameterDirection parameterDirection, Byte precision, Byte scale, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, object value) : this() { // V2.0 everything - round trip all browsable properties + precision/scale this.ParameterName = parameterName; this.OdbcType = odbcType; this.Size = size; this.Direction = parameterDirection; this.PrecisionInternal = precision; this.ScaleInternal = scale; this.SourceColumn = sourceColumn; this.SourceVersion = sourceVersion; this.SourceColumnNullMapping = sourceColumnNullMapping; this.Value = value; } override public System.Data.DbType DbType { get { if (_userSpecifiedType) { return _typemap._dbType; } return TypeMap._NVarChar._dbType; // default type } set { if ((null == _typemap) || (_typemap._dbType != value)) { PropertyTypeChanging(); _typemap = TypeMap.FromDbType(value); _userSpecifiedType = true; } } } public override void ResetDbType() { ResetOdbcType(); } [ DefaultValue(OdbcType.NChar), RefreshProperties(RefreshProperties.All), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.OdbcParameter_OdbcType), System.Data.Common.DbProviderSpecificTypePropertyAttribute(true), ] public OdbcType OdbcType { get { if (_userSpecifiedType) { return _typemap._odbcType; } return TypeMap._NVarChar._odbcType; // default type } set { if ((null == _typemap) || (_typemap._odbcType != value)) { PropertyTypeChanging(); _typemap = TypeMap.FromOdbcType(value); _userSpecifiedType = true; } } } public void ResetOdbcType() { PropertyTypeChanging(); _typemap = null; _userSpecifiedType = false; } internal bool HasChanged { set { _hasChanged = value; } } internal bool UserSpecifiedType { get { return _userSpecifiedType; } } [ ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DbParameter_ParameterName), ] override public string ParameterName { // V1.2.3300, XXXParameter V1.0.3300 get { string parameterName = _parameterName; return ((null != parameterName) ? parameterName : ADP.StrEmpty); } set { if (_parameterName != value) { PropertyChanging(); _parameterName = value; } } } [DefaultValue((Byte)0)] // MDAC 65862 [ResCategoryAttribute(Res.DataCategory_Data)] [ResDescriptionAttribute(Res.DbDataParameter_Precision)] public new Byte Precision { get { return PrecisionInternal; } set { PrecisionInternal = value; } } internal byte PrecisionInternal { get { byte precision = _precision; if (0 == precision) { precision = ValuePrecision(Value); } return precision; } set { if (_precision != value) { PropertyChanging(); _precision = value; } } } private bool ShouldSerializePrecision() { return (0 != _precision); } [DefaultValue((Byte)0)] // MDAC 65862 [ResCategoryAttribute(Res.DataCategory_Data)] [ResDescriptionAttribute(Res.DbDataParameter_Scale)] public new Byte Scale { get { return ScaleInternal; } set { ScaleInternal = value; } } internal byte ScaleInternal { get { byte scale = _scale; if (!ShouldSerializeScale(scale)) { // WebData 94688 scale = ValueScale(Value); } return scale; } set { if (_scale != value || !_hasScale) { PropertyChanging(); _scale = value; _hasScale = true; } } } private bool ShouldSerializeScale() { return ShouldSerializeScale(_scale); } private bool ShouldSerializeScale(byte scale) { return _hasScale && ((0 != scale) || ShouldSerializePrecision()); } // returns the count of bytes for the data (ColumnSize argument to SqlBindParameter) private int GetColumnSize(object value, int offset, int ordinal) { if ((ODBC32.SQL_C.NUMERIC == _bindtype._sql_c) && (0 != _internalPrecision)){ return Math.Min((int)_internalPrecision,ADP.DecimalMaxPrecision); } int cch = _bindtype._columnSize; if (0 >= cch) { if (ODBC32.SQL_C.NUMERIC == _typemap._sql_c) { cch = 62; // (DecimalMaxPrecision+sign+terminator)*BytesPerUnicodeCharater } else { cch = _internalSize; if (!_internalShouldSerializeSize || 0x3fffffff<=cch || cch<0) { Debug.Assert((ODBC32.SQL_C.WCHAR == _bindtype._sql_c) || (ODBC32.SQL_C.BINARY == _bindtype._sql_c), "not wchar or binary"); if (!_internalShouldSerializeSize && (0 != (ParameterDirection.Output & _internalDirection))) { throw ADP.UninitializedParameterSize(ordinal, _bindtype._type); } if ((null == value) || Convert.IsDBNull(value)) { cch = 0; } else if (value is String) { cch = ((String)value).Length - offset; if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) { // restrict output parameters when user set Size to Int32.MaxValue // to the greater of intput size or 8K cch = Math.Max(cch, 4 * 1024); // MDAC 69224 } // the following code causes failure against SQL 6.5 // ERROR [HY104] [Microsoft][ODBC SQL Server Driver]Invalid precision value // // the code causes failure if it is NOT there (remark added by [....]) // it causes failure with jet if it is there // // MDAC 76227: Code is required for japanese client/server tests. // If this causes regressions with Jet please doc here including bug#. ([....]) // if ((ODBC32.SQL_TYPE.CHAR == _bindtype._sql_type) || (ODBC32.SQL_TYPE.VARCHAR == _bindtype._sql_type) || (ODBC32.SQL_TYPE.LONGVARCHAR == _bindtype._sql_type)) { cch = System.Text.Encoding.Default.GetMaxByteCount(cch); } } else if (value is char[]) { cch = ((char[])value).Length - offset; if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) { cch = Math.Max(cch, 4 * 1024); // MDAC 69224 } if ((ODBC32.SQL_TYPE.CHAR == _bindtype._sql_type) || (ODBC32.SQL_TYPE.VARCHAR == _bindtype._sql_type) || (ODBC32.SQL_TYPE.LONGVARCHAR == _bindtype._sql_type)) { cch = System.Text.Encoding.Default.GetMaxByteCount(cch); } } else if (value is byte[]) { cch = ((byte[])value).Length - offset; if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) { // restrict output parameters when user set Size to Int32.MaxValue // to the greater of intput size or 8K cch = Math.Max(cch, 8 * 1024); // MDAC 69224 } } #if DEBUG else { Debug.Assert(false, "not expecting this"); } #endif // Note: ColumnSize should never be 0, // this represents the size of the column on the backend. // // without the following code causes failure //ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]Invalid precision value cch = Math.Max(2, cch); } } } Debug.Assert((0 <= cch) && (cch < 0x3fffffff), String.Format((IFormatProvider)null, "GetColumnSize: cch = {0} out of range, _internalShouldSerializeSize = {1}, _internalSize = {2}",cch, _internalShouldSerializeSize, _internalSize)); return cch; } // Return the count of bytes for the data (size in bytes for the native buffer) // private int GetValueSize(object value, int offset) { if ((ODBC32.SQL_C.NUMERIC == _bindtype._sql_c) && (0 != _internalPrecision)){ return Math.Min((int)_internalPrecision,ADP.DecimalMaxPrecision); } int cch = _bindtype._columnSize; if (0 >= cch) { bool twobytesperunit = false; if (value is String) { cch = ((string)value).Length - offset; twobytesperunit = true; } else if (value is char[]) { cch = ((char[])value).Length - offset; twobytesperunit = true; } else if (value is byte[]) { cch = ((byte[])value).Length - offset; } else { cch = 0; } if (_internalShouldSerializeSize && (_internalSize>=0) && (_internalSize= ccb) { if (ODBC32.SQL_C.NUMERIC == _typemap._sql_c) { ccb = 518; // _bindtype would be VarChar ([0-9]?{255} + '-' + '.') * 2 } else { ccb = _internalSize; if (!_internalShouldSerializeSize || (0x3fffffff <= ccb)||(ccb < 0)) { Debug.Assert((ODBC32.SQL_C.WCHAR == _bindtype._sql_c) || (ODBC32.SQL_C.BINARY == _bindtype._sql_c), "not wchar or binary"); if ((ccb <= 0) && (0 != (ParameterDirection.Output & _internalDirection))) { throw ADP.UninitializedParameterSize(ordinal, _bindtype._type); } if ((null == value) || Convert.IsDBNull(value)) { if (_bindtype._sql_c == ODBC32.SQL_C.WCHAR) { ccb = 2; // allow for null termination } else { ccb = 0; } } else if (value is String) { ccb = (((String)value).Length - offset ) * 2 + 2; } else if (value is char[]) { ccb = (((char[])value).Length - offset ) * 2 + 2; } else if (value is byte[]) { ccb = ((byte[])value).Length - offset; } #if DEBUG else { Debug.Assert(false, "not expecting this"); } #endif if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) { // restrict output parameters when user set Size to Int32.MaxValue // to the greater of intput size or 8K ccb = Math.Max(ccb, 8 * 1024); // MDAC 69224 } } else if (ODBC32.SQL_C.WCHAR == _bindtype._sql_c) { if ((value is String) && (ccb < ((String)value).Length) && (_bindtype == _originalbindtype)) { // silently truncate ... MDAC 84408 ... do not truncate upgraded values ... MDAC 84706 ccb = ((String)value).Length; } ccb = (ccb * 2) + 2; // allow for null termination } else if ((value is byte[]) && (ccb < ((byte[])value).Length) && (_bindtype == _originalbindtype)) { // silently truncate ... MDAC 84408 ... do not truncate upgraded values ... MDAC 84706 ccb = ((byte[])value).Length; } } } Debug.Assert((0 <= ccb) && (ccb < 0x3fffffff), "GetParameterSize: out of range " + ccb); return ccb; } private byte GetParameterPrecision(object value) { if (0 != _internalPrecision && value is decimal) { // from qfe 762 if (_internalPrecision<29) { // from SqlClient ... if (_internalPrecision != 0) { // devnote: If the userspecified precision (_internalPrecision) is less than the actual values precision // we silently adjust the userspecified precision to the values precision. byte precision = ((SqlDecimal)(decimal)value).Precision; _internalPrecision = Math.Max(_internalPrecision, precision); // silently adjust the precision } return _internalPrecision; } return ADP.DecimalMaxPrecision; } if ((null == value) || (value is Decimal) || Convert.IsDBNull(value)) { // MDAC 60882 return ADP.DecimalMaxPrecision28; } return 0; } private byte GetParameterScale(object value) { // For any value that is not decimal simply return the Scale // if (!(value is decimal)) { return _internalScale; } // Determin the values scale // If the user specified a lower scale we return the user specified scale, // otherwise the values scale // byte s = (byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10); if ((_internalScale > 0) && (_internalScale < s)){ return _internalScale; } return s; } //This is required for OdbcCommand.Clone to deep copy the parameters collection object ICloneable.Clone() { return new OdbcParameter(this); } private void CopyParameterInternal () { _internalValue = Value; // we should coerce the parameter value at this time. _internalPrecision = ShouldSerializePrecision() ? PrecisionInternal : ValuePrecision(_internalValue); _internalShouldSerializeSize = ShouldSerializeSize(); _internalSize = _internalShouldSerializeSize ? Size : ValueSize(_internalValue); _internalDirection = Direction; _internalScale = ShouldSerializeScale() ? ScaleInternal : ValueScale(_internalValue); _internalOffset = Offset; _internalUserSpecifiedType = UserSpecifiedType; } private void CloneHelper(OdbcParameter destination) { CloneHelperCore(destination); destination._userSpecifiedType = _userSpecifiedType; destination._typemap = _typemap; destination._parameterName = _parameterName; destination._precision = _precision; destination._scale = _scale; destination._hasScale = _hasScale; } internal void ClearBinding() { if (!_userSpecifiedType) { _typemap = null; } _bindtype = null; } internal void PrepareForBind(OdbcCommand command, short ordinal, ref int parameterBufferSize) { // make a snapshot of the current properties. Properties may change while we work on them // CopyParameterInternal(); object value = ProcessAndGetParameterValue(); int offset = _internalOffset; int size = _internalSize; ODBC32.SQL_C sql_c_type; // offset validation based on the values type // if (offset > 0) { if (value is string) { if (offset > ((string)value).Length) { throw ADP.OffsetOutOfRangeException(); } } else if (value is char[]) { if (offset > ((char[])value).Length) { throw ADP.OffsetOutOfRangeException(); } } else if (value is byte[]) { if (offset > ((byte[])value).Length) { throw ADP.OffsetOutOfRangeException(); } } else { // for all other types offset has no meaning // this is important since we might upgrade some types to strings offset = 0; } } // type support verification for certain data types // switch(_bindtype._sql_type) { case ODBC32.SQL_TYPE.DECIMAL: case ODBC32.SQL_TYPE.NUMERIC: if ( !command.Connection.IsV3Driver // for non V3 driver we always do the conversion || !command.Connection.TestTypeSupport(ODBC32.SQL_TYPE.NUMERIC) // otherwise we convert if the driver does not support numeric || command.Connection.TestRestrictedSqlBindType(_bindtype._sql_type)// or the type is not supported ){ // No support for NUMERIC // Change the type _bindtype = TypeMap._VarChar; if ((null != value) && !Convert.IsDBNull(value)) { value = ((Decimal)value).ToString(CultureInfo.CurrentCulture); size = ((string)value).Length; offset = 0; } } break; case ODBC32.SQL_TYPE.BIGINT: if (!command.Connection.IsV3Driver){ // No support for BIGINT // Change the type _bindtype = TypeMap._VarChar; if ((null != value) && !Convert.IsDBNull(value)) { value = ((Int64)value).ToString(CultureInfo.CurrentCulture); size = ((string)value).Length; offset = 0; } } break; case ODBC32.SQL_TYPE.WCHAR: // MDAC 68993 case ODBC32.SQL_TYPE.WVARCHAR: case ODBC32.SQL_TYPE.WLONGVARCHAR: if (value is Char) { value = value.ToString(); size = ((string)value).Length; offset = 0; } if (!command.Connection.TestTypeSupport (_bindtype._sql_type)) { // No support for WCHAR, WVARCHAR or WLONGVARCHAR // Change the type if (ODBC32.SQL_TYPE.WCHAR == _bindtype._sql_type) { _bindtype = TypeMap._Char; } else if (ODBC32.SQL_TYPE.WVARCHAR == _bindtype._sql_type) { _bindtype = TypeMap._VarChar; } else if (ODBC32.SQL_TYPE.WLONGVARCHAR == _bindtype._sql_type) { _bindtype = TypeMap._Text; } } break; } // end switch // Conversation from WCHAR to CHAR, VARCHAR or LONVARCHAR (AnsiString) is different for some providers // we need to chonvert WCHAR to CHAR and bind as sql_c_type = CHAR // sql_c_type = _bindtype._sql_c; if (!command.Connection.IsV3Driver) { if (sql_c_type == ODBC32.SQL_C.WCHAR) { sql_c_type = ODBC32.SQL_C.CHAR; if (null != value){ if (!Convert.IsDBNull(value) && value is string) { int lcid = System.Globalization.CultureInfo.CurrentCulture.LCID; CultureInfo culInfo = new CultureInfo(lcid); Encoding cpe = System.Text.Encoding.GetEncoding(culInfo.TextInfo.ANSICodePage); value = cpe.GetBytes(value.ToString()); size = ((byte[])value).Length; } } } }; int cbParameterSize = GetParameterSize(value, offset, ordinal); // count of bytes for the data, for SQLBindParameter // here we upgrade the datatypes if the given values size is bigger than the types columnsize // switch(_bindtype._sql_type) { case ODBC32.SQL_TYPE.VARBINARY: // MDAC 74372 // Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding if ((cbParameterSize > 8000)) { _bindtype = TypeMap._Image; } // will change to LONGVARBINARY break; case ODBC32.SQL_TYPE.VARCHAR: // MDAC 74372 // Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding if ((cbParameterSize > 8000)) { _bindtype = TypeMap._Text; } // will change to LONGVARCHAR break; case ODBC32.SQL_TYPE.WVARCHAR : // MDAC 75099 // Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding if ((cbParameterSize > 4000)) { _bindtype = TypeMap._NText; } // will change to WLONGVARCHAR break; } _prepared_Sql_C_Type = sql_c_type; _preparedOffset = offset; _preparedSize = size; _preparedValue = value; _preparedBufferSize = cbParameterSize; _preparedIntOffset = parameterBufferSize; _preparedValueOffset = _preparedIntOffset + IntPtr.Size; parameterBufferSize += (cbParameterSize + IntPtr.Size); } internal void Bind(OdbcStatementHandle hstmt, OdbcCommand command, short ordinal, CNativeBuffer parameterBuffer, bool allowReentrance) { ODBC32.RetCode retcode; ODBC32.SQL_C sql_c_type = _prepared_Sql_C_Type; ODBC32.SQL_PARAM sqldirection = SqlDirectionFromParameterDirection(); int offset = _preparedOffset; int size = _preparedSize; object value = _preparedValue; int cbValueSize = GetValueSize(value, offset); // count of bytes for the data int cchSize = GetColumnSize(value, offset, ordinal); // count of bytes for the data, used to allocate the buffer length byte precision = GetParameterPrecision(value); byte scale = GetParameterScale(value); int cbActual; HandleRef valueBuffer = parameterBuffer.PtrOffset(_preparedValueOffset, _preparedBufferSize); HandleRef intBuffer = parameterBuffer.PtrOffset(_preparedIntOffset, IntPtr.Size); // for the numeric datatype we need to do some special case handling ... // if (ODBC32.SQL_C.NUMERIC == sql_c_type) { // for input/output parameters we need to adjust the scale of the input value since the convert function in // sqlsrv32 takes this scale for the output parameter (possible bug in sqlsrv32?) // if ((ODBC32.SQL_PARAM.INPUT_OUTPUT == sqldirection) && (value is Decimal)) { if (scale < _internalScale) { while (scale < _internalScale) { value = ((decimal)value ) * 10; scale++; } } } SetInputValue(value, sql_c_type, cbValueSize, precision, 0, parameterBuffer); // for output parameters we need to write precision and scale to the buffer since the convert function in // sqlsrv32 expects these values there (possible bug in sqlsrv32?) // if (ODBC32.SQL_PARAM.INPUT != sqldirection) { parameterBuffer.WriteInt16(_preparedValueOffset, (short)(((ushort)scale << 8) | (ushort)precision)); } } else { SetInputValue(value, sql_c_type, cbValueSize, size, offset, parameterBuffer); } // Try to reuse existing bindings if // the binding is valid (means we already went through binding all parameters) // the parametercollection is bound already // the bindtype ParameterType did not change (forced upgrade) if (!_hasChanged && (_boundSqlCType == sql_c_type) && (_boundParameterType == _bindtype._sql_type) && (_boundSize == cchSize) && (_boundScale == scale) && (_boundBuffer == valueBuffer.Handle) && (_boundIntbuffer == intBuffer.Handle) ) { return; } //SQLBindParameter retcode = hstmt.BindParameter( ordinal, // Parameter Number (short)sqldirection, // InputOutputType sql_c_type, // ValueType _bindtype._sql_type, // ParameterType (IntPtr)cchSize, // ColumnSize (IntPtr)scale, // DecimalDigits valueBuffer, // ParameterValuePtr (IntPtr)_preparedBufferSize, intBuffer); // StrLen_or_IndPtr if (ODBC32.RetCode.SUCCESS != retcode) { if ("07006" == command.GetDiagSqlState()) { Bid.Trace(" Call to BindParameter returned errorcode [07006]\n"); command.Connection.FlagRestrictedSqlBindType(_bindtype._sql_type); if (allowReentrance) { this.Bind(hstmt, command, ordinal, parameterBuffer, false); return; } } command.Connection.HandleError(hstmt, retcode); } _hasChanged = false; _boundSqlCType = sql_c_type; _boundParameterType = _bindtype._sql_type; _boundSize = cchSize; _boundScale = scale; _boundBuffer = valueBuffer.Handle; _boundIntbuffer = intBuffer.Handle; if (ODBC32.SQL_C.NUMERIC == sql_c_type) { OdbcDescriptorHandle hdesc = command.GetDescriptorHandle(ODBC32.SQL_ATTR.APP_PARAM_DESC); // descriptor handle is cached on command wrapper, don't release it // Set descriptor Type // //SQLSetDescField(hdesc, i+1, SQL_DESC_TYPE, (void *)SQL_C_NUMERIC, 0); retcode = hdesc.SetDescriptionField1(ordinal, ODBC32.SQL_DESC.TYPE, (IntPtr)ODBC32.SQL_C.NUMERIC); if (ODBC32.RetCode.SUCCESS != retcode) { command.Connection.HandleError(hstmt, retcode); } // Set precision // cbActual= (int)precision; //SQLSetDescField(hdesc, i+1, SQL_DESC_PRECISION, (void *)precision, 0); retcode = hdesc.SetDescriptionField1(ordinal, ODBC32.SQL_DESC.PRECISION, (IntPtr)cbActual); if (ODBC32.RetCode.SUCCESS != retcode) { command.Connection.HandleError(hstmt, retcode); } // Set scale // // SQLSetDescField(hdesc, i+1, SQL_DESC_SCALE, (void *)llen, 0); cbActual= (int)scale; retcode = hdesc.SetDescriptionField1(ordinal, ODBC32.SQL_DESC.SCALE, (IntPtr)cbActual); if (ODBC32.RetCode.SUCCESS != retcode) { command.Connection.HandleError(hstmt, retcode); } // Set data pointer // // SQLSetDescField(hdesc, i+1, SQL_DESC_DATA_PTR, (void *)&numeric, 0); retcode = hdesc.SetDescriptionField2(ordinal, ODBC32.SQL_DESC.DATA_PTR, valueBuffer); if (ODBC32.RetCode.SUCCESS != retcode) { command.Connection.HandleError(hstmt, retcode); } } } internal void GetOutputValue(CNativeBuffer parameterBuffer) { //Handle any output params // No value is available if the user fiddles with the parameters properties // if (_hasChanged) return; if ((null != _bindtype) && (_internalDirection != ParameterDirection.Input)) { TypeMap typemap = _bindtype; _bindtype = null; int cbActual = (int)parameterBuffer.ReadIntPtr(_preparedIntOffset); if (ODBC32.SQL_NULL_DATA == cbActual) { Value = DBNull.Value; } else if ((0 <= cbActual) || (cbActual == ODBC32.SQL_NTS)){ // safeguard Value = parameterBuffer.MarshalToManaged(_preparedValueOffset, _boundSqlCType, cbActual); if (_boundSqlCType== ODBC32.SQL_C.CHAR) { if ((null != Value) && !Convert.IsDBNull(Value)) { int lcid = System.Globalization.CultureInfo.CurrentCulture.LCID; CultureInfo culInfo = new CultureInfo(lcid); Encoding cpe = System.Text.Encoding.GetEncoding(culInfo.TextInfo.ANSICodePage); Value = cpe.GetString((Byte[])Value); } } if ((typemap != _typemap) && (null != Value) && !Convert.IsDBNull(Value) && (Value.GetType() != _typemap._type)) { Debug.Assert(ODBC32.SQL_C.NUMERIC == _typemap._sql_c, "unexpected"); Value = Decimal.Parse((string)Value, System.Globalization.CultureInfo.CurrentCulture); } } } } private object ProcessAndGetParameterValue() { object value = _internalValue; if (_internalUserSpecifiedType) { if ((null != value) && !Convert.IsDBNull(value)) { Type valueType = value.GetType(); if (!valueType.IsArray) { if (valueType != _typemap._type) { try { value = Convert.ChangeType (value, _typemap._type, (System.IFormatProvider)null); } catch(Exception e) { // Don't know which exception to expect from ChangeType so we filter out the serious ones // if (!ADP.IsCatchableExceptionType(e)) { throw; } throw ADP.ParameterConversionFailed(value, _typemap._type, e); // WebData 75433 } } } else if (valueType == typeof(char[])) { value = new String((char[])value); } } } else if (null == _typemap) { if ((null == value) || Convert.IsDBNull (value)) { _typemap = TypeMap._NVarChar; // default type } else { Type type = value.GetType (); _typemap = TypeMap.FromSystemType (type); } } Debug.Assert(null != _typemap, "GetParameterValue: null _typemap"); _originalbindtype = _bindtype = _typemap; return value; } private void PropertyChanging() { _hasChanged = true; } private void PropertyTypeChanging() { PropertyChanging(); //CoercedValue = null; } internal void SetInputValue(object value, ODBC32.SQL_C sql_c_type, int cbsize, int sizeorprecision, int offset, CNativeBuffer parameterBuffer) { //Handle any input params if((ParameterDirection.Input == _internalDirection) || (ParameterDirection.InputOutput == _internalDirection)) { //Note: (lang) "null" means to use the servers default (not DBNull). //We probably should just not have bound this parameter, period, but that //would mess up the users question marks, etc... if((null == value)) { parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)ODBC32.SQL_DEFAULT_PARAM); } else if(Convert.IsDBNull(value)) { parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)ODBC32.SQL_NULL_DATA); } else { switch(sql_c_type) { case ODBC32.SQL_C.CHAR: case ODBC32.SQL_C.WCHAR: case ODBC32.SQL_C.BINARY: //StrLen_or_IndPtr is ignored except for Character or Binary or data. parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)cbsize); break; default: parameterBuffer.WriteIntPtr(_preparedIntOffset, IntPtr.Zero); break; } //Place the input param value into the native buffer parameterBuffer.MarshalToNative(_preparedValueOffset, value, sql_c_type, sizeorprecision, offset); } } else { // always set ouput only and return value parameter values to null when executing _internalValue = null; //Always initialize the intbuffer (for output params). Since we need to know //if/when the parameters are available for output. (ie: when is the buffer valid...) //if (_sqldirection != ODBC32.SQL_PARAM.INPUT) parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)ODBC32.SQL_NULL_DATA); } } private ODBC32.SQL_PARAM SqlDirectionFromParameterDirection () { switch(_internalDirection) { case ParameterDirection.Input: return ODBC32.SQL_PARAM.INPUT; case ParameterDirection.Output: case ParameterDirection.ReturnValue: //ODBC doesn't seem to distinguish between output and return value //as SQL_PARAM_RETURN_VALUE fails with "Invalid parameter type" return ODBC32.SQL_PARAM.OUTPUT; case ParameterDirection.InputOutput: return ODBC32.SQL_PARAM.INPUT_OUTPUT; default: Debug.Assert (false, "Unexpected Direction Property on Parameter"); return ODBC32.SQL_PARAM.INPUT; } } [ RefreshProperties(RefreshProperties.All), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DbParameter_Value), TypeConverterAttribute(typeof(StringConverter)), ] override public object Value { // V1.2.3300, XXXParameter V1.0.3300 get { return _value; } set { _coercedValue = null; _value = value; } } private byte ValuePrecision(object value) { return ValuePrecisionCore(value); } private byte ValueScale(object value) { return ValueScaleCore(value); } private int ValueSize(object value) { return ValueSizeCore(value); } // implemented as nested class to take advantage of the private/protected ShouldSerializeXXX methods sealed internal class OdbcParameterConverter : ExpandableObjectConverter { // converter classes should have public ctor public OdbcParameterConverter() { } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor)) { return true; } return base.CanConvertTo(context, destinationType); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (destinationType == null) { throw ADP.ArgumentNull("destinationType"); } if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) && value is OdbcParameter) { OdbcParameter p = (OdbcParameter)value; // MDAC 67321 - reducing parameter generated code int flags = 0; // if part of the collection - the parametername can't be empty if (OdbcType.NChar != p.OdbcType) { flags |= 1; } if (p.ShouldSerializeSize()) { flags |= 2; } if (!ADP.IsEmpty(p.SourceColumn)) { flags |= 4; } if (null != p.Value) { flags |= 8; } if ((ParameterDirection.Input != p.Direction) || p.IsNullable || p.ShouldSerializePrecision() || p.ShouldSerializeScale() || (DataRowVersion.Current != p.SourceVersion)) { flags |= 16; // V1.0 everything } if (p.SourceColumnNullMapping) { flags |= 32; // v2.0 everything } Type[] ctorParams; object[] ctorValues; switch(flags) { case 0: // ParameterName case 1: // SqlDbType ctorParams = new Type[] { typeof(string), typeof(OdbcType) }; ctorValues = new object[] { p.ParameterName, p.OdbcType }; break; case 2: // Size case 3: // Size, SqlDbType ctorParams = new Type[] { typeof(string), typeof(OdbcType), typeof(int) }; ctorValues = new object[] { p.ParameterName, p.OdbcType, p.Size }; break; case 4: // SourceColumn case 5: // SourceColumn, SqlDbType case 6: // SourceColumn, Size case 7: // SourceColumn, Size, SqlDbType ctorParams = new Type[] { typeof(string), typeof(OdbcType), typeof(int), typeof(string) }; ctorValues = new object[] { p.ParameterName, p.OdbcType, p.Size, p.SourceColumn }; break; case 8: // Value ctorParams = new Type[] { typeof(string), typeof(object) }; ctorValues = new object[] { p.ParameterName, p.Value }; break; default: if (0 == (32 & flags)) { // V1.0 everything ctorParams = new Type[] { typeof(string), typeof(OdbcType), typeof(int), typeof(ParameterDirection), typeof(bool), typeof(byte), typeof(byte), typeof(string), typeof(DataRowVersion), typeof(object) }; ctorValues = new object[] { p.ParameterName, p.OdbcType, p.Size, p.Direction, p.IsNullable, p.PrecisionInternal, p.ScaleInternal, p.SourceColumn, p.SourceVersion, p.Value }; } else { // v2.0 everything - round trip all browsable properties + precision/scale ctorParams = new Type[] { typeof(string), typeof(OdbcType), typeof(int), typeof(ParameterDirection), typeof(byte), typeof(byte), typeof(string), typeof(DataRowVersion), typeof(bool), typeof(object) }; ctorValues = new object[] { p.ParameterName, p.OdbcType, p.Size, p.Direction, p.PrecisionInternal, p.ScaleInternal, p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping, p.Value }; } break; } System.Reflection.ConstructorInfo ctor = typeof(OdbcParameter).GetConstructor(ctorParams); if (null != ctor) { return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ctor, ctorValues); } } return base.ConvertTo(context, culture, value, destinationType); } } } }