ad824e1a7ddffdf5af8fae5e4888ccdd42e352ad
[mono.git] / mcs / class / referencesource / System.Data / System / Data / OleDb / OleDbParameter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="OleDbParameter.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 namespace System.Data.OleDb {
10
11     using System;
12     using System.ComponentModel;
13     using System.Data;
14     using System.Data.Common;
15     using System.Data.ProviderBase;
16     using System.Diagnostics;
17     using System.Globalization;
18
19     [
20     System.ComponentModel.TypeConverterAttribute(typeof(System.Data.OleDb.OleDbParameter.OleDbParameterConverter))
21     ]
22     public sealed partial class OleDbParameter : DbParameter, ICloneable, IDbDataParameter {
23         private NativeDBType _metaType;
24         private int _changeID;
25
26         private string _parameterName;
27         private byte _precision;
28         private byte _scale;
29         private bool _hasScale;
30
31         private NativeDBType _coerceMetaType;
32
33         public OleDbParameter() : base() { // V1.0 nothing
34         }
35
36         public OleDbParameter(string name, object value) : this() { // MDAC 59521
37             Debug.Assert(!(value is OleDbType), "use OleDbParameter(string, OleDbType)");
38             Debug.Assert(!(value is SqlDbType), "use OleDbParameter(string, OleDbType)");
39
40             ParameterName = name;
41             Value = value;
42         }
43
44         public OleDbParameter(string name, OleDbType dataType) : this() {
45             ParameterName = name;
46             OleDbType = dataType;
47         }
48
49         public OleDbParameter(string name, OleDbType dataType, int size) : this() {
50             ParameterName = name;
51             OleDbType = dataType;
52             Size = size;
53         }
54
55         public OleDbParameter(string name, OleDbType dataType, int size, string srcColumn) : this() {
56             ParameterName = name;
57             OleDbType = dataType;
58             Size = size;
59             SourceColumn = srcColumn;
60         }
61
62         [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
63         public OleDbParameter(string parameterName,
64                               OleDbType dbType, int size,
65                               ParameterDirection direction, Boolean isNullable,
66                               Byte precision, Byte scale,
67                               string srcColumn, DataRowVersion srcVersion,
68                               object value) : this() { // V1.0 everything
69             ParameterName = parameterName;
70             OleDbType = dbType;
71             Size = size;
72             Direction = direction;
73             IsNullable = isNullable;
74             PrecisionInternal = precision;
75             ScaleInternal = scale;
76             SourceColumn = srcColumn;
77             SourceVersion = srcVersion;
78             Value = value;
79         }
80
81         [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
82         public OleDbParameter(string parameterName,
83                               OleDbType dbType, int size,
84                               ParameterDirection direction,
85                               Byte precision, Byte scale,
86                               string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping,
87                               object value) : this() { // V2.0 everything - round trip all browsable properties + precision/scale
88             ParameterName = parameterName;
89             OleDbType = dbType;
90             Size = size;
91             Direction = direction;
92             PrecisionInternal = precision;
93             ScaleInternal = scale;
94             SourceColumn = sourceColumn;
95             SourceVersion = sourceVersion;
96             SourceColumnNullMapping = sourceColumnNullMapping;
97             Value = value;
98         }
99
100         internal int ChangeID {
101             get {
102                 return _changeID;
103             }
104         }
105
106         override public DbType DbType {
107             get {
108                 return GetBindType(Value).enumDbType;
109             }
110             set {
111                 NativeDBType dbtype = _metaType;
112                 if ((null == dbtype) || (dbtype.enumDbType != value)) { // MDAC 63571
113                     PropertyTypeChanging();
114                     _metaType = NativeDBType.FromDbType(value);
115                 }
116             }
117         }
118
119         public override void ResetDbType() {
120             ResetOleDbType();
121         }
122
123         [
124         RefreshProperties(RefreshProperties.All),
125         ResCategoryAttribute(Res.DataCategory_Data),
126         ResDescriptionAttribute(Res.OleDbParameter_OleDbType),
127         System.Data.Common.DbProviderSpecificTypePropertyAttribute(true),
128         ]
129         public OleDbType OleDbType {
130             get {
131                 return GetBindType(Value).enumOleDbType;
132             }
133             set {
134                 NativeDBType dbtype = _metaType;
135                 if ((null == dbtype) || (dbtype.enumOleDbType != value)) { // MDAC 63571
136                     PropertyTypeChanging();
137                     _metaType = NativeDBType.FromDataType(value);
138                 }
139             }
140         }
141
142         private bool ShouldSerializeOleDbType() {
143             return (null != _metaType);
144         }
145
146         public void ResetOleDbType() {
147             if (null != _metaType) {
148                 PropertyTypeChanging();
149                 _metaType = null;
150             }
151         }
152
153         [
154         ResCategoryAttribute(Res.DataCategory_Data),
155         ResDescriptionAttribute(Res.DbParameter_ParameterName),
156         ]
157         override public string ParameterName { // V1.2.3300, XXXParameter V1.0.3300
158             get {
159                 string parameterName = _parameterName;
160                 return ((null != parameterName) ? parameterName : ADP.StrEmpty);
161             }
162             set {
163                 if (_parameterName != value) {
164                     PropertyChanging();
165                     _parameterName = value;
166                 }
167             }
168         }
169
170         [DefaultValue((Byte)0)] // MDAC 65862
171         [ResCategoryAttribute(Res.DataCategory_Data)]
172         [ResDescriptionAttribute(Res.DbDataParameter_Precision)]
173         public new Byte Precision {
174             get {
175                 return PrecisionInternal;
176             }
177             set {
178                 PrecisionInternal = value;
179             }
180         }
181         internal byte PrecisionInternal {
182             get {
183                 byte precision = _precision;
184                 if (0 == precision) {
185                     precision = ValuePrecision(Value);
186                 }
187                 return precision;
188             }
189             set {
190                 if (_precision != value) {
191                     PropertyChanging();
192                     _precision = value;
193                 }
194             }
195         }
196         private bool ShouldSerializePrecision() {
197             return (0 != _precision);
198         }
199
200         [DefaultValue((Byte)0)] // MDAC 65862
201         [ResCategoryAttribute(Res.DataCategory_Data)]
202         [ResDescriptionAttribute(Res.DbDataParameter_Scale)]
203         public new Byte Scale {
204             get {
205                 return ScaleInternal;
206             }
207             set {
208                 ScaleInternal = value;
209             }
210         }
211         internal byte ScaleInternal {
212             get {
213                 byte scale = _scale;
214                 if (!ShouldSerializeScale(scale)) { // WebData 94688
215                     scale = ValueScale(Value);
216                 }
217                 return scale;
218             }
219             set {
220                 if (_scale != value || !_hasScale) {
221                     PropertyChanging();
222                     _scale = value;
223                     _hasScale = true;
224                 }
225             }
226         }
227         private bool ShouldSerializeScale() {
228             return ShouldSerializeScale(_scale);
229         }
230
231         private bool ShouldSerializeScale(byte scale) {
232             return _hasScale && ((0 != scale) ||  ShouldSerializePrecision());
233         }
234
235         object ICloneable.Clone() {
236             return new OleDbParameter(this);
237         }
238
239         private void CloneHelper(OleDbParameter destination) {
240             CloneHelperCore(destination);
241             destination._metaType = _metaType;
242             destination._parameterName = _parameterName;
243             destination._precision = _precision;
244             destination._scale = _scale;
245             destination._hasScale = _hasScale;
246         }
247
248         private void PropertyChanging() {
249             unchecked { _changeID++; }
250         }
251
252         private void PropertyTypeChanging() {
253             PropertyChanging();
254             _coerceMetaType = null;
255             CoercedValue = null;
256         }
257
258         // goal: call virtual property getters only once per parameter
259         internal bool BindParameter(int index, Bindings bindings) {
260             int changeID = _changeID;
261             object value = Value;
262
263             NativeDBType dbtype = GetBindType(value);
264             if (OleDbType.Empty == dbtype.enumOleDbType) {
265                 throw ODB.UninitializedParameters(index, dbtype.enumOleDbType);
266             }
267             _coerceMetaType = dbtype;
268             value = CoerceValue(value, dbtype);
269             CoercedValue = value;
270
271             ParameterDirection direction = Direction;
272
273             byte precision;
274             if (ShouldSerializePrecision()) {
275                 precision = PrecisionInternal;
276             }
277             else {
278                 precision = ValuePrecision(value);
279             }
280             if (0 == precision) {
281                 precision = dbtype.maxpre;
282             }
283
284             byte scale;
285             if (ShouldSerializeScale()) {
286                 scale = ScaleInternal;
287             }
288             else {
289                 scale = ValueScale(value);
290             }
291
292             int wtype = dbtype.wType;
293             int bytecount, size;
294
295             if (dbtype.islong) { // long data (image, text, ntext)
296                 bytecount = ADP.PtrSize;
297                 if (ShouldSerializeSize()) {
298                     size = Size;
299                 }
300                 else {
301                     if (NativeDBType.STR == dbtype.dbType) {
302                         size = Int32.MaxValue; // WebData 98940
303                     }
304                     else if (NativeDBType.WSTR == dbtype.dbType) {
305                         size = Int32.MaxValue/2;
306                     }
307                     else {
308                         size = Int32.MaxValue;
309                     }
310                 }
311                 wtype |= NativeDBType.BYREF;
312             }
313             else if (dbtype.IsVariableLength) { // variable length data (varbinary, varchar, nvarchar)
314                 if (!ShouldSerializeSize() && ADP.IsDirection(this, ParameterDirection.Output)) {
315                     throw ADP.UninitializedParameterSize(index, _coerceMetaType.dataType);
316                 }
317
318                 bool computedSize;
319                 if (ShouldSerializeSize()) {
320                     size = Size;
321                     computedSize = false;
322                 }
323                 else {
324                     size = ValueSize(value);
325                     computedSize = true;
326                 }
327                 if (0 < size) {
328                     if (NativeDBType.WSTR == dbtype.wType) {
329                         // maximum 0x3FFFFFFE characters, computed this way to avoid overflow exception
330                         bytecount = Math.Min(size, 0x3FFFFFFE) * 2 + 2;
331                     }
332                     else {
333                         Debug.Assert(NativeDBType.STR != dbtype.wType, "should have ANSI binding, describing is okay");
334                         bytecount = size;
335                     }
336
337                     if (computedSize) {
338                         if (NativeDBType.STR == dbtype.dbType) { // WebData 98140
339                             // maximum 0x7ffffffe characters, computed this way to avoid overflow exception
340                             size = Math.Min(size, 0x3FFFFFFE) * 2;
341                         }
342                     }
343
344                     if (ODB.LargeDataSize < bytecount) {
345                         bytecount = ADP.PtrSize;
346                         wtype |= NativeDBType.BYREF;
347                     }
348                 }
349                 else if (0 == size) {
350                     if (NativeDBType.WSTR == wtype) { // allow space for null termination character
351                         bytecount = 2;
352                         // 0 == size, okay for (STR == dbType)
353                     }
354                     else {
355                         Debug.Assert(NativeDBType.STR != dbtype.wType, "should have ANSI binding, describing is okay");
356                         bytecount = 0;
357                     }
358                 }
359                 else if (-1 == size) {
360                     bytecount = ADP.PtrSize;
361                     wtype |= NativeDBType.BYREF;
362                 }
363                 else {
364                     throw ADP.InvalidSizeValue(size);
365                 }
366             }
367             else { // fixed length data
368                 bytecount = dbtype.fixlen;
369                 size = bytecount;
370             }
371             bindings.CurrentIndex = index;
372
373             // tagDBPARAMBINDINFO info for SetParameterInfo
374             bindings.DataSourceType = dbtype.dbString.DangerousGetHandle(); // NOTE: This is a constant and isn't exposed publicly, so there really isn't a potential for Handle Recycling.
375             bindings.Name = ADP.PtrZero;
376             bindings.ParamSize = new IntPtr(size);
377             bindings.Flags = GetBindFlags(direction);
378           //bindings.Precision    = precision;
379           //bindings.Scale        = scale;
380
381             // tagDBBINDING info for CreateAccessor
382             bindings.Ordinal      = (IntPtr)(index+1);
383             bindings.Part         = dbtype.dbPart;
384             bindings.ParamIO      = GetBindDirection(direction);
385             bindings.Precision    = precision;
386             bindings.Scale        = scale;
387             bindings.DbType       = wtype;
388             bindings.MaxLen       = bytecount; // also increments databuffer size (uses DbType)
389           //bindings.ValueOffset  = bindings.DataBufferSize; // set via MaxLen
390           //bindings.LengthOffset = i * sizeof_int64;
391           //bindings.StatusOffset = i * sizeof_int64 + sizeof_int32;
392           //bindings.TypeInfoPtr  = 0;
393           //bindings.ObjectPtr    = 0;
394           //bindings.BindExtPtr   = 0;
395           //bindings.MemOwner     = /*DBMEMOWNER_CLIENTOWNED*/0;
396           //bindings.Flags        = 0;
397
398           //bindings.ParameterChangeID = changeID; // bind until something changes
399             Debug.Assert(_changeID == changeID, "parameter has unexpectedly changed");
400
401             if (Bid.AdvancedOn) {
402                 Bid.Trace("<oledb.struct.tagDBPARAMBINDINFO|INFO|ADV> index=%d, parameterName='%ls'\n", index, ParameterName);//, bindings.BindInfo[index]);
403                 Bid.Trace("<oledb.struct.tagDBBINDING|INFO|ADV>\n");//, bindings.DBBinding[index]);
404             }
405             return IsParameterComputed();
406         }
407
408         private static object CoerceValue(object value, NativeDBType destinationType) {
409             Debug.Assert(null != destinationType, "null destinationType");
410             if ((null != value) && (DBNull.Value != value) && (typeof(object) != destinationType.dataType)) {
411                 Type currentType = value.GetType();
412                 if (currentType != destinationType.dataType) {
413                     try {
414                         if ((typeof(string) == destinationType.dataType) && (typeof(char[]) == currentType)) {
415                         }
416                         else if ((NativeDBType.CY == destinationType.dbType) && (typeof(string) == currentType)) {
417                             value = Decimal.Parse((string)value, NumberStyles.Currency, (IFormatProvider)null); // WebData 99376
418                         }
419                         else {
420                             value = Convert.ChangeType(value, destinationType.dataType, (IFormatProvider)null);
421                         }
422                     }
423                     catch (Exception e) {
424                         // 
425                         if (!ADP.IsCatchableExceptionType(e)) {
426                             throw;
427                         }
428
429                         throw ADP.ParameterConversionFailed(value, destinationType.dataType, e); // WebData 75433
430                     }
431                 }
432             }
433             return value;
434         }
435
436         private NativeDBType GetBindType(object value) {
437             NativeDBType dbtype = _metaType;
438             if (null == dbtype) {
439                 if (ADP.IsNull(value)) {
440                     dbtype = OleDb.NativeDBType.Default;
441                 }
442                 else {
443                     dbtype = NativeDBType.FromSystemType(value);
444                 }
445             }
446             return dbtype;
447         }
448
449         internal object GetCoercedValue() {
450             object value = CoercedValue; // will also be set during binding, will rebind everytime if _metaType not set
451             if (null == value) {
452                 value = CoerceValue(Value, _coerceMetaType);
453                 CoercedValue = value;
454             }
455             return value;
456         }
457
458         internal bool IsParameterComputed() {
459             NativeDBType metaType = _metaType;
460             return ((null == metaType)
461                     || (!ShouldSerializeSize() && metaType.IsVariableLength)
462                     || ((NativeDBType.DECIMAL == metaType.dbType) || (NativeDBType.NUMERIC == metaType.dbType)
463                         && (!ShouldSerializeScale() || !ShouldSerializePrecision())
464                         )
465                     ); // MDAC 69299
466         }
467
468         // @devnote: use IsParameterComputed which is called in the normal case
469         // only to call Prepare to throw the specialized error message
470         // reducing the overall number of methods to actually jit
471         internal void Prepare(OleDbCommand cmd) { // MDAC 70232
472             Debug.Assert(IsParameterComputed(), "Prepare computed parameter");
473             if (null == _metaType) {
474                 throw ADP.PrepareParameterType(cmd);
475             }
476             else if (!ShouldSerializeSize() && _metaType.IsVariableLength) {
477                 throw ADP.PrepareParameterSize(cmd);
478             }
479             else if (!ShouldSerializePrecision() && !ShouldSerializeScale() && ((NativeDBType.DECIMAL == _metaType.wType) || (NativeDBType.NUMERIC == _metaType.wType))) { // MDAC 71441
480                 throw ADP.PrepareParameterScale(cmd, _metaType.wType.ToString("G", CultureInfo.InvariantCulture));
481             }
482
483             // Disabling the assert, see Dev11 775862 for details: http://vstfdevdiv.redmond.corp.microsoft.com:8080/WorkItemTracking/WorkItem.aspx?artifactMoniker=775862
484             // Debug.Assert(false, "OleDbParameter.Prepare didn't throw");
485         }
486
487         [
488         RefreshProperties(RefreshProperties.All),
489         ResCategoryAttribute(Res.DataCategory_Data),
490         ResDescriptionAttribute(Res.DbParameter_Value),
491         TypeConverterAttribute(typeof(StringConverter)),
492         ]
493         override public object Value { // V1.2.3300, XXXParameter V1.0.3300
494             get {
495                 return _value;
496             }
497             set {
498                 _coercedValue = null;
499                 _value = value;
500             }
501         }
502
503         private byte ValuePrecision(object value) {
504             return ValuePrecisionCore(value);
505         }
506
507         private byte ValueScale(object value) {
508             return ValueScaleCore(value);
509         }
510
511         private int ValueSize(object value) {
512             return ValueSizeCore(value);
513         }
514
515         static private int GetBindDirection(ParameterDirection direction) {
516             return (ODB.ParameterDirectionFlag & (int)direction);
517             /*switch(Direction) {
518             default:
519             case ParameterDirection.Input:
520                 return ODB.DBPARAMIO_INPUT;
521             case ParameterDirection.Output:
522             case ParameterDirection.ReturnValue:
523                 return ODB.DBPARAMIO_OUTPUT;
524             case ParameterDirection.InputOutput:
525                 return (ODB.DBPARAMIO_INPUT | ODB.DBPARAMIO_OUTPUT);
526             }*/
527         }
528
529         static private int GetBindFlags(ParameterDirection direction) {
530             return (ODB.ParameterDirectionFlag & (int)direction);
531             /*switch(Direction) {
532             default:
533             case ParameterDirection.Input:
534                 return ODB.DBPARAMFLAGS_ISINPUT;
535             case ParameterDirection.Output:
536             case ParameterDirection.ReturnValue:
537                 return ODB.DBPARAMFLAGS_ISOUTPUT;
538             case ParameterDirection.InputOutput:
539                 return (ODB.DBPARAMFLAGS_ISINPUT | ODB.DBPARAMFLAGS_ISOUTPUT);
540             }*/
541         }
542
543         // implemented as nested class to take advantage of the private/protected ShouldSerializeXXX methods
544         sealed internal class OleDbParameterConverter : System.ComponentModel.ExpandableObjectConverter {
545
546             // converter classes should have public ctor
547             public OleDbParameterConverter() {
548             }
549
550             public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
551                 if (typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) {
552                     return true;
553                 }
554                 return base.CanConvertTo(context, destinationType);
555             }
556
557             public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
558                 if (null == destinationType) {
559                     throw ADP.ArgumentNull("destinationType");
560                 }
561                 if ((typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) && (value is OleDbParameter)) {
562                     return ConvertToInstanceDescriptor(value as OleDbParameter);
563                 }
564                 return base.ConvertTo(context, culture, value, destinationType);
565             }
566
567             private System.ComponentModel.Design.Serialization.InstanceDescriptor ConvertToInstanceDescriptor(OleDbParameter p) {
568                 int flags = 0;
569
570                 if (p.ShouldSerializeOleDbType()) {
571                     flags |= 1;
572                 }
573                 if (p.ShouldSerializeSize()) {
574                     flags |= 2;
575                 }
576                 if (!ADP.IsEmpty(p.SourceColumn)) {
577                     flags |= 4;
578                 }
579                 if (null != p.Value) {
580                     flags |= 8;
581                 }
582                 if ((ParameterDirection.Input != p.Direction) || p.IsNullable
583                     || p.ShouldSerializePrecision() || p.ShouldSerializeScale()
584                     || (DataRowVersion.Current != p.SourceVersion)) {
585                     flags |= 16; // V1.0 everything
586                 }
587                 if (p.SourceColumnNullMapping) {
588                     flags |= 32; // v2.0 everything
589                 }
590
591                 Type[] ctorParams;
592                 object[] ctorValues;
593                 switch(flags) {
594                 case  0: // ParameterName
595                 case  1: // OleDbType
596                     ctorParams = new Type[] { typeof(string), typeof(OleDbType) };
597                     ctorValues = new object[] { p.ParameterName, p.OleDbType };
598                     break;
599                 case  2: // Size
600                 case  3: // Size, OleDbType
601                     ctorParams = new Type[] { typeof(string), typeof(OleDbType), typeof(int) };
602                     ctorValues = new object[] { p.ParameterName, p.OleDbType, p.Size };
603                     break;
604                 case  4: // SourceColumn
605                 case  5: // SourceColumn, OleDbType
606                 case  6: // SourceColumn, Size
607                 case  7: // SourceColumn, Size, OleDbType
608                     ctorParams = new Type[] { typeof(string), typeof(OleDbType), typeof(int), typeof(string) };
609                     ctorValues = new object[] { p.ParameterName, p.OleDbType, p.Size, p.SourceColumn };
610                     break;
611                 case  8: // Value
612                     ctorParams = new Type[] { typeof(string), typeof(object) };
613                     ctorValues = new object[] { p.ParameterName, p.Value };
614                     break;
615                 default: // everything else
616                     if (0 == (32 & flags)) { // V1.0 everything
617                         ctorParams = new Type[] {
618                             typeof(string), typeof(OleDbType), typeof(int), typeof(ParameterDirection),
619                             typeof(bool), typeof(byte), typeof(byte), typeof(string),
620                             typeof(DataRowVersion), typeof(object) };
621                         ctorValues = new object[] {
622                             p.ParameterName, p.OleDbType,  p.Size, p.Direction,
623                             p.IsNullable, p.PrecisionInternal, p.ScaleInternal, p.SourceColumn,
624                             p.SourceVersion, p.Value };
625                     }
626                     else { // v2.0 everything - round trip all browsable properties + precision/scale
627                         ctorParams = new Type[] {
628                             typeof(string), typeof(OleDbType), typeof(int), typeof(ParameterDirection),
629                             typeof(byte), typeof(byte),
630                             typeof(string), typeof(DataRowVersion), typeof(bool),
631                             typeof(object) };
632                         ctorValues = new object[] {
633                             p.ParameterName, p.OleDbType,  p.Size, p.Direction,
634                             p.PrecisionInternal, p.ScaleInternal,
635                             p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping,
636                             p.Value };
637                     }
638                     break;
639                 }
640                 System.Reflection.ConstructorInfo ctor = typeof(OleDbParameter).GetConstructor(ctorParams);
641                 return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ctor, ctorValues);
642             }
643         }
644     }
645 }