1 //------------------------------------------------------------------------------
2 // <copyright file="SqlParameter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data.SqlClient {
12 using System.ComponentModel;
13 using System.Collections.Generic;
15 using System.Data.Common;
16 using System.Data.ProviderBase;
17 using System.Data.Sql;
18 using System.Data.SqlTypes;
19 using System.Diagnostics;
21 using System.Globalization;
22 using System.Reflection;
25 using MSS=Microsoft.SqlServer.Server;
27 using Microsoft.SqlServer.Server;
28 using System.Threading.Tasks;
30 internal abstract class DataFeed {
33 internal class StreamDataFeed : DataFeed {
34 internal Stream _source;
36 internal StreamDataFeed(Stream source) {
41 internal class TextDataFeed : DataFeed {
42 internal TextReader _source;
44 internal TextDataFeed(TextReader source) {
49 internal class XmlDataFeed : DataFeed {
50 internal XmlReader _source;
52 internal XmlDataFeed(XmlReader source) {
58 System.ComponentModel.TypeConverterAttribute(typeof(System.Data.SqlClient.SqlParameter.SqlParameterConverter))
60 public sealed partial class SqlParameter : DbParameter, IDbDataParameter, ICloneable {
61 private MetaType _metaType;
63 private SqlCollation _collation;
64 private string _xmlSchemaCollectionDatabase;
65 private string _xmlSchemaCollectionOwningSchema;
66 private string _xmlSchemaCollectionName;
68 private string _udtTypeName;
69 private string _typeName;
70 private Type _udtType;
71 private Exception _udtLoadError;
73 private string _parameterName;
74 private byte _precision;
76 private bool _hasScale; // V1.0 compat, ignore _hasScale
78 private MetaType _internalMetaType;
79 private SqlBuffer _sqlBufferReturnValue;
80 private INullable _valueAsINullable;
81 private bool _isSqlParameterSqlType;
82 private bool _isNull = true;
83 private bool _coercedValueIsSqlType;
84 private bool _coercedValueIsDataFeed;
85 private int _actualSize = -1;
88 /// Column Encryption Cipher Related Metadata.
90 private SqlCipherMetadata _columnEncryptionCipherMetadata;
93 /// Get or set the encryption related metadata of this SqlParameter.
94 /// Should be set to a non-null value only once.
96 internal SqlCipherMetadata CipherMetadata {
98 return _columnEncryptionCipherMetadata;
102 Debug.Assert(_columnEncryptionCipherMetadata == null || value == null,
103 "_columnEncryptionCipherMetadata should be set to a non-null value only once.");
105 _columnEncryptionCipherMetadata = value;
110 /// Indicates if the parameter encryption metadata received by sp_describe_parameter_encryption.
111 /// For unencrypted parameters, the encryption metadata should still be sent (and will indicate
112 /// that no encryption is needed).
114 internal bool HasReceivedMetadata { get; set; }
117 /// Returns the normalization rule version number as a byte
119 internal byte NormalizationRuleVersion {
121 if (_columnEncryptionCipherMetadata != null) {
122 return _columnEncryptionCipherMetadata.NormalizationRuleVersion;
129 public SqlParameter() : base() {
132 [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ] // MDAC 69508
133 public SqlParameter(string parameterName,
134 SqlDbType dbType, int size,
135 ParameterDirection direction, bool isNullable,
136 byte precision, byte scale,
137 string sourceColumn, DataRowVersion sourceVersion,
138 object value) : this() { // V1.0 everything
139 this.ParameterName = parameterName;
140 this.SqlDbType = dbType;
142 this.Direction = direction;
143 this.IsNullable = isNullable;
144 PrecisionInternal = precision;
145 ScaleInternal = scale;
146 this.SourceColumn = sourceColumn;
147 this.SourceVersion = sourceVersion;
150 public SqlParameter(string parameterName,
151 SqlDbType dbType, int size,
152 ParameterDirection direction,
153 byte precision, byte scale,
154 string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping,
156 string xmlSchemaCollectionDatabase, string xmlSchemaCollectionOwningSchema,
157 string xmlSchemaCollectionName
158 ) { // V2.0 everything - round trip all browsable properties + precision/scale
159 this.ParameterName = parameterName;
160 this.SqlDbType = dbType;
162 this.Direction = direction;
163 this.PrecisionInternal = precision;
164 this.ScaleInternal = scale;
165 this.SourceColumn = sourceColumn;
166 this.SourceVersion = sourceVersion;
167 this.SourceColumnNullMapping = sourceColumnNullMapping;
169 this._xmlSchemaCollectionDatabase = xmlSchemaCollectionDatabase;
170 this._xmlSchemaCollectionOwningSchema = xmlSchemaCollectionOwningSchema;
171 this._xmlSchemaCollectionName = xmlSchemaCollectionName;
173 public SqlParameter(string parameterName, SqlDbType dbType) : this() {
174 this.ParameterName = parameterName;
175 this.SqlDbType = dbType;
178 public SqlParameter(string parameterName, object value) : this() {
179 Debug.Assert(!(value is SqlDbType), "use SqlParameter(string, SqlDbType)");
181 this.ParameterName = parameterName;
185 public SqlParameter(string parameterName, SqlDbType dbType, int size) : this() {
186 this.ParameterName = parameterName;
187 this.SqlDbType = dbType;
191 public SqlParameter(string parameterName, SqlDbType dbType, int size, string sourceColumn) : this() {
192 this.ParameterName = parameterName;
193 this.SqlDbType = dbType;
195 this.SourceColumn = sourceColumn;
199 // currently the user can't set this value. it gets set by the returnvalue from tds
201 internal SqlCollation Collation {
213 public SqlCompareOptions CompareInfo {
214 // Bits 21 through 25 represent the CompareInfo
216 SqlCollation collation = _collation;
217 if (null != collation) {
218 return collation.SqlCompareOptions;
220 return SqlCompareOptions.None;
223 SqlCollation collation = _collation;
224 if (null == collation) {
225 _collation = collation = new SqlCollation();
227 if ((value & SqlString.x_iValidSqlCompareOptionMask) != value) {
228 throw ADP.ArgumentOutOfRange("CompareInfo");
230 collation.SqlCompareOptions = value;
235 ResCategoryAttribute(Res.DataCategory_Xml),
236 ResDescriptionAttribute(Res.SqlParameter_XmlSchemaCollectionDatabase),
238 public string XmlSchemaCollectionDatabase {
240 string xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase;
241 return ((xmlSchemaCollectionDatabase != null) ? xmlSchemaCollectionDatabase : ADP.StrEmpty);
244 _xmlSchemaCollectionDatabase = value;
249 ResCategoryAttribute(Res.DataCategory_Xml),
250 ResDescriptionAttribute(Res.SqlParameter_XmlSchemaCollectionOwningSchema),
252 public string XmlSchemaCollectionOwningSchema {
254 string xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema;
255 return ((xmlSchemaCollectionOwningSchema != null) ? xmlSchemaCollectionOwningSchema : ADP.StrEmpty);
258 _xmlSchemaCollectionOwningSchema = value;
263 ResCategoryAttribute(Res.DataCategory_Xml),
264 ResDescriptionAttribute(Res.SqlParameter_XmlSchemaCollectionName),
266 public string XmlSchemaCollectionName {
268 string xmlSchemaCollectionName = _xmlSchemaCollectionName;
269 return ((xmlSchemaCollectionName != null) ? xmlSchemaCollectionName : ADP.StrEmpty);
272 _xmlSchemaCollectionName = value;
278 ResCategoryAttribute(Res.DataCategory_Data),
279 ResDescriptionAttribute(Res.TCE_SqlParameter_ForceColumnEncryption),
281 public bool ForceColumnEncryption {
286 override public DbType DbType {
288 return GetMetaTypeOnly().DbType;
291 MetaType metatype = _metaType;
292 if ((null == metatype) || (metatype.DbType != value) ||
293 // SQLBU 504029: Two special datetime cases for backward compat
294 // DbType.Date and DbType.Time should always be treated as setting DbType.DateTime instead
295 value == DbType.Date ||
296 value == DbType.Time) {
297 PropertyTypeChanging();
298 _metaType = MetaType.GetMetaTypeFromDbType(value);
303 public override void ResetDbType() {
307 internal MetaType InternalMetaType {
309 Debug.Assert(null != _internalMetaType, "null InternalMetaType");
310 return _internalMetaType;
312 set { _internalMetaType = value; }
318 public int LocaleId {
319 // Lowest 20 bits represent LocaleId
321 SqlCollation collation = _collation;
322 if (null != collation) {
323 return collation.LCID;
328 SqlCollation collation = _collation;
329 if (null == collation) {
330 _collation = collation = new SqlCollation();
332 if (value != (SqlCollation.MaskLcid & value)) {
333 throw ADP.ArgumentOutOfRange("LocaleId");
335 collation.LCID = value;
339 private SqlMetaData MetaData {
341 MetaType mt = GetMetaTypeOnly();
345 maxlen = (long)mt.FixedLength;
347 else if (Size > 0 || Size < 0) {
348 maxlen = Size; // Bug Fix: 302768, 302695, 302694, 302693
351 maxlen = MSS.SmiMetaData.GetDefaultForType( mt.SqlDbType ).MaxLength;
353 return new SqlMetaData(this.ParameterName, mt.SqlDbType, maxlen, GetActualPrecision(), GetActualScale(), LocaleId, CompareInfo,
354 XmlSchemaCollectionDatabase, XmlSchemaCollectionOwningSchema, XmlSchemaCollectionName, mt.IsPlp, _udtType);
358 internal bool SizeInferred {
365 /// Get SMI Metadata to write out type_info stream.
367 /// <returns></returns>
368 internal MSS.SmiParameterMetaData GetMetadataForTypeInfo() {
369 ParameterPeekAheadValue peekAhead = null;
371 if (_internalMetaType == null) {
372 _internalMetaType = GetMetaTypeOnly();
375 return MetaDataForSmi(out peekAhead);
378 // IMPORTANT DEVNOTE: This method is being used for parameter encryption functionality, to get the type_info TDS object from SqlParameter.
379 // Please consider impact to that when changing this method. Refer to the callers of SqlParameter.GetMetadataForTypeInfo().
380 internal MSS.SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhead) {
382 MetaType mt = ValidateTypeLengths( true /* Yukon or newer */ );
383 long actualLen = GetActualSize( );
384 long maxLen = this.Size;
386 // GetActualSize returns bytes length, but smi expects char length for
387 // character types, so adjust
389 if ( SqlDbType.NChar == mt.SqlDbType || SqlDbType.NVarChar == mt.SqlDbType ) {
390 actualLen = actualLen / sizeof( char );
393 if ( actualLen > maxLen ) {
398 // Determine maxLength for types that ValidateTypeLengths won't figure out
400 if ( SqlDbType.Binary == mt.SqlDbType || SqlDbType.VarBinary == mt.SqlDbType ) {
401 maxLen = MSS.SmiMetaData.MaxBinaryLength;
403 else if ( SqlDbType.Char == mt.SqlDbType || SqlDbType.VarChar == mt.SqlDbType ) {
404 maxLen = MSS.SmiMetaData.MaxANSICharacters;
406 else if ( SqlDbType.NChar == mt.SqlDbType || SqlDbType.NVarChar == mt.SqlDbType ) {
407 maxLen = MSS.SmiMetaData.MaxUnicodeCharacters;
410 else if (( maxLen > MSS.SmiMetaData.MaxBinaryLength && ( SqlDbType.Binary == mt.SqlDbType || SqlDbType.VarBinary == mt.SqlDbType ))
411 || ( maxLen > MSS.SmiMetaData.MaxANSICharacters && ( SqlDbType.Char == mt.SqlDbType || SqlDbType.VarChar == mt.SqlDbType ))
412 || ( maxLen > MSS.SmiMetaData.MaxUnicodeCharacters&& ( SqlDbType.NChar == mt.SqlDbType || SqlDbType.NVarChar == mt.SqlDbType )) ) {
417 int localeId = LocaleId;
418 if ( 0 == localeId && mt.IsCharType ) {
419 object value = GetCoercedValue();
420 if ( value is SqlString && !( (SqlString)value ).IsNull ) {
421 localeId = ( (SqlString)value ).LCID;
424 localeId = System.Globalization.CultureInfo.CurrentCulture.LCID;
428 SqlCompareOptions compareOpts = CompareInfo;
429 if ( 0 == compareOpts && mt.IsCharType ) {
430 object value = GetCoercedValue();
431 if ( value is SqlString && !( (SqlString)value ).IsNull ) {
432 compareOpts = ( (SqlString)value ).SqlCompareOptions;
435 compareOpts = MSS.SmiMetaData.GetDefaultForType( mt.SqlDbType ).CompareOptions;
439 string typeSpecificNamePart1 = null;
440 string typeSpecificNamePart2 = null;
441 string typeSpecificNamePart3 = null;
443 if (SqlDbType.Xml == mt.SqlDbType) {
444 typeSpecificNamePart1 = this.XmlSchemaCollectionDatabase;
445 typeSpecificNamePart2 = this.XmlSchemaCollectionOwningSchema;
446 typeSpecificNamePart3 = this.XmlSchemaCollectionName;
448 else if (SqlDbType.Udt == mt.SqlDbType || (SqlDbType.Structured == mt.SqlDbType && !ADP.IsEmpty(this.TypeName))) {
449 // Split the input name. The type name is specified as single 3 part name.
450 // NOTE: ParseTypeName throws if format is incorrect
452 if (SqlDbType.Udt == mt.SqlDbType) {
453 names = ParseTypeName(this.UdtTypeName, true /* is UdtTypeName */);
456 names = ParseTypeName(this.TypeName, false /* not UdtTypeName */);
459 if (1 == names.Length) {
460 typeSpecificNamePart3 = names[0];
462 else if (2 == names.Length) {
463 typeSpecificNamePart2 = names[0];
464 typeSpecificNamePart3 = names[1];
466 else if (3 == names.Length) {
467 typeSpecificNamePart1 = names[0];
468 typeSpecificNamePart2 = names[1];
469 typeSpecificNamePart3 = names[2];
472 throw ADP.ArgumentOutOfRange("names");
475 if ((!ADP.IsEmpty(typeSpecificNamePart1) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart1.Length)
476 || (!ADP.IsEmpty(typeSpecificNamePart2) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart2.Length)
477 || (!ADP.IsEmpty(typeSpecificNamePart3) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart3.Length)) {
478 throw ADP.ArgumentOutOfRange("names");
482 byte precision = GetActualPrecision();
483 byte scale = GetActualScale();
485 // precision for decimal types may still need adjustment.
486 if ( SqlDbType.Decimal == mt.SqlDbType ) {
487 if ( 0 == precision ) {
488 precision = TdsEnums.DEFAULT_NUMERIC_PRECISION;
492 // Sub-field determination
493 List<SmiExtendedMetaData> fields = null;
494 MSS.SmiMetaDataPropertyCollection extendedProperties = null;
495 if (SqlDbType.Structured == mt.SqlDbType) {
496 GetActualFieldsAndProperties(out fields, out extendedProperties, out peekAhead);
499 return new MSS.SmiParameterMetaData( mt.SqlDbType,
505 null, // Udt type not used for parameters
506 SqlDbType.Structured == mt.SqlDbType,
509 this.ParameterNameFixed,
510 typeSpecificNamePart1,
511 typeSpecificNamePart2,
512 typeSpecificNamePart3,
516 internal bool ParamaterIsSqlType {
518 return _isSqlParameterSqlType;
521 _isSqlParameterSqlType = value;
526 ResCategoryAttribute(Res.DataCategory_Data),
527 ResDescriptionAttribute(Res.SqlParameter_ParameterName),
529 override public string ParameterName {
531 string parameterName = _parameterName;
532 return ((null != parameterName) ? parameterName : ADP.StrEmpty);
535 if (ADP.IsEmpty(value) || (value.Length < TdsEnums.MAX_PARAMETER_NAME_LENGTH)
536 || (('@' == value[0]) && (value.Length <= TdsEnums.MAX_PARAMETER_NAME_LENGTH))) {
537 if (_parameterName != value) {
539 _parameterName = value;
543 throw SQL.InvalidParameterNameLength(value);
548 internal string ParameterNameFixed {
550 string parameterName = ParameterName;
551 if ((0 < parameterName.Length) && ('@' != parameterName[0])) {
552 parameterName = "@" + parameterName;
554 Debug.Assert(parameterName.Length <= TdsEnums.MAX_PARAMETER_NAME_LENGTH, "parameter name too long");
555 return parameterName;
559 [DefaultValue((Byte)0)] // MDAC 65862
560 [ResCategoryAttribute(Res.DataCategory_Data)]
561 [ResDescriptionAttribute(Res.DbDataParameter_Precision)]
562 public new Byte Precision {
564 return PrecisionInternal;
567 PrecisionInternal = value;
571 internal byte PrecisionInternal {
573 byte precision = _precision;
574 SqlDbType dbtype = GetMetaSqlDbTypeOnly();
575 if ((0 == precision) && (SqlDbType.Decimal == dbtype)) {
576 precision = ValuePrecision(SqlValue);
581 SqlDbType sqlDbType = SqlDbType;
582 if (sqlDbType == SqlDbType.Decimal && value > TdsEnums.MAX_NUMERIC_PRECISION) {
583 throw SQL.PrecisionValueOutOfRange(value);
585 if (_precision != value) {
592 private bool ShouldSerializePrecision() {
593 return (0 != _precision);
596 [DefaultValue((Byte)0)] // MDAC 65862
597 [ResCategoryAttribute(Res.DataCategory_Data)]
598 [ResDescriptionAttribute(Res.DbDataParameter_Scale)]
599 public new Byte Scale {
601 return ScaleInternal;
604 ScaleInternal = value;
607 internal byte ScaleInternal {
610 SqlDbType dbtype = GetMetaSqlDbTypeOnly();
611 if ((0 == scale) && (SqlDbType.Decimal == dbtype)) {
612 scale = ValueScale(SqlValue);
617 if (_scale != value || !_hasScale) {
621 _actualSize = -1; // Invalidate actual size such that it is re-calculated
626 private bool ShouldSerializeScale() {
627 return (0 != _scale); // V1.0 compat, ignore _hasScale
631 RefreshProperties(RefreshProperties.All),
632 ResCategoryAttribute(Res.DataCategory_Data),
633 ResDescriptionAttribute(Res.SqlParameter_SqlDbType),
634 System.Data.Common.DbProviderSpecificTypePropertyAttribute(true),
636 public SqlDbType SqlDbType {
638 return GetMetaTypeOnly().SqlDbType;
641 MetaType metatype = _metaType;
643 // We didn't want to expose SmallVarBinary on SqlDbType so we
644 // stuck it at the end of SqlDbType in v1.0, except that now
645 // we have new data types after that and it's smack dab in the
646 // middle of the valid range. To prevent folks from setting
647 // this invalid value we have to have this code here until we
648 // can take the time to fix it later.
649 if ((SqlDbType)TdsEnums.SmallVarBinary == value) {
650 throw SQL.InvalidSqlDbType(value);
652 if ((null == metatype) || (metatype.SqlDbType != value)) {
653 PropertyTypeChanging();
654 _metaType = MetaType.GetMetaTypeFromSqlDbType(value, value == SqlDbType.Structured);
659 private bool ShouldSerializeSqlDbType() {
660 return (null != _metaType);
663 public void ResetSqlDbType() {
664 if (null != _metaType) {
665 PropertyTypeChanging();
672 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
674 public object SqlValue {
676 if (_udtLoadError != null) { // SQL BU DT 329981
680 if (_value != null) {
681 if (_value == DBNull.Value) {
682 return MetaType.GetNullSqlValue(GetMetaTypeOnly().SqlType);
684 if (_value is INullable) {
688 // SQLBU 503165: for Date and DateTime2, return the CLR object directly without converting it to a SqlValue
689 // SQLBU 527900: GetMetaTypeOnly() will convert _value to a string in the case of char or char[], so only check
690 // the SqlDbType for DateTime. This is the only case when we might return the CLR value directly.
691 if (_value is DateTime) {
692 SqlDbType sqlDbType = GetMetaTypeOnly().SqlDbType;
693 if (sqlDbType == SqlDbType.Date || sqlDbType == SqlDbType.DateTime2) {
698 return (MetaType.GetSqlValueFromComVariant(_value));
700 else if (_sqlBufferReturnValue != null) {
701 return _sqlBufferReturnValue.SqlValue;
712 EditorBrowsableAttribute(EditorBrowsableState.Advanced)
714 public String UdtTypeName {
716 string typeName = _udtTypeName;
717 return ((null != typeName) ? typeName : ADP.StrEmpty);
720 _udtTypeName = value;
726 EditorBrowsableAttribute(EditorBrowsableState.Advanced)
728 public String TypeName {
730 string typeName = _typeName;
731 return ((null != typeName) ? typeName : ADP.StrEmpty);
739 RefreshProperties(RefreshProperties.All),
740 ResCategoryAttribute(Res.DataCategory_Data),
741 ResDescriptionAttribute(Res.DbParameter_Value),
742 TypeConverterAttribute(typeof(StringConverter)),
744 override public object Value { // V1.2.3300, XXXParameter V1.0.3300
746 if (_udtLoadError != null) { // SQL BU DT 329981
750 if (_value != null) {
753 else if (_sqlBufferReturnValue != null) {
754 if (ParamaterIsSqlType) {
755 return _sqlBufferReturnValue.SqlValue;
757 return _sqlBufferReturnValue.Value;
763 _sqlBufferReturnValue = null;
764 _coercedValue = null;
765 _valueAsINullable = _value as INullable;
766 _isSqlParameterSqlType = (_valueAsINullable != null);
767 _isNull = ((_value == null) || (_value == DBNull.Value) || ((_isSqlParameterSqlType) && (_valueAsINullable.IsNull)));
768 _udtLoadError = null;
773 internal INullable ValueAsINullable {
775 return _valueAsINullable;
779 internal bool IsNull {
781 // NOTE: Udts can change their value any time
782 if (_internalMetaType.SqlDbType == Data.SqlDbType.Udt) {
783 _isNull = ((_value == null) || (_value == DBNull.Value) || ((_isSqlParameterSqlType) && (_valueAsINullable.IsNull)));
790 // always returns data in bytes - except for non-unicode chars, which will be in number of chars
792 internal int GetActualSize() {
793 MetaType mt = InternalMetaType;
794 SqlDbType actualType = mt.SqlDbType;
795 // NOTE: Users can change the Udt at any time, so we may need to recalculate
796 if ((_actualSize == -1) || (actualType == Data.SqlDbType.Udt)) {
798 object val = GetCoercedValue();
799 bool isSqlVariant = false;
802 if (IsNull && !mt.IsVarTime) {
806 // if this is a backend SQLVariant type, then infer the TDS type from the SQLVariant type
807 if (actualType == SqlDbType.Variant) {
808 mt = MetaType.GetMetaTypeFromValue(val, streamAllowed: false);
809 actualType = MetaType.GetSqlDataType(mt.TDSType, 0 /*no user type*/, 0 /*non-nullable type*/).SqlDbType;
814 _actualSize = mt.FixedLength;
817 // @hack: until we have ForceOffset behavior we have the following semantics:
818 // @hack: if the user supplies a Size through the Size propeprty or constructor,
819 // @hack: we only send a MAX of Size bytes over. If the actualSize is < Size, then
820 // @hack: we send over actualSize
823 // get the actual length of the data, in bytes
824 switch (actualType) {
825 case SqlDbType.NChar:
826 case SqlDbType.NVarChar:
827 case SqlDbType.NText:
830 coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0;
831 _actualSize = (ShouldSerializeSize() ? Size : 0);
832 _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize);
833 if (_actualSize == -1)
834 _actualSize = coercedSize;
839 case SqlDbType.VarChar:
842 // for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size
843 coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0;
844 _actualSize = (ShouldSerializeSize() ? Size : 0);
845 _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize);
846 if (_actualSize == -1)
847 _actualSize = coercedSize;
850 case SqlDbType.Binary:
851 case SqlDbType.VarBinary:
852 case SqlDbType.Image:
853 case SqlDbType.Timestamp:
854 coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (BinarySize(val, _coercedValueIsSqlType)) : 0;
855 _actualSize = (ShouldSerializeSize() ? Size : 0);
856 _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize);
857 if (_actualSize == -1)
858 _actualSize = coercedSize;
861 //we assume that the object is UDT
863 //call the static function
864 coercedSize = AssemblyCache.GetLength(val);
867 case SqlDbType.Structured:
871 _actualSize = (isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale()));
873 case SqlDbType.DateTime2:
874 // Date in number of days (3 bytes) + time
875 _actualSize = 3 + (isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale()));
877 case SqlDbType.DateTimeOffset:
878 // Date in days (3 bytes) + offset in minutes (2 bytes) + time
879 _actualSize = 5 + (isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale()));
882 Debug.Assert(false, "Unknown variable length type!");
886 // don't even send big values over to the variant
887 if (isSqlVariant && (coercedSize > TdsEnums.TYPE_SIZE_LIMIT))
888 throw SQL.ParameterInvalidVariant(this.ParameterName);
895 object ICloneable.Clone() {
896 return new SqlParameter(this);
899 // Coerced Value is also used in SqlBulkCopy.ConvertValue(object value, _SqlMetaData metadata)
900 internal static object CoerceValue(object value, MetaType destinationType, out bool coercedToDataFeed, out bool typeChanged, bool allowStreaming = true) {
901 Debug.Assert(!(value is DataFeed), "Value provided should not already be a data feed");
902 Debug.Assert(!ADP.IsNull(value), "Value provided should not be null");
903 Debug.Assert(null != destinationType, "null destinationType");
905 coercedToDataFeed = false;
907 Type currentType = value.GetType();
909 if ((typeof(object) != destinationType.ClassType) &&
910 (currentType != destinationType.ClassType) &&
911 ((currentType != destinationType.SqlType) || (SqlDbType.Xml == destinationType.SqlDbType))) { // Special case for Xml types (since we need to convert SqlXml into a string)
913 // Assume that the type changed
915 if ((typeof(string) == destinationType.ClassType)) {
916 // For Xml data, destination Type is always string
917 if (typeof(SqlXml) == currentType) {
918 value = MetaType.GetStringFromXml((XmlReader)(((SqlXml)value).CreateReader()));
920 else if (typeof(SqlString) == currentType) {
921 typeChanged = false; // Do nothing
923 else if (typeof(XmlReader).IsAssignableFrom(currentType)) {
924 if (allowStreaming) {
925 coercedToDataFeed = true;
926 value = new XmlDataFeed((XmlReader)value);
929 value = MetaType.GetStringFromXml((XmlReader)value);
932 else if (typeof(char[]) == currentType) {
933 value = new string((char[])value);
935 else if (typeof(SqlChars) == currentType) {
936 value = new string(((SqlChars)value).Value);
938 else if (value is TextReader && allowStreaming) {
939 coercedToDataFeed = true;
940 value = new TextDataFeed((TextReader)value);
943 value = Convert.ChangeType(value, destinationType.ClassType, (IFormatProvider)null);
946 else if ((DbType.Currency == destinationType.DbType) && (typeof(string) == currentType)) {
947 value = Decimal.Parse((string)value, NumberStyles.Currency, (IFormatProvider)null); // WebData 99376
949 else if ((typeof(SqlBytes) == currentType) && (typeof(byte[]) == destinationType.ClassType)) {
950 typeChanged = false; // Do nothing
952 else if ((typeof(string) == currentType) && (SqlDbType.Time == destinationType.SqlDbType)) {
953 value = TimeSpan.Parse((string)value);
955 else if ((typeof(string) == currentType) && (SqlDbType.DateTimeOffset == destinationType.SqlDbType)) {
956 value = DateTimeOffset.Parse((string)value, (IFormatProvider)null);
958 else if ((typeof(DateTime) == currentType) && (SqlDbType.DateTimeOffset == destinationType.SqlDbType)) {
959 value = new DateTimeOffset((DateTime)value);
961 else if (TdsEnums.SQLTABLE == destinationType.TDSType &&
962 (value is DataTable ||
963 value is DbDataReader ||
964 value is System.Collections.Generic.IEnumerable<SqlDataRecord>)) {
965 // no conversion for TVPs.
968 else if (destinationType.ClassType==typeof(byte[]) && value is Stream && allowStreaming) {
969 coercedToDataFeed = true;
970 value = new StreamDataFeed((Stream)value);
973 value = Convert.ChangeType(value, destinationType.ClassType, (IFormatProvider)null);
978 if (!ADP.IsCatchableExceptionType(e)) {
982 throw ADP.ParameterConversionFailed(value, destinationType.ClassType, e); // WebData 75433
986 Debug.Assert(allowStreaming || !coercedToDataFeed, "Streaming is not allowed, but type was coerced into a data feed");
987 Debug.Assert(value.GetType() == currentType ^ typeChanged, "Incorrect value for typeChanged");
991 internal void FixStreamDataForNonPLP() {
992 object value = GetCoercedValue();
993 AssertCachedPropertiesAreValid();
994 if (!_coercedValueIsDataFeed) {
998 _coercedValueIsDataFeed = false;
1000 if (value is TextDataFeed) {
1002 char[] buffer = new char[Size];
1003 int nRead = ((TextDataFeed)value)._source.ReadBlock(buffer, 0, Size);
1004 CoercedValue = new string(buffer, 0, nRead);
1007 CoercedValue = ((TextDataFeed)value)._source.ReadToEnd();
1012 if (value is StreamDataFeed) {
1014 byte[] buffer = new byte[Size];
1016 Stream sourceStream = ((StreamDataFeed)value)._source;
1017 while (totalRead < Size) {
1018 int nRead = sourceStream.Read(buffer, totalRead, Size - totalRead);
1024 if (totalRead < Size) {
1025 Array.Resize(ref buffer, totalRead);
1027 CoercedValue = buffer;
1030 MemoryStream ms = new MemoryStream();
1031 ((StreamDataFeed)value)._source.CopyTo(ms);
1032 CoercedValue = ms.ToArray();
1037 if (value is XmlDataFeed) {
1038 CoercedValue = MetaType.GetStringFromXml(((XmlDataFeed)value)._source);
1042 // We should have returned before reaching here
1043 Debug.Assert(false, "_coercedValueIsDataFeed was true, but the value was not a known DataFeed type");
1046 private void CloneHelper(SqlParameter destination) {
1047 CloneHelperCore(destination);
1048 destination._metaType = _metaType;
1049 destination._collation = _collation;
1050 destination._xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase;
1051 destination._xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema;
1052 destination._xmlSchemaCollectionName = _xmlSchemaCollectionName;
1053 destination._udtTypeName = _udtTypeName;
1054 destination._typeName = _typeName;
1055 destination._udtLoadError = _udtLoadError;
1057 destination._parameterName = _parameterName;
1058 destination._precision = _precision;
1059 destination._scale = _scale;
1060 destination._sqlBufferReturnValue = _sqlBufferReturnValue;
1061 destination._isSqlParameterSqlType = _isSqlParameterSqlType;
1062 destination._internalMetaType = _internalMetaType;
1063 destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem
1064 destination._valueAsINullable = _valueAsINullable;
1065 destination._isNull = _isNull;
1066 destination._coercedValueIsDataFeed = _coercedValueIsDataFeed;
1067 destination._coercedValueIsSqlType = _coercedValueIsSqlType;
1068 destination._actualSize = _actualSize;
1069 destination.ForceColumnEncryption = ForceColumnEncryption;
1072 internal byte GetActualPrecision() {
1073 return ShouldSerializePrecision() ? PrecisionInternal: ValuePrecision(CoercedValue);
1076 internal byte GetActualScale() {
1077 if (ShouldSerializeScale()) {
1078 return ScaleInternal;
1081 // issue: how could a user specify 0 as the actual scale?
1082 if (GetMetaTypeOnly().IsVarTime) {
1083 return TdsEnums.DEFAULT_VARTIME_SCALE;
1085 return ValueScale(CoercedValue);
1088 internal int GetParameterSize() {
1089 return ShouldSerializeSize() ? Size : ValueSize(CoercedValue);
1092 private void GetActualFieldsAndProperties(out List<MSS.SmiExtendedMetaData> fields, out SmiMetaDataPropertyCollection props, out ParameterPeekAheadValue peekAhead) {
1097 object value = GetCoercedValue();
1098 if (value is DataTable) {
1099 DataTable dt = value as DataTable;
1100 if (dt.Columns.Count <= 0) {
1101 throw SQL.NotEnoughColumnsInStructuredType();
1103 fields = new List<MSS.SmiExtendedMetaData>(dt.Columns.Count);
1104 bool[] keyCols = new bool[dt.Columns.Count];
1105 bool hasKey = false;
1107 // set up primary key as unique key list
1108 // do this prior to general metadata loop to favor the primary key
1109 if (null != dt.PrimaryKey && 0 < dt.PrimaryKey.Length) {
1110 foreach(DataColumn col in dt.PrimaryKey) {
1111 keyCols[col.Ordinal] = true;
1116 for(int i=0; i<dt.Columns.Count; i++) {
1117 fields.Add(MSS.MetaDataUtilsSmi.SmiMetaDataFromDataColumn(dt.Columns[i], dt));
1119 // DataColumn uniqueness is only for a single column, so don't add
1120 // more than one. (keyCols.Count first for assumed minimal perf benefit)
1121 if (!hasKey && dt.Columns[i].Unique) {
1127 // Add unique key property, if any found.
1129 props = new SmiMetaDataPropertyCollection();
1130 props[MSS.SmiPropertySelector.UniqueKey] = new MSS.SmiUniqueKeyProperty(new List<bool>(keyCols));
1133 else if (value is SqlDataReader) {
1134 fields = new List<MSS.SmiExtendedMetaData>(((SqlDataReader)value).GetInternalSmiMetaData());
1135 if (fields.Count <= 0) {
1136 throw SQL.NotEnoughColumnsInStructuredType();
1139 bool[] keyCols = new bool[fields.Count];
1140 bool hasKey = false;
1141 for(int i=0; i<fields.Count; i++) {
1142 MSS.SmiQueryMetaData qmd = fields[i] as MSS.SmiQueryMetaData;
1143 if (null != qmd && !qmd.IsKey.IsNull && qmd.IsKey.Value) {
1149 // Add unique key property, if any found.
1151 props = new SmiMetaDataPropertyCollection();
1152 props[MSS.SmiPropertySelector.UniqueKey] = new MSS.SmiUniqueKeyProperty(new List<bool>(keyCols));
1155 else if (value is IEnumerable<SqlDataRecord>) {
1156 // must grab the first record of the enumerator to get the metadata
1157 IEnumerator<MSS.SqlDataRecord> enumerator = ((IEnumerable<MSS.SqlDataRecord>) value).GetEnumerator();
1158 MSS.SqlDataRecord firstRecord = null;
1160 // no need for fields if there's no rows or no columns -- we'll be sending a null instance anyway.
1161 if (enumerator.MoveNext()) {
1162 firstRecord = enumerator.Current;
1163 int fieldCount = firstRecord.FieldCount;
1164 if (0 < fieldCount) {
1165 // It's valid! Grab those fields.
1166 bool[] keyCols = new bool[fieldCount];
1167 bool[] defaultFields = new bool[fieldCount];
1168 bool[] sortOrdinalSpecified = new bool[fieldCount];
1169 int maxSortOrdinal = -1; // largest sort ordinal seen, used to optimize locating holes in the list
1170 bool hasKey = false;
1171 bool hasDefault = false;
1173 SmiOrderProperty.SmiColumnOrder[] sort = new SmiOrderProperty.SmiColumnOrder[fieldCount];
1174 fields = new List<MSS.SmiExtendedMetaData>(fieldCount);
1175 for (int i = 0; i < fieldCount; i++) {
1176 SqlMetaData colMeta = firstRecord.GetSqlMetaData(i);
1177 fields.Add(MSS.MetaDataUtilsSmi.SqlMetaDataToSmiExtendedMetaData(colMeta));
1178 if (colMeta.IsUniqueKey) {
1183 if (colMeta.UseServerDefault) {
1184 defaultFields[i] = true;
1188 sort[i].Order = colMeta.SortOrder;
1189 if (SortOrder.Unspecified != colMeta.SortOrder) {
1190 // SqlMetaData takes care of checking for negative sort ordinals with specified sort order
1192 // bail early if there's no way sort order could be monotonically increasing
1193 if (fieldCount <= colMeta.SortOrdinal) {
1194 throw SQL.SortOrdinalGreaterThanFieldCount(i, colMeta.SortOrdinal);
1197 // Check to make sure we haven't seen this ordinal before
1198 if (sortOrdinalSpecified[colMeta.SortOrdinal]) {
1199 throw SQL.DuplicateSortOrdinal(colMeta.SortOrdinal);
1202 sort[i].SortOrdinal = colMeta.SortOrdinal;
1203 sortOrdinalSpecified[colMeta.SortOrdinal] = true;
1204 if (colMeta.SortOrdinal > maxSortOrdinal) {
1205 maxSortOrdinal = colMeta.SortOrdinal;
1212 props = new SmiMetaDataPropertyCollection();
1213 props[MSS.SmiPropertySelector.UniqueKey] = new MSS.SmiUniqueKeyProperty(new List<bool>(keyCols));
1217 // May have already created props list in unique key handling
1218 if (null == props) {
1219 props = new SmiMetaDataPropertyCollection();
1222 props[MSS.SmiPropertySelector.DefaultFields] = new MSS.SmiDefaultFieldsProperty(new List<bool>(defaultFields));
1225 if (0 < sortCount) {
1226 // validate monotonically increasing sort order.
1227 // Since we already checked for duplicates, we just need
1228 // to watch for values outside of the sortCount range.
1229 if (maxSortOrdinal >= sortCount) {
1230 // there is at least one hole, find the first one
1232 for (i = 0; i < sortCount; i++) {
1233 if (!sortOrdinalSpecified[i]) {
1237 Debug.Assert(i < sortCount, "SqlParameter.GetActualFieldsAndProperties: SortOrdinal hole-finding algorithm failed!");
1238 throw SQL.MissingSortOrdinal(i);
1241 // May have already created props list
1242 if (null == props) {
1243 props = new SmiMetaDataPropertyCollection();
1246 props[MSS.SmiPropertySelector.SortOrder] = new MSS.SmiOrderProperty(
1247 new List<SmiOrderProperty.SmiColumnOrder>(sort));
1250 // pack it up so we don't have to rewind to send the first value
1251 peekAhead = new ParameterPeekAheadValue();
1252 peekAhead.Enumerator = enumerator;
1253 peekAhead.FirstRecord = firstRecord;
1255 // now that it's all packaged, make sure we don't dispose it.
1259 throw SQL.NotEnoughColumnsInStructuredType();
1263 throw SQL.IEnumerableOfSqlDataRecordHasNoRows();
1267 if (enumerator != null) {
1268 enumerator.Dispose();
1272 else if (value is DbDataReader) {
1273 DataTable schema = ((DbDataReader)value).GetSchemaTable();
1274 if (schema.Rows.Count <= 0) {
1275 throw SQL.NotEnoughColumnsInStructuredType();
1278 int fieldCount = schema.Rows.Count;
1279 fields = new List<MSS.SmiExtendedMetaData>(fieldCount);
1280 bool[] keyCols = new bool[fieldCount];
1281 bool hasKey = false;
1282 int ordinalForIsKey = schema.Columns[SchemaTableColumn.IsKey].Ordinal;
1283 int ordinalForColumnOrdinal = schema.Columns[SchemaTableColumn.ColumnOrdinal].Ordinal;
1284 // Extract column metadata
1285 for(int rowOrdinal=0; rowOrdinal<fieldCount; rowOrdinal++) {
1286 DataRow row = schema.Rows[rowOrdinal];
1287 SmiExtendedMetaData candidateMd = MSS.MetaDataUtilsSmi.SmiMetaDataFromSchemaTableRow(row);
1289 // Determine destination ordinal. Allow for ordinal not specified by assuming rowOrdinal *is* columnOrdinal
1290 // in that case, but don't worry about mix-and-match of the two techniques
1291 int columnOrdinal = rowOrdinal;
1292 if (!row.IsNull(ordinalForColumnOrdinal)) {
1293 columnOrdinal = (int) row[ordinalForColumnOrdinal];
1296 // After this point, things we are creating (keyCols, fields) should be accessed by columnOrdinal
1297 // while the source should just be accessed via "row".
1299 // Watch for out-of-range ordinals
1300 if (columnOrdinal >= fieldCount || columnOrdinal < 0) {
1301 throw SQL.InvalidSchemaTableOrdinals();
1304 // extend empty space if out-of-order ordinal
1305 while (columnOrdinal > fields.Count) {
1309 // Now add the candidate to the list
1310 if (fields.Count == columnOrdinal) {
1311 fields.Add(candidateMd);
1314 // Disallow two columns using the same ordinal (even if due to mixing null and non-null columnOrdinals)
1315 if (fields[columnOrdinal] != null) {
1316 throw SQL.InvalidSchemaTableOrdinals();
1319 // Don't use insert, since it shifts all later columns down a notch
1320 fields[columnOrdinal] = candidateMd;
1323 // Propogate key information
1324 if (!row.IsNull(ordinalForIsKey) && (bool)row[ordinalForIsKey]) {
1325 keyCols[columnOrdinal] = true;
1332 // Above loop logic prevents holes since:
1333 // 1) loop processes fieldcount # of columns
1334 // 2) no ordinals outside continuous range from 0 to fieldcount - 1 are allowed
1335 // 3) no duplicate ordinals are allowed
1336 // But assert no holes to be sure.
1337 foreach (SmiExtendedMetaData md in fields) {
1338 Debug.Assert(null != md, "Shouldn't be able to have holes, since original loop algorithm prevents such.");
1342 // Add unique key property, if any defined.
1344 props = new SmiMetaDataPropertyCollection();
1345 props[MSS.SmiPropertySelector.UniqueKey] = new MSS.SmiUniqueKeyProperty(new List<bool>(keyCols));
1350 internal object GetCoercedValue() {
1351 // NOTE: User can change the Udt at any time
1352 if ((null == _coercedValue) || (_internalMetaType.SqlDbType == Data.SqlDbType.Udt)) { // will also be set during parameter Validation
1353 bool isDataFeed = Value is DataFeed;
1354 if ((IsNull) || (isDataFeed)) {
1355 // No coercion is done for DataFeeds and Nulls
1356 _coercedValue = Value;
1357 _coercedValueIsSqlType = (_coercedValue==null) ? false: _isSqlParameterSqlType; // set to null for output parameters that keeps _isSqlParameterSqlType
1358 _coercedValueIsDataFeed = isDataFeed;
1359 _actualSize = IsNull ? 0 : -1;
1363 _coercedValue = CoerceValue(Value, _internalMetaType, out _coercedValueIsDataFeed, out typeChanged);
1364 _coercedValueIsSqlType = ((_isSqlParameterSqlType) && (!typeChanged)); // Type changed always results in a CLR type
1368 AssertCachedPropertiesAreValid();
1369 return _coercedValue;
1372 internal bool CoercedValueIsSqlType {
1374 if (null == _coercedValue) {
1377 AssertCachedPropertiesAreValid();
1378 return _coercedValueIsSqlType;
1382 internal bool CoercedValueIsDataFeed {
1384 if (null == _coercedValue) {
1387 AssertCachedPropertiesAreValid();
1388 return _coercedValueIsDataFeed;
1392 [Conditional("DEBUG")]
1393 internal void AssertCachedPropertiesAreValid()
1395 AssertPropertiesAreValid(_coercedValue, _coercedValueIsSqlType, _coercedValueIsDataFeed, IsNull);
1398 [Conditional("DEBUG")]
1399 internal void AssertPropertiesAreValid(object value, bool? isSqlType = null, bool? isDataFeed = null, bool? isNull = null)
1401 Debug.Assert(!isSqlType.HasValue || (isSqlType.Value == (value is INullable)), "isSqlType is incorrect");
1402 Debug.Assert(!isDataFeed.HasValue || (isDataFeed.Value == (value is DataFeed)), "isDataFeed is incorrect");
1403 Debug.Assert(!isNull.HasValue || (isNull.Value == ADP.IsNull(value)), "isNull is incorrect");
1406 private SqlDbType GetMetaSqlDbTypeOnly() {
1407 MetaType metaType = _metaType;
1408 if (null == metaType) { // infer the type from the value
1409 metaType = MetaType.GetDefaultMetaType();
1411 return metaType.SqlDbType;
1414 // This may not be a good thing to do in case someone overloads the parameter type but I
1415 // don't want to go from SqlDbType -> metaType -> TDSType
1416 private MetaType GetMetaTypeOnly() {
1417 if (null != _metaType) {
1420 if (null != _value && DBNull.Value != _value) {
1421 // We have a value set by the user then just use that value
1422 // char and char[] are not directly supported so we convert those values to string
1423 Type valueType = _value.GetType ();
1424 if (typeof(char) == valueType) {
1425 _value = _value.ToString();
1426 valueType = typeof (string);
1428 else if (typeof(char[]) == valueType) {
1429 _value = new string((char[])_value);
1430 valueType = typeof (string);
1432 return MetaType.GetMetaTypeFromType(valueType);
1434 else if (null != _sqlBufferReturnValue) { // value came back from the server
1435 Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType (_isSqlParameterSqlType);
1436 if (null != valueType) {
1437 return MetaType.GetMetaTypeFromType(valueType);
1440 return MetaType.GetDefaultMetaType();
1443 internal void Prepare(SqlCommand cmd) { // MDAC 67063
1444 if (null == _metaType) {
1445 throw ADP.PrepareParameterType(cmd);
1447 else if (!ShouldSerializeSize() && !_metaType.IsFixed) {
1448 throw ADP.PrepareParameterSize(cmd);
1450 else if ( (!ShouldSerializePrecision() && !ShouldSerializeScale()) && (_metaType.SqlDbType == SqlDbType.Decimal) ) {
1451 throw ADP.PrepareParameterScale(cmd, SqlDbType.ToString());
1455 private void PropertyChanging () {
1456 _internalMetaType = null;
1459 private void PropertyTypeChanging () {
1461 CoercedValue = null;
1464 internal void SetSqlBuffer (SqlBuffer buff){
1465 _sqlBufferReturnValue = buff;
1467 _coercedValue = null;
1468 _isNull = _sqlBufferReturnValue.IsNull;
1469 _coercedValueIsDataFeed = false;
1470 _coercedValueIsSqlType = false;
1471 _udtLoadError = null;
1475 internal void SetUdtLoadError(Exception e) { // SQL BU DT 329981
1479 internal void Validate(int index, bool isCommandProc) {
1480 MetaType metaType = GetMetaTypeOnly();
1481 _internalMetaType = metaType;
1483 // NOTE: (General Criteria): SqlParameter does a Size Validation check and would fail if the size is 0.
1484 // This condition filters all scenarios where we view a valid size 0.
1485 if (ADP.IsDirection(this, ParameterDirection.Output) &&
1486 !ADP.IsDirection(this, ParameterDirection.ReturnValue) && // SQL BU DT 372370
1487 (!metaType.IsFixed) &&
1488 !ShouldSerializeSize() &&
1489 ((null == _value) || Convert.IsDBNull(_value)) &&
1490 (SqlDbType != SqlDbType.Timestamp) &&
1491 (SqlDbType != SqlDbType.Udt) &&
1494 (SqlDbType != SqlDbType.Xml) &&
1495 !metaType.IsVarTime) {
1497 throw ADP.UninitializedParameterSize(index, metaType.ClassType);
1500 if (metaType.SqlDbType != SqlDbType.Udt && Direction != ParameterDirection.Output) {
1504 //check if the UdtTypeName is specified for Udt params
1505 if (metaType.SqlDbType == SqlDbType.Udt) {
1506 if (ADP.IsEmpty (UdtTypeName))
1507 throw SQL.MustSetUdtTypeNameForUdtParams ();
1509 else if (!ADP.IsEmpty (UdtTypeName)) {
1510 throw SQL.UnexpectedUdtTypeNameForNonUdtParams();
1513 // Validate structured-type-specific details.
1514 if (metaType.SqlDbType == SqlDbType.Structured) {
1515 if (!isCommandProc && ADP.IsEmpty (TypeName))
1516 throw SQL.MustSetTypeNameForParam(metaType.TypeName, this.ParameterName);
1518 if (ParameterDirection.Input != this.Direction) {
1519 throw SQL.UnsupportedTVPOutputParameter(this.Direction, this.ParameterName);
1522 if (DBNull.Value == GetCoercedValue()) {
1523 throw SQL.DBNullNotSupportedForTVPValues(this.ParameterName);
1526 else if (!ADP.IsEmpty (TypeName)) {
1527 throw SQL.UnexpectedTypeNameForNonStructParams(this.ParameterName);
1531 // func will change type to that with a 4 byte length if the type has a two
1532 // byte length and a parameter length > than that expressable in 2 bytes
1533 internal MetaType ValidateTypeLengths(bool yukonOrNewer) {
1534 MetaType mt = InternalMetaType;
1535 // MDAC bug #50839 + #52829 : Since the server will automatically reject any
1536 // char, varchar, binary, varbinary, nchar, or nvarchar parameter that has a
1537 // byte sizeInCharacters > 8000 bytes, we promote the parameter to image, text, or ntext. This
1538 // allows the user to specify a parameter type using a COM+ datatype and be able to
1539 // use that parameter against a BLOB column.
1540 if ((SqlDbType.Udt != mt.SqlDbType) && (false == mt.IsFixed) && (false == mt.IsLong)) { // if type has 2 byte length
1541 long actualSizeInBytes = this.GetActualSize();
1542 long sizeInCharacters = this.Size;
1544 // Bug: VSTFDevDiv #636867
1546 // 'actualSizeInBytes' is the size of value passed;
1547 // 'sizeInCharacters' is the parameter size;
1548 // 'actualSizeInBytes' is in bytes;
1549 // 'this.Size' is in charaters;
1550 // 'sizeInCharacters' is in characters;
1551 // 'TdsEnums.TYPE_SIZE_LIMIT' is in bytes;
1552 // For Non-NCharType and for non-Yukon or greater variables, size should be maintained;
1553 // Reverting changes from bug VSTFDevDiv # 479739 as it caused an regression;
1554 // Modifed variable names from 'size' to 'sizeInCharacters', 'actualSize' to 'actualSizeInBytes', and
1555 // 'maxSize' to 'maxSizeInBytes'
1557 // 1) revert the regression from bug 479739
1558 // 2) fix as many scenarios as possible including bug 636867
1559 // 3) cause no additional regression from 3.5 sp1
1560 // Keeping these goals in mind - the following are the changes we are making
1562 long maxSizeInBytes = 0;
1563 if ((mt.IsNCharType) && (yukonOrNewer))
1564 maxSizeInBytes = ((sizeInCharacters * sizeof(char)) > actualSizeInBytes) ? sizeInCharacters * sizeof(char) : actualSizeInBytes;
1568 // Elevation from (n)(var)char (4001+) to (n)text succeeds without failure only with Yukon and greater.
1569 // it fails in sql server 2000
1570 maxSizeInBytes = (sizeInCharacters > actualSizeInBytes) ? sizeInCharacters : actualSizeInBytes;
1573 if ((maxSizeInBytes > TdsEnums.TYPE_SIZE_LIMIT) || (_coercedValueIsDataFeed) ||
1574 (sizeInCharacters == -1) || (actualSizeInBytes == -1)) { // is size > size able to be described by 2 bytes
1576 // Convert the parameter to its max type
1577 mt = MetaType.GetMaxMetaTypeFromMetaType(mt);
1579 InternalMetaType = mt;
1581 if (mt.SqlDbType == SqlDbType.Xml) {
1582 throw ADP.InvalidMetaDataValue(); //Xml should always have IsPartialLength = true
1584 if (mt.SqlDbType == SqlDbType.NVarChar
1585 || mt.SqlDbType == SqlDbType.VarChar
1586 || mt.SqlDbType == SqlDbType.VarBinary) {
1587 Size = (int)(SmiMetaData.UnlimitedMaxLengthIndicator);
1592 switch (mt.SqlDbType) { // widening the SqlDbType is automatic
1593 case SqlDbType.Binary:
1594 case SqlDbType.VarBinary:
1595 mt = MetaType.GetMetaTypeFromSqlDbType (SqlDbType.Image, false);
1596 _metaType = mt; // do not use SqlDbType property which calls PropertyTypeChanging resetting coerced value
1597 InternalMetaType = mt;
1599 case SqlDbType.Char:
1600 case SqlDbType.VarChar:
1601 mt = MetaType.GetMetaTypeFromSqlDbType (SqlDbType.Text, false);
1603 InternalMetaType = mt;
1605 case SqlDbType.NChar:
1606 case SqlDbType.NVarChar:
1607 mt = MetaType.GetMetaTypeFromSqlDbType (SqlDbType.NText, false);
1609 InternalMetaType = mt;
1612 Debug.Assert(false, "Missed metatype in SqlCommand.BuildParamList()");
1621 private byte ValuePrecision(object value) {
1622 if (value is SqlDecimal) {
1623 if (((SqlDecimal) value).IsNull) // MDAC #79648
1626 return ((SqlDecimal)value).Precision;
1628 return ValuePrecisionCore(value);
1631 private byte ValueScale(object value) {
1632 if (value is SqlDecimal) {
1633 if (((SqlDecimal) value).IsNull) // MDAC #79648
1636 return ((SqlDecimal) value).Scale;
1638 return ValueScaleCore(value);
1641 private static int StringSize(object value, bool isSqlType) {
1643 Debug.Assert(!((INullable)value).IsNull, "Should not call StringSize on null values");
1644 if (value is SqlString) {
1645 return ((SqlString)value).Value.Length;
1647 if (value is SqlChars) {
1648 return ((SqlChars)value).Value.Length;
1652 string svalue = (value as string);
1653 if (null != svalue) {
1654 return svalue.Length;
1656 char[] cvalue = (value as char[]);
1657 if (null != cvalue) {
1658 return cvalue.Length;
1660 if (value is char) {
1665 // Didn't match, unknown size
1669 private static int BinarySize(object value, bool isSqlType) {
1671 Debug.Assert(!((INullable)value).IsNull, "Should not call StringSize on null values");
1672 if (value is SqlBinary) {
1673 return ((SqlBinary)value).Length;
1675 if (value is SqlBytes) {
1676 return ((SqlBytes)value).Value.Length;
1680 byte[] bvalue = (value as byte[]);
1681 if (null != bvalue) {
1682 return bvalue.Length;
1684 if (value is byte) {
1689 // Didn't match, unknown size
1693 private int ValueSize(object value) {
1694 if (value is SqlString) {
1695 if (((SqlString) value).IsNull) // MDAC #79648
1698 return ((SqlString) value).Value.Length;
1700 if (value is SqlChars) {
1701 if (((SqlChars) value).IsNull)
1704 return ((SqlChars) value).Value.Length;
1707 if (value is SqlBinary) {
1708 if (((SqlBinary) value).IsNull) // MDAC #79648
1711 return ((SqlBinary) value).Length;
1713 if (value is SqlBytes) {
1714 if (((SqlBytes) value).IsNull)
1717 return (int)(((SqlBytes) value).Length);
1719 if (value is DataFeed)
1724 return ValueSizeCore(value);
1729 // parse an string of the form db.schema.name where any of the three components
1730 // might have "[" "]" and dots within it.
1732 // [0] dbname (or null)
1733 // [1] schema (or null)
1735 // NOTE: if perf/space implications of Regex is not a problem, we can get rid
1736 // of this and use a simple regex to do the parsing
1737 internal static string[] ParseTypeName(string typeName, bool isUdtTypeName) {
1738 Debug.Assert(null != typeName, "null typename passed to ParseTypeName");
1742 if (isUdtTypeName) {
1743 errorMsg = Res.SQL_UDTTypeName;
1746 errorMsg = Res.SQL_TypeName;
1748 return MultipartIdentifier.ParseMultipartIdentifier(typeName, "[\"", "]\"", '.', 3, true, errorMsg, true);
1750 catch (ArgumentException) {
1751 if (isUdtTypeName) {
1752 throw SQL.InvalidUdt3PartNameFormat();
1755 throw SQL.InvalidParameterTypeNameFormat();
1760 sealed internal class SqlParameterConverter : ExpandableObjectConverter {
1762 // converter classes should have public ctor
1763 public SqlParameterConverter() {
1766 override public bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
1767 if (typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) {
1770 return base.CanConvertTo(context, destinationType);
1773 override public object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
1774 if (destinationType == null) {
1775 throw ADP.ArgumentNull("destinationType");
1777 if ((typeof(System.ComponentModel.Design.Serialization.InstanceDescriptor) == destinationType) && (value is SqlParameter)) {
1778 return ConvertToInstanceDescriptor(value as SqlParameter);
1780 return base.ConvertTo(context, culture, value, destinationType);
1783 private System.ComponentModel.Design.Serialization.InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) {
1784 // MDAC 67321 - reducing parameter generated code
1785 int flags = 0; // if part of the collection - the parametername can't be empty
1787 if (p.ShouldSerializeSqlDbType()) {
1790 if (p.ShouldSerializeSize()) {
1793 if (!ADP.IsEmpty(p.SourceColumn)) {
1796 if (null != p.Value) {
1799 if ((ParameterDirection.Input != p.Direction) || p.IsNullable
1800 || p.ShouldSerializePrecision() || p.ShouldSerializeScale()
1801 || (DataRowVersion.Current != p.SourceVersion)
1803 flags |= 16; // v1.0 everything
1806 if (p.SourceColumnNullMapping || !ADP.IsEmpty(p.XmlSchemaCollectionDatabase) ||
1807 !ADP.IsEmpty(p.XmlSchemaCollectionOwningSchema) || !ADP.IsEmpty(p.XmlSchemaCollectionName)) {
1808 flags |= 32; // v2.0 everything
1812 object[] ctorValues;
1814 case 0: // ParameterName
1815 case 1: // SqlDbType
1816 ctorParams = new Type[] { typeof(string), typeof(SqlDbType) };
1817 ctorValues = new object[] { p.ParameterName, p.SqlDbType };
1820 case 3: // Size, SqlDbType
1821 ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int) };
1822 ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size };
1824 case 4: // SourceColumn
1825 case 5: // SourceColumn, SqlDbType
1826 case 6: // SourceColumn, Size
1827 case 7: // SourceColumn, Size, SqlDbType
1828 ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int), typeof(string) };
1829 ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size, p.SourceColumn };
1832 ctorParams = new Type[] { typeof(string), typeof(object) };
1833 ctorValues = new object[] { p.ParameterName, p.Value };
1836 if (0 == (32 & flags)) { // v1.0 everything
1837 ctorParams = new Type[] {
1838 typeof(string), typeof(SqlDbType), typeof(int), typeof(ParameterDirection),
1839 typeof(bool), typeof(byte), typeof(byte),
1840 typeof(string), typeof(DataRowVersion),
1842 ctorValues = new object[] {
1843 p.ParameterName, p.SqlDbType, p.Size, p.Direction,
1844 p.IsNullable, p.PrecisionInternal, p.ScaleInternal,
1845 p.SourceColumn, p.SourceVersion,
1848 else { // v2.0 everything - round trip all browsable properties + precision/scale
1849 ctorParams = new Type[] {
1850 typeof(string), typeof(SqlDbType), typeof(int), typeof(ParameterDirection),
1851 typeof(byte), typeof(byte),
1852 typeof(string), typeof(DataRowVersion), typeof(bool),
1854 typeof(string), typeof(string),
1856 ctorValues = new object[] {
1857 p.ParameterName, p.SqlDbType, p.Size, p.Direction,
1858 p.PrecisionInternal, p.ScaleInternal,
1859 p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping,
1861 p.XmlSchemaCollectionDatabase, p.XmlSchemaCollectionOwningSchema,
1862 p.XmlSchemaCollectionName};
1866 System.Reflection.ConstructorInfo ctor = typeof(SqlParameter).GetConstructor(ctorParams);
1867 return new System.ComponentModel.Design.Serialization.InstanceDescriptor(ctor, ctorValues);