536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
529 lines
22 KiB
C#
529 lines
22 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="dbmetadatafactory.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
// <owner current="true" primary="false">Microsoft</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Data.ProviderBase {
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Xml;
|
|
using System.Xml.Schema;
|
|
|
|
internal class DbMetaDataFactory{ // V1.2.3300
|
|
|
|
private DataSet _metaDataCollectionsDataSet;
|
|
private string _normalizedServerVersion;
|
|
private string _serverVersionString;
|
|
// well known column names
|
|
private const string _collectionName = "CollectionName";
|
|
private const string _populationMechanism = "PopulationMechanism";
|
|
private const string _populationString = "PopulationString";
|
|
private const string _maximumVersion = "MaximumVersion";
|
|
private const string _minimumVersion = "MinimumVersion";
|
|
private const string _dataSourceProductVersionNormalized = "DataSourceProductVersionNormalized";
|
|
private const string _dataSourceProductVersion = "DataSourceProductVersion";
|
|
private const string _restrictionDefault = "RestrictionDefault";
|
|
private const string _restrictionNumber = "RestrictionNumber";
|
|
private const string _numberOfRestrictions = "NumberOfRestrictions";
|
|
private const string _restrictionName = "RestrictionName";
|
|
private const string _parameterName = "ParameterName";
|
|
|
|
// population mechanisms
|
|
private const string _dataTable = "DataTable";
|
|
private const string _sqlCommand = "SQLCommand";
|
|
private const string _prepareCollection = "PrepareCollection";
|
|
|
|
|
|
public DbMetaDataFactory(Stream xmlStream, string serverVersion, string normalizedServerVersion) {
|
|
|
|
ADP.CheckArgumentNull(xmlStream, "xmlStream");
|
|
ADP.CheckArgumentNull(serverVersion, "serverVersion");
|
|
ADP.CheckArgumentNull(normalizedServerVersion, "normalizedServerVersion");
|
|
|
|
LoadDataSetFromXml(xmlStream);
|
|
|
|
_serverVersionString = serverVersion;
|
|
_normalizedServerVersion = normalizedServerVersion;
|
|
}
|
|
|
|
protected DataSet CollectionDataSet {
|
|
get {
|
|
return _metaDataCollectionsDataSet;
|
|
}
|
|
}
|
|
|
|
protected string ServerVersion {
|
|
get {
|
|
return _serverVersionString;
|
|
}
|
|
}
|
|
|
|
|
|
protected string ServerVersionNormalized {
|
|
get {
|
|
return _normalizedServerVersion;
|
|
}
|
|
}
|
|
|
|
protected DataTable CloneAndFilterCollection(string collectionName, string[] hiddenColumnNames) {
|
|
|
|
DataTable sourceTable;
|
|
DataTable destinationTable;
|
|
DataColumn[] filteredSourceColumns;
|
|
DataColumnCollection destinationColumns;
|
|
DataRow newRow;
|
|
|
|
sourceTable = _metaDataCollectionsDataSet.Tables[collectionName];
|
|
|
|
if ((sourceTable == null) || (collectionName != sourceTable.TableName)) {
|
|
throw ADP.DataTableDoesNotExist(collectionName);
|
|
}
|
|
|
|
destinationTable = new DataTable(collectionName);
|
|
destinationTable.Locale = CultureInfo.InvariantCulture;
|
|
destinationColumns = destinationTable.Columns;
|
|
|
|
filteredSourceColumns = FilterColumns(sourceTable,hiddenColumnNames,destinationColumns);
|
|
|
|
foreach (DataRow row in sourceTable.Rows) {
|
|
if (SupportedByCurrentVersion(row) == true) {
|
|
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);
|
|
}
|
|
|
|
virtual protected 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[_populationString];
|
|
DataColumn numberOfRestrictionsColumn = metaDataCollectionsTable.Columns[_numberOfRestrictions];
|
|
DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[_collectionName];
|
|
//DataColumn restrictionNameColumn = metaDataCollectionsTable.Columns[_restrictionName];
|
|
|
|
DataTable resultTable = null;
|
|
DbCommand command = null;
|
|
DataTable schemaTable = 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);
|
|
}
|
|
|
|
command = connection.CreateCommand();
|
|
command.CommandText = sqlCommand;
|
|
command.CommandTimeout = System.Math.Max(command.CommandTimeout,180);
|
|
|
|
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);
|
|
resultTable.Locale = CultureInfo.InvariantCulture;
|
|
|
|
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 {
|
|
if (reader != null) {
|
|
reader.Dispose();
|
|
reader = null;
|
|
}
|
|
}
|
|
return resultTable;
|
|
}
|
|
|
|
private DataColumn[] FilterColumns(DataTable sourceTable, string[] hiddenColumnNames, DataColumnCollection destinationColumns) {
|
|
|
|
DataColumn newDestinationColumn;
|
|
int currentColumn;
|
|
DataColumn[] filteredSourceColumns = null;
|
|
|
|
int columnCount = 0;
|
|
foreach (DataColumn sourceColumn in sourceTable.Columns){
|
|
if (IncludeThisColumn(sourceColumn,hiddenColumnNames) == true) {
|
|
columnCount++;
|
|
}
|
|
}
|
|
|
|
if (columnCount == 0) {
|
|
throw ADP.NoColumns();
|
|
}
|
|
|
|
currentColumn= 0;
|
|
filteredSourceColumns = new DataColumn[columnCount];
|
|
|
|
foreach(DataColumn sourceColumn in sourceTable.Columns){
|
|
if (IncludeThisColumn(sourceColumn,hiddenColumnNames) == true) {
|
|
newDestinationColumn = new DataColumn(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 ((null == collectionNameColumn) || (typeof(System.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 (ADP.IsEmpty(candidateCollectionName)) {
|
|
throw ADP.InvalidXmlInvalidValue(DbMetaDataCollectionNames.MetaDataCollections,DbMetaDataColumnNames.CollectionName);
|
|
}
|
|
|
|
if (ADP.CompareInsensitiveInvariant(candidateCollectionName, collectionName)){
|
|
if (SupportedByCurrentVersion(row) == false) {
|
|
versionFailure = true;
|
|
}
|
|
else{
|
|
if (collectionName == candidateCollectionName) {
|
|
if (haveExactMatch == true) {
|
|
throw ADP.CollectionNameIsNotUnique(collectionName);
|
|
}
|
|
requestedCollectionRow = row;
|
|
exactCollectionName = candidateCollectionName;
|
|
haveExactMatch = true;
|
|
}
|
|
else {
|
|
// 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 == false) {
|
|
throw ADP.UndefinedCollection(collectionName);
|
|
}
|
|
else {
|
|
throw ADP.UnsupportedVersion(collectionName);
|
|
}
|
|
}
|
|
|
|
if ((haveExactMatch == false) && (haveMultipleInexactMatches == true)) {
|
|
throw ADP.AmbigousCollectionName(collectionName);
|
|
}
|
|
|
|
return requestedCollectionRow;
|
|
|
|
}
|
|
|
|
private void FixUpVersion(DataTable dataSourceInfoTable){
|
|
Debug.Assert(dataSourceInfoTable.TableName == DbMetaDataCollectionNames.DataSourceInformation);
|
|
DataColumn versionColumn = dataSourceInfoTable.Columns[_dataSourceProductVersion];
|
|
DataColumn normalizedVersionColumn = dataSourceInfoTable.Columns[_dataSourceProductVersionNormalized];
|
|
|
|
if ((versionColumn == null) || (normalizedVersionColumn == null)) {
|
|
throw ADP.MissingDataSourceInformationColumn();
|
|
}
|
|
|
|
if (dataSourceInfoTable.Rows.Count != 1) {
|
|
throw ADP.IncorrectNumberOfDataSourceInformationRows();
|
|
}
|
|
|
|
DataRow dataSourceInfoRow = dataSourceInfoTable.Rows[0];
|
|
|
|
dataSourceInfoRow[versionColumn] = _serverVersionString;
|
|
dataSourceInfoRow[normalizedVersionColumn] = _normalizedServerVersion;
|
|
dataSourceInfoRow.AcceptChanges();
|
|
}
|
|
|
|
|
|
private string GetParameterName(string neededCollectionName, int neededRestrictionNumber) {
|
|
|
|
DataTable restrictionsTable = null;
|
|
DataColumnCollection restrictionColumns = null;
|
|
DataColumn collectionName = null;
|
|
DataColumn parameterName = null;
|
|
DataColumn restrictionName = null;
|
|
DataColumn restrictionNumber = null;;
|
|
string result = null;
|
|
|
|
restrictionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.Restrictions];
|
|
if (restrictionsTable != null) {
|
|
restrictionColumns = restrictionsTable.Columns;
|
|
if (restrictionColumns != null) {
|
|
collectionName = restrictionColumns[_collectionName];
|
|
parameterName = restrictionColumns[_parameterName];
|
|
restrictionName = restrictionColumns[_restrictionName];
|
|
restrictionNumber = restrictionColumns[_restrictionNumber];
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
virtual public DataTable GetSchema(DbConnection connection, string collectionName, string[] restrictions) {
|
|
Debug.Assert (_metaDataCollectionsDataSet != null);
|
|
|
|
//
|
|
DataTable metaDataCollectionsTable = _metaDataCollectionsDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
|
|
DataColumn populationMechanismColumn = metaDataCollectionsTable.Columns[_populationMechanism];
|
|
DataColumn collectionNameColumn = metaDataCollectionsTable.Columns[DbMetaDataColumnNames.CollectionName];
|
|
DataRow requestedCollectionRow = null;
|
|
DataTable requestedSchema = null;
|
|
string[] hiddenColumns;
|
|
string exactCollectionName = null;
|
|
|
|
requestedCollectionRow = FindMetaDataCollectionRow(collectionName);
|
|
exactCollectionName = requestedCollectionRow[collectionNameColumn,DataRowVersion.Current] as string;
|
|
|
|
if (ADP.IsEmptyArray(restrictions) == false){
|
|
|
|
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
|
|
//
|
|
throw ADP.NotSupported();
|
|
}
|
|
}
|
|
}
|
|
|
|
string populationMechanism = requestedCollectionRow[populationMechanismColumn,DataRowVersion.Current] as string;
|
|
switch (populationMechanism) {
|
|
|
|
case _dataTable:
|
|
if (exactCollectionName == DbMetaDataCollectionNames.MetaDataCollections) {
|
|
hiddenColumns = new string[2];
|
|
hiddenColumns[0] = _populationMechanism;
|
|
hiddenColumns[1] = _populationString;
|
|
}
|
|
else {
|
|
hiddenColumns = null;
|
|
}
|
|
// none of the datatable collections support restrictions
|
|
if (ADP.IsEmptyArray(restrictions) == false){
|
|
throw ADP.TooManyRestrictions(exactCollectionName);
|
|
}
|
|
|
|
|
|
requestedSchema = CloneAndFilterCollection(exactCollectionName,hiddenColumns);
|
|
|
|
//
|
|
|
|
// for the data source infomation table we need to fix up the version columns at run time
|
|
// since the version is determined at run time
|
|
if (exactCollectionName == DbMetaDataCollectionNames.DataSourceInformation) {
|
|
FixUpVersion(requestedSchema);
|
|
}
|
|
break;
|
|
|
|
case _sqlCommand:
|
|
requestedSchema = ExecuteCommand(requestedCollectionRow,restrictions,connection);
|
|
break;
|
|
|
|
case _prepareCollection:
|
|
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 _minimumVersion:
|
|
case _maximumVersion:
|
|
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){
|
|
_metaDataCollectionsDataSet = new DataSet();
|
|
_metaDataCollectionsDataSet.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
|
_metaDataCollectionsDataSet.ReadXml(XmlStream);
|
|
}
|
|
|
|
virtual protected 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[_minimumVersion];
|
|
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 minmum version was ok what about the maximum version
|
|
if (result == true) {
|
|
versionColumn = tableColumns[_maximumVersion];
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|