|
//------------------------------------------------------------------------------
// <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;
}
}
}
|