From 80ccc7b87026ff554f690cab7e69892dbc4be327 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 18:27:05 -0500 Subject: [PATCH 01/10] SqlMetaDataUdt => TdsUdtTypeInfo --- .../src/Microsoft/Data/SqlClient/Parser/TdsParser.cs | 2 +- .../src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs | 4 ++-- .../Parser/Tokens/{SqlMetaDataUdt.cs => TdsUdtTypeInfo.cs} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{SqlMetaDataUdt.cs => TdsUdtTypeInfo.cs} (90%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 4707cb9086..758681c20f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -5793,7 +5793,7 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser } if (metaData.Udt is null) { - metaData.Udt = new SqlMetaDataUdt(); + metaData.Udt = new TdsUdtTypeInfo(); } if (byteLength != 0) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index 1447d74298..d5cde01a2a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -130,7 +130,7 @@ public bool IsNullable /// /// Gets or sets the UDT metadata associated with this object. /// - public SqlMetaDataUdt Udt { get; set; } + public TdsUdtTypeInfo Udt { get; set; } /// /// Gets or sets the metadata for the XML schema collection associated with this data type. @@ -153,7 +153,7 @@ internal virtual void CopyFrom(TdsTypeInfo original) if (original.Udt != null) { - Udt = new SqlMetaDataUdt(); + Udt = new TdsUdtTypeInfo(); Udt.CopyFrom(original.Udt); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataUdt.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs similarity index 90% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataUdt.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs index 06367dbda4..089c8dceb2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataUdt.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs @@ -7,7 +7,7 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal sealed class SqlMetaDataUdt +internal sealed class TdsUdtTypeInfo { #if NET [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] @@ -22,7 +22,7 @@ internal sealed class SqlMetaDataUdt internal string AssemblyQualifiedName; - public void CopyFrom(SqlMetaDataUdt original) + public void CopyFrom(TdsUdtTypeInfo original) { if (original != null) { From 351b26436dbe66e1dc9c77faf17fe0806a433fbc Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 18:36:00 -0500 Subject: [PATCH 02/10] Fields => properties where possible, add comments --- .../SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs index 089c8dceb2..6330b928bb 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs @@ -7,21 +7,50 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; +/// +/// Represents UDT metadata inside a TYPE_INFO TDS object used by metadata and return value tokens. +/// internal sealed class TdsUdtTypeInfo { - #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] - #endif - internal Type Type; + /// + /// Represents the fully qualified type name of the UDT, including the name of the assembly + /// in which the type is defined. + /// + internal string AssemblyQualifiedName; + /// + /// Represents the name of the database associated with the UDT. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal string DatabaseName; + /// + /// Represents the schema name of the UDT within the TYPE_INFO TDS object. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal string SchemaName; + /// + /// Represents the name of the UDT as specified in the TYPE_INFO TDS object. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal string TypeName; - internal string AssemblyQualifiedName; + /// + /// Represents the data type the UDT, providing metadata necessary for runtime type resolution + /// and compatibility checks. + /// + #if NET + [field: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + #endif + internal Type Type { get; set; } + /// + /// Copies the information from the provided instance to the current instance. + /// + /// + /// The instance from which to copy the information. + /// public void CopyFrom(TdsUdtTypeInfo original) { if (original != null) From 7c5e18c01ffa5b5c415ea01a2584ba1e6b77ba47 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 18:39:43 -0500 Subject: [PATCH 03/10] SqlMetaDataXmlSchemaCollection.cs => TdsXmlTypeInfo --- .../Data/SqlClient/Parser/TdsParser.cs | 32 +++++++++---------- .../SqlClient/Parser/Tokens/TdsTypeInfo.cs | 16 +++++----- ...lSchemaCollection.cs => TdsXmlTypeInfo.cs} | 4 +-- .../Microsoft/Data/SqlClient/SqlConnection.cs | 16 +++++----- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 28 ++++++++-------- .../Microsoft/Data/SqlClient/SqlDbColumn.cs | 2 +- .../Microsoft/Data/SqlClient/SqlParameter.cs | 26 +++++++-------- 7 files changed, 62 insertions(+), 62 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{SqlMetaDataXmlSchemaCollection.cs => TdsXmlTypeInfo.cs} (81%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 758681c20f..fd8fa365e4 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -4793,13 +4793,13 @@ internal TdsOperationStatus TryProcessReturnValue(int length, { return result; } - if (rec.XmlSchemaCollection is null) + if (rec.XmlTypeInfo is null) { - rec.XmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + rec.XmlTypeInfo = new TdsXmlTypeInfo(); } if (len != 0) { - result = stateObj.TryReadString(len, out rec.XmlSchemaCollection.Database); + result = stateObj.TryReadString(len, out rec.XmlTypeInfo.Database); if (result != TdsOperationStatus.Done) { return result; @@ -4813,7 +4813,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } if (len != 0) { - result = stateObj.TryReadString(len, out rec.XmlSchemaCollection.OwningSchema); + result = stateObj.TryReadString(len, out rec.XmlTypeInfo.OwningSchema); if (result != TdsOperationStatus.Done) { return result; @@ -4829,7 +4829,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, if (slen != 0) { - result = stateObj.TryReadString(slen, out rec.XmlSchemaCollection.Name); + result = stateObj.TryReadString(slen, out rec.XmlTypeInfo.Name); if (result != TdsOperationStatus.Done) { return result; @@ -5551,13 +5551,13 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds { return result; } - if (col.XmlSchemaCollection is null) + if (col.XmlTypeInfo is null) { - col.XmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + col.XmlTypeInfo = new TdsXmlTypeInfo(); } if (byteLen != 0) { - result = stateObj.TryReadString(byteLen, out col.XmlSchemaCollection.Database); + result = stateObj.TryReadString(byteLen, out col.XmlTypeInfo.Database); if (result != TdsOperationStatus.Done) { return result; @@ -5571,7 +5571,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds } if (byteLen != 0) { - result = stateObj.TryReadString(byteLen, out col.XmlSchemaCollection.OwningSchema); + result = stateObj.TryReadString(byteLen, out col.XmlTypeInfo.OwningSchema); if (result != TdsOperationStatus.Done) { return result; @@ -5586,7 +5586,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds } if (byteLen != 0) { - result = stateObj.TryReadString(shortLen, out col.XmlSchemaCollection.Name); + result = stateObj.TryReadString(shortLen, out col.XmlTypeInfo.Name); if (result != TdsOperationStatus.Done) { return result; @@ -5791,13 +5791,13 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser { return result; } - if (metaData.Udt is null) + if (metaData.UdtTypeInfo is null) { - metaData.Udt = new TdsUdtTypeInfo(); + metaData.UdtTypeInfo = new TdsUdtTypeInfo(); } if (byteLength != 0) { - result = stateObj.TryReadString(byteLength, out metaData.Udt.DatabaseName); + result = stateObj.TryReadString(byteLength, out metaData.UdtTypeInfo.DatabaseName); if (result != TdsOperationStatus.Done) { return result; @@ -5812,7 +5812,7 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser } if (byteLength != 0) { - result = stateObj.TryReadString(byteLength, out metaData.Udt.SchemaName); + result = stateObj.TryReadString(byteLength, out metaData.UdtTypeInfo.SchemaName); if (result != TdsOperationStatus.Done) { return result; @@ -5827,7 +5827,7 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser } if (byteLength != 0) { - result = stateObj.TryReadString(byteLength, out metaData.Udt.TypeName); + result = stateObj.TryReadString(byteLength, out metaData.UdtTypeInfo.TypeName); if (result != TdsOperationStatus.Done) { return result; @@ -5841,7 +5841,7 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser } if (shortLength != 0) { - result = stateObj.TryReadString(shortLength, out metaData.Udt.AssemblyQualifiedName); + result = stateObj.TryReadString(shortLength, out metaData.UdtTypeInfo.AssemblyQualifiedName); if (result != TdsOperationStatus.Done) { return result; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index d5cde01a2a..c44650a5fc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -130,12 +130,12 @@ public bool IsNullable /// /// Gets or sets the UDT metadata associated with this object. /// - public TdsUdtTypeInfo Udt { get; set; } + public TdsUdtTypeInfo UdtTypeInfo { get; set; } /// /// Gets or sets the metadata for the XML schema collection associated with this data type. /// - public SqlMetaDataXmlSchemaCollection XmlSchemaCollection { get; set; } + public TdsXmlTypeInfo XmlTypeInfo { get; set; } // @TODO: Can this be converted to Clone like all the other token types do? internal virtual void CopyFrom(TdsTypeInfo original) @@ -151,16 +151,16 @@ internal virtual void CopyFrom(TdsTypeInfo original) this.MetaType = original.MetaType; this.flags = original.flags; - if (original.Udt != null) + if (original.UdtTypeInfo != null) { - Udt = new TdsUdtTypeInfo(); - Udt.CopyFrom(original.Udt); + UdtTypeInfo = new TdsUdtTypeInfo(); + UdtTypeInfo.CopyFrom(original.UdtTypeInfo); } - if (original.XmlSchemaCollection != null) + if (original.XmlTypeInfo != null) { - XmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); - XmlSchemaCollection.CopyFrom(original.XmlSchemaCollection); + XmlTypeInfo = new TdsXmlTypeInfo(); + XmlTypeInfo.CopyFrom(original.XmlTypeInfo); } this.IsEncrypted = original.IsEncrypted; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataXmlSchemaCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs similarity index 81% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataXmlSchemaCollection.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs index e5d609917c..5ab82f7645 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataXmlSchemaCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs @@ -4,13 +4,13 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal sealed class SqlMetaDataXmlSchemaCollection +internal sealed class TdsXmlTypeInfo { internal string Database; internal string OwningSchema; internal string Name; - public void CopyFrom(SqlMetaDataXmlSchemaCollection original) + public void CopyFrom(TdsXmlTypeInfo original) { if (original != null) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs index 038b04d7d1..8282f039c2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -3094,16 +3094,16 @@ private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) internal void CheckGetExtendedUDTInfo(TdsTypeInfo metaData, bool fThrow) { - if (metaData.Udt?.Type == null) + if (metaData.UdtTypeInfo?.Type == null) { // If null, we have not obtained extended info. - Debug.Assert(!string.IsNullOrEmpty(metaData.Udt?.AssemblyQualifiedName), "Unexpected state on GetUDTInfo"); + Debug.Assert(!string.IsNullOrEmpty(metaData.UdtTypeInfo?.AssemblyQualifiedName), "Unexpected state on GetUDTInfo"); // Parameter throwOnError determines whether exception from Assembly.Load is thrown. - metaData.Udt.Type = - Type.GetType(typeName: metaData.Udt.AssemblyQualifiedName, assemblyResolver: asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver: null, throwOnError: fThrow); + metaData.UdtTypeInfo.Type = + Type.GetType(typeName: metaData.UdtTypeInfo.AssemblyQualifiedName, assemblyResolver: asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver: null, throwOnError: fThrow); - if (fThrow && metaData.Udt.Type == null) + if (fThrow && metaData.UdtTypeInfo.Type == null) { - throw SQL.UDTUnexpectedResult(metaData.Udt.AssemblyQualifiedName); + throw SQL.UDTUnexpectedResult(metaData.UdtTypeInfo.AssemblyQualifiedName); } } } @@ -3120,7 +3120,7 @@ internal object GetUdtValue(object value, TdsTypeInfo metaData, bool returnDBNul // Since the serializer doesn't handle nulls... if (ADP.IsNull(value)) { - Type t = metaData.Udt?.Type; + Type t = metaData.UdtTypeInfo?.Type; Debug.Assert(t != null, "Unexpected null of udtType on GetUdtValue!"); o = t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, Array.Empty(), CultureInfo.InvariantCulture); Debug.Assert(o != null); @@ -3131,7 +3131,7 @@ internal object GetUdtValue(object value, TdsTypeInfo metaData, bool returnDBNul MemoryStream stm = new MemoryStream((byte[])value); - o = Server.SerializationHelperSql9.Deserialize(stm, metaData.Udt?.Type); + o = Server.SerializationHelperSql9.Deserialize(stm, metaData.UdtTypeInfo?.Type); Debug.Assert(o != null, "object could NOT be created"); return o; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 9f66021958..e885e9a026 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -303,17 +303,17 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() if (SqlDbType.Xml == colMetaData.DbType) { - typeSpecificNamePart1 = colMetaData.XmlSchemaCollection?.Database; - typeSpecificNamePart2 = colMetaData.XmlSchemaCollection?.OwningSchema; - typeSpecificNamePart3 = colMetaData.XmlSchemaCollection?.Name; + typeSpecificNamePart1 = colMetaData.XmlTypeInfo?.Database; + typeSpecificNamePart2 = colMetaData.XmlTypeInfo?.OwningSchema; + typeSpecificNamePart3 = colMetaData.XmlTypeInfo?.Name; } else if (SqlDbType.Udt == colMetaData.DbType) { Connection.CheckGetExtendedUDTInfo(colMetaData, true); // Ensure that colMetaData.udtType is set - typeSpecificNamePart1 = colMetaData.Udt?.DatabaseName; - typeSpecificNamePart2 = colMetaData.Udt?.SchemaName; - typeSpecificNamePart3 = colMetaData.Udt?.TypeName; + typeSpecificNamePart1 = colMetaData.UdtTypeInfo?.DatabaseName; + typeSpecificNamePart2 = colMetaData.UdtTypeInfo?.SchemaName; + typeSpecificNamePart3 = colMetaData.UdtTypeInfo?.TypeName; } int length = colMetaData.length; @@ -335,7 +335,7 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() colMetaData.scale, collation != null ? collation.LCID : _defaultLCID, collation != null ? collation.SqlCompareOptions : SqlCompareOptions.None, - colMetaData.Udt?.Type, + colMetaData.UdtTypeInfo?.Type, isMultiValued: false, fieldMetaData: null, extendedProperties: null, @@ -610,13 +610,13 @@ internal DataTable BuildSchemaTable() if (col.DbType == SqlDbType.Udt) { // Additional metadata for UDTs. Debug.Assert(Connection.Parser.Capabilities.UserDefinedTypes, "Invalid Column type received from the server"); - schemaRow[udtAssemblyQualifiedName] = col.Udt?.AssemblyQualifiedName; + schemaRow[udtAssemblyQualifiedName] = col.UdtTypeInfo?.AssemblyQualifiedName; } else if (col.DbType == SqlDbType.Xml) { // Additional metadata for Xml. - schemaRow[xmlSchemaCollectionDatabase] = col.XmlSchemaCollection?.Database; - schemaRow[xmlSchemaCollectionOwningSchema] = col.XmlSchemaCollection?.OwningSchema; - schemaRow[xmlSchemaCollectionName] = col.XmlSchemaCollection?.Name; + schemaRow[xmlSchemaCollectionDatabase] = col.XmlTypeInfo?.Database; + schemaRow[xmlSchemaCollectionOwningSchema] = col.XmlTypeInfo?.OwningSchema; + schemaRow[xmlSchemaCollectionName] = col.XmlTypeInfo?.Name; } } @@ -1205,7 +1205,7 @@ private string GetDataTypeNameInternal(TdsColumnMetadata metaData) if (metaData.DbType == SqlDbType.Udt) { - dataTypeName = metaData.Udt?.DatabaseName + "." + metaData.Udt?.SchemaName + "." + metaData.Udt?.TypeName; + dataTypeName = metaData.UdtTypeInfo?.DatabaseName + "." + metaData.UdtTypeInfo?.SchemaName + "." + metaData.UdtTypeInfo?.TypeName; } else { // For all other types, including Xml - use data in MetaType. @@ -1282,7 +1282,7 @@ private Type GetFieldTypeInternal(TdsColumnMetadata metaData) if (metaData.DbType == SqlDbType.Udt) { Connection.CheckGetExtendedUDTInfo(metaData, false); - fieldType = metaData.Udt?.Type; + fieldType = metaData.UdtTypeInfo?.Type; } else if (metaData.DbType == SqlDbTypeExtensions.Vector) { @@ -1401,7 +1401,7 @@ private Type GetProviderSpecificFieldTypeInternal(TdsColumnMetadata metaData) if (metaData.DbType == SqlDbType.Udt) { Connection.CheckGetExtendedUDTInfo(metaData, false); - providerSpecificFieldType = metaData.Udt?.Type; + providerSpecificFieldType = metaData.UdtTypeInfo?.Type; } else if (metaData.DbType == SqlDbTypeExtensions.Vector) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs index cb2ee9a269..891204b139 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs @@ -55,7 +55,7 @@ private void Populate() IsReadOnly = _metadata.IsReadOnly; - UdtAssemblyQualifiedName = _metadata.Udt?.AssemblyQualifiedName; + UdtAssemblyQualifiedName = _metadata.UdtTypeInfo?.AssemblyQualifiedName; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs index e8ade71ea6..3e1181405c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -226,7 +226,7 @@ private enum SqlParameterFlags : ushort private MetaType _metaType; private SqlCollation _collation; - private SqlMetaDataXmlSchemaCollection _xmlSchemaCollection; + private TdsXmlTypeInfo _xmlTypeInfo; private string _udtTypeName; private string _typeName; private Exception _udtLoadError; @@ -353,9 +353,9 @@ string xmlSchemaCollectionName if (!string.IsNullOrEmpty(xmlSchemaCollectionDatabase) || !string.IsNullOrEmpty(xmlSchemaCollectionOwningSchema) || !string.IsNullOrEmpty(xmlSchemaCollectionName)) { EnsureXmlSchemaCollection(); - _xmlSchemaCollection.Database = xmlSchemaCollectionDatabase; - _xmlSchemaCollection.OwningSchema = xmlSchemaCollectionOwningSchema; - _xmlSchemaCollection.Name = xmlSchemaCollectionName; + _xmlTypeInfo.Database = xmlSchemaCollectionDatabase; + _xmlTypeInfo.OwningSchema = xmlSchemaCollectionOwningSchema; + _xmlTypeInfo.Name = xmlSchemaCollectionName; } } @@ -431,7 +431,7 @@ public SqlCompareOptions CompareInfo [ResCategory(nameof(Strings.DataCategory_Xml))] public string XmlSchemaCollectionDatabase { - get => _xmlSchemaCollection?.Database ?? string.Empty; + get => _xmlTypeInfo?.Database ?? string.Empty; set => EnsureXmlSchemaCollection().Database = value; } @@ -439,7 +439,7 @@ public string XmlSchemaCollectionDatabase [ResCategory(nameof(Strings.DataCategory_Xml))] public string XmlSchemaCollectionOwningSchema { - get => _xmlSchemaCollection?.OwningSchema ?? string.Empty; + get => _xmlTypeInfo?.OwningSchema ?? string.Empty; set => EnsureXmlSchemaCollection().OwningSchema = value; } @@ -447,7 +447,7 @@ public string XmlSchemaCollectionOwningSchema [ResCategory(nameof(Strings.DataCategory_Xml))] public string XmlSchemaCollectionName { - get => _xmlSchemaCollection?.Name ?? string.Empty; + get => _xmlTypeInfo?.Name ?? string.Empty; set => EnsureXmlSchemaCollection().Name = value; } @@ -1131,9 +1131,9 @@ private void CloneHelper(SqlParameter destination) ); destination._metaType = _metaType; destination._collation = _collation; - if (_xmlSchemaCollection != null) + if (_xmlTypeInfo != null) { - destination.EnsureXmlSchemaCollection().CopyFrom(_xmlSchemaCollection); + destination.EnsureXmlSchemaCollection().CopyFrom(_xmlTypeInfo); } destination._udtTypeName = _udtTypeName; destination._typeName = _typeName; @@ -1168,13 +1168,13 @@ internal object CompareExchangeParent(object value, object comparand) return parent; } - private SqlMetaDataXmlSchemaCollection EnsureXmlSchemaCollection() + private TdsXmlTypeInfo EnsureXmlSchemaCollection() { - if (_xmlSchemaCollection is null) + if (_xmlTypeInfo is null) { - _xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + _xmlTypeInfo = new TdsXmlTypeInfo(); } - return _xmlSchemaCollection; + return _xmlTypeInfo; } internal void FixStreamDataForNonPLP() From d138bbd2836d47589eeda6631665c83b65f4d338 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 18:41:55 -0500 Subject: [PATCH 04/10] Add comments --- .../SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs | 1 + .../SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs index 6330b928bb..4052cb89a7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs @@ -16,6 +16,7 @@ internal sealed class TdsUdtTypeInfo /// Represents the fully qualified type name of the UDT, including the name of the assembly /// in which the type is defined. /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal string AssemblyQualifiedName; /// diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs index 5ab82f7645..1cd40a6c36 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs @@ -6,10 +6,31 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; internal sealed class TdsXmlTypeInfo { + /// + /// Represents the name of the database associated with the XML schema collection. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal string Database; - internal string OwningSchema; + + /// + /// Represents the name of the XML schema collection associated with the type information. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal string Name; + /// + /// Represents the schema that owns the XML schema collection associated with the database. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. + internal string OwningSchema; + + /// + /// Copies the properties of the specified instance + /// to the current instance. + /// + /// + /// The instance from which to copy properties. + /// public void CopyFrom(TdsXmlTypeInfo original) { if (original != null) From 0599b4d5fa731491ccf2274382faca0c20ed1159 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 18:50:23 -0500 Subject: [PATCH 05/10] TdsXmlTypeInfo.CopyFrom => Clone --- .../SqlClient/Parser/Tokens/TdsTypeInfo.cs | 6 +-- .../SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs | 19 ++++----- .../Microsoft/Data/SqlClient/SqlParameter.cs | 6 +-- .../Parser/Tokens/TdsXmlTypeInfoTests.cs | 42 +++++++++++++++++++ 4 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index c44650a5fc..8774165a80 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -157,11 +157,7 @@ internal virtual void CopyFrom(TdsTypeInfo original) UdtTypeInfo.CopyFrom(original.UdtTypeInfo); } - if (original.XmlTypeInfo != null) - { - XmlTypeInfo = new TdsXmlTypeInfo(); - XmlTypeInfo.CopyFrom(original.XmlTypeInfo); - } + this.XmlTypeInfo = original.XmlTypeInfo?.Clone(); this.IsEncrypted = original.IsEncrypted; this.BaseTypeInfo = original.BaseTypeInfo; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs index 1cd40a6c36..888770d757 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfo.cs @@ -25,19 +25,16 @@ internal sealed class TdsXmlTypeInfo internal string OwningSchema; /// - /// Copies the properties of the specified instance - /// to the current instance. + /// Creates a new instance of and initializes it with the + /// properties of the current instance. /// - /// - /// The instance from which to copy properties. - /// - public void CopyFrom(TdsXmlTypeInfo original) + internal TdsXmlTypeInfo Clone() { - if (original != null) + return new TdsXmlTypeInfo { - Database = original.Database; - OwningSchema = original.OwningSchema; - Name = original.Name; - } + Database = Database, + Name = Name, + OwningSchema = OwningSchema + }; } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs index 3e1181405c..093465ba33 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs @@ -1131,10 +1131,8 @@ private void CloneHelper(SqlParameter destination) ); destination._metaType = _metaType; destination._collation = _collation; - if (_xmlTypeInfo != null) - { - destination.EnsureXmlSchemaCollection().CopyFrom(_xmlTypeInfo); - } + + destination._xmlTypeInfo = _xmlTypeInfo?.Clone(); destination._udtTypeName = _udtTypeName; destination._typeName = _typeName; destination._udtLoadError = _udtLoadError; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs new file mode 100644 index 0000000000..88a8ce3587 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs @@ -0,0 +1,42 @@ +// 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 Xunit; + +namespace Microsoft.Data.SqlClient.Parser.Tokens; + +public class TdsXmlTypeInfoTests +{ + [Fact] + public void Constructor_InitializesPropertiesWithDefaultValues() + { + // Arrange & Act + var udtTypeInfo = new TdsXmlTypeInfo(); + + // Assert + Assert.Null(udtTypeInfo.Database); + Assert.Null(udtTypeInfo.Name); + Assert.Null(udtTypeInfo.OwningSchema); + } + + [Fact] + public void Clone_CopiesAllProperties() + { + // Arrange + var original = new TdsXmlTypeInfo + { + Database = "TestDatabase", + Name = "TestName", + OwningSchema = "TestSchema" + }; + + // Act + var cloned = original.Clone(); + + // Assert + Assert.Equal(original.Database, cloned.Database); + Assert.Equal(original.Name, cloned.Name); + Assert.Equal(original.OwningSchema, cloned.OwningSchema); + } +} From e12e88c471e740a479a9aba9012751bbfa719805 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 18:55:53 -0500 Subject: [PATCH 06/10] TdsUdtTypeInfo.CopyFrom => Clone --- .../SqlClient/Parser/Tokens/TdsTypeInfo.cs | 7 +-- .../SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs | 22 +++---- .../Parser/Tokens/TdsUdtTypeInfoTests.cs | 63 +++++++++++++++++++ 3 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index 8774165a80..8a29fca38f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -151,12 +151,7 @@ internal virtual void CopyFrom(TdsTypeInfo original) this.MetaType = original.MetaType; this.flags = original.flags; - if (original.UdtTypeInfo != null) - { - UdtTypeInfo = new TdsUdtTypeInfo(); - UdtTypeInfo.CopyFrom(original.UdtTypeInfo); - } - + this.UdtTypeInfo = original.UdtTypeInfo?.Clone(); this.XmlTypeInfo = original.XmlTypeInfo?.Clone(); this.IsEncrypted = original.IsEncrypted; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs index 4052cb89a7..ca7d9df384 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs @@ -47,20 +47,18 @@ internal sealed class TdsUdtTypeInfo internal Type Type { get; set; } /// - /// Copies the information from the provided instance to the current instance. + /// Creates a new instance of and initializes it with the values + /// of the current instance. /// - /// - /// The instance from which to copy the information. - /// - public void CopyFrom(TdsUdtTypeInfo original) + internal TdsUdtTypeInfo Clone() { - if (original != null) + return new TdsUdtTypeInfo { - Type = original.Type; - DatabaseName = original.DatabaseName; - SchemaName = original.SchemaName; - TypeName = original.TypeName; - AssemblyQualifiedName = original.AssemblyQualifiedName; - } + AssemblyQualifiedName = AssemblyQualifiedName, + DatabaseName = DatabaseName, + SchemaName = SchemaName, + Type = Type, + TypeName = TypeName + }; } } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs new file mode 100644 index 0000000000..5ff99a8dc6 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs @@ -0,0 +1,63 @@ +// 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 Xunit; + +namespace Microsoft.Data.SqlClient.Parser.Tokens; + +public class TdsUdtTypeInfoTests +{ + [Fact] + public void Constructor_InitializesPropertiesWithDefaultValues() + { + // Arrange & Act + var udtTypeInfo = new TdsUdtTypeInfo(); + + // Assert + Assert.Null(udtTypeInfo.AssemblyQualifiedName); + Assert.Null(udtTypeInfo.DatabaseName); + Assert.Null(udtTypeInfo.SchemaName); + Assert.Null(udtTypeInfo.TypeName); + Assert.Null(udtTypeInfo.Type); + } + + [Fact] + public void Clone_CopiesAllProperties() + { + // Arrange + var original = new TdsUdtTypeInfo + { + AssemblyQualifiedName = "MyAssembly.MyType, MyAssembly, Version=1.0.0.0", + DatabaseName = "TestDatabase", + SchemaName = "dbo", + TypeName = "MyUdtType", + Type = typeof(string) + }; + + // Act + var clone = original.Clone(); + + // Assert + Assert.Equal(original.AssemblyQualifiedName, clone.AssemblyQualifiedName); + Assert.Equal(original.DatabaseName, clone.DatabaseName); + Assert.Equal(original.SchemaName, clone.SchemaName); + Assert.Equal(original.TypeName, clone.TypeName); + Assert.Equal(original.Type, clone.Type); + } + + [Fact] + public void Type_CanBeSetAndRetrieved() + { + // Arrange + var udtTypeInfo = new TdsUdtTypeInfo(); + var testType = typeof(DateTime); + + // Act + udtTypeInfo.Type = testType; + + // Assert + Assert.Equal(testType, udtTypeInfo.Type); + } +} From d0eb6b38ca2f2a88e847bbcae9eba9794e661012 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 19:29:14 -0500 Subject: [PATCH 07/10] Clone methods (and ctors) in TdsColumnMetadata and TdsTypeInfo --- .../Parser/Tokens/TdsColumnMetadata.cs | 35 ++++---- .../Parser/Tokens/TdsColumnMetadataToken.cs | 2 +- .../SqlClient/Parser/Tokens/TdsTypeInfo.cs | 53 +++++++----- .../Parser/Tokens/TdsTypeInfoTests.cs | 80 +++++++++++++++++++ 4 files changed, 132 insertions(+), 38 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index 267c96a1e2..5b8b4b6eb4 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -11,6 +11,7 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; /// Represents metadata information for a specific column in a TDS stream. Corresponds with a /// single column within a SQLCOLMETADATA/SQLALTMETADATA token. /// +// @TODO: We have a mix of patterns here where some subtypes are contained within the types, while some are inherited. It seems this class would be better suited to contain the TdsTypeInfo rather than inherit from it. internal sealed class TdsColumnMetadata : TdsTypeInfo { [Flags] @@ -70,6 +71,19 @@ internal TdsColumnMetadata(int ordinal) : base() Ordinal = ordinal; } + private TdsColumnMetadata(TdsColumnMetadata original) : base(original) + { + _flags = original._flags; + + baseColumn = original.baseColumn; + column = original.column; + tableName = original.tableName; + tableNum = original.tableNum; + op = original.op; + Operand = original.Operand; + Ordinal = original.Ordinal; + } + /// /// Gets the catalog name associated with the column's metadata. /// @@ -77,9 +91,9 @@ internal TdsColumnMetadata(int ordinal) : base() internal bool Is2008DateTimeType => DbType is SqlDbType.Date - or SqlDbType.Time - or SqlDbType.DateTime2 - or SqlDbType.DateTimeOffset; + or SqlDbType.Time + or SqlDbType.DateTime2 + or SqlDbType.DateTimeOffset; /// /// Indicates whether the column is part of a sparse column set. @@ -187,19 +201,8 @@ public byte Updatability /// /// A new TdsColumnMetadata object that is a copy of the current instance. /// - public object Clone() - { - TdsColumnMetadata result = new TdsColumnMetadata(Ordinal); - result.CopyFrom(this); - result.column = column; - result.baseColumn = baseColumn; - result.tableName = tableName; - result.tableNum = tableNum; - result._flags = _flags; - result.op = op; - result.Operand = Operand; - return result; - } + internal new TdsColumnMetadata Clone() => + new TdsColumnMetadata(this); private bool HasFlag(MetadataFlags flag) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs index e4e2a85474..9a95a6d24e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs @@ -59,7 +59,7 @@ private TdsColumnMetadataToken(TdsColumnMetadataToken original) _metadataArray = new TdsColumnMetadata[original._metadataArray.Length]; for (int idx = 0; idx < _metadataArray.Length; idx++) { - _metadataArray[idx] = (TdsColumnMetadata)original._metadataArray[idx].Clone(); + _metadataArray[idx] = original._metadataArray[idx].Clone(); } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index 8a29fca38f..e2fbcd9c09 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -58,6 +58,30 @@ internal TdsTypeInfo() { } + protected TdsTypeInfo(TdsTypeInfo original) + { + // @TODO: Some of these fields are not simple types, should they also be cloned? + + collation = original.collation; + flags = original.flags; + length = original.length; + precision = original.precision; + scale = original.scale; + + CodePage = original.CodePage; + DbType = original.DbType; + Encoding = original.Encoding; + MetaType = original.MetaType; // This is a cached instance, it should not be cloned. + TdsType = original.TdsType; + + UdtTypeInfo = original.UdtTypeInfo?.Clone(); + XmlTypeInfo = original.XmlTypeInfo?.Clone(); + + BaseTypeInfo = original.BaseTypeInfo; + CipherMetadata = original.CipherMetadata; + IsEncrypted = original.IsEncrypted; + } + /// /// Gets or sets the plaintext type information for the column if the column is encrypted. /// @@ -137,27 +161,14 @@ public bool IsNullable /// public TdsXmlTypeInfo XmlTypeInfo { get; set; } - // @TODO: Can this be converted to Clone like all the other token types do? - internal virtual void CopyFrom(TdsTypeInfo original) - { - this.DbType = original.DbType; - this.TdsType = original.TdsType; - this.precision = original.precision; - this.scale = original.scale; - this.length = original.length; - this.collation = original.collation; - this.CodePage = original.CodePage; - this.Encoding = original.Encoding; - this.MetaType = original.MetaType; - this.flags = original.flags; - - this.UdtTypeInfo = original.UdtTypeInfo?.Clone(); - this.XmlTypeInfo = original.XmlTypeInfo?.Clone(); - - this.IsEncrypted = original.IsEncrypted; - this.BaseTypeInfo = original.BaseTypeInfo; - this.CipherMetadata = original.CipherMetadata; - } + /// + /// Creates a new instance of the TdsTypeInfo class that is a copy of the current instance. + /// + /// + /// A new TdsTypeInfo instance populated with the same values as the original. + /// + internal TdsTypeInfo Clone() => + new TdsTypeInfo(this); private bool HasFlag(TdsTypeInfoFlags flag) { diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs new file mode 100644 index 0000000000..24956a31e5 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs @@ -0,0 +1,80 @@ +// 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.Text; +using Xunit; + +namespace Microsoft.Data.SqlClient.Parser.Tokens; + +public class TdsTypeInfoTests +{ + [Fact] + public void Clone_CopiesAllUnclonedProperties() + { + // Arrange + var original = new TdsTypeInfo + { + CodePage = 1251, + DbType = SqlDbType.Decimal, + Encoding = Encoding.Unicode, + TdsType = 0x6A, + IsEncrypted = true + }; + original.length = 17; + original.precision = 28; + original.scale = 8; + original.collation = new SqlCollation(0x00, 0x00); + + // Act + var clone = original.Clone(); + + // Assert + Assert.Equal(original.CodePage, clone.CodePage); + Assert.Equal(original.DbType, clone.DbType); + Assert.Equal(original.Encoding, clone.Encoding); + Assert.Equal(original.TdsType, clone.TdsType); + Assert.Equal(original.IsEncrypted, clone.IsEncrypted); + Assert.Equal(original.length, clone.length); + Assert.Equal(original.precision, clone.precision); + Assert.Equal(original.scale, clone.scale); + Assert.Equal(original.collation, clone.collation); + } + + [Fact] + public void Clone_WithUdtTypeInfo_CreatesDeepCopy() + { + // Arrange + var original = new TdsTypeInfo + { + TdsType = 0xF0, + UdtTypeInfo = new TdsUdtTypeInfo() + }; + + // Act + var clone = original.Clone(); + + // Assert + Assert.NotNull(clone.UdtTypeInfo); + Assert.NotSame(original.UdtTypeInfo, clone.UdtTypeInfo); + } + + [Fact] + public void Clone_WithXmlTypeInfo_CreatesDeepCopy() + { + // Arrange + var original = new TdsTypeInfo + { + TdsType = 0xF1, + XmlTypeInfo = new TdsXmlTypeInfo() + }; + + // Act + var clone = original.Clone(); + + // Assert + Assert.NotNull(clone.XmlTypeInfo); + Assert.NotSame(original.XmlTypeInfo, clone.XmlTypeInfo); + } +} From b91d8844fd871aa76f80f79a0f18e2d55c946ddb Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Fri, 11 Sep 2026 13:25:45 -0500 Subject: [PATCH 08/10] :robot: comments Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs index ca7d9df384..d96d7109f7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfo.cs @@ -38,11 +38,11 @@ internal sealed class TdsUdtTypeInfo internal string TypeName; /// - /// Represents the data type the UDT, providing metadata necessary for runtime type resolution + /// Represents the data type of the UDT, providing metadata necessary for runtime type resolution /// and compatibility checks. /// #if NET - [field: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] #endif internal Type Type { get; set; } From 0e92e7ac4bdc929c04c79852d3f78ead95f10292 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 5 Oct 2026 18:00:21 -0400 Subject: [PATCH 09/10] Adding unit tests for TdsColumnMetadata and changing Clone to be virtual instead of hidden. --- .../Parser/Tokens/TdsColumnMetadata.cs | 2 +- .../SqlClient/Parser/Tokens/TdsTypeInfo.cs | 2 +- .../Parser/Tokens/TdsColumnMetadataTests.cs | 303 ++++++++++++++++++ .../Parser/Tokens/TdsUdtTypeInfoTests.cs | 1 + .../Parser/Tokens/TdsXmlTypeInfoTests.cs | 9 +- 5 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index 5b8b4b6eb4..053bdd7f8e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -201,7 +201,7 @@ public byte Updatability /// /// A new TdsColumnMetadata object that is a copy of the current instance. /// - internal new TdsColumnMetadata Clone() => + internal override TdsColumnMetadata Clone() => new TdsColumnMetadata(this); private bool HasFlag(MetadataFlags flag) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index e2fbcd9c09..ec8425ac54 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -167,7 +167,7 @@ public bool IsNullable /// /// A new TdsTypeInfo instance populated with the same values as the original. /// - internal TdsTypeInfo Clone() => + internal virtual TdsTypeInfo Clone() => new TdsTypeInfo(this); private bool HasFlag(TdsTypeInfoFlags flag) diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs new file mode 100644 index 0000000000..f3bf8d2fb9 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs @@ -0,0 +1,303 @@ +// 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.Text; +using Xunit; + +namespace Microsoft.Data.SqlClient.Parser.Tokens; + +/// +/// Verifies column metadata defaults, flag isolation, derived properties, and cloning. +/// +public class TdsColumnMetadataTests +{ + /// + /// Ensures a new column preserves its ordinal and starts without optional metadata or flags. + /// + [Theory] + [InlineData(0)] + [InlineData(7)] + public void Constructor_InitializesPropertiesWithDefaultValues(int ordinal) + { + var metadata = new TdsColumnMetadata(ordinal); + + Assert.Equal(ordinal, metadata.Ordinal); + Assert.Null(metadata.baseColumn); + Assert.Null(metadata.column); + Assert.Equal(0, metadata.op); + Assert.Equal(0, metadata.tableNum); + Assert.Equal(0, metadata.Operand); + Assert.Null(metadata.ServerName); + Assert.Null(metadata.CatalogName); + Assert.Null(metadata.SchemaName); + Assert.Null(metadata.TableName); + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.Equal(0, metadata.Updatability); + Assert.True(metadata.IsReadOnly); + Assert.False(metadata.Is2008DateTimeType); + Assert.False(metadata.IsLargeUdt); + Assert.False(metadata.IsNullable); + Assert.False(metadata.IsMultiValued); + Assert.Null(metadata.UdtTypeInfo); + Assert.Null(metadata.XmlTypeInfo); + } + + /// + /// Ensures cloning preserves column-specific and inherited metadata, including packed flags. + /// + [Fact] + public void Clone_CopiesAllProperties() + { + var original = new TdsColumnMetadata(7) + { + baseColumn = "OriginalColumn", + column = "ColumnAlias", + op = 3, + tableNum = 2, + tableName = new TdsTableName(new[] { "TestServer", "TestCatalog", "dbo", "TestTable" }), + Operand = 42, + IsColumnSet = true, + IsDifferentName = true, + IsExpression = true, + IsHidden = true, + IsIdentity = true, + IsKey = true, + Updatability = 2, + CodePage = 1251, + DbType = SqlDbType.Decimal, + Encoding = Encoding.Unicode, + MetaType = MetaType.GetMetaTypeFromSqlDbType(SqlDbType.Decimal, false), + TdsType = 0x6A, + IsEncrypted = true, + IsNullable = true, + IsMultiValued = true, + length = 17, + precision = 28, + scale = 8, + collation = new SqlCollation(0x00, 0x00), + BaseTypeInfo = new TdsTypeInfo { DbType = SqlDbType.Int }, + CipherMetadata = new SqlCipherMetadata(default, 1, 2, "TestAlgorithm", 1, 1) + }; + + // Exercise the virtual override through the base type as well. + TdsTypeInfo typeInfo = original; + var clone = Assert.IsType(typeInfo.Clone()); + + Assert.NotSame(original, clone); + Assert.Equal(original.Ordinal, clone.Ordinal); + Assert.Equal(original.baseColumn, clone.baseColumn); + Assert.Equal(original.column, clone.column); + Assert.Equal(original.op, clone.op); + Assert.Equal(original.tableNum, clone.tableNum); + Assert.Equal(original.Operand, clone.Operand); + Assert.Equal(original.ServerName, clone.ServerName); + Assert.Equal(original.CatalogName, clone.CatalogName); + Assert.Equal(original.SchemaName, clone.SchemaName); + Assert.Equal(original.TableName, clone.TableName); + Assert.Equal(original.IsColumnSet, clone.IsColumnSet); + Assert.Equal(original.IsDifferentName, clone.IsDifferentName); + Assert.Equal(original.IsExpression, clone.IsExpression); + Assert.Equal(original.IsHidden, clone.IsHidden); + Assert.Equal(original.IsIdentity, clone.IsIdentity); + Assert.Equal(original.IsKey, clone.IsKey); + Assert.Equal(original.Updatability, clone.Updatability); + Assert.Equal(original.IsReadOnly, clone.IsReadOnly); + Assert.Equal(original.CodePage, clone.CodePage); + Assert.Equal(original.DbType, clone.DbType); + Assert.Same(original.Encoding, clone.Encoding); + Assert.Same(original.MetaType, clone.MetaType); + Assert.Equal(original.TdsType, clone.TdsType); + Assert.Equal(original.IsEncrypted, clone.IsEncrypted); + Assert.Equal(original.IsNullable, clone.IsNullable); + Assert.Equal(original.IsMultiValued, clone.IsMultiValued); + Assert.Equal(original.length, clone.length); + Assert.Equal(original.precision, clone.precision); + Assert.Equal(original.scale, clone.scale); + Assert.Same(original.collation, clone.collation); + Assert.Same(original.BaseTypeInfo, clone.BaseTypeInfo); + Assert.Same(original.CipherMetadata, clone.CipherMetadata); + Assert.Null(clone.UdtTypeInfo); + Assert.Null(clone.XmlTypeInfo); + + clone.column = "ChangedAlias"; + clone.tableName.TableName = "ChangedTable"; + clone.IsKey = false; + clone.IsNullable = false; + clone.Updatability = 0; + Assert.Equal("ColumnAlias", original.column); + Assert.Equal("TestTable", original.TableName); + Assert.True(original.IsKey); + Assert.True(original.IsNullable); + Assert.Equal(2, original.Updatability); + } + + /// + /// Ensures the derived clone owns independent copies of optional UDT and XML metadata. + /// + [Fact] + public void Clone_WithUdtAndXmlTypeInfo_CreatesDeepCopies() + { + var original = new TdsColumnMetadata(0) + { + UdtTypeInfo = new TdsUdtTypeInfo + { + AssemblyQualifiedName = "MyAssembly.MyType, MyAssembly, Version=1.0.0.0", + DatabaseName = "TestDatabase", + SchemaName = "dbo", + TypeName = "MyUdtType", + Type = typeof(string) + }, + XmlTypeInfo = new TdsXmlTypeInfo + { + Database = "TestDatabase", + OwningSchema = "dbo", + Name = "TestSchemaCollection" + } + }; + + var clone = original.Clone(); + + Assert.NotNull(clone.UdtTypeInfo); + Assert.NotSame(original.UdtTypeInfo, clone.UdtTypeInfo); + Assert.Equal(original.UdtTypeInfo.AssemblyQualifiedName, + clone.UdtTypeInfo.AssemblyQualifiedName); + Assert.Equal(original.UdtTypeInfo.DatabaseName, clone.UdtTypeInfo.DatabaseName); + Assert.Equal(original.UdtTypeInfo.SchemaName, clone.UdtTypeInfo.SchemaName); + Assert.Equal(original.UdtTypeInfo.TypeName, clone.UdtTypeInfo.TypeName); + Assert.Equal(original.UdtTypeInfo.Type, clone.UdtTypeInfo.Type); + Assert.NotNull(clone.XmlTypeInfo); + Assert.NotSame(original.XmlTypeInfo, clone.XmlTypeInfo); + Assert.Equal(original.XmlTypeInfo.Database, clone.XmlTypeInfo.Database); + Assert.Equal(original.XmlTypeInfo.OwningSchema, clone.XmlTypeInfo.OwningSchema); + Assert.Equal(original.XmlTypeInfo.Name, clone.XmlTypeInfo.Name); + + clone.UdtTypeInfo.TypeName = "ChangedType"; + clone.XmlTypeInfo.Name = "ChangedSchemaCollection"; + Assert.Equal("MyUdtType", original.UdtTypeInfo.TypeName); + Assert.Equal("TestSchemaCollection", original.XmlTypeInfo.Name); + } + + /// + /// Ensures each packed boolean flag can be set and cleared without altering other flags. + /// + [Fact] + public void Flags_SetAndClear_PreserveOtherFlagsAndUpdatability() + { + var metadata = new TdsColumnMetadata(0) { Updatability = 2 }; + Action[] setters = + { + value => metadata.IsColumnSet = value, + value => metadata.IsDifferentName = value, + value => metadata.IsExpression = value, + value => metadata.IsHidden = value, + value => metadata.IsIdentity = value, + value => metadata.IsKey = value + }; + Func[] getters = + { + () => metadata.IsColumnSet, + () => metadata.IsDifferentName, + () => metadata.IsExpression, + () => metadata.IsHidden, + () => metadata.IsIdentity, + () => metadata.IsKey + }; + + for (int i = 0; i < setters.Length; i++) + { + setters[i](true); + for (int j = 0; j < getters.Length; j++) + { + Assert.Equal(j <= i, getters[j]()); + } + Assert.Equal(2, metadata.Updatability); + } + + for (int i = 0; i < setters.Length; i++) + { + setters[i](false); + for (int j = 0; j < getters.Length; j++) + { + Assert.Equal(j > i, getters[j]()); + } + Assert.Equal(2, metadata.Updatability); + } + } + + /// + /// Ensures updatability uses only its two bits and determines read-only status without + /// overwriting the other column flags, including when transitioning back to zero. + /// + [Theory] + [InlineData(0, 0, true)] + [InlineData(1, 1, false)] + [InlineData(2, 2, false)] + [InlineData(3, 3, false)] + [InlineData(4, 0, true)] + [InlineData(255, 3, false)] + public void Updatability_MasksValueAndPreservesFlags(byte value, byte expected, bool isReadOnly) + { + var metadata = new TdsColumnMetadata(0) + { + IsColumnSet = true, + IsDifferentName = true, + IsExpression = true, + IsHidden = true, + IsIdentity = true, + IsKey = true, + Updatability = 3 + }; + + metadata.Updatability = value; + + Assert.Equal(expected, metadata.Updatability); + Assert.Equal(isReadOnly, metadata.IsReadOnly); + Assert.True(metadata.IsColumnSet); + Assert.True(metadata.IsDifferentName); + Assert.True(metadata.IsExpression); + Assert.True(metadata.IsHidden); + Assert.True(metadata.IsIdentity); + Assert.True(metadata.IsKey); + } + + /// + /// Ensures only the four SQL Server 2008 date/time types are classified as such. + /// + [Theory] + [InlineData(SqlDbType.Date, true)] + [InlineData(SqlDbType.Time, true)] + [InlineData(SqlDbType.DateTime2, true)] + [InlineData(SqlDbType.DateTimeOffset, true)] + [InlineData(SqlDbType.DateTime, false)] + [InlineData(SqlDbType.SmallDateTime, false)] + [InlineData(SqlDbType.Int, false)] + public void Is2008DateTimeType_ClassifiesDbType(SqlDbType dbType, bool expected) + { + var metadata = new TdsColumnMetadata(0) { DbType = dbType }; + + Assert.Equal(expected, metadata.Is2008DateTimeType); + } + + /// + /// Ensures large UDT classification requires both the UDT type and maximum length. + /// + [Theory] + [InlineData(SqlDbType.Udt, int.MaxValue, true)] + [InlineData(SqlDbType.Udt, int.MaxValue - 1, false)] + [InlineData(SqlDbType.Udt, 0, false)] + [InlineData(SqlDbType.VarBinary, int.MaxValue, false)] + public void IsLargeUdt_RequiresUdtAndMaximumLength(SqlDbType dbType, int length, bool expected) + { + var metadata = new TdsColumnMetadata(0) { DbType = dbType, length = length }; + + Assert.Equal(expected, metadata.IsLargeUdt); + } +} diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs index 5ff99a8dc6..7b457e55e8 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsUdtTypeInfoTests.cs @@ -40,6 +40,7 @@ public void Clone_CopiesAllProperties() var clone = original.Clone(); // Assert + Assert.NotSame(original, clone); Assert.Equal(original.AssemblyQualifiedName, clone.AssemblyQualifiedName); Assert.Equal(original.DatabaseName, clone.DatabaseName); Assert.Equal(original.SchemaName, clone.SchemaName); diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs index 88a8ce3587..ad05825506 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsXmlTypeInfoTests.cs @@ -12,12 +12,12 @@ public class TdsXmlTypeInfoTests public void Constructor_InitializesPropertiesWithDefaultValues() { // Arrange & Act - var udtTypeInfo = new TdsXmlTypeInfo(); + var xmlTypeInfo = new TdsXmlTypeInfo(); // Assert - Assert.Null(udtTypeInfo.Database); - Assert.Null(udtTypeInfo.Name); - Assert.Null(udtTypeInfo.OwningSchema); + Assert.Null(xmlTypeInfo.Database); + Assert.Null(xmlTypeInfo.Name); + Assert.Null(xmlTypeInfo.OwningSchema); } [Fact] @@ -35,6 +35,7 @@ public void Clone_CopiesAllProperties() var cloned = original.Clone(); // Assert + Assert.NotSame(original, cloned); Assert.Equal(original.Database, cloned.Database); Assert.Equal(original.Name, cloned.Name); Assert.Equal(original.OwningSchema, cloned.OwningSchema); From 7474421b8f3e59d823ac8e547a817e4955e8c2c0 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 5 Oct 2026 18:41:23 -0400 Subject: [PATCH 10/10] Removing clone from TdsTypeInfo as it is not used. --- .../Parser/Tokens/TdsColumnMetadata.cs | 2 +- .../SqlClient/Parser/Tokens/TdsTypeInfo.cs | 9 - .../Parser/Tokens/TdsColumnMetadataTests.cs | 273 +++++++++++++----- .../Parser/Tokens/TdsTypeInfoTests.cs | 80 ----- 4 files changed, 206 insertions(+), 158 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index 053bdd7f8e..7273191876 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -201,7 +201,7 @@ public byte Updatability /// /// A new TdsColumnMetadata object that is a copy of the current instance. /// - internal override TdsColumnMetadata Clone() => + internal TdsColumnMetadata Clone() => new TdsColumnMetadata(this); private bool HasFlag(MetadataFlags flag) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index ec8425ac54..d8f91e00c1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -161,15 +161,6 @@ public bool IsNullable /// public TdsXmlTypeInfo XmlTypeInfo { get; set; } - /// - /// Creates a new instance of the TdsTypeInfo class that is a copy of the current instance. - /// - /// - /// A new TdsTypeInfo instance populated with the same values as the original. - /// - internal virtual TdsTypeInfo Clone() => - new TdsTypeInfo(this); - private bool HasFlag(TdsTypeInfoFlags flag) { return (flags & flag) != 0; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs index f3bf8d2fb9..124ab12247 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataTests.cs @@ -2,7 +2,6 @@ // 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.Text; using Xunit; @@ -56,6 +55,7 @@ public void Constructor_InitializesPropertiesWithDefaultValues(int ordinal) [Fact] public void Clone_CopiesAllProperties() { + // Arrange var original = new TdsColumnMetadata(7) { baseColumn = "OriginalColumn", @@ -84,13 +84,13 @@ public void Clone_CopiesAllProperties() scale = 8, collation = new SqlCollation(0x00, 0x00), BaseTypeInfo = new TdsTypeInfo { DbType = SqlDbType.Int }, - CipherMetadata = new SqlCipherMetadata(default, 1, 2, "TestAlgorithm", 1, 1) + CipherMetadata = new SqlCipherMetadata(null, 1, 2, "TestAlgorithm", 1, 1) }; - // Exercise the virtual override through the base type as well. - TdsTypeInfo typeInfo = original; - var clone = Assert.IsType(typeInfo.Clone()); + // Act + TdsColumnMetadata clone = original.Clone(); + // Assert Assert.NotSame(original, clone); Assert.Equal(original.Ordinal, clone.Ordinal); Assert.Equal(original.baseColumn, clone.baseColumn); @@ -126,17 +126,6 @@ public void Clone_CopiesAllProperties() Assert.Same(original.CipherMetadata, clone.CipherMetadata); Assert.Null(clone.UdtTypeInfo); Assert.Null(clone.XmlTypeInfo); - - clone.column = "ChangedAlias"; - clone.tableName.TableName = "ChangedTable"; - clone.IsKey = false; - clone.IsNullable = false; - clone.Updatability = 0; - Assert.Equal("ColumnAlias", original.column); - Assert.Equal("TestTable", original.TableName); - Assert.True(original.IsKey); - Assert.True(original.IsNullable); - Assert.Equal(2, original.Updatability); } /// @@ -145,7 +134,8 @@ public void Clone_CopiesAllProperties() [Fact] public void Clone_WithUdtAndXmlTypeInfo_CreatesDeepCopies() { - var original = new TdsColumnMetadata(0) + // Arrange + TdsColumnMetadata original = new TdsColumnMetadata(0) { UdtTypeInfo = new TdsUdtTypeInfo { @@ -163,73 +153,213 @@ public void Clone_WithUdtAndXmlTypeInfo_CreatesDeepCopies() } }; - var clone = original.Clone(); + // Act + TdsColumnMetadata clone = original.Clone(); + // Assert + // - UdtTypeInfo was deep cloned Assert.NotNull(clone.UdtTypeInfo); Assert.NotSame(original.UdtTypeInfo, clone.UdtTypeInfo); - Assert.Equal(original.UdtTypeInfo.AssemblyQualifiedName, - clone.UdtTypeInfo.AssemblyQualifiedName); Assert.Equal(original.UdtTypeInfo.DatabaseName, clone.UdtTypeInfo.DatabaseName); Assert.Equal(original.UdtTypeInfo.SchemaName, clone.UdtTypeInfo.SchemaName); Assert.Equal(original.UdtTypeInfo.TypeName, clone.UdtTypeInfo.TypeName); Assert.Equal(original.UdtTypeInfo.Type, clone.UdtTypeInfo.Type); + Assert.Equal( + original.UdtTypeInfo.AssemblyQualifiedName, + clone.UdtTypeInfo.AssemblyQualifiedName); + + // - XmlTypeInfo was deep cloned Assert.NotNull(clone.XmlTypeInfo); Assert.NotSame(original.XmlTypeInfo, clone.XmlTypeInfo); Assert.Equal(original.XmlTypeInfo.Database, clone.XmlTypeInfo.Database); Assert.Equal(original.XmlTypeInfo.OwningSchema, clone.XmlTypeInfo.OwningSchema); Assert.Equal(original.XmlTypeInfo.Name, clone.XmlTypeInfo.Name); + } + + [Fact] + public void IsColumnSet_PreservesOtherFlagsAndUpdatability() + { + // Arrange + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { Updatability = 2 }; + + // Act 1) Set IsColumnSet + metadata.IsColumnSet = true; + + // Assert + Assert.True(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + + // Act 2) Clear IsColumnSet + metadata.IsColumnSet = false; - clone.UdtTypeInfo.TypeName = "ChangedType"; - clone.XmlTypeInfo.Name = "ChangedSchemaCollection"; - Assert.Equal("MyUdtType", original.UdtTypeInfo.TypeName); - Assert.Equal("TestSchemaCollection", original.XmlTypeInfo.Name); + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); } - /// - /// Ensures each packed boolean flag can be set and cleared without altering other flags. - /// [Fact] - public void Flags_SetAndClear_PreserveOtherFlagsAndUpdatability() + public void IsDifferentName_PreservesOtherFlagsAndUpdatability() { - var metadata = new TdsColumnMetadata(0) { Updatability = 2 }; - Action[] setters = - { - value => metadata.IsColumnSet = value, - value => metadata.IsDifferentName = value, - value => metadata.IsExpression = value, - value => metadata.IsHidden = value, - value => metadata.IsIdentity = value, - value => metadata.IsKey = value - }; - Func[] getters = - { - () => metadata.IsColumnSet, - () => metadata.IsDifferentName, - () => metadata.IsExpression, - () => metadata.IsHidden, - () => metadata.IsIdentity, - () => metadata.IsKey - }; + // Arrange + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { Updatability = 2 }; - for (int i = 0; i < setters.Length; i++) - { - setters[i](true); - for (int j = 0; j < getters.Length; j++) - { - Assert.Equal(j <= i, getters[j]()); - } - Assert.Equal(2, metadata.Updatability); - } + // Act 1) Set IsColumnSet + metadata.IsDifferentName = true; - for (int i = 0; i < setters.Length; i++) - { - setters[i](false); - for (int j = 0; j < getters.Length; j++) - { - Assert.Equal(j > i, getters[j]()); - } - Assert.Equal(2, metadata.Updatability); - } + // Assert + Assert.False(metadata.IsColumnSet); + Assert.True(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + + // Act 2) Clear IsColumnSet + metadata.IsDifferentName = false; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + } + + [Fact] + public void IsExpression_PreservesOtherFlagsAndUpdatability() + { + // Arrange + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { Updatability = 2 }; + + // Act 1) Set IsColumnSet + metadata.IsExpression = true; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.True(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + + // Act 2) Clear IsColumnSet + metadata.IsExpression = false; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + } + + [Fact] + public void IsHidden_PreservesOtherFlagsAndUpdatability() + { + // Arrange + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { Updatability = 2 }; + + // Act 1) Set IsColumnSet + metadata.IsHidden = true; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.True(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + + // Act 2) Clear IsColumnSet + metadata.IsHidden = false; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + } + + [Fact] + public void IsIdentity_PreservesOtherFlagsAndUpdatability() + { + // Arrange + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { Updatability = 2 }; + + // Act 1) Set IsColumnSet + metadata.IsIdentity = true; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.True(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + + // Act 2) Clear IsColumnSet + metadata.IsIdentity = false; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + } + + [Fact] + public void IsKey_PreservesOtherFlagsAndUpdatability() + { + // Arrange + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { Updatability = 2 }; + + // Act 1) Set IsColumnSet + metadata.IsKey = true; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.True(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); + + // Act 2) Clear IsColumnSet + metadata.IsKey = false; + + // Assert + Assert.False(metadata.IsColumnSet); + Assert.False(metadata.IsDifferentName); + Assert.False(metadata.IsExpression); + Assert.False(metadata.IsHidden); + Assert.False(metadata.IsIdentity); + Assert.False(metadata.IsKey); + Assert.StrictEqual(2, metadata.Updatability); } /// @@ -245,7 +375,8 @@ public void Flags_SetAndClear_PreserveOtherFlagsAndUpdatability() [InlineData(255, 3, false)] public void Updatability_MasksValueAndPreservesFlags(byte value, byte expected, bool isReadOnly) { - var metadata = new TdsColumnMetadata(0) + // Arrange + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { IsColumnSet = true, IsDifferentName = true, @@ -256,8 +387,10 @@ public void Updatability_MasksValueAndPreservesFlags(byte value, byte expected, Updatability = 3 }; + // Act metadata.Updatability = value; + // Assert Assert.Equal(expected, metadata.Updatability); Assert.Equal(isReadOnly, metadata.IsReadOnly); Assert.True(metadata.IsColumnSet); @@ -281,8 +414,10 @@ public void Updatability_MasksValueAndPreservesFlags(byte value, byte expected, [InlineData(SqlDbType.Int, false)] public void Is2008DateTimeType_ClassifiesDbType(SqlDbType dbType, bool expected) { - var metadata = new TdsColumnMetadata(0) { DbType = dbType }; + // Act + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { DbType = dbType }; + // Assert Assert.Equal(expected, metadata.Is2008DateTimeType); } @@ -296,8 +431,10 @@ public void Is2008DateTimeType_ClassifiesDbType(SqlDbType dbType, bool expected) [InlineData(SqlDbType.VarBinary, int.MaxValue, false)] public void IsLargeUdt_RequiresUdtAndMaximumLength(SqlDbType dbType, int length, bool expected) { - var metadata = new TdsColumnMetadata(0) { DbType = dbType, length = length }; + // Act + TdsColumnMetadata metadata = new TdsColumnMetadata(0) { DbType = dbType, length = length }; + // Assert Assert.Equal(expected, metadata.IsLargeUdt); } } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs deleted file mode 100644 index 24956a31e5..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfoTests.cs +++ /dev/null @@ -1,80 +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.Data; -using System.Text; -using Xunit; - -namespace Microsoft.Data.SqlClient.Parser.Tokens; - -public class TdsTypeInfoTests -{ - [Fact] - public void Clone_CopiesAllUnclonedProperties() - { - // Arrange - var original = new TdsTypeInfo - { - CodePage = 1251, - DbType = SqlDbType.Decimal, - Encoding = Encoding.Unicode, - TdsType = 0x6A, - IsEncrypted = true - }; - original.length = 17; - original.precision = 28; - original.scale = 8; - original.collation = new SqlCollation(0x00, 0x00); - - // Act - var clone = original.Clone(); - - // Assert - Assert.Equal(original.CodePage, clone.CodePage); - Assert.Equal(original.DbType, clone.DbType); - Assert.Equal(original.Encoding, clone.Encoding); - Assert.Equal(original.TdsType, clone.TdsType); - Assert.Equal(original.IsEncrypted, clone.IsEncrypted); - Assert.Equal(original.length, clone.length); - Assert.Equal(original.precision, clone.precision); - Assert.Equal(original.scale, clone.scale); - Assert.Equal(original.collation, clone.collation); - } - - [Fact] - public void Clone_WithUdtTypeInfo_CreatesDeepCopy() - { - // Arrange - var original = new TdsTypeInfo - { - TdsType = 0xF0, - UdtTypeInfo = new TdsUdtTypeInfo() - }; - - // Act - var clone = original.Clone(); - - // Assert - Assert.NotNull(clone.UdtTypeInfo); - Assert.NotSame(original.UdtTypeInfo, clone.UdtTypeInfo); - } - - [Fact] - public void Clone_WithXmlTypeInfo_CreatesDeepCopy() - { - // Arrange - var original = new TdsTypeInfo - { - TdsType = 0xF1, - XmlTypeInfo = new TdsXmlTypeInfo() - }; - - // Act - var clone = original.Clone(); - - // Assert - Assert.NotNull(clone.XmlTypeInfo); - Assert.NotSame(original.XmlTypeInfo, clone.XmlTypeInfo); - } -}