diff --git a/src/Common/src/System/Data/Common/AdapterUtil.cs b/src/Common/src/System/Data/Common/AdapterUtil.cs index e260a9c322fe..8070af7548e3 100644 --- a/src/Common/src/System/Data/Common/AdapterUtil.cs +++ b/src/Common/src/System/Data/Common/AdapterUtil.cs @@ -361,24 +361,33 @@ internal static Exception StreamClosed([CallerMemberName] string method = "") internal static string BuildQuotedString(string quotePrefix, string quoteSuffix, string unQuotedString) { - var resultString = new StringBuilder(); + var resultString = new StringBuilder(unQuotedString.Length + quoteSuffix.Length + quoteSuffix.Length); + AppendQuotedString(resultString, quotePrefix, quoteSuffix, unQuotedString); + return resultString.ToString(); + } + + internal static string AppendQuotedString(StringBuilder buffer, string quotePrefix, string quoteSuffix, string unQuotedString) + { + if (!string.IsNullOrEmpty(quotePrefix)) { - resultString.Append(quotePrefix); + buffer.Append(quotePrefix); } // Assuming that the suffix is escaped by doubling it. i.e. foo"bar becomes "foo""bar". if (!string.IsNullOrEmpty(quoteSuffix)) { - resultString.Append(unQuotedString.Replace(quoteSuffix, quoteSuffix + quoteSuffix)); - resultString.Append(quoteSuffix); + int start = buffer.Length; + buffer.Append(unQuotedString); + buffer.Replace(quoteSuffix, quoteSuffix + quoteSuffix, start, unQuotedString.Length); + buffer.Append(quoteSuffix); } else { - resultString.Append(unQuotedString); + buffer.Append(unQuotedString); } - return resultString.ToString(); + return buffer.ToString(); } // diff --git a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs index 6c63de6e2a30..a5cc9627fb23 100644 --- a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs +++ b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs @@ -2222,6 +2222,13 @@ internal void DeriveParameters() p.TypeName = r[colNames[(int)ProcParamsColIndex.TypeCatalogName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeSchemaName]] + "." + r[colNames[(int)ProcParamsColIndex.TypeName]]; + + // the constructed type name above is incorrectly formatted, it should be a 2 part name not 3 + // for compatibility we can't change this because the bug has existed for a long time and been + // worked around by users, so identify that it is present and catch it later in the execution + // process once users can no longer interact with with the parameter type name + p.IsDerivedParameterTypeName = true; + } // XmlSchema name for Xml types @@ -3321,6 +3328,23 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP // set default value bit if (parameter.Direction != ParameterDirection.Output) { + // detect incorrectly derived type names unchanged by the caller and fix them + if (parameter.IsDerivedParameterTypeName) + { + string[] parts = MultipartIdentifier.ParseMultipartIdentifier(parameter.TypeName, "[\"", "]\"", SR.SQL_TDSParserTableName, false); + if (parts != null && parts.Length==4) // will always return int[4] right justified + { + if ( + parts[3] != null && // name must not be null + parts[2] != null && // schema must not be null + parts[1] != null // server should not be null or we don't need to remove it + ) + { + parameter.TypeName = QuoteIdentifier(parts.AsSpan(2,2)); + } + } + } + // remember that null == Convert.IsEmpty, DBNull.Value is a database null! // Don't assume a default value exists for parameters in the case when @@ -3665,9 +3689,14 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete // Adds quotes to each part of a SQL identifier that may be multi-part, while leaving // the result as a single composite name. - private string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) + private static string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) { string[] strings = SqlParameter.ParseTypeName(identifier, isUdtTypeName); + return QuoteIdentifier(strings); + } + + private static string QuoteIdentifier(ReadOnlySpan strings) + { StringBuilder bld = new StringBuilder(); // Stitching back together is a little tricky. Assume we want to build a full multi-part name @@ -3682,7 +3711,7 @@ private string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) } if (null != strings[i] && 0 != strings[i].Length) { - bld.Append(ADP.BuildQuotedString("[", "]", strings[i])); + ADP.AppendQuotedString(bld, "[", "]", strings[i]); } } diff --git a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs index 46a89e62b1f3..d2d8fa52d68f 100644 --- a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs +++ b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameter.cs @@ -74,12 +74,23 @@ internal XmlDataFeed(XmlReader source) [TypeConverter(typeof(SqlParameterConverter))] public sealed partial class SqlParameter : DbParameter, IDbDataParameter, ICloneable { - private MetaType _metaType; + [Flags] + private enum SqlParameterFlags : short + { + None = 0, + IsNullable = 1 << 1, + IsNull = 1 << 2, + IsSqlParameterSqlType = 1 << 3, + CoercedValueIsSqlType = 1 << 4, + CoercedValueIsDataFeed = 1 << 5, + IsDerivedParameterTypeName = 1 << 6, + SourceColumnNullMapping = 1 << 7, + HasScale = 1 << 8, // V1.0 compat, ignore _hasScale + } + private MetaType _metaType; private SqlCollation _collation; - private string _xmlSchemaCollectionDatabase; - private string _xmlSchemaCollectionOwningSchema; - private string _xmlSchemaCollectionName; + private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; private string _udtTypeName; private string _typeName; @@ -88,17 +99,14 @@ public sealed partial class SqlParameter : DbParameter, IDbDataParameter, IClone private string _parameterName; private byte _precision; private byte _scale; - private bool _hasScale; // V1.0 compat, ignore _hasScale private MetaType _internalMetaType; private SqlBuffer _sqlBufferReturnValue; private INullable _valueAsINullable; - private bool _isSqlParameterSqlType; - private bool _isNull = true; - private bool _coercedValueIsSqlType; - private bool _coercedValueIsDataFeed; private int _actualSize = -1; + private SqlParameterFlags _flags = SqlParameterFlags.IsNull; + private DataRowVersion _sourceVersion; public SqlParameter() : base() @@ -249,12 +257,19 @@ public string XmlSchemaCollectionDatabase { get { - string xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase; - return (xmlSchemaCollectionDatabase ?? ADP.StrEmpty); + return (_xmlSchemaCollection?.Database ?? ADP.StrEmpty); } set { - _xmlSchemaCollectionDatabase = value; + bool collectionIsNull = _xmlSchemaCollection != null; + if (collectionIsNull) + { + _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + } + if (value != null || collectionIsNull) + { + _xmlSchemaCollection.Database = value; + } } } @@ -262,12 +277,19 @@ public string XmlSchemaCollectionOwningSchema { get { - string xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema; - return (xmlSchemaCollectionOwningSchema ?? ADP.StrEmpty); + return (_xmlSchemaCollection?.OwningSchema ?? ADP.StrEmpty); } set { - _xmlSchemaCollectionOwningSchema = value; + bool collectionIsNull = _xmlSchemaCollection != null; + if (collectionIsNull) + { + _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + } + if (value != null || collectionIsNull) + { + _xmlSchemaCollection.OwningSchema = value; + } } } @@ -275,12 +297,19 @@ public string XmlSchemaCollectionName { get { - string xmlSchemaCollectionName = _xmlSchemaCollectionName; - return (xmlSchemaCollectionName ?? ADP.StrEmpty); + return (_xmlSchemaCollection?.Name ?? ADP.StrEmpty); } set { - _xmlSchemaCollectionName = value; + bool collectionIsNull = _xmlSchemaCollection != null; + if (collectionIsNull) + { + _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + } + if (value != null || collectionIsNull) + { + _xmlSchemaCollection.Name = value; + } } } @@ -320,6 +349,12 @@ internal MetaType InternalMetaType set { _internalMetaType = value; } } + internal bool IsDerivedParameterTypeName + { + get => _flags.HasFlag(SqlParameterFlags.IsDerivedParameterTypeName); + set => Set(SqlParameterFlags.IsDerivedParameterTypeName, value); + } + public int LocaleId { // Lowest 20 bits represent LocaleId @@ -512,14 +547,8 @@ internal MSS.SmiParameterMetaData MetaDataForSmi(out ParameterPeekAheadValue pee internal bool ParameterIsSqlType { - get - { - return _isSqlParameterSqlType; - } - set - { - _isSqlParameterSqlType = value; - } + get => _flags.HasFlag(SqlParameterFlags.IsSqlParameterSqlType); + set => Set(SqlParameterFlags.IsSqlParameterSqlType, value); } public override string ParameterName @@ -633,11 +662,11 @@ internal byte ScaleInternal } set { - if (_scale != value || !_hasScale) + if (_scale != value || !_flags.HasFlag(SqlParameterFlags.HasScale)) { PropertyChanging(); _scale = value; - _hasScale = true; + Set(SqlParameterFlags.HasScale, true); _actualSize = -1; // Invalidate actual size such that it is re-calculated } } @@ -746,14 +775,11 @@ public string UdtTypeName public string TypeName { - get - { - string typeName = _typeName; - return (typeName ?? ADP.StrEmpty); - } + get => (_typeName ?? ADP.StrEmpty); set { _typeName = value; + Set(SqlParameterFlags.IsDerivedParameterTypeName, false); } } @@ -787,8 +813,8 @@ public override object Value _sqlBufferReturnValue = null; _coercedValue = null; _valueAsINullable = _value as INullable; - _isSqlParameterSqlType = (_valueAsINullable != null); - _isNull = ((_value == null) || (_value == DBNull.Value) || ((_isSqlParameterSqlType) && (_valueAsINullable.IsNull))); + Set(SqlParameterFlags.IsSqlParameterSqlType, (_valueAsINullable != null)); + Set(SqlParameterFlags.IsNull, ((_value == null) || (_value == DBNull.Value) || (((_valueAsINullable != null)) && (_valueAsINullable.IsNull)))); _udtLoadError = null; _actualSize = -1; } @@ -809,9 +835,9 @@ internal bool IsNull // NOTE: Udts can change their value any time if (_internalMetaType.SqlDbType == Data.SqlDbType.Udt) { - _isNull = ((_value == null) || (_value == DBNull.Value) || ((_isSqlParameterSqlType) && (_valueAsINullable.IsNull))); + Set(SqlParameterFlags.IsNull, ((_value == null) || (_value == DBNull.Value) || (( _flags.HasFlag(SqlParameterFlags.IsSqlParameterSqlType)) && (_valueAsINullable.IsNull)))); } - return _isNull; + return _flags.HasFlag(SqlParameterFlags.IsNull); } } @@ -853,7 +879,9 @@ internal int GetActualSize() // @hack: we only send a MAX of Size bytes over. If the actualSize is < Size, then // @hack: we send over actualSize int coercedSize = 0; - + bool coercedValueIsDataFeed = _flags.HasFlag(SqlParameterFlags.CoercedValueIsDataFeed); + bool coercedValueIsSqlType = _flags.HasFlag(SqlParameterFlags.CoercedValueIsSqlType); + bool isNull = _flags.HasFlag(SqlParameterFlags.IsNull); // get the actual length of the data, in bytes switch (actualType) { @@ -862,7 +890,7 @@ internal int GetActualSize() case SqlDbType.NText: case SqlDbType.Xml: { - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0; + coercedSize = ((!isNull) && (!coercedValueIsDataFeed)) ? (StringSize(val, coercedValueIsSqlType)) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); if (_actualSize == -1) @@ -875,7 +903,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 = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (StringSize(val, _coercedValueIsSqlType)) : 0; + coercedSize = ((!isNull) && (!coercedValueIsDataFeed)) ? (StringSize(val, coercedValueIsSqlType)) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); if (_actualSize == -1) @@ -886,7 +914,7 @@ internal int GetActualSize() case SqlDbType.VarBinary: case SqlDbType.Image: case SqlDbType.Timestamp: - coercedSize = ((!_isNull) && (!_coercedValueIsDataFeed)) ? (BinarySize(val, _coercedValueIsSqlType)) : 0; + coercedSize = ((!isNull) && (!coercedValueIsDataFeed)) ? (BinarySize(val, coercedValueIsSqlType)) : 0; _actualSize = (ShouldSerializeSize() ? Size : 0); _actualSize = ((ShouldSerializeSize() && (_actualSize <= coercedSize)) ? _actualSize : coercedSize); if (_actualSize == -1) @@ -1049,12 +1077,12 @@ internal void FixStreamDataForNonPLP() { object value = GetCoercedValue(); AssertCachedPropertiesAreValid(); - if (!_coercedValueIsDataFeed) + if (!_flags.HasFlag(SqlParameterFlags.CoercedValueIsDataFeed)) { return; } - _coercedValueIsDataFeed = false; + Set(SqlParameterFlags.CoercedValueIsDataFeed, false); if (value is TextDataFeed) { @@ -1121,30 +1149,34 @@ private void CloneHelper(SqlParameter destination) destination._offset = _offset; destination._sourceColumn = _sourceColumn; destination._sourceVersion = _sourceVersion; - destination._sourceColumnNullMapping = _sourceColumnNullMapping; - destination._isNullable = _isNullable; - destination._metaType = _metaType; destination._collation = _collation; - destination._xmlSchemaCollectionDatabase = _xmlSchemaCollectionDatabase; - destination._xmlSchemaCollectionOwningSchema = _xmlSchemaCollectionOwningSchema; - destination._xmlSchemaCollectionName = _xmlSchemaCollectionName; destination._udtTypeName = _udtTypeName; destination._typeName = _typeName; destination._udtLoadError = _udtLoadError; - destination._parameterName = _parameterName; destination._precision = _precision; destination._scale = _scale; destination._sqlBufferReturnValue = _sqlBufferReturnValue; - destination._isSqlParameterSqlType = _isSqlParameterSqlType; destination._internalMetaType = _internalMetaType; destination.CoercedValue = CoercedValue; // copy cached value reference because of XmlReader problem destination._valueAsINullable = _valueAsINullable; - destination._isNull = _isNull; - destination._coercedValueIsDataFeed = _coercedValueIsDataFeed; - destination._coercedValueIsSqlType = _coercedValueIsSqlType; + + SqlParameterFlags setFlags = + SqlParameterFlags.IsSqlParameterSqlType | + SqlParameterFlags.IsNull | + SqlParameterFlags.IsNullable | + SqlParameterFlags.CoercedValueIsDataFeed | + SqlParameterFlags.CoercedValueIsSqlType | + SqlParameterFlags.SourceColumnNullMapping; + destination._flags = (destination._flags & ~setFlags) | (_flags & setFlags); destination._actualSize = _actualSize; + + if (_xmlSchemaCollection != null) + { + destination._xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + destination._xmlSchemaCollection.CopyFrom(_xmlSchemaCollection); + } } public override DataRowVersion SourceVersion @@ -1508,19 +1540,23 @@ internal object GetCoercedValue() if ((null == _coercedValue) || (_internalMetaType.SqlDbType == Data.SqlDbType.Udt)) { // will also be set during parameter Validation bool isDataFeed = Value is DataFeed; - if ((IsNull) || (isDataFeed)) + bool isSqlParameterSqlType = _flags.HasFlag(SqlParameterFlags.IsSqlParameterSqlType); + bool isNull = IsNull; + if ((isNull) || (isDataFeed)) { // No coercion is done for DataFeeds and Nulls _coercedValue = Value; - _coercedValueIsSqlType = (_coercedValue == null) ? false : _isSqlParameterSqlType; // set to null for output parameters that keeps _isSqlParameterSqlType - _coercedValueIsDataFeed = isDataFeed; - _actualSize = IsNull ? 0 : -1; + Set(SqlParameterFlags.CoercedValueIsSqlType, (_coercedValue is null) ? false : isSqlParameterSqlType); // set to null for output parameters that keeps _isSqlParameterSqlType + Set(SqlParameterFlags.CoercedValueIsDataFeed, isDataFeed); + _actualSize = isNull ? 0 : -1; } else { bool typeChanged; - _coercedValue = CoerceValue(Value, _internalMetaType, out _coercedValueIsDataFeed, out typeChanged); - _coercedValueIsSqlType = ((_isSqlParameterSqlType) && (!typeChanged)); // Type changed always results in a CLR type + bool coercedValueIsDataFeed; + _coercedValue = CoerceValue(Value, _internalMetaType, out coercedValueIsDataFeed, out typeChanged); + Set(SqlParameterFlags.CoercedValueIsDataFeed, coercedValueIsDataFeed); + Set(SqlParameterFlags.CoercedValueIsSqlType, ((isSqlParameterSqlType) && (!typeChanged))); // Type changed always results in a CLR type _actualSize = -1; } } @@ -1537,7 +1573,7 @@ internal bool CoercedValueIsSqlType GetCoercedValue(); } AssertCachedPropertiesAreValid(); - return _coercedValueIsSqlType; + return _flags.HasFlag(SqlParameterFlags.CoercedValueIsSqlType); } } @@ -1550,14 +1586,14 @@ internal bool CoercedValueIsDataFeed GetCoercedValue(); } AssertCachedPropertiesAreValid(); - return _coercedValueIsDataFeed; + return _flags.HasFlag(SqlParameterFlags.CoercedValueIsDataFeed); } } [Conditional("DEBUG")] internal void AssertCachedPropertiesAreValid() { - AssertPropertiesAreValid(_coercedValue, _coercedValueIsSqlType, _coercedValueIsDataFeed, IsNull); + AssertPropertiesAreValid(_coercedValue, _flags.HasFlag(SqlParameterFlags.CoercedValueIsSqlType), _flags.HasFlag(SqlParameterFlags.CoercedValueIsDataFeed), IsNull); } [Conditional("DEBUG")] @@ -1605,7 +1641,7 @@ private MetaType GetMetaTypeOnly() } else if (null != _sqlBufferReturnValue) { // value came back from the server - Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(_isSqlParameterSqlType); + Type valueType = _sqlBufferReturnValue.GetTypeFromStorageType(_flags.HasFlag(SqlParameterFlags.IsSqlParameterSqlType)); if (null != valueType) { return MetaType.GetMetaTypeFromType(valueType); @@ -1646,9 +1682,9 @@ internal void SetSqlBuffer(SqlBuffer buff) _sqlBufferReturnValue = buff; _value = null; _coercedValue = null; - _isNull = _sqlBufferReturnValue.IsNull; - _coercedValueIsDataFeed = false; - _coercedValueIsSqlType = false; + Set(SqlParameterFlags.IsNull, _sqlBufferReturnValue.IsNull); + Set(SqlParameterFlags.CoercedValueIsDataFeed, false); + Set(SqlParameterFlags.CoercedValueIsSqlType, false); _udtLoadError = null; _actualSize = -1; } @@ -1658,6 +1694,18 @@ internal void SetUdtLoadError(Exception e) _udtLoadError = e; } + private void Set(SqlParameterFlags flag, bool value) + { + if (value) + { + _flags |= flag; + } + else + { + _flags &= ~flag; + } + } + internal void Validate(int index, bool isCommandProc) { MetaType metaType = GetMetaTypeOnly(); @@ -1745,7 +1793,7 @@ internal MetaType ValidateTypeLengths() maxSizeInBytes = (sizeInCharacters > actualSizeInBytes) ? sizeInCharacters : actualSizeInBytes; } - if ((maxSizeInBytes > TdsEnums.TYPE_SIZE_LIMIT) || (_coercedValueIsDataFeed) || + if ((maxSizeInBytes > TdsEnums.TYPE_SIZE_LIMIT) || (_flags.HasFlag(SqlParameterFlags.CoercedValueIsDataFeed)) || (sizeInCharacters == -1) || (actualSizeInBytes == -1)) { // is size > size able to be described by 2 bytes // Convert the parameter to its max type diff --git a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameterHelper.cs b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameterHelper.cs index a6992793adc9..d09912376ae9 100644 --- a/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameterHelper.cs +++ b/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlParameterHelper.cs @@ -13,31 +13,17 @@ namespace System.Data.SqlClient public sealed partial class SqlParameter : DbParameter { private object _value; - private object _parent; - private ParameterDirection _direction; private int _size; - private int _offset; private string _sourceColumn; - private bool _sourceColumnNullMapping; - - private bool _isNullable; - private object _coercedValue; - private object CoercedValue { - get - { - return _coercedValue; - } - set - { - _coercedValue = value; - } + get => _coercedValue; + set => _coercedValue = value; } override public ParameterDirection Direction @@ -69,17 +55,10 @@ override public ParameterDirection Direction override public bool IsNullable { - get - { - return _isNullable; - } - set - { - _isNullable = value; - } + get => _flags.HasFlag(SqlParameterFlags.IsNullable); + set => Set(SqlParameterFlags.IsNullable, value); } - public int Offset { get @@ -121,7 +100,6 @@ override public int Size } } - private bool ShouldSerializeSize() { return (0 != _size); @@ -129,30 +107,16 @@ private bool ShouldSerializeSize() override public string SourceColumn { - get - { - string sourceColumn = _sourceColumn; - return ((null != sourceColumn) ? sourceColumn : ADP.StrEmpty); - } - set - { - _sourceColumn = value; - } + get => (_sourceColumn ?? ADP.StrEmpty); + set => _sourceColumn = value; } public override bool SourceColumnNullMapping { - get - { - return _sourceColumnNullMapping; - } - set - { - _sourceColumnNullMapping = value; - } + get => _flags.HasFlag(SqlParameterFlags.SourceColumnNullMapping); + set => Set(SqlParameterFlags.SourceColumnNullMapping, value); } - internal object CompareExchangeParent(object value, object comparand) { object parent = _parent; @@ -229,10 +193,10 @@ internal void CopyTo(SqlParameter destination) destination._offset = _offset; destination._sourceColumn = _sourceColumn; destination._sourceVersion = _sourceVersion; - destination._sourceColumnNullMapping = _sourceColumnNullMapping; - destination._isNullable = _isNullable; destination._parameterName = _parameterName; - destination._isNull = _isNull; + + SqlParameterFlags setFlags = SqlParameterFlags.SourceColumnNullMapping | SqlParameterFlags.IsNullable | SqlParameterFlags.IsNull; + destination._flags = (destination._flags & ~setFlags) | (_flags & setFlags); } } diff --git a/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs b/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs index 1e6f45ab0188..cd9392c91680 100644 --- a/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs +++ b/src/System.Data.SqlClient/tests/ManualTests/SQL/UdtTest/UdtTest2.cs @@ -342,6 +342,128 @@ public void UDT_DataSetFill() } } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] + public void UDTParams_DeriveParameters_CheckAutoFixSuccess() + { + // the type and sproc must be commited to the database or this test will deadlock with a schema lock violation + // if you are missing these database entities then you should look for an updated version of the database creation script + + string sprocName = "sp_insert_customers"; + string typeName = "CustomerAddress"; + string customerAddressTypeIncorrectName = $"{DataTestUtility.UdtTestDbName}.dbo.{typeName.Trim('[',']')}"; + string customerAddressTypeCorrectedName = $"[dbo].[{typeName.Trim('[',']')}]"; + string customerParameterName = "@customers"; + + Address addr = Address.Parse("123 baker st || Redmond"); + DataTable table = new DataTable(); + table.Columns.Add(); + table.Columns.Add(); + table.Rows.Add("john", addr); + + using (SqlConnection connection = new SqlConnection(_connStr)) + { + connection.Open(); + using (SqlTransaction transaction = connection.BeginTransaction()) + using (SqlCommand cmd = new SqlCommand(sprocName, connection, transaction)) + { + try + { + cmd.CommandType = CommandType.StoredProcedure; + + SqlCommandBuilder.DeriveParameters(cmd); + + Assert.NotNull(cmd.Parameters); + Assert.Equal(2, cmd.Parameters.Count); // [return_value, table] + + SqlParameter p = cmd.Parameters[1]; + + Assert.Equal(customerParameterName, p.ParameterName); + Assert.Equal(SqlDbType.Structured, p.SqlDbType); + Assert.Equal(customerAddressTypeIncorrectName, p.TypeName); // the 3 part name is incorrect but needs to be maintained for compatibility + p.Value = table; + + cmd.ExecuteNonQuery(); + + Assert.Equal(customerAddressTypeCorrectedName, p.TypeName); // check that the auto fix has been applied correctly + } + finally + { + try + { + transaction.Rollback(); + } + catch + { + // ignore rollback failure exceptions to preserve original thrown error in test result + } + } + } + + } + } + + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] + public void UDTParams_DeriveParameters_CheckAutoFixOverride() + { + // the type and sproc must be commited to the database or this test will deadlock with a schema lock violation + // if you are missing these database entities then you should look for an updated version of the database creation script + + string sprocName = "sp_insert_customers"; + string typeName = "CustomerAddress"; + string customerAddressTypeIncorrectName = $"{DataTestUtility.UdtTestDbName}.dbo.{typeName.Trim('[', ']')}"; + string customerAddressTypeCorrectedName = $"[dbo].[{typeName.Trim('[', ']')}]"; + string customerParameterName = "@customers"; + + Address addr = Address.Parse("123 baker st || Redmond"); + DataTable table = new DataTable(); + table.Columns.Add(); + table.Columns.Add(); + table.Rows.Add("john", addr); + + using (SqlConnection connection = new SqlConnection(_connStr)) + { + connection.Open(); + using (SqlTransaction transaction = connection.BeginTransaction()) + using (SqlCommand cmd = new SqlCommand(sprocName, connection, transaction)) + { + try + { + cmd.CommandType = CommandType.StoredProcedure; + + SqlCommandBuilder.DeriveParameters(cmd); + + Assert.NotNull(cmd.Parameters); + Assert.Equal(2, cmd.Parameters.Count); // [return_value, table] + + SqlParameter p = cmd.Parameters[1]; + + Assert.Equal(customerParameterName, p.ParameterName); + Assert.Equal(SqlDbType.Structured, p.SqlDbType); + Assert.Equal(customerAddressTypeIncorrectName, p.TypeName); // the 3 part name is incorrect but needs to be maintained for compatibility + p.Value = table; + + p.TypeName = customerAddressTypeIncorrectName; // force using the incorrect name by manually setting it + + Assert.Throws( + () => cmd.ExecuteNonQuery() + ); + } + finally + { + try + { + transaction.Rollback(); + } + catch + { + // ignore rollback failure exceptions to preserve original thrown error in test result + } + } + } + + } + } + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsUdtTestDatabasePresent), nameof(DataTestUtility.AreConnStringsSetup))] public void Reader_PointEarly() { diff --git a/src/System.Data.SqlClient/tests/ManualTests/createUdtTestDb_corefx.sql b/src/System.Data.SqlClient/tests/ManualTests/createUdtTestDb_corefx.sql index 3dc4c0938d59..d81b49aa3522 100644 --- a/src/System.Data.SqlClient/tests/ManualTests/createUdtTestDb_corefx.sql +++ b/src/System.Data.SqlClient/tests/ManualTests/createUdtTestDb_corefx.sql @@ -64,6 +64,9 @@ create type WeakPoint external name weakpoint.[WeakPoint] create type WeakLine external name weakline.[WeakLine] go +create type CustomerAddress as table( name nvarchar(30) NOT NULL, addr Address NULL ) +go + create table cities (name sysname,location Point) create table lines(ids int null, pos Line null) create table customers(name nvarchar(30), address Address) @@ -125,6 +128,11 @@ as insert into customers values (@name, @addr) go +create proc sp_insert_customers ( @customers CustomerAddress READONLY ) +as +insert into customers(name,address) select name,addr from @customers +go + create assembly Utf8String from 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0103000F35305A0000000000000000E00022200B0130000016000000060000000000006A350000002000000040000000000010002000000002000004000000000000000600000000000000008000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000173500004F00000000400000B002000000000000000000000000000000000000006000000C000000883400001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000070150000002000000016000000020000000000000000000000000000200000602E72737263000000B0020000004000000004000000180000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000001C000000000000000000000000000040000042000000000000000000000000000000004B350000000000004800000002000500C0250000C80E00000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000133001002500000001000011000F00280600000A0A062C0828160000060B2B0F0F00280700000A73130000060B2B00072A000000133003006D00000002000011000228150000060A062C087E0800000A0B2B58027B0200000414FE030C082C0E027B02000004280900000A0B2B3D027B0100000414FE030D092C250002280A00000A027B010000046F0B00000A7D02000004027B02000004730C00000A0B2B0B7201000070730D00000A7A072A000000133002003500000003000011000F01280E00000A0A062C120002147D0200000402147D01000004002B1600020F01280F00000A7D0200000402147D01000004002A000000133003005F00000004000011000228150000060A062C04140B2B4E027B0100000414FE030C082C09027B010000040B2B38027B0200000414FE030D092C200002280A00000A027B020000046F1000000A7D01000004027B010000040B2B0B724F000070730D00000A7A072A00133001002300000005000011000228150000060A062C087E1100000A0B2B0E026F1200000A731300000A0B2B00072A00133003004D00000006000011000228150000060B072C087E1100000A0C2B38160A040D092C040617600A05130411042C040618600A0E04130511052C05061F10600A026F1200000A036F1400000A06731500000A0C2B00082A000000133006003F000000070000110003281600000A0A0614FE010B072C1F281700000A72B5000070178D07000001251603A2281800000A731900000A7A020604050E0428060000060C2B00082A001330050014000000080000110002281A00000A03040528060000060A2B00062A133002001000000003000011000203281200000616FE010A2B00062A133002000D000000030000110002036F1B00000A0A2B00062A000000133002001000000003000011000203280A00000616FE010A2B00062A1330020010000000030000110002036F1200000616FE040A2B00062A1330020010000000030000110002036F1200000616FE020A2B00062A133006006200000009000011000314280A0000060A062C04170B2B500228150000060C082C12036F150000060D092C04160B2B38150B2B34036F15000006130411042C04170B2B240204050E040E052806000006130512050304050E040E056F06000006281C00000A0B2B00072A000013300600410000000A0000110004281600000A0A0614FE010B072C1F281700000A72B5000070178D07000001251604A2281800000A731900000A7A020306050E040E05280E0000060C2B00082A00000013300600160000000B000011000203281A00000A04050E04280E0000060A2B00062A0000133001001F0000000C000011000228150000060A062C04160B2B0E026F1200000A6F1D00000A0B2B00072A0013300200710000000D000011000314FE010B072C04170C2B620375020000020A0614280A0000060D092C0B72ED000070731900000A7A022815000006130411042C1500066F15000006130511052C04160C2B28150C2B24066F15000006130611062C04170C2B14026F1200000A066F1200000A6F1E00000A0C2B00082A4202281F00000A000002037D010000042A4202281F00000A000002037D020000042A00133002001A0000000300001100027B010000042D0B027B0200000414FE012B01160A2B00062A0000133001000E0000000E000011001473130000060A060B2B00072A2602281F00000A00002A13300200430000000F000011000228150000062D03162B0117D20A03066F2000000A000617FE010C082C022B210228020000060D1203280F00000A0B03078E696F2100000A0003076F2200000A002A0013300300310000001000001100036F2300000A0A06175F16FE020C082C0A0002147D020000042B14036F2400000A0B0203076F2500000A7D020000042A00000042534A4201000100000000000C00000076322E302E35303732370000000005006C0000000C050000237E0000780500008804000023537472696E677300000000000A00004801000023555300480B0000100000002347554944000000580B00007003000023426C6F6200000000000000020000015717A2010900000000FA01330016000001000000180000000200000002000000190000002900000003000000250000000700000010000000010000000300000004000000010000000200000000007E0201000000000006006F01960306008F01960306003F0183030F00B60300000A00300446030A005301460306003704A1020A007A00C50306008400A1020A00C60146030A000102C5030A006304C5030A002C0146030A00500046030A004A00460306000A03C3020A001E04C5030600390326000600160326000600D7014A040600D802A10206002102A10206002303A1020600EE02A102000000001400000000000100010001201000E00161031D00010001000100F60108010100F6030B01502000000000960020010F0101008420000000008608DA03160102000021000000008608E8031B010200442100000000C600150220000300B021000000008600FF0121010300E0210000000081005F02260103003C22000000008600CE00300107008822000000008600580439010B00A82200000000C6001704A5000E00C4220000000096086D0441010F00E022000000009608790441011100FC22000000009608B702410113001823000000009608A802410115003423000000008100430249011700A423000000008600BA0054011C00F4230000000086009C005E012100182400000000C6006E0078002500442400000000E601000367012500C1240000000086187D0348002600D2240000000086187D0342002700E42400000000E60996021C0028000C250000000096088D026C01280026250000000086187D0306002800302500000000E601260171012800802500000000E601450077012900000001000B0200000100C001000001000D01000002001501000003005F00000004003302000001009000000002001501000003005F00000004003302000001001501000002005F00000003003302000001003F0200000100EB0100000200330300000100EB0100000200330300000100EB0100000200330300000100EB01000002003303000001003303000002000D01000003001501000004005F00000005003302000001003303000002009000000003001501000004005F00000005003302000001003303000002001501000003005F00000004003302000001003F0200000100C00100000100110400000100560400000100810302002100020025000200290009007D03010011007D03060019007D030A0031007D03100069007D030600590096021C005900B601200061009C022C0061003E043000A1000B003700A10008043C0061007D034200A9007D034800610096021C006100B6015100A1001E025D0059009C02690039001502200059007D03480081001D00780059007D037C008100A4008C008100E5009200B10030049700C1007D0348008100FA00920039001704A50059000003B40039006E007800B1000003D50039007D03060091002601EA009100260101009100260142009900AD01F5009900010078009900FE03F9002E000B008B012E00130094012E001B00B30143002300410380002B00BC01E0002B00040300012B002903160024004D00560063006D008400A000AA00BA00C100C500CA00DA00E100EF00020001000000EC037D0100009A02820100009C02860102000200030001000300030002001500050002001600070004800000030002000100010000000000000028020000020000000000000000000000FF003C0000000000020000000000000000000000FF0030000000000000000052656164496E743332006765745F55544638003C4D6F64756C653E006765745F4C4349440053797374656D2E494F0053797374656D2E44617461006D73636F726C696200526561640053797374656D446174614163636573734B696E640069676E6F72654E6F6E53706163650047657448617368436F646500494E756C6C61626C650049436F6D70617261626C650063756C747572654E616D6500436F6D7061726500437265617465537065636966696343756C7475726500436F6D706172655573696E6743756C7475726500476574536F72744B65795573696E6743756C74757265006765745F496E76617269616E7443756C74757265006765745F43757272656E7443756C747572650063756C747572650069676E6F7265436173650050617273650057726974650053716C4D6574686F644174747269627574650044656275676761626C654174747269627574650053716C55736572446566696E65645479706541747472696275746500436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465005265616442797465006765745F56616C75650076616C7565004942696E61727953657269616C697A6500456E636F64696E670055746638537472696E670075746638537472696E67006D5F537472696E6700546F53716C537472696E670073716C537472696E6700546F537472696E6700476574537472696E670075746638737472696E670069676E6F72655769647468006F626A00436F6D706172655573696E6743756C74757265496E7465726E616C00476574536F72744B65795573696E6743756C74757265496E7465726E616C0075746638737472696E672E646C6C006765745F4E756C6C006765745F49734E756C6C0053797374656D006F705F477265617465725468616E006F705F4C6573735468616E0053797374656D2E476C6F62616C697A6174696F6E004E6F74537570706F72746564457863657074696F6E00417267756D656E74457863657074696F6E00436F6D70617265546F0043756C74757265496E666F0042696E6172795265616465720049466F726D617450726F7669646572006F746865720042696E617279577269746572004D6963726F736F66742E53716C5365727665722E536572766572004D6963726F736F66742E53616D706C65732E53716C536572766572002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F6465730053797374656D2E446174612E53716C5479706573006765745F557466384279746573007365745F557466384279746573006D5F42797465730052656164427974657300476574427974657300627974657300457175616C730053716C436F6D706172654F7074696F6E7300466F726D6174004F626A656374006F705F496D706C696369740053797374656D2E54657874007700476574536F72744B65790053716C42696E617279006F705F457175616C697479006F705F496E657175616C6974790000004D630061006E006E006F0074002000720065007400750072006E00200062007900740065007300200066006F007200200065006D00700074007900200069006E007300740061006E0063006500006564006F006E00740020006B006E006F007700200068006F007700200074006F002000720065007400750072006E00200073007400720069006E0067002000660072006F006D00200065006D00700074007900200069006E007300740061006E00630065000037430075006C00740075007200650020007B0030007D0020006E006F00740020007200650063006F0067006E0069007A00650064002E000057740068006500200061007200670075006D0065006E007400200074006F00200063006F006D00700061007200650020006900730020006E006F007400200061002000550074006600380053007400720069006E006700000000000EC59433DD025D47A6AE3D09E458D39500042001010803200001052001011111052001011115050702021208032000020320000E07070402113102020306113106000111311D0504000012510520011D050E052001011D05042001010E030701020420001D05060704020E02020520010E1D0505070202112D0306112D0A0706114502112D02020203200008072003010E081145070703124102112D05000112410E04000012410800030E125D0E1D1C040701112D042001021C0907060208020202112D05200108112D060703124102080307010804070202080A07071208020802020202042001080E06070212081208080704051D050211310420010105050703050802032000050520011D050808B77A5C561934E08902060E03061D050600011208112D0420001131052001011131042000112D092004112D1241020202082004112D0E020202072003112D02020207000202120812080A200508120812410202020920050812080E020202082004081208020202042001081C040000120805200101124905200101124D04280011310328000204080012080801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730108010007010000000081460100040054020F497344657465726D696E697374696301540209497350726563697365015455794D6963726F736F66742E53716C5365727665722E5365727665722E446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D322E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038390A446174614163636573730000000054557F4D6963726F736F66742E53716C5365727665722E5365727665722E53797374656D446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D322E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038391053797374656D4461746141636365737300000000240100020054020F497344657465726D696E69737469630154020949735072656369736501170100010054020F497344657465726D696E6973746963002B010002000000020054020D4973427974654F7264657265640154080B4D61784279746553697A65401F0000000000000000000F35305A000000000200000073000000A4340000A416000052534453F3CBC33915671D4B9EE0AEAF82371B5101000000443A5C4E65744658446576335C496E7465726D6564696174655C73756974657372635C75746638737472696E672E637370726F6A5F5F313733303533303137335C6F626A635C616D6436345C75746638737472696E672E706462003F3500000000000000000000593500000020000000000000000000000000000000000000000000004B350000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C000000000000FF250020001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000540200000000000000000000540234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100020003000100010002000300010001003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004B4010000010053007400720069006E006700460069006C00650049006E0066006F0000009001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000033002E0032002E0031002E00310000003E000F00010049006E007400650072006E0061006C004E0061006D0065000000750074006600380073007400720069006E0067002E0064006C006C00000000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000046000F0001004F0072006900670069006E0061006C00460069006C0065006E0061006D0065000000750074006600380073007400720069006E0067002E0064006C006C0000000000340008000100500072006F006400750063007400560065007200730069006F006E00000033002E0032002E0031002E003100000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000033002E0032002E0031002E003100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000C0000006C3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 with permission_set = safe