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 f8fc03f915..160feb0b6c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -860,6 +860,9 @@ Microsoft\Data\SqlClient\SqlMetaDataFactory.cs + + Microsoft\Data\SqlClient\SqlMetaDataFactory.DataTypes.cs + Microsoft\Data\SqlClient\SqlNotificationEventArgs.cs 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 e0974d0d44..61f67b9b5b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -843,6 +843,9 @@ Microsoft\Data\SqlClient\SqlMetaDataFactory.cs + + Microsoft\Data\SqlClient\SqlMetaDataFactory.DataTypes.cs + Microsoft\Data\SqlClient\SqlNotificationEventArgs.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs new file mode 100644 index 0000000000..eba61969c3 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs @@ -0,0 +1,438 @@ +// 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.Data; +using System.Data.Common; +using System.Diagnostics; +using Microsoft.Data.Common; + +#nullable enable + +namespace Microsoft.Data.SqlClient; + +internal sealed partial class SqlMetaDataFactory +{ + private static void LoadDataTypesDataTables(DataSet metaDataCollectionsDataSet) + { + DataTable dataTypesDataTable = CreateDataTypesDataTable(); + + dataTypesDataTable.BeginLoadData(); + + // SQL Server data types are grouped into the rough categories below: + // 1. Fixed-scale and fixed-precision numeric types: tinyint, smallint, int, bigint, real, money, smallmoney, bit. + // 2. Variable-precision numeric types: float and decimal / numeric. + // 3. Fixed-precision date/time types: datetime, smalldatetime, date. + // 4. Variable-precision date/time types: time, datetime2, datetimeoffset. + // 5. Long types: xml, json, text, ntext, image. + // 6. Fixed-length string and binary types: char, nchar, binary. + // 7. Variable-length string and binary types: varchar, nvarchar, varbinary. + // 8. Miscellaneous fixed-length types: uniqueidentifier, sql_variant, timestamp + + AddFixedNumericType(SqlDbType.TinyInt, isBestMatch: true); + AddFixedNumericType(SqlDbType.SmallInt, isBestMatch: true); + AddFixedNumericType(SqlDbType.Int, isBestMatch: true); + AddFixedNumericType(SqlDbType.BigInt, isBestMatch: true); + AddFixedNumericType(SqlDbType.Real, isBestMatch: true); + // Money and smallmoney are not the best ways to represent System.Decimal values. SQL Server + // provides a variable-precision "numeric" type for that purpose. + AddFixedNumericType(SqlDbType.Money, isBestMatch: false); + AddFixedNumericType(SqlDbType.SmallMoney, isBestMatch: false); + AddFixedNumericType(SqlDbType.Bit, isBestMatch: false); + + AddVariablePrecisionNumericType(SqlDbType.Float, columnSize: 53); + AddVariablePrecisionNumericType(SqlDbType.Decimal, columnSize: 38); + AddVariablePrecisionNumericType(SqlDbType.Decimal, columnSize: 38, + aliasType: "numeric"); + + AddFixedPrecisionDateTimeType(SqlDbType.DateTime, isBestMatch: true); + AddFixedPrecisionDateTimeType(SqlDbType.SmallDateTime, isBestMatch: true); + AddFixedPrecisionDateTimeType(SqlDbType.Date, isBestMatch: false, + minimumVersion: "10.00.000.0"); + + AddVariablePrecisionDateTimeType(SqlDbType.Time, columnSize: 5, isBestMatch: false, + minimumVersion: "10.00.000.0"); + AddVariablePrecisionDateTimeType(SqlDbType.DateTime2, columnSize: 8, + minimumVersion: "10.00.000.0"); + AddVariablePrecisionDateTimeType(SqlDbType.DateTimeOffset, columnSize: 10, + minimumVersion: "10.00.000.0"); + + AddLongStringOrBinaryType(SqlDbType.Xml); + AddLongStringOrBinaryType(SqlDbTypeExtensions.Json, literalPrefix: "'", literalSuffix: "'", + minimumVersion: "17.00.000.0"); + AddLongStringOrBinaryType(SqlDbType.Text, literalPrefix: "'", literalSuffix: "'"); + AddLongStringOrBinaryType(SqlDbType.NText, literalPrefix: "N'", literalSuffix: "'"); + AddLongStringOrBinaryType(SqlDbType.Image, literalPrefix: "0x"); + + AddFixedLengthStringOrBinaryType(SqlDbType.Char, literalPrefix: "'", literalSuffix: "'"); + AddFixedLengthStringOrBinaryType(SqlDbType.NChar, literalPrefix: "N'", literalSuffix: "'"); + AddFixedLengthStringOrBinaryType(SqlDbType.Binary, literalPrefix: "0x"); + + AddVariableLengthStringOrBinaryType(SqlDbType.VarChar, literalPrefix: "'", literalSuffix: "'"); + AddVariableLengthStringOrBinaryType(SqlDbType.NVarChar, literalPrefix: "N'", literalSuffix: "'"); + AddVariableLengthStringOrBinaryType(SqlDbType.VarBinary, literalPrefix: "0x"); + + AddUniqueIdentifierType(); + AddSqlVariantType(); + AddRowVersionType(); + + dataTypesDataTable.EndLoadData(); + dataTypesDataTable.AcceptChanges(); + + metaDataCollectionsDataSet.Tables.Add(dataTypesDataTable); + + void AddFixedNumericType(SqlDbType integerDbType, bool isBestMatch) + { + MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(integerDbType, isMultiValued: false); + DataRow typeRow = dataTypesDataTable.NewRow(); + + typeRow[DbMetaDataColumnNames.TypeName] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.ProviderDbType] = (int)metaType.SqlDbType; + typeRow[DbMetaDataColumnNames.CreateFormat] = metaType.TypeName; + + // A fixed-scale integer always has precision equal to the column size and a scale of 0xFF. + // If the precision is marked as unknown, then report the column size directly. + Debug.Assert(metaType.Scale == TdsEnums.UNKNOWN_PRECISION_SCALE); + typeRow[DbMetaDataColumnNames.ColumnSize] = + metaType.Precision == TdsEnums.UNKNOWN_PRECISION_SCALE + ? metaType.FixedLength + : metaType.Precision; + + typeRow[DbMetaDataColumnNames.DataType] = metaType.ClassType.FullName; + // Of all fixed-scale integer types, only "tinyint", "smallint", "int" and "bigint" are auto-incrementable. + typeRow[DbMetaDataColumnNames.IsAutoIncrementable] = + integerDbType is SqlDbType.TinyInt or SqlDbType.SmallInt or SqlDbType.Int or SqlDbType.BigInt; + typeRow[DbMetaDataColumnNames.IsBestMatch] = isBestMatch; + typeRow[DbMetaDataColumnNames.IsCaseSensitive] = false; + typeRow[DbMetaDataColumnNames.IsConcurrencyType] = false; + typeRow[DbMetaDataColumnNames.IsFixedLength] = true; + // "real" is an ISO synonym of "float(24)". This means that it's a fixed-scale alias of a dynamic-scale type. + // "bit" is also not considered fixed precision/scale, since SQL Server packs multiple bit columns into the same byte. + typeRow[DbMetaDataColumnNames.IsFixedPrecisionScale] = integerDbType is not SqlDbType.Real and not SqlDbType.Bit; + typeRow[DbMetaDataColumnNames.IsLong] = false; + typeRow[DbMetaDataColumnNames.IsNullable] = true; + typeRow[DbMetaDataColumnNames.IsSearchable] = true; + typeRow[DbMetaDataColumnNames.IsSearchableWithLike] = false; + // Only "tinyint" is unsigned. "bit" does not have the concept of signed/unsigned. + if (integerDbType is not SqlDbType.Bit) + { + typeRow[DbMetaDataColumnNames.IsUnsigned] = integerDbType is SqlDbType.TinyInt; + } + + dataTypesDataTable.Rows.Add(typeRow); + } + + void AddVariablePrecisionNumericType(SqlDbType numericDbType, int columnSize, + string? aliasType = null) + { + MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(numericDbType, isMultiValued: false); + string typeName = aliasType ?? metaType.TypeName; + bool variableScale = numericDbType is SqlDbType.Decimal; + DataRow typeRow = dataTypesDataTable.NewRow(); + + typeRow[DbMetaDataColumnNames.TypeName] = typeName; + typeRow[DbMetaDataColumnNames.ProviderDbType] = (int)metaType.SqlDbType; + typeRow[DbMetaDataColumnNames.ColumnSize] = columnSize; + + // Both "float" and "decimal" have variable precision, but "decimal" also has variable scale. + if (variableScale) + { + typeRow[DbMetaDataColumnNames.CreateFormat] = $"{typeName}({{0}}, {{1}})"; + typeRow[DbMetaDataColumnNames.CreateParameters] = "precision,scale"; + typeRow[DbMetaDataColumnNames.MinimumScale] = 0; + // The data type is a fixed number of bytes, which can be distributed between the precision + // and the scale. Therefore, the maximum scale is equal to the column size. + typeRow[DbMetaDataColumnNames.MaximumScale] = columnSize; + } + else + { + typeRow[DbMetaDataColumnNames.CreateFormat] = $"{typeName}({{0}})"; + typeRow[DbMetaDataColumnNames.CreateParameters] = "number of bits used to store the mantissa"; + } + + typeRow[DbMetaDataColumnNames.DataType] = metaType.ClassType.FullName; + // Only the "decimal" type is auto-incrementable. + typeRow[DbMetaDataColumnNames.IsAutoIncrementable] = numericDbType is SqlDbType.Decimal; + typeRow[DbMetaDataColumnNames.IsBestMatch] = true; + typeRow[DbMetaDataColumnNames.IsCaseSensitive] = false; + typeRow[DbMetaDataColumnNames.IsConcurrencyType] = false; + typeRow[DbMetaDataColumnNames.IsFixedLength] = true; + typeRow[DbMetaDataColumnNames.IsFixedPrecisionScale] = false; + typeRow[DbMetaDataColumnNames.IsLong] = false; + typeRow[DbMetaDataColumnNames.IsNullable] = true; + typeRow[DbMetaDataColumnNames.IsSearchable] = true; + typeRow[DbMetaDataColumnNames.IsSearchableWithLike] = false; + typeRow[DbMetaDataColumnNames.IsUnsigned] = false; + + dataTypesDataTable.Rows.Add(typeRow); + } + + void AddFixedPrecisionDateTimeType(SqlDbType dateTimeDbType, bool isBestMatch, + string? minimumVersion = null) + { + MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(dateTimeDbType, isMultiValued: false); + DataRow typeRow = dataTypesDataTable.NewRow(); + + typeRow[DbMetaDataColumnNames.TypeName] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.ProviderDbType] = (int)metaType.SqlDbType; + // "date" reports an unknown precision and an unknown scale, even though its precision and scale are fixed. + // To that end, we report its column length directly. + typeRow[DbMetaDataColumnNames.ColumnSize] = + metaType.Precision == TdsEnums.UNKNOWN_PRECISION_SCALE + ? metaType.FixedLength + : metaType.Precision; + typeRow[DbMetaDataColumnNames.CreateFormat] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.DataType] = metaType.ClassType.FullName; + typeRow[DbMetaDataColumnNames.IsAutoIncrementable] = false; + typeRow[DbMetaDataColumnNames.IsBestMatch] = isBestMatch; + typeRow[DbMetaDataColumnNames.IsCaseSensitive] = false; + typeRow[DbMetaDataColumnNames.IsConcurrencyType] = false; + typeRow[DbMetaDataColumnNames.IsFixedLength] = true; + typeRow[DbMetaDataColumnNames.IsFixedPrecisionScale] = dateTimeDbType is SqlDbType.Date; + typeRow[DbMetaDataColumnNames.IsLong] = false; + typeRow[DbMetaDataColumnNames.IsNullable] = true; + typeRow[DbMetaDataColumnNames.IsSearchable] = true; + typeRow[DbMetaDataColumnNames.IsSearchableWithLike] = true; + typeRow[DbMetaDataColumnNames.LiteralPrefix] = @"{ts '"; + typeRow[DbMetaDataColumnNames.LiteralSuffix] = @"'}"; + + if (minimumVersion is not null) + { + typeRow[MinimumVersionKey] = minimumVersion; + } + + dataTypesDataTable.Rows.Add(typeRow); + } + + void AddVariablePrecisionDateTimeType(SqlDbType dateTimeDbType, int columnSize, + bool isBestMatch = true, + string? minimumVersion = null) + { + MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(dateTimeDbType, isMultiValued: false); + DataRow typeRow = dataTypesDataTable.NewRow(); + + typeRow[DbMetaDataColumnNames.TypeName] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.ProviderDbType] = (int)metaType.SqlDbType; + typeRow[DbMetaDataColumnNames.ColumnSize] = columnSize; + typeRow[DbMetaDataColumnNames.CreateFormat] = $"{metaType.TypeName}({{0}})"; + // The documentation describes these data types as having variable precision, but GetSchema reports that they + // have variable scale. + typeRow[DbMetaDataColumnNames.CreateParameters] = "scale"; + typeRow[DbMetaDataColumnNames.DataType] = metaType.ClassType.FullName; + typeRow[DbMetaDataColumnNames.IsAutoIncrementable] = false; + typeRow[DbMetaDataColumnNames.IsBestMatch] = isBestMatch; + typeRow[DbMetaDataColumnNames.IsCaseSensitive] = false; + typeRow[DbMetaDataColumnNames.IsConcurrencyType] = false; + typeRow[DbMetaDataColumnNames.IsFixedLength] = false; + typeRow[DbMetaDataColumnNames.IsFixedPrecisionScale] = false; + typeRow[DbMetaDataColumnNames.IsLong] = false; + typeRow[DbMetaDataColumnNames.IsNullable] = true; + typeRow[DbMetaDataColumnNames.IsSearchable] = true; + typeRow[DbMetaDataColumnNames.IsSearchableWithLike] = true; + typeRow[DbMetaDataColumnNames.MinimumScale] = 0; + typeRow[DbMetaDataColumnNames.MaximumScale] = metaType.Scale; + typeRow[DbMetaDataColumnNames.LiteralPrefix] = @"{ts '"; + typeRow[DbMetaDataColumnNames.LiteralSuffix] = @"'}"; + + if (minimumVersion is not null) + { + typeRow[MinimumVersionKey] = minimumVersion; + } + + dataTypesDataTable.Rows.Add(typeRow); + } + + void AddLongStringOrBinaryType(SqlDbType longDbType, + string? literalPrefix = null, string? literalSuffix = null, + string? minimumVersion = null) => + AddStringOrBinaryType(longDbType, + // The column size is measured in elements, not bytes. For ntext, each element is a 2 byte Unicode character. + columnSize: longDbType is SqlDbType.NText ? int.MaxValue / ADP.CharSize : int.MaxValue, + isLong: true, isFixedLength: false, + isSearchable: false, + literalPrefix: literalPrefix, literalSuffix: literalSuffix, + minimumVersion: minimumVersion); + + void AddFixedLengthStringOrBinaryType(SqlDbType fixedLengthDbType, + string? literalPrefix = null, string? literalSuffix = null) => + AddStringOrBinaryType(fixedLengthDbType, + // See the comment on AddLongStringOrBinary regarding column sizes. To add: the "binary" type can be up to 8000 bytes. + columnSize: fixedLengthDbType switch + { + SqlDbType.Binary => 8000, + SqlDbType.NChar => int.MaxValue / ADP.CharSize, + _ => int.MaxValue + }, + isLong: false, isFixedLength: true, + isSearchable: true, + literalPrefix: literalPrefix, literalSuffix: literalSuffix); + + void AddVariableLengthStringOrBinaryType(SqlDbType variableLengthDbType, + string? literalPrefix = null, string? literalSuffix = null) => + AddStringOrBinaryType(variableLengthDbType, + // See the comment on AddLongStringOrBinary regarding column sizes. Unlike the "binary" type, varbinary is reported to + // have a maximum column size of (2^32-1) / 2 elements, just as nvarchar does. + columnSize: variableLengthDbType is SqlDbType.NVarChar or SqlDbType.VarBinary + ? int.MaxValue / ADP.CharSize + : int.MaxValue, + isLong: false, isFixedLength: false, + isSearchable: true, + literalPrefix: literalPrefix, literalSuffix: literalSuffix); + + void AddRowVersionType() => + AddStringOrBinaryType(SqlDbType.Timestamp, + columnSize: TdsEnums.TEXT_TIME_STAMP_LEN, isLong: false, isFixedLength: true, + isSearchable: true, + literalPrefix: "0x"); + + void AddStringOrBinaryType(SqlDbType sqlDbType, int columnSize, bool isLong, + bool isFixedLength, bool isSearchable, + string? literalPrefix = null, string? literalSuffix = null, + string? minimumVersion = null) + { + MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(sqlDbType, isMultiValued: false); + DataRow typeRow = dataTypesDataTable.NewRow(); + bool hasLengthSpecifier = sqlDbType is SqlDbType.Char or SqlDbType.NChar or SqlDbType.Binary + or SqlDbType.VarChar or SqlDbType.NVarChar or SqlDbType.VarBinary; + + typeRow[DbMetaDataColumnNames.TypeName] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.ProviderDbType] = (int)metaType.SqlDbType; + typeRow[DbMetaDataColumnNames.ColumnSize] = columnSize; + // Several string or binary data types do not include the length in their type declaration. + // Of the data types which do, fixed-length types use the length parameter to decide the full length, + // while the rest use it to decide the maximum length. + if (hasLengthSpecifier) + { + typeRow[DbMetaDataColumnNames.CreateFormat] = $"{metaType.TypeName}({{0}})"; + typeRow[DbMetaDataColumnNames.CreateParameters] = + isFixedLength ? "length" : "max length"; + } + else + { + typeRow[DbMetaDataColumnNames.CreateFormat] = metaType.TypeName; + } + typeRow[DbMetaDataColumnNames.DataType] = metaType.ClassType.FullName; + typeRow[DbMetaDataColumnNames.IsAutoIncrementable] = false; + // The DataType for XML is string, which is not the best match for an XML type. + // Similarly, although timestamp/rowversion is represented as a byte array, it's not best + // represented as such. + typeRow[DbMetaDataColumnNames.IsBestMatch] = sqlDbType is not SqlDbType.Xml and not SqlDbType.Timestamp and not SqlDbTypeExtensions.Json; + typeRow[DbMetaDataColumnNames.IsCaseSensitive] = false; + typeRow[DbMetaDataColumnNames.IsConcurrencyType] = sqlDbType is SqlDbType.Timestamp; + typeRow[DbMetaDataColumnNames.IsFixedLength] = isFixedLength; + typeRow[DbMetaDataColumnNames.IsFixedPrecisionScale] = false; + typeRow[DbMetaDataColumnNames.IsLong] = isLong; + typeRow[DbMetaDataColumnNames.IsNullable] = sqlDbType is not SqlDbType.Timestamp; + typeRow[DbMetaDataColumnNames.IsSearchable] = isSearchable; + // String types are searchable with LIKE; binary types are not. SQL Server considers XML and JSON a binary type for this purpose. + typeRow[DbMetaDataColumnNames.IsSearchableWithLike] = + metaType.IsCharType && sqlDbType is not SqlDbType.Xml and not SqlDbTypeExtensions.Json; + + if (literalPrefix is null && literalSuffix is null) + { + // If no literal prefix or suffix is specified, then literal support is not available. + typeRow[DbMetaDataColumnNames.IsLiteralSupported] = false; + } + else + { + if (literalPrefix is not null) + { + typeRow[DbMetaDataColumnNames.LiteralPrefix] = literalPrefix; + } + if (literalSuffix is not null) + { + typeRow[DbMetaDataColumnNames.LiteralSuffix] = literalSuffix; + } + } + + if (minimumVersion is not null) + { + typeRow[MinimumVersionKey] = minimumVersion; + } + + dataTypesDataTable.Rows.Add(typeRow); + } + + void AddSqlVariantType() + { + MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.Variant, isMultiValued: false); + DataRow typeRow = dataTypesDataTable.NewRow(); + + typeRow[DbMetaDataColumnNames.TypeName] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.ProviderDbType] = (int)metaType.SqlDbType; + typeRow[DbMetaDataColumnNames.CreateFormat] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.DataType] = metaType.ClassType.FullName; + typeRow[DbMetaDataColumnNames.IsAutoIncrementable] = false; + typeRow[DbMetaDataColumnNames.IsBestMatch] = true; + typeRow[DbMetaDataColumnNames.IsCaseSensitive] = false; + typeRow[DbMetaDataColumnNames.IsConcurrencyType] = false; + typeRow[DbMetaDataColumnNames.IsFixedLength] = false; + typeRow[DbMetaDataColumnNames.IsFixedPrecisionScale] = false; + typeRow[DbMetaDataColumnNames.IsLong] = false; + typeRow[DbMetaDataColumnNames.IsNullable] = true; + typeRow[DbMetaDataColumnNames.IsSearchable] = true; + typeRow[DbMetaDataColumnNames.IsSearchableWithLike] = false; + typeRow[DbMetaDataColumnNames.IsLiteralSupported] = false; + + dataTypesDataTable.Rows.Add(typeRow); + } + + void AddUniqueIdentifierType() + { + MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.UniqueIdentifier, isMultiValued: false); + DataRow typeRow = dataTypesDataTable.NewRow(); + + typeRow[DbMetaDataColumnNames.TypeName] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.ProviderDbType] = (int)metaType.SqlDbType; + typeRow[DbMetaDataColumnNames.ColumnSize] = metaType.FixedLength; + typeRow[DbMetaDataColumnNames.CreateFormat] = metaType.TypeName; + typeRow[DbMetaDataColumnNames.DataType] = metaType.ClassType.FullName; + typeRow[DbMetaDataColumnNames.IsAutoIncrementable] = false; + typeRow[DbMetaDataColumnNames.IsBestMatch] = true; + typeRow[DbMetaDataColumnNames.IsCaseSensitive] = false; + typeRow[DbMetaDataColumnNames.IsConcurrencyType] = false; + typeRow[DbMetaDataColumnNames.IsFixedLength] = true; + typeRow[DbMetaDataColumnNames.IsFixedPrecisionScale] = false; + typeRow[DbMetaDataColumnNames.IsLong] = false; + typeRow[DbMetaDataColumnNames.IsNullable] = true; + typeRow[DbMetaDataColumnNames.IsSearchable] = true; + typeRow[DbMetaDataColumnNames.IsSearchableWithLike] = false; + typeRow[DbMetaDataColumnNames.LiteralPrefix] = "'"; + typeRow[DbMetaDataColumnNames.LiteralSuffix] = "'"; + + dataTypesDataTable.Rows.Add(typeRow); + } + } + + private static DataTable CreateDataTypesDataTable() + => new(DbMetaDataCollectionNames.DataTypes) + { + Columns = + { + new DataColumn(DbMetaDataColumnNames.TypeName, typeof(string)), + new DataColumn(DbMetaDataColumnNames.ProviderDbType, typeof(int)), + new DataColumn(DbMetaDataColumnNames.ColumnSize, typeof(long)), + new DataColumn(DbMetaDataColumnNames.CreateFormat, typeof(string)), + new DataColumn(DbMetaDataColumnNames.CreateParameters, typeof(string)), + new DataColumn(DbMetaDataColumnNames.DataType, typeof(string)), + new DataColumn(DbMetaDataColumnNames.IsAutoIncrementable, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsBestMatch, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsCaseSensitive, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsFixedLength, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsFixedPrecisionScale, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsLong, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsNullable, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsSearchable, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsSearchableWithLike, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.IsUnsigned, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.MaximumScale, typeof(short)), + new DataColumn(DbMetaDataColumnNames.MinimumScale, typeof(short)), + new DataColumn(DbMetaDataColumnNames.IsConcurrencyType, typeof(bool)), + new DataColumn(MaximumVersionKey, typeof(string)), + new DataColumn(MinimumVersionKey, typeof(string)), + new DataColumn(DbMetaDataColumnNames.IsLiteralSupported, typeof(bool)), + new DataColumn(DbMetaDataColumnNames.LiteralPrefix, typeof(string)), + new DataColumn(DbMetaDataColumnNames.LiteralSuffix, typeof(string)) + } + }; +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs index ca3dec0658..2bc38b34f8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs @@ -15,7 +15,7 @@ namespace Microsoft.Data.SqlClient { - internal sealed class SqlMetaDataFactory : IDisposable + internal sealed partial class SqlMetaDataFactory : IDisposable { // Well-known column names private const string CollectionNameKey = "CollectionName"; @@ -689,6 +689,9 @@ private DataSet LoadDataSetFromXml(Stream XmlStream) { Locale = CultureInfo.InvariantCulture }; + + LoadDataTypesDataTables(metaDataCollectionsDataSet); + XmlReaderSettings settings = new() { XmlResolver = null, @@ -738,9 +741,6 @@ private DataSet LoadDataSetFromXml(Stream XmlStream) dataTable = CreateDataSourceInformationDataTable(); rowFixup = FixUpDataSourceInformationRow; break; - case "DataTypesTable": - dataTable = CreateDataTypesDataTable(); - break; case "ReservedWordsTable": dataTable = CreateReservedWordsDataTable(); break; @@ -874,38 +874,6 @@ private static DataTable CreateDataSourceInformationDataTable() } }; - private static DataTable CreateDataTypesDataTable() - => new(DbMetaDataCollectionNames.DataTypes) - { - Columns = - { - new DataColumn(DbMetaDataColumnNames.TypeName, typeof(string)), - new DataColumn(DbMetaDataColumnNames.ProviderDbType, typeof(int)), - new DataColumn(DbMetaDataColumnNames.ColumnSize, typeof(long)), - new DataColumn(DbMetaDataColumnNames.CreateFormat, typeof(string)), - new DataColumn(DbMetaDataColumnNames.CreateParameters, typeof(string)), - new DataColumn(DbMetaDataColumnNames.DataType, typeof(string)), - new DataColumn(DbMetaDataColumnNames.IsAutoIncrementable, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsBestMatch, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsCaseSensitive, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsFixedLength, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsFixedPrecisionScale, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsLong, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsNullable, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsSearchable, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsSearchableWithLike, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.IsUnsigned, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.MaximumScale, typeof(short)), - new DataColumn(DbMetaDataColumnNames.MinimumScale, typeof(short)), - new DataColumn(DbMetaDataColumnNames.IsConcurrencyType, typeof(bool)), - new DataColumn(MaximumVersionKey, typeof(string)), - new DataColumn(MinimumVersionKey, typeof(string)), - new DataColumn(DbMetaDataColumnNames.IsLiteralSupported, typeof(bool)), - new DataColumn(DbMetaDataColumnNames.LiteralPrefix, typeof(string)), - new DataColumn(DbMetaDataColumnNames.LiteralSuffix, typeof(string)) - } - }; - private static DataTable CreateReservedWordsDataTable() => new(DbMetaDataCollectionNames.ReservedWords) { diff --git a/src/Microsoft.Data.SqlClient/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml b/src/Microsoft.Data.SqlClient/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml index 15090edf8e..494e9a9106 100644 --- a/src/Microsoft.Data.SqlClient/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml +++ b/src/Microsoft.Data.SqlClient/src/Resources/Microsoft.Data.SqlClient.SqlMetaData.xml @@ -580,695 +580,6 @@ 15 - - - smallint - 16 - 5 - smallint - System.Int16 - true - true - false - false - true - true - false - true - true - false - false - - - int - 8 - 10 - int - System.Int32 - true - true - false - false - true - true - false - true - true - false - false - - - real - 13 - 7 - real - System.Single - false - true - false - false - true - false - false - true - true - false - false - - - float - 6 - 53 - float({0}) - number of bits used to store the mantissa - System.Double - false - true - false - false - true - false - false - true - true - false - false - - - money - 9 - 19 - money - System.Decimal - false - false - false - false - true - true - false - true - true - false - false - - - smallmoney - 17 - 10 - smallmoney - System.Decimal - false - false - false - false - true - true - false - true - true - false - false - - - bit - 2 - 1 - bit - System.Boolean - false - false - false - false - true - false - false - true - true - false - - - tinyint - 20 - 3 - tinyint - System.Byte - true - true - false - false - true - true - false - true - true - false - true - - - bigint - 0 - 19 - bigint - System.Int64 - true - true - false - false - true - true - false - true - true - false - false - - - varbinary - 21 - 8000 - varbinary({0}) - max length - System.Byte[] - false - true - false - false - false - false - false - true - true - false - 08.99.999.9 - 0x - - - timestamp - 19 - 8 - timestamp - System.Byte[] - false - false - false - true - true - false - false - false - true - false - 0x - - - binary - 1 - 8000 - binary({0}) - length - System.Byte[] - false - true - false - false - true - false - false - true - true - false - 0x - - - image - 7 - 2147483647 - image - System.Byte[] - false - true - false - false - false - false - true - true - false - false - 0x - - - char - 3 - 8000 - char - length - System.String - false - true - false - false - true - false - false - true - true - true - 08.99.999.9 - ' - ' - - - text - 18 - 2147483647 - text - System.String - false - true - false - false - false - false - true - true - false - true - ' - ' - - - varchar - 22 - 8000 - varchar({0}) - max length - System.String - false - true - false - false - false - false - false - true - true - true - 08.99.999.9 - ' - ' - - - nchar - 10 - 4000 - nchar({0}) - length - System.String - false - true - false - false - true - false - false - true - true - true - 08.99.999.9 - N' - ' - - - ntext - 11 - 1073741823 - ntext - System.String - false - true - false - false - false - false - true - true - false - true - N' - ' - - - nvarchar - 12 - 4000 - nvarchar({0}) - max length - System.String - false - true - false - false - false - false - false - true - true - true - 08.99.999.9 - N' - ' - - - decimal - 5 - 38 - decimal({0}, {1}) - precision,scale - System.Decimal - true - true - false - false - true - false - false - true - true - false - false - 38 - 0 - - - numeric - 5 - 38 - numeric({0}, {1}) - precision,scale - System.Decimal - true - true - false - false - true - false - false - true - true - false - false - 38 - 0 - - - datetime - 4 - 23 - datetime - System.DateTime - false - true - false - false - true - false - false - true - true - true - {ts ' - '} - - - smalldatetime - 15 - 16 - smalldatetime - System.DateTime - false - true - false - false - true - false - false - true - true - true - {ts ' - '} - - - sql_variant - 23 - sql_variant - System.Object - false - true - false - false - false - false - false - true - true - false - false - - - xml - 25 - 2147483647 - xml - System.String - false - false - false - false - false - false - true - true - false - false - 09.00.000.0 - false - - - varchar - 22 - 2147483647 - varchar({0}) - max length - System.String - false - true - false - false - false - false - false - true - true - true - 09.00.000.0 - ' - ' - - - char - 3 - 2147483647 - char({0}) - length - System.String - false - true - false - false - true - false - false - true - true - true - 09.00.000.0 - ' - ' - - - nchar - 10 - 1073741823 - nchar({0}) - length - System.String - false - true - false - false - true - false - false - true - true - true - 09.00.000.0 - N' - ' - - - nvarchar - 12 - 1073741823 - nvarchar({0}) - max length - System.String - false - true - false - false - false - false - false - true - true - true - 09.00.000.0 - N' - ' - - - varbinary - 21 - 1073741823 - varbinary({0}) - max length - System.Byte[] - false - true - false - false - false - false - false - true - true - false - 09.00.000.0 - 0x - - - uniqueidentifier - 14 - 16 - uniqueidentifier - System.Guid - false - true - false - false - true - false - false - true - true - false - ' - ' - - - date - 31 - 3 - date - System.DateTime - false - false - false - false - true - true - false - true - true - true - 10.00.000.0 - {ts ' - '} - - - time - 32 - 5 - time({0}) - scale - System.TimeSpan - false - false - false - false - false - false - false - true - true - true - 7 - 0 - 10.00.000.0 - {ts ' - '} - - - datetime2 - 33 - 8 - datetime2({0}) - scale - System.DateTime - false - true - false - false - false - false - false - true - true - true - 7 - 0 - 10.00.000.0 - {ts ' - '} - - - datetimeoffset - 34 - 10 - datetimeoffset({0}) - scale - System.DateTimeOffset - false - true - false - false - false - false - false - true - true - true - 7 - 0 - 10.00.000.0 - {ts ' - '} - - ADD diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 7b1fb3bc30..96a09125f4 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -93,6 +93,9 @@ public static class DataTestUtility //SQL Server EngineEdition private static string s_sqlServerEngineEdition; + // SQL Server capabilities + private static bool? s_isJsonSupported; + // Azure Synapse EngineEditionId == 6 // More could be read at https://learn.microsoft.com/en-us/sql/t-sql/functions/serverproperty-transact-sql?view=sql-server-ver16#propertyname public static bool IsAzureSynapse @@ -142,6 +145,17 @@ public static bool IsTDS8Supported } } + /// + /// Determines whether the SQL Server supports the 'json' data type. + /// + /// + /// This method attempts to connect to the SQL Server and check for the existence of the + /// 'json' data type. + /// + public static bool IsJsonSupported => + s_isJsonSupported ??= IsTCPConnStringSetup() && + IsTypePresent("json"); + static DataTestUtility() { Config c = Config.Load(); @@ -402,6 +416,17 @@ public static bool GetSQLServerStatusOnTDS8(string connectionString) return isTDS8Supported; } + public static bool IsTypePresent(string typeName) + { + using SqlConnection connection = new(TCPConnectionString); + using SqlCommand command = new("SELECT COUNT(1) FROM SYS.TYPES WHERE [name] = @name", connection); + + connection.Open(); + command.Parameters.AddWithValue("@name", typeName); + + return (int)command.ExecuteScalar() > 0; + } + public static bool IsNotX86Architecture => RuntimeInformation.ProcessArchitecture != Architecture.X86; public static bool IsDatabasePresent(string name) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs index 0bc5b29806..00d0d49a15 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Data; +using System.Data.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -107,29 +108,91 @@ public static void GetStructuredTypeMembersFromSchema() VerifySchemaTable(SqlClientMetaDataCollectionNames.StructuredTypeMembers, new string[] { "TYPE_CATALOG", "TYPE_SCHEMA", "TYPE_NAME", "MEMBER_NAME", "ORDINAL_POSITION" }); } - private static void VerifySchemaTable(string schemaItemName, string[] testColumnNames) - { - SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) + [ConditionalFact(nameof(CanRunSchemaTests))] + public static void GetDataTypesFromSchema() + { + DataTable schemaTable = VerifySchemaTable(DbMetaDataCollectionNames.DataTypes, [ + DbMetaDataColumnNames.TypeName, + DbMetaDataColumnNames.ProviderDbType, + DbMetaDataColumnNames.ColumnSize, + DbMetaDataColumnNames.CreateFormat, + DbMetaDataColumnNames.CreateParameters, + DbMetaDataColumnNames.DataType, + DbMetaDataColumnNames.IsAutoIncrementable, + DbMetaDataColumnNames.IsBestMatch, + DbMetaDataColumnNames.IsCaseSensitive, + DbMetaDataColumnNames.IsFixedLength, + DbMetaDataColumnNames.IsFixedPrecisionScale, + DbMetaDataColumnNames.IsLong, + DbMetaDataColumnNames.IsNullable, + DbMetaDataColumnNames.IsSearchable, + DbMetaDataColumnNames.IsSearchableWithLike, + DbMetaDataColumnNames.IsUnsigned, + DbMetaDataColumnNames.MaximumScale, + DbMetaDataColumnNames.MinimumScale, + DbMetaDataColumnNames.IsConcurrencyType, + DbMetaDataColumnNames.IsLiteralSupported, + DbMetaDataColumnNames.LiteralPrefix, + DbMetaDataColumnNames.LiteralSuffix + ]); + + VerifyDataTypesTable(schemaTable); + } + + private static DataTable GetSchemaTable(string schemaItemName) + { + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString) { InitialCatalog = "master" }; - using (SqlConnection connection = new SqlConnection(builder.ConnectionString)) + using SqlConnection connection = new(builder.ConnectionString); + // Connect to the database then retrieve the schema information + connection.Open(); + + return connection.GetSchema(schemaItemName); + } + + private static DataTable VerifySchemaTable(string schemaItemName, string[] testColumnNames) + { + HashSet columnNames = []; + DataTable schemaTable = GetSchemaTable(schemaItemName); + + // Get all table columns + foreach (DataColumn column in schemaTable.Columns) { - // Connect to the database then retrieve the schema information - connection.Open(); - DataTable table = connection.GetSchema(schemaItemName); + columnNames.Add(column.ColumnName); + } - // Get all table columns - HashSet columnNames = new HashSet(); + Assert.All(testColumnNames, column => Assert.Contains(column, columnNames)); + return schemaTable; + } - foreach (DataColumn column in table.Columns) - { - columnNames.Add(column.ColumnName); - } + private static void VerifyDataTypesTable(DataTable dataTypesTable) + { + string[] expectedTypes = [ + "smallint", "int", "real", "float", "money", "smallmoney", "bit", "tinyint", "bigint", "timestamp", + "binary", "image", "text", "ntext", "decimal", "numeric", "datetime", "smalldatetime", "sql_variant", "xml", + "varchar", "char", "nchar", "nvarchar", "varbinary", "uniqueidentifier", "date", "time", "datetime2", "datetimeoffset" + ]; + HashSet actualTypes = []; - Assert.All(testColumnNames, column => Assert.Contains(column, columnNames)); + // Get every type name, asserting that it is a unique string. + foreach (DataRow row in dataTypesTable.Rows) + { + string typeName = row[DbMetaDataColumnNames.TypeName] as string; + + Assert.False(string.IsNullOrEmpty(typeName)); + Assert.True(actualTypes.Add(typeName)); } + + // Every expected type should be present. There will often be additional types present - user-defined table types + // and CLR types (such as geography and geometry.) + Assert.All(expectedTypes, type => Assert.Contains(type, actualTypes)); + + // The "json" type should only be present when running against a SQL Server version which supports it. + // SQL Azure reports a version of 12.x but supports JSON, so SqlClient doesn't include it in the list of types. + Assert.Equal(DataTestUtility.IsJsonSupported && DataTestUtility.IsNotAzureServer(), actualTypes.Contains("json")); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs index d722ceb4b3..c9f540b68f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs @@ -265,7 +265,7 @@ private async Task BulkCopyDataAsync(CommandBehavior cb, bool enableStraming, in } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsJsonSupported))] [MemberData( nameof(JsonBulkCopyTestData) #if NETFRAMEWORK @@ -289,7 +289,7 @@ public void TestJsonBulkCopy(CommandBehavior cb, bool enableStraming, int jsonAr } } - [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalTheory(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsJsonSupported))] [MemberData( nameof(JsonBulkCopyTestData) #if NETFRAMEWORK diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs index 19a0559e1b..bdfe90c98f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs @@ -157,7 +157,7 @@ private void DeleteFile(string filename) } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsJsonSupported))] public void TestJsonStreaming() { GenerateJsonFile(1000, _jsonFile); @@ -173,7 +173,7 @@ public void TestJsonStreaming() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsJsonSupported))] public async Task TestJsonStreamingAsync() { GenerateJsonFile(1000, _jsonFile); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonTest.cs index 516f9d1750..ccf0c00919 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonTest.cs @@ -73,7 +73,7 @@ private void ValidateNullJson(SqlDataReader reader) } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public void TestJsonWrite() { string tableName = DataTestUtility.GenerateObjectName(); @@ -137,7 +137,7 @@ public void TestJsonWrite() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public async Task TestJsonWriteAsync() { string tableName = DataTestUtility.GenerateObjectName(); @@ -201,7 +201,7 @@ public async Task TestJsonWriteAsync() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public void TestJsonRead() { string tableName = DataTestUtility.GenerateObjectName(); @@ -260,7 +260,7 @@ public void TestJsonRead() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public async Task TestJsonReadAsync() { string tableName = DataTestUtility.GenerateObjectName(); @@ -319,7 +319,7 @@ public async Task TestJsonReadAsync() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public void TestNullJson() { string tableName = DataTestUtility.GenerateObjectName(); @@ -350,7 +350,7 @@ public void TestNullJson() DataTestUtility.DropTable(connection, tableName); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public void TestJsonAPIs() { string tableName = DataTestUtility.GenerateObjectName(); @@ -398,7 +398,7 @@ public void TestJsonAPIs() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public void TestJsonWithMARS() { string table1Name = DataTestUtility.GenerateObjectName(); @@ -454,7 +454,7 @@ public void TestJsonWithMARS() } } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsAzureServer), nameof(DataTestUtility.IsNotManagedInstance))] + [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))] public void TestJsonSPParams() { string tableName = DataTestUtility.GenerateObjectName();