Update Reference Sources to .NET Framework 4.6
[mono.git] / mcs / class / referencesource / System.Data / System / Data / Odbc / OdbcParameter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="OdbcParameter.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9
10 using System;
11 using System.ComponentModel;
12 using System.Data;
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;
19 using System.Text;
20 using System.Threading;
21
22 namespace System.Data.Odbc {
23
24     [
25     TypeConverterAttribute(typeof(System.Data.Odbc.OdbcParameter.OdbcParameterConverter))
26     ]
27     public sealed partial class OdbcParameter : DbParameter, ICloneable, IDbDataParameter {
28
29         private bool _hasChanged;
30         private bool _userSpecifiedType;
31
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
36         //
37         // set_DbType:      _bindtype = _infertype = _typemap = TypeMap.FromDbType(value)
38         // set_OdbcType:    _bindtype = _infertype = _typemap = TypeMap.FromOdbcType(value)
39         //
40         // GetParameterType:    If _typemap != _infertype AND value != 0
41         //                      _bindtype = _infertype = TypeMap.FromSystemType(value.GetType());
42         //                      otherwise
43         //                      _bindtype = _infertype
44         //
45         // Bind:            Bind may change _bindtype if the type is not supported through the driver
46         //
47
48         private TypeMap _typemap;
49         private TypeMap _bindtype;
50
51         private string _parameterName;
52         private byte _precision;
53         private byte _scale;
54         private bool _hasScale;
55
56
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;
73
74         private int             _preparedOffset;
75         private int             _preparedSize;
76         private int             _preparedBufferSize;
77         private object          _preparedValue;
78         private int             _preparedIntOffset;
79         private int             _preparedValueOffset;
80
81         private ODBC32.SQL_C    _prepared_Sql_C_Type;
82
83         public OdbcParameter() : base() {
84             // uses System.Threading!
85         }
86
87         public OdbcParameter(string name, object value) : this() {
88             ParameterName  = name;
89             Value          = value;
90         }
91
92         public OdbcParameter(string name, OdbcType type) : this() {
93             ParameterName  = name;
94             OdbcType       = type;
95         }
96
97         public OdbcParameter(string name, OdbcType type, int size) : this() {
98             ParameterName  = name;
99             OdbcType       = type;
100             Size           = size;
101         }
102
103         public OdbcParameter(string name, OdbcType type, int size, string sourcecolumn) : this() {
104             ParameterName  = name;
105             OdbcType       = type;
106             Size           = size;
107             SourceColumn   = sourcecolumn;
108         }
109
110         
111         [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
112         public OdbcParameter(string parameterName,
113                              OdbcType odbcType,
114                              int size,
115                              ParameterDirection parameterDirection,
116                              Boolean isNullable,
117                              Byte precision,
118                              Byte scale,
119                              string srcColumn,
120                              DataRowVersion srcVersion,
121                              object value
122                              ) : this() { // V1.0 everything
123             this.ParameterName = parameterName;
124             this.OdbcType = odbcType;
125             this.Size = size;
126             this.Direction = parameterDirection;
127             this.IsNullable = isNullable;
128             PrecisionInternal = precision;
129             ScaleInternal = scale;
130             this.SourceColumn = srcColumn;
131             this.SourceVersion = srcVersion;
132             this.Value = value;
133         }
134
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;
144             this.Size = size;
145             this.Direction = parameterDirection;
146             this.PrecisionInternal = precision;
147             this.ScaleInternal = scale;
148             this.SourceColumn = sourceColumn;
149             this.SourceVersion = sourceVersion;
150             this.SourceColumnNullMapping = sourceColumnNullMapping;
151             this.Value = value;
152         }
153
154         override public System.Data.DbType DbType {
155             get {
156                 if (_userSpecifiedType) {
157                     return _typemap._dbType;
158                 }
159                 return TypeMap._NVarChar._dbType; // default type
160             }
161             set {
162                 if ((null == _typemap) || (_typemap._dbType != value)) {
163                     PropertyTypeChanging();
164                     _typemap = TypeMap.FromDbType(value);
165                     _userSpecifiedType = true;
166                 }
167             }
168         }
169
170         public override void ResetDbType() {
171             ResetOdbcType();
172         }
173
174         [
175         DefaultValue(OdbcType.NChar),
176         RefreshProperties(RefreshProperties.All),
177         ResCategoryAttribute(Res.DataCategory_Data),
178         ResDescriptionAttribute(Res.OdbcParameter_OdbcType),
179         System.Data.Common.DbProviderSpecificTypePropertyAttribute(true),
180         ]
181         public OdbcType OdbcType {
182             get {
183                 if (_userSpecifiedType) {
184                     return _typemap._odbcType;
185                 }
186                 return TypeMap._NVarChar._odbcType; // default type
187             }
188             set {
189                 if ((null == _typemap) || (_typemap._odbcType != value)) {
190                     PropertyTypeChanging();
191                     _typemap = TypeMap.FromOdbcType(value);
192                     _userSpecifiedType = true;
193                 }
194             }
195         }
196
197         public void ResetOdbcType() {
198             PropertyTypeChanging();
199             _typemap = null;
200             _userSpecifiedType = false;
201         }
202
203         internal bool HasChanged {
204             set {
205                 _hasChanged = value;
206             }
207         }
208
209         internal bool UserSpecifiedType {
210             get {
211                 return _userSpecifiedType;
212             }
213         }
214
215         [
216         ResCategoryAttribute(Res.DataCategory_Data),
217         ResDescriptionAttribute(Res.DbParameter_ParameterName),
218         ]
219         override public string ParameterName { // V1.2.3300, XXXParameter V1.0.3300
220             get {
221                 string parameterName = _parameterName;
222                 return ((null != parameterName) ? parameterName : ADP.StrEmpty);
223             }
224             set {
225                 if (_parameterName != value) {
226                     PropertyChanging();
227                     _parameterName = value;
228                 }
229             }
230         }
231
232         [DefaultValue((Byte)0)] // MDAC 65862
233         [ResCategoryAttribute(Res.DataCategory_Data)]
234         [ResDescriptionAttribute(Res.DbDataParameter_Precision)]
235         public new Byte Precision {
236             get {
237                 return PrecisionInternal;
238             }
239             set {
240                 PrecisionInternal = value;
241             }
242         }
243         internal byte PrecisionInternal {
244             get {
245                 byte precision = _precision;
246                 if (0 == precision) {
247                     precision = ValuePrecision(Value);
248                 }
249                 return precision;
250             }
251             set {
252                 if (_precision != value) {
253                     PropertyChanging();
254                     _precision = value;
255                 }
256             }
257         }
258         private bool ShouldSerializePrecision() {
259             return (0 != _precision);
260         }
261
262         [DefaultValue((Byte)0)] // MDAC 65862
263         [ResCategoryAttribute(Res.DataCategory_Data)]
264         [ResDescriptionAttribute(Res.DbDataParameter_Scale)]
265         public new Byte Scale {
266             get {
267                 return ScaleInternal;
268             }
269             set {
270                 ScaleInternal = value;
271             }
272         }
273         internal byte ScaleInternal {
274             get {
275                 byte scale = _scale;
276                 if (!ShouldSerializeScale(scale)) { // WebData 94688
277                     scale = ValueScale(Value);
278                 }
279                 return scale;
280             }
281             set {
282                 if (_scale != value || !_hasScale) {
283                     PropertyChanging();
284                     _scale = value;
285                     _hasScale = true;
286                 }
287             }
288         }
289         private bool ShouldSerializeScale() {
290             return ShouldSerializeScale(_scale);
291         }
292         private bool ShouldSerializeScale(byte scale) {
293             return _hasScale && ((0 != scale) ||  ShouldSerializePrecision());
294         }
295
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);
300             }
301             int cch = _bindtype._columnSize;
302             if (0 >= cch) {
303                 if (ODBC32.SQL_C.NUMERIC == _typemap._sql_c) {
304                     cch = 62;  // (DecimalMaxPrecision+sign+terminator)*BytesPerUnicodeCharater
305                 }
306                 else {
307                     cch = _internalSize;
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);
312                         }
313                         if ((null == value) || Convert.IsDBNull(value)) {
314                             cch = 0;
315                         }
316                         else if (value is String) {
317                             cch = ((String)value).Length - offset;
318
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
323                             }
324
325
326                             // the following code causes failure against SQL 6.5
327                             // ERROR [HY104] [Microsoft][ODBC SQL Server Driver]Invalid precision value
328                             //
329                             // the code causes failure if it is NOT there (remark added by Microsoft)
330                             // it causes failure with jet if it is there
331                             //
332                             // MDAC 76227: Code is required for japanese client/server tests.
333                             // If this causes regressions with Jet please doc here including 
334
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);
339                             }
340
341                         }
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
346                             }
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);
351                             }
352                         }
353                         else if (value is byte[]) {
354                             cch = ((byte[])value).Length - offset;
355
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
360                             }
361                         }
362 #if DEBUG
363                         else { Debug.Assert(false, "not expecting this"); }
364 #endif
365                         // Note: ColumnSize should never be 0,
366                         // this represents the size of the column on the backend.
367                         //
368                         // without the following code causes failure
369                         //ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]Invalid precision value
370                         cch = Math.Max(2, cch);
371                        }
372                 }
373             }
374             Debug.Assert((0 <= cch) && (cch < 0x3fffffff), String.Format((IFormatProvider)null, "GetColumnSize: cch = {0} out of range, _internalShouldSerializeSize = {1}, _internalSize = {2}",cch, _internalShouldSerializeSize, _internalSize));
375             return cch;
376         }
377
378
379
380         // Return the count of bytes for the data (size in bytes for the native buffer)
381         //
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);
385             }
386             int cch = _bindtype._columnSize;
387             if (0 >= cch) {
388                 bool twobytesperunit = false;
389                 if (value is String) {
390                     cch = ((string)value).Length - offset;
391                     twobytesperunit = true;
392                 }
393                 else if (value is char[]) {
394                     cch = ((char[])value).Length - offset;
395                     twobytesperunit = true;
396                 }
397                 else if (value is byte[]) {
398                     cch = ((byte[])value).Length - offset;
399                 }
400                 else {
401                     cch = 0;
402                 }
403                 if (_internalShouldSerializeSize && (_internalSize>=0) && (_internalSize<cch) && (_bindtype==_originalbindtype)) {
404                     cch = _internalSize;
405                 }
406                 if (twobytesperunit) {
407                     cch *= 2;
408                 }
409             }
410             Debug.Assert((0 <= cch) && (cch < 0x3fffffff), String.Format((IFormatProvider)null, "GetValueSize: cch = {0} out of range, _internalShouldSerializeSize = {1}, _internalSize = {2}",cch, _internalShouldSerializeSize, _internalSize));
411             return cch;
412         }
413
414         // return the count of bytes for the data, used for SQLBindParameter
415         //
416         private int GetParameterSize(object value, int offset, int ordinal) {
417             int ccb = _bindtype._bufferSize;
418             if (0 >= ccb) {
419                 if (ODBC32.SQL_C.NUMERIC == _typemap._sql_c) {
420                     ccb = 518; // _bindtype would be VarChar ([0-9]?{255} + '-' + '.') * 2
421                 }
422                 else {
423                     ccb = _internalSize;
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);
428                         }
429                         if ((null == value) || Convert.IsDBNull(value)) {
430                             if (_bindtype._sql_c == ODBC32.SQL_C.WCHAR) {
431                                 ccb = 2; // allow for null termination
432                             }
433                             else {
434                                 ccb = 0;
435                             }
436                         }
437                         else if (value is String) {
438                             ccb = (((String)value).Length - offset ) * 2 + 2;
439                         }
440                         else if (value is char[]) {
441                             ccb = (((char[])value).Length - offset ) * 2 + 2;
442                         }
443                         else if (value is byte[]) {
444                             ccb = ((byte[])value).Length - offset;
445                         }
446 #if DEBUG
447                         else { Debug.Assert(false, "not expecting this"); }
448 #endif
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
453                         }
454                     }
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;
459                         }
460                         ccb = (ccb * 2) + 2; // allow for null termination
461                     }
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;
465                     }
466                 }
467             }
468             Debug.Assert((0 <= ccb) && (ccb < 0x3fffffff), "GetParameterSize: out of range " + ccb);
469             return ccb;
470          }
471
472          private byte GetParameterPrecision(object value) {
473             if (0 != _internalPrecision && value is decimal) {
474                 // from qfe 762
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
482                     }
483                     return _internalPrecision;
484                 }
485                 return ADP.DecimalMaxPrecision;
486             }
487             if ((null == value) || (value is Decimal) || Convert.IsDBNull(value)) { // MDAC 60882
488                 return ADP.DecimalMaxPrecision28;
489             }
490             return 0;
491         }
492
493
494         private byte GetParameterScale(object value) {
495
496             // For any value that is not decimal simply return the Scale
497             //
498             if (!(value is decimal)) {
499                 return _internalScale;
500             }
501
502             // Determin the values scale
503             // If the user specified a lower scale we return the user specified scale,
504             // otherwise the values scale
505             //
506             byte s = (byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10);
507             if ((_internalScale > 0) && (_internalScale < s)){
508                 return _internalScale;
509             }
510             return s;
511         }
512
513         //This is required for OdbcCommand.Clone to deep copy the parameters collection
514         object ICloneable.Clone() {
515             return new OdbcParameter(this);
516         }
517
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;
528         }
529
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;
538         }
539
540         internal void ClearBinding() {
541             if (!_userSpecifiedType) {
542                 _typemap = null;
543             }
544             _bindtype = null;
545         }
546
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
549             //
550             CopyParameterInternal();
551
552             object value  = ProcessAndGetParameterValue();
553             int    offset = _internalOffset;
554             int    size   = _internalSize;
555             ODBC32.SQL_C sql_c_type;
556
557
558             // offset validation based on the values type
559             //
560             if (offset > 0) {
561                 if (value is string) {
562                     if (offset > ((string)value).Length) {
563                         throw ADP.OffsetOutOfRangeException();
564                     }
565                 }
566                 else if (value is char[]) {
567                     if (offset > ((char[])value).Length) {
568                         throw ADP.OffsetOutOfRangeException();
569                     }
570                 }
571                 else if (value is byte[]) {
572                     if (offset > ((byte[])value).Length) {
573                         throw ADP.OffsetOutOfRangeException();
574                     }
575                 }
576                 else {
577                     // for all other types offset has no meaning
578                     // this is important since we might upgrade some types to strings
579                     offset = 0;
580                 }
581             }
582
583             // type support verification for certain data types
584             //
585             switch(_bindtype._sql_type) {
586                 case ODBC32.SQL_TYPE.DECIMAL:
587                 case ODBC32.SQL_TYPE.NUMERIC:
588                     if (
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
592                     ){
593                         // No support for NUMERIC
594                         // Change the type
595                         _bindtype = TypeMap._VarChar;
596                         if ((null != value) && !Convert.IsDBNull(value)) {
597                             value = ((Decimal)value).ToString(CultureInfo.CurrentCulture);
598                             size = ((string)value).Length;
599                             offset = 0;
600                         }
601                     }
602                     break;
603                 case ODBC32.SQL_TYPE.BIGINT:
604                     if (!command.Connection.IsV3Driver){
605                         // No support for BIGINT
606                         // Change the type
607                         _bindtype = TypeMap._VarChar;
608                         if ((null != value) && !Convert.IsDBNull(value)) {
609                             value = ((Int64)value).ToString(CultureInfo.CurrentCulture);
610                             size = ((string)value).Length;
611                             offset = 0;
612                         }
613                     }
614                     break;
615                 case ODBC32.SQL_TYPE.WCHAR: // MDAC 68993
616                 case ODBC32.SQL_TYPE.WVARCHAR:
617                 case ODBC32.SQL_TYPE.WLONGVARCHAR:
618                     if (value is Char) {
619                         value = value.ToString();
620                         size = ((string)value).Length;
621                         offset = 0;
622                     }
623                     if (!command.Connection.TestTypeSupport (_bindtype._sql_type)) {
624                         // No support for WCHAR, WVARCHAR or WLONGVARCHAR
625                         // Change the type
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;
630                         }
631                     }
632                     break;
633             } // end switch
634
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
637             //
638             sql_c_type = _bindtype._sql_c;
639
640             if (!command.Connection.IsV3Driver) {
641                   if (sql_c_type == ODBC32.SQL_C.WCHAR) {
642                     sql_c_type = ODBC32.SQL_C.CHAR;
643
644                     if (null != value){
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;
651                         }
652                     }
653                 }
654             };
655
656             int cbParameterSize = GetParameterSize(value, offset, ordinal);      // count of bytes for the data, for SQLBindParameter
657
658             // here we upgrade the datatypes if the given values size is bigger than the types columnsize
659             //
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
665                     break;
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
670                     break;
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
675                     break;
676             }
677
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);
686         }
687
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();
692
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);
700             int cbActual;
701
702             HandleRef valueBuffer  = parameterBuffer.PtrOffset(_preparedValueOffset, _preparedBufferSize);
703             HandleRef intBuffer    = parameterBuffer.PtrOffset(_preparedIntOffset, IntPtr.Size);
704
705             // for the numeric datatype we need to do some special case handling ...
706             //
707             if (ODBC32.SQL_C.NUMERIC == sql_c_type) {
708
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 
711
712                 if ((ODBC32.SQL_PARAM.INPUT_OUTPUT == sqldirection) && (value is Decimal)) {
713                     if (scale < _internalScale) {
714                         while (scale < _internalScale) {
715                             value = ((decimal)value ) * 10;
716                             scale++;
717                         }
718                     }
719                 }
720                 SetInputValue(value, sql_c_type, cbValueSize, precision, 0, parameterBuffer);
721
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 
724
725                 if (ODBC32.SQL_PARAM.INPUT != sqldirection) {
726                     parameterBuffer.WriteInt16(_preparedValueOffset, (short)(((ushort)scale << 8) | (ushort)precision));
727                 }
728             }
729             else {
730                 SetInputValue(value, sql_c_type, cbValueSize, size, offset, parameterBuffer);
731             }
732
733
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)
738
739             if (!_hasChanged
740                 && (_boundSqlCType == sql_c_type)
741                 && (_boundParameterType == _bindtype._sql_type)
742                 && (_boundSize == cchSize)
743                 && (_boundScale == scale)
744                 && (_boundBuffer == valueBuffer.Handle)
745                 && (_boundIntbuffer == intBuffer.Handle)
746             ) {
747                 return;
748             }
749
750             //SQLBindParameter
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
761
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);
768                         return;
769                     }
770                 }
771                 command.Connection.HandleError(hstmt, retcode);
772             }
773             _hasChanged = false;
774             _boundSqlCType = sql_c_type;
775             _boundParameterType = _bindtype._sql_type;
776             _boundSize = cchSize;
777             _boundScale = scale;
778             _boundBuffer = valueBuffer.Handle;
779             _boundIntbuffer = intBuffer.Handle;
780
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
784
785                 // Set descriptor Type
786                 //
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);
789
790                 if (ODBC32.RetCode.SUCCESS != retcode) {
791                     command.Connection.HandleError(hstmt, retcode);
792                 }
793
794
795                 // Set precision
796                 //
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);
800
801                 if (ODBC32.RetCode.SUCCESS != retcode) {
802                     command.Connection.HandleError(hstmt, retcode);
803                 }
804
805
806                 // Set scale
807                 //
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);
811
812                 if (ODBC32.RetCode.SUCCESS != retcode) {
813                     command.Connection.HandleError(hstmt, retcode);
814                 }
815
816                 // Set data pointer
817                 //
818                 // SQLSetDescField(hdesc, i+1, SQL_DESC_DATA_PTR,  (void *)&numeric, 0);
819                 retcode = hdesc.SetDescriptionField2(ordinal, ODBC32.SQL_DESC.DATA_PTR, valueBuffer);
820
821                 if (ODBC32.RetCode.SUCCESS != retcode) {
822                     command.Connection.HandleError(hstmt, retcode);
823                 }
824             }
825         }
826
827         internal void GetOutputValue(CNativeBuffer parameterBuffer) { //Handle any output params
828
829             // No value is available if the user fiddles with the parameters properties
830             //
831             if (_hasChanged) return;
832
833             if ((null != _bindtype) && (_internalDirection != ParameterDirection.Input)) {
834
835                TypeMap typemap = _bindtype;
836                 _bindtype = null;
837
838                 int cbActual = (int)parameterBuffer.ReadIntPtr(_preparedIntOffset);
839                 if (ODBC32.SQL_NULL_DATA == cbActual) {
840                     Value = DBNull.Value;
841                 }
842                 else if ((0 <= cbActual)  || (cbActual == ODBC32.SQL_NTS)){ // safeguard
843                     Value = parameterBuffer.MarshalToManaged(_preparedValueOffset, _boundSqlCType, cbActual);
844
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);
851                     }
852                 }
853
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);
857                     }
858                 }
859             }
860         }
861
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) {
869                             try {
870                                 value = Convert.ChangeType (value, _typemap._type, (System.IFormatProvider)null);
871                             }
872                             catch(Exception e) {
873                                 // Don't know which exception to expect from ChangeType so we filter out the serious ones
874                                 // 
875                                 if (!ADP.IsCatchableExceptionType(e)) {
876                                     throw;
877                                 }
878                                 throw ADP.ParameterConversionFailed(value, _typemap._type, e); // WebData 75433
879                             }
880                         }
881                     }
882                     else if (valueType == typeof(char[])) {
883                         value = new String((char[])value);
884                     }
885                 }
886             }
887             else if (null == _typemap) {
888                 if ((null == value) || Convert.IsDBNull (value)) {
889                     _typemap = TypeMap._NVarChar; // default type
890                 }
891                 else {
892                     Type type = value.GetType ();
893
894                     _typemap = TypeMap.FromSystemType (type);
895                 }
896             }
897             Debug.Assert(null != _typemap, "GetParameterValue: null _typemap");
898             _originalbindtype = _bindtype = _typemap;
899             return value;
900         }
901
902         private void PropertyChanging() {
903             _hasChanged = true;
904         }
905
906         private void PropertyTypeChanging() {
907             PropertyChanging();
908             //CoercedValue = null;
909         }
910
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);
918                 }
919                 else if(Convert.IsDBNull(value)) {
920                     parameterBuffer.WriteIntPtr(_preparedIntOffset, (IntPtr)ODBC32.SQL_NULL_DATA);
921                 }
922                 else {
923                     switch(sql_c_type) {
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);
929                         break;
930                     default:
931                         parameterBuffer.WriteIntPtr(_preparedIntOffset, IntPtr.Zero);
932                         break;
933
934                     }
935
936                     //Place the input param value into the native buffer
937                     parameterBuffer.MarshalToNative(_preparedValueOffset, value, sql_c_type, sizeorprecision, offset);
938                 }
939             }
940             else {
941                 // always set ouput only and return value parameter values to null when executing
942                 _internalValue = null;
943
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);
948             }
949         }
950
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;
962                 default:
963                     Debug.Assert (false, "Unexpected Direction Property on Parameter");
964                     return ODBC32.SQL_PARAM.INPUT;
965             }
966         }
967
968         [
969         RefreshProperties(RefreshProperties.All),
970         ResCategoryAttribute(Res.DataCategory_Data),
971         ResDescriptionAttribute(Res.DbParameter_Value),
972         TypeConverterAttribute(typeof(StringConverter)),
973         ]
974         override public object Value { // V1.2.3300, XXXParameter V1.0.3300
975             get {
976                 return _value;
977             }
978             set {
979                 _coercedValue = null;
980                 _value = value;
981             }
982         }
983
984         private byte ValuePrecision(object value) {
985             return ValuePrecisionCore(value);
986         }
987
988         private byte ValueScale(object value) {
989             return ValueScaleCore(value);
990         }
991
992         private int ValueSize(object value) {
993             return ValueSizeCore(value);
994         }
995
996         // implemented as nested class to take advantage of the private/protected ShouldSerializeXXX methods
997         sealed internal class OdbcParameterConverter : ExpandableObjectConverter {
998
999             // converter classes should have public ctor
1000             public OdbcParameterConverter() {
1001             }
1002
1003             public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
1004                 if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor)) {
1005                     return true;
1006                 }
1007                 return base.CanConvertTo(context, destinationType);
1008             }
1009
1010             public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
1011                 if (destinationType == null) {
1012                     throw ADP.ArgumentNull("destinationType");
1013                 }
1014
1015                 if (destinationType == typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) && value is OdbcParameter) {
1016                     OdbcParameter p = (OdbcParameter)value;
1017
1018                     // MDAC 67321 - reducing parameter generated code
1019                     int flags = 0; // if part of the collection - the parametername can't be empty
1020
1021                     if (OdbcType.NChar != p.OdbcType) {
1022                         flags |= 1;
1023                     }
1024                     if (p.ShouldSerializeSize()) {
1025                         flags |= 2;
1026                     }
1027                     if (!ADP.IsEmpty(p.SourceColumn)) {
1028                         flags |= 4;
1029                     }
1030                     if (null != p.Value) {
1031                         flags |= 8;
1032                     }
1033                     if ((ParameterDirection.Input != p.Direction) || p.IsNullable
1034                         || p.ShouldSerializePrecision() || p.ShouldSerializeScale()
1035                         || (DataRowVersion.Current != p.SourceVersion)) {
1036                         flags |= 16; // V1.0 everything
1037                     }
1038                     if (p.SourceColumnNullMapping) {
1039                         flags |= 32; // v2.0 everything
1040                     }
1041
1042                     Type[] ctorParams;
1043                     object[] ctorValues;
1044                     switch(flags) {
1045                     case  0: // ParameterName
1046                     case  1: // SqlDbType
1047                         ctorParams = new Type[] { typeof(string), typeof(OdbcType) };
1048                         ctorValues = new object[] { p.ParameterName, p.OdbcType };
1049                         break;
1050                     case  2: // Size
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 };
1054                         break;
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 };
1061                         break;
1062                     case  8: // Value
1063                         ctorParams = new Type[] { typeof(string), typeof(object) };
1064                         ctorValues = new object[] { p.ParameterName, p.Value };
1065                         break;
1066                     default:
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 };
1076                         }
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),
1082                                 typeof(object) };
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,
1087                                 p.Value };
1088                         }
1089                         break;
1090                     }
1091                     System.Reflection.ConstructorInfo ctor = typeof(OdbcParameter).GetConstructor(ctorParams);
1092                     if (null != ctor) {
1093                         return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ctor, ctorValues);
1094                     }
1095                 }
1096                 return base.ConvertTo(context, culture, value, destinationType);
1097             }
1098         }
1099     }
1100 }