diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 8ca4487b3a..a63604a311 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -427,6 +427,9 @@ Microsoft\Data\SqlClient\SqlObjectPool.cs + + Microsoft\Data\SqlClient\SqlParameter.cs + Microsoft\Data\SqlClient\SqlParameterCollection.cs @@ -562,7 +565,7 @@ - + @@ -639,7 +642,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs index f33751fb08..0629ceb1ac 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.Designer.cs @@ -942,6 +942,24 @@ internal static string Data_InvalidOffsetLength { } } + /// + /// Looks up a localized string similar to Update. + /// + internal static string DataCategory_Update { + get { + return ResourceManager.GetString("DataCategory_Update", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to XML. + /// + internal static string DataCategory_Xml { + get { + return ResourceManager.GetString("DataCategory_Xml", resourceCulture); + } + } + /// /// Looks up a localized string similar to Internal error occurred when retrying the download of the HGS root certificate after the initial request failed. Contact Customer Support Services.. /// diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx index bfe5559389..6be833f7ee 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/Strings.resx @@ -1932,4 +1932,10 @@ Parameter '{0}' cannot have Direction Output or InputOutput when EnableOptimizedParameterBinding is enabled on the parent command. + + Update + + + XML + \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs b/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs index 4b61d284ff..067426eee3 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Resources/StringsHelper.cs @@ -76,6 +76,7 @@ internal class ResourceNames { internal const string DataCategory_Data = @"Data"; internal const string DataCategory_Update = @"Update"; + internal const string DataCategory_Xml = @"XML"; internal const string DbCommand_CommandTimeout = @"Time to wait for command to execute."; internal const string DbConnection_State = @"The ConnectionState indicating whether the connection is open or closed."; internal const string DataCategory_Fill = @"Fill"; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index b1e683badc..306a120c9f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -491,6 +491,9 @@ Microsoft\Data\SqlClient\SqlParameterCollection.cs + + Microsoft\Data\SqlClient\SqlParameter.cs + Microsoft\Data\SqlClient\SqlQueryMetadataCache.cs @@ -624,7 +627,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 89a56464be..cdb492fbb0 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -6527,7 +6527,6 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP int paramCount = GetParameterCount(parameters); int j = startCount; TdsParser parser = _activeConnection.Parser; - bool is2005OrNewer = parser.Is2005OrNewer; for (ii = 0; ii < paramCount; ii++) { @@ -6536,7 +6535,7 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP // func will change type to that with a 4 byte length if the type has a two // byte length and a parameter length > than that expressable in 2 bytes - if ((!parameter.ValidateTypeLengths(is2005OrNewer).IsPlp) && (parameter.Direction != ParameterDirection.Output)) + if ((!parameter.ValidateTypeLengths().IsPlp) && (parameter.Direction != ParameterDirection.Output)) { parameter.FixStreamDataForNonPLP(); } @@ -6912,8 +6911,6 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete StringBuilder paramList = new StringBuilder(); bool fAddSeparator = false; - bool is2005OrNewer = parser.Is2005OrNewer; - int count = 0; count = parameters.Count; @@ -6963,7 +6960,7 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete { // func will change type to that with a 4 byte length if the type has a two // byte length and a parameter length > than that expressable in 2 bytes - mt = sqlParam.ValidateTypeLengths(is2005OrNewer); + mt = sqlParam.ValidateTypeLengths(); if ((!mt.IsPlp) && (sqlParam.Direction != ParameterDirection.Output)) { sqlParam.FixStreamDataForNonPLP(); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs deleted file mode 100644 index 503273c317..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ /dev/null @@ -1,2396 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.Design.Serialization; -using System.Data; -using System.Data.Common; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Xml; -using Microsoft.Data.Common; -using Microsoft.Data.SqlClient.Server; - -namespace Microsoft.Data.SqlClient -{ - internal abstract class DataFeed - { - } - - internal class StreamDataFeed : DataFeed - { - internal Stream _source; - - internal StreamDataFeed(Stream source) - { - _source = source; - } - } - - internal class TextDataFeed : DataFeed - { - internal TextReader _source; - - internal TextDataFeed(TextReader source) - { - _source = source; - } - } - - internal class XmlDataFeed : DataFeed - { - internal XmlReader _source; - - internal XmlDataFeed(XmlReader source) - { - _source = source; - } - } - - /// - [TypeConverter(typeof(SqlParameter.SqlParameterConverter))] - public sealed partial class SqlParameter : DbParameter, IDbDataParameter, ICloneable - { - internal sealed class SqlParameterConverter : ExpandableObjectConverter - { - - // converter classes should have public ctor - public SqlParameterConverter() - { - } - - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) - { - if (typeof(InstanceDescriptor) == destinationType) - { - return true; - } - return base.CanConvertTo(context, destinationType); - } - - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) - { - if (destinationType == null) - { - throw ADP.ArgumentNull(nameof(destinationType)); - } - if ((typeof(InstanceDescriptor) == destinationType) && (value is SqlParameter)) - { - return ConvertToInstanceDescriptor(value as SqlParameter); - } - return base.ConvertTo(context, culture, value, destinationType); - } - - private InstanceDescriptor ConvertToInstanceDescriptor(SqlParameter p) - { - int flags = 0; // if part of the collection - the parametername can't be empty - - if (p.ShouldSerializeSqlDbType()) - { - flags |= 1; - } - if (p.ShouldSerializeSize()) - { - flags |= 2; - } - if (!string.IsNullOrEmpty(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 || - !string.IsNullOrEmpty(p.XmlSchemaCollectionDatabase) || - !string.IsNullOrEmpty(p.XmlSchemaCollectionOwningSchema) || - !string.IsNullOrEmpty(p.XmlSchemaCollectionName) - ) - { - flags |= 32; // v2.0 everything - } - - Type[] ctorParams; - object[] ctorValues; - switch (flags) - { - case 0: // ParameterName - case 1: // SqlDbType - ctorParams = new Type[] { typeof(string), typeof(SqlDbType) }; - ctorValues = new object[] { p.ParameterName, p.SqlDbType }; - break; - case 2: // Size - case 3: // Size, SqlDbType - ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int) }; - ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size }; - break; - case 4: // SourceColumn - case 5: // SourceColumn, SqlDbType - case 6: // SourceColumn, Size - case 7: // SourceColumn, Size, SqlDbType - ctorParams = new Type[] { typeof(string), typeof(SqlDbType), typeof(int), typeof(string) }; - ctorValues = new object[] { p.ParameterName, p.SqlDbType, p.Size, p.SourceColumn }; - break; - case 8: // Value - ctorParams = new Type[] { typeof(string), typeof(object) }; - ctorValues = new object[] { p.ParameterName, p.Value }; - break; - default: - if (0 == (32 & flags)) - { // v1.0 everything - ctorParams = new Type[] { - typeof(string), typeof(SqlDbType), typeof(int), typeof(ParameterDirection), - typeof(bool), typeof(byte), typeof(byte), - typeof(string), typeof(DataRowVersion), - typeof(object) }; - ctorValues = new object[] { - p.ParameterName, p.SqlDbType, 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(SqlDbType), typeof(int), typeof(ParameterDirection), - typeof(byte), typeof(byte), - typeof(string), typeof(DataRowVersion), typeof(bool), - typeof(object), - typeof(string), typeof(string), - typeof(string) }; - ctorValues = new object[] { - p.ParameterName, p.SqlDbType, p.Size, p.Direction, - p.PrecisionInternal, p.ScaleInternal, - p.SourceColumn, p.SourceVersion, p.SourceColumnNullMapping, - p.Value, - p.XmlSchemaCollectionDatabase, p.XmlSchemaCollectionOwningSchema, - p.XmlSchemaCollectionName}; - } - break; - } - ConstructorInfo ctor = typeof(SqlParameter).GetConstructor(ctorParams); - return new InstanceDescriptor(ctor, ctorValues); - } - } - - [Flags] - private enum SqlParameterFlags : ushort - { - None = 0, - IsNull = 1, - IsNullable = 2, - IsSqlParameterSqlType = 4, - SourceColumnNullMapping = 8, - CoercedValueIsSqlType = 16, - CoercedValueIsDataFeed = 32, - HasReceivedMetadata = 64, - ForceColumnEncryption = 128, - IsDerivedParameterTypeName = 256, - HasScale = 512, - } - - private MetaType _metaType; - private SqlCollation _collation; - private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; - private string _udtTypeName; - private string _typeName; - private Exception _udtLoadError; - private string _parameterName; - private byte _precision; - private byte _scale; - private MetaType _internalMetaType; - private SqlBuffer _sqlBufferReturnValue; - private INullable _valueAsINullable; - private int _actualSize; - private object _value; - private object _coercedValue; - private object _parent; - private ParameterDirection _direction; - private int _size; - private int _offset; - private string _sourceColumn; - private DataRowVersion _sourceVersion; - private SqlParameterFlags _flags; - - /// - public SqlParameter() : base() - { - _flags = SqlParameterFlags.IsNull; - _actualSize = -1; - _direction = ParameterDirection.Input; - } - - /// - public SqlParameter(string parameterName, SqlDbType dbType) : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - } - - /// - public SqlParameter(string parameterName, object value) : this() - { - Debug.Assert(!(value is SqlDbType), "use SqlParameter(string, SqlDbType)"); - - ParameterName = parameterName; - Value = value; - } - - /// - public SqlParameter(string parameterName, SqlDbType dbType, int size) : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - Size = size; - } - - /// - public SqlParameter(string parameterName, SqlDbType dbType, int size, string sourceColumn) : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - Size = size; - SourceColumn = sourceColumn; - } - - /// - [EditorBrowsable(EditorBrowsableState.Advanced)] - public SqlParameter( - string parameterName, - SqlDbType dbType, - int size, - ParameterDirection direction, - bool isNullable, - byte precision, - byte scale, - string sourceColumn, - DataRowVersion sourceVersion, - object value - ) - : this(parameterName, dbType, size, sourceColumn) - { - Direction = direction; - IsNullable = isNullable; - PrecisionInternal = precision; - ScaleInternal = scale; - SourceVersion = sourceVersion; - Value = value; - } - - /// - public SqlParameter( - string parameterName, - SqlDbType dbType, - int size, - ParameterDirection direction, - byte precision, - byte scale, - string sourceColumn, - DataRowVersion sourceVersion, - bool sourceColumnNullMapping, - object value, - string xmlSchemaCollectionDatabase, - string xmlSchemaCollectionOwningSchema, - string xmlSchemaCollectionName - ) - : this() - { - ParameterName = parameterName; - SqlDbType = dbType; - Size = size; - Direction = direction; - PrecisionInternal = precision; - ScaleInternal = scale; - SourceColumn = sourceColumn; - SourceVersion = sourceVersion; - SourceColumnNullMapping = sourceColumnNullMapping; - Value = value; - if (!string.IsNullOrEmpty(xmlSchemaCollectionDatabase) || !string.IsNullOrEmpty(xmlSchemaCollectionOwningSchema) || !string.IsNullOrEmpty(xmlSchemaCollectionName)) - { - EnsureXmlSchemaCollection(); - _xmlSchemaCollection.Database = xmlSchemaCollectionDatabase; - _xmlSchemaCollection.OwningSchema = xmlSchemaCollectionOwningSchema; - _xmlSchemaCollection.Name = xmlSchemaCollectionName; - } - } - - private SqlParameter(SqlParameter source) : this() - { - ADP.CheckArgumentNull(source, nameof(source)); - source.CloneHelper(this); - if (_value is ICloneable cloneable) - { - _value = cloneable.Clone(); - } - } - - /// - /// Get or set the encryption related metadata of this SqlParameter. - /// Should be set to a non-null value only once. - /// - internal SqlCipherMetadata CipherMetadata { get; set; } - - /// - /// Indicates if the parameter encryption metadata received by sp_describe_parameter_encryption. - /// For unencrypted parameters, the encryption metadata should still be sent (and will indicate - /// that no encryption is needed). - /// - internal bool HasReceivedMetadata - { - get => HasFlag(SqlParameterFlags.HasReceivedMetadata); - set => SetFlag(SqlParameterFlags.HasReceivedMetadata, value); - } - - /// - /// Returns the normalization rule version number as a byte - /// - internal byte NormalizationRuleVersion => CipherMetadata?.NormalizationRuleVersion ?? 0x00; - - /// - [Browsable(false)] - public SqlCompareOptions CompareInfo - { - // Bits 21 through 25 represent the CompareInfo - get - { - SqlCollation collation = _collation; - if (null != collation) - { - return collation.SqlCompareOptions; - } - return SqlCompareOptions.None; - } - set - { - SqlCollation collation = _collation; - - // Copied from SQLString.x_iValidSqlCompareOptionMask - SqlCompareOptions validSqlCompareOptionMask = - SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreWidth | - SqlCompareOptions.IgnoreNonSpace | SqlCompareOptions.IgnoreKanaType | - SqlCompareOptions.BinarySort | SqlCompareOptions.BinarySort2; - - if ((value & validSqlCompareOptionMask) != value) - { - throw ADP.ArgumentOutOfRange(nameof(CompareInfo)); - } - - if (collation == null || collation.SqlCompareOptions != value) - { - _collation = SqlCollation.FromLCIDAndSort(collation?.LCID ?? 0, value); - } - } - } - - /// - [ResCategory("XML")] - public string XmlSchemaCollectionDatabase - { - get => _xmlSchemaCollection?.Database ?? string.Empty; - set => EnsureXmlSchemaCollection().Database = value; - } - - /// - [ResCategory("XML")] - public string XmlSchemaCollectionOwningSchema - { - get => _xmlSchemaCollection?.OwningSchema ?? string.Empty; - set => EnsureXmlSchemaCollection().OwningSchema = value; - } - - /// - [ResCategory("XML")] - public string XmlSchemaCollectionName - { - get => _xmlSchemaCollection?.Name ?? string.Empty; - set => EnsureXmlSchemaCollection().Name = value; - } - - /// - [ - DefaultValue(false), - ResCategory("Data") - ] - public bool ForceColumnEncryption - { - get => HasFlag(SqlParameterFlags.ForceColumnEncryption); - set => SetFlag(SqlParameterFlags.ForceColumnEncryption, value); - } - - /// - public override DbType DbType - { - get => GetMetaTypeOnly().DbType; - set - { - MetaType metatype = _metaType; - if ((null == metatype) || (metatype.DbType != value)) - { - PropertyTypeChanging(); - _metaType = MetaType.GetMetaTypeFromDbType(value); - } - } - } - - /// - public override void ResetDbType() => ResetSqlDbType(); - - /// - [ResCategory("Data")] - public override string ParameterName - { - get => _parameterName ?? string.Empty; - set - { - if ( - string.IsNullOrEmpty(value) || - (value.Length < TdsEnums.MAX_PARAMETER_NAME_LENGTH) || - ( - (value[0] == '@') && - (value.Length <= TdsEnums.MAX_PARAMETER_NAME_LENGTH) - ) - ) - { - if (_parameterName != value) - { - PropertyChanging(); - _parameterName = value; - } - } - else - { - throw SQL.InvalidParameterNameLength(value); - } - } - } - - /// - [Browsable(false)] - public int LocaleId - { - // Lowest 20 bits represent LocaleId - get - { - SqlCollation collation = _collation; - if (null != collation) - { - return collation.LCID; - } - return 0; - } - set - { - SqlCollation collation = _collation; - - if (value != (SqlCollation.MaskLcid & value)) - { - throw ADP.ArgumentOutOfRange(nameof(LocaleId)); - } - - if (collation == null || collation.LCID != value) - { - _collation = SqlCollation.FromLCIDAndSort(value, collation?.SqlCompareOptions ?? SqlCompareOptions.None); - } - } - } - - /// - [ - DefaultValue((byte)0), - ResCategory(StringsHelper.ResourceNames.DataCategory_Data) - ] - public new byte Precision - { - get => PrecisionInternal; - set => PrecisionInternal = value; - } - - private bool ShouldSerializePrecision() => _precision != 0; - - /// - [ - DefaultValue((byte)0), - ResCategory(StringsHelper.ResourceNames.DataCategory_Data) - ] - public new byte Scale - { - get => ScaleInternal; - set => ScaleInternal = value; - } - - internal byte ScaleInternal - { - get - { - byte scale = _scale; - SqlDbType dbtype = GetMetaSqlDbTypeOnly(); - if ((scale == 0) && (dbtype == SqlDbType.Decimal)) - { - scale = ValueScale(SqlValue); - } - return scale; - } - set - { - if (_scale != value || !HasFlag(SqlParameterFlags.HasScale)) - { - PropertyChanging(); - _scale = value; - SetFlag(SqlParameterFlags.HasScale, true); - _actualSize = -1; // Invalidate actual size such that it is re-calculated - } - } - } - - private bool ShouldSerializeScale() => _scale != 0; // V1.0 compat, ignore _hasScale - - /// - [ - RefreshProperties(RefreshProperties.All), - ResCategory("Data"), - DbProviderSpecificTypeProperty(true) - ] - public SqlDbType SqlDbType - { - get => GetMetaTypeOnly().SqlDbType; - set - { - MetaType metatype = _metaType; - // HACK!!! - // We didn't want to expose SmallVarBinary on SqlDbType so we - // stuck it at the end of SqlDbType in v1.0, except that now - // we have new data types after that and it's smack dab in the - // middle of the valid range. To prevent folks from setting - // this invalid value we have to have this code here until we - // can take the time to fix it later. - if (TdsEnums.SmallVarBinary == value) - { - throw SQL.InvalidSqlDbType(value); - } - if ((null == metatype) || (metatype.SqlDbType != value)) - { - PropertyTypeChanging(); - _metaType = MetaType.GetMetaTypeFromSqlDbType(value, value == SqlDbType.Structured); - } - } - } - - private bool ShouldSerializeSqlDbType() => _metaType != null; - - /// - public void ResetSqlDbType() - { - if (_metaType != null) - { - PropertyTypeChanging(); - _metaType = null; - } - } - - /// - [ - Browsable(false), - DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), - ] - public object SqlValue - { - get - { - if (_udtLoadError != null) - { - throw _udtLoadError; - } - - if (_value != null) - { - if (_value == DBNull.Value) - { - return MetaType.GetNullSqlValue(GetMetaTypeOnly().SqlType); - } - if (_value is INullable) - { - return _value; - } - - // For Date and DateTime2, return the CLR object directly without converting it to a SqlValue - // GetMetaTypeOnly() will convert _value to a string in the case of char or char[], so only check - // the SqlDbType for DateTime. This is the only case when we might return the CLR value directly. - if (_value is DateTime) - { - SqlDbType sqlDbType = GetMetaTypeOnly().SqlDbType; - if (sqlDbType == SqlDbType.Date || sqlDbType == SqlDbType.DateTime2) - { - return _value; - } - } - - return (MetaType.GetSqlValueFromComVariant(_value)); - } - else if (_sqlBufferReturnValue != null) - { - return _sqlBufferReturnValue.SqlValue; - } - return null; - } - set - { - Value = value; - } - } - - /// - [ - Browsable(false), - EditorBrowsable(EditorBrowsableState.Advanced) - ] - public string UdtTypeName - { - get => _udtTypeName ?? string.Empty; - set => _udtTypeName = value; - } - - /// - [ - Browsable(false), - EditorBrowsable(EditorBrowsableState.Advanced) - ] - public string TypeName - { - get => _typeName ?? string.Empty; - set - { - _typeName = value; - IsDerivedParameterTypeName = false; - } - } - - /// - [ - RefreshProperties(RefreshProperties.All), - ResCategory("Data"), - TypeConverter(typeof(StringConverter)), - ] - public override object Value - { - get - { - if (_udtLoadError != null) - { - throw _udtLoadError; - } - - if (_value != null) - { - return _value; - } - else if (_sqlBufferReturnValue != null) - { - if (ParameterIsSqlType) - { - return _sqlBufferReturnValue.SqlValue; - } - return _sqlBufferReturnValue.Value; - } - return null; - } - set - { - _value = value; - _sqlBufferReturnValue = null; - _coercedValue = null; - _valueAsINullable = _value as INullable; - SetFlag(SqlParameterFlags.IsSqlParameterSqlType, _valueAsINullable != null); - SetFlag(SqlParameterFlags.IsNull, (null == _value) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); - _udtLoadError = null; - _actualSize = -1; - } - } - - /// - [ - RefreshProperties(RefreshProperties.All), - ResCategory("Data"), - ] - public override ParameterDirection Direction - { - get => _direction; - set - { - if (_direction != value) - { - switch (value) - { - case ParameterDirection.Input: - case ParameterDirection.Output: - case ParameterDirection.InputOutput: - case ParameterDirection.ReturnValue: - PropertyChanging(); - _direction = value; - break; - default: - throw ADP.InvalidParameterDirection(value); - } - } - } - } - - /// - public override bool IsNullable - { - get => HasFlag(SqlParameterFlags.IsNullable); - set => SetFlag(SqlParameterFlags.IsNullable, value); - } - - /// - public int Offset - { - get => _offset; - set - { - if (value < 0) - { - throw ADP.InvalidOffsetValue(value); - } - _offset = value; - } - } - - /// - [ResCategory("Data")] - public override int Size - { - get - { - int size = _size; - if (size == 0) - { - size = ValueSize(Value); - } - return size; - } - set - { - if (value != _size) - { - if (value < -1) - { - throw ADP.InvalidSizeValue(value); - } - PropertyChanging(); - _size = value; - } - } - } - - private void ResetSize() - { - if (_size != 0) - { - PropertyChanging(); - _size = 0; - } - } - - private bool ShouldSerializeSize() => _size != 0; - - /// - [ResCategory("Update")] - public override string SourceColumn - { - get => _sourceColumn ?? string.Empty; - set => _sourceColumn = value; - } - - /// - public override bool SourceColumnNullMapping - { - get => HasFlag(SqlParameterFlags.SourceColumnNullMapping); - set => SetFlag(SqlParameterFlags.SourceColumnNullMapping, value); - } - - /// - public override string ToString() => ParameterName; - - /// - [ResCategory(StringsHelper.ResourceNames.DataCategory_Update)] - public override DataRowVersion SourceVersion - { - get - { - DataRowVersion sourceVersion = _sourceVersion; - return (sourceVersion != 0) ? sourceVersion : DataRowVersion.Current; - } - set - { - switch (value) - { - case DataRowVersion.Original: - case DataRowVersion.Current: - case DataRowVersion.Proposed: - case DataRowVersion.Default: - _sourceVersion = value; - break; - default: - throw ADP.InvalidDataRowVersion(value); - } - } - } - - - /// - object ICloneable.Clone() => new SqlParameter(this); - - - private object CoercedValue - { - get => _coercedValue; - set => _coercedValue = value; - } - - internal bool CoercedValueIsDataFeed - { - get - { - if (null == _coercedValue) - { - GetCoercedValue(); - } - AssertCachedPropertiesAreValid(); - return HasFlag(SqlParameterFlags.CoercedValueIsDataFeed); - } - } - - internal bool CoercedValueIsSqlType - { - get - { - if (_coercedValue == null) - { - GetCoercedValue(); - } - AssertCachedPropertiesAreValid(); - return HasFlag(SqlParameterFlags.CoercedValueIsSqlType); - } - } - - // - // currently the user can't set this value. it gets set by the returnvalue from tds - // - internal SqlCollation Collation - { - get => _collation; - set => _collation = value; - } - - private bool HasFlag(SqlParameterFlags flag) - { - return (_flags & flag) != 0; - } - - internal bool IsNull - { - get - { - // NOTE: Udts can change their value any time - if (_internalMetaType.SqlDbType == SqlDbType.Udt) - { - SetFlag(SqlParameterFlags.IsNull, (_value == null) || (_value == DBNull.Value) || (HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && _valueAsINullable.IsNull)); - } - return HasFlag(SqlParameterFlags.IsNull); - } - } - - internal MetaType InternalMetaType - { - get - { - Debug.Assert(null != _internalMetaType, "null InternalMetaType"); - return _internalMetaType; - } - set => _internalMetaType = value; - } - - internal byte PrecisionInternal - { - get - { - byte precision = _precision; - SqlDbType dbtype = GetMetaSqlDbTypeOnly(); - if ((0 == precision) && (SqlDbType.Decimal == dbtype)) - { - precision = ValuePrecision(SqlValue); - } - return precision; - } - set - { - SqlDbType sqlDbType = SqlDbType; - if (sqlDbType == SqlDbType.Decimal && value > TdsEnums.MAX_NUMERIC_PRECISION) - { - throw SQL.PrecisionValueOutOfRange(value); - } - if (_precision != value) - { - PropertyChanging(); - _precision = value; - } - } - } - - internal bool ParameterIsSqlType - { - get => HasFlag(SqlParameterFlags.IsSqlParameterSqlType); - set => SetFlag(SqlParameterFlags.IsSqlParameterSqlType, value); - } - - internal string ParameterNameFixed - { - get - { - string parameterName = ParameterName; - if ((parameterName.Length > 0) && (parameterName[0] != '@')) - { - parameterName = "@" + parameterName; - } - Debug.Assert(parameterName.Length <= TdsEnums.MAX_PARAMETER_NAME_LENGTH, "parameter name too long"); - return parameterName; - } - } - - internal bool SizeInferred => 0 == _size; - - internal INullable ValueAsINullable => _valueAsINullable; - - internal bool IsDerivedParameterTypeName - { - get => HasFlag(SqlParameterFlags.IsDerivedParameterTypeName); - set => SetFlag(SqlParameterFlags.IsDerivedParameterTypeName, value); - } - - private void CloneHelper(SqlParameter destination) - { - // NOTE: _parent is not cloned - destination._value = _value; - destination._direction = _direction; - destination._size = _size; - destination._offset = _offset; - destination._sourceColumn = _sourceColumn; - destination._sourceVersion = _sourceVersion; - destination._flags = _flags & ( - SqlParameterFlags.SourceColumnNullMapping | - SqlParameterFlags.IsNull | - SqlParameterFlags.IsNullable | - SqlParameterFlags.IsSqlParameterSqlType | - SqlParameterFlags.CoercedValueIsDataFeed | - SqlParameterFlags.CoercedValueIsSqlType | - SqlParameterFlags.ForceColumnEncryption | - SqlParameterFlags.IsDerivedParameterTypeName - // HasScale and HasReceivedMetadata deliberately omitted - ); - destination._metaType = _metaType; - destination._collation = _collation; - if (_xmlSchemaCollection != null) - { - destination.EnsureXmlSchemaCollection().CopyFrom(_xmlSchemaCollection); - } - destination._udtTypeName = _udtTypeName; - destination._typeName = _typeName; - destination._udtLoadError = _udtLoadError; - destination._parameterName = _parameterName; - destination._precision = _precision; - destination._scale = _scale; - destination._sqlBufferReturnValue = _sqlBufferReturnValue; - destination._internalMetaType = _internalMetaType; - destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem - destination._valueAsINullable = _valueAsINullable; - destination._actualSize = _actualSize; - } - - internal void CopyTo(SqlParameter destination) - { - ADP.CheckArgumentNull(destination, nameof(destination)); - CloneHelper(destination); - } - - internal object CompareExchangeParent(object value, object comparand) - { - // the interlock guarantees same parameter won't belong to multiple collections - // at the same time, but to actually occur the user must really try - // since we never declared thread safety, we don't care at this time - //return System.Threading.Interlocked.CompareExchange(ref _parent, value, comparand); - object parent = _parent; - if (comparand == parent) - { - _parent = value; - } - return parent; - } - - private SqlMetaDataXmlSchemaCollection EnsureXmlSchemaCollection() - { - if (_xmlSchemaCollection is null) - { - _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); - } - return _xmlSchemaCollection; - } - - internal void FixStreamDataForNonPLP() - { - object value = GetCoercedValue(); - AssertCachedPropertiesAreValid(); - if (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed)) - { - return; - } - - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); - - if (value is TextDataFeed textFeed) - { - if (Size > 0) - { - char[] buffer = new char[Size]; - int nRead = textFeed._source.ReadBlock(buffer, 0, Size); - CoercedValue = new string(buffer, 0, nRead); - } - else - { - CoercedValue = textFeed._source.ReadToEnd(); - } - return; - } - - if (value is StreamDataFeed streamFeed) - { - if (Size > 0) - { - byte[] buffer = new byte[Size]; - int totalRead = 0; - Stream sourceStream = streamFeed._source; - while (totalRead < Size) - { - int nRead = sourceStream.Read(buffer, totalRead, Size - totalRead); - if (nRead == 0) - { - break; - } - totalRead += nRead; - } - if (totalRead < Size) - { - Array.Resize(ref buffer, totalRead); - } - CoercedValue = buffer; - } - else - { - MemoryStream ms = new MemoryStream(); - streamFeed._source.CopyTo(ms); - CoercedValue = ms.ToArray(); - } - return; - } - - if (value is XmlDataFeed xmlFeed) - { - CoercedValue = MetaType.GetStringFromXml(xmlFeed._source); - return; - } - - // We should have returned before reaching here - Debug.Fail("_coercedValueIsDataFeed was true, but the value was not a known DataFeed type"); - } - - private void GetActualFieldsAndProperties(out List fields, out SmiMetaDataPropertyCollection props, out ParameterPeekAheadValue peekAhead) - { - fields = null; - props = null; - peekAhead = null; - - object value = GetCoercedValue(); - if (value is DataTable dt) - { - if (dt.Columns.Count <= 0) - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - fields = new List(dt.Columns.Count); - bool[] keyCols = new bool[dt.Columns.Count]; - bool hasKey = false; - - // set up primary key as unique key list - // do this prior to general metadata loop to favor the primary key - if (null != dt.PrimaryKey && 0 < dt.PrimaryKey.Length) - { - foreach (DataColumn col in dt.PrimaryKey) - { - keyCols[col.Ordinal] = true; - hasKey = true; - } - } - - for (int i = 0; i < dt.Columns.Count; i++) - { - fields.Add(MetaDataUtilsSmi.SmiMetaDataFromDataColumn(dt.Columns[i], dt)); - - // DataColumn uniqueness is only for a single column, so don't add - // more than one. (keyCols.Count first for assumed minimal perf benefit) - if (!hasKey && dt.Columns[i].Unique) - { - keyCols[i] = true; - hasKey = true; - } - } - - // Add unique key property, if any found. - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - } - else if (value is SqlDataReader sqlReader) - { - fields = new List(sqlReader.GetInternalSmiMetaData()); - if (fields.Count <= 0) - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - - bool[] keyCols = new bool[fields.Count]; - bool hasKey = false; - for (int i = 0; i < fields.Count; i++) - { - if (fields[i] is SmiQueryMetaData qmd && !qmd.IsKey.IsNull && qmd.IsKey.Value) - { - keyCols[i] = true; - hasKey = true; - } - } - - // Add unique key property, if any found. - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - } - else if (value is IEnumerable enumerable) - { - // must grab the first record of the enumerator to get the metadata - IEnumerator enumerator = enumerable.GetEnumerator(); - SqlDataRecord firstRecord = null; - try - { - // no need for fields if there's no rows or no columns -- we'll be sending a null instance anyway. - if (enumerator.MoveNext()) - { - firstRecord = enumerator.Current; - int fieldCount = firstRecord.FieldCount; - if (0 < fieldCount) - { - // It's valid! Grab those fields. - bool[] keyCols = new bool[fieldCount]; - bool[] defaultFields = new bool[fieldCount]; - bool[] sortOrdinalSpecified = new bool[fieldCount]; - int maxSortOrdinal = -1; // largest sort ordinal seen, used to optimize locating holes in the list - bool hasKey = false; - bool hasDefault = false; - int sortCount = 0; - SmiOrderProperty.SmiColumnOrder[] sort = new SmiOrderProperty.SmiColumnOrder[fieldCount]; - fields = new List(fieldCount); - for (int i = 0; i < fieldCount; i++) - { - SqlMetaData colMeta = firstRecord.GetSqlMetaData(i); - fields.Add(MetaDataUtilsSmi.SqlMetaDataToSmiExtendedMetaData(colMeta)); - if (colMeta.IsUniqueKey) - { - keyCols[i] = true; - hasKey = true; - } - - if (colMeta.UseServerDefault) - { - defaultFields[i] = true; - hasDefault = true; - } - - PropertyInfo serverTypeNameProperty = colMeta.GetType().GetProperty("SortOrder", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - MethodInfo getter = serverTypeNameProperty.GetGetMethod(nonPublic: true); - SortOrder sortOrder = (SortOrder)getter.Invoke(colMeta, null); - - sort[i]._order = sortOrder; - if (SortOrder.Unspecified != sortOrder) - { - // SqlMetaData takes care of checking for negative sort ordinals with specified sort order - - // bail early if there's no way sort order could be monotonically increasing - if (fieldCount <= colMeta.SortOrdinal) - { - throw SQL.SortOrdinalGreaterThanFieldCount(i, colMeta.SortOrdinal); - } - - // Check to make sure we haven't seen this ordinal before - if (sortOrdinalSpecified[colMeta.SortOrdinal]) - { - throw SQL.DuplicateSortOrdinal(colMeta.SortOrdinal); - } - - sort[i]._sortOrdinal = colMeta.SortOrdinal; - sortOrdinalSpecified[colMeta.SortOrdinal] = true; - if (colMeta.SortOrdinal > maxSortOrdinal) - { - maxSortOrdinal = colMeta.SortOrdinal; - } - sortCount++; - } - } - - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - - if (hasDefault) - { - // May have already created props list in unique key handling - if (null == props) - { - props = new SmiMetaDataPropertyCollection(); - } - - props[SmiPropertySelector.DefaultFields] = new SmiDefaultFieldsProperty(new List(defaultFields)); - } - - if (0 < sortCount) - { - // validate monotonically increasing sort order. - // Since we already checked for duplicates, we just need - // to watch for values outside of the sortCount range. - if (maxSortOrdinal >= sortCount) - { - // there is at least one hole, find the first one - int i; - for (i = 0; i < sortCount; i++) - { - if (!sortOrdinalSpecified[i]) - { - break; - } - } - Debug.Assert(i < sortCount, "SqlParameter.GetActualFieldsAndProperties: SortOrdinal hole-finding algorithm failed!"); - throw SQL.MissingSortOrdinal(i); - } - - // May have already created props list - if (null == props) - { - props = new SmiMetaDataPropertyCollection(); - } - - props[SmiPropertySelector.SortOrder] = new SmiOrderProperty( - new List(sort)); - } - - // pack it up so we don't have to rewind to send the first value - peekAhead = new ParameterPeekAheadValue() - { - Enumerator = enumerator, - FirstRecord = firstRecord - }; - - // now that it's all packaged, make sure we don't dispose it. - enumerator = null; - } - else - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - } - else - { - throw SQL.IEnumerableOfSqlDataRecordHasNoRows(); - } - } - finally - { - if (enumerator != null) - { - enumerator.Dispose(); - } - } - } - else if (value is DbDataReader dbReader) - { - DataTable schema = dbReader.GetSchemaTable(); - if (schema.Rows.Count <= 0) - { - throw SQL.NotEnoughColumnsInStructuredType(); - } - - int fieldCount = schema.Rows.Count; - fields = new List(fieldCount); - bool[] keyCols = new bool[fieldCount]; - bool hasKey = false; - int ordinalForIsKey = schema.Columns[SchemaTableColumn.IsKey].Ordinal; - int ordinalForColumnOrdinal = schema.Columns[SchemaTableColumn.ColumnOrdinal].Ordinal; - // Extract column metadata - for (int rowOrdinal = 0; rowOrdinal < fieldCount; rowOrdinal++) - { - DataRow row = schema.Rows[rowOrdinal]; - SmiExtendedMetaData candidateMd = MetaDataUtilsSmi.SmiMetaDataFromSchemaTableRow(row); - - // Determine destination ordinal. Allow for ordinal not specified by assuming rowOrdinal *is* columnOrdinal - // in that case, but don't worry about mix-and-match of the two techniques - int columnOrdinal = rowOrdinal; - if (!row.IsNull(ordinalForColumnOrdinal)) - { - columnOrdinal = (int)row[ordinalForColumnOrdinal]; - } - - // After this point, things we are creating (keyCols, fields) should be accessed by columnOrdinal - // while the source should just be accessed via "row". - - // Watch for out-of-range ordinals - if (columnOrdinal >= fieldCount || columnOrdinal < 0) - { - throw SQL.InvalidSchemaTableOrdinals(); - } - - // extend empty space if out-of-order ordinal - while (columnOrdinal > fields.Count) - { - fields.Add(null); - } - - // Now add the candidate to the list - if (fields.Count == columnOrdinal) - { - fields.Add(candidateMd); - } - else - { - // Disallow two columns using the same ordinal (even if due to mixing null and non-null columnOrdinals) - if (fields[columnOrdinal] != null) - { - throw SQL.InvalidSchemaTableOrdinals(); - } - - // Don't use insert, since it shifts all later columns down a notch - fields[columnOrdinal] = candidateMd; - } - - // Propagate key information - if (!row.IsNull(ordinalForIsKey) && (bool)row[ordinalForIsKey]) - { - keyCols[columnOrdinal] = true; - hasKey = true; - } - } - -#if DEBUG - // Check for holes - // Above loop logic prevents holes since: - // 1) loop processes fieldcount # of columns - // 2) no ordinals outside continuous range from 0 to fieldcount - 1 are allowed - // 3) no duplicate ordinals are allowed - // But assert no holes to be sure. - foreach (SmiExtendedMetaData md in fields) - { - Debug.Assert(null != md, "Shouldn't be able to have holes, since original loop algorithm prevents such."); - } -#endif - - // Add unique key property, if any defined. - if (hasKey) - { - props = new SmiMetaDataPropertyCollection(); - props[SmiPropertySelector.UniqueKey] = new SmiUniqueKeyProperty(new List(keyCols)); - } - } - } - - internal byte GetActualScale() - { - if (ShouldSerializeScale()) - { - return ScaleInternal; - } - - // issue: how could a user specify 0 as the actual scale? - if (GetMetaTypeOnly().IsVarTime) - { - return TdsEnums.DEFAULT_VARTIME_SCALE; - } - return ValueScale(CoercedValue); - } - - // - // always returns data in bytes - except for non-unicode chars, which will be in number of chars - // - internal int GetActualSize() - { - MetaType mt = InternalMetaType; - SqlDbType actualType = mt.SqlDbType; - // NOTE: Users can change the Udt at any time, so we may need to recalculate - if ((_actualSize == -1) || (actualType == SqlDbType.Udt)) - { - _actualSize = 0; - object val = GetCoercedValue(); - bool isSqlVariant = false; - - if (IsNull && !mt.IsVarTime) - { - return 0; - } - - // if this is a backend SQLVariant type, then infer the TDS type from the SQLVariant type - if (actualType == SqlDbType.Variant) - { - mt = MetaType.GetMetaTypeFromValue(val, streamAllowed: false); - actualType = MetaType.GetSqlDataType(mt.TDSType, 0 /*no user type*/, 0 /*non-nullable type*/).SqlDbType; - isSqlVariant = true; - } - - if (mt.IsFixed) - { - _actualSize = mt.FixedLength; - } - else - { - // @hack: until we have ForceOffset behavior we have the following semantics: - // @hack: if the user supplies a Size through the Size property or constructor, - // @hack: we only send a MAX of Size bytes over. If the actualSize is < Size, then - // @hack: we send over actualSize - int coercedSize = 0; - - // get the actual length of the data, in bytes - switch (actualType) - { - case SqlDbType.NChar: - case SqlDbType.NVarChar: - case SqlDbType.NText: - case SqlDbType.Xml: - { - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; - _actualSize = (ShouldSerializeSize() ? Size : 0); - _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; - if (_actualSize == -1) - { - _actualSize = coercedSize; - } - _actualSize <<= 1; - } - break; - case SqlDbType.Char: - case SqlDbType.VarChar: - case SqlDbType.Text: - { - // for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size - coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; - _actualSize = (ShouldSerializeSize() ? Size : 0); - _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; - if (_actualSize == -1) - { - _actualSize = coercedSize; - } - } - break; - case SqlDbType.Binary: - case SqlDbType.VarBinary: - case SqlDbType.Image: - case SqlDbType.Timestamp: - coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; - _actualSize = (ShouldSerializeSize() ? Size : 0); - _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); - if (_actualSize == -1) - { - _actualSize = coercedSize; - } - break; - case SqlDbType.Udt: - if (!IsNull) - { - //call the static function - coercedSize = AssemblyCache.GetLength(val); - } - break; - case SqlDbType.Structured: - coercedSize = -1; - break; - case SqlDbType.Time: - _actualSize = isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale()); - break; - case SqlDbType.DateTime2: - // Date in number of days (3 bytes) + time - _actualSize = 3 + (isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale())); - break; - case SqlDbType.DateTimeOffset: - // Date in days (3 bytes) + offset in minutes (2 bytes) + time - _actualSize = 5 + (isSqlVariant ? 5 : MetaType.GetTimeSizeFromScale(GetActualScale())); - break; - default: - Debug.Fail("Unknown variable length type!"); - break; - } - - // don't even send big values over to the variant - if (isSqlVariant && (coercedSize > TdsEnums.TYPE_SIZE_LIMIT)) - { - throw SQL.ParameterInvalidVariant(ParameterName); - } - } - } - - return _actualSize; - } - - internal byte GetActualPrecision() - { - return ShouldSerializePrecision() ? PrecisionInternal : ValuePrecision(CoercedValue); - } - - internal object GetCoercedValue() - { - // NOTE: User can change the Udt at any time - if ((_coercedValue == null) || (_internalMetaType.SqlDbType == SqlDbType.Udt)) - { // will also be set during parameter Validation - bool isDataFeed = Value is DataFeed; - if (IsNull || isDataFeed) - { - // No coercion is done for DataFeeds and Nulls - _coercedValue = Value; - SetFlag(SqlParameterFlags.CoercedValueIsSqlType, _coercedValue != null && HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); // set to null for output parameters that keeps _isSqlParameterSqlType - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, isDataFeed); - _actualSize = IsNull ? 0 : -1; - } - else - { - _coercedValue = CoerceValue(Value, _internalMetaType, out bool coercedValueIsDataFeed, out bool typeChanged); - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, coercedValueIsDataFeed); - SetFlag(SqlParameterFlags.CoercedValueIsSqlType, HasFlag(SqlParameterFlags.IsSqlParameterSqlType) && (!typeChanged)); // Type changed always results in a CLR type - _actualSize = -1; - } - } - AssertCachedPropertiesAreValid(); - return _coercedValue; - } - - internal int GetParameterSize() - { - return ShouldSerializeSize() ? Size : ValueSize(CoercedValue); - } - - /// - /// Get SMI Metadata to write out type_info stream. - /// - /// - internal SmiParameterMetaData GetMetadataForTypeInfo() - { - if (_internalMetaType == null) - { - _internalMetaType = GetMetaTypeOnly(); - } - - return MetaDataForSmi(out _); - } - - // IMPORTANT DEVNOTE: This method is being used for parameter encryption functionality, to get the type_info TDS object from SqlParameter. - // Please consider impact to that when changing this method. Refer to the callers of SqlParameter.GetMetadataForTypeInfo(). - internal SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhead) - { - peekAhead = null; - MetaType mt = ValidateTypeLengths(true /* 2005 or newer */ ); - long actualLen = GetActualSize(); - long maxLen = Size; - - // GetActualSize returns bytes length, but smi expects char length for - // character types, so adjust - if (!mt.IsLong) - { - if (mt.SqlDbType == SqlDbType.NChar || mt.SqlDbType == SqlDbType.NVarChar) - { - actualLen /= sizeof(char); - } - - if (actualLen > maxLen) - { - maxLen = actualLen; - } - } - - // Determine maxLength for types that ValidateTypeLengths won't figure out - if (maxLen == 0) - { - if (mt.SqlDbType == SqlDbType.Binary || mt.SqlDbType == SqlDbType.VarBinary) - { - maxLen = SmiMetaData.MaxBinaryLength; - } - else if (mt.SqlDbType == SqlDbType.Char || mt.SqlDbType == SqlDbType.VarChar) - { - maxLen = SmiMetaData.MaxANSICharacters; - } - else if (mt.SqlDbType == SqlDbType.NChar || mt.SqlDbType == SqlDbType.NVarChar) - { - maxLen = SmiMetaData.MaxUnicodeCharacters; - } - } - else if ( - (maxLen > SmiMetaData.MaxBinaryLength && (SqlDbType.Binary == mt.SqlDbType || SqlDbType.VarBinary == mt.SqlDbType)) || - (maxLen > SmiMetaData.MaxANSICharacters && (SqlDbType.Char == mt.SqlDbType || SqlDbType.VarChar == mt.SqlDbType)) || - (maxLen > SmiMetaData.MaxUnicodeCharacters && (SqlDbType.NChar == mt.SqlDbType || SqlDbType.NVarChar == mt.SqlDbType)) - ) - { - maxLen = -1; - } - - - int localeId = LocaleId; - if (localeId == 0 && mt.IsCharType) - { - if (GetCoercedValue() is SqlString sqlString && !sqlString.IsNull) - { - localeId = sqlString.LCID; - } - else - { - localeId = CultureInfo.CurrentCulture.LCID; - } - } - - SqlCompareOptions compareOpts = CompareInfo; - if (compareOpts == 0 && mt.IsCharType) - { - if (GetCoercedValue() is SqlString sqlString && !sqlString.IsNull) - { - compareOpts = sqlString.SqlCompareOptions; - } - else - { - compareOpts = SmiMetaData.GetDefaultForType(mt.SqlDbType).CompareOptions; - } - } - - string typeSpecificNamePart1 = null; - string typeSpecificNamePart2 = null; - string typeSpecificNamePart3 = null; - - if (SqlDbType.Xml == mt.SqlDbType) - { - typeSpecificNamePart1 = XmlSchemaCollectionDatabase; - typeSpecificNamePart2 = XmlSchemaCollectionOwningSchema; - typeSpecificNamePart3 = XmlSchemaCollectionName; - } - else if (SqlDbType.Udt == mt.SqlDbType || (SqlDbType.Structured == mt.SqlDbType && !string.IsNullOrEmpty(TypeName))) - { - // Split the input name. The type name is specified as single 3 part name. - // NOTE: ParseTypeName throws if format is incorrect - string[] names; - if (mt.SqlDbType == SqlDbType.Udt) - { - names = ParseTypeName(UdtTypeName, true /* is UdtTypeName */); - } - else - { - names = ParseTypeName(TypeName, false /* not UdtTypeName */); - } - - if (names.Length == 1) - { - typeSpecificNamePart3 = names[0]; - } - else if (names.Length == 2) - { - typeSpecificNamePart2 = names[0]; - typeSpecificNamePart3 = names[1]; - } - else if (names.Length == 3) - { - typeSpecificNamePart1 = names[0]; - typeSpecificNamePart2 = names[1]; - typeSpecificNamePart3 = names[2]; - } - else - { - throw ADP.ArgumentOutOfRange(nameof(names)); - } - - if ( - (!string.IsNullOrEmpty(typeSpecificNamePart1) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart1.Length) || - (!string.IsNullOrEmpty(typeSpecificNamePart2) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart2.Length) || - (!string.IsNullOrEmpty(typeSpecificNamePart3) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart3.Length) - ) - { - throw ADP.ArgumentOutOfRange(nameof(names)); - } - } - - byte precision = GetActualPrecision(); - byte scale = GetActualScale(); - - // precision for decimal types may still need adjustment. - if (mt.SqlDbType == SqlDbType.Decimal) - { - if (precision == 0) - { - precision = TdsEnums.DEFAULT_NUMERIC_PRECISION; - } - } - - // Sub-field determination - List fields = null; - SmiMetaDataPropertyCollection extendedProperties = null; - if (mt.SqlDbType == SqlDbType.Structured) - { - GetActualFieldsAndProperties(out fields, out extendedProperties, out peekAhead); - } - - return new SmiParameterMetaData( - mt.SqlDbType, - maxLen, - precision, - scale, - localeId, - compareOpts, - null, // Udt type not used for parameters - SqlDbType.Structured == mt.SqlDbType, - fields, - extendedProperties, - ParameterNameFixed, - typeSpecificNamePart1, - typeSpecificNamePart2, - typeSpecificNamePart3, - Direction - ); - } - - [Conditional("DEBUG")] - internal void AssertCachedPropertiesAreValid() - { - AssertPropertiesAreValid(_coercedValue, HasFlag(SqlParameterFlags.CoercedValueIsSqlType), HasFlag(SqlParameterFlags.CoercedValueIsDataFeed), IsNull); - } - - [Conditional("DEBUG")] - internal void AssertPropertiesAreValid(object value, bool? isSqlType = null, bool? isDataFeed = null, bool? isNull = null) - { - Debug.Assert(!isSqlType.HasValue || (isSqlType.Value == (value is INullable)), "isSqlType is incorrect"); - Debug.Assert(!isDataFeed.HasValue || (isDataFeed.Value == (value is DataFeed)), "isDataFeed is incorrect"); - Debug.Assert(!isNull.HasValue || (isNull.Value == ADP.IsNull(value)), "isNull is incorrect"); - } - - private SqlDbType GetMetaSqlDbTypeOnly() - { - MetaType metaType = _metaType; - if (null == metaType) - { // infer the type from the value - metaType = MetaType.GetDefaultMetaType(); - } - return metaType.SqlDbType; - } - - // This may not be a good thing to do in case someone overloads the parameter type but I - // don't want to go from SqlDbType -> metaType -> TDSType - private MetaType GetMetaTypeOnly() - { - if (_metaType != null) - { - return _metaType; - } - if (null != _value && DBNull.Value != _value) - { - // We have a value set by the user then just use that value - // char and char[] are not directly supported so we convert those values to string - Type valueType = _value.GetType(); - if (valueType == typeof(char)) - { - _value = _value.ToString(); - valueType = typeof(string); - } - else if (valueType == typeof(char[])) - { - _value = new string((char[])_value); - valueType = typeof(string); - } - return MetaType.GetMetaTypeFromType(valueType); - } - else if (_sqlBufferReturnValue != null) - { // value came back from the server - Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); - if (valueType != null) - { - return MetaType.GetMetaTypeFromType(valueType); - } - } - return MetaType.GetDefaultMetaType(); - } - - internal void Prepare(SqlCommand cmd) - { - if (_metaType == null) - { - throw ADP.PrepareParameterType(cmd); - } - else if (!ShouldSerializeSize() && !_metaType.IsFixed) - { - throw ADP.PrepareParameterSize(cmd); - } - else if ((!ShouldSerializePrecision() && !ShouldSerializeScale()) && (_metaType.SqlDbType == SqlDbType.Decimal)) - { - throw ADP.PrepareParameterScale(cmd, SqlDbType.ToString()); - } - } - - private void PropertyChanging() - { - _internalMetaType = null; - } - - private void PropertyTypeChanging() - { - PropertyChanging(); - CoercedValue = null; - } - - internal void ResetParent() => _parent = null; - - private void SetFlag(SqlParameterFlags flag, bool value) - { - _flags = value ? _flags | flag : _flags & ~flag; - } - - internal void SetSqlBuffer(SqlBuffer buff) - { - _sqlBufferReturnValue = buff; - _value = null; - _coercedValue = null; - SetFlag(SqlParameterFlags.IsNull, _sqlBufferReturnValue.IsNull); - SetFlag(SqlParameterFlags.CoercedValueIsDataFeed, false); - SetFlag(SqlParameterFlags.CoercedValueIsSqlType, false); - _udtLoadError = null; - _actualSize = -1; - } - - internal void SetUdtLoadError(Exception e) - { - _udtLoadError = e; - } - - internal void Validate(int index, bool isCommandProc) - { - MetaType metaType = GetMetaTypeOnly(); - _internalMetaType = metaType; - - // SqlParameter does a Size Validation check and would fail if the size is 0. - // This condition filters all scenarios where we view a valid size 0. - if ( - ADP.IsDirection(this, ParameterDirection.Output) && - !ADP.IsDirection(this, ParameterDirection.ReturnValue) && - (!metaType.IsFixed) && - !ShouldSerializeSize() && - ((null == _value) || Convert.IsDBNull(_value)) && - (SqlDbType != SqlDbType.Timestamp) && - (SqlDbType != SqlDbType.Udt) && - // BUG: (VSTFDevDiv - 479609): Output parameter with size 0 throws for XML, TEXT, NTEXT, IMAGE. - // NOTE: (VSTFDevDiv - 479609): Not Fixed for TEXT, NTEXT, IMAGE as these are deprecated LOB types. - (SqlDbType != SqlDbType.Xml) && - !metaType.IsVarTime - ) - { - throw ADP.UninitializedParameterSize(index, metaType.ClassType); - } - - if (metaType.SqlDbType != SqlDbType.Udt && Direction != ParameterDirection.Output) - { - GetCoercedValue(); - } - - //check if the UdtTypeName is specified for Udt params - if (metaType.SqlDbType == SqlDbType.Udt) - { - if (string.IsNullOrEmpty(UdtTypeName)) - { - throw SQL.MustSetUdtTypeNameForUdtParams(); - } - } - else if (!string.IsNullOrEmpty(UdtTypeName)) - { - throw SQL.UnexpectedUdtTypeNameForNonUdtParams(); - } - - // Validate structured-type-specific details. - if (metaType.SqlDbType == SqlDbType.Structured) - { - if (!isCommandProc && string.IsNullOrEmpty(TypeName)) - { - throw SQL.MustSetTypeNameForParam(metaType.TypeName, ParameterName); - } - - if (Direction != ParameterDirection.Input) - { - throw SQL.UnsupportedTVPOutputParameter(Direction, ParameterName); - } - - if (GetCoercedValue() == DBNull.Value) - { - throw SQL.DBNullNotSupportedForTVPValues(ParameterName); - } - } - else if (!string.IsNullOrEmpty(TypeName)) - { - throw SQL.UnexpectedTypeNameForNonStructParams(ParameterName); - } - } - - // func will change type to that with a 4 byte length if the type has a two - // byte length and a parameter length > than that expressible in 2 bytes - internal MetaType ValidateTypeLengths(bool is2005OrNewer) - { - MetaType mt = InternalMetaType; - // Since the server will automatically reject any - // char, varchar, binary, varbinary, nchar, or nvarchar parameter that has a - // byte sizeInCharacters > 8000 bytes, we promote the parameter to image, text, or ntext. This - // allows the user to specify a parameter type using a COM+ datatype and be able to - // use that parameter against a BLOB column. - if ((mt.SqlDbType != SqlDbType.Udt) && !mt.IsFixed && !mt.IsLong) - { // if type has 2 byte length - long actualSizeInBytes = GetActualSize(); - long sizeInCharacters = Size; - - // Bug: VSTFDevDiv #636867 - // Notes: - // 'actualSizeInBytes' is the size of value passed; - // 'sizeInCharacters' is the parameter size; - // 'actualSizeInBytes' is in bytes; - // 'this.Size' is in charaters; - // 'sizeInCharacters' is in characters; - // 'TdsEnums.TYPE_SIZE_LIMIT' is in bytes; - // For Non-NCharType and for non-2005 or greater variables, size should be maintained; - // Reverting changes from bug VSTFDevDiv # 479739 as it caused an regression; - // Modifed variable names from 'size' to 'sizeInCharacters', 'actualSize' to 'actualSizeInBytes', and - // 'maxSize' to 'maxSizeInBytes' - // The idea is to - // 1) revert the regression from bug 479739 - // 2) fix as many scenarios as possible including bug 636867 - // 3) cause no additional regression from 3.5 sp1 - // Keeping these goals in mind - the following are the changes we are making - - long maxSizeInBytes; - if (mt.IsNCharType && is2005OrNewer) - { - maxSizeInBytes = ((sizeInCharacters * sizeof(char)) > actualSizeInBytes) ? sizeInCharacters * sizeof(char) : actualSizeInBytes; - } - else - { - // Notes: - // Elevation from (n)(var)char (4001+) to (n)text succeeds without failure only with 2005 and greater. - // it fails in sql server 2000 - maxSizeInBytes = (sizeInCharacters > actualSizeInBytes) ? sizeInCharacters : actualSizeInBytes; - } - - if ( - (maxSizeInBytes > TdsEnums.TYPE_SIZE_LIMIT) || - HasFlag(SqlParameterFlags.CoercedValueIsDataFeed) || - (sizeInCharacters == -1) || - (actualSizeInBytes == -1) - ) - { // is size > size able to be described by 2 bytes - if (is2005OrNewer) - { - // Convert the parameter to its max type - mt = MetaType.GetMaxMetaTypeFromMetaType(mt); - _metaType = mt; - InternalMetaType = mt; - if (!mt.IsPlp) - { - if (mt.SqlDbType == SqlDbType.Xml) - { - throw ADP.InvalidMetaDataValue(); //Xml should always have IsPartialLength = true - } - if ( - mt.SqlDbType == SqlDbType.NVarChar || - mt.SqlDbType == SqlDbType.VarChar || - mt.SqlDbType == SqlDbType.VarBinary - ) - { - Size = (int)SmiMetaData.UnlimitedMaxLengthIndicator; - } - } - } - else - { - switch (mt.SqlDbType) - { // widening the SqlDbType is automatic - case SqlDbType.Binary: - case SqlDbType.VarBinary: - mt = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.Image, false); - _metaType = mt; // do not use SqlDbType property which calls PropertyTypeChanging resetting coerced value - InternalMetaType = mt; - break; - case SqlDbType.Char: - case SqlDbType.VarChar: - mt = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.Text, false); - _metaType = mt; - InternalMetaType = mt; - break; - case SqlDbType.NChar: - case SqlDbType.NVarChar: - mt = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.NText, false); - _metaType = mt; - InternalMetaType = mt; - break; - default: - Debug.Assert(false, "Missed metatype in SqlCommand.BuildParamList()"); - break; - } - } - } - } - return mt; - } - - private byte ValuePrecision(object value) - { - if (value is SqlDecimal sqlDecimal) - { - if (sqlDecimal.IsNull) - { - return 0; - } - return sqlDecimal.Precision; - } - return ValuePrecisionCore(value); - } - - private byte ValueScale(object value) - { - if (value is SqlDecimal sqlDecimal) - { - if (sqlDecimal.IsNull) - { - return 0; - } - return sqlDecimal.Scale; - } - return ValueScaleCore(value); - } - - private int ValueSize(object value) - { - if (value is SqlString sqlString) - { - if (sqlString.IsNull) - { - return 0; - } - return sqlString.Value.Length; - } - if (value is SqlChars sqlChars) - { - if (sqlChars.IsNull) - { - return 0; - } - return sqlChars.Value.Length; - } - - if (value is SqlBinary sqlBinary) - { - if (sqlBinary.IsNull) - { - return 0; - } - return sqlBinary.Length; - } - if (value is SqlBytes sqlBytes) - { - if (sqlBytes.IsNull) - { - return 0; - } - return (int)(sqlBytes.Length); - } - if (value is DataFeed) - { - // Unknown length - return 0; - } - return ValueSizeCore(value); - } - - private byte ValuePrecisionCore(object value) - { - if (value is decimal decimalValue) - { - return ((SqlDecimal)decimalValue).Precision; - } - return 0; - } - - private byte ValueScaleCore(object value) - { - if (value is decimal decimalValue) - { - return (byte)((decimal.GetBits(decimalValue)[3] & 0x00ff0000) >> 0x10); - } - return 0; - } - - private int ValueSizeCore(object value) - { - if (!ADP.IsNull(value)) - { - switch (value) - { - case string svalue: - return svalue.Length; - case byte[] bvalue: - return bvalue.Length; - case char[] cvalue: - return cvalue.Length; - case byte _: - case char _: - return 1; - } - } - return 0; - } - - - // Coerced Value is also used in SqlBulkCopy.ConvertValue(object value, _SqlMetaData metadata) - internal static object CoerceValue(object value, MetaType destinationType, out bool coercedToDataFeed, out bool typeChanged, bool allowStreaming = true) - { - Debug.Assert(!(value is DataFeed), "Value provided should not already be a data feed"); - Debug.Assert(!ADP.IsNull(value), "Value provided should not be null"); - Debug.Assert(null != destinationType, "null destinationType"); - - coercedToDataFeed = false; - typeChanged = false; - Type currentType = value.GetType(); - - if ( - (destinationType.ClassType != typeof(object)) && - (destinationType.ClassType != currentType) && - ( - (destinationType.SqlType != currentType) || - (destinationType.SqlDbType == SqlDbType.Xml) - ) - ) - { // Special case for Xml types (since we need to convert SqlXml into a string) - try - { - // Assume that the type changed - typeChanged = true; - if (destinationType.ClassType == typeof(string)) - { - // For Xml data, destination Type is always string - if (currentType == typeof(SqlXml)) - { - value = MetaType.GetStringFromXml((XmlReader)(((SqlXml)value).CreateReader())); - } - else if (currentType == typeof(SqlString)) - { - typeChanged = false; // Do nothing - } - else if (typeof(XmlReader).IsAssignableFrom(currentType)) - { - if (allowStreaming) - { - coercedToDataFeed = true; - value = new XmlDataFeed((XmlReader)value); - } - else - { - value = MetaType.GetStringFromXml((XmlReader)value); - } - } - else if (currentType == typeof(char[])) - { - value = new string((char[])value); - } - else if (currentType == typeof(SqlChars)) - { - value = new string(((SqlChars)value).Value); - } - else if (value is TextReader textReader && allowStreaming) - { - coercedToDataFeed = true; - value = new TextDataFeed(textReader); - } - else - { - value = Convert.ChangeType(value, destinationType.ClassType, null); - } - } - else if ((destinationType.DbType == DbType.Currency) && (currentType == typeof(string))) - { - value = decimal.Parse((string)value, NumberStyles.Currency, null); - } - else if ((currentType == typeof(SqlBytes)) && (destinationType.ClassType == typeof(byte[]))) - { - typeChanged = false; // Do nothing - } - else if ((currentType == typeof(string)) && (destinationType.SqlDbType == SqlDbType.Time)) - { - value = TimeSpan.Parse((string)value); - } - else if ((currentType == typeof(string)) && (destinationType.SqlDbType == SqlDbType.DateTimeOffset)) - { - value = DateTimeOffset.Parse((string)value, (IFormatProvider)null); - } - else if ((currentType == typeof(DateTime)) && (destinationType.SqlDbType == SqlDbType.DateTimeOffset)) - { - value = new DateTimeOffset((DateTime)value); - } - else if ( - TdsEnums.SQLTABLE == destinationType.TDSType && - ( - value is DataTable || - value is DbDataReader || - value is IEnumerable - ) - ) - { - // no conversion for TVPs. - typeChanged = false; - } - else if (destinationType.ClassType == typeof(byte[]) && allowStreaming && value is Stream stream) - { - coercedToDataFeed = true; - value = new StreamDataFeed(stream); - } - else - { - value = Convert.ChangeType(value, destinationType.ClassType, null); - } - } - catch (Exception e) - { - if (!ADP.IsCatchableExceptionType(e)) - { - throw; - } - - throw ADP.ParameterConversionFailed(value, destinationType.ClassType, e); - } - } - - Debug.Assert(allowStreaming || !coercedToDataFeed, "Streaming is not allowed, but type was coerced into a data feed"); - Debug.Assert(value.GetType() == currentType ^ typeChanged, "Incorrect value for typeChanged"); - return value; - } - - private static int StringSize(object value, bool isSqlType) - { - if (isSqlType) - { - Debug.Assert(!((INullable)value).IsNull, "Should not call StringSize on null values"); - if (value is SqlString sqlString) - { - return sqlString.Value.Length; - } - if (value is SqlChars sqlChars) - { - return sqlChars.Value.Length; - } - } - else - { - if (value is string svalue) - { - return svalue.Length; - } - if (value is char[] cvalue) - { - return cvalue.Length; - } - if (value is char) - { - return 1; - } - } - - // Didn't match, unknown size - return 0; - } - - private static int BinarySize(object value, bool isSqlType) - { - if (isSqlType) - { - Debug.Assert(!((INullable)value).IsNull, "Should not call StringSize on null values"); - if (value is SqlBinary sqlBinary) - { - return sqlBinary.Length; - } - if (value is SqlBytes sqlBytes) - { - return sqlBytes.Value.Length; - } - } - else - { - if (value is byte[] bvalue) - { - return bvalue.Length; - } - if (value is byte) - { - return 1; - } - } - - // Didn't match, unknown size - return 0; - } - - // parse an string of the form db.schema.name where any of the three components - // might have "[" "]" and dots within it. - // returns: - // [0] dbname (or null) - // [1] schema (or null) - // [2] name - // NOTE: if perf/space implications of Regex is not a problem, we can get rid - // of this and use a simple regex to do the parsing - internal static string[] ParseTypeName(string typeName, bool isUdtTypeName) - { - Debug.Assert(null != typeName, "null typename passed to ParseTypeName"); - - try - { - string errorMsg = isUdtTypeName ? Strings.SQL_UDTTypeName : Strings.SQL_TypeName; - return MultipartIdentifier.ParseMultipartIdentifier(typeName, "[\"", "]\"", '.', 3, true, errorMsg, true); - } - catch (ArgumentException) - { - if (isUdtTypeName) - { - throw SQL.InvalidUdt3PartNameFormat(); - } - else - { - throw SQL.InvalidParameterTypeNameFormat(); - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs similarity index 90% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs index 9845a03aa7..c78ef7dfc7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -44,7 +44,6 @@ internal TextDataFeed(TextReader source) { _source = source; } - internal static UnicodeEncoding DefaultEncoding { get @@ -64,13 +63,10 @@ internal class XmlDataFeed : DataFeed { internal XmlReader _source; - internal XmlDataFeed(XmlReader source) - { - _source = source; - } + internal XmlDataFeed(XmlReader source) => _source = source; } - /// + /// [TypeConverter(typeof(SqlParameter.SqlParameterConverter))] public sealed partial class SqlParameter : DbParameter, IDbDataParameter, ICloneable { @@ -247,7 +243,7 @@ private enum SqlParameterFlags : ushort private DataRowVersion _sourceVersion; private SqlParameterFlags _flags; - /// + /// public SqlParameter() : base() { _flags = SqlParameterFlags.IsNull; @@ -255,14 +251,14 @@ public SqlParameter() : base() _direction = ParameterDirection.Input; } - /// + /// public SqlParameter(string parameterName, SqlDbType dbType) : this() { ParameterName = parameterName; SqlDbType = dbType; } - /// + /// public SqlParameter(string parameterName, object value) : this() { Debug.Assert(!(value is SqlDbType), "use SqlParameter(string, SqlDbType)"); @@ -271,7 +267,7 @@ public SqlParameter(string parameterName, object value) : this() Value = value; } - /// + /// public SqlParameter(string parameterName, SqlDbType dbType, int size) : this() { ParameterName = parameterName; @@ -279,7 +275,7 @@ public SqlParameter(string parameterName, SqlDbType dbType, int size) : this() Size = size; } - /// + /// public SqlParameter(string parameterName, SqlDbType dbType, int size, string sourceColumn) : this() { ParameterName = parameterName; @@ -288,7 +284,7 @@ public SqlParameter(string parameterName, SqlDbType dbType, int size, string sou SourceColumn = sourceColumn; } - /// + /// [EditorBrowsable(EditorBrowsableState.Advanced)] public SqlParameter( string parameterName, @@ -306,13 +302,18 @@ object value { Direction = direction; IsNullable = isNullable; +#if NETFRAMEWORK + PrecisionInternal = precision; + ScaleInternal = scale; +#else Precision = precision; Scale = scale; +#endif SourceVersion = sourceVersion; Value = value; } - /// + /// public SqlParameter( string parameterName, SqlDbType dbType, @@ -334,8 +335,13 @@ string xmlSchemaCollectionName SqlDbType = dbType; Size = size; Direction = direction; +#if NETFRAMEWORK + PrecisionInternal = precision; + ScaleInternal = scale; +#else Precision = precision; Scale = scale; +#endif SourceColumn = sourceColumn; SourceVersion = sourceVersion; SourceColumnNullMapping = sourceColumnNullMapping; @@ -381,7 +387,7 @@ internal bool HasReceivedMetadata /// internal byte NormalizationRuleVersion => CipherMetadata?.NormalizationRuleVersion ?? 0x00; - /// + /// [Browsable(false)] public SqlCompareOptions CompareInfo { @@ -417,34 +423,34 @@ public SqlCompareOptions CompareInfo } } - /// - [ResCategory("XML")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Xml)] public string XmlSchemaCollectionDatabase { get => _xmlSchemaCollection?.Database ?? string.Empty; set => EnsureXmlSchemaCollection().Database = value; } - /// - [ResCategory("XML")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Xml)] public string XmlSchemaCollectionOwningSchema { get => _xmlSchemaCollection?.OwningSchema ?? string.Empty; set => EnsureXmlSchemaCollection().OwningSchema = value; } - /// - [ResCategory("XML")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Xml)] public string XmlSchemaCollectionName { get => _xmlSchemaCollection?.Name ?? string.Empty; set => EnsureXmlSchemaCollection().Name = value; } - /// + /// [ DefaultValue(false), - ResCategory("Data") + ResCategory(StringsHelper.ResourceNames.DataCategory_Data) ] public bool ForceColumnEncryption { @@ -452,7 +458,7 @@ public bool ForceColumnEncryption set => SetFlag(SqlParameterFlags.ForceColumnEncryption, value); } - /// + /// public override DbType DbType { get => GetMetaTypeOnly().DbType; @@ -467,11 +473,11 @@ public override DbType DbType } } - /// + /// public override void ResetDbType() => ResetSqlDbType(); - /// - [ResCategory("Data")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Data)] public override string ParameterName { get => _parameterName ?? string.Empty; @@ -499,7 +505,7 @@ public override string ParameterName } } - /// + /// [Browsable(false)] public int LocaleId { @@ -529,10 +535,10 @@ public int LocaleId } } - /// + /// [ DefaultValue((byte)0), - ResCategory("Data") + ResCategory(StringsHelper.ResourceNames.DataCategory_Data) ] public new byte Precision { @@ -542,10 +548,10 @@ public int LocaleId private bool ShouldSerializePrecision() => _precision != 0; - /// + /// [ DefaultValue((byte)0), - ResCategory("Data") + ResCategory(StringsHelper.ResourceNames.DataCategory_Data) ] public new byte Scale { @@ -579,10 +585,10 @@ internal byte ScaleInternal private bool ShouldSerializeScale() => _scale != 0; // V1.0 compat, ignore _hasScale - /// + /// [ RefreshProperties(RefreshProperties.All), - ResCategory("Data"), + ResCategory(StringsHelper.ResourceNames.DataCategory_Data), DbProviderSpecificTypeProperty(true) ] public SqlDbType SqlDbType @@ -612,7 +618,7 @@ public SqlDbType SqlDbType private bool ShouldSerializeSqlDbType() => _metaType != null; - /// + /// public void ResetSqlDbType() { if (_metaType != null) @@ -622,7 +628,7 @@ public void ResetSqlDbType() } } - /// + /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), @@ -673,7 +679,7 @@ public object SqlValue } } - /// + /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) @@ -684,7 +690,7 @@ public string UdtTypeName set => _udtTypeName = value; } - /// + /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced) @@ -699,10 +705,10 @@ public string TypeName } } - /// + /// [ RefreshProperties(RefreshProperties.All), - ResCategory("Data"), + ResCategory(StringsHelper.ResourceNames.DataCategory_Data), TypeConverter(typeof(StringConverter)), ] public override object Value @@ -741,10 +747,10 @@ public override object Value } } - /// + /// [ RefreshProperties(RefreshProperties.All), - ResCategory("Data"), + ResCategory(StringsHelper.ResourceNames.DataCategory_Data), ] public override ParameterDirection Direction { @@ -769,14 +775,14 @@ public override ParameterDirection Direction } } - /// + /// public override bool IsNullable { get => HasFlag(SqlParameterFlags.IsNullable); set => SetFlag(SqlParameterFlags.IsNullable, value); } - /// + /// public int Offset { get => _offset; @@ -790,8 +796,8 @@ public int Offset } } - /// - [ResCategory("Data")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Data)] public override int Size { get @@ -828,28 +834,30 @@ private void ResetSize() private bool ShouldSerializeSize() => _size != 0; - /// - [ResCategory("Update")] + /// + [ResCategory(StringsHelper.ResourceNames.DataCategory_Update)] public override string SourceColumn { get => _sourceColumn ?? string.Empty; set => _sourceColumn = value; } - /// + /// [ResCategory("DataCategory_Update")] +#if !NETFRAMEWORK [ResDescription(StringsHelper.ResourceNames.SqlParameter_SourceColumnNullMapping)] +#endif public override bool SourceColumnNullMapping { get => HasFlag(SqlParameterFlags.SourceColumnNullMapping); set => SetFlag(SqlParameterFlags.SourceColumnNullMapping, value); } - /// + /// [ResCategory("Data")] public override string ToString() => ParameterName; - /// + /// [ResCategory(StringsHelper.ResourceNames.DataCategory_Update)] public override DataRowVersion SourceVersion { @@ -874,9 +882,11 @@ public override DataRowVersion SourceVersion } } - /// + + /// object ICloneable.Clone() => new SqlParameter(this); + private object CoercedValue { get => _coercedValue; @@ -993,6 +1003,8 @@ internal string ParameterNameFixed } } + internal bool SizeInferred => 0 == _size; + internal INullable ValueAsINullable => _valueAsINullable; internal bool IsDerivedParameterTypeName @@ -1048,6 +1060,10 @@ internal void CopyTo(SqlParameter destination) internal object CompareExchangeParent(object value, object comparand) { + // the interlock guarantees same parameter won't belong to multiple collections + // at the same time, but to actually occur the user must really try + // since we never declared thread safety, we don't care at this time + //return System.Threading.Interlocked.CompareExchange(ref _parent, value, comparand); object parent = _parent; if (comparand == parent) { @@ -1500,7 +1516,7 @@ internal int GetActualSize() case SqlDbType.NText: case SqlDbType.Xml: { - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; + coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType)) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1515,7 +1531,7 @@ internal int GetActualSize() case SqlDbType.Text: { // for these types, ActualSize is the num of chars, not actual bytes - since non-unicode chars are not always uniform size - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; + coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (StringSize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = (ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize; if (_actualSize == -1) @@ -1528,7 +1544,7 @@ internal int GetActualSize() case SqlDbType.VarBinary: case SqlDbType.Image: case SqlDbType.Timestamp: - coercedSize = ((!HasFlag(SqlParameterFlags.IsNull)) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; + coercedSize = (!HasFlag(SqlParameterFlags.IsNull) && (!HasFlag(SqlParameterFlags.CoercedValueIsDataFeed))) ? (BinarySize(val, HasFlag(SqlParameterFlags.CoercedValueIsSqlType))) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); if (_actualSize == -1) @@ -1539,7 +1555,12 @@ internal int GetActualSize() case SqlDbType.Udt: if (!IsNull) { +#if NETFRAMEWORK + //call the static function + coercedSize = AssemblyCache.GetLength(val); +#else coercedSize = SerializationHelperSql9.SizeInBytes(val); +#endif } break; case SqlDbType.Structured: @@ -1671,6 +1692,7 @@ internal SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue peekAhe maxLen = -1; } + int localeId = LocaleId; if (localeId == 0 && mt.IsCharType) { @@ -1866,10 +1888,7 @@ internal void Prepare(SqlCommand cmd) } } - private void PropertyChanging() - { - _internalMetaType = null; - } + private void PropertyChanging() => _internalMetaType = null; private void PropertyTypeChanging() { @@ -1879,6 +1898,11 @@ private void PropertyTypeChanging() internal void ResetParent() => _parent = null; + private void SetFlag(SqlParameterFlags flag, bool value) + { + _flags = value ? _flags | flag : _flags & ~flag; + } + internal void SetSqlBuffer(SqlBuffer buff) { _sqlBufferReturnValue = buff; @@ -1891,15 +1915,7 @@ internal void SetSqlBuffer(SqlBuffer buff) _actualSize = -1; } - private void SetFlag(SqlParameterFlags flag, bool value) - { - _flags = value ? _flags | flag : _flags & ~flag; - } - - internal void SetUdtLoadError(Exception e) - { - _udtLoadError = e; - } + internal void SetUdtLoadError(Exception e) => _udtLoadError = e; internal void Validate(int index, bool isCommandProc) { @@ -1916,6 +1932,8 @@ internal void Validate(int index, bool isCommandProc) ((null == _value) || Convert.IsDBNull(_value)) && (SqlDbType != SqlDbType.Timestamp) && (SqlDbType != SqlDbType.Udt) && + // BUG: (VSTFDevDiv - 479609): Output parameter with size 0 throws for XML, TEXT, NTEXT, IMAGE. + // NOTE: (VSTFDevDiv - 479609): Not Fixed for TEXT, NTEXT, IMAGE as these are deprecated LOB types. (SqlDbType != SqlDbType.Xml) && !metaType.IsVarTime ) @@ -1980,6 +1998,24 @@ internal MetaType ValidateTypeLengths() long actualSizeInBytes = GetActualSize(); long sizeInCharacters = Size; + // Bug: VSTFDevDiv #636867 + // Notes: + // 'actualSizeInBytes' is the size of value passed; + // 'sizeInCharacters' is the parameter size; + // 'actualSizeInBytes' is in bytes; + // 'this.Size' is in charaters; + // 'sizeInCharacters' is in characters; + // 'TdsEnums.TYPE_SIZE_LIMIT' is in bytes; + // For Non-NCharType and for non-2005 or greater variables, size should be maintained; + // Reverting changes from bug VSTFDevDiv # 479739 as it caused an regression; + // Modifed variable names from 'size' to 'sizeInCharacters', 'actualSize' to 'actualSizeInBytes', and + // 'maxSize' to 'maxSizeInBytes' + // The idea is to + // 1) revert the regression from bug 479739 + // 2) fix as many scenarios as possible including bug 636867 + // 3) cause no additional regression from 3.5 sp1 + // Keeping these goals in mind - the following are the changes we are making + long maxSizeInBytes; if (mt.IsNCharType) { @@ -1998,9 +2034,8 @@ internal MetaType ValidateTypeLengths() HasFlag(SqlParameterFlags.CoercedValueIsDataFeed) || (sizeInCharacters == -1) || (actualSizeInBytes == -1) - ) - { // is size > size able to be described by 2 bytes - // Convert the parameter to its max type + ) + { mt = MetaType.GetMaxMetaTypeFromMetaType(mt); _metaType = mt; InternalMetaType = mt; @@ -2131,6 +2166,7 @@ private int ValueSizeCore(object value) return 0; } + // Coerced Value is also used in SqlBulkCopy.ConvertValue(object value, _SqlMetaData metadata) internal static object CoerceValue(object value, MetaType destinationType, out bool coercedToDataFeed, out bool typeChanged, bool allowStreaming = true) {