1 //------------------------------------------------------------------------------
2 // <copyright file="OleDbParameter.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 //------------------------------------------------------------------------------
9 namespace System.Data.OleDb {
12 using System.ComponentModel;
14 using System.Data.Common;
15 using System.Data.ProviderBase;
16 using System.Diagnostics;
17 using System.Globalization;
20 System.ComponentModel.TypeConverterAttribute(typeof(System.Data.OleDb.OleDbParameter.OleDbParameterConverter))
22 public sealed partial class OleDbParameter : DbParameter, ICloneable, IDbDataParameter {
23 private NativeDBType _metaType;
24 private int _changeID;
26 private string _parameterName;
27 private byte _precision;
29 private bool _hasScale;
31 private NativeDBType _coerceMetaType;
33 public OleDbParameter() : base() { // V1.0 nothing
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)");
44 public OleDbParameter(string name, OleDbType dataType) : this() {
49 public OleDbParameter(string name, OleDbType dataType, int size) : this() {
55 public OleDbParameter(string name, OleDbType dataType, int size, string srcColumn) : this() {
59 SourceColumn = srcColumn;
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;
72 Direction = direction;
73 IsNullable = isNullable;
74 PrecisionInternal = precision;
75 ScaleInternal = scale;
76 SourceColumn = srcColumn;
77 SourceVersion = srcVersion;
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;
91 Direction = direction;
92 PrecisionInternal = precision;
93 ScaleInternal = scale;
94 SourceColumn = sourceColumn;
95 SourceVersion = sourceVersion;
96 SourceColumnNullMapping = sourceColumnNullMapping;
100 internal int ChangeID {
106 override public DbType DbType {
108 return GetBindType(Value).enumDbType;
111 NativeDBType dbtype = _metaType;
112 if ((null == dbtype) || (dbtype.enumDbType != value)) { // MDAC 63571
113 PropertyTypeChanging();
114 _metaType = NativeDBType.FromDbType(value);
119 public override void ResetDbType() {
124 RefreshProperties(RefreshProperties.All),
125 ResCategoryAttribute(Res.DataCategory_Data),
126 ResDescriptionAttribute(Res.OleDbParameter_OleDbType),
127 System.Data.Common.DbProviderSpecificTypePropertyAttribute(true),
129 public OleDbType OleDbType {
131 return GetBindType(Value).enumOleDbType;
134 NativeDBType dbtype = _metaType;
135 if ((null == dbtype) || (dbtype.enumOleDbType != value)) { // MDAC 63571
136 PropertyTypeChanging();
137 _metaType = NativeDBType.FromDataType(value);
142 private bool ShouldSerializeOleDbType() {
143 return (null != _metaType);
146 public void ResetOleDbType() {
147 if (null != _metaType) {
148 PropertyTypeChanging();
154 ResCategoryAttribute(Res.DataCategory_Data),
155 ResDescriptionAttribute(Res.DbParameter_ParameterName),
157 override public string ParameterName { // V1.2.3300, XXXParameter V1.0.3300
159 string parameterName = _parameterName;
160 return ((null != parameterName) ? parameterName : ADP.StrEmpty);
163 if (_parameterName != value) {
165 _parameterName = value;
170 [DefaultValue((Byte)0)] // MDAC 65862
171 [ResCategoryAttribute(Res.DataCategory_Data)]
172 [ResDescriptionAttribute(Res.DbDataParameter_Precision)]
173 public new Byte Precision {
175 return PrecisionInternal;
178 PrecisionInternal = value;
181 internal byte PrecisionInternal {
183 byte precision = _precision;
184 if (0 == precision) {
185 precision = ValuePrecision(Value);
190 if (_precision != value) {
196 private bool ShouldSerializePrecision() {
197 return (0 != _precision);
200 [DefaultValue((Byte)0)] // MDAC 65862
201 [ResCategoryAttribute(Res.DataCategory_Data)]
202 [ResDescriptionAttribute(Res.DbDataParameter_Scale)]
203 public new Byte Scale {
205 return ScaleInternal;
208 ScaleInternal = value;
211 internal byte ScaleInternal {
214 if (!ShouldSerializeScale(scale)) { // WebData 94688
215 scale = ValueScale(Value);
220 if (_scale != value || !_hasScale) {
227 private bool ShouldSerializeScale() {
228 return ShouldSerializeScale(_scale);
231 private bool ShouldSerializeScale(byte scale) {
232 return _hasScale && ((0 != scale) || ShouldSerializePrecision());
235 object ICloneable.Clone() {
236 return new OleDbParameter(this);
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;
248 private void PropertyChanging() {
249 unchecked { _changeID++; }
252 private void PropertyTypeChanging() {
254 _coerceMetaType = null;
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;
263 NativeDBType dbtype = GetBindType(value);
264 if (OleDbType.Empty == dbtype.enumOleDbType) {
265 throw ODB.UninitializedParameters(index, dbtype.enumOleDbType);
267 _coerceMetaType = dbtype;
268 value = CoerceValue(value, dbtype);
269 CoercedValue = value;
271 ParameterDirection direction = Direction;
274 if (ShouldSerializePrecision()) {
275 precision = PrecisionInternal;
278 precision = ValuePrecision(value);
280 if (0 == precision) {
281 precision = dbtype.maxpre;
285 if (ShouldSerializeScale()) {
286 scale = ScaleInternal;
289 scale = ValueScale(value);
292 int wtype = dbtype.wType;
295 if (dbtype.islong) { // long data (image, text, ntext)
296 bytecount = ADP.PtrSize;
297 if (ShouldSerializeSize()) {
301 if (NativeDBType.STR == dbtype.dbType) {
302 size = Int32.MaxValue; // WebData 98940
304 else if (NativeDBType.WSTR == dbtype.dbType) {
305 size = Int32.MaxValue/2;
308 size = Int32.MaxValue;
311 wtype |= NativeDBType.BYREF;
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);
319 if (ShouldSerializeSize()) {
321 computedSize = false;
324 size = ValueSize(value);
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;
333 Debug.Assert(NativeDBType.STR != dbtype.wType, "should have ANSI binding, describing is okay");
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;
344 if (ODB.LargeDataSize < bytecount) {
345 bytecount = ADP.PtrSize;
346 wtype |= NativeDBType.BYREF;
349 else if (0 == size) {
350 if (NativeDBType.WSTR == wtype) { // allow space for null termination character
352 // 0 == size, okay for (STR == dbType)
355 Debug.Assert(NativeDBType.STR != dbtype.wType, "should have ANSI binding, describing is okay");
359 else if (-1 == size) {
360 bytecount = ADP.PtrSize;
361 wtype |= NativeDBType.BYREF;
364 throw ADP.InvalidSizeValue(size);
367 else { // fixed length data
368 bytecount = dbtype.fixlen;
371 bindings.CurrentIndex = index;
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;
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;
398 //bindings.ParameterChangeID = changeID; // bind until something changes
399 Debug.Assert(_changeID == changeID, "parameter has unexpectedly changed");
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]);
405 return IsParameterComputed();
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) {
414 if ((typeof(string) == destinationType.dataType) && (typeof(char[]) == currentType)) {
416 else if ((NativeDBType.CY == destinationType.dbType) && (typeof(string) == currentType)) {
417 value = Decimal.Parse((string)value, NumberStyles.Currency, (IFormatProvider)null); // WebData 99376
420 value = Convert.ChangeType(value, destinationType.dataType, (IFormatProvider)null);
423 catch (Exception e) {
425 if (!ADP.IsCatchableExceptionType(e)) {
429 throw ADP.ParameterConversionFailed(value, destinationType.dataType, e); // WebData 75433
436 private NativeDBType GetBindType(object value) {
437 NativeDBType dbtype = _metaType;
438 if (null == dbtype) {
439 if (ADP.IsNull(value)) {
440 dbtype = OleDb.NativeDBType.Default;
443 dbtype = NativeDBType.FromSystemType(value);
449 internal object GetCoercedValue() {
450 object value = CoercedValue; // will also be set during binding, will rebind everytime if _metaType not set
452 value = CoerceValue(Value, _coerceMetaType);
453 CoercedValue = value;
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())
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);
476 else if (!ShouldSerializeSize() && _metaType.IsVariableLength) {
477 throw ADP.PrepareParameterSize(cmd);
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));
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");
488 RefreshProperties(RefreshProperties.All),
489 ResCategoryAttribute(Res.DataCategory_Data),
490 ResDescriptionAttribute(Res.DbParameter_Value),
491 TypeConverterAttribute(typeof(StringConverter)),
493 override public object Value { // V1.2.3300, XXXParameter V1.0.3300
498 _coercedValue = null;
503 private byte ValuePrecision(object value) {
504 return ValuePrecisionCore(value);
507 private byte ValueScale(object value) {
508 return ValueScaleCore(value);
511 private int ValueSize(object value) {
512 return ValueSizeCore(value);
515 static private int GetBindDirection(ParameterDirection direction) {
516 return (ODB.ParameterDirectionFlag & (int)direction);
517 /*switch(Direction) {
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);
529 static private int GetBindFlags(ParameterDirection direction) {
530 return (ODB.ParameterDirectionFlag & (int)direction);
531 /*switch(Direction) {
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);
543 // implemented as nested class to take advantage of the private/protected ShouldSerializeXXX methods
544 sealed internal class OleDbParameterConverter : System.ComponentModel.ExpandableObjectConverter {
546 // converter classes should have public ctor
547 public OleDbParameterConverter() {
550 public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
551 if (typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) {
554 return base.CanConvertTo(context, destinationType);
557 public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
558 if (null == destinationType) {
559 throw ADP.ArgumentNull("destinationType");
561 if ((typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) && (value is OleDbParameter)) {
562 return ConvertToInstanceDescriptor(value as OleDbParameter);
564 return base.ConvertTo(context, culture, value, destinationType);
567 private System.ComponentModel.Design.Serialization.InstanceDescriptor ConvertToInstanceDescriptor(OleDbParameter p) {
570 if (p.ShouldSerializeOleDbType()) {
573 if (p.ShouldSerializeSize()) {
576 if (!ADP.IsEmpty(p.SourceColumn)) {
579 if (null != p.Value) {
582 if ((ParameterDirection.Input != p.Direction) || p.IsNullable
583 || p.ShouldSerializePrecision() || p.ShouldSerializeScale()
584 || (DataRowVersion.Current != p.SourceVersion)) {
585 flags |= 16; // V1.0 everything
587 if (p.SourceColumnNullMapping) {
588 flags |= 32; // v2.0 everything
594 case 0: // ParameterName
596 ctorParams = new Type[] { typeof(string), typeof(OleDbType) };
597 ctorValues = new object[] { p.ParameterName, p.OleDbType };
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 };
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 };
612 ctorParams = new Type[] { typeof(string), typeof(object) };
613 ctorValues = new object[] { p.ParameterName, p.Value };
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 };
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),
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,
640 System.Reflection.ConstructorInfo ctor = typeof(OleDbParameter).GetConstructor(ctorParams);
641 return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ctor, ctorValues);