2016-08-03 10:59:49 +00:00
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// <copyright file="OdbcMetaDataFactory.cs" company="Microsoft">
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
// </copyright>
|
|
|
|
//
|
2017-08-21 15:34:15 +00:00
|
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
|
|
// <owner current="true" primary="false">Microsoft</owner>
|
2016-08-03 10:59:49 +00:00
|
|
|
//
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
namespace System.Data.Odbc{
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Data;
|
|
|
|
using System.IO;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Data.Common;
|
|
|
|
using System.Xml;
|
|
|
|
using System.Xml.Schema;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Data.ProviderBase;
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
|
|
internal class OdbcMetaDataFactory : DbMetaDataFactory{
|
|
|
|
|
|
|
|
private struct SchemaFunctionName {
|
|
|
|
internal SchemaFunctionName(string schemaName, ODBC32.SQL_API odbcFunction) {
|
|
|
|
_schemaName = schemaName;
|
|
|
|
_odbcFunction = odbcFunction;
|
|
|
|
}
|
|
|
|
internal readonly string _schemaName;
|
|
|
|
internal readonly ODBC32.SQL_API _odbcFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
private const string _collectionName = "CollectionName";
|
|
|
|
private const string _populationMechanism = "PopulationMechanism";
|
|
|
|
private const string _prepareCollection = "PrepareCollection";
|
|
|
|
|
|
|
|
private readonly SchemaFunctionName[] _schemaMapping;
|
|
|
|
|
|
|
|
static internal readonly char[] KeywordSeparatorChar = new char[1] { ',' };
|
|
|
|
|
|
|
|
|
|
|
|
internal OdbcMetaDataFactory(Stream XMLStream,
|
|
|
|
string serverVersion,
|
|
|
|
string serverVersionNormalized,
|
|
|
|
OdbcConnection connection):
|
|
|
|
base(XMLStream, serverVersion, serverVersionNormalized) {
|
|
|
|
|
|
|
|
// set up the colletion name ODBC function mapping guid mapping
|
|
|
|
_schemaMapping = new SchemaFunctionName[] {
|
|
|
|
|
|
|
|
new SchemaFunctionName(DbMetaDataCollectionNames.DataTypes,ODBC32.SQL_API.SQLGETTYPEINFO),
|
|
|
|
new SchemaFunctionName(OdbcMetaDataCollectionNames.Columns,ODBC32.SQL_API.SQLCOLUMNS),
|
|
|
|
new SchemaFunctionName(OdbcMetaDataCollectionNames.Indexes,ODBC32.SQL_API.SQLSTATISTICS),
|
|
|
|
new SchemaFunctionName(OdbcMetaDataCollectionNames.Procedures,ODBC32.SQL_API.SQLPROCEDURES),
|
|
|
|
new SchemaFunctionName(OdbcMetaDataCollectionNames.ProcedureColumns,ODBC32.SQL_API.SQLPROCEDURECOLUMNS),
|
|
|
|
new SchemaFunctionName(OdbcMetaDataCollectionNames.ProcedureParameters,ODBC32.SQL_API.SQLPROCEDURECOLUMNS),
|
|
|
|
new SchemaFunctionName(OdbcMetaDataCollectionNames.Tables,ODBC32.SQL_API.SQLTABLES),
|
|
|
|
new SchemaFunctionName(OdbcMetaDataCollectionNames.Views,ODBC32.SQL_API.SQLTABLES)};
|
|
|
|
|
|
|
|
// verify the existance of the table in the data set
|
|
|
|
DataTable metaDataCollectionsTable = CollectionDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections];
|
|
|
|
if (metaDataCollectionsTable == null){
|
|
|
|
throw ADP.UnableToBuildCollection(DbMetaDataCollectionNames.MetaDataCollections);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy the table filtering out any rows that don't apply to the current version of the provider
|
|
|
|
metaDataCollectionsTable = CloneAndFilterCollection(DbMetaDataCollectionNames.MetaDataCollections, null);
|
|
|
|
|
|
|
|
// verify the existance of the table in the data set
|
|
|
|
DataTable restrictionsTable = CollectionDataSet.Tables[DbMetaDataCollectionNames.Restrictions];
|
|
|
|
if (restrictionsTable != null){
|
|
|
|
// copy the table filtering out any rows that don't apply to the current version of the provider
|
|
|
|
restrictionsTable= CloneAndFilterCollection(DbMetaDataCollectionNames.Restrictions, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// need to filter out any of the collections where
|
|
|
|
// 1) it is populated using prepare collection
|
|
|
|
// 2) it is in the collection to odbc function mapping above
|
|
|
|
// 3) the provider does not support the necessary odbc function
|
|
|
|
|
|
|
|
|
|
|
|
DataColumn populationMechanism = metaDataCollectionsTable.Columns[_populationMechanism];
|
|
|
|
DataColumn collectionName = metaDataCollectionsTable.Columns[_collectionName];
|
|
|
|
DataColumn restrictionCollectionName = null;
|
|
|
|
if (restrictionsTable != null){
|
|
|
|
restrictionCollectionName = restrictionsTable.Columns[_collectionName];
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (DataRow collection in metaDataCollectionsTable.Rows){
|
|
|
|
if ((string)collection[populationMechanism] == _prepareCollection) {
|
|
|
|
// is the collection in the mapping
|
|
|
|
int mapping = -1;
|
|
|
|
for (int i = 0; i < _schemaMapping.Length; i++){
|
|
|
|
if (_schemaMapping[i]._schemaName == (string)collection[collectionName]){
|
|
|
|
mapping = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// no go on to the next collection
|
|
|
|
if (mapping == -1) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// does the provider support the necessary odbc function
|
|
|
|
// if not delete the row from the table
|
|
|
|
if (connection.SQLGetFunctions(_schemaMapping[mapping]._odbcFunction) == false){
|
|
|
|
// but first delete any related restrictions
|
|
|
|
if (restrictionsTable != null) {
|
|
|
|
foreach (DataRow restriction in restrictionsTable.Rows) {
|
|
|
|
if ((string)collection[collectionName]==(string)restriction[restrictionCollectionName]) {
|
|
|
|
restriction.Delete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
restrictionsTable.AcceptChanges();
|
|
|
|
}
|
|
|
|
collection.Delete();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace the original table with the updated one
|
|
|
|
metaDataCollectionsTable.AcceptChanges();
|
|
|
|
CollectionDataSet.Tables.Remove(CollectionDataSet.Tables[DbMetaDataCollectionNames.MetaDataCollections]);
|
|
|
|
CollectionDataSet.Tables.Add(metaDataCollectionsTable);
|
|
|
|
|
|
|
|
if (restrictionsTable != null) {
|
|
|
|
CollectionDataSet.Tables.Remove(CollectionDataSet.Tables[DbMetaDataCollectionNames.Restrictions]);
|
|
|
|
CollectionDataSet.Tables.Add(restrictionsTable);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private object BooleanFromODBC(object odbcSource) {
|
|
|
|
|
|
|
|
if (odbcSource != DBNull.Value){
|
|
|
|
//convert to Int32 before doing the comparison
|
|
|
|
//some odbc drivers report the odbcSource value as unsigned, in which case we will
|
|
|
|
//have upgraded the type to Int32, and thus can't cast directly to short
|
|
|
|
if (Convert.ToInt32(odbcSource, null) == 0)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return DBNull.Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
private OdbcCommand GetCommand(OdbcConnection connection){
|
|
|
|
|
|
|
|
OdbcCommand command = connection.CreateCommand();
|
|
|
|
|
|
|
|
// You need to make sure you pick up the transaction from the connection,
|
|
|
|
// or odd things can happen...
|
|
|
|
command.Transaction = connection.LocalTransaction;
|
|
|
|
return command;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable DataTableFromDataReader(IDataReader reader, string tableName) {
|
|
|
|
|
|
|
|
// set up the column structure of the data table from the reader
|
|
|
|
object[] values;
|
|
|
|
DataTable resultTable = NewDataTableFromReader(reader, out values, tableName);
|
|
|
|
|
|
|
|
// populate the data table from the data reader
|
|
|
|
while (reader.Read()) {
|
|
|
|
reader.GetValues(values);
|
|
|
|
resultTable.Rows.Add(values);
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void DataTableFromDataReaderDataTypes(DataTable dataTypesTable, OdbcDataReader dataReader, OdbcConnection connection) {
|
|
|
|
|
|
|
|
DataTable schemaTable = null;
|
|
|
|
//
|
|
|
|
|
|
|
|
// Build a DataTable from the reader
|
|
|
|
schemaTable = dataReader.GetSchemaTable();
|
|
|
|
|
|
|
|
// vstfdevdiv:479715 Handle cases where reader is empty
|
|
|
|
if (null == schemaTable) {
|
|
|
|
throw ADP.OdbcNoTypesFromProvider();
|
|
|
|
}
|
|
|
|
|
|
|
|
object[] getTypeInfoValues = new object[schemaTable.Rows.Count];
|
|
|
|
DataRow dataTypesRow;
|
|
|
|
|
|
|
|
DataColumn typeNameColumn = dataTypesTable.Columns[DbMetaDataColumnNames.TypeName];
|
|
|
|
DataColumn providerDbTypeColumn = dataTypesTable.Columns[DbMetaDataColumnNames.ProviderDbType];
|
|
|
|
DataColumn columnSizeColumn = dataTypesTable.Columns[DbMetaDataColumnNames.ColumnSize];
|
|
|
|
DataColumn createParametersColumn = dataTypesTable.Columns[DbMetaDataColumnNames.CreateParameters];
|
|
|
|
DataColumn dataTypeColumn = dataTypesTable.Columns[DbMetaDataColumnNames.DataType];
|
|
|
|
DataColumn isAutoIncermentableColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsAutoIncrementable];
|
|
|
|
DataColumn isCaseSensitiveColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsCaseSensitive];
|
|
|
|
DataColumn isFixedLengthColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsFixedLength];
|
|
|
|
DataColumn isFixedPrecisionScaleColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsFixedPrecisionScale];
|
|
|
|
DataColumn isLongColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsLong];
|
|
|
|
DataColumn isNullableColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsNullable];
|
|
|
|
DataColumn isSearchableColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsSearchable];
|
|
|
|
DataColumn isSearchableWithLikeColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsSearchableWithLike];
|
|
|
|
DataColumn isUnsignedColumn = dataTypesTable.Columns[DbMetaDataColumnNames.IsUnsigned];
|
|
|
|
DataColumn maximumScaleColumn = dataTypesTable.Columns[DbMetaDataColumnNames.MaximumScale];
|
|
|
|
DataColumn minimumScaleColumn = dataTypesTable.Columns[DbMetaDataColumnNames.MinimumScale];
|
|
|
|
DataColumn literalPrefixColumn = dataTypesTable.Columns[DbMetaDataColumnNames.LiteralPrefix];
|
|
|
|
DataColumn literalSuffixColumn = dataTypesTable.Columns[DbMetaDataColumnNames.LiteralSuffix];
|
|
|
|
DataColumn SQLTypeNameColumn = dataTypesTable.Columns[OdbcMetaDataColumnNames.SQLType];
|
|
|
|
|
|
|
|
|
|
|
|
const int indexTYPE_NAME = 0;
|
|
|
|
const int indexDATA_TYPE = 1;
|
|
|
|
const int indexCOLUMN_SIZE = 2;
|
|
|
|
const int indexCREATE_PARAMS = 5;
|
|
|
|
const int indexAUTO_UNIQUE_VALUE = 11;
|
|
|
|
const int indexCASE_SENSITIVE = 7;
|
|
|
|
const int indexFIXED_PREC_SCALE = 10;
|
|
|
|
const int indexNULLABLE = 6;
|
|
|
|
const int indexSEARCHABLE = 8;
|
|
|
|
const int indexUNSIGNED_ATTRIBUTE = 9;
|
|
|
|
const int indexMAXIMUM_SCALE = 14;
|
|
|
|
const int indexMINIMUM_SCALE = 13;
|
|
|
|
const int indexLITERAL_PREFIX = 3;
|
|
|
|
const int indexLITERAL_SUFFIX = 4;
|
|
|
|
|
|
|
|
const int SQL_DATE_V2 = 9;
|
|
|
|
const int SQL_TIME_V2 = 10;
|
|
|
|
|
|
|
|
TypeMap typeMap ;
|
|
|
|
|
|
|
|
|
|
|
|
while (dataReader.Read()) {
|
|
|
|
dataReader.GetValues(getTypeInfoValues);
|
|
|
|
dataTypesRow = dataTypesTable.NewRow();
|
|
|
|
|
|
|
|
ODBC32.SQL_TYPE sqlType;
|
|
|
|
|
|
|
|
dataTypesRow[typeNameColumn] = getTypeInfoValues[indexTYPE_NAME];
|
|
|
|
dataTypesRow[SQLTypeNameColumn] = getTypeInfoValues[indexDATA_TYPE];
|
|
|
|
|
|
|
|
sqlType = (ODBC32.SQL_TYPE)(Int32) Convert.ChangeType(getTypeInfoValues[indexDATA_TYPE],
|
|
|
|
typeof(Int32),
|
|
|
|
(System.IFormatProvider)null);
|
|
|
|
// if the driver is pre version 3 and it returned the v2 SQL_DATE or SQL_TIME types they need
|
|
|
|
// to be mapped to thier v3 equlivants
|
|
|
|
if (connection.IsV3Driver == false) {
|
|
|
|
if ((int)sqlType == SQL_DATE_V2) {
|
|
|
|
sqlType = ODBC32.SQL_TYPE.TYPE_DATE;
|
|
|
|
}
|
|
|
|
else if ((int)sqlType == SQL_TIME_V2) {
|
|
|
|
sqlType = ODBC32.SQL_TYPE.TYPE_TIME;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
typeMap = TypeMap.FromSqlType(sqlType);
|
|
|
|
}
|
|
|
|
// FromSqlType will throw an argument exception if it does not recognize the SqlType.
|
|
|
|
// This is not an error since the GetTypeInfo DATA_TYPE may be a SQL data type or a driver specific
|
|
|
|
// type. If there is no TypeMap for the type its not an error but it will degrade our level of
|
|
|
|
// understanding of/ support for the type.
|
|
|
|
catch (ArgumentException) {
|
|
|
|
typeMap = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we have a type map we can determine the dbType and the CLR type if not leave them null
|
|
|
|
if (typeMap != null) {
|
|
|
|
dataTypesRow[providerDbTypeColumn] = typeMap._odbcType;
|
|
|
|
dataTypesRow[dataTypeColumn] = typeMap._type.FullName;
|
|
|
|
// setting isLong and isFixedLength only if we have a type map because for provider
|
|
|
|
// specific types we have no idea what the types attributes are if GetTypeInfo did not
|
|
|
|
// tell us
|
|
|
|
switch (sqlType) {
|
|
|
|
|
|
|
|
case ODBC32.SQL_TYPE.LONGVARCHAR:
|
|
|
|
case ODBC32.SQL_TYPE.WLONGVARCHAR:
|
|
|
|
case ODBC32.SQL_TYPE.LONGVARBINARY:
|
|
|
|
case ODBC32.SQL_TYPE.SS_XML:
|
|
|
|
dataTypesRow[isLongColumn] = true;
|
|
|
|
dataTypesRow[isFixedLengthColumn] = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODBC32.SQL_TYPE.VARCHAR:
|
|
|
|
case ODBC32.SQL_TYPE.WVARCHAR:
|
|
|
|
case ODBC32.SQL_TYPE.VARBINARY:
|
|
|
|
dataTypesRow[isLongColumn] = false;
|
|
|
|
dataTypesRow[isFixedLengthColumn] = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODBC32.SQL_TYPE.CHAR:
|
|
|
|
case ODBC32.SQL_TYPE.WCHAR:
|
|
|
|
case ODBC32.SQL_TYPE.DECIMAL:
|
|
|
|
case ODBC32.SQL_TYPE.NUMERIC:
|
|
|
|
case ODBC32.SQL_TYPE.SMALLINT:
|
|
|
|
case ODBC32.SQL_TYPE.INTEGER:
|
|
|
|
case ODBC32.SQL_TYPE.REAL:
|
|
|
|
case ODBC32.SQL_TYPE.FLOAT:
|
|
|
|
case ODBC32.SQL_TYPE.DOUBLE:
|
|
|
|
case ODBC32.SQL_TYPE.BIT:
|
|
|
|
case ODBC32.SQL_TYPE.TINYINT:
|
|
|
|
case ODBC32.SQL_TYPE.BIGINT:
|
|
|
|
case ODBC32.SQL_TYPE.TYPE_DATE:
|
|
|
|
case ODBC32.SQL_TYPE.TYPE_TIME:
|
|
|
|
case ODBC32.SQL_TYPE.TIMESTAMP:
|
|
|
|
case ODBC32.SQL_TYPE.TYPE_TIMESTAMP:
|
|
|
|
case ODBC32.SQL_TYPE.GUID:
|
|
|
|
case ODBC32.SQL_TYPE.SS_VARIANT:
|
|
|
|
case ODBC32.SQL_TYPE.SS_UTCDATETIME:
|
|
|
|
case ODBC32.SQL_TYPE.SS_TIME_EX:
|
|
|
|
case ODBC32.SQL_TYPE.BINARY:
|
|
|
|
dataTypesRow[isLongColumn] = false;
|
|
|
|
dataTypesRow[isFixedLengthColumn] = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODBC32.SQL_TYPE.SS_UDT:
|
|
|
|
default:
|
|
|
|
// for User defined types don't know if its long or or if it is
|
|
|
|
// varaible length or not so leave the fields null
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dataTypesRow[columnSizeColumn] = getTypeInfoValues[indexCOLUMN_SIZE];
|
|
|
|
dataTypesRow[createParametersColumn] = getTypeInfoValues[indexCREATE_PARAMS];
|
|
|
|
|
|
|
|
if ((getTypeInfoValues[indexAUTO_UNIQUE_VALUE] == DBNull.Value) ||
|
|
|
|
(Convert.ToInt16(getTypeInfoValues[indexAUTO_UNIQUE_VALUE], null) == 0)) {
|
|
|
|
dataTypesRow[isAutoIncermentableColumn] = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dataTypesRow[isAutoIncermentableColumn] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
dataTypesRow[isCaseSensitiveColumn] = BooleanFromODBC(getTypeInfoValues[indexCASE_SENSITIVE]);
|
|
|
|
dataTypesRow[isFixedPrecisionScaleColumn] = BooleanFromODBC(getTypeInfoValues[indexFIXED_PREC_SCALE]);
|
|
|
|
|
|
|
|
if (getTypeInfoValues[indexNULLABLE] != DBNull.Value) {
|
|
|
|
//Use Convert.ToInt16 instead of direct cast to short because the value will be Int32 in some cases
|
|
|
|
switch ((ODBC32.SQL_NULLABILITY)Convert.ToInt16(getTypeInfoValues[indexNULLABLE], null)) {
|
|
|
|
|
|
|
|
case ODBC32.SQL_NULLABILITY.NO_NULLS:
|
|
|
|
dataTypesRow[isNullableColumn] = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODBC32.SQL_NULLABILITY.NULLABLE:
|
|
|
|
dataTypesRow[isNullableColumn] = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ODBC32.SQL_NULLABILITY.UNKNOWN:
|
|
|
|
dataTypesRow[isNullableColumn] = DBNull.Value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( DBNull.Value != getTypeInfoValues[indexSEARCHABLE]){
|
|
|
|
|
|
|
|
//Use Convert.ToInt16 instead of direct cast to short because the value will be Int32 in some cases
|
|
|
|
Int16 searchableValue = Convert.ToInt16(getTypeInfoValues[indexSEARCHABLE], null);
|
|
|
|
switch (searchableValue){
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_SEARCHABLE.UNSEARCHABLE:
|
|
|
|
dataTypesRow[isSearchableColumn] = false;
|
|
|
|
dataTypesRow[isSearchableWithLikeColumn] = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_SEARCHABLE.LIKE_ONLY:
|
|
|
|
dataTypesRow[isSearchableColumn] = false;
|
|
|
|
dataTypesRow[isSearchableWithLikeColumn] = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_SEARCHABLE.ALL_EXCEPT_LIKE:
|
|
|
|
dataTypesRow[isSearchableColumn] = true;
|
|
|
|
dataTypesRow[isSearchableWithLikeColumn] = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_SEARCHABLE.SEARCHABLE:
|
|
|
|
dataTypesRow[isSearchableColumn] = true;
|
|
|
|
dataTypesRow[isSearchableWithLikeColumn] = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dataTypesRow[isUnsignedColumn] = BooleanFromODBC(getTypeInfoValues[indexUNSIGNED_ATTRIBUTE]);
|
|
|
|
|
|
|
|
//For assignment to the DataSet, don't cast the data types -- let the DataSet take care of any conversion
|
|
|
|
if (getTypeInfoValues[indexMAXIMUM_SCALE] != DBNull.Value ) {
|
|
|
|
dataTypesRow[maximumScaleColumn] = getTypeInfoValues[indexMAXIMUM_SCALE];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getTypeInfoValues[indexMINIMUM_SCALE] != DBNull.Value ) {
|
|
|
|
dataTypesRow[minimumScaleColumn] = getTypeInfoValues[indexMINIMUM_SCALE];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getTypeInfoValues[indexLITERAL_PREFIX] != DBNull.Value ) {
|
|
|
|
dataTypesRow[literalPrefixColumn] = getTypeInfoValues[indexLITERAL_PREFIX];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getTypeInfoValues[indexLITERAL_SUFFIX] != DBNull.Value ) {
|
|
|
|
dataTypesRow[literalSuffixColumn] = getTypeInfoValues[indexLITERAL_SUFFIX];
|
|
|
|
}
|
|
|
|
|
|
|
|
dataTypesTable.Rows.Add(dataTypesRow);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable DataTableFromDataReaderIndex(IDataReader reader,
|
|
|
|
string tableName,
|
|
|
|
string restrictionIndexName) {
|
|
|
|
|
|
|
|
// set up the column structure of the data table from the reader
|
|
|
|
object[] values;
|
|
|
|
DataTable resultTable = NewDataTableFromReader(reader, out values, tableName);
|
|
|
|
|
|
|
|
// populate the data table from the data reader
|
|
|
|
int positionOfType = 6;
|
|
|
|
int positionOfIndexName = 5;
|
|
|
|
while (reader.Read()) {
|
|
|
|
reader.GetValues(values);
|
|
|
|
if (IncludeIndexRow(values[positionOfIndexName],
|
|
|
|
restrictionIndexName,
|
|
|
|
Convert.ToInt16(values[positionOfType], null)) == true){
|
|
|
|
resultTable.Rows.Add(values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable DataTableFromDataReaderProcedureColumns(IDataReader reader, string tableName,Boolean isColumn) {
|
|
|
|
|
|
|
|
// set up the column structure of the data table from the reader
|
|
|
|
object[] values;
|
|
|
|
DataTable resultTable = NewDataTableFromReader(reader, out values, tableName);
|
|
|
|
|
|
|
|
// populate the data table from the data reader
|
|
|
|
int positionOfColumnType = 4;
|
|
|
|
while (reader.Read()) {
|
|
|
|
reader.GetValues(values);
|
|
|
|
// the column type should always be short but need to check just in case
|
|
|
|
if (values[positionOfColumnType].GetType() == typeof(short)) {
|
|
|
|
if ((((short)values[positionOfColumnType] == ODBC32.SQL_RESULT_COL) && (isColumn == true)) ||
|
|
|
|
(((short)values[positionOfColumnType] != ODBC32.SQL_RESULT_COL) && (isColumn == false))) {
|
|
|
|
resultTable.Rows.Add(values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable DataTableFromDataReaderProcedures(IDataReader reader, string tableName,Int16 procedureType) {
|
|
|
|
|
|
|
|
// Build a DataTable from the reader
|
|
|
|
|
|
|
|
// set up the column structure of the data table from the reader
|
|
|
|
object[] values;
|
|
|
|
DataTable resultTable = NewDataTableFromReader(reader, out values, tableName);
|
|
|
|
|
|
|
|
// populate the data table from the data reader
|
|
|
|
int positionOfProcedureType = 7;
|
|
|
|
while (reader.Read()) {
|
|
|
|
reader.GetValues(values);
|
|
|
|
// the column type should always be short but need to check just in case its null
|
|
|
|
if (values[positionOfProcedureType].GetType() == typeof(short)) {
|
|
|
|
if ((short)values[positionOfProcedureType] == procedureType) {
|
|
|
|
resultTable.Rows.Add(values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void FillOutRestrictions(int restrictionsCount, string[] restrictions, object[] allRestrictions, string collectionName) {
|
|
|
|
|
|
|
|
Debug.Assert(allRestrictions.Length >= restrictionsCount);
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
// if we have restrictions put them in the restrictions array
|
|
|
|
if (restrictions != null) {
|
|
|
|
|
|
|
|
if (restrictions.Length > restrictionsCount) {
|
|
|
|
throw ADP.TooManyRestrictions(collectionName);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i < restrictions.Length; i++) {
|
|
|
|
if (restrictions[i] != null) {
|
|
|
|
allRestrictions[i] = restrictions[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// initalize the rest to no restrictions
|
|
|
|
for (; i < restrictionsCount; i++) {
|
|
|
|
allRestrictions[i] = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private DataTable GetColumnsCollection(String[] restrictions, OdbcConnection connection){
|
|
|
|
|
|
|
|
OdbcCommand command = null;
|
|
|
|
OdbcDataReader dataReader =null;
|
|
|
|
DataTable resultTable = null;
|
|
|
|
const int columnsRestrictionsCount = 4;
|
|
|
|
|
|
|
|
try {
|
|
|
|
command = GetCommand(connection);
|
|
|
|
String[] allRestrictions = new string[columnsRestrictionsCount];
|
|
|
|
FillOutRestrictions(columnsRestrictionsCount,restrictions,allRestrictions,OdbcMetaDataCollectionNames.Columns);
|
|
|
|
|
|
|
|
dataReader = command.ExecuteReaderFromSQLMethod(allRestrictions, ODBC32.SQL_API.SQLCOLUMNS);
|
|
|
|
|
|
|
|
resultTable = DataTableFromDataReader(dataReader, OdbcMetaDataCollectionNames.Columns);
|
|
|
|
}
|
|
|
|
|
|
|
|
finally {
|
|
|
|
if (dataReader != null) {
|
|
|
|
dataReader.Dispose();
|
|
|
|
};
|
|
|
|
if (command != null) {
|
|
|
|
command.Dispose();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private DataTable GetDataSourceInformationCollection(string [] restrictions,
|
|
|
|
OdbcConnection connection){
|
|
|
|
|
|
|
|
if (ADP.IsEmptyArray(restrictions) == false) {
|
|
|
|
throw ADP.TooManyRestrictions(DbMetaDataCollectionNames.DataSourceInformation);
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify that the data source information table is in the data set
|
|
|
|
DataTable dataSourceInformationTable = CollectionDataSet.Tables[DbMetaDataCollectionNames.DataSourceInformation];
|
|
|
|
if (dataSourceInformationTable == null){
|
|
|
|
throw ADP.UnableToBuildCollection(DbMetaDataCollectionNames.DataSourceInformation);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy the table filtering out any rows that don't apply to the current version of the provider
|
|
|
|
dataSourceInformationTable = CloneAndFilterCollection(DbMetaDataCollectionNames.DataSourceInformation, null);
|
|
|
|
|
|
|
|
// after filtering there better be just one row
|
|
|
|
if (dataSourceInformationTable.Rows.Count != 1){
|
|
|
|
throw ADP.IncorrectNumberOfDataSourceInformationRows();
|
|
|
|
}
|
|
|
|
DataRow dataSourceInformation = dataSourceInformationTable.Rows[0];
|
|
|
|
|
|
|
|
string stringValue;
|
|
|
|
Int16 int16Value;
|
|
|
|
Int32 int32Value;
|
|
|
|
ODBC32.RetCode retcode;
|
|
|
|
|
|
|
|
// update the catalog separator
|
|
|
|
stringValue = connection.GetInfoStringUnhandled(ODBC32.SQL_INFO.CATALOG_NAME_SEPARATOR);
|
|
|
|
if (!ADP.IsEmpty(stringValue)) {
|
|
|
|
StringBuilder patternEscaped = new StringBuilder();
|
|
|
|
ADP.EscapeSpecialCharacters(stringValue,patternEscaped);
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern] = patternEscaped.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the DBMS Name
|
|
|
|
stringValue = connection.GetInfoStringUnhandled(ODBC32.SQL_INFO.DBMS_NAME);
|
|
|
|
if (stringValue != null) {
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.DataSourceProductName] = stringValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// update the server version strings
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.DataSourceProductVersion] = ServerVersion;
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.DataSourceProductVersionNormalized] = ServerVersionNormalized;
|
|
|
|
|
|
|
|
|
|
|
|
// values that are the same for all ODBC drivers. See bug 105333
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.ParameterMarkerFormat] = "?";
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.ParameterMarkerPattern] = "\\?";
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.ParameterNameMaxLength] = 0;
|
|
|
|
|
|
|
|
// determine the supportedJoinOperators
|
|
|
|
// leave the column null if the GetInfo fails. There is no explicit value for
|
|
|
|
// unknown.
|
|
|
|
if (connection.IsV3Driver) {
|
|
|
|
|
|
|
|
retcode = connection.GetInfoInt32Unhandled(ODBC32.SQL_INFO.SQL_OJ_CAPABILITIES_30,out int32Value);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
retcode = connection.GetInfoInt32Unhandled(ODBC32.SQL_INFO.SQL_OJ_CAPABILITIES_20, out int32Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)){
|
|
|
|
|
|
|
|
Common. SupportedJoinOperators supportedJoinOperators = Common.SupportedJoinOperators.None;
|
|
|
|
if ((int32Value & (Int32)ODBC32.SQL_OJ_CAPABILITIES.LEFT) != 0){
|
|
|
|
supportedJoinOperators = supportedJoinOperators | Common.SupportedJoinOperators.LeftOuter;
|
|
|
|
}
|
|
|
|
if ((int32Value & (Int32)ODBC32.SQL_OJ_CAPABILITIES.RIGHT) != 0){
|
|
|
|
supportedJoinOperators = supportedJoinOperators | Common.SupportedJoinOperators.RightOuter;
|
|
|
|
}
|
|
|
|
if ((int32Value & (Int32)ODBC32.SQL_OJ_CAPABILITIES.FULL) != 0){
|
|
|
|
supportedJoinOperators = supportedJoinOperators | Common.SupportedJoinOperators.FullOuter;
|
|
|
|
}
|
|
|
|
if ((int32Value & (Int32)ODBC32.SQL_OJ_CAPABILITIES.INNER) != 0){
|
|
|
|
supportedJoinOperators = supportedJoinOperators | Common.SupportedJoinOperators.Inner;
|
|
|
|
}
|
|
|
|
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.SupportedJoinOperators] = supportedJoinOperators;
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine the GroupByBehavior
|
|
|
|
retcode = connection.GetInfoInt16Unhandled(ODBC32.SQL_INFO.GROUP_BY, out int16Value);
|
|
|
|
Common.GroupByBehavior groupByBehavior = Common.GroupByBehavior.Unknown;
|
|
|
|
|
|
|
|
if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)){
|
|
|
|
|
|
|
|
switch (int16Value) {
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_GROUP_BY.NOT_SUPPORTED:
|
|
|
|
groupByBehavior = Common.GroupByBehavior.NotSupported;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_GROUP_BY.GROUP_BY_EQUALS_SELECT:
|
|
|
|
groupByBehavior = Common.GroupByBehavior.ExactMatch;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_GROUP_BY.GROUP_BY_CONTAINS_SELECT:
|
|
|
|
groupByBehavior = Common.GroupByBehavior.MustContainAll;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_GROUP_BY.NO_RELATION:
|
|
|
|
groupByBehavior = Common.GroupByBehavior.Unrelated;
|
|
|
|
break;
|
|
|
|
/* COLLATE is new in ODBC 3.0 and GroupByBehavior does not have a value for it.
|
|
|
|
case ODBC32.SQL_GROUP_BY.COLLATE:
|
|
|
|
groupByBehavior = Common.GroupByBehavior.Unknown;
|
|
|
|
break;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.GroupByBehavior] = groupByBehavior;
|
|
|
|
|
|
|
|
// determine the identifier case
|
|
|
|
retcode = connection.GetInfoInt16Unhandled(ODBC32.SQL_INFO.IDENTIFIER_CASE, out int16Value);
|
|
|
|
Common.IdentifierCase identifierCase = Common.IdentifierCase.Unknown;
|
|
|
|
|
|
|
|
if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)){
|
|
|
|
|
|
|
|
switch (int16Value) {
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.SENSITIVE:
|
|
|
|
identifierCase = Common.IdentifierCase.Sensitive;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.UPPER:
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.LOWER:
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.MIXED:
|
|
|
|
identifierCase = Common.IdentifierCase.Insensitive;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.IdentifierCase] = identifierCase;
|
|
|
|
|
|
|
|
// OrderByColumnsInSelect
|
|
|
|
stringValue = connection.GetInfoStringUnhandled(ODBC32.SQL_INFO.ORDER_BY_COLUMNS_IN_SELECT);
|
|
|
|
if (stringValue != null) {
|
|
|
|
if (stringValue == "Y"){
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.OrderByColumnsInSelect] = true;
|
|
|
|
}
|
|
|
|
else if (stringValue == "N") {
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.OrderByColumnsInSelect] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// build the QuotedIdentifierPattern using the quote prefix and suffix from the provider and
|
|
|
|
// assuming that the quote suffix is escaped via repetion (i.e " becomes "")
|
|
|
|
stringValue = connection.QuoteChar(ADP.GetSchema);
|
|
|
|
|
|
|
|
if (stringValue != null){
|
|
|
|
|
|
|
|
// by spec a blank identifier quote char indicates that the provider does not suppport
|
|
|
|
// quoted identifiers
|
|
|
|
if (stringValue != " ") {
|
|
|
|
|
|
|
|
// only know how to build the parttern if the quote characters is 1 character
|
|
|
|
// in all other cases just leave the field null
|
|
|
|
if (stringValue.Length == 1) {
|
|
|
|
StringBuilder scratchStringBuilder = new StringBuilder();
|
|
|
|
ADP.EscapeSpecialCharacters(stringValue,scratchStringBuilder );
|
|
|
|
string escapedQuoteSuffixString = scratchStringBuilder.ToString();
|
|
|
|
scratchStringBuilder.Length = 0;
|
|
|
|
|
|
|
|
ADP.EscapeSpecialCharacters(stringValue, scratchStringBuilder);
|
|
|
|
scratchStringBuilder.Append("(([^");
|
|
|
|
scratchStringBuilder.Append(escapedQuoteSuffixString);
|
|
|
|
scratchStringBuilder.Append("]|");
|
|
|
|
scratchStringBuilder.Append(escapedQuoteSuffixString);
|
|
|
|
scratchStringBuilder.Append(escapedQuoteSuffixString);
|
|
|
|
scratchStringBuilder.Append(")*)");
|
|
|
|
scratchStringBuilder.Append(escapedQuoteSuffixString);
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.QuotedIdentifierPattern] = scratchStringBuilder.ToString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine the quoted identifier case
|
|
|
|
retcode = connection.GetInfoInt16Unhandled(ODBC32.SQL_INFO.QUOTED_IDENTIFIER_CASE, out int16Value);
|
|
|
|
Common.IdentifierCase quotedIdentifierCase = Common.IdentifierCase.Unknown;
|
|
|
|
|
|
|
|
if ((retcode == ODBC32.RetCode.SUCCESS) || (retcode == ODBC32.RetCode.SUCCESS_WITH_INFO)){
|
|
|
|
|
|
|
|
switch (int16Value) {
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.SENSITIVE:
|
|
|
|
quotedIdentifierCase = Common.IdentifierCase.Sensitive;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.UPPER:
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.LOWER:
|
|
|
|
case (Int16)ODBC32.SQL_IDENTIFIER_CASE.MIXED:
|
|
|
|
quotedIdentifierCase = Common.IdentifierCase.Insensitive;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dataSourceInformation[DbMetaDataColumnNames.QuotedIdentifierCase] = quotedIdentifierCase;
|
|
|
|
|
|
|
|
dataSourceInformationTable.AcceptChanges();
|
|
|
|
|
|
|
|
return dataSourceInformationTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private DataTable GetDataTypesCollection(String[] restrictions, OdbcConnection connection){
|
|
|
|
|
|
|
|
if (ADP.IsEmptyArray(restrictions) == false){
|
|
|
|
throw ADP.TooManyRestrictions(DbMetaDataCollectionNames.DataTypes);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// verify the existance of the table in the data set
|
|
|
|
DataTable dataTypesTable = CollectionDataSet.Tables[DbMetaDataCollectionNames.DataTypes];
|
|
|
|
if (dataTypesTable == null){
|
|
|
|
throw ADP.UnableToBuildCollection(DbMetaDataCollectionNames.DataTypes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy the data table it
|
|
|
|
dataTypesTable = CloneAndFilterCollection(DbMetaDataCollectionNames.DataTypes, null);
|
|
|
|
|
|
|
|
OdbcCommand command = null;
|
|
|
|
OdbcDataReader dataReader =null;
|
|
|
|
object[] allArguments = new object[1];
|
|
|
|
allArguments[0] = ODBC32.SQL_ALL_TYPES;
|
|
|
|
|
|
|
|
try {
|
|
|
|
command = GetCommand(connection);
|
|
|
|
|
|
|
|
|
|
|
|
dataReader = command.ExecuteReaderFromSQLMethod(allArguments, ODBC32.SQL_API.SQLGETTYPEINFO);
|
|
|
|
|
|
|
|
DataTableFromDataReaderDataTypes(dataTypesTable,dataReader,connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
finally {
|
|
|
|
if (dataReader != null) {
|
|
|
|
dataReader.Dispose();
|
|
|
|
};
|
|
|
|
if (command != null) {
|
|
|
|
command.Dispose();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
dataTypesTable.AcceptChanges();
|
|
|
|
return dataTypesTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable GetIndexCollection(String[] restrictions, OdbcConnection connection){
|
|
|
|
|
|
|
|
OdbcCommand command = null;
|
|
|
|
OdbcDataReader dataReader =null;
|
|
|
|
DataTable resultTable = null;
|
|
|
|
const int nativeRestrictionsCount = 5;
|
|
|
|
const int indexRestrictionsCount = 4;
|
|
|
|
const int indexOfTableName = 2;
|
|
|
|
const int indexOfIndexName = 3;
|
|
|
|
|
|
|
|
try {
|
|
|
|
command = GetCommand(connection);
|
|
|
|
object[] allRestrictions = new object[nativeRestrictionsCount];
|
|
|
|
FillOutRestrictions(indexRestrictionsCount,restrictions,allRestrictions,OdbcMetaDataCollectionNames.Indexes);
|
|
|
|
|
|
|
|
if (allRestrictions[indexOfTableName] == null) {
|
|
|
|
throw ODBC.GetSchemaRestrictionRequired();
|
|
|
|
}
|
|
|
|
|
|
|
|
allRestrictions[3] = (Int16)ODBC32.SQL_INDEX.ALL;
|
|
|
|
allRestrictions[4] = (Int16)ODBC32.SQL_STATISTICS_RESERVED.ENSURE;
|
|
|
|
|
|
|
|
dataReader = command.ExecuteReaderFromSQLMethod(allRestrictions, ODBC32.SQL_API.SQLSTATISTICS);
|
|
|
|
|
|
|
|
string indexName = null;
|
|
|
|
if (restrictions != null) {
|
|
|
|
|
|
|
|
if (restrictions.Length >= indexOfIndexName+1) {
|
|
|
|
indexName = restrictions[indexOfIndexName];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
resultTable = DataTableFromDataReaderIndex(dataReader,
|
|
|
|
OdbcMetaDataCollectionNames.Indexes,
|
|
|
|
indexName);
|
|
|
|
}
|
|
|
|
|
|
|
|
finally {
|
|
|
|
if (dataReader != null) {
|
|
|
|
dataReader.Dispose();
|
|
|
|
};
|
|
|
|
if (command != null) {
|
|
|
|
command.Dispose();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable GetProcedureColumnsCollection(String[] restrictions, OdbcConnection connection,Boolean isColumns){
|
|
|
|
|
|
|
|
OdbcCommand command = null;
|
|
|
|
OdbcDataReader dataReader =null;
|
|
|
|
DataTable resultTable = null;
|
|
|
|
const int procedureColumnsRestrictionsCount = 4;
|
|
|
|
|
|
|
|
try {
|
|
|
|
command = GetCommand(connection);
|
|
|
|
String[] allRestrictions = new string[procedureColumnsRestrictionsCount];
|
|
|
|
FillOutRestrictions(procedureColumnsRestrictionsCount,restrictions, allRestrictions,OdbcMetaDataCollectionNames.Columns);
|
|
|
|
|
|
|
|
dataReader = command.ExecuteReaderFromSQLMethod(allRestrictions,ODBC32.SQL_API.SQLPROCEDURECOLUMNS);
|
|
|
|
|
|
|
|
string collectionName;
|
|
|
|
if (isColumns == true) {
|
|
|
|
collectionName = OdbcMetaDataCollectionNames.ProcedureColumns;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
collectionName = OdbcMetaDataCollectionNames.ProcedureParameters;
|
|
|
|
}
|
|
|
|
resultTable = DataTableFromDataReaderProcedureColumns(dataReader,
|
|
|
|
collectionName,
|
|
|
|
isColumns);
|
|
|
|
}
|
|
|
|
|
|
|
|
finally {
|
|
|
|
if (dataReader != null) {
|
|
|
|
dataReader.Dispose();
|
|
|
|
};
|
|
|
|
if (command != null) {
|
|
|
|
command.Dispose();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable GetProceduresCollection(String[] restrictions, OdbcConnection connection){
|
|
|
|
|
|
|
|
OdbcCommand command = null;
|
|
|
|
OdbcDataReader dataReader =null;
|
|
|
|
DataTable resultTable = null;
|
|
|
|
const int columnsRestrictionsCount = 4;
|
|
|
|
const int indexOfProcedureType = 3;
|
|
|
|
|
|
|
|
try {
|
|
|
|
command = GetCommand(connection);
|
|
|
|
String[] allRestrictions = new string[columnsRestrictionsCount];
|
|
|
|
FillOutRestrictions(columnsRestrictionsCount,restrictions, allRestrictions,OdbcMetaDataCollectionNames.Procedures);
|
|
|
|
|
|
|
|
|
|
|
|
dataReader = command.ExecuteReaderFromSQLMethod(allRestrictions,ODBC32.SQL_API.SQLPROCEDURES);
|
|
|
|
|
|
|
|
if (allRestrictions[indexOfProcedureType] == null) {
|
|
|
|
resultTable = DataTableFromDataReader(dataReader, OdbcMetaDataCollectionNames.Procedures);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Int16 procedureType;
|
|
|
|
if ((restrictions[indexOfProcedureType] == "SQL_PT_UNKNOWN") ||
|
|
|
|
(restrictions[indexOfProcedureType] == "0" /*ODBC32.SQL_PROCEDURETYPE.UNKNOWN*/)) {
|
|
|
|
procedureType = (Int16)ODBC32.SQL_PROCEDURETYPE.UNKNOWN;
|
|
|
|
}
|
|
|
|
else if ((restrictions[indexOfProcedureType] == "SQL_PT_PROCEDURE") ||
|
|
|
|
(restrictions[indexOfProcedureType] == "1" /*ODBC32.SQL_PROCEDURETYPE.PROCEDURE*/)) {
|
|
|
|
procedureType = (Int16)ODBC32.SQL_PROCEDURETYPE.PROCEDURE;
|
|
|
|
}
|
|
|
|
else if ((restrictions[indexOfProcedureType] == "SQL_PT_FUNCTION") ||
|
|
|
|
(restrictions[indexOfProcedureType] == "2" /*ODBC32.SQL_PROCEDURETYPE.FUNCTION*/)) {
|
|
|
|
procedureType = (Int16)ODBC32.SQL_PROCEDURETYPE.FUNCTION;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
throw ADP.InvalidRestrictionValue(OdbcMetaDataCollectionNames.Procedures,"PROCEDURE_TYPE",restrictions[indexOfProcedureType]);
|
|
|
|
}
|
|
|
|
|
|
|
|
resultTable = DataTableFromDataReaderProcedures(dataReader, OdbcMetaDataCollectionNames.Procedures,procedureType);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
finally {
|
|
|
|
if (dataReader != null) {
|
|
|
|
dataReader.Dispose();
|
|
|
|
};
|
|
|
|
if (command != null) {
|
|
|
|
command.Dispose();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable GetReservedWordsCollection(string[] restrictions, OdbcConnection connection){
|
|
|
|
|
|
|
|
if (ADP.IsEmptyArray(restrictions) == false){
|
|
|
|
throw ADP.TooManyRestrictions(DbMetaDataCollectionNames.ReservedWords);
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify the existance of the table in the data set
|
|
|
|
DataTable reservedWordsTable = CollectionDataSet.Tables[DbMetaDataCollectionNames.ReservedWords];
|
|
|
|
if (reservedWordsTable == null){
|
|
|
|
throw ADP.UnableToBuildCollection(DbMetaDataCollectionNames.ReservedWords);
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy the table filtering out any rows that don't apply to tho current version of the prrovider
|
|
|
|
reservedWordsTable = CloneAndFilterCollection(DbMetaDataCollectionNames.ReservedWords, null);
|
|
|
|
|
|
|
|
DataColumn reservedWordColumn = reservedWordsTable.Columns[DbMetaDataColumnNames.ReservedWord];
|
|
|
|
if (reservedWordColumn == null){
|
|
|
|
throw ADP.UnableToBuildCollection(DbMetaDataCollectionNames.ReservedWords);
|
|
|
|
}
|
|
|
|
|
|
|
|
string keywords = connection.GetInfoStringUnhandled(ODBC32.SQL_INFO.KEYWORDS);
|
|
|
|
|
|
|
|
if (null != keywords) {
|
|
|
|
string[] values = keywords.Split(KeywordSeparatorChar);
|
|
|
|
for (int i = 0; i < values.Length; ++i) {
|
|
|
|
DataRow row = reservedWordsTable.NewRow();
|
|
|
|
row[reservedWordColumn] = values[i];
|
|
|
|
|
|
|
|
reservedWordsTable.Rows.Add(row);
|
|
|
|
row.AcceptChanges();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return reservedWordsTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable GetTablesCollection(String[] restrictions, OdbcConnection connection, Boolean isTables){
|
|
|
|
|
|
|
|
OdbcCommand command = null;
|
|
|
|
OdbcDataReader dataReader =null;
|
|
|
|
DataTable resultTable = null;
|
|
|
|
const int tablesRestrictionsCount = 3;
|
|
|
|
const string includedTableTypesTables = "TABLE,SYSTEM TABLE";
|
|
|
|
const string includedTableTypesViews = "VIEW";
|
|
|
|
string includedTableTypes;
|
|
|
|
string dataTableName;
|
|
|
|
|
|
|
|
try {
|
|
|
|
//command = (OdbcCommand) connection.CreateCommand();
|
|
|
|
command = GetCommand(connection);
|
|
|
|
string [] allArguments = new string[tablesRestrictionsCount+1];
|
|
|
|
if (isTables == true) {
|
|
|
|
includedTableTypes = includedTableTypesTables;
|
|
|
|
dataTableName = OdbcMetaDataCollectionNames.Tables;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
includedTableTypes = includedTableTypesViews;
|
|
|
|
dataTableName = OdbcMetaDataCollectionNames.Views;
|
|
|
|
}
|
|
|
|
FillOutRestrictions(tablesRestrictionsCount,restrictions,allArguments,dataTableName);
|
|
|
|
|
|
|
|
allArguments[tablesRestrictionsCount] = includedTableTypes;
|
|
|
|
|
|
|
|
dataReader = command.ExecuteReaderFromSQLMethod(allArguments, ODBC32.SQL_API.SQLTABLES);
|
|
|
|
|
|
|
|
resultTable = DataTableFromDataReader(dataReader,dataTableName);
|
|
|
|
}
|
|
|
|
|
|
|
|
finally {
|
|
|
|
if (dataReader != null) {
|
|
|
|
dataReader.Dispose();
|
|
|
|
};
|
|
|
|
if (command != null) {
|
|
|
|
command.Dispose();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Boolean IncludeIndexRow(object rowIndexName,
|
|
|
|
string restrictionIndexName,
|
|
|
|
Int16 rowIndexType) {
|
|
|
|
// never include table statictics rows
|
|
|
|
if (rowIndexType == (Int16)ODBC32.SQL_STATISTICSTYPE.TABLE_STAT) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((restrictionIndexName != null) && (restrictionIndexName != (string)rowIndexName)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataTable NewDataTableFromReader(IDataReader reader, out object[] values, string tableName){
|
|
|
|
|
|
|
|
DataTable resultTable = new DataTable(tableName);
|
|
|
|
resultTable.Locale = System.Globalization.CultureInfo.InvariantCulture;
|
|
|
|
DataTable schemaTable = reader.GetSchemaTable();
|
|
|
|
foreach (DataRow row in schemaTable.Rows){
|
|
|
|
resultTable.Columns.Add(row["ColumnName"] as string, (Type)row["DataType"] as Type);
|
|
|
|
}
|
|
|
|
|
|
|
|
values = new object[resultTable.Columns.Count];
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override DataTable PrepareCollection(String collectionName, String[] restrictions, DbConnection connection){
|
|
|
|
|
|
|
|
DataTable resultTable = null;
|
|
|
|
OdbcConnection odbcConnection = (OdbcConnection) connection;
|
|
|
|
|
|
|
|
if (collectionName == OdbcMetaDataCollectionNames.Tables) {
|
|
|
|
resultTable = GetTablesCollection(restrictions, odbcConnection, true);
|
|
|
|
}
|
|
|
|
else if (collectionName == OdbcMetaDataCollectionNames.Views) {
|
|
|
|
resultTable = GetTablesCollection(restrictions, odbcConnection, false);
|
|
|
|
}
|
|
|
|
else if (collectionName == OdbcMetaDataCollectionNames.Columns) {
|
|
|
|
resultTable = GetColumnsCollection(restrictions, odbcConnection);
|
|
|
|
}
|
|
|
|
else if (collectionName == OdbcMetaDataCollectionNames.Procedures) {
|
|
|
|
resultTable = GetProceduresCollection(restrictions, odbcConnection);
|
|
|
|
}
|
|
|
|
else if (collectionName == OdbcMetaDataCollectionNames.ProcedureColumns) {
|
|
|
|
resultTable = GetProcedureColumnsCollection(restrictions, odbcConnection, true);
|
|
|
|
}
|
|
|
|
else if (collectionName == OdbcMetaDataCollectionNames.ProcedureParameters) {
|
|
|
|
resultTable = GetProcedureColumnsCollection(restrictions, odbcConnection, false);
|
|
|
|
}
|
|
|
|
else if (collectionName == OdbcMetaDataCollectionNames.Indexes) {
|
|
|
|
resultTable = GetIndexCollection(restrictions, odbcConnection);
|
|
|
|
}
|
|
|
|
else if (collectionName == DbMetaDataCollectionNames.DataTypes) {
|
|
|
|
resultTable = GetDataTypesCollection(restrictions, odbcConnection);
|
|
|
|
}
|
|
|
|
else if (collectionName == DbMetaDataCollectionNames.DataSourceInformation) {
|
|
|
|
resultTable = GetDataSourceInformationCollection(restrictions, odbcConnection);
|
|
|
|
}
|
|
|
|
else if (collectionName == DbMetaDataCollectionNames.ReservedWords) {
|
|
|
|
resultTable = GetReservedWordsCollection(restrictions, odbcConnection);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resultTable == null){
|
|
|
|
throw ADP.UnableToBuildCollection(collectionName);
|
|
|
|
}
|
|
|
|
|
|
|
|
return resultTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|