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 73c0cb3c9c..f8fc03f915 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -209,9 +209,6 @@
Microsoft\Data\ProviderBase\DbConnectionInternal.cs
-
- Microsoft\Data\ProviderBase\DbMetaDataFactory.cs
-
Microsoft\Data\ProviderBase\DbReferenceCollection.cs
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index f8041d0d61..e0974d0d44 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -282,9 +282,6 @@
Microsoft\Data\ProviderBase\DbConnectionInternal.cs
-
- Microsoft\Data\ProviderBase\DbMetaDataFactory.cs
-
Microsoft\Data\ProviderBase\DbReferenceCollection.cs
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs
index dcd9f4bac9..08a6dcc2b8 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/AdapterUtil.cs
@@ -1129,9 +1129,6 @@ internal static IndexOutOfRangeException InvalidBufferSizeOrIndex(int numBytes,
internal static Exception InvalidDataLength(long length)
=> IndexOutOfRange(StringsHelper.GetString(Strings.SQL_InvalidDataLength, length.ToString(CultureInfo.InvariantCulture)));
- internal static bool CompareInsensitiveInvariant(string strvalue, string strconst)
- => 0 == CultureInfo.InvariantCulture.CompareInfo.Compare(strvalue, strconst, CompareOptions.IgnoreCase);
-
internal static void SetCurrentTransaction(Transaction transaction) => Transaction.Current = transaction;
internal static Exception NonSeqByteAccess(long badIndex, long currIndex, string method)
@@ -1142,9 +1139,6 @@ internal static Exception NonSeqByteAccess(long badIndex, long currIndex, string
internal static Exception NegativeParameter(string parameterName) => InvalidOperation(StringsHelper.GetString(Strings.ADP_NegativeParameter, parameterName));
- internal static Exception InvalidXmlMissingColumn(string collectionName, string columnName)
- => Argument(StringsHelper.GetString(Strings.MDF_InvalidXmlMissingColumn, collectionName, columnName));
-
internal static InvalidOperationException AsyncOperationPending() => InvalidOperation(StringsHelper.GetString(Strings.ADP_PendingAsyncOperation));
#endregion
@@ -1231,7 +1225,7 @@ internal static Exception InvalidCommandTimeout(int value, [CallerMemberName] st
=> Argument(StringsHelper.GetString(Strings.ADP_InvalidCommandTimeout, value.ToString(CultureInfo.InvariantCulture)), property);
#endregion
-#region DbMetaDataFactory
+#region SqlMetaDataFactory
internal static Exception DataTableDoesNotExist(string collectionName)
=> Argument(StringsHelper.GetString(Strings.MDF_DataTableDoesNotExist, collectionName));
@@ -1268,8 +1262,6 @@ internal static InvalidOperationException OpenConnectionRequired(string method,
internal static Exception OpenReaderExists(Exception e, bool marsOn)
=> InvalidOperation(StringsHelper.GetString(Strings.ADP_OpenReaderExists, marsOn ? ADP.Command : ADP.Connection), e);
- internal static Exception InvalidXml() => Argument(StringsHelper.GetString(Strings.MDF_InvalidXml));
-
internal static Exception InvalidXmlInvalidValue(string collectionName, string columnName)
=> Argument(StringsHelper.GetString(Strings.MDF_InvalidXmlInvalidValue, collectionName, columnName));
@@ -1287,8 +1279,6 @@ internal static Exception UndefinedCollection(string collectionName)
internal static Exception AmbiguousCollectionName(string collectionName)
=> Argument(StringsHelper.GetString(Strings.MDF_AmbiguousCollectionName, collectionName));
- internal static Exception MissingRestrictionColumn() => Argument(StringsHelper.GetString(Strings.MDF_MissingRestrictionColumn));
-
internal static Exception MissingRestrictionRow() => Argument(StringsHelper.GetString(Strings.MDF_MissingRestrictionRow));
internal static Exception UndefinedPopulationMechanism(string populationMechanism)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
index dda18faa70..a7debdb0bd 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionInternal.cs
@@ -860,7 +860,7 @@ protected internal virtual DataTable GetSchema(
{
Debug.Assert(outerConnection is not null, "outerConnection may not be null.");
- DbMetaDataFactory metaDataFactory = factory.GetMetaDataFactory(poolGroup, this);
+ SqlMetaDataFactory metaDataFactory = factory.GetMetaDataFactory(poolGroup, this);
Debug.Assert(metaDataFactory is not null, "metaDataFactory may not be null.");
return metaDataFactory.GetSchema(outerConnection, collectionName, restrictions);
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbMetaDataFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbMetaDataFactory.cs
deleted file mode 100644
index 4eea91478c..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbMetaDataFactory.cs
+++ /dev/null
@@ -1,755 +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 Microsoft.Data.Common;
-using Microsoft.Data.SqlClient;
-using System;
-using System.Data;
-using System.Data.Common;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Xml;
-
-namespace Microsoft.Data.ProviderBase
-{
- internal class DbMetaDataFactory
- {
-
- private DataSet _metaDataCollectionsDataSet;
- private string _normalizedServerVersion;
- private string _serverVersionString;
- // well known column names
- private const string CollectionNameKey = "CollectionName";
- private const string PopulationMechanismKey = "PopulationMechanism";
- private const string PopulationStringKey = "PopulationString";
- private const string MaximumVersionKey = "MaximumVersion";
- private const string MinimumVersionKey = "MinimumVersion";
- private const string RestrictionDefaultKey = "RestrictionDefault";
- private const string RestrictionNumberKey = "RestrictionNumber";
- private const string NumberOfRestrictionsKey = "NumberOfRestrictions";
- private const string RestrictionNameKey = "RestrictionName";
- private const string ParameterNameKey = "ParameterName";
-
- // population mechanisms
- private const string DataTableKey = "DataTable";
- private const string SqlCommandKey = "SQLCommand";
- private const string PrepareCollectionKey = "PrepareCollection";
-
- public DbMetaDataFactory(Stream xmlStream, string serverVersion, string normalizedServerVersion)
- {
- ADP.CheckArgumentNull(xmlStream, nameof(xmlStream));
- ADP.CheckArgumentNull(serverVersion, nameof(serverVersion));
- ADP.CheckArgumentNull(normalizedServerVersion, nameof(normalizedServerVersion));
-
- _serverVersionString = serverVersion;
- _normalizedServerVersion = normalizedServerVersion;
-
- LoadDataSetFromXml(xmlStream);
- }
-
- protected DataSet CollectionDataSet => _metaDataCollectionsDataSet;
-
- protected string ServerVersion => _serverVersionString;
-
- protected string ServerVersionNormalized => _normalizedServerVersion;
-
- protected DataTable CloneAndFilterCollection(string collectionName, string[] hiddenColumnNames)
- {
- DataTable destinationTable;
- DataColumn[] filteredSourceColumns;
- DataColumnCollection destinationColumns;
- DataRow newRow;
-
- DataTable sourceTable = _metaDataCollectionsDataSet.Tables[collectionName];
-
- if ((sourceTable == null) || (collectionName != sourceTable.TableName))
- {
- throw ADP.DataTableDoesNotExist(collectionName);
- }
-
- destinationTable = new DataTable(collectionName)
- {
- Locale = CultureInfo.InvariantCulture
- };
- destinationColumns = destinationTable.Columns;
-
- filteredSourceColumns = FilterColumns(sourceTable, hiddenColumnNames, destinationColumns);
-
- foreach (DataRow row in sourceTable.Rows)
- {
- if (SupportedByCurrentVersion(row))
- {
- newRow = destinationTable.NewRow();
- for (int i = 0; i < destinationColumns.Count; i++)
- {
- newRow[destinationColumns[i]] = row[filteredSourceColumns[i], DataRowVersion.Current];
- }
- destinationTable.Rows.Add(newRow);
- newRow.AcceptChanges();
- }
- }
-
- return destinationTable;
- }
-
- public void Dispose() => Dispose(true);
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- _normalizedServerVersion = null;
- _serverVersionString = null;
- _metaDataCollectionsDataSet.Dispose();
- }
- }
-
- private DataTable ExecuteCommand(DataRow requestedCollectionRow, string[] restrictions, DbConnection connection)
- {
- DataTable metaDataCollectionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
- DataColumn populationStringColumn = metaDataCollectionsTable.Columns[PopulationStringKey];
- DataColumn numberOfRestrictionsColumn = metaDataCollectionsTable.Columns[NumberOfRestrictionsKey];
- DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[CollectionNameKey];
-
- DataTable resultTable = null;
-
- Debug.Assert(requestedCollectionRow != null);
- string sqlCommand = requestedCollectionRow[populationStringColumn, DataRowVersion.Current] as string;
- int numberOfRestrictions = (int)requestedCollectionRow[numberOfRestrictionsColumn, DataRowVersion.Current];
- string collectionName = requestedCollectionRow[collectionNameColumn, DataRowVersion.Current] as string;
-
- if ((restrictions != null) && (restrictions.Length > numberOfRestrictions))
- {
- throw ADP.TooManyRestrictions(collectionName);
- }
-
- DbCommand command = connection.CreateCommand();
- SqlConnection castConnection = connection as SqlConnection;
-
- command.CommandText = sqlCommand;
- command.CommandTimeout = Math.Max(command.CommandTimeout, 180);
- command.Transaction = castConnection?.GetOpenTdsConnection()?.CurrentTransaction?.Parent;
-
- for (int i = 0; i < numberOfRestrictions; i++)
- {
-
- DbParameter restrictionParameter = command.CreateParameter();
-
- if ((restrictions != null) && (restrictions.Length > i) && (restrictions[i] != null))
- {
- restrictionParameter.Value = restrictions[i];
- }
- else
- {
- // This is where we have to assign null to the value of the parameter.
- restrictionParameter.Value = DBNull.Value;
- }
-
- restrictionParameter.ParameterName = GetParameterName(collectionName, i + 1);
- restrictionParameter.Direction = ParameterDirection.Input;
- command.Parameters.Add(restrictionParameter);
- }
-
- DbDataReader reader = null;
- try
- {
- try
- {
- reader = command.ExecuteReader();
- }
- catch (Exception e)
- {
- if (!ADP.IsCatchableExceptionType(e))
- {
- throw;
- }
- throw ADP.QueryFailed(collectionName, e);
- }
-
- // Build a DataTable from the reader
- resultTable = new DataTable(collectionName)
- {
- Locale = CultureInfo.InvariantCulture
- };
-
- DataTable schemaTable = reader.GetSchemaTable();
- foreach (DataRow row in schemaTable.Rows)
- {
- resultTable.Columns.Add(row["ColumnName"] as string, (Type)row["DataType"] as Type);
- }
- object[] values = new object[resultTable.Columns.Count];
- while (reader.Read())
- {
- reader.GetValues(values);
- resultTable.Rows.Add(values);
- }
- }
- finally
- {
- reader?.Dispose();
- }
- return resultTable;
- }
-
- private DataColumn[] FilterColumns(DataTable sourceTable, string[] hiddenColumnNames, DataColumnCollection destinationColumns)
- {
- int columnCount = 0;
- foreach (DataColumn sourceColumn in sourceTable.Columns)
- {
- if (IncludeThisColumn(sourceColumn, hiddenColumnNames))
- {
- columnCount++;
- }
- }
-
- if (columnCount == 0)
- {
- throw ADP.NoColumns();
- }
-
- int currentColumn = 0;
- DataColumn[] filteredSourceColumns = new DataColumn[columnCount];
-
- foreach (DataColumn sourceColumn in sourceTable.Columns)
- {
- if (IncludeThisColumn(sourceColumn, hiddenColumnNames))
- {
- DataColumn newDestinationColumn = new(sourceColumn.ColumnName, sourceColumn.DataType);
- destinationColumns.Add(newDestinationColumn);
- filteredSourceColumns[currentColumn] = sourceColumn;
- currentColumn++;
- }
- }
- return filteredSourceColumns;
- }
-
- internal DataRow FindMetaDataCollectionRow(string collectionName)
- {
- bool versionFailure;
- bool haveExactMatch;
- bool haveMultipleInexactMatches;
- string candidateCollectionName;
-
- DataTable metaDataCollectionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
- if (metaDataCollectionsTable == null)
- {
- throw ADP.InvalidXml();
- }
-
- DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[DbMetaDataColumnNames.CollectionName];
-
- if (collectionNameColumn == null || (typeof(string) != collectionNameColumn.DataType))
- {
- throw ADP.InvalidXmlMissingColumn(DbMetaDataCollectionNames.MetaDataCollections, DbMetaDataColumnNames.CollectionName);
- }
-
- DataRow requestedCollectionRow = null;
- string exactCollectionName = null;
-
- // find the requested collection
- versionFailure = false;
- haveExactMatch = false;
- haveMultipleInexactMatches = false;
-
- foreach (DataRow row in metaDataCollectionsTable.Rows)
- {
-
- candidateCollectionName = row[collectionNameColumn, DataRowVersion.Current] as string;
- if (string.IsNullOrEmpty(candidateCollectionName))
- {
- throw ADP.InvalidXmlInvalidValue(DbMetaDataCollectionNames.MetaDataCollections, DbMetaDataColumnNames.CollectionName);
- }
-
- if (ADP.CompareInsensitiveInvariant(candidateCollectionName, collectionName))
- {
- if (!SupportedByCurrentVersion(row))
- {
- versionFailure = true;
- }
- else
- {
- if (collectionName == candidateCollectionName)
- {
- if (haveExactMatch)
- {
- throw ADP.CollectionNameIsNotUnique(collectionName);
- }
- requestedCollectionRow = row;
- exactCollectionName = candidateCollectionName;
- haveExactMatch = true;
- }
- else if (!haveExactMatch)
- {
- // have an inexact match - ok only if it is the only one
- if (exactCollectionName != null)
- {
- // can't fail here becasue we may still find an exact match
- haveMultipleInexactMatches = true;
- }
- requestedCollectionRow = row;
- exactCollectionName = candidateCollectionName;
- }
- }
- }
- }
-
- if (requestedCollectionRow == null)
- {
- if (!versionFailure)
- {
- throw ADP.UndefinedCollection(collectionName);
- }
- else
- {
- throw ADP.UnsupportedVersion(collectionName);
- }
- }
-
- if (!haveExactMatch && haveMultipleInexactMatches)
- {
- throw ADP.AmbiguousCollectionName(collectionName);
- }
-
- return requestedCollectionRow;
-
- }
-
- private void FixUpDataSourceInformationRow(DataRow dataSourceInfoRow)
- {
- Debug.Assert(dataSourceInfoRow.Table.Columns.Contains(DbMetaDataColumnNames.DataSourceProductVersion));
- Debug.Assert(dataSourceInfoRow.Table.Columns.Contains(DbMetaDataColumnNames.DataSourceProductVersionNormalized));
-
- dataSourceInfoRow[DbMetaDataColumnNames.DataSourceProductVersion] = _serverVersionString;
- dataSourceInfoRow[DbMetaDataColumnNames.DataSourceProductVersionNormalized] = _normalizedServerVersion;
- }
-
-
- private string GetParameterName(string neededCollectionName, int neededRestrictionNumber)
- {
- DataColumn collectionName = null;
- DataColumn parameterName = null;
- DataColumn restrictionName = null;
- DataColumn restrictionNumber = null;
-
- string result = null;
-
- DataTable restrictionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.Restrictions];
- if (restrictionsTable != null)
- {
- DataColumnCollection restrictionColumns = restrictionsTable.Columns;
- if (restrictionColumns != null)
- {
- collectionName = restrictionColumns[DbMetaDataFactory.CollectionNameKey];
- parameterName = restrictionColumns[ParameterNameKey];
- restrictionName = restrictionColumns[RestrictionNameKey];
- restrictionNumber = restrictionColumns[RestrictionNumberKey];
- }
- }
-
- if ((parameterName == null) || (collectionName == null) || (restrictionName == null) || (restrictionNumber == null))
- {
- throw ADP.MissingRestrictionColumn();
- }
-
- foreach (DataRow restriction in restrictionsTable.Rows)
- {
-
- if (((string)restriction[collectionName] == neededCollectionName) &&
- ((int)restriction[restrictionNumber] == neededRestrictionNumber) &&
- (SupportedByCurrentVersion(restriction)))
- {
-
- result = (string)restriction[parameterName];
- break;
- }
- }
-
- if (result == null)
- {
- throw ADP.MissingRestrictionRow();
- }
-
- return result;
- }
-
- public virtual DataTable GetSchema(DbConnection connection, string collectionName, string[] restrictions)
- {
- Debug.Assert(_metaDataCollectionsDataSet != null);
-
- DataTable metaDataCollectionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
- DataColumn populationMechanismColumn = metaDataCollectionsTable.Columns[PopulationMechanismKey];
- DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[DbMetaDataColumnNames.CollectionName];
-
- string[] hiddenColumns;
-
- DataRow requestedCollectionRow = FindMetaDataCollectionRow(collectionName);
- string exactCollectionName = requestedCollectionRow[collectionNameColumn, DataRowVersion.Current] as string;
-
- if (!ADP.IsEmptyArray(restrictions))
- {
-
- for (int i = 0; i < restrictions.Length; i++)
- {
- if ((restrictions[i] != null) && (restrictions[i].Length > 4096))
- {
- // use a non-specific error because no new beta 2 error messages are allowed
- // TODO: will add a more descriptive error in RTM
- throw ADP.NotSupported();
- }
- }
- }
-
- string populationMechanism = requestedCollectionRow[populationMechanismColumn, DataRowVersion.Current] as string;
-
- DataTable requestedSchema;
- switch (populationMechanism)
- {
-
- case DataTableKey:
- if (exactCollectionName == DbMetaDataCollectionNames.MetaDataCollections)
- {
- hiddenColumns = new string[2];
- hiddenColumns[0] = PopulationMechanismKey;
- hiddenColumns[1] = PopulationStringKey;
- }
- else
- {
- hiddenColumns = null;
- }
- // none of the datatable collections support restrictions
- if (!ADP.IsEmptyArray(restrictions))
- {
- throw ADP.TooManyRestrictions(exactCollectionName);
- }
-
-
- requestedSchema = CloneAndFilterCollection(exactCollectionName, hiddenColumns);
- break;
-
- case SqlCommandKey:
- requestedSchema = ExecuteCommand(requestedCollectionRow, restrictions, connection);
- break;
-
- case PrepareCollectionKey:
- requestedSchema = PrepareCollection(exactCollectionName, restrictions, connection);
- break;
-
- default:
- throw ADP.UndefinedPopulationMechanism(populationMechanism);
- }
-
- return requestedSchema;
- }
-
- private bool IncludeThisColumn(DataColumn sourceColumn, string[] hiddenColumnNames)
- {
-
- bool result = true;
- string sourceColumnName = sourceColumn.ColumnName;
-
- switch (sourceColumnName)
- {
-
- case MinimumVersionKey:
- case MaximumVersionKey:
- result = false;
- break;
-
- default:
- if (hiddenColumnNames == null)
- {
- break;
- }
- for (int i = 0; i < hiddenColumnNames.Length; i++)
- {
- if (hiddenColumnNames[i] == sourceColumnName)
- {
- result = false;
- break;
- }
- }
- break;
- }
-
- return result;
- }
-
- private void LoadDataSetFromXml(Stream XmlStream)
- {
- DataSet metaDataCollectionsDataSet = new DataSet("NewDataSet")
- {
- Locale = CultureInfo.InvariantCulture
- };
- XmlReaderSettings settings = new()
- {
- XmlResolver = null,
- IgnoreComments = true,
- IgnoreWhitespace = true
- };
- using XmlReader reader = XmlReader.Create(XmlStream, settings);
-
- // Build up the schema DataSet manually, then load data from XmlStream. The schema of the DataSet is defined at:
- // * https://learn.microsoft.com/en-us/sql/connect/ado-net/common-schema-collections
- // * https://learn.microsoft.com/en-us/sql/connect/ado-net/sql-server-schema-collections
- // Building the schema manually is necessary because DataSet.ReadXml uses XML serialization. This is slow, and it
- // increases the binary size of AOT assemblies by approximately 4MB.
-
- bool readContainer = reader.Read();
-
- // Skip past the XML declaration and the outer container element. This XML document is hardcoded;
- // these checks will need to be adjusted if its structure changes.
- Debug.Assert(readContainer);
- Debug.Assert(reader.NodeType == XmlNodeType.XmlDeclaration);
-
- readContainer = reader.Read();
- Debug.Assert(readContainer);
- Debug.Assert(reader.NodeType == XmlNodeType.Element);
- Debug.Assert(reader.Name == "MetaData");
- Debug.Assert(reader.Depth == 0);
-
- // Iterate over each "table element" of the outer container element.
- // LoadDataTable will read the child elements of each table element.
- while (reader.Read()
- && reader.Depth == 1)
- {
- DataTable dataTable = null;
- Action rowFixup = null;
-
- Debug.Assert(reader.NodeType == XmlNodeType.Element);
-
- switch (reader.Name)
- {
- case "MetaDataCollectionsTable":
- dataTable = CreateMetaDataCollectionsDataTable();
- break;
- case "RestrictionsTable":
- dataTable = CreateRestrictionsDataTable();
- break;
- case "DataSourceInformationTable":
- dataTable = CreateDataSourceInformationDataTable();
- rowFixup = FixUpDataSourceInformationRow;
- break;
- case "DataTypesTable":
- dataTable = CreateDataTypesDataTable();
- break;
- case "ReservedWordsTable":
- dataTable = CreateReservedWordsDataTable();
- break;
- default:
- Debug.Fail($"Unexpected table element name: {reader.Name}");
- break;
- }
-
- if (dataTable != null)
- {
- LoadDataTable(reader, dataTable, rowFixup);
-
- metaDataCollectionsDataSet.Tables.Add(dataTable);
- }
- }
-
- _metaDataCollectionsDataSet = metaDataCollectionsDataSet;
- }
-
- private static void LoadDataTable(XmlReader reader, DataTable table, Action rowFixup)
- {
- int parentDepth = reader.Depth;
-
- table.BeginLoadData();
-
- // One outer loop per element, each loop reading every property of the row
- while (reader.Read()
- && reader.Depth == parentDepth + 1)
- {
- Debug.Assert(reader.NodeType == XmlNodeType.Element);
- Debug.Assert(reader.Name == table.TableName);
-
- int childDepth = reader.Depth;
- DataRow row = table.NewRow();
-
- // Read every child property. Hardcoded structure - start with the element name, advance to the text, then to the EndElement
- while (reader.Read()
- && reader.Depth == childDepth + 1)
- {
- DataColumn column;
- bool successfulRead;
-
- Debug.Assert(reader.NodeType == XmlNodeType.Element);
-
- column = table.Columns[reader.Name];
- Debug.Assert(column != null);
-
- successfulRead = reader.Read();
- Debug.Assert(successfulRead);
- Debug.Assert(reader.NodeType == XmlNodeType.Text);
-
- row[column] = reader.Value;
-
- successfulRead = reader.Read();
- Debug.Assert(successfulRead);
- Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
- }
-
- rowFixup?.Invoke(row);
-
- table.Rows.Add(row);
-
- Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
- }
-
- table.EndLoadData();
- table.AcceptChanges();
- }
-
- private static DataTable CreateMetaDataCollectionsDataTable()
- => new DataTable(DbMetaDataCollectionNames.MetaDataCollections)
- {
- Columns =
- {
- new DataColumn(DbMetaDataColumnNames.CollectionName, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.NumberOfRestrictions, typeof(int)),
- new DataColumn(DbMetaDataColumnNames.NumberOfIdentifierParts, typeof(int)),
- new DataColumn(PopulationMechanismKey, typeof(string)),
- new DataColumn(PopulationStringKey, typeof(string)),
- new DataColumn(MinimumVersionKey, typeof(string)),
- new DataColumn(MaximumVersionKey, typeof(string))
- }
- };
-
- private static DataTable CreateRestrictionsDataTable()
- => new DataTable(DbMetaDataCollectionNames.Restrictions)
- {
- Columns =
- {
- new DataColumn(DbMetaDataColumnNames.CollectionName, typeof(string)),
- new DataColumn(RestrictionNameKey, typeof(string)),
- new DataColumn(ParameterNameKey, typeof(string)),
- new DataColumn(RestrictionDefaultKey, typeof(string)),
- new DataColumn(RestrictionNumberKey, typeof(int)),
- new DataColumn(MinimumVersionKey, typeof(string)),
- new DataColumn(MaximumVersionKey, typeof(string))
- }
- };
-
- private static DataTable CreateDataSourceInformationDataTable()
- => new DataTable(DbMetaDataCollectionNames.DataSourceInformation)
- {
- Columns =
- {
- new DataColumn(DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.DataSourceProductName, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.DataSourceProductVersion, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.DataSourceProductVersionNormalized, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.GroupByBehavior, typeof(GroupByBehavior)),
- new DataColumn(DbMetaDataColumnNames.IdentifierPattern, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.IdentifierCase, typeof(IdentifierCase)),
- new DataColumn(DbMetaDataColumnNames.OrderByColumnsInSelect, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.ParameterMarkerFormat, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.ParameterMarkerPattern, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.ParameterNameMaxLength, typeof(int)),
- new DataColumn(DbMetaDataColumnNames.ParameterNamePattern, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.QuotedIdentifierPattern, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.QuotedIdentifierCase, typeof(IdentifierCase)),
- new DataColumn(DbMetaDataColumnNames.StatementSeparatorPattern, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.StringLiteralPattern, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.SupportedJoinOperators, typeof(SupportedJoinOperators))
- }
- };
-
- private static DataTable CreateDataTypesDataTable()
- => new DataTable(DbMetaDataCollectionNames.DataTypes)
- {
- Columns =
- {
- new DataColumn(DbMetaDataColumnNames.TypeName, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.ProviderDbType, typeof(int)),
- new DataColumn(DbMetaDataColumnNames.ColumnSize, typeof(long)),
- new DataColumn(DbMetaDataColumnNames.CreateFormat, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.CreateParameters, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.DataType, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.IsAutoIncrementable, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsBestMatch, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsCaseSensitive, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsFixedLength, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsFixedPrecisionScale, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsLong, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsNullable, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsSearchable, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsSearchableWithLike, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.IsUnsigned, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.MaximumScale, typeof(short)),
- new DataColumn(DbMetaDataColumnNames.MinimumScale, typeof(short)),
- new DataColumn(DbMetaDataColumnNames.IsConcurrencyType, typeof(bool)),
- new DataColumn(MaximumVersionKey, typeof(string)),
- new DataColumn(MinimumVersionKey, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.IsLiteralSupported, typeof(bool)),
- new DataColumn(DbMetaDataColumnNames.LiteralPrefix, typeof(string)),
- new DataColumn(DbMetaDataColumnNames.LiteralSuffix, typeof(string))
- }
- };
-
- private static DataTable CreateReservedWordsDataTable()
- => new DataTable(DbMetaDataCollectionNames.ReservedWords)
- {
- Columns =
- {
- new DataColumn(DbMetaDataColumnNames.ReservedWord, typeof(string)),
- new DataColumn(MinimumVersionKey, typeof(string)),
- new DataColumn(MaximumVersionKey, typeof(string))
- }
- };
-
- protected virtual DataTable PrepareCollection(string collectionName, string[] restrictions, DbConnection connection)
- {
- throw ADP.NotSupported();
- }
-
- private bool SupportedByCurrentVersion(DataRow requestedCollectionRow)
- {
- bool result = true;
- DataColumnCollection tableColumns = requestedCollectionRow.Table.Columns;
- DataColumn versionColumn;
- object version;
-
- // check the minimum version first
- versionColumn = tableColumns[MinimumVersionKey];
- if (versionColumn != null)
- {
- version = requestedCollectionRow[versionColumn];
- if (version != null)
- {
- if (version != DBNull.Value)
- {
- if (0 > string.Compare(_normalizedServerVersion, (string)version, StringComparison.OrdinalIgnoreCase))
- {
- result = false;
- }
- }
- }
- }
-
- // if the minimum version was ok what about the maximum version
- if (result)
- {
- versionColumn = tableColumns[MaximumVersionKey];
- if (versionColumn != null)
- {
- version = requestedCollectionRow[versionColumn];
- if (version != null)
- {
- if (version != DBNull.Value)
- {
- if (0 < string.Compare(_normalizedServerVersion, (string)version, StringComparison.OrdinalIgnoreCase))
- {
- result = false;
- }
- }
- }
- }
- }
- return result;
- }
- }
-}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs
index 226a5749b4..3a7b01945a 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/ConnectionPool/DbConnectionPoolGroup.cs
@@ -40,7 +40,6 @@ sealed internal class DbConnectionPoolGroup
private int _state; // see PoolGroupState* below
private DbConnectionPoolGroupProviderInfo _providerInfo;
- private DbMetaDataFactory _metaDataFactory;
private static int s_objectTypeCount; // EventSource counter
@@ -95,18 +94,7 @@ internal DbConnectionPoolGroupProviderInfo ProviderInfo
internal DbConnectionPoolGroupOptions PoolGroupOptions => _poolGroupOptions;
- internal DbMetaDataFactory MetaDataFactory
- {
- get
- {
- return _metaDataFactory;
- }
-
- set
- {
- _metaDataFactory = value;
- }
- }
+ internal SqlMetaDataFactory MetaDataFactory { get; set; }
internal int Clear()
{
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
index 6f697d37e8..22eb0a3d06 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnectionFactory.cs
@@ -265,28 +265,15 @@ internal DbConnectionPoolGroup GetConnectionPoolGroup(
return connectionPoolGroup;
}
- internal DbMetaDataFactory GetMetaDataFactory(
+ internal SqlMetaDataFactory GetMetaDataFactory(
DbConnectionPoolGroup poolGroup,
DbConnectionInternal internalConnection)
{
Debug.Assert(poolGroup is not null, "connectionPoolGroup may not be null.");
- // Get the matadatafactory from the pool entry. If it does not already have one
+ // Get the metadata factory from the pool entry. If it does not already have one
// create one and save it on the pool entry
- DbMetaDataFactory metaDataFactory = poolGroup.MetaDataFactory;
-
- // CONSIDER: serializing this so we don't construct multiple metadata factories
- // if two threads happen to hit this at the same time. One will be GC'd
- if (metaDataFactory is null)
- {
- metaDataFactory = CreateMetaDataFactory(internalConnection, out bool allowCache);
- if (allowCache)
- {
- poolGroup.MetaDataFactory = metaDataFactory;
- }
- }
-
- return metaDataFactory;
+ return poolGroup.MetaDataFactory ??= CreateMetaDataFactory(internalConnection);
}
internal void QueuePoolForRelease(IDbConnectionPool pool, bool clearing)
@@ -756,19 +743,14 @@ private static DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions(Sql
return poolingOptions;
}
- private static DbMetaDataFactory CreateMetaDataFactory(
- DbConnectionInternal internalConnection,
- out bool cacheMetaDataFactory)
+ private static SqlMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection)
{
Debug.Assert(internalConnection is not null, "internalConnection may not be null.");
Stream xmlStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.Data.SqlClient.SqlMetaData.xml");
Debug.Assert(xmlStream is not null, $"{nameof(xmlStream)} may not be null.");
- cacheMetaDataFactory = true;
- return new SqlMetaDataFactory(xmlStream,
- internalConnection.ServerVersion,
- internalConnection.ServerVersion);
+ return new SqlMetaDataFactory(xmlStream, internalConnection.ServerVersion);
}
private Task CreateReplaceConnectionContinuation(
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs
index da39c1f375..ca3dec0658 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlMetaDataFactory.cs
@@ -6,29 +6,452 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
+using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Text;
+using System.Xml;
using Microsoft.Data.Common;
-using Microsoft.Data.ProviderBase;
namespace Microsoft.Data.SqlClient
{
- internal sealed class SqlMetaDataFactory : DbMetaDataFactory
+ internal sealed class SqlMetaDataFactory : IDisposable
{
-
+ // Well-known column names
+ private const string CollectionNameKey = "CollectionName";
+ private const string PopulationMechanismKey = "PopulationMechanism";
+ private const string PopulationStringKey = "PopulationString";
+ private const string MaximumVersionKey = "MaximumVersion";
+ private const string MinimumVersionKey = "MinimumVersion";
+ private const string RestrictionDefaultKey = "RestrictionDefault";
+ private const string RestrictionNumberKey = "RestrictionNumber";
+ private const string NumberOfRestrictionsKey = "NumberOfRestrictions";
+ private const string RestrictionNameKey = "RestrictionName";
+ private const string ParameterNameKey = "ParameterName";
+
+ // Well-known server versions
private const string ServerVersionNormalized90 = "09.00.0000";
private const string ServerVersionNormalized10 = "10.00.0000";
- private readonly HashSet _assemblyPropertyUnsupportedEngines = new() { 6, 9, 11 };
- public SqlMetaDataFactory(Stream XMLStream,
- string serverVersion,
- string serverVersionNormalized) :
- base(XMLStream, serverVersion, serverVersionNormalized)
- { }
+ private static readonly HashSet s_assemblyPropertyUnsupportedEngines = new() { 6, 9, 11 };
+
+ private readonly DataSet _collectionDataSet;
+ private readonly string _serverVersion;
+
+ public SqlMetaDataFactory(Stream xmlStream, string serverVersion)
+ {
+ ADP.CheckArgumentNull(xmlStream, nameof(xmlStream));
+ ADP.CheckArgumentNull(serverVersion, nameof(serverVersion));
+
+ _serverVersion = serverVersion;
+
+ _collectionDataSet = LoadDataSetFromXml(xmlStream);
+ }
+
+ private bool SupportedByCurrentVersion(DataRow requestedCollectionRow)
+ {
+ DataColumnCollection tableColumns = requestedCollectionRow.Table.Columns;
+ DataColumn versionColumn;
+ object version;
+
+ // check the minimum version first
+ versionColumn = tableColumns[MinimumVersionKey];
+ if (versionColumn is not null)
+ {
+ version = requestedCollectionRow[versionColumn];
+
+ if (version is string minVersion
+ && string.Compare(_serverVersion, minVersion, StringComparison.OrdinalIgnoreCase) < 0)
+ {
+ return false;
+ }
+ }
+
+ // if the minimum version was ok what about the maximum version
+ versionColumn = tableColumns[MaximumVersionKey];
+ if (versionColumn is not null)
+ {
+ version = requestedCollectionRow[versionColumn];
+
+ if (version is string maxVersion
+ && string.Compare(_serverVersion, maxVersion, StringComparison.OrdinalIgnoreCase) > 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private DataRow FindMetaDataCollectionRow(string collectionName)
+ {
+ bool versionFailure = false;
+ bool haveExactMatch = false;
+ bool haveMultipleInexactMatches = false;
+
+ DataTable metaDataCollectionsTable = _collectionDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
+ Debug.Assert(metaDataCollectionsTable is not null);
+
+ DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[DbMetaDataColumnNames.CollectionName];
+ Debug.Assert(collectionNameColumn is not null && collectionNameColumn.DataType == typeof(string));
+
+ DataRow requestedCollectionRow = null;
+ string exactCollectionName = null;
+
+ // find the requested collection
+ foreach (DataRow row in metaDataCollectionsTable.Rows)
+ {
+ string candidateCollectionName = row[collectionNameColumn, DataRowVersion.Current] as string;
+
+ if (string.IsNullOrEmpty(candidateCollectionName))
+ {
+ throw ADP.InvalidXmlInvalidValue(DbMetaDataCollectionNames.MetaDataCollections, DbMetaDataColumnNames.CollectionName);
+ }
+
+ if (string.Equals(candidateCollectionName, collectionName, StringComparison.InvariantCultureIgnoreCase))
+ {
+ if (!SupportedByCurrentVersion(row))
+ {
+ versionFailure = true;
+ }
+ else if (collectionName == candidateCollectionName)
+ {
+ if (haveExactMatch)
+ {
+ throw ADP.CollectionNameIsNotUnique(collectionName);
+ }
+ requestedCollectionRow = row;
+ exactCollectionName = candidateCollectionName;
+ haveExactMatch = true;
+ }
+ else if (!haveExactMatch)
+ {
+ // have an inexact match - ok only if it is the only one
+ if (exactCollectionName is not null)
+ {
+ // can't fail here because we may still find an exact match
+ haveMultipleInexactMatches = true;
+ }
+ requestedCollectionRow = row;
+ exactCollectionName = candidateCollectionName;
+ }
+ }
+ }
+
+ if (requestedCollectionRow is null)
+ {
+ if (!versionFailure)
+ {
+ throw ADP.UndefinedCollection(collectionName);
+ }
+ else
+ {
+ throw ADP.UnsupportedVersion(collectionName);
+ }
+ }
+
+ if (!haveExactMatch && haveMultipleInexactMatches)
+ {
+ throw ADP.AmbiguousCollectionName(collectionName);
+ }
+
+ return requestedCollectionRow;
+
+ }
+
+ public DataTable GetSchema(DbConnection connection, string collectionName, string[] restrictions)
+ {
+ const string DataTableKey = "DataTable";
+ const string SqlCommandKey = "SQLCommand";
+ const string PrepareCollectionKey = "PrepareCollection";
+
+ Debug.Assert(_collectionDataSet is not null);
+
+ DataTable metaDataCollectionsTable = _collectionDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
+ DataColumn populationMechanismColumn = metaDataCollectionsTable.Columns[PopulationMechanismKey];
+ DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[DbMetaDataColumnNames.CollectionName];
+ DataRow requestedCollectionRow = FindMetaDataCollectionRow(collectionName);
+ string exactCollectionName = requestedCollectionRow[collectionNameColumn, DataRowVersion.Current] as string;
+
+ if (!ADP.IsEmptyArray(restrictions))
+ {
+
+ for (int i = 0; i < restrictions.Length; i++)
+ {
+ if ((restrictions[i] is not null) && (restrictions[i].Length > 4096))
+ {
+ // use a non-specific error because no new beta 2 error messages are allowed
+ // TODO: will add a more descriptive error in RTM
+ throw ADP.NotSupported();
+ }
+ }
+ }
+
+ string populationMechanism = requestedCollectionRow[populationMechanismColumn, DataRowVersion.Current] as string;
+
+ DataTable requestedSchema;
+ switch (populationMechanism)
+ {
+ case DataTableKey:
+ ReadOnlySpan hiddenColumns = exactCollectionName == DbMetaDataCollectionNames.MetaDataCollections
+ ? [PopulationMechanismKey, PopulationStringKey]
+ : [];
+
+ // none of the datatable collections support restrictions
+ if (!ADP.IsEmptyArray(restrictions))
+ {
+ throw ADP.TooManyRestrictions(exactCollectionName);
+ }
+
+ requestedSchema = CloneAndFilterCollection(exactCollectionName, hiddenColumns);
+ break;
+
+ case SqlCommandKey:
+ requestedSchema = ExecuteCommand(requestedCollectionRow, restrictions, connection);
+ break;
+
+ case PrepareCollectionKey:
+ requestedSchema = PrepareCollection(exactCollectionName, restrictions, connection);
+ break;
+
+ default:
+ throw ADP.UndefinedPopulationMechanism(populationMechanism);
+ }
+
+ return requestedSchema;
+ }
+
+ public void Dispose() => Dispose(true);
+
+ private void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ _collectionDataSet.Dispose();
+ }
+ }
+
+ #region GetSchema Helpers: DataTable Population Method
+ private static bool IncludeThisColumn(DataColumn sourceColumn, ReadOnlySpan hiddenColumnNames)
+ {
+ string sourceColumnName = sourceColumn.ColumnName;
+
+ return sourceColumnName switch
+ {
+ MinimumVersionKey or MaximumVersionKey => false,
+#if NET
+ _ => !hiddenColumnNames.Contains(sourceColumnName),
+#else
+ _ => hiddenColumnNames.IndexOf(sourceColumnName) == -1,
+#endif
+ };
+ }
+
+ private static DataColumn[] FilterColumns(DataTable sourceTable, ReadOnlySpan hiddenColumnNames, DataColumnCollection destinationColumns)
+ {
+ int columnCount = 0;
+ foreach (DataColumn sourceColumn in sourceTable.Columns)
+ {
+ if (IncludeThisColumn(sourceColumn, hiddenColumnNames))
+ {
+ columnCount++;
+ }
+ }
+
+ if (columnCount == 0)
+ {
+ throw ADP.NoColumns();
+ }
+
+ int currentColumn = 0;
+ DataColumn[] filteredSourceColumns = new DataColumn[columnCount];
+
+ foreach (DataColumn sourceColumn in sourceTable.Columns)
+ {
+ if (IncludeThisColumn(sourceColumn, hiddenColumnNames))
+ {
+ DataColumn newDestinationColumn = new(sourceColumn.ColumnName, sourceColumn.DataType);
+ destinationColumns.Add(newDestinationColumn);
+ filteredSourceColumns[currentColumn] = sourceColumn;
+ currentColumn++;
+ }
+ }
+ return filteredSourceColumns;
+ }
+
+ private DataTable CloneAndFilterCollection(string collectionName, ReadOnlySpan hiddenColumnNames)
+ {
+ DataTable destinationTable;
+ DataColumn[] filteredSourceColumns;
+ DataColumnCollection destinationColumns;
+ DataRow newRow;
+
+ DataTable sourceTable = _collectionDataSet.Tables[collectionName];
+
+ if (sourceTable?.TableName != collectionName)
+ {
+ throw ADP.DataTableDoesNotExist(collectionName);
+ }
+
+ destinationTable = new DataTable(collectionName)
+ {
+ Locale = CultureInfo.InvariantCulture
+ };
+ destinationColumns = destinationTable.Columns;
+
+ filteredSourceColumns = FilterColumns(sourceTable, hiddenColumnNames, destinationColumns);
+
+ foreach (DataRow row in sourceTable.Rows)
+ {
+ if (SupportedByCurrentVersion(row))
+ {
+ newRow = destinationTable.NewRow();
+ for (int i = 0; i < destinationColumns.Count; i++)
+ {
+ newRow[destinationColumns[i]] = row[filteredSourceColumns[i], DataRowVersion.Current];
+ }
+ destinationTable.Rows.Add(newRow);
+ newRow.AcceptChanges();
+ }
+ }
+
+ return destinationTable;
+ }
+ #endregion
+
+ #region GetSchema Helpers: ExecuteCommand Population Method
+ private string GetParameterName(string neededCollectionName, int neededRestrictionNumber)
+ {
+ DataTable restrictionsTable = _collectionDataSet.Tables[DbMetaDataCollectionNames.Restrictions];
+
+ Debug.Assert(restrictionsTable is not null);
+
+ DataColumnCollection restrictionColumns = restrictionsTable.Columns;
+ DataColumn collectionName = restrictionColumns[CollectionNameKey];
+ DataColumn parameterName = restrictionColumns[ParameterNameKey];
+ DataColumn restrictionName = restrictionColumns[RestrictionNameKey];
+ DataColumn restrictionNumber = restrictionColumns[RestrictionNumberKey];
+
+ Debug.Assert(parameterName is not null);
+ Debug.Assert(collectionName is not null);
+ Debug.Assert(restrictionName is not null);
+ Debug.Assert(restrictionNumber is not null);
+
+ string result = null;
+
+ foreach (DataRow restriction in restrictionsTable.Rows)
+ {
+
+ if (((string)restriction[collectionName] == neededCollectionName) &&
+ ((int)restriction[restrictionNumber] == neededRestrictionNumber) &&
+ (SupportedByCurrentVersion(restriction)))
+ {
+
+ result = (string)restriction[parameterName];
+ break;
+ }
+ }
+
+ if (result is null)
+ {
+ throw ADP.MissingRestrictionRow();
+ }
+
+ return result;
+ }
+
+ private DataTable ExecuteCommand(DataRow requestedCollectionRow, string[] restrictions, DbConnection connection)
+ {
+ Debug.Assert(requestedCollectionRow is not null);
+
+ DataTable metaDataCollectionsTable = _collectionDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
+ DataColumn populationStringColumn = metaDataCollectionsTable.Columns[PopulationStringKey];
+ DataColumn numberOfRestrictionsColumn = metaDataCollectionsTable.Columns[NumberOfRestrictionsKey];
+ DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[CollectionNameKey];
+
+ DataTable resultTable = null;
+ string sqlCommand = requestedCollectionRow[populationStringColumn, DataRowVersion.Current] as string;
+ int numberOfRestrictions = (int)requestedCollectionRow[numberOfRestrictionsColumn, DataRowVersion.Current];
+ string collectionName = requestedCollectionRow[collectionNameColumn, DataRowVersion.Current] as string;
+
+ if ((restrictions is not null) && (restrictions.Length > numberOfRestrictions))
+ {
+ throw ADP.TooManyRestrictions(collectionName);
+ }
+
+ SqlConnection castConnection = connection as SqlConnection;
+ using SqlCommand command = castConnection.CreateCommand();
+
+ command.CommandText = sqlCommand;
+ command.CommandTimeout = Math.Max(command.CommandTimeout, 180);
+ command.Transaction = castConnection?.GetOpenTdsConnection()?.CurrentTransaction?.Parent;
+
+ for (int i = 0; i < numberOfRestrictions; i++)
+ {
+ SqlParameter restrictionParameter = command.CreateParameter();
+
+ if ((restrictions is not null) && (i < restrictions.Length) && (restrictions[i] is not null))
+ {
+ restrictionParameter.Value = restrictions[i];
+ }
+ else
+ {
+ // This is where we have to assign null to the value of the parameter.
+ restrictionParameter.Value = DBNull.Value;
+ }
+
+ restrictionParameter.ParameterName = GetParameterName(collectionName, i + 1);
+ restrictionParameter.Direction = ParameterDirection.Input;
+ command.Parameters.Add(restrictionParameter);
+ }
+
+ SqlDataReader reader = null;
+ try
+ {
+ try
+ {
+ reader = command.ExecuteReader();
+ }
+ catch (Exception e)
+ {
+ if (!ADP.IsCatchableExceptionType(e))
+ {
+ throw;
+ }
+ throw ADP.QueryFailed(collectionName, e);
+ }
+
+ // Build a DataTable from the reader
+ resultTable = new DataTable(collectionName)
+ {
+ Locale = CultureInfo.InvariantCulture
+ };
+
+ System.Collections.ObjectModel.ReadOnlyCollection colSchema = reader.GetColumnSchema();
+ foreach (DbColumn col in colSchema)
+ {
+ resultTable.Columns.Add(col.ColumnName, col.DataType);
+ }
+ object[] values = new object[resultTable.Columns.Count];
+ while (reader.Read())
+ {
+ reader.GetValues(values);
+ resultTable.Rows.Add(values);
+ }
+ }
+ finally
+ {
+ reader?.Dispose();
+ }
+ return resultTable;
+ }
+ #endregion
- private void addUDTsToDataTypesTable(DataTable dataTypesTable, SqlConnection connection, string ServerVersion)
+ #region GetSchema Helpers: PrepareCollection Population Method
+ private static void AddUDTsToDataTypesTable(DataTable dataTypesTable, SqlConnection connection, string serverVersion)
{
- const string sqlCommand =
+ const string GetEngineEditionSqlCommand = "SELECT SERVERPROPERTY('EngineEdition');";
+ const string ListUdtSqlCommand =
"select " +
"assemblies.name, " +
"types.assembly_class, " +
@@ -43,18 +466,30 @@ private void addUDTsToDataTypesTable(DataTable dataTypesTable, SqlConnection con
"max_length " +
"from sys.assemblies as assemblies join sys.assembly_types as types " +
"on assemblies.assembly_id = types.assembly_id ";
+ const int AssemblyNameSqlIndex = 0;
+ const int AssemblyClassSqlIndex = 1;
+ const int VersionMajorSqlIndex = 2;
+ const int VersionMinorSqlIndex = 3;
+ const int VersionBuildSqlIndex = 4;
+ const int VersionRevisionSqlIndex = 5;
+ const int CultureInfoSqlIndex = 6;
+ const int PublicKeySqlIndex = 7;
+ const int IsNullableSqlIndex = 8;
+ const int IsFixedLengthSqlIndex = 9;
+ const int ColumnSizeSqlIndex = 10;
// pre 9.0/2005 servers do not have UDTs
- if (0 > string.Compare(ServerVersion, ServerVersionNormalized90, StringComparison.OrdinalIgnoreCase))
+ if (0 > string.Compare(serverVersion, ServerVersionNormalized90, StringComparison.OrdinalIgnoreCase))
{
return;
}
- SqlCommand engineEditionCommand = connection.CreateCommand();
- engineEditionCommand.CommandText = "SELECT SERVERPROPERTY('EngineEdition');";
- var engineEdition = (int)engineEditionCommand.ExecuteScalar();
+ using SqlCommand command = connection.CreateCommand();
+
+ command.CommandText = GetEngineEditionSqlCommand;
+ int engineEdition = (int)command.ExecuteScalar();
- if (_assemblyPropertyUnsupportedEngines.Contains(engineEdition))
+ if (s_assemblyPropertyUnsupportedEngines.Contains(engineEdition))
{
// Azure SQL Edge (9) throws an exception when querying sys.assemblies
// Azure Synapse Analytics (6) and Azure Synapse serverless SQL pool (11)
@@ -62,10 +497,7 @@ private void addUDTsToDataTypesTable(DataTable dataTypesTable, SqlConnection con
return;
}
- // Execute the SELECT statement
- SqlCommand command = connection.CreateCommand();
- command.CommandText = sqlCommand;
- DataColumn providerDbtype = dataTypesTable.Columns[DbMetaDataColumnNames.ProviderDbType];
+ DataColumn providerDbType = dataTypesTable.Columns[DbMetaDataColumnNames.ProviderDbType];
DataColumn columnSize = dataTypesTable.Columns[DbMetaDataColumnNames.ColumnSize];
DataColumn isFixedLength = dataTypesTable.Columns[DbMetaDataColumnNames.IsFixedLength];
DataColumn isSearchable = dataTypesTable.Columns[DbMetaDataColumnNames.IsSearchable];
@@ -73,206 +505,161 @@ private void addUDTsToDataTypesTable(DataTable dataTypesTable, SqlConnection con
DataColumn typeName = dataTypesTable.Columns[DbMetaDataColumnNames.TypeName];
DataColumn isNullable = dataTypesTable.Columns[DbMetaDataColumnNames.IsNullable];
- if ((providerDbtype == null) ||
- (columnSize == null) ||
- (isFixedLength == null) ||
- (isSearchable == null) ||
- (isLiteralSupported == null) ||
- (typeName == null) ||
- (isNullable == null))
- {
- throw ADP.InvalidXml();
- }
+ Debug.Assert(providerDbType is not null, "providerDbType column not found");
+ Debug.Assert(columnSize is not null, "columnSize column not found");
+ Debug.Assert(isFixedLength is not null, "isFixedLength column not found");
+ Debug.Assert(isSearchable is not null, "isSearchable column not found");
+ Debug.Assert(isLiteralSupported is not null, "isLiteralSupported column not found");
+ Debug.Assert(typeName is not null, "typeName column not found");
+ Debug.Assert(isNullable is not null, "isNullable column not found");
- const int columnSizeIndex = 10;
- const int isFixedLengthIndex = 9;
- const int isNullableIndex = 8;
- const int assemblyNameIndex = 0;
- const int assemblyClassIndex = 1;
- const int versionMajorIndex = 2;
- const int versionMinorIndex = 3;
- const int versionBuildIndex = 4;
- const int versionRevisionIndex = 5;
- const int cultureInfoIndex = 6;
- const int publicKeyIndex = 7;
+ // Execute the SELECT statement
+ command.CommandText = ListUdtSqlCommand;
+ using SqlDataReader reader = command.ExecuteReader();
+ object[] values = new object[11];
- using (IDataReader reader = command.ExecuteReader())
+ while (reader.Read())
{
+ reader.GetValues(values);
+ DataRow newRow = dataTypesTable.NewRow();
- object[] values = new object[11];
- while (reader.Read())
- {
+ newRow[providerDbType] = SqlDbType.Udt;
+ newRow[columnSize] = values[ColumnSizeSqlIndex];
- reader.GetValues(values);
- DataRow newRow = dataTypesTable.NewRow();
+ if (values[IsFixedLengthSqlIndex] != DBNull.Value)
+ {
+ newRow[isFixedLength] = values[IsFixedLengthSqlIndex];
+ }
- newRow[providerDbtype] = SqlDbType.Udt;
+ newRow[isSearchable] = true;
+ newRow[isLiteralSupported] = false;
+ if (values[IsNullableSqlIndex] != DBNull.Value)
+ {
+ newRow[isNullable] = values[IsNullableSqlIndex];
+ }
- if (values[columnSizeIndex] != DBNull.Value)
- {
- newRow[columnSize] = values[columnSizeIndex];
- }
+ if ((values[AssemblyClassSqlIndex] != DBNull.Value) &&
+ (values[VersionMajorSqlIndex] != DBNull.Value) &&
+ (values[VersionMinorSqlIndex] != DBNull.Value) &&
+ (values[VersionBuildSqlIndex] != DBNull.Value) &&
+ (values[VersionRevisionSqlIndex] != DBNull.Value))
+ {
- if (values[isFixedLengthIndex] != DBNull.Value)
- {
- newRow[isFixedLength] = values[isFixedLengthIndex];
- }
+ StringBuilder nameString = new();
+ nameString.Append($"{values[AssemblyClassSqlIndex]}, {values[AssemblyNameSqlIndex]}"
+ + $", Version={values[VersionMajorSqlIndex]}.{values[VersionMinorSqlIndex]}.{values[VersionBuildSqlIndex]}.{values[VersionRevisionSqlIndex]}");
- newRow[isSearchable] = true;
- newRow[isLiteralSupported] = false;
- if (values[isNullableIndex] != DBNull.Value)
+ if (values[CultureInfoSqlIndex] != DBNull.Value)
{
- newRow[isNullable] = values[isNullableIndex];
+ nameString.Append($", Culture={values[CultureInfoSqlIndex]}");
}
- if ((values[assemblyNameIndex] != DBNull.Value) &&
- (values[assemblyClassIndex] != DBNull.Value) &&
- (values[versionMajorIndex] != DBNull.Value) &&
- (values[versionMinorIndex] != DBNull.Value) &&
- (values[versionBuildIndex] != DBNull.Value) &&
- (values[versionRevisionIndex] != DBNull.Value))
+ if (values[PublicKeySqlIndex] != DBNull.Value)
{
- StringBuilder nameString = new();
- nameString.Append(values[assemblyClassIndex].ToString());
- nameString.Append(", ");
- nameString.Append(values[assemblyNameIndex].ToString());
- nameString.Append(", Version=");
-
- nameString.Append(values[versionMajorIndex].ToString());
- nameString.Append(".");
- nameString.Append(values[versionMinorIndex].ToString());
- nameString.Append(".");
- nameString.Append(values[versionBuildIndex].ToString());
- nameString.Append(".");
- nameString.Append(values[versionRevisionIndex].ToString());
-
- if (values[cultureInfoIndex] != DBNull.Value)
- {
- nameString.Append(", Culture=");
- nameString.Append(values[cultureInfoIndex].ToString());
- }
+ nameString.Append(", PublicKeyToken=");
- if (values[publicKeyIndex] != DBNull.Value)
+ byte[] byteArrayValue = (byte[])values[PublicKeySqlIndex];
+#if NET9_0_OR_GREATER
+ nameString.Append(Convert.ToHexStringLower(byteArrayValue));
+#elif NET8_0
+ nameString.Append(Convert.ToHexString(byteArrayValue).ToLowerInvariant());
+#else
+ foreach (byte b in byteArrayValue)
{
-
- nameString.Append(", PublicKeyToken=");
-
- StringBuilder resultString = new();
- byte[] byteArrayValue = (byte[])values[publicKeyIndex];
- foreach (byte b in byteArrayValue)
- {
- resultString.Append(string.Format("{0,-2:x2}", b));
- }
- nameString.Append(resultString.ToString());
+ nameString.Append(string.Format("{0,-2:x2}", b));
}
+#endif
+ }
- newRow[typeName] = nameString.ToString();
- dataTypesTable.Rows.Add(newRow);
- newRow.AcceptChanges();
- } // if assembly name
-
- }//end while
- } // end using
+ newRow[typeName] = nameString.ToString();
+ dataTypesTable.Rows.Add(newRow);
+ newRow.AcceptChanges();
+ } // if assembly name
+ }//end while
}
- private void AddTVPsToDataTypesTable(DataTable dataTypesTable, SqlConnection connection, string ServerVersion)
+ private static void AddTVPsToDataTypesTable(DataTable dataTypesTable, SqlConnection connection, string serverVersion)
{
-
- const string sqlCommand =
+ const string ListTvpsSqlCommand =
"select " +
"name, " +
"is_nullable, " +
"max_length " +
"from sys.types " +
"where is_table_type = 1";
+ const int TypeNameSqlIndex = 0;
+ const int IsNullableSqlIndex = 1;
+ const int ColumnSizeSqlIndex = 2;
// TODO: update this check once the server upgrades major version number!!!
// pre 9.0/2005 servers do not have Table types
- if (0 > string.Compare(ServerVersion, ServerVersionNormalized10, StringComparison.OrdinalIgnoreCase))
+ if (0 > string.Compare(serverVersion, ServerVersionNormalized10, StringComparison.OrdinalIgnoreCase))
{
return;
}
// Execute the SELECT statement
- SqlCommand command = connection.CreateCommand();
- command.CommandText = sqlCommand;
- DataColumn providerDbtype = dataTypesTable.Columns[DbMetaDataColumnNames.ProviderDbType];
+ using SqlCommand command = connection.CreateCommand();
+ command.CommandText = ListTvpsSqlCommand;
+
+ DataColumn providerDbType = dataTypesTable.Columns[DbMetaDataColumnNames.ProviderDbType];
DataColumn columnSize = dataTypesTable.Columns[DbMetaDataColumnNames.ColumnSize];
DataColumn isSearchable = dataTypesTable.Columns[DbMetaDataColumnNames.IsSearchable];
DataColumn isLiteralSupported = dataTypesTable.Columns[DbMetaDataColumnNames.IsLiteralSupported];
DataColumn typeName = dataTypesTable.Columns[DbMetaDataColumnNames.TypeName];
DataColumn isNullable = dataTypesTable.Columns[DbMetaDataColumnNames.IsNullable];
- if ((providerDbtype == null) ||
- (columnSize == null) ||
- (isSearchable == null) ||
- (isLiteralSupported == null) ||
- (typeName == null) ||
- (isNullable == null))
- {
- throw ADP.InvalidXml();
- }
+ Debug.Assert(providerDbType is not null, "providerDbType column not found");
+ Debug.Assert(columnSize is not null, "columnSize column not found");
+ Debug.Assert(isSearchable is not null, "isSearchable column not found");
+ Debug.Assert(isLiteralSupported is not null, "isLiteralSupported column not found");
+ Debug.Assert(typeName is not null, "typeName column not found");
+ Debug.Assert(isNullable is not null, "isNullable column not found");
- const int columnSizeIndex = 2;
- const int isNullableIndex = 1;
- const int typeNameIndex = 0;
+ using SqlDataReader reader = command.ExecuteReader();
- using (IDataReader reader = command.ExecuteReader())
+ object[] values = new object[11];
+ while (reader.Read())
{
- object[] values = new object[11];
- while (reader.Read())
- {
+ reader.GetValues(values);
+ DataRow newRow = dataTypesTable.NewRow();
- reader.GetValues(values);
- DataRow newRow = dataTypesTable.NewRow();
-
- newRow[providerDbtype] = SqlDbType.Structured;
+ newRow[providerDbType] = SqlDbType.Structured;
+ newRow[columnSize] = values[ColumnSizeSqlIndex];
+ newRow[isSearchable] = false;
+ newRow[isLiteralSupported] = false;
- if (values[columnSizeIndex] != DBNull.Value)
- {
- newRow[columnSize] = values[columnSizeIndex];
- }
+ if (values[IsNullableSqlIndex] != DBNull.Value)
+ {
+ newRow[isNullable] = values[IsNullableSqlIndex];
+ }
- newRow[isSearchable] = false;
- newRow[isLiteralSupported] = false;
- if (values[isNullableIndex] != DBNull.Value)
- {
- newRow[isNullable] = values[isNullableIndex];
- }
+ newRow[typeName] = values[TypeNameSqlIndex];
- if (values[typeNameIndex] != DBNull.Value)
- {
- newRow[typeName] = values[typeNameIndex];
- dataTypesTable.Rows.Add(newRow);
- newRow.AcceptChanges();
- } // if type name
- }//end while
- } // end using
+ dataTypesTable.Rows.Add(newRow);
+ newRow.AcceptChanges();
+ }//end while
}
private DataTable GetDataTypesTable(SqlConnection connection)
{
// verify the existence of the table in the data set
- DataTable dataTypesTable = CollectionDataSet.Tables[DbMetaDataCollectionNames.DataTypes];
- if (dataTypesTable == null)
- {
- throw ADP.UnableToBuildCollection(DbMetaDataCollectionNames.DataTypes);
- }
+ Debug.Assert(_collectionDataSet.Tables.Contains(DbMetaDataCollectionNames.DataTypes), "DataTypes collection not found in the DataSet");
// copy the table filtering out any rows that don't apply to tho current version of the provider
- dataTypesTable = CloneAndFilterCollection(DbMetaDataCollectionNames.DataTypes, null);
+ DataTable dataTypesTable = CloneAndFilterCollection(DbMetaDataCollectionNames.DataTypes, []);
- addUDTsToDataTypesTable(dataTypesTable, connection, ServerVersionNormalized);
- AddTVPsToDataTypesTable(dataTypesTable, connection, ServerVersionNormalized);
+ AddUDTsToDataTypesTable(dataTypesTable, connection, _serverVersion);
+ AddTVPsToDataTypesTable(dataTypesTable, connection, _serverVersion);
dataTypesTable.AcceptChanges();
return dataTypesTable;
}
- protected override DataTable PrepareCollection(string collectionName, string[] restrictions, DbConnection connection)
+ private DataTable PrepareCollection(string collectionName, string[] restrictions, DbConnection connection)
{
SqlConnection sqlConnection = (SqlConnection)connection;
DataTable resultTable = null;
@@ -292,10 +679,243 @@ protected override DataTable PrepareCollection(string collectionName, string[] r
}
return resultTable;
+ }
+ #endregion
+
+ #region Create MetaDataCollections DataSet from XML
+ private DataSet LoadDataSetFromXml(Stream XmlStream)
+ {
+ DataSet metaDataCollectionsDataSet = new("NewDataSet")
+ {
+ Locale = CultureInfo.InvariantCulture
+ };
+ XmlReaderSettings settings = new()
+ {
+ XmlResolver = null,
+ IgnoreComments = true,
+ IgnoreWhitespace = true
+ };
+ using XmlReader reader = XmlReader.Create(XmlStream, settings);
+
+ // Build up the schema DataSet manually, then load data from XmlStream. The schema of the DataSet is defined at:
+ // * https://learn.microsoft.com/en-us/sql/connect/ado-net/common-schema-collections
+ // * https://learn.microsoft.com/en-us/sql/connect/ado-net/sql-server-schema-collections
+ // Building the schema manually is necessary because DataSet.ReadXml uses XML serialization. This is slow, and it
+ // increases the binary size of AOT assemblies by approximately 4MB.
+
+ bool readContainer = reader.Read();
+
+ // Skip past the XML declaration and the outer container element. This XML document is hardcoded;
+ // these checks will need to be adjusted if its structure changes.
+ Debug.Assert(readContainer);
+ Debug.Assert(reader.NodeType == XmlNodeType.XmlDeclaration);
+
+ readContainer = reader.Read();
+ Debug.Assert(readContainer);
+ Debug.Assert(reader.NodeType == XmlNodeType.Element);
+ Debug.Assert(reader.Name == "MetaData");
+ Debug.Assert(reader.Depth == 0);
+
+ // Iterate over each "table element" of the outer container element.
+ // LoadDataTable will read the child elements of each table element.
+ while (reader.Read()
+ && reader.Depth == 1)
+ {
+ DataTable dataTable = null;
+ Action rowFixup = null;
+
+ Debug.Assert(reader.NodeType == XmlNodeType.Element);
+
+ switch (reader.Name)
+ {
+ case "MetaDataCollectionsTable":
+ dataTable = CreateMetaDataCollectionsDataTable();
+ break;
+ case "RestrictionsTable":
+ dataTable = CreateRestrictionsDataTable();
+ break;
+ case "DataSourceInformationTable":
+ dataTable = CreateDataSourceInformationDataTable();
+ rowFixup = FixUpDataSourceInformationRow;
+ break;
+ case "DataTypesTable":
+ dataTable = CreateDataTypesDataTable();
+ break;
+ case "ReservedWordsTable":
+ dataTable = CreateReservedWordsDataTable();
+ break;
+ default:
+ Debug.Fail($"Unexpected table element name: {reader.Name}");
+ break;
+ }
+
+ if (dataTable is not null)
+ {
+ LoadDataTable(reader, dataTable, rowFixup);
+
+ metaDataCollectionsDataSet.Tables.Add(dataTable);
+ }
+ }
+
+ return metaDataCollectionsDataSet;
+ }
+
+ private static void LoadDataTable(XmlReader reader, DataTable table, Action rowFixup)
+ {
+ int parentDepth = reader.Depth;
+
+ table.BeginLoadData();
+ // One outer loop per element, each loop reading every property of the row
+ while (reader.Read()
+ && reader.Depth == parentDepth + 1)
+ {
+ Debug.Assert(reader.NodeType == XmlNodeType.Element);
+ Debug.Assert(reader.Name == table.TableName);
+
+ int childDepth = reader.Depth;
+ DataRow row = table.NewRow();
+
+ // Read every child property. Hardcoded structure - start with the element name, advance to the text, then to the EndElement
+ while (reader.Read()
+ && reader.Depth == childDepth + 1)
+ {
+ DataColumn column;
+ bool successfulRead;
+
+ Debug.Assert(reader.NodeType == XmlNodeType.Element);
+
+ column = table.Columns[reader.Name];
+ Debug.Assert(column is not null);
+
+ successfulRead = reader.Read();
+ Debug.Assert(successfulRead);
+ Debug.Assert(reader.NodeType == XmlNodeType.Text);
+
+ row[column] = reader.Value;
+
+ successfulRead = reader.Read();
+ Debug.Assert(successfulRead);
+ Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
+ }
+
+ rowFixup?.Invoke(row);
+
+ table.Rows.Add(row);
+
+ Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
+ }
+
+ table.EndLoadData();
+ table.AcceptChanges();
+ }
+
+ private void FixUpDataSourceInformationRow(DataRow dataSourceInfoRow)
+ {
+ Debug.Assert(dataSourceInfoRow.Table.Columns.Contains(DbMetaDataColumnNames.DataSourceProductVersion));
+ Debug.Assert(dataSourceInfoRow.Table.Columns.Contains(DbMetaDataColumnNames.DataSourceProductVersionNormalized));
+
+ dataSourceInfoRow[DbMetaDataColumnNames.DataSourceProductVersion] = _serverVersion;
+ dataSourceInfoRow[DbMetaDataColumnNames.DataSourceProductVersionNormalized] = _serverVersion;
}
+ private static DataTable CreateMetaDataCollectionsDataTable()
+ => new(DbMetaDataCollectionNames.MetaDataCollections)
+ {
+ Columns =
+ {
+ new DataColumn(DbMetaDataColumnNames.CollectionName, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.NumberOfRestrictions, typeof(int)),
+ new DataColumn(DbMetaDataColumnNames.NumberOfIdentifierParts, typeof(int)),
+ new DataColumn(PopulationMechanismKey, typeof(string)),
+ new DataColumn(PopulationStringKey, typeof(string)),
+ new DataColumn(MinimumVersionKey, typeof(string)),
+ new DataColumn(MaximumVersionKey, typeof(string))
+ }
+ };
+
+ private static DataTable CreateRestrictionsDataTable()
+ => new(DbMetaDataCollectionNames.Restrictions)
+ {
+ Columns =
+ {
+ new DataColumn(DbMetaDataColumnNames.CollectionName, typeof(string)),
+ new DataColumn(RestrictionNameKey, typeof(string)),
+ new DataColumn(ParameterNameKey, typeof(string)),
+ new DataColumn(RestrictionDefaultKey, typeof(string)),
+ new DataColumn(RestrictionNumberKey, typeof(int)),
+ new DataColumn(MinimumVersionKey, typeof(string)),
+ new DataColumn(MaximumVersionKey, typeof(string))
+ }
+ };
+
+ private static DataTable CreateDataSourceInformationDataTable()
+ => new(DbMetaDataCollectionNames.DataSourceInformation)
+ {
+ Columns =
+ {
+ new DataColumn(DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.DataSourceProductName, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.DataSourceProductVersion, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.DataSourceProductVersionNormalized, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.GroupByBehavior, typeof(GroupByBehavior)),
+ new DataColumn(DbMetaDataColumnNames.IdentifierPattern, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.IdentifierCase, typeof(IdentifierCase)),
+ new DataColumn(DbMetaDataColumnNames.OrderByColumnsInSelect, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.ParameterMarkerFormat, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.ParameterMarkerPattern, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.ParameterNameMaxLength, typeof(int)),
+ new DataColumn(DbMetaDataColumnNames.ParameterNamePattern, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.QuotedIdentifierPattern, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.QuotedIdentifierCase, typeof(IdentifierCase)),
+ new DataColumn(DbMetaDataColumnNames.StatementSeparatorPattern, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.StringLiteralPattern, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.SupportedJoinOperators, typeof(SupportedJoinOperators))
+ }
+ };
+ private static DataTable CreateDataTypesDataTable()
+ => new(DbMetaDataCollectionNames.DataTypes)
+ {
+ Columns =
+ {
+ new DataColumn(DbMetaDataColumnNames.TypeName, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.ProviderDbType, typeof(int)),
+ new DataColumn(DbMetaDataColumnNames.ColumnSize, typeof(long)),
+ new DataColumn(DbMetaDataColumnNames.CreateFormat, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.CreateParameters, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.DataType, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.IsAutoIncrementable, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsBestMatch, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsCaseSensitive, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsFixedLength, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsFixedPrecisionScale, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsLong, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsNullable, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsSearchable, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsSearchableWithLike, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.IsUnsigned, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.MaximumScale, typeof(short)),
+ new DataColumn(DbMetaDataColumnNames.MinimumScale, typeof(short)),
+ new DataColumn(DbMetaDataColumnNames.IsConcurrencyType, typeof(bool)),
+ new DataColumn(MaximumVersionKey, typeof(string)),
+ new DataColumn(MinimumVersionKey, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.IsLiteralSupported, typeof(bool)),
+ new DataColumn(DbMetaDataColumnNames.LiteralPrefix, typeof(string)),
+ new DataColumn(DbMetaDataColumnNames.LiteralSuffix, typeof(string))
+ }
+ };
+ private static DataTable CreateReservedWordsDataTable()
+ => new(DbMetaDataCollectionNames.ReservedWords)
+ {
+ Columns =
+ {
+ new DataColumn(DbMetaDataColumnNames.ReservedWord, typeof(string)),
+ new DataColumn(MinimumVersionKey, typeof(string)),
+ new DataColumn(MaximumVersionKey, typeof(string))
+ }
+ };
+ #endregion
}
}