1 //------------------------------------------------------------------------------
2 // <copyright file="OdbcParameter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
11 using System.ComponentModel;
13 using System.Data.Common;
14 using System.Data.ProviderBase;
15 using System.Data.SqlTypes;
16 using System.Diagnostics;
17 using System.Globalization;
18 using System.Runtime.InteropServices;
20 using System.Threading;
22 namespace System.Data.Odbc {
25 TypeConverterAttribute(typeof(System.Data.Odbc.OdbcParameter.OdbcParameterConverter))
27 public sealed partial class OdbcParameter : DbParameter, ICloneable, IDbDataParameter {
29 private bool _hasChanged;
30 private bool _userSpecifiedType;
32 // _typemap User explicit set type or default parameter type
33 // _infertpe _typemap if the user explicitly sets type
34 // otherwise it is infered from the value
35 // _bindtype The actual type used for binding. E.g. string substitutes numeric
37 // set_DbType: _bindtype = _infertype = _typemap = TypeMap.FromDbType(value)
38 // set_OdbcType: _bindtype = _infertype = _typemap = TypeMap.FromOdbcType(value)
40 // GetParameterType: If _typemap != _infertype AND value != 0
41 // _bindtype = _infertype = TypeMap.FromSystemType(value.GetType());
43 // _bindtype = _infertype
45 // Bind: Bind may change _bindtype if the type is not supported through the driver
48 private TypeMap _typemap;
49 private TypeMap _bindtype;
51 private string _parameterName;
52 private byte _precision;
54 private bool _hasScale;
57 private ODBC32.SQL_C _boundSqlCType;
58 private ODBC32.SQL_TYPE _boundParameterType; // if we bound already that is the type we used
59 private int _boundSize;
60 private int _boundScale;
61 private IntPtr _boundBuffer;
62 private IntPtr _boundIntbuffer;
63 private TypeMap _originalbindtype; // the original type in case we had to change the bindtype
64 // (e.g. decimal to string)
65 private byte _internalPrecision;
66 private bool _internalShouldSerializeSize;
67 private int _internalSize;
68 private ParameterDirection _internalDirection;
69 private byte _internalScale;
70 private int _internalOffset;
71 internal bool _internalUserSpecifiedType;
72 private object _internalValue;
74 private int _preparedOffset;
75 private int _preparedSize;
76 private int _preparedBufferSize;
77 private object _preparedValue;
78 private int _preparedIntOffset;
79 private int _preparedValueOffset;
81 private ODBC32.SQL_C _prepared_Sql_C_Type;
83 public OdbcParameter() : base() {
84 // uses System.Threading!
87 public OdbcParameter(string name, object value) : this() {
92 public OdbcParameter(string name, OdbcType type) : this() {
97 public OdbcParameter(string name, OdbcType type, int size) : this() {
103 public OdbcParameter(string name, OdbcType type, int size, string sourcecolumn) : this() {
104 ParameterName = name;
107 SourceColumn = sourcecolumn;
111 [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
112 public OdbcParameter(string parameterName,
115 ParameterDirection parameterDirection,
120 DataRowVersion srcVersion,
122 ) : this() { // V1.0 everything
123 this.ParameterName = parameterName;
124 this.OdbcType = odbcType;
126 this.Direction = parameterDirection;
127 this.IsNullable = isNullable;
128 PrecisionInternal = precision;
129 ScaleInternal = scale;
130 this.SourceColumn = srcColumn;
131 this.SourceVersion = srcVersion;
135 [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
136 public OdbcParameter(string parameterName,
137 OdbcType odbcType, int size,
138 ParameterDirection parameterDirection,
139 Byte precision, Byte scale,
140 string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping,
141 object value) : this() { // V2.0 everything - round trip all browsable properties + precision/scale
142 this.ParameterName = parameterName;
143 this.OdbcType = odbcType;
145 this.Direction = parameterDirection;
146 this.PrecisionInternal = precision;
147 this.ScaleInternal = scale;
148 this.SourceColumn = sourceColumn;
149 this.SourceVersion = sourceVersion;
150 this.SourceColumnNullMapping = sourceColumnNullMapping;
154 override public System.Data.DbType DbType {
156 if (_userSpecifiedType) {
157 return _typemap._dbType;
159 return TypeMap._NVarChar._dbType; // default type
162 if ((null == _typemap) || (_typemap._dbType != value)) {
163 PropertyTypeChanging();
164 _typemap = TypeMap.FromDbType(value);
165 _userSpecifiedType = true;
170 public override void ResetDbType() {
175 DefaultValue(OdbcType.NChar),
176 RefreshProperties(RefreshProperties.All),
177 ResCategoryAttribute(Res.DataCategory_Data),
178 ResDescriptionAttribute(Res.OdbcParameter_OdbcType),
179 System.Data.Common.DbProviderSpecificTypePropertyAttribute(true),
181 public OdbcType OdbcType {
183 if (_userSpecifiedType) {
184 return _typemap._odbcType;
186 return TypeMap._NVarChar._odbcType; // default type
189 if ((null == _typemap) || (_typemap._odbcType != value)) {
190 PropertyTypeChanging();
191 _typemap = TypeMap.FromOdbcType(value);
192 _userSpecifiedType = true;
197 public void ResetOdbcType() {
198 PropertyTypeChanging();
200 _userSpecifiedType = false;
203 internal bool HasChanged {
209 internal bool UserSpecifiedType {
211 return _userSpecifiedType;
216 ResCategoryAttribute(Res.DataCategory_Data),
217 ResDescriptionAttribute(Res.DbParameter_ParameterName),
219 override public string ParameterName { // V1.2.3300, XXXParameter V1.0.3300
221 string parameterName = _parameterName;
222 return ((null != parameterName) ? parameterName : ADP.StrEmpty);
225 if (_parameterName != value) {
227 _parameterName = value;
232 [DefaultValue((Byte)0)] // MDAC 65862
233 [ResCategoryAttribute(Res.DataCategory_Data)]
234 [ResDescriptionAttribute(Res.DbDataParameter_Precision)]
235 public new Byte Precision {
237 return PrecisionInternal;
240 PrecisionInternal = value;
243 internal byte PrecisionInternal {
245 byte precision = _precision;
246 if (0 == precision) {
247 precision = ValuePrecision(Value);
252 if (_precision != value) {
258 private bool ShouldSerializePrecision() {
259 return (0 != _precision);
262 [DefaultValue((Byte)0)] // MDAC 65862
263 [ResCategoryAttribute(Res.DataCategory_Data)]
264 [ResDescriptionAttribute(Res.DbDataParameter_Scale)]
265 public new Byte Scale {
267 return ScaleInternal;
270 ScaleInternal = value;
273 internal byte ScaleInternal {
276 if (!ShouldSerializeScale(scale)) { // WebData 94688
277 scale = ValueScale(Value);
282 if (_scale != value || !_hasScale) {
289 private bool ShouldSerializeScale() {
290 return ShouldSerializeScale(_scale);
292 private bool ShouldSerializeScale(byte scale) {
293 return _hasScale && ((0 != scale) || ShouldSerializePrecision());
296 // returns the count of bytes for the data (ColumnSize argument to SqlBindParameter)
297 private int GetColumnSize(object value, int offset, int ordinal) {
298 if ((ODBC32.SQL_C.NUMERIC == _bindtype._sql_c) && (0 != _internalPrecision)){
299 return Math.Min((int)_internalPrecision,ADP.DecimalMaxPrecision);
301 int cch = _bindtype._columnSize;
303 if (ODBC32.SQL_C.NUMERIC == _typemap._sql_c) {
304 cch = 62; // (DecimalMaxPrecision+sign+terminator)*BytesPerUnicodeCharater
308 if (!_internalShouldSerializeSize || 0x3fffffff<=cch || cch<0) {
309 Debug.Assert((ODBC32.SQL_C.WCHAR == _bindtype._sql_c) || (ODBC32.SQL_C.BINARY == _bindtype._sql_c), "not wchar or binary");
310 if (!_internalShouldSerializeSize && (0 != (ParameterDirection.Output & _internalDirection))) {
311 throw ADP.UninitializedParameterSize(ordinal, _bindtype._type);
313 if ((null == value) || Convert.IsDBNull(value)) {
316 else if (value is String) {
317 cch = ((String)value).Length - offset;
319 if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) {
320 // restrict output parameters when user set Size to Int32.MaxValue
321 // to the greater of intput size or 8K
322 cch = Math.Max(cch, 4 * 1024); // MDAC 69224
326 // the following code causes failure against SQL 6.5
327 // ERROR [HY104] [Microsoft][ODBC SQL Server Driver]Invalid precision value
329 // the code causes failure if it is NOT there (remark added by Microsoft)
330 // it causes failure with jet if it is there
332 // MDAC 76227: Code is required for japanese client/server tests.
333 // If this causes regressions with Jet please doc here including
335 if ((ODBC32.SQL_TYPE.CHAR == _bindtype._sql_type)
336 || (ODBC32.SQL_TYPE.VARCHAR == _bindtype._sql_type)
337 || (ODBC32.SQL_TYPE.LONGVARCHAR == _bindtype._sql_type)) {
338 cch = System.Text.Encoding.Default.GetMaxByteCount(cch);
342 else if (value is char[]) {
343 cch = ((char[])value).Length - offset;
344 if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) {
345 cch = Math.Max(cch, 4 * 1024); // MDAC 69224
347 if ((ODBC32.SQL_TYPE.CHAR == _bindtype._sql_type)
348 || (ODBC32.SQL_TYPE.VARCHAR == _bindtype._sql_type)
349 || (ODBC32.SQL_TYPE.LONGVARCHAR == _bindtype._sql_type)) {
350 cch = System.Text.Encoding.Default.GetMaxByteCount(cch);
353 else if (value is byte[]) {
354 cch = ((byte[])value).Length - offset;
356 if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) {
357 // restrict output parameters when user set Size to Int32.MaxValue
358 // to the greater of intput size or 8K
359 cch = Math.Max(cch, 8 * 1024); // MDAC 69224
363 else { Debug.Assert(false, "not expecting this"); }
365 // Note: ColumnSize should never be 0,
366 // this represents the size of the column on the backend.
368 // without the following code causes failure
369 //ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]Invalid precision value
370 cch = Math.Max(2, cch);
374 Debug.Assert((0 <= cch) && (cch < 0x3fffffff), String.Format((IFormatProvider)null, "GetColumnSize: cch = {0} out of range, _internalShouldSerializeSize = {1}, _internalSize = {2}",cch, _internalShouldSerializeSize, _internalSize));
380 // Return the count of bytes for the data (size in bytes for the native buffer)
382 private int GetValueSize(object value, int offset) {
383 if ((ODBC32.SQL_C.NUMERIC == _bindtype._sql_c) && (0 != _internalPrecision)){
384 return Math.Min((int)_internalPrecision,ADP.DecimalMaxPrecision);
386 int cch = _bindtype._columnSize;
388 bool twobytesperunit = false;
389 if (value is String) {
390 cch = ((string)value).Length - offset;
391 twobytesperunit = true;
393 else if (value is char[]) {
394 cch = ((char[])value).Length - offset;
395 twobytesperunit = true;
397 else if (value is byte[]) {
398 cch = ((byte[])value).Length - offset;
403 if (_internalShouldSerializeSize && (_internalSize>=0) && (_internalSize<cch) && (_bindtype==_originalbindtype)) {
406 if (twobytesperunit) {
410 Debug.Assert((0 <= cch) && (cch < 0x3fffffff), String.Format((IFormatProvider)null, "GetValueSize: cch = {0} out of range, _internalShouldSerializeSize = {1}, _internalSize = {2}",cch, _internalShouldSerializeSize, _internalSize));
414 // return the count of bytes for the data, used for SQLBindParameter
416 private int GetParameterSize(object value, int offset, int ordinal) {
417 int ccb = _bindtype._bufferSize;
419 if (ODBC32.SQL_C.NUMERIC == _typemap._sql_c) {
420 ccb = 518; // _bindtype would be VarChar ([0-9]?{255} + '-' + '.') * 2
424 if (!_internalShouldSerializeSize || (0x3fffffff <= ccb)||(ccb < 0)) {
425 Debug.Assert((ODBC32.SQL_C.WCHAR == _bindtype._sql_c) || (ODBC32.SQL_C.BINARY == _bindtype._sql_c), "not wchar or binary");
426 if ((ccb <= 0) && (0 != (ParameterDirection.Output & _internalDirection))) {
427 throw ADP.UninitializedParameterSize(ordinal, _bindtype._type);
429 if ((null == value) || Convert.IsDBNull(value)) {
430 if (_bindtype._sql_c == ODBC32.SQL_C.WCHAR) {
431 ccb = 2; // allow for null termination
437 else if (value is String) {
438 ccb = (((String)value).Length - offset ) * 2 + 2;
440 else if (value is char[]) {
441 ccb = (((char[])value).Length - offset ) * 2 + 2;
443 else if (value is byte[]) {
444 ccb = ((byte[])value).Length - offset;
447 else { Debug.Assert(false, "not expecting this"); }
449 if ((0 != (ParameterDirection.Output & _internalDirection)) && (0x3fffffff <= _internalSize)) {
450 // restrict output parameters when user set Size to Int32.MaxValue
451 // to the greater of intput size or 8K
452 ccb = Math.Max(ccb, 8 * 1024); // MDAC 69224
455 else if (ODBC32.SQL_C.WCHAR == _bindtype._sql_c) {
456 if ((value is String) && (ccb < ((String)value).Length) && (_bindtype == _originalbindtype)) {
457 // silently truncate ... MDAC 84408 ... do not truncate upgraded values ... MDAC 84706
458 ccb = ((String)value).Length;
460 ccb = (ccb * 2) + 2; // allow for null termination
462 else if ((value is byte[]) && (ccb < ((byte[])value).Length) && (_bindtype == _originalbindtype)) {
463 // silently truncate ... MDAC 84408 ... do not truncate upgraded values ... MDAC 84706
464 ccb = ((byte[])value).Length;
468 Debug.Assert((0 <= ccb) && (ccb < 0x3fffffff), "GetParameterSize: out of range " + ccb);
472 private byte GetParameterPrecision(object value) {
473 if (0 != _internalPrecision && value is decimal) {
475 if (_internalPrecision<29) {
476 // from SqlClient ...
477 if (_internalPrecision != 0) {
478 // devnote: If the userspecified precision (_internalPrecision) is less than the actual values precision
479 // we silently adjust the userspecified precision to the values precision.
480 byte precision = ((SqlDecimal)(decimal)value).Precision;
481 _internalPrecision = Math.Max(_internalPrecision, precision); // silently adjust the precision
483 return _internalPrecision;
485 return ADP.DecimalMaxPrecision;
487 if ((null == value) || (value is Decimal) || Convert.IsDBNull(value)) { // MDAC 60882
488 return ADP.DecimalMaxPrecision28;
494 private byte GetParameterScale(object value) {
496 // For any value that is not decimal simply return the Scale
498 if (!(value is decimal)) {
499 return _internalScale;
502 // Determin the values scale
503 // If the user specified a lower scale we return the user specified scale,
504 // otherwise the values scale
506 byte s = (byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10);
507 if ((_internalScale > 0) && (_internalScale < s)){
508 return _internalScale;
513 //This is required for OdbcCommand.Clone to deep copy the parameters collection
514 object ICloneable.Clone() {
515 return new OdbcParameter(this);
518 private void CopyParameterInternal () {
519 _internalValue = Value;
520 // we should coerce the parameter value at this time.
521 _internalPrecision = ShouldSerializePrecision() ? PrecisionInternal : ValuePrecision(_internalValue);
522 _internalShouldSerializeSize = ShouldSerializeSize();
523 _internalSize = _internalShouldSerializeSize ? Size : ValueSize(_internalValue);
524 _internalDirection = Direction;
525 _internalScale = ShouldSerializeScale() ? ScaleInternal : ValueScale(_internalValue);
526 _internalOffset = Offset;
527 _internalUserSpecifiedType = UserSpecifiedType;
530 private void CloneHelper(OdbcParameter destination) {
531 CloneHelperCore(destination);
532 destination._userSpecifiedType = _userSpecifiedType;
533 destination._typemap = _typemap;
534 destination._parameterName = _parameterName;
535 destination._precision = _precision;
536 destination._scale = _scale;
537 destination._hasScale = _hasScale;
540 internal void ClearBinding() {
541 if (!_userSpecifiedType) {
547 internal void PrepareForBind(OdbcCommand command, short ordinal, ref int parameterBufferSize) {
548 // make a snapshot of the current properties. Properties may change while we work on them
550 CopyParameterInternal();
552 object value = ProcessAndGetParameterValue();
553 int offset = _internalOffset;
554 int size = _internalSize;
555 ODBC32.SQL_C sql_c_type;
558 // offset validation based on the values type
561 if (value is string) {
562 if (offset > ((string)value).Length) {
563 throw ADP.OffsetOutOfRangeException();
566 else if (value is char[]) {
567 if (offset > ((char[])value).Length) {
568 throw ADP.OffsetOutOfRangeException();
571 else if (value is byte[]) {
572 if (offset > ((byte[])value).Length) {
573 throw ADP.OffsetOutOfRangeException();
577 // for all other types offset has no meaning
578 // this is important since we might upgrade some types to strings
583 // type support verification for certain data types
585 switch(_bindtype._sql_type) {
586 case ODBC32.SQL_TYPE.DECIMAL:
587 case ODBC32.SQL_TYPE.NUMERIC:
589 !command.Connection.IsV3Driver // for non V3 driver we always do the conversion
590 || !command.Connection.TestTypeSupport(ODBC32.SQL_TYPE.NUMERIC) // otherwise we convert if the driver does not support numeric
591 || command.Connection.TestRestrictedSqlBindType(_bindtype._sql_type)// or the type is not supported
593 // No support for NUMERIC
595 _bindtype = TypeMap._VarChar;
596 if ((null != value) && !Convert.IsDBNull(value)) {
597 value = ((Decimal)value).ToString(CultureInfo.CurrentCulture);
598 size = ((string)value).Length;
603 case ODBC32.SQL_TYPE.BIGINT:
604 if (!command.Connection.IsV3Driver){
605 // No support for BIGINT
607 _bindtype = TypeMap._VarChar;
608 if ((null != value) && !Convert.IsDBNull(value)) {
609 value = ((Int64)value).ToString(CultureInfo.CurrentCulture);
610 size = ((string)value).Length;
615 case ODBC32.SQL_TYPE.WCHAR: // MDAC 68993
616 case ODBC32.SQL_TYPE.WVARCHAR:
617 case ODBC32.SQL_TYPE.WLONGVARCHAR:
619 value = value.ToString();
620 size = ((string)value).Length;
623 if (!command.Connection.TestTypeSupport (_bindtype._sql_type)) {
624 // No support for WCHAR, WVARCHAR or WLONGVARCHAR
626 if (ODBC32.SQL_TYPE.WCHAR == _bindtype._sql_type) { _bindtype = TypeMap._Char; }
627 else if (ODBC32.SQL_TYPE.WVARCHAR == _bindtype._sql_type) { _bindtype = TypeMap._VarChar; }
628 else if (ODBC32.SQL_TYPE.WLONGVARCHAR == _bindtype._sql_type) {
629 _bindtype = TypeMap._Text;
635 // Conversation from WCHAR to CHAR, VARCHAR or LONVARCHAR (AnsiString) is different for some providers
636 // we need to chonvert WCHAR to CHAR and bind as sql_c_type = CHAR
638 sql_c_type = _bindtype._sql_c;
640 if (!command.Connection.IsV3Driver) {
641 if (sql_c_type == ODBC32.SQL_C.WCHAR) {
642 sql_c_type = ODBC32.SQL_C.CHAR;
645 if (!Convert.IsDBNull(value) && value is string) {
646 int lcid = System.Globalization.CultureInfo.CurrentCulture.LCID;
647 CultureInfo culInfo = new CultureInfo(lcid);
648 Encoding cpe = System.Text.Encoding.GetEncoding(culInfo.TextInfo.ANSICodePage);
649 value = cpe.GetBytes(value.ToString());
650 size = ((byte[])value).Length;
656 int cbParameterSize = GetParameterSize(value, offset, ordinal); // count of bytes for the data, for SQLBindParameter
658 // here we upgrade the datatypes if the given values size is bigger than the types columnsize
660 switch(_bindtype._sql_type) {
661 case ODBC32.SQL_TYPE.VARBINARY: // MDAC 74372
662 // Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding
663 if ((cbParameterSize > 8000))
664 { _bindtype = TypeMap._Image; } // will change to LONGVARBINARY
666 case ODBC32.SQL_TYPE.VARCHAR: // MDAC 74372
667 // Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding
668 if ((cbParameterSize > 8000))
669 { _bindtype = TypeMap._Text; } // will change to LONGVARCHAR
671 case ODBC32.SQL_TYPE.WVARCHAR : // MDAC 75099
672 // Note: per definition DbType.Binary does not support more than 8000 bytes so we change the type for binding
673 if ((cbParameterSize > 4000))
674 { _bindtype = TypeMap._NText; } // will change to WLONGVARCHAR
678 _prepared_Sql_C_Type = sql_c_type;
679 _preparedOffset = offset;
680 _preparedSize = size;
681 _preparedValue = value;
682 _preparedBufferSize = cbParameterSize;
683 _preparedIntOffset = parameterBufferSize;
684 _preparedValueOffset = _preparedIntOffset + IntPtr.Size;
685 parameterBufferSize += (cbParameterSize + IntPtr.Size);
688 internal void Bind(OdbcStatementHandle hstmt, OdbcCommand command, short ordinal, CNativeBuffer parameterBuffer, bool allowReentrance) {
689 ODBC32.RetCode retcode;
690 ODBC32.SQL_C sql_c_type = _prepared_Sql_C_Type;
691 ODBC32.SQL_PARAM sqldirection = SqlDirectionFromParameterDirection();
693 int offset = _preparedOffset;
694 int size = _preparedSize;
695 object value = _preparedValue;
696 int cbValueSize = GetValueSize(value, offset); // count of bytes for the data
697 int cchSize = GetColumnSize(value, offset, ordinal); // count of bytes for the data, used to allocate the buffer length
698 byte precision = GetParameterPrecision(value);
699 byte scale = GetParameterScale(value);
702 HandleRef valueBuffer = parameterBuffer.PtrOffset(_preparedValueOffset, _preparedBufferSize);
703 HandleRef intBuffer = parameterBuffer.PtrOffset(_preparedIntOffset, IntPtr.Size);
705 // for the numeric datatype we need to do some special case handling ...
707 if (ODBC32.SQL_C.NUMERIC == sql_c_type) {
709 // for input/output parameters we need to adjust the scale of the input value since the convert function in
710 // sqlsrv32 takes this scale for the output parameter (possible
712 if ((ODBC32.SQL_PARAM.INPUT_OUTPUT == sqldirection) && (value is Decimal)) {
713 if (scale < _internalScale) {
714 while (scale < _internalScale) {
715 value = ((decimal)value ) * 10;
720 SetInputValue(value, sql_c_type, cbValueSize, precision, 0, parameterBuffer);
722 // for output parameters we need to write precision and scale to the buffer since the convert function in
723 // sqlsrv32 expects these values there (possible
725 if (ODBC32.SQL_PARAM.INPUT != sqldirection) {
726 parameterBuffer.WriteInt16(_preparedValueOffset, (short)(((ushort)scale << 8) | (ushort)precision));
730 SetInputValue(value, sql_c_type, cbValueSize, size, offset, parameterBuffer);
734 // Try to reuse existing bindings if
735 // the binding is valid (means we already went through binding all parameters)
736 // the parametercollection is bound already
737 // the bindtype ParameterType did not change (forced upgrade)
740 && (_boundSqlCType == sql_c_type)
741 && (_boundParameterType == _bindtype._sql_type)
742 && (_boundSize == cchSize)
743 && (_boundScale == scale)
744 && (_boundBuffer == valueBuffer.Handle)
745 && (_boundIntbuffer == intBuffer.Handle)
751 retcode = hstmt.BindParameter(
752 ordinal, // Parameter Number
753 (short)sqldirection, // InputOutputType
754 sql_c_type, // ValueType
755 _bindtype._sql_type, // ParameterType
756 (IntPtr)cchSize, // ColumnSize
757 (IntPtr)scale, // DecimalDigits
758 valueBuffer, // ParameterValuePtr
759 (IntPtr)_preparedBufferSize,
760 intBuffer); // StrLen_or_IndPtr
762 if (ODBC32.RetCode.SUCCESS != retcode) {
763 if ("07006" == command.GetDiagSqlState()) {
764 Bid.Trace("<odbc.OdbcParameter.Bind|ERR> Call to BindParameter returned errorcode [07006]\n");
765 command.Connection.FlagRestrictedSqlBindType(_bindtype._sql_type);
766 if (allowReentrance) {
767 this.Bind(hstmt, command, ordinal, parameterBuffer, false);
771 command.Connection.HandleError(hstmt, retcode);
774 _boundSqlCType = sql_c_type;
775 _boundParameterType = _bindtype._sql_type;
776 _boundSize = cchSize;
778 _boundBuffer = valueBuffer.Handle;
779 _boundIntbuffer = intBuffer.Handle;
781 if (ODBC32.SQL_C.NUMERIC == sql_c_type) {
782 OdbcDescriptorHandle hdesc = command.GetDescriptorHandle(ODBC32.SQL_ATTR.APP_PARAM_DESC);
783 // descriptor handle is cached on command wrapper, don't release it
785 // Set descriptor Type
787 //SQLSetDescField(hdesc, i+1, SQL_DESC_TYPE, (void *)SQL_C_NUMERIC, 0);
788 retcode = hdesc.SetDescriptionField1(ordinal, ODBC32.SQL_DESC.TYPE, (IntPtr)ODBC32.SQL_C.NUMERIC);
790 if (ODBC32.RetCode.SUCCESS != retcode) {
791 command.Connection.HandleError(hstmt, retcode);
797 cbActual= (int)precision;
798 //SQLSetDescField(hdesc, i+1, SQL_DESC_PRECISION, (void *)precision, 0);
799 retcode = hdesc.SetDescriptionField1(ordinal, ODBC32.SQL_DESC.PRECISION, (IntPtr)cbActual);
801 if (ODBC32.RetCode.SUCCESS != retcode) {
802 command.Connection.HandleError(hstmt, retcode);
808 // SQLSetDescField(hdesc, i+1, SQL_DESC_SCALE, (void *)llen, 0);
809 cbActual= (int)scale;
810 retcode = hdesc.SetDescriptionField1(ordinal, ODBC32.SQL_DESC.SCALE, (IntPtr)cbActual);
812 if (ODBC32.RetCode.SUCCESS != retcode) {
813 command.Connection.HandleError(hstmt, retcode);
818 // SQLSetDescField(hdesc, i+1, SQL_DESC_DATA_PTR, (void *)&numeric, 0);
819 retcode = hdesc.SetDescriptionField2(ordinal, ODBC32.SQL_DESC.DATA_PTR, valueBuffer);
821 if (ODBC32.RetCode.SUCCESS != retcode) {
822 command.Connection.HandleError(hstmt, retcode);
827 internal void GetOutputValue(CNativeBuffer parameterBuffer) { //Handle any output params
829 // No value is available if the user fiddles with the parameters properties
831 if (_hasChanged) return;
833 if ((null != _bindtype) && (_internalDirection != ParameterDirection.Input)) {
835 TypeMap typemap = _bindtype;
838 int cbActual = (int)parameterBuffer.ReadIntPtr(_preparedIntOffset);
839 if (ODBC32.SQL_NULL_DATA == cbActual) {
840 Value = DBNull.Value;
842 else if ((0 <= cbActual) || (cbActual == ODBC32.SQL_NTS)){ // safeguard
843 Value = parameterBuffer.MarshalToManaged(_preparedValueOffset, _boundSqlCType, cbActual);
845 if (_boundSqlCType== ODBC32.SQL_C.CHAR) {
846 if ((null != Value) && !Convert.IsDBNull(Value)) {
847 int lcid = System.Globalization.CultureInfo.CurrentCulture.LCID;
848 CultureInfo culInfo = new CultureInfo(lcid);
849 Encoding cpe = System.Text.Encoding.GetEncoding(culInfo.TextInfo.ANSICodePage);
850 Value = cpe.GetString((Byte[])Value);
854 if ((typemap != _typemap) && (null != Value) && !Convert.IsDBNull(Value) && (Value.GetType() != _typemap._type)) {
855 Debug.Assert(ODBC32.SQL_C.NUMERIC == _typemap._sql_c, "unexpected");
856 Value = Decimal.Parse((string)Value, System.Globalization.CultureInfo.CurrentCulture);
862 private object ProcessAndGetParameterValue() {
863 object value = _internalValue;
864 if (_internalUserSpecifiedType) {
865 if ((null != value) && !Convert.IsDBNull(value)) {
866 Type valueType = value.GetType();
867 if (!valueType.IsArray) {
868 if (valueType != _typemap._type) {
870 value = Convert.ChangeType (value, _typemap._type, (System.IFormatProvider)null);
873 // Don't know which exception to expect from ChangeType so we filter out the serious ones
875 if (!ADP.IsCatchableExceptionType(e)) {
878 throw ADP.ParameterConversionFailed(value, _typemap._type, e); // WebData 75433
882 else if (valueType == typeof(char[])) {
883 value = new String((char[])value);
887 else if (null == _typemap) {
888 if ((null == value) || Convert.IsDBNull (value)) {
889 _typemap = TypeMap._NVarChar; // default type
892 Type type = value.GetType ();
894 _typemap = TypeMap.FromSystemType (type);
897 Debug.Assert(null != _typemap, "GetParameterValue: null _typemap");
898 _originalbindtype = _bindtype = _typemap;
902 private void PropertyChanging() {
906 private void PropertyTypeChanging() {
908 //CoercedValue = null;
911 internal void SetInputValue(object value, ODBC32.SQL_C sql_c_type, int cbsize, int sizeorprecision, int offset, CNativeBuffer parameterBuffer) { //Handle any input params
912 if((ParameterDirection.Input == _internalDirection) || (ParameterDirection.InputOutput == _internalDirection)) {
913 //Note: (lang) "null" means to use the servers default (not DBNull).
914 //We probably should just not have bound this parameter, period, but that
915 //would mess up the users question marks, etc...
916 if((null == value)) {
917 parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)ODBC32.SQL_DEFAULT_PARAM);
919 else if(Convert.IsDBNull(value)) {
920 parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)ODBC32.SQL_NULL_DATA);
924 case ODBC32.SQL_C.CHAR:
925 case ODBC32.SQL_C.WCHAR:
926 case ODBC32.SQL_C.BINARY:
927 //StrLen_or_IndPtr is ignored except for Character or Binary or data.
928 parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)cbsize);
931 parameterBuffer.WriteIntPtr(_preparedIntOffset, IntPtr.Zero);
936 //Place the input param value into the native buffer
937 parameterBuffer.MarshalToNative(_preparedValueOffset, value, sql_c_type, sizeorprecision, offset);
941 // always set ouput only and return value parameter values to null when executing
942 _internalValue = null;
944 //Always initialize the intbuffer (for output params). Since we need to know
945 //if/when the parameters are available for output. (ie: when is the buffer valid...)
946 //if (_sqldirection != ODBC32.SQL_PARAM.INPUT)
947 parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)ODBC32.SQL_NULL_DATA);
951 private ODBC32.SQL_PARAM SqlDirectionFromParameterDirection () {
952 switch(_internalDirection) {
953 case ParameterDirection.Input:
954 return ODBC32.SQL_PARAM.INPUT;
955 case ParameterDirection.Output:
956 case ParameterDirection.ReturnValue:
957 //ODBC doesn't seem to distinguish between output and return value
958 //as SQL_PARAM_RETURN_VALUE fails with "Invalid parameter type"
959 return ODBC32.SQL_PARAM.OUTPUT;
960 case ParameterDirection.InputOutput:
961 return ODBC32.SQL_PARAM.INPUT_OUTPUT;
963 Debug.Assert (false, "Unexpected Direction Property on Parameter");
964 return ODBC32.SQL_PARAM.INPUT;
969 RefreshProperties(RefreshProperties.All),
970 ResCategoryAttribute(Res.DataCategory_Data),
971 ResDescriptionAttribute(Res.DbParameter_Value),
972 TypeConverterAttribute(typeof(StringConverter)),
974 override public object Value { // V1.2.3300, XXXParameter V1.0.3300
979 _coercedValue = null;
984 private byte ValuePrecision(object value) {
985 return ValuePrecisionCore(value);
988 private byte ValueScale(object value) {
989 return ValueScaleCore(value);
992 private int ValueSize(object value) {
993 return ValueSizeCore(value);
996 // implemented as nested class to take advantage of the private/protected ShouldSerializeXXX methods
997 sealed internal class OdbcParameterConverter : ExpandableObjectConverter {
999 // converter classes should have public ctor
1000 public OdbcParameterConverter() {
1003 public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
1004 if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor)) {
1007 return base.CanConvertTo(context, destinationType);
1010 public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
1011 if (destinationType == null) {
1012 throw ADP.ArgumentNull("destinationType");
1015 if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) && value is OdbcParameter) {
1016 OdbcParameter p = (OdbcParameter)value;
1018 // MDAC 67321 - reducing parameter generated code
1019 int flags = 0; // if part of the collection - the parametername can't be empty
1021 if (OdbcType.NChar != p.OdbcType) {
1024 if (p.ShouldSerializeSize()) {
1027 if (!ADP.IsEmpty(p.SourceColumn)) {
1030 if (null != p.Value) {
1033 if ((ParameterDirection.Input != p.Direction) || p.IsNullable
1034 || p.ShouldSerializePrecision() || p.ShouldSerializeScale()
1035 || (DataRowVersion.Current != p.SourceVersion)) {
1036 flags |= 16; // V1.0 everything
1038 if (p.SourceColumnNullMapping) {
1039 flags |= 32; // v2.0 everything
1043 object[] ctorValues;
1045 case 0: // ParameterName
1046 case 1: // SqlDbType
1047 ctorParams = new Type[] { typeof(string), typeof(OdbcType) };
1048 ctorValues = new object[] { p.ParameterName, p.OdbcType };
1051 case 3: // Size, SqlDbType
1052 ctorParams = new Type[] { typeof(string), typeof(OdbcType), typeof(int) };
1053 ctorValues = new object[] { p.ParameterName, p.OdbcType, p.Size };
1055 case 4: // SourceColumn
1056 case 5: // SourceColumn, SqlDbType
1057 case 6: // SourceColumn, Size
1058 case 7: // SourceColumn, Size, SqlDbType
1059 ctorParams = new Type[] { typeof(string), typeof(OdbcType), typeof(int), typeof(string) };
1060 ctorValues = new object[] { p.ParameterName, p.OdbcType, p.Size, p.SourceColumn };
1063 ctorParams = new Type[] { typeof(string), typeof(object) };
1064 ctorValues = new object[] { p.ParameterName, p.Value };
1067 if (0 == (32 & flags)) { // V1.0 everything
1068 ctorParams = new Type[] {
1069 typeof(string), typeof(OdbcType), typeof(int), typeof(ParameterDirection),
1070 typeof(bool), typeof(byte), typeof(byte), typeof(string),
1071 typeof(DataRowVersion), typeof(object) };
1072 ctorValues = new object[] {
1073 p.ParameterName, p.OdbcType, p.Size, p.Direction,
1074 p.IsNullable, p.PrecisionInternal, p.ScaleInternal, p.SourceColumn,
1075 p.SourceVersion, p.Value };
1077 else { // v2.0 everything - round trip all browsable properties + precision/scale
1078 ctorParams = new Type[] {
1079 typeof(string), typeof(OdbcType), typeof(int), typeof(ParameterDirection),
1080 typeof(byte), typeof(byte),
1081 typeof(string), typeof(DataRowVersion), typeof(bool),
1083 ctorValues = new object[] {
1084 p.ParameterName, p.OdbcType, p.Size, p.Direction,
1085 p.PrecisionInternal, p.ScaleInternal,
1086 p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping,
1091 System.Reflection.ConstructorInfo ctor = typeof(OdbcParameter).GetConstructor(ctorParams);
1093 return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ctor, ctorValues);
1096 return base.ConvertTo(context, culture, value, destinationType);