From de2b12ec6831e6c0785e914a97e1d6d3e515578b Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Fri, 19 Dec 2025 21:11:40 +0000
Subject: [PATCH 1/7] Create structure of DataTypes static population
---
.../src/Microsoft.Data.SqlClient.csproj | 3 +
.../netfx/src/Microsoft.Data.SqlClient.csproj | 3 +
.../SqlClient/SqlMetaDataFactory.DataTypes.cs | 59 +++++++++++++++++++
.../Data/SqlClient/SqlMetaDataFactory.cs | 34 +----------
4 files changed, 66 insertions(+), 33 deletions(-)
create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
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..ee38a6a13c
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
@@ -0,0 +1,59 @@
+// 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;
+
+#nullable enable
+
+namespace Microsoft.Data.SqlClient;
+
+internal sealed partial class SqlMetaDataFactory
+{
+ private static void LoadDataTypesDataTables(DataSet metaDataCollectionsDataSet)
+ {
+ DataTable dataTypesDataTable = CreateDataTypesDataTable();
+
+ dataTypesDataTable.BeginLoadData();
+
+ //
+
+ dataTypesDataTable.EndLoadData();
+ dataTypesDataTable.AcceptChanges();
+
+ metaDataCollectionsDataSet.Tables.Add(dataTypesDataTable);
+ }
+
+ 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..b6b2cb5aed 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";
@@ -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)
{
From 3414a47ead49ce16ad7d238008e799cd792c07aa Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Fri, 19 Dec 2025 23:45:48 +0000
Subject: [PATCH 2/7] Remove redundant data types from metadata
SqlClient cannot connect to SQL Server 2000 instances, so the minimum server version is 9.0.
* Remove data type entries with a maximum version below this.
* Remove the minimum version constraint where this constraint will always be true.
---
.../Microsoft.Data.SqlClient.SqlMetaData.xml | 110 ------------------
1 file changed, 110 deletions(-)
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..2691be3382 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
@@ -743,26 +743,6 @@
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
@@ -818,27 +798,6 @@
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
@@ -858,48 +817,6 @@
'
'
-
- 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
@@ -919,27 +836,6 @@
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
@@ -1053,7 +949,6 @@
true
false
false
- 09.00.000.0
false
@@ -1073,7 +968,6 @@
true
true
true
- 09.00.000.0
'
'
@@ -1094,7 +988,6 @@
true
true
true
- 09.00.000.0
'
'
@@ -1115,7 +1008,6 @@
true
true
true
- 09.00.000.0
N'
'
@@ -1136,7 +1028,6 @@
true
true
true
- 09.00.000.0
N'
'
@@ -1157,7 +1048,6 @@
true
true
false
- 09.00.000.0
0x
From 2f907edb6481597d364a5c0220de083be10fa124 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Sun, 21 Dec 2025 23:54:59 +0000
Subject: [PATCH 3/7] Populate DataTypes table from MetaType information
---
.../SqlClient/SqlMetaDataFactory.DataTypes.cs | 429 ++++++++++++-
.../Data/SqlClient/SqlMetaDataFactory.cs | 6 +-
.../Microsoft.Data.SqlClient.SqlMetaData.xml | 579 ------------------
3 files changed, 402 insertions(+), 612 deletions(-)
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
index ee38a6a13c..7b6705da84 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
@@ -4,6 +4,8 @@
using System.Data;
using System.Data.Common;
+using System.Diagnostics;
+using Microsoft.Data.Common;
#nullable enable
@@ -17,43 +19,410 @@ private static void LoadDataTypesDataTables(DataSet metaDataCollectionsDataSet)
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, 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(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);
- }
- private static DataTable CreateDataTypesDataTable()
- => new(DbMetaDataCollectionNames.DataTypes)
+ 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) =>
+ 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);
+
+ 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)
+ {
+ 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;
+ 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)
{
- Columns =
+ // 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)
{
- 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))
+ typeRow[DbMetaDataColumnNames.LiteralSuffix] = literalSuffix;
}
- };
+ }
+
+ 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 b6b2cb5aed..2bc38b34f8 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs
@@ -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;
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 2691be3382..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,585 +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
-
-
- 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
-
-
- text
- 18
- 2147483647
- text
- System.String
- false
- true
- false
- false
- false
- false
- true
- true
- false
- true
- '
- '
-
-
- ntext
- 11
- 1073741823
- ntext
- System.String
- false
- true
- false
- false
- false
- false
- true
- true
- false
- true
- 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
- false
-
-
- varchar
- 22
- 2147483647
- varchar({0})
- max length
- System.String
- false
- true
- false
- false
- false
- false
- false
- true
- true
- true
- '
- '
-
-
- char
- 3
- 2147483647
- char({0})
- length
- System.String
- false
- true
- false
- false
- true
- false
- false
- true
- true
- true
- '
- '
-
-
- nchar
- 10
- 1073741823
- nchar({0})
- length
- System.String
- false
- true
- false
- false
- true
- false
- false
- true
- true
- true
- N'
- '
-
-
- nvarchar
- 12
- 1073741823
- nvarchar({0})
- max length
- System.String
- false
- true
- false
- false
- false
- false
- false
- true
- true
- true
- N'
- '
-
-
- varbinary
- 21
- 1073741823
- varbinary({0})
- max length
- System.Byte[]
- false
- true
- false
- false
- false
- false
- false
- true
- true
- false
- 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
From cb376785809a8da2d9890a7603d46848e0cb10fb Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Mon, 22 Dec 2025 13:40:42 +0000
Subject: [PATCH 4/7] Add test to verify presence of data types
---
.../ConnectionSchemaTest.cs | 87 ++++++++++++++++---
1 file changed, 73 insertions(+), 14 deletions(-)
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..468b881480 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,87 @@ 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);
+ }
+
+ Assert.All(testColumnNames, column => Assert.Contains(column, columnNames));
+ return schemaTable;
+ }
- // Get all table columns
- HashSet columnNames = new HashSet();
+ 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 = [];
- foreach (DataColumn column in table.Columns)
- {
- columnNames.Add(column.ColumnName);
- }
+ // 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.All(testColumnNames, column => Assert.Contains(column, columnNames));
+ 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));
}
}
}
From 2ac06238f7d6f87c757a930e1fb9b515422fcfe4 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Mon, 22 Dec 2025 14:24:10 +0000
Subject: [PATCH 5/7] Add json data type to DataTypes schema collection
---
.../SqlClient/SqlMetaDataFactory.DataTypes.cs | 7 ++++--
.../ManualTests/DataCommon/DataTestUtility.cs | 25 +++++++++++++++++++
.../ConnectionSchemaTest.cs | 3 +++
3 files changed, 33 insertions(+), 2 deletions(-)
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
index 7b6705da84..c23ef32255 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
@@ -58,6 +58,8 @@ private static void LoadDataTypesDataTables(DataSet metaDataCollectionsDataSet)
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");
@@ -241,7 +243,8 @@ void AddVariablePrecisionDateTimeType(SqlDbType dateTimeDbType, int columnSize,
}
void AddLongStringOrBinaryType(SqlDbType longDbType,
- string? literalPrefix = null, string? literalSuffix = null) =>
+ 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,
@@ -311,7 +314,7 @@ void AddStringOrBinaryType(SqlDbType sqlDbType, int columnSize, bool isLong,
// 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;
+ 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;
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 468b881480..47903fd019 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs
@@ -189,6 +189,9 @@ private static void VerifyDataTypesTable(DataTable dataTypesTable)
// 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.
+ Assert.Equal(DataTestUtility.IsJsonSupported, actualTypes.Contains("json"));
}
}
}
From 81aed9d6e6994f322572f7c807f6d91c41e31d30 Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Mon, 22 Dec 2025 14:28:07 +0000
Subject: [PATCH 6/7] Use IsJsonSupported more widely
This enables the JSON tests to run against SQL Server 2025.
Also correct a comment in LoadDataTypesDataTables.
---
.../SqlClient/SqlMetaDataFactory.DataTypes.cs | 2 +-
.../ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs | 4 ++--
.../ManualTests/SQL/JsonTest/JsonStreamTest.cs | 4 ++--
.../tests/ManualTests/SQL/JsonTest/JsonTest.cs | 16 ++++++++--------
4 files changed, 13 insertions(+), 13 deletions(-)
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
index c23ef32255..df00f4ffa7 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
@@ -24,7 +24,7 @@ private static void LoadDataTypesDataTables(DataSet metaDataCollectionsDataSet)
// 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, text, ntext, image.
+ // 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
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();
From 219824b97e2d85a0ca20f46cdf3774294d38f66e Mon Sep 17 00:00:00 2001
From: Edward Neal <55035479+edwardneal@users.noreply.github.com>
Date: Mon, 22 Dec 2025 17:51:49 +0000
Subject: [PATCH 7/7] Plumb minimum version logic to AddStringOrBinaryType
---
.../Data/SqlClient/SqlMetaDataFactory.DataTypes.cs | 11 +++++++++--
.../SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs | 3 ++-
2 files changed, 11 insertions(+), 3 deletions(-)
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
index df00f4ffa7..eba61969c3 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.DataTypes.cs
@@ -250,7 +250,8 @@ void AddLongStringOrBinaryType(SqlDbType longDbType,
columnSize: longDbType is SqlDbType.NText ? int.MaxValue / ADP.CharSize : int.MaxValue,
isLong: true, isFixedLength: false,
isSearchable: false,
- literalPrefix: literalPrefix, literalSuffix: literalSuffix);
+ literalPrefix: literalPrefix, literalSuffix: literalSuffix,
+ minimumVersion: minimumVersion);
void AddFixedLengthStringOrBinaryType(SqlDbType fixedLengthDbType,
string? literalPrefix = null, string? literalSuffix = null) =>
@@ -286,7 +287,8 @@ void AddRowVersionType() =>
void AddStringOrBinaryType(SqlDbType sqlDbType, int columnSize, bool isLong,
bool isFixedLength, bool isSearchable,
- string? literalPrefix = null, string? literalSuffix = null)
+ string? literalPrefix = null, string? literalSuffix = null,
+ string? minimumVersion = null)
{
MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(sqlDbType, isMultiValued: false);
DataRow typeRow = dataTypesDataTable.NewRow();
@@ -343,6 +345,11 @@ void AddStringOrBinaryType(SqlDbType sqlDbType, int columnSize, bool isLong,
}
}
+ if (minimumVersion is not null)
+ {
+ typeRow[MinimumVersionKey] = minimumVersion;
+ }
+
dataTypesDataTable.Rows.Add(typeRow);
}
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 47903fd019..00d0d49a15 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataBaseSchemaTest/ConnectionSchemaTest.cs
@@ -191,7 +191,8 @@ private static void VerifyDataTypesTable(DataTable dataTypesTable)
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.
- Assert.Equal(DataTestUtility.IsJsonSupported, actualTypes.Contains("json"));
+ // 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"));
}
}
}