From 942750a0a963cf87ef881217ff13f5eacd772015 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Sat, 2 Nov 2024 20:50:52 +0100 Subject: [PATCH 1/7] Implement IDbColumnSchemaGenerator in netfx to align SqlDataReader to netcore --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 76 +++++++++++- .../src/Microsoft.Data.SqlClient.csproj | 1 + .../Microsoft/Data/SqlClient/SqlDbColumn.cs | 117 ++++++++++++++++++ .../Data/SqlClient/TdsParserHelperClasses.cs | 6 +- .../SQL/UdtTest/SqlServerTypesTest.cs | 1 - 5 files changed, 192 insertions(+), 9 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index a918f61a1f..c0dd0fa9ca 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -4,6 +4,7 @@ using System; using System.Collections; +using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Data.Common; @@ -27,7 +28,7 @@ namespace Microsoft.Data.SqlClient { /// - public class SqlDataReader : DbDataReader, IDataReader + public class SqlDataReader : DbDataReader, IDataReader, IDbColumnSchemaGenerator { private enum ALTROWSTATUS { @@ -74,6 +75,7 @@ internal class SharedState private CommandBehavior _commandBehavior; private static int s_objectTypeCount; // EventSource Counter + private static readonly ReadOnlyCollection s_emptySchema = new ReadOnlyCollection(Array.Empty()); internal readonly int ObjectID = Interlocked.Increment(ref s_objectTypeCount); // context @@ -6402,5 +6404,73 @@ private void SwitchToAsyncWithoutSnapshot() _stateObj._asyncReadWithoutSnapshot = true; } - }// SqlDataReader -}// namespace + /// + public ReadOnlyCollection GetColumnSchema() + { + SqlStatistics statistics = null; + try + { + statistics = SqlStatistics.StartTimer(Statistics); + if (_metaData == null || _metaData.dbColumnSchema == null) + { + if (this.MetaData != null) + { + + _metaData.dbColumnSchema = BuildColumnSchema(); + Debug.Assert(_metaData.dbColumnSchema != null, "No schema information yet!"); + // filter table? + } + } + if (_metaData != null) + { + return _metaData.dbColumnSchema; + } + return s_emptySchema; + } + finally + { + SqlStatistics.StopTimer(statistics); + } + } + + private ReadOnlyCollection BuildColumnSchema() + { + _SqlMetaDataSet md = MetaData; + DbColumn[] columnSchema = new DbColumn[md.Length]; + for (int i = 0; i < md.Length; i++) + { + _SqlMetaData col = md[i]; + SqlDbColumn dbColumn = new SqlDbColumn(md[i]); + + if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.Is2008DateTimeType) + { + dbColumn.SqlNumericScale = MetaType.MetaNVarChar.Scale; + } + else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale) + { + dbColumn.SqlNumericScale = col.scale; + } + else + { + dbColumn.SqlNumericScale = col.metaType.Scale; + } + + if (_browseModeInfoConsumed) + { + dbColumn.SqlIsAliased = col.IsDifferentName; + dbColumn.SqlIsKey = col.IsKey; + dbColumn.SqlIsHidden = col.IsHidden; + dbColumn.SqlIsExpression = col.IsExpression; + } + + dbColumn.SqlDataType = GetFieldTypeInternal(col); + + dbColumn.SqlDataTypeName = GetDataTypeNameInternal(col); + + columnSchema[i] = dbColumn; + } + + return new ReadOnlyCollection(columnSchema); + } + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj index a5e25db563..120ca06273 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/src/Microsoft.Data.SqlClient.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs new file mode 100644 index 0000000000..bebe02b575 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Data; +using System.Data.Common; + +namespace Microsoft.Data.SqlClient +{ + internal class SqlDbColumn : DbColumn + { + private readonly _SqlMetaData _metadata; + + internal SqlDbColumn(_SqlMetaData md) + { + _metadata = md; + Populate(); + } + + private void Populate() + { + AllowDBNull = _metadata.IsNullable; + BaseCatalogName = _metadata.catalogName; + BaseColumnName = _metadata.baseColumn; + BaseSchemaName = _metadata.schemaName; + BaseServerName = _metadata.serverName; + BaseTableName = _metadata.tableName; + ColumnName = _metadata.column; + ColumnOrdinal = _metadata.ordinal; + ColumnSize = (_metadata.metaType.IsSizeInCharacters && (_metadata.length != 0x7fffffff)) ? (_metadata.length / 2) : _metadata.length; + IsAutoIncrement = _metadata.IsIdentity; + IsIdentity = _metadata.IsIdentity; + IsLong = _metadata.metaType.IsLong; + + if (SqlDbType.Timestamp == _metadata.type) + { + IsUnique = true; + } + else + { + IsUnique = false; + } + + if (TdsEnums.UNKNOWN_PRECISION_SCALE != _metadata.precision) + { + NumericPrecision = _metadata.precision; + } + else + { + NumericPrecision = _metadata.metaType.Precision; + } + + IsReadOnly = _metadata.IsReadOnly; + + UdtAssemblyQualifiedName = _metadata.udt?.AssemblyQualifiedName; + + } + + internal bool? SqlIsAliased + { + set + { + IsAliased = value; + } + } + + internal bool? SqlIsKey + { + set + { + IsKey = value; + } + } + + internal bool? SqlIsHidden + { + set + { + IsHidden = value; + } + } + + internal bool? SqlIsExpression + { + set + { + IsExpression = value; + } + } + + internal Type SqlDataType + { + set + { + DataType = value; + } + } + + internal string SqlDataTypeName + { + set + { + DataTypeName = value; + } + } + + internal int? SqlNumericScale + { + set + { + NumericScale = value; + } + } + + } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index 3f4186186b..72068bddb8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -305,9 +305,7 @@ internal sealed partial class _SqlMetaDataSet internal DataTable schemaTable; private readonly _SqlMetaData[] _metaDataArray; -#if !NETFRAMEWORK internal ReadOnlyCollection dbColumnSchema; -#endif private int _hiddenColumnCount; private int[] _visibleColumnMap; @@ -327,11 +325,9 @@ private _SqlMetaDataSet(_SqlMetaDataSet original) id = original.id; _hiddenColumnCount = original._hiddenColumnCount; _visibleColumnMap = original._visibleColumnMap; -#if !NETFRAMEWORK dbColumnSchema = original.dbColumnSchema; -#else schemaTable = original.schemaTable; -#endif + if (original._metaDataArray == null) { _metaDataArray = null; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs index bdbd805e73..f939b42cc7 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs @@ -252,7 +252,6 @@ private static void TestUdtSqlDataReaderGetStream(CommandBehavior behavior) } // Synapse: Parse error at line: 1, column: 41: Incorrect syntax near 'hierarchyid'. - [ActiveIssue("25421", TargetFrameworkMonikers.NetFramework)] [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static void TestUdtSchemaMetadata() { From df7811d20b72bc19586ee464a5b6d030d32d6ed9 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Sat, 2 Nov 2024 20:52:49 +0100 Subject: [PATCH 2/7] Add SystemDataCommonVersion variable --- tools/props/Versions.props | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/props/Versions.props b/tools/props/Versions.props index 76f8f3572e..b34e7b1c01 100644 --- a/tools/props/Versions.props +++ b/tools/props/Versions.props @@ -26,6 +26,7 @@ 6.0.0-preview1.24226.4 4.5.1 6.0.0 + 4.3.0 From 652e763066db75a9ea5efffd3e4ba0f46508c6a5 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Sat, 2 Nov 2024 21:03:03 +0100 Subject: [PATCH 3/7] Properly link SqlDbColumn --- .../src/Microsoft.Data.SqlClient.csproj | 4 +- .../Microsoft/Data/SqlClient/SqlDbColumn.cs | 117 ------------------ .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 + 3 files changed, 6 insertions(+), 118 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDbColumn.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 4f632cb12e..55c7d7a82e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -443,6 +443,9 @@ Microsoft\Data\SqlClient\SqlDataAdapter.cs + + Microsoft\Data\SqlClient\SqlDbColumn.cs + Microsoft\Data\SqlClient\SqlDependency.cs @@ -670,7 +673,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDbColumn.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDbColumn.cs deleted file mode 100644 index bebe02b575..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDbColumn.cs +++ /dev/null @@ -1,117 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Data; -using System.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - internal class SqlDbColumn : DbColumn - { - private readonly _SqlMetaData _metadata; - - internal SqlDbColumn(_SqlMetaData md) - { - _metadata = md; - Populate(); - } - - private void Populate() - { - AllowDBNull = _metadata.IsNullable; - BaseCatalogName = _metadata.catalogName; - BaseColumnName = _metadata.baseColumn; - BaseSchemaName = _metadata.schemaName; - BaseServerName = _metadata.serverName; - BaseTableName = _metadata.tableName; - ColumnName = _metadata.column; - ColumnOrdinal = _metadata.ordinal; - ColumnSize = (_metadata.metaType.IsSizeInCharacters && (_metadata.length != 0x7fffffff)) ? (_metadata.length / 2) : _metadata.length; - IsAutoIncrement = _metadata.IsIdentity; - IsIdentity = _metadata.IsIdentity; - IsLong = _metadata.metaType.IsLong; - - if (SqlDbType.Timestamp == _metadata.type) - { - IsUnique = true; - } - else - { - IsUnique = false; - } - - if (TdsEnums.UNKNOWN_PRECISION_SCALE != _metadata.precision) - { - NumericPrecision = _metadata.precision; - } - else - { - NumericPrecision = _metadata.metaType.Precision; - } - - IsReadOnly = _metadata.IsReadOnly; - - UdtAssemblyQualifiedName = _metadata.udt?.AssemblyQualifiedName; - - } - - internal bool? SqlIsAliased - { - set - { - IsAliased = value; - } - } - - internal bool? SqlIsKey - { - set - { - IsKey = value; - } - } - - internal bool? SqlIsHidden - { - set - { - IsHidden = value; - } - } - - internal bool? SqlIsExpression - { - set - { - IsExpression = value; - } - } - - internal Type SqlDataType - { - set - { - DataType = value; - } - } - - internal string SqlDataTypeName - { - set - { - DataTypeName = value; - } - } - - internal int? SqlNumericScale - { - set - { - NumericScale = value; - } - } - - } -} 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 ef8113aa21..3412cb66b9 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -577,6 +577,9 @@ Microsoft\Data\SqlClient\SqlDataAdapter.cs + + Microsoft\Data\SqlClient\SqlDbColumn.cs + Microsoft\Data\SqlClient\SqlDependency.cs From 7fc93516bbdaff307c64b16a397a0394aa970c78 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Sun, 3 Nov 2024 07:25:13 +0100 Subject: [PATCH 4/7] Adding System.Data.Common to nuspec & netfx reference project --- .../netfx/ref/Microsoft.Data.SqlClient.csproj | 1 + tools/specs/Microsoft.Data.SqlClient.nuspec | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj index 02e66b7aee..742a37417f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.csproj @@ -27,5 +27,6 @@ + diff --git a/tools/specs/Microsoft.Data.SqlClient.nuspec b/tools/specs/Microsoft.Data.SqlClient.nuspec index 08e612a660..1f9374390e 100644 --- a/tools/specs/Microsoft.Data.SqlClient.nuspec +++ b/tools/specs/Microsoft.Data.SqlClient.nuspec @@ -37,6 +37,7 @@ When using NuGet 3.x this package requires at least version 3.4. + From e77498ef2ce3cf7658aa5528c02d67b2820ec1e4 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Fri, 22 Nov 2024 07:24:54 +0100 Subject: [PATCH 5/7] Fix csproj references --- .../netcore/src/Microsoft.Data.SqlClient.csproj | 3 --- 1 file changed, 3 deletions(-) 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 1e0f88a35f..82ad9f3831 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -657,9 +657,6 @@ - - - From 27a117e11b23f9416f221187806f7c4a1b72ab39 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Fri, 22 Nov 2024 08:49:58 +0100 Subject: [PATCH 6/7] Remove using per PR comment --- .../tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs index f939b42cc7..0fd8f22daf 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/UdtTest/SqlServerTypesTest.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; -using System.Data.Common; using System.Data.SqlTypes; using System.Globalization; using System.IO; @@ -262,9 +261,9 @@ public static void TestUdtSchemaMetadata() command.CommandText = "select hierarchyid::Parse('/1/1/3/') as col0, geometry::Parse('LINESTRING (100 100, 20 180, 180 180)') as col1, geography::Parse('LINESTRING(-122.360 47.656, -122.343 47.656)') as col2"; using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SchemaOnly)) { - ReadOnlyCollection columns = reader.GetColumnSchema(); + ReadOnlyCollection columns = reader.GetColumnSchema(); - DbColumn column = null; + System.Data.Common.DbColumn column = null; // Validate Microsoft.SqlServer.Types.SqlHierarchyId, Microsoft.SqlServer.Types, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91 column = columns[0]; From 7e73ae75cb3c5c0bb9a892065f249c4f8d85b148 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Fri, 22 Nov 2024 20:50:39 +0100 Subject: [PATCH 7/7] Add IDbColumnSchemaGenerator API Surface to reference projects --- .../netcore/ref/Microsoft.Data.SqlClient.cs | 5 +++-- .../netfx/ref/Microsoft.Data.SqlClient.cs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs index 20997a5b31..66a59fdb14 100644 --- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs @@ -1248,7 +1248,7 @@ protected override void OnRowUpdating(System.Data.Common.RowUpdatingEventArgs va object System.ICloneable.Clone() { throw null; } } /// - public partial class SqlDataReader : System.Data.Common.DbDataReader, System.Data.IDataReader, System.IDisposable + public partial class SqlDataReader : System.Data.Common.DbDataReader, System.Data.IDataReader, System.Data.Common.IDbColumnSchemaGenerator, System.IDisposable { internal SqlDataReader() { } /// @@ -1285,7 +1285,8 @@ public override void Close() { } /// public override long GetChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) { throw null; } /// - public System.Collections.ObjectModel.ReadOnlyCollection GetColumnSchema() { throw null; }/// + public System.Collections.ObjectModel.ReadOnlyCollection GetColumnSchema() { throw null; } + /// public override string GetDataTypeName(int i) { throw null; } /// public override System.DateTime GetDateTime(int i) { throw null; } diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs index 9ad6c68967..848f10283a 100644 --- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs +++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs @@ -1255,7 +1255,7 @@ protected override void OnRowUpdating(System.Data.Common.RowUpdatingEventArgs va protected override void TerminateBatching() { } } /// - public partial class SqlDataReader : System.Data.Common.DbDataReader, System.Data.IDataReader, System.IDisposable + public partial class SqlDataReader : System.Data.Common.DbDataReader, System.Data.IDataReader, System.Data.Common.IDbColumnSchemaGenerator, System.IDisposable { internal SqlDataReader() { } /// @@ -1291,6 +1291,8 @@ public override void Close() { } public override char GetChar(int i) { throw null; } /// public override long GetChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) { throw null; } + /// + public System.Collections.ObjectModel.ReadOnlyCollection GetColumnSchema() { throw null; } /// public override string GetDataTypeName(int i) { throw null; } ///