//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // Microsoft // Microsoft //------------------------------------------------------------------------------ namespace System.Data.OleDb { using System; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Diagnostics; using System.Globalization; [ System.ComponentModel.TypeConverterAttribute(typeof(System.Data.OleDb.OleDbParameter.OleDbParameterConverter)) ] public sealed partial class OleDbParameter : DbParameter, ICloneable, IDbDataParameter { private NativeDBType _metaType; private int _changeID; private string _parameterName; private byte _precision; private byte _scale; private bool _hasScale; private NativeDBType _coerceMetaType; public OleDbParameter() : base() { // V1.0 nothing } public OleDbParameter(string name, object value) : this() { // MDAC 59521 Debug.Assert(!(value is OleDbType), "use OleDbParameter(string, OleDbType)"); Debug.Assert(!(value is SqlDbType), "use OleDbParameter(string, OleDbType)"); ParameterName = name; Value = value; } public OleDbParameter(string name, OleDbType dataType) : this() { ParameterName = name; OleDbType = dataType; } public OleDbParameter(string name, OleDbType dataType, int size) : this() { ParameterName = name; OleDbType = dataType; Size = size; } public OleDbParameter(string name, OleDbType dataType, int size, string srcColumn) : this() { ParameterName = name; OleDbType = dataType; Size = size; SourceColumn = srcColumn; } [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508 public OleDbParameter(string parameterName, OleDbType dbType, int size, ParameterDirection direction, Boolean isNullable, Byte precision, Byte scale, string srcColumn, DataRowVersion srcVersion, object value) : this() { // V1.0 everything ParameterName = parameterName; OleDbType = dbType; Size = size; Direction = direction; IsNullable = isNullable; PrecisionInternal = precision; ScaleInternal = scale; SourceColumn = srcColumn; SourceVersion = srcVersion; Value = value; } [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508 public OleDbParameter(string parameterName, OleDbType dbType, int size, ParameterDirection direction, Byte precision, Byte scale, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, object value) : this() { // V2.0 everything - round trip all browsable properties + precision/scale ParameterName = parameterName; OleDbType = dbType; Size = size; Direction = direction; PrecisionInternal = precision; ScaleInternal = scale; SourceColumn = sourceColumn; SourceVersion = sourceVersion; SourceColumnNullMapping = sourceColumnNullMapping; Value = value; } internal int ChangeID { get { return _changeID; } } override public DbType DbType { get { return GetBindType(Value).enumDbType; } set { NativeDBType dbtype = _metaType; if ((null == dbtype) || (dbtype.enumDbType != value)) { // MDAC 63571 PropertyTypeChanging(); _metaType = NativeDBType.FromDbType(value); } } } public override void ResetDbType() { ResetOleDbType(); } [ RefreshProperties(RefreshProperties.All), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.OleDbParameter_OleDbType), System.Data.Common.DbProviderSpecificTypePropertyAttribute(true), ] public OleDbType OleDbType { get { return GetBindType(Value).enumOleDbType; } set { NativeDBType dbtype = _metaType; if ((null == dbtype) || (dbtype.enumOleDbType != value)) { // MDAC 63571 PropertyTypeChanging(); _metaType = NativeDBType.FromDataType(value); } } } private bool ShouldSerializeOleDbType() { return (null != _metaType); } public void ResetOleDbType() { if (null != _metaType) { PropertyTypeChanging(); _metaType = null; } } [ ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DbParameter_ParameterName), ] override public string ParameterName { // V1.2.3300, XXXParameter V1.0.3300 get { string parameterName = _parameterName; return ((null != parameterName) ? parameterName : ADP.StrEmpty); } set { if (_parameterName != value) { PropertyChanging(); _parameterName = value; } } } [DefaultValue((Byte)0)] // MDAC 65862 [ResCategoryAttribute(Res.DataCategory_Data)] [ResDescriptionAttribute(Res.DbDataParameter_Precision)] public new Byte Precision { get { return PrecisionInternal; } set { PrecisionInternal = value; } } internal byte PrecisionInternal { get { byte precision = _precision; if (0 == precision) { precision = ValuePrecision(Value); } return precision; } set { if (_precision != value) { PropertyChanging(); _precision = value; } } } private bool ShouldSerializePrecision() { return (0 != _precision); } [DefaultValue((Byte)0)] // MDAC 65862 [ResCategoryAttribute(Res.DataCategory_Data)] [ResDescriptionAttribute(Res.DbDataParameter_Scale)] public new Byte Scale { get { return ScaleInternal; } set { ScaleInternal = value; } } internal byte ScaleInternal { get { byte scale = _scale; if (!ShouldSerializeScale(scale)) { // WebData 94688 scale = ValueScale(Value); } return scale; } set { if (_scale != value || !_hasScale) { PropertyChanging(); _scale = value; _hasScale = true; } } } private bool ShouldSerializeScale() { return ShouldSerializeScale(_scale); } private bool ShouldSerializeScale(byte scale) { return _hasScale && ((0 != scale) || ShouldSerializePrecision()); } object ICloneable.Clone() { return new OleDbParameter(this); } private void CloneHelper(OleDbParameter destination) { CloneHelperCore(destination); destination._metaType = _metaType; destination._parameterName = _parameterName; destination._precision = _precision; destination._scale = _scale; destination._hasScale = _hasScale; } private void PropertyChanging() { unchecked { _changeID++; } } private void PropertyTypeChanging() { PropertyChanging(); _coerceMetaType = null; CoercedValue = null; } // goal: call virtual property getters only once per parameter internal bool BindParameter(int index, Bindings bindings) { int changeID = _changeID; object value = Value; NativeDBType dbtype = GetBindType(value); if (OleDbType.Empty == dbtype.enumOleDbType) { throw ODB.UninitializedParameters(index, dbtype.enumOleDbType); } _coerceMetaType = dbtype; value = CoerceValue(value, dbtype); CoercedValue = value; ParameterDirection direction = Direction; byte precision; if (ShouldSerializePrecision()) { precision = PrecisionInternal; } else { precision = ValuePrecision(value); } if (0 == precision) { precision = dbtype.maxpre; } byte scale; if (ShouldSerializeScale()) { scale = ScaleInternal; } else { scale = ValueScale(value); } int wtype = dbtype.wType; int bytecount, size; if (dbtype.islong) { // long data (image, text, ntext) bytecount = ADP.PtrSize; if (ShouldSerializeSize()) { size = Size; } else { if (NativeDBType.STR == dbtype.dbType) { size = Int32.MaxValue; // WebData 98940 } else if (NativeDBType.WSTR == dbtype.dbType) { size = Int32.MaxValue/2; } else { size = Int32.MaxValue; } } wtype |= NativeDBType.BYREF; } else if (dbtype.IsVariableLength) { // variable length data (varbinary, varchar, nvarchar) if (!ShouldSerializeSize() && ADP.IsDirection(this, ParameterDirection.Output)) { throw ADP.UninitializedParameterSize(index, _coerceMetaType.dataType); } bool computedSize; if (ShouldSerializeSize()) { size = Size; computedSize = false; } else { size = ValueSize(value); computedSize = true; } if (0 < size) { if (NativeDBType.WSTR == dbtype.wType) { // maximum 0x3FFFFFFE characters, computed this way to avoid overflow exception bytecount = Math.Min(size, 0x3FFFFFFE) * 2 + 2; } else { Debug.Assert(NativeDBType.STR != dbtype.wType, "should have ANSI binding, describing is okay"); bytecount = size; } if (computedSize) { if (NativeDBType.STR == dbtype.dbType) { // WebData 98140 // maximum 0x7ffffffe characters, computed this way to avoid overflow exception size = Math.Min(size, 0x3FFFFFFE) * 2; } } if (ODB.LargeDataSize < bytecount) { bytecount = ADP.PtrSize; wtype |= NativeDBType.BYREF; } } else if (0 == size) { if (NativeDBType.WSTR == wtype) { // allow space for null termination character bytecount = 2; // 0 == size, okay for (STR == dbType) } else { Debug.Assert(NativeDBType.STR != dbtype.wType, "should have ANSI binding, describing is okay"); bytecount = 0; } } else if (-1 == size) { bytecount = ADP.PtrSize; wtype |= NativeDBType.BYREF; } else { throw ADP.InvalidSizeValue(size); } } else { // fixed length data bytecount = dbtype.fixlen; size = bytecount; } bindings.CurrentIndex = index; // tagDBPARAMBINDINFO info for SetParameterInfo 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. bindings.Name = ADP.PtrZero; bindings.ParamSize = new IntPtr(size); bindings.Flags = GetBindFlags(direction); //bindings.Precision = precision; //bindings.Scale = scale; // tagDBBINDING info for CreateAccessor bindings.Ordinal = (IntPtr)(index+1); bindings.Part = dbtype.dbPart; bindings.ParamIO = GetBindDirection(direction); bindings.Precision = precision; bindings.Scale = scale; bindings.DbType = wtype; bindings.MaxLen = bytecount; // also increments databuffer size (uses DbType) //bindings.ValueOffset = bindings.DataBufferSize; // set via MaxLen //bindings.LengthOffset = i * sizeof_int64; //bindings.StatusOffset = i * sizeof_int64 + sizeof_int32; //bindings.TypeInfoPtr = 0; //bindings.ObjectPtr = 0; //bindings.BindExtPtr = 0; //bindings.MemOwner = /*DBMEMOWNER_CLIENTOWNED*/0; //bindings.Flags = 0; //bindings.ParameterChangeID = changeID; // bind until something changes Debug.Assert(_changeID == changeID, "parameter has unexpectedly changed"); if (Bid.AdvancedOn) { Bid.Trace(" index=%d, parameterName='%ls'\n", index, ParameterName);//, bindings.BindInfo[index]); Bid.Trace("\n");//, bindings.DBBinding[index]); } return IsParameterComputed(); } private static object CoerceValue(object value, NativeDBType destinationType) { Debug.Assert(null != destinationType, "null destinationType"); if ((null != value) && (DBNull.Value != value) && (typeof(object) != destinationType.dataType)) { Type currentType = value.GetType(); if (currentType != destinationType.dataType) { try { if ((typeof(string) == destinationType.dataType) && (typeof(char[]) == currentType)) { } else if ((NativeDBType.CY == destinationType.dbType) && (typeof(string) == currentType)) { value = Decimal.Parse((string)value, NumberStyles.Currency, (IFormatProvider)null); // WebData 99376 } else { value = Convert.ChangeType(value, destinationType.dataType, (IFormatProvider)null); } } catch (Exception e) { // if (!ADP.IsCatchableExceptionType(e)) { throw; } throw ADP.ParameterConversionFailed(value, destinationType.dataType, e); // WebData 75433 } } } return value; } private NativeDBType GetBindType(object value) { NativeDBType dbtype = _metaType; if (null == dbtype) { if (ADP.IsNull(value)) { dbtype = OleDb.NativeDBType.Default; } else { dbtype = NativeDBType.FromSystemType(value); } } return dbtype; } internal object GetCoercedValue() { object value = CoercedValue; // will also be set during binding, will rebind everytime if _metaType not set if (null == value) { value = CoerceValue(Value, _coerceMetaType); CoercedValue = value; } return value; } internal bool IsParameterComputed() { NativeDBType metaType = _metaType; return ((null == metaType) || (!ShouldSerializeSize() && metaType.IsVariableLength) || ((NativeDBType.DECIMAL == metaType.dbType) || (NativeDBType.NUMERIC == metaType.dbType) && (!ShouldSerializeScale() || !ShouldSerializePrecision()) ) ); // MDAC 69299 } // @devnote: use IsParameterComputed which is called in the normal case // only to call Prepare to throw the specialized error message // reducing the overall number of methods to actually jit internal void Prepare(OleDbCommand cmd) { // MDAC 70232 Debug.Assert(IsParameterComputed(), "Prepare computed parameter"); if (null == _metaType) { throw ADP.PrepareParameterType(cmd); } else if (!ShouldSerializeSize() && _metaType.IsVariableLength) { throw ADP.PrepareParameterSize(cmd); } else if (!ShouldSerializePrecision() && !ShouldSerializeScale() && ((NativeDBType.DECIMAL == _metaType.wType) || (NativeDBType.NUMERIC == _metaType.wType))) { // MDAC 71441 throw ADP.PrepareParameterScale(cmd, _metaType.wType.ToString("G", CultureInfo.InvariantCulture)); } // Disabling the assert, see Dev11 775862 for details: http://vstfdevdiv.redmond.corp.microsoft.com:8080/WorkItemTracking/WorkItem.aspx?artifactMoniker=775862 // Debug.Assert(false, "OleDbParameter.Prepare didn't throw"); } [ RefreshProperties(RefreshProperties.All), ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DbParameter_Value), TypeConverterAttribute(typeof(StringConverter)), ] override public object Value { // V1.2.3300, XXXParameter V1.0.3300 get { return _value; } set { _coercedValue = null; _value = value; } } private byte ValuePrecision(object value) { return ValuePrecisionCore(value); } private byte ValueScale(object value) { return ValueScaleCore(value); } private int ValueSize(object value) { return ValueSizeCore(value); } static private int GetBindDirection(ParameterDirection direction) { return (ODB.ParameterDirectionFlag & (int)direction); /*switch(Direction) { default: case ParameterDirection.Input: return ODB.DBPARAMIO_INPUT; case ParameterDirection.Output: case ParameterDirection.ReturnValue: return ODB.DBPARAMIO_OUTPUT; case ParameterDirection.InputOutput: return (ODB.DBPARAMIO_INPUT | ODB.DBPARAMIO_OUTPUT); }*/ } static private int GetBindFlags(ParameterDirection direction) { return (ODB.ParameterDirectionFlag & (int)direction); /*switch(Direction) { default: case ParameterDirection.Input: return ODB.DBPARAMFLAGS_ISINPUT; case ParameterDirection.Output: case ParameterDirection.ReturnValue: return ODB.DBPARAMFLAGS_ISOUTPUT; case ParameterDirection.InputOutput: return (ODB.DBPARAMFLAGS_ISINPUT | ODB.DBPARAMFLAGS_ISOUTPUT); }*/ } // implemented as nested class to take advantage of the private/protected ShouldSerializeXXX methods sealed internal class OleDbParameterConverter : System.ComponentModel.ExpandableObjectConverter { // converter classes should have public ctor public OleDbParameterConverter() { } public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) { return true; } return base.CanConvertTo(context, destinationType); } public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if (null == destinationType) { throw ADP.ArgumentNull("destinationType"); } if ((typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) && (value is OleDbParameter)) { return ConvertToInstanceDescriptor(value as OleDbParameter); } return base.ConvertTo(context, culture, value, destinationType); } private System.ComponentModel.Design.Serialization.InstanceDescriptor ConvertToInstanceDescriptor(OleDbParameter p) { int flags = 0; if (p.ShouldSerializeOleDbType()) { flags |= 1; } if (p.ShouldSerializeSize()) { flags |= 2; } if (!ADP.IsEmpty(p.SourceColumn)) { flags |= 4; } if (null != p.Value) { flags |= 8; } if ((ParameterDirection.Input != p.Direction) || p.IsNullable || p.ShouldSerializePrecision() || p.ShouldSerializeScale() || (DataRowVersion.Current != p.SourceVersion)) { flags |= 16; // V1.0 everything } if (p.SourceColumnNullMapping) { flags |= 32; // v2.0 everything } Type[] ctorParams; object[] ctorValues; switch(flags) { case 0: // ParameterName case 1: // OleDbType ctorParams = new Type[] { typeof(string), typeof(OleDbType) }; ctorValues = new object[] { p.ParameterName, p.OleDbType }; break; case 2: // Size case 3: // Size, OleDbType ctorParams = new Type[] { typeof(string), typeof(OleDbType), typeof(int) }; ctorValues = new object[] { p.ParameterName, p.OleDbType, p.Size }; break; case 4: // SourceColumn case 5: // SourceColumn, OleDbType case 6: // SourceColumn, Size case 7: // SourceColumn, Size, OleDbType ctorParams = new Type[] { typeof(string), typeof(OleDbType), typeof(int), typeof(string) }; ctorValues = new object[] { p.ParameterName, p.OleDbType, p.Size, p.SourceColumn }; break; case 8: // Value ctorParams = new Type[] { typeof(string), typeof(object) }; ctorValues = new object[] { p.ParameterName, p.Value }; break; default: // everything else if (0 == (32 & flags)) { // V1.0 everything ctorParams = new Type[] { typeof(string), typeof(OleDbType), typeof(int), typeof(ParameterDirection), typeof(bool), typeof(byte), typeof(byte), typeof(string), typeof(DataRowVersion), typeof(object) }; ctorValues = new object[] { p.ParameterName, p.OleDbType, p.Size, p.Direction, p.IsNullable, p.PrecisionInternal, p.ScaleInternal, p.SourceColumn, p.SourceVersion, p.Value }; } else { // v2.0 everything - round trip all browsable properties + precision/scale ctorParams = new Type[] { typeof(string), typeof(OleDbType), typeof(int), typeof(ParameterDirection), typeof(byte), typeof(byte), typeof(string), typeof(DataRowVersion), typeof(bool), typeof(object) }; ctorValues = new object[] { p.ParameterName, p.OleDbType, p.Size, p.Direction, p.PrecisionInternal, p.ScaleInternal, p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping, p.Value }; } break; } System.Reflection.ConstructorInfo ctor = typeof(OleDbParameter).GetConstructor(ctorParams); return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ctor, ctorValues); } } } }