2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="OleDbDataReader.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.OleDb {
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Data ;
using System.Data.Common ;
using System.Data.ProviderBase ;
using System.Diagnostics ;
using System.Globalization ;
using System.IO ;
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
using System.Text ;
public sealed class OleDbDataReader : DbDataReader {
private CommandBehavior _commandBehavior ;
private static int _objectTypeCount ; // Bid counter
internal readonly int ObjectID = System . Threading . Interlocked . Increment ( ref _objectTypeCount ) ;
// object model interaction
private OleDbConnection _connection ;
private OleDbCommand _command ;
// DataReader owns the parameter bindings until CloseDataReader
// this allows OleDbCommand.Dispose to not require OleDbDataReader.Dispose
private Bindings _parameterBindings ;
// OLEDB interfaces
private UnsafeNativeMethods . IMultipleResults _imultipleResults ;
private UnsafeNativeMethods . IRowset _irowset ;
private UnsafeNativeMethods . IRow _irow ;
private ChapterHandle _chapterHandle = ChapterHandle . DB_NULL_HCHAPTER ;
private int _depth ;
private bool _isClosed , _isRead , _hasRows , _hasRowsReadCheck ;
long _sequentialBytesRead ;
int _sequentialOrdinal ;
private Bindings [ ] _bindings ; // _metdata contains the ColumnBinding
// do we need to jump to the next accessor
private int _nextAccessorForRetrieval ;
// must increment the counter before retrieving value so that
// if an exception is thrown, user can continue without erroring again
private int _nextValueForRetrieval ;
// record affected for the current dataset
private IntPtr _recordsAffected = ADP . RecordsUnaffected ;
private bool _useIColumnsRowset ;
private bool _sequentialAccess ;
private bool _singleRow ;
// cached information for Reading (rowhandles/status)
private IntPtr _rowHandleFetchCount ; // MDAC 60111 (>1 fails against jet)
private RowHandleBuffer _rowHandleNativeBuffer ;
private IntPtr _rowFetchedCount ;
private int _currentRow ;
private DataTable _dbSchemaTable ;
private int _visibleFieldCount ;
private MetaData [ ] _metadata ;
private FieldNameLookup _fieldNameLookup ;
// ctor for an ICommandText, IMultipleResults, IRowset, IRow
// ctor for an ADODB.Recordset, ADODB.Record or Hierarchial resultset
internal OleDbDataReader ( OleDbConnection connection , OleDbCommand command , int depth , CommandBehavior commandBehavior ) {
OleDbConnection . VerifyExecutePermission ( ) ;
_connection = connection ;
_command = command ;
_commandBehavior = commandBehavior ;
if ( ( null ! = command ) & & ( 0 = = _depth ) ) {
_parameterBindings = command . TakeBindingOwnerShip ( ) ;
}
_depth = depth ;
}
private void Initialize ( ) {
CommandBehavior behavior = _commandBehavior ;
_useIColumnsRowset = ( 0 ! = ( CommandBehavior . KeyInfo & behavior ) ) ;
_sequentialAccess = ( 0 ! = ( CommandBehavior . SequentialAccess & behavior ) ) ; // MDAC 60296
if ( 0 = = _depth ) { // MDAC 70886
_singleRow = ( 0 ! = ( CommandBehavior . SingleRow & behavior ) ) ;
}
}
internal void InitializeIMultipleResults ( object result ) {
Initialize ( ) ;
_imultipleResults = ( UnsafeNativeMethods . IMultipleResults ) result ; // maybe null if no results
}
internal void InitializeIRowset ( object result , ChapterHandle chapterHandle , IntPtr recordsAffected ) {
// if from ADODB, connection will be null
if ( ( null = = _connection ) | | ( ChapterHandle . DB_NULL_HCHAPTER ! = chapterHandle ) ) { // MDAC 59629
_rowHandleFetchCount = new IntPtr ( 1 ) ;
}
Initialize ( ) ;
_recordsAffected = recordsAffected ;
_irowset = ( UnsafeNativeMethods . IRowset ) result ; // maybe null if no results
_chapterHandle = chapterHandle ;
}
internal void InitializeIRow ( object result , IntPtr recordsAffected ) {
Initialize ( ) ;
Debug . Assert ( _singleRow , "SingleRow not already set" ) ;
_singleRow = true ;
_recordsAffected = recordsAffected ;
_irow = ( UnsafeNativeMethods . IRow ) result ; // maybe null if no results
_hasRows = ( null ! = _irow ) ;
}
internal OleDbCommand Command {
get {
return _command ;
}
}
override public int Depth {
get {
Bid . Trace ( "<oledb.OleDbDataReader.get_Depth|API> %d#\n" , ObjectID ) ;
if ( IsClosed ) { // MDAC 63669
throw ADP . DataReaderClosed ( "Depth" ) ;
}
return _depth ;
}
}
override public Int32 FieldCount {
get {
Bid . Trace ( "<oledb.OleDbDataReader.get_FieldCount|API> %d#\n" , ObjectID ) ;
if ( IsClosed ) { // MDAC 63669
throw ADP . DataReaderClosed ( "FieldCount" ) ;
}
MetaData [ ] metadata = MetaData ;
return ( ( null ! = metadata ) ? metadata . Length : 0 ) ;
}
}
override public bool HasRows { // MDAC 78405
get {
Bid . Trace ( "<oledb.OleDbDataReader.get_HasRows|API> %d#\n" , ObjectID ) ;
if ( IsClosed ) { // MDAC 63669
throw ADP . DataReaderClosed ( "HasRows" ) ;
}
return _hasRows ;
}
}
override public Boolean IsClosed {
get { // if we have a rowset or multipleresults, we may have more to read
Bid . Trace ( "<oledb.OleDbDataReader.get_IsClosed|API> %d#\n" , ObjectID ) ;
Debug . Assert ( ( _singleRow & & ! _isClosed & & ! _isRead & & ( null = = _irow ) & & ( null = = _irowset ) ) | |
_isClosed = = ( ( null = = _irow ) & & ( null = = _irowset ) & & ( null = = _imultipleResults )
& & ( null = = _dbSchemaTable ) & & ( null = = _connection ) & & ( null = = _command ) ) , // MDAC 59536
"IsClosed mismatch" ) ;
return _isClosed ;
}
}
private MetaData [ ] MetaData {
get { return _metadata ; }
}
override public int RecordsAffected {
get {
Bid . Trace ( "<oledb.OleDbDataReader.get_RecordsAffected|API> %d#\n" , ObjectID ) ;
return ADP . IntPtrToInt32 ( _recordsAffected ) ;
}
}
/ *
internal long RecordsAffectedLong {
get {
return ( long ) _recordsAffected ;
}
} * /
override public object this [ Int32 index ] {
get {
return GetValue ( index ) ;
}
}
override public object this [ String name ] {
get {
int ordinal = GetOrdinal ( name ) ;
return GetValue ( ordinal ) ;
}
}
// grouping the native OLE DB casts togther by required interfaces and optional interfaces
// want these to be methods, not properties otherwise they appear in VS7 managed debugger which attempts to evaluate them
// required interface, safe cast
private UnsafeNativeMethods . IAccessor IAccessor ( ) {
Bid . Trace ( "<oledb.IUnknown.QueryInterface|API|OLEDB|rowset> %d#, IAccessor\n" , ObjectID ) ;
return ( UnsafeNativeMethods . IAccessor ) IRowset ( ) ;
}
// required interface, safe cast
private UnsafeNativeMethods . IRowsetInfo IRowsetInfo ( ) {
Bid . Trace ( "<oledb.IUnknown.QueryInterface|API|OLEDB|rowset> %d#, IRowsetInfo\n" , ObjectID ) ;
return ( UnsafeNativeMethods . IRowsetInfo ) IRowset ( ) ;
}
private UnsafeNativeMethods . IRowset IRowset ( ) {
UnsafeNativeMethods . IRowset irowset = _irowset ;
if ( null = = irowset ) {
Debug . Assert ( false , "object is disposed" ) ;
throw new ObjectDisposedException ( GetType ( ) . Name ) ;
}
return irowset ;
}
private UnsafeNativeMethods . IRow IRow ( ) {
UnsafeNativeMethods . IRow irow = _irow ;
if ( null = = irow ) {
Debug . Assert ( false , "object is disposed" ) ;
throw new ObjectDisposedException ( GetType ( ) . Name ) ;
}
return irow ;
}
override public DataTable GetSchemaTable ( ) {
IntPtr hscp ;
Bid . ScopeEnter ( out hscp , "<oledb.OleDbDataReader.GetSchemaTable|API> %d#\n" , ObjectID ) ;
try {
DataTable schemaTable = _dbSchemaTable ;
if ( null = = schemaTable ) {
MetaData [ ] metadata = MetaData ;
if ( ( null ! = metadata ) & & ( 0 < metadata . Length ) ) {
if ( ( 0 < metadata . Length ) & & _useIColumnsRowset & & ( null ! = _connection ) ) {
AppendSchemaInfo ( ) ;
}
schemaTable = BuildSchemaTable ( metadata ) ;
}
else if ( IsClosed ) { // MDAC 68331
throw ADP . DataReaderClosed ( "GetSchemaTable" ) ;
}
//GetSchemaTable() is defined to return null after NextResult returns false
//throw ADP.DataReaderNoData();
}
return schemaTable ;
}
finally {
Bid . ScopeLeave ( ref hscp ) ;
}
}
internal void BuildMetaInfo ( ) {
Debug . Assert ( null = = _metadata , "BuildMetaInfo: already built, by _metadata" ) ;
if ( null ! = _irowset ) {
if ( _useIColumnsRowset ) {
BuildSchemaTableRowset ( _irowset ) ;
}
else {
BuildSchemaTableInfo ( _irowset , false , false ) ;
}
if ( null ! = _metadata & & 0 < _metadata . Length ) {
// @devnote: because we want to use the DBACCESSOR_OPTIMIZED bit,
// we are required to create the accessor before fetching any rows
CreateAccessors ( true ) ;
Debug . Assert ( null ! = _bindings , "unexpected dbBindings" ) ;
}
}
else if ( null ! = _irow ) {
BuildSchemaTableInfo ( _irow , false , false ) ;
if ( null ! = _metadata & & 0 < _metadata . Length ) {
CreateBindingsFromMetaData ( true ) ;
}
}
if ( null = = _metadata ) {
_hasRows = false ;
_visibleFieldCount = 0 ;
_metadata = new MetaData [ 0 ] ;
}
}
private DataTable BuildSchemaTable ( MetaData [ ] metadata ) {
Debug . Assert ( null = = _dbSchemaTable , "BuildSchemaTable: schema table already exists" ) ;
Debug . Assert ( null ! = metadata , "BuildSchemaTable: no _metadata" ) ;
DataTable schemaTable = new DataTable ( "SchemaTable" ) ;
schemaTable . Locale = CultureInfo . InvariantCulture ;
schemaTable . MinimumCapacity = metadata . Length ;
DataColumn name = new DataColumn ( "ColumnName" , typeof ( System . String ) ) ;
DataColumn ordinal = new DataColumn ( "ColumnOrdinal" , typeof ( System . Int32 ) ) ;
DataColumn size = new DataColumn ( "ColumnSize" , typeof ( System . Int32 ) ) ;
DataColumn precision = new DataColumn ( "NumericPrecision" , typeof ( System . Int16 ) ) ;
DataColumn scale = new DataColumn ( "NumericScale" , typeof ( System . Int16 ) ) ;
DataColumn dataType = new DataColumn ( "DataType" , typeof ( System . Type ) ) ;
DataColumn providerType = new DataColumn ( "ProviderType" , typeof ( System . Int32 ) ) ;
DataColumn isLong = new DataColumn ( "IsLong" , typeof ( System . Boolean ) ) ;
DataColumn allowDBNull = new DataColumn ( "AllowDBNull" , typeof ( System . Boolean ) ) ;
DataColumn isReadOnly = new DataColumn ( "IsReadOnly" , typeof ( System . Boolean ) ) ;
DataColumn isRowVersion = new DataColumn ( "IsRowVersion" , typeof ( System . Boolean ) ) ;
DataColumn isUnique = new DataColumn ( "IsUnique" , typeof ( System . Boolean ) ) ;
DataColumn isKey = new DataColumn ( "IsKey" , typeof ( System . Boolean ) ) ;
DataColumn isAutoIncrement = new DataColumn ( "IsAutoIncrement" , typeof ( System . Boolean ) ) ;
DataColumn isHidden = new DataColumn ( "IsHidden" , typeof ( System . Boolean ) ) ;
DataColumn baseSchemaName = new DataColumn ( "BaseSchemaName" , typeof ( System . String ) ) ;
DataColumn baseCatalogName = new DataColumn ( "BaseCatalogName" , typeof ( System . String ) ) ;
DataColumn baseTableName = new DataColumn ( "BaseTableName" , typeof ( System . String ) ) ;
DataColumn baseColumnName = new DataColumn ( "BaseColumnName" , typeof ( System . String ) ) ;
ordinal . DefaultValue = 0 ;
isLong . DefaultValue = false ;
DataColumnCollection columns = schemaTable . Columns ;
columns . Add ( name ) ;
columns . Add ( ordinal ) ;
columns . Add ( size ) ;
columns . Add ( precision ) ;
columns . Add ( scale ) ;
columns . Add ( dataType ) ;
columns . Add ( providerType ) ;
columns . Add ( isLong ) ;
columns . Add ( allowDBNull ) ;
columns . Add ( isReadOnly ) ;
columns . Add ( isRowVersion ) ;
columns . Add ( isUnique ) ;
columns . Add ( isKey ) ;
columns . Add ( isAutoIncrement ) ;
if ( _visibleFieldCount < metadata . Length ) {
columns . Add ( isHidden ) ;
}
columns . Add ( baseSchemaName ) ;
columns . Add ( baseCatalogName ) ;
columns . Add ( baseTableName ) ;
columns . Add ( baseColumnName ) ;
for ( int i = 0 ; i < metadata . Length ; + + i ) {
MetaData info = metadata [ i ] ;
DataRow newRow = schemaTable . NewRow ( ) ;
newRow [ name ] = info . columnName ;
newRow [ ordinal ] = i ; // MDAC 68319
// @devnote: size is count of characters for WSTR or STR, bytes otherwise
// @devnote: see OLEDB spec under IColumnsInfo::GetColumnInfo
newRow [ size ] = ( ( info . type . enumOleDbType ! = OleDbType . BSTR ) ? info . size : - 1 ) ; // MDAC 72653
newRow [ precision ] = info . precision ; // MDAC 72800
newRow [ scale ] = info . scale ;
newRow [ dataType ] = info . type . dataType ;
newRow [ providerType ] = info . type . enumOleDbType ;
newRow [ isLong ] = OleDbDataReader . IsLong ( info . flags ) ;
if ( info . isKeyColumn ) {
newRow [ allowDBNull ] = OleDbDataReader . AllowDBNull ( info . flags ) ;
}
else {
newRow [ allowDBNull ] = OleDbDataReader . AllowDBNullMaybeNull ( info . flags ) ;
}
newRow [ isReadOnly ] = OleDbDataReader . IsReadOnly ( info . flags ) ;
newRow [ isRowVersion ] = OleDbDataReader . IsRowVersion ( info . flags ) ;
newRow [ isUnique ] = info . isUnique ;
newRow [ isKey ] = info . isKeyColumn ;
newRow [ isAutoIncrement ] = info . isAutoIncrement ;
if ( _visibleFieldCount < metadata . Length ) {
newRow [ isHidden ] = info . isHidden ;
}
if ( null ! = info . baseSchemaName ) {
newRow [ baseSchemaName ] = info . baseSchemaName ;
}
if ( null ! = info . baseCatalogName ) {
newRow [ baseCatalogName ] = info . baseCatalogName ;
}
if ( null ! = info . baseTableName ) {
newRow [ baseTableName ] = info . baseTableName ;
}
if ( null ! = info . baseColumnName ) {
newRow [ baseColumnName ] = info . baseColumnName ;
}
schemaTable . Rows . Add ( newRow ) ;
newRow . AcceptChanges ( ) ;
}
// mark all columns as readonly
int count = columns . Count ;
for ( int i = 0 ; i < count ; i + + ) {
columns [ i ] . ReadOnly = true ; // MDAC 70943
}
_dbSchemaTable = schemaTable ;
return schemaTable ;
}
private void BuildSchemaTableInfo ( object handle , bool filterITypeInfo , bool filterChapters ) {
Debug . Assert ( null = = _dbSchemaTable , "non-null SchemaTable" ) ;
Debug . Assert ( null = = _metadata , "non-null metadata" ) ;
Debug . Assert ( null ! = handle , "unexpected null rowset" ) ;
Bid . Trace ( "<oledb.IUnknown.QueryInterface|API|OLEDB|rowset_row> %d#, IColumnsInfo\n" , ObjectID ) ;
UnsafeNativeMethods . IColumnsInfo icolumnsInfo = ( handle as UnsafeNativeMethods . IColumnsInfo ) ;
if ( null = = icolumnsInfo ) {
Bid . Trace ( "<oledb.IUnknown.QueryInterface|API|OLEDB|RET> %08X{HRESULT}\n" , OleDbHResult . E_NOINTERFACE ) ;
_dbSchemaTable = null ;
#if DEBUG
if ( handle is UnsafeNativeMethods . IRow ) {
Debug . Assert ( false , "bad IRow - IColumnsInfo not available" ) ;
}
else {
Debug . Assert ( handle is UnsafeNativeMethods . IRowset , "bad IRowset - IColumnsInfo not available" ) ;
}
#endif
return ;
}
OleDbHResult hr ;
IntPtr columnCount = ADP . PtrZero ; // column count
IntPtr columnInfos = ADP . PtrZero ; // ptr to byvalue tagDBCOLUMNINFO[]
using ( DualCoTaskMem safehandle = new DualCoTaskMem ( icolumnsInfo , out columnCount , out columnInfos , out hr ) ) {
if ( hr < 0 ) {
ProcessResults ( hr ) ;
}
if ( 0 < ( int ) columnCount ) {
BuildSchemaTableInfoTable ( columnCount . ToInt32 ( ) , columnInfos , filterITypeInfo , filterChapters ) ;
}
}
}
// create DataColumns
// add DataColumns to DataTable
// add schema information to DataTable
// generate unique column names
private void BuildSchemaTableInfoTable ( int columnCount , IntPtr columnInfos , bool filterITypeInfo , bool filterChapters ) {
Debug . Assert ( 0 < columnCount , "BuildSchemaTableInfoTable - no column" ) ;
int rowCount = 0 ;
MetaData [ ] metainfo = new MetaData [ columnCount ] ;
// for every column, build an equivalent to tagDBCOLUMNINFO
tagDBCOLUMNINFO dbColumnInfo = new tagDBCOLUMNINFO ( ) ;
for ( int i = 0 , offset = 0 ; i < columnCount ; + + i , offset + = ODB . SizeOf_tagDBCOLUMNINFO ) {
Marshal . PtrToStructure ( ADP . IntPtrOffset ( columnInfos , offset ) , dbColumnInfo ) ;
#if WIN32
if ( 0 > = ( int ) dbColumnInfo . iOrdinal ) {
#else
if ( 0 > = ( long ) dbColumnInfo . iOrdinal ) {
#endif
continue ;
}
if ( OleDbDataReader . DoColumnDropFilter ( dbColumnInfo . dwFlags ) ) {
continue ;
}
if ( null = = dbColumnInfo . pwszName ) {
dbColumnInfo . pwszName = "" ;
}
if ( filterITypeInfo & & ( ODB . DBCOLUMN_TYPEINFO = = dbColumnInfo . pwszName ) ) { // MDAC 65306
continue ;
}
if ( filterChapters & & ( NativeDBType . HCHAPTER = = dbColumnInfo . wType ) ) {
continue ; // filter chapters in IRowset from IDBSchemaRowset for DumpToTable
}
bool islong = OleDbDataReader . IsLong ( dbColumnInfo . dwFlags ) ;
bool isfixed = OleDbDataReader . IsFixed ( dbColumnInfo . dwFlags ) ;
NativeDBType dbType = NativeDBType . FromDBType ( dbColumnInfo . wType , islong , isfixed ) ;
MetaData info = new MetaData ( ) ;
info . columnName = dbColumnInfo . pwszName ;
info . type = dbType ;
info . ordinal = dbColumnInfo . iOrdinal ;
#if WIN32
info . size = ( int ) dbColumnInfo . ulColumnSize ;
#else
long maxsize = ( long ) dbColumnInfo . ulColumnSize ;
info . size = ( ( ( maxsize < 0 ) | | ( Int32 . MaxValue < maxsize ) ) ? Int32 . MaxValue : ( int ) maxsize ) ;
#endif
info . flags = dbColumnInfo . dwFlags ;
info . precision = dbColumnInfo . bPrecision ;
info . scale = dbColumnInfo . bScale ;
info . kind = dbColumnInfo . columnid . eKind ;
switch ( dbColumnInfo . columnid . eKind ) {
case ODB . DBKIND_GUID_NAME :
case ODB . DBKIND_GUID_PROPID :
case ODB . DBKIND_GUID :
info . guid = dbColumnInfo . columnid . uGuid ;
break ;
default :
Debug . Assert ( ODB . DBKIND_PGUID_NAME ! = dbColumnInfo . columnid . eKind , "OLE DB providers never return pGuid-style bindings." ) ;
Debug . Assert ( ODB . DBKIND_PGUID_PROPID ! = dbColumnInfo . columnid . eKind , "OLE DB providers never return pGuid-style bindings." ) ;
info . guid = Guid . Empty ;
break ;
}
switch ( dbColumnInfo . columnid . eKind ) {
case ODB . DBKIND_GUID_PROPID :
case ODB . DBKIND_PROPID :
info . propid = dbColumnInfo . columnid . ulPropid ;
break ;
case ODB . DBKIND_GUID_NAME :
case ODB . DBKIND_NAME :
if ( ADP . PtrZero ! = dbColumnInfo . columnid . ulPropid ) {
info . idname = Marshal . PtrToStringUni ( dbColumnInfo . columnid . ulPropid ) ;
}
else {
info . idname = null ;
}
break ;
default :
info . propid = ADP . PtrZero ;
break ;
}
metainfo [ rowCount ] = info ;
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "OleDbDataReader[" + info . ordinal . ToInt64 ( ) . ToString ( CultureInfo . InvariantCulture ) + ", " + dbColumnInfo . pwszName + "]=" + dbType . enumOleDbType . ToString ( ) + "," + dbType . dataSourceType + ", " + dbType . wType ) ;
}
#endif
rowCount + + ;
}
if ( rowCount < columnCount ) { // shorten names array appropriately
MetaData [ ] tmpinfo = new MetaData [ rowCount ] ;
for ( int i = 0 ; i < rowCount ; + + i ) {
tmpinfo [ i ] = metainfo [ i ] ;
}
metainfo = tmpinfo ;
}
_visibleFieldCount = rowCount ;
_metadata = metainfo ;
}
private void BuildSchemaTableRowset ( object handle ) {
Debug . Assert ( null = = _dbSchemaTable , "BuildSchemaTableRowset - non-null SchemaTable" ) ;
Debug . Assert ( null ! = handle , "BuildSchemaTableRowset(object) - unexpected null handle" ) ;
Bid . Trace ( "<oledb.IUnknown.QueryInterface|API|OLEDB|rowset_row> %d, IColumnsRowset\n" , ObjectID ) ;
UnsafeNativeMethods . IColumnsRowset icolumnsRowset = ( handle as UnsafeNativeMethods . IColumnsRowset ) ;
if ( null ! = icolumnsRowset ) {
UnsafeNativeMethods . IRowset rowset = null ;
IntPtr cOptColumns ;
OleDbHResult hr ;
using ( DualCoTaskMem prgOptColumns = new DualCoTaskMem ( icolumnsRowset , out cOptColumns , out hr ) ) {
Debug . Assert ( ( 0 = = hr ) | | prgOptColumns . IsInvalid , "GetAvailableCOlumns: unexpected return" ) ;
Bid . Trace ( "<oledb.IColumnsRowset.GetColumnsRowset|API|OLEDB> %d#, IID_IRowset\n" , ObjectID ) ;
hr = icolumnsRowset . GetColumnsRowset ( ADP . PtrZero , cOptColumns , prgOptColumns , ref ODB . IID_IRowset , 0 , ADP . PtrZero , out rowset ) ;
Bid . Trace ( "<oledb.IColumnsRowset.GetColumnsRowset|API|OLEDB|RET> %08X{HRESULT}\n" , hr ) ;
}
Debug . Assert ( ( 0 < = hr ) | | ( null = = rowset ) , "if GetColumnsRowset failed, rowset should be null" ) ;
if ( hr < 0 ) {
ProcessResults ( hr ) ;
}
DumpToSchemaTable ( rowset ) ;
// VSTFDEVDIV 479576: release the rowset to avoid race condition between the GC and the user code causing
// "Connection is busy with results for another command" exception
if ( null ! = rowset ) {
Marshal . ReleaseComObject ( rowset ) ;
}
}
else {
Bid . Trace ( "<oledb.IUnknown.QueryInterface|API|OLEDB|RET> %08X{HRESULT}\n" , OleDbHResult . E_NOINTERFACE ) ;
_useIColumnsRowset = false ; // MDAC 72653
BuildSchemaTableInfo ( handle , false , false ) ;
}
}
override public void Close ( ) {
IntPtr hscp ;
Bid . ScopeEnter ( out hscp , "<oledb.OleDbDataReader.Close|API> %d#\n" , ObjectID ) ;
try {
OleDbConnection con = _connection ;
OleDbCommand cmd = _command ;
Bindings bindings = _parameterBindings ;
_connection = null ;
_command = null ;
_parameterBindings = null ;
_isClosed = true ;
DisposeOpenResults ( ) ;
_hasRows = false ;
if ( ( null ! = cmd ) & & cmd . canceling ) { // MDAC 68964
DisposeNativeMultipleResults ( ) ;
if ( null ! = bindings ) {
bindings . CloseFromConnection ( ) ;
bindings = null ;
}
}
else {
UnsafeNativeMethods . IMultipleResults multipleResults = _imultipleResults ;
_imultipleResults = null ;
if ( null ! = multipleResults ) {
// if we don't have a cmd, same as a cancel (don't call NextResults) which is ADODB behavior
try {
// tricky code path is an exception is thrown
// causing connection to do a ResetState and connection.Close
// resulting in OleDbCommand.CloseFromConnection
if ( ( null ! = cmd ) & & ! cmd . canceling ) { // MDAC 71435
IntPtr affected = IntPtr . Zero ;
OleDbException nextResultsFailure = NextResults ( multipleResults , null , cmd , out affected ) ;
_recordsAffected = AddRecordsAffected ( _recordsAffected , affected ) ;
if ( null ! = nextResultsFailure ) {
throw nextResultsFailure ;
}
}
}
finally {
if ( null ! = multipleResults ) {
Marshal . ReleaseComObject ( multipleResults ) ;
}
}
}
}
if ( ( null ! = cmd ) & & ( 0 = = _depth ) ) {
// return bindings back to the cmd after closure of root DataReader
cmd . CloseFromDataReader ( bindings ) ; // MDAC 52283
}
if ( null ! = con ) {
con . RemoveWeakReference ( this ) ;
// if the DataReader is Finalized it will not close the connection
if ( IsCommandBehavior ( CommandBehavior . CloseConnection ) ) {
con . Close ( ) ;
}
}
// release unmanaged objects
RowHandleBuffer rowHandleNativeBuffer = _rowHandleNativeBuffer ;
_rowHandleNativeBuffer = null ;
if ( null ! = rowHandleNativeBuffer ) {
rowHandleNativeBuffer . Dispose ( ) ;
}
}
finally {
Bid . ScopeLeave ( ref hscp ) ;
}
}
internal void CloseReaderFromConnection ( bool canceling ) {
// being called from the connection, we will have a command. that command
// may be Disposed, but it doesn't matter since another command can't execute
// until all DataReader are closed
if ( null ! = _command ) { //
_command . canceling = canceling ;
}
// called from the connection which will remove this from its ReferenceCollection
// we want the NextResult behavior, but no errors
_connection = null ;
Close ( ) ;
}
private void DisposeManagedRowset ( ) {
//not cleared after last rowset
//_hasRows = false;
_isRead = false ;
_hasRowsReadCheck = false ;
_nextAccessorForRetrieval = 0 ;
_nextValueForRetrieval = 0 ;
Bindings [ ] bindings = _bindings ;
_bindings = null ;
if ( null ! = bindings ) {
for ( int i = 0 ; i < bindings . Length ; + + i ) {
if ( null ! = bindings [ i ] ) { // MDAC 77007
bindings [ i ] . Dispose ( ) ;
}
}
}
_currentRow = 0 ;
_rowFetchedCount = IntPtr . Zero ;
_dbSchemaTable = null ;
_visibleFieldCount = 0 ;
_metadata = null ;
_fieldNameLookup = null ;
}
private void DisposeNativeMultipleResults ( ) {
UnsafeNativeMethods . IMultipleResults imultipleResults = _imultipleResults ;
_imultipleResults = null ;
if ( null ! = imultipleResults ) {
Marshal . ReleaseComObject ( imultipleResults ) ;
}
}
private void DisposeNativeRowset ( ) {
UnsafeNativeMethods . IRowset irowset = _irowset ;
_irowset = null ;
ChapterHandle chapter = _chapterHandle ;
_chapterHandle = ChapterHandle . DB_NULL_HCHAPTER ;
if ( ChapterHandle . DB_NULL_HCHAPTER ! = chapter ) { // MDAC 81441
chapter . Dispose ( ) ;
}
if ( null ! = irowset ) {
Marshal . ReleaseComObject ( irowset ) ;
}
}
private void DisposeNativeRow ( ) {
UnsafeNativeMethods . IRow irow = _irow ;
_irow = null ;
if ( null ! = irow ) {
Marshal . ReleaseComObject ( irow ) ;
}
}
private void DisposeOpenResults ( ) {
DisposeManagedRowset ( ) ;
DisposeNativeRow ( ) ;
DisposeNativeRowset ( ) ;
}
override public Boolean GetBoolean ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueBoolean ( ) ;
}
override public Byte GetByte ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueByte ( ) ;
}
private ColumnBinding DoSequentialCheck ( int ordinal , Int64 dataIndex , string method ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
if ( dataIndex > Int32 . MaxValue ) {
throw ADP . InvalidSourceBufferIndex ( 0 , dataIndex , "dataIndex" ) ;
}
if ( _sequentialOrdinal ! = ordinal ) {
_sequentialOrdinal = ordinal ;
_sequentialBytesRead = 0 ;
}
else if ( _sequentialAccess & & ( _sequentialBytesRead < dataIndex ) ) {
throw ADP . NonSeqByteAccess ( dataIndex , _sequentialBytesRead , method ) ;
}
// getting the value doesn't really belong, but it's common to both
// callers GetBytes and GetChars so we might as well have the code here
return binding ;
}
override public Int64 GetBytes ( int ordinal , Int64 dataIndex , byte [ ] buffer , Int32 bufferIndex , Int32 length ) {
ColumnBinding binding = DoSequentialCheck ( ordinal , dataIndex , ADP . GetBytes ) ;
byte [ ] value = binding . ValueByteArray ( ) ;
if ( null = = buffer ) {
return value . Length ;
}
int srcIndex = ( int ) dataIndex ;
int byteCount = Math . Min ( value . Length - srcIndex , length ) ;
if ( srcIndex < 0 ) { // MDAC 72830
throw ADP . InvalidSourceBufferIndex ( value . Length , srcIndex , "dataIndex" ) ;
}
else if ( ( bufferIndex < 0 ) | | ( bufferIndex > = buffer . Length ) ) { // MDAC 71013
throw ADP . InvalidDestinationBufferIndex ( buffer . Length , bufferIndex , "bufferIndex" ) ;
}
if ( 0 < byteCount ) {
// @usernote: user may encounter ArgumentException from Buffer.BlockCopy
Buffer . BlockCopy ( value , srcIndex , buffer , bufferIndex , byteCount ) ;
_sequentialBytesRead = srcIndex + byteCount ; // MDAC 71013
}
else if ( length < 0 ) { // MDAC 71007
throw ADP . InvalidDataLength ( length ) ;
}
else {
byteCount = 0 ;
}
return byteCount ;
}
override public Int64 GetChars ( int ordinal , Int64 dataIndex , char [ ] buffer , Int32 bufferIndex , Int32 length ) {
ColumnBinding binding = DoSequentialCheck ( ordinal , dataIndex , ADP . GetChars ) ;
string value = binding . ValueString ( ) ;
if ( null = = buffer ) {
return value . Length ;
}
int srcIndex = ( int ) dataIndex ;
int charCount = Math . Min ( value . Length - srcIndex , length ) ;
if ( srcIndex < 0 ) { // MDAC 72830
throw ADP . InvalidSourceBufferIndex ( value . Length , srcIndex , "dataIndex" ) ;
}
else if ( ( bufferIndex < 0 ) | | ( bufferIndex > = buffer . Length ) ) { // MDAC 71013
throw ADP . InvalidDestinationBufferIndex ( buffer . Length , bufferIndex , "bufferIndex" ) ;
}
if ( 0 < charCount ) {
// @usernote: user may encounter ArgumentException from String.CopyTo
value . CopyTo ( srcIndex , buffer , bufferIndex , charCount ) ;
_sequentialBytesRead = srcIndex + charCount ; // MDAC 71013
}
else if ( length < 0 ) { // MDAC 71007
throw ADP . InvalidDataLength ( length ) ;
}
else {
charCount = 0 ;
}
return charCount ;
}
[ EditorBrowsableAttribute(EditorBrowsableState.Never) ] // MDAC 69508
override public Char GetChar ( int ordinal ) {
throw ADP . NotSupported ( ) ;
}
[ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ]
new public OleDbDataReader GetData ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueChapter ( ) ;
}
override protected DbDataReader GetDbDataReader ( int ordinal ) {
return GetData ( ordinal ) ;
}
internal OleDbDataReader ResetChapter ( int bindingIndex , int index , RowBinding rowbinding , int valueOffset ) {
return GetDataForReader ( _metadata [ bindingIndex + index ] . ordinal , rowbinding , valueOffset ) ;
}
private OleDbDataReader GetDataForReader ( IntPtr ordinal , RowBinding rowbinding , int valueOffset ) {
UnsafeNativeMethods . IRowsetInfo rowsetInfo = IRowsetInfo ( ) ;
UnsafeNativeMethods . IRowset result ;
OleDbHResult hr ;
Bid . Trace ( "<oledb.IRowsetInfo.GetReferencedRowset|API|OLEDB> %d#, ColumnOrdinal=%Id\n" , ObjectID , ordinal ) ;
hr = rowsetInfo . GetReferencedRowset ( ( IntPtr ) ordinal , ref ODB . IID_IRowset , out result ) ;
Bid . Trace ( "<oledb.IRowsetInfo.GetReferencedRowset|API|OLEDB|RET> %08X{HRESULT}\n" , hr ) ;
ProcessResults ( hr ) ;
OleDbDataReader reader = null ;
if ( null ! = result ) {
// only when the first datareader is closed will the connection close
ChapterHandle chapterHandle = ChapterHandle . CreateChapterHandle ( result , rowbinding , valueOffset ) ;
reader = new OleDbDataReader ( _connection , _command , 1 + Depth , _commandBehavior & ~ CommandBehavior . CloseConnection ) ;
reader . InitializeIRowset ( result , chapterHandle , ADP . RecordsUnaffected ) ;
reader . BuildMetaInfo ( ) ;
reader . HasRowsRead ( ) ;
if ( null ! = _connection ) {
// connection tracks all readers to prevent cmd from executing
// until all readers (including nested) are closed
_connection . AddWeakReference ( reader , OleDbReferenceCollection . DataReaderTag ) ;
}
}
return reader ;
}
override public String GetDataTypeName ( int index ) {
if ( null ! = _metadata ) {
return _metadata [ index ] . type . dataSourceType ;
}
throw ADP . DataReaderNoData ( ) ;
}
override public DateTime GetDateTime ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueDateTime ( ) ;
}
override public Decimal GetDecimal ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueDecimal ( ) ;
}
override public Double GetDouble ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueDouble ( ) ;
}
override public IEnumerator GetEnumerator ( ) {
return new DbEnumerator ( ( IDataReader ) this , IsCommandBehavior ( CommandBehavior . CloseConnection ) ) ;
}
override public Type GetFieldType ( int index ) {
if ( null ! = _metadata ) {
return _metadata [ index ] . type . dataType ;
}
throw ADP . DataReaderNoData ( ) ;
}
override public Single GetFloat ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueSingle ( ) ;
}
override public Guid GetGuid ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueGuid ( ) ;
}
override public Int16 GetInt16 ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueInt16 ( ) ;
}
override public Int32 GetInt32 ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueInt32 ( ) ;
}
override public Int64 GetInt64 ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueInt64 ( ) ;
}
override public String GetName ( int index ) {
if ( null ! = _metadata ) {
Debug . Assert ( null ! = _metadata [ index ] . columnName , "MDAC 66681" ) ;
return _metadata [ index ] . columnName ;
}
throw ADP . DataReaderNoData ( ) ;
}
override public Int32 GetOrdinal ( String name ) {
if ( null = = _fieldNameLookup ) {
if ( null = = _metadata ) {
throw ADP . DataReaderNoData ( ) ;
}
_fieldNameLookup = new FieldNameLookup ( this , - 1 ) ;
}
return _fieldNameLookup . GetOrdinal ( name ) ; // MDAC 71470
}
override public String GetString ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . ValueString ( ) ;
}
public TimeSpan GetTimeSpan ( int ordinal ) {
return ( TimeSpan ) GetValue ( ordinal ) ;
}
private MetaData DoValueCheck ( int ordinal ) {
if ( ! _isRead ) {
// Read hasn't been called yet or no more data
throw ADP . DataReaderNoData ( ) ;
}
else if ( _sequentialAccess & & ( ordinal < _nextValueForRetrieval ) ) {
throw ADP . NonSequentialColumnAccess ( ordinal , _nextValueForRetrieval ) ;
}
// @usernote: user may encounter the IndexOutOfRangeException
MetaData info = _metadata [ ordinal ] ;
return info ;
}
private ColumnBinding GetColumnBinding ( int ordinal ) {
MetaData info = DoValueCheck ( ordinal ) ;
return GetValueBinding ( info ) ;
}
private ColumnBinding GetValueBinding ( MetaData info ) {
ColumnBinding binding = info . columnBinding ;
Debug . Assert ( null ! = binding , "null binding" ) ;
// do we need to jump to the next accessor
for ( int i = _nextAccessorForRetrieval ; i < = binding . IndexForAccessor ; + + i ) {
Debug . Assert ( _nextAccessorForRetrieval < = binding . IndexForAccessor , "backwards index for accessor" ) ;
Debug . Assert ( _nextAccessorForRetrieval = = i , "failed to increment" ) ;
if ( _sequentialAccess ) {
if ( _nextValueForRetrieval ! = binding . Index ) { // release old value
_metadata [ _nextValueForRetrieval ] . columnBinding . ResetValue ( ) ;
}
_nextAccessorForRetrieval = binding . IndexForAccessor ;
}
if ( null ! = _irowset ) {
GetRowDataFromHandle ( ) ; // will increment _nextAccessorForRetrieval
}
else if ( null ! = _irow ) {
GetRowValue ( ) ; // will increment _nextAccessorForRetrieval
}
else {
throw ADP . DataReaderNoData ( ) ;
}
}
// to enforce sequential access
_nextValueForRetrieval = binding . Index ;
return binding ;
}
override public object GetValue ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
object value = binding . Value ( ) ;
return value ;
}
override public Int32 GetValues ( object [ ] values ) {
if ( null = = values ) {
throw ADP . ArgumentNull ( "values" ) ;
}
MetaData info = DoValueCheck ( 0 ) ;
int count = Math . Min ( values . Length , _visibleFieldCount ) ;
for ( int i = 0 ; ( i < _metadata . Length ) & & ( i < count ) ; + + i ) {
ColumnBinding binding = GetValueBinding ( _metadata [ i ] ) ;
values [ i ] = binding . Value ( ) ;
}
return count ;
}
private bool IsCommandBehavior ( CommandBehavior condition ) {
return ( condition = = ( condition & _commandBehavior ) ) ;
}
override public Boolean IsDBNull ( int ordinal ) {
ColumnBinding binding = GetColumnBinding ( ordinal ) ;
return binding . IsValueNull ( ) ;
}
private void ProcessResults ( OleDbHResult hr ) {
Exception e ;
if ( null ! = _command ) {
e = OleDbConnection . ProcessResults ( hr , _connection , _command ) ;
}
else {
e = OleDbConnection . ProcessResults ( hr , _connection , _connection ) ;
}
if ( null ! = e ) { throw e ; }
}
static private IntPtr AddRecordsAffected ( IntPtr recordsAffected , IntPtr affected ) { // MDAC 65374
#if WIN32
if ( 0 < = ( int ) affected ) {
if ( 0 < = ( int ) recordsAffected ) {
return ( IntPtr ) ( ( int ) recordsAffected + ( int ) affected ) ;
#else
if ( 0 < = ( long ) affected ) {
if ( 0 < = ( long ) recordsAffected ) {
return ( IntPtr ) ( ( long ) recordsAffected + ( long ) affected ) ;
#endif
}
return affected ;
}
return recordsAffected ;
}
override public int VisibleFieldCount {
get {
Bid . Trace ( "<oledb.OleDbDataReader.get_VisibleFieldCount|API> %d#\n" , ObjectID ) ;
if ( IsClosed ) {
throw ADP . DataReaderClosed ( "VisibleFieldCount" ) ;
}
return _visibleFieldCount ;
}
}
internal void HasRowsRead ( ) { // MDAC 78405
Debug . Assert ( ! _hasRowsReadCheck , "_hasRowsReadCheck not reset" ) ;
bool flag = Read ( ) ;
_hasRows = flag ;
_hasRowsReadCheck = true ;
_isRead = false ;
}
static internal OleDbException NextResults ( UnsafeNativeMethods . IMultipleResults imultipleResults , OleDbConnection connection , OleDbCommand command , out IntPtr recordsAffected ) {
recordsAffected = ADP . RecordsUnaffected ;
List < OleDbException > exceptions = null ; // WebData 104564
if ( null ! = imultipleResults ) {
object result ;
IntPtr affected ;
OleDbHResult hr ;
// MSOLAP provider doesn't move onto the next result when calling GetResult with IID_NULL, but does return S_OK with 0 affected records.
// we want to break out of that infinite loop for ExecuteNonQuery and the multiple result Close scenarios
for ( int loop = 0 ; ; + + loop ) {
if ( ( null ! = command ) & & command . canceling ) { // MDAC 68964
break ;
}
Bid . Trace ( "<oledb.IMultipleResults.GetResult|API|OLEDB> DBRESULTFLAG_DEFAULT, IID_NULL\n" ) ;
hr = imultipleResults . GetResult ( ADP . PtrZero , ODB . DBRESULTFLAG_DEFAULT , ref ODB . IID_NULL , out affected , out result ) ;
Bid . Trace ( "<oledb.IMultipleResults.GetResult|API|OLEDB|RET> %08X{HRESULT}, RecordAffected=%Id\n" , hr , affected ) ;
// If a provider doesn't support IID_NULL and returns E_NOINTERFACE we want to break out
// of the loop without throwing an exception. Our behavior will match ADODB in that scenario
// where Recordset.Close just releases the interfaces without proccessing remaining results
if ( ( OleDbHResult . DB_S_NORESULT = = hr ) | | ( OleDbHResult . E_NOINTERFACE = = hr ) ) { // MDAC 70874
break ;
}
if ( null ! = connection ) {
Exception e = OleDbConnection . ProcessResults ( hr , connection , command ) ;
if ( null ! = e ) {
OleDbException excep = ( e as OleDbException ) ;
if ( null ! = excep ) {
if ( null = = exceptions ) {
exceptions = new List < OleDbException > ( ) ;
}
exceptions . Add ( excep ) ;
}
else {
Debug . Assert ( OleDbHResult . DB_E_OBJECTOPEN = = hr , "unexpected" ) ;
throw e ; // we don't expect to be here, but it could happen
}
}
}
else if ( hr < 0 ) {
SafeNativeMethods . Wrapper . ClearErrorInfo ( ) ;
break ; // MDAC 72694
}
recordsAffected = AddRecordsAffected ( recordsAffected , affected ) ;
if ( 0 ! = ( int ) affected ) {
loop = 0 ;
}
else if ( 2000 < = loop ) { // MDAC 72126 (reason for more than 1000 iterations)
NextResultsInfinite ( ) ; // MDAC 72738
break ;
}
}
}
if ( null ! = exceptions ) {
return OleDbException . CombineExceptions ( exceptions ) ;
}
return null ;
}
static private void NextResultsInfinite ( ) { // MDAC 72738
Bid . Trace ( "<oledb.OleDbDataReader.NextResultsInfinite|INFO> System.Data.OleDb.OleDbDataReader: 2000 IMultipleResult.GetResult(NULL, DBRESULTFLAG_DEFAULT, IID_NULL, NULL, NULL) iterations with 0 records affected. Stopping suspect infinite loop. To work-around try using ExecuteReader() and iterating through results with NextResult().\n" ) ;
2017-08-21 15:34:15 +00:00
// Microsoft's suggestion is that we debug assert so that users will learn of MSOLAP's misbehavior and not call ExecuteNonQuery
2016-08-03 10:59:49 +00:00
Debug . Assert ( false , "<oledb.OleDbDataReader.NextResultsInfinite|INFO> System.Data.OleDb.OleDbDataReader: 2000 IMultipleResult.GetResult(NULL, DBRESULTFLAG_DEFAULT, IID_NULL, NULL, NULL) iterations with 0 records affected. Stopping suspect infinite loop. To work-around try using ExecuteReader() and iterating through results with NextResult().\n" ) ;
}
override public bool NextResult ( ) {
IntPtr hscp ;
Bid . ScopeEnter ( out hscp , "<oledb.OleDbDataReader.NextResult|API> %d#\n" , ObjectID ) ;
try {
bool retflag = false ;
if ( IsClosed ) {
throw ADP . DataReaderClosed ( "NextResult" ) ;
}
_fieldNameLookup = null ;
OleDbCommand command = _command ;
UnsafeNativeMethods . IMultipleResults imultipleResults = _imultipleResults ;
if ( null ! = imultipleResults ) {
DisposeOpenResults ( ) ;
_hasRows = false ;
for ( ; ; ) {
Debug . Assert ( null = = _irow , "NextResult: row loop check" ) ;
Debug . Assert ( null = = _irowset , "NextResult: rowset loop check" ) ;
object result = null ;
OleDbHResult hr ;
IntPtr affected ;
if ( ( null ! = command ) & & command . canceling ) { // MDAC 69986
Close ( ) ;
break ;
}
Bid . Trace ( "<oledb.IMultipleResults.GetResult|API|OLEDB> %d#, IID_IRowset\n" , ObjectID ) ;
hr = imultipleResults . GetResult ( ADP . PtrZero , ODB . DBRESULTFLAG_DEFAULT , ref ODB . IID_IRowset , out affected , out result ) ;
Bid . Trace ( "<oledb.IMultipleResults.GetResult|API|OLEDB|RET> %08X{HRESULT}, RecordAffected=%Id\n" , hr , affected ) ;
if ( ( 0 < = hr ) & & ( null ! = result ) ) {
Bid . Trace ( "<oledb.IUnknown.QueryInterface|API|OLEDB|RowSet> %d#, IRowset\n" , ObjectID ) ;
_irowset = ( UnsafeNativeMethods . IRowset ) result ;
}
_recordsAffected = AddRecordsAffected ( _recordsAffected , affected ) ;
if ( OleDbHResult . DB_S_NORESULT = = hr ) {
DisposeNativeMultipleResults ( ) ;
break ;
}
// @devnote: infomessage events may be fired from here
ProcessResults ( hr ) ;
if ( null ! = _irowset ) {
BuildMetaInfo ( ) ;
HasRowsRead ( ) ;
retflag = true ;
break ;
}
}
}
else {
DisposeOpenResults ( ) ; // MDAC 70934
_hasRows = false ; // MDAC 85850
}
return retflag ;
}
finally {
Bid . ScopeLeave ( ref hscp ) ;
}
}
override public bool Read ( ) {
IntPtr hscp ;
Bid . ScopeEnter ( out hscp , "<oledb.OleDbDataReader.Read|API> %d#\n" , ObjectID ) ;
try {
bool retflag = false ;
OleDbCommand command = _command ;
if ( ( null ! = command ) & & command . canceling ) { // MDAC 69986
DisposeOpenResults ( ) ;
}
else if ( null ! = _irowset ) {
if ( _hasRowsReadCheck ) {
_isRead = retflag = _hasRows ;
_hasRowsReadCheck = false ;
}
else if ( _singleRow & & _isRead ) {
DisposeOpenResults ( ) ; // MDAC 66109
}
else {
retflag = ReadRowset ( ) ;
}
}
else if ( null ! = _irow ) {
retflag = ReadRow ( ) ;
}
else if ( IsClosed ) {
throw ADP . DataReaderClosed ( "Read" ) ;
}
return retflag ;
}
finally {
Bid . ScopeLeave ( ref hscp ) ;
}
}
private bool ReadRow ( ) {
if ( _isRead ) {
_isRead = false ; // for DoValueCheck
DisposeNativeRow ( ) ;
_sequentialOrdinal = - 1 ; // sequentialBytesRead will reset when used
}
else {
_isRead = true ;
return ( 0 < _metadata . Length ) ;
}
return false ;
}
private bool ReadRowset ( ) {
Debug . Assert ( null ! = _irowset , "ReadRow: null IRowset" ) ;
Debug . Assert ( 0 < = _metadata . Length , "incorrect state for fieldCount" ) ;
// releases bindings as necessary
// bumps current row, else resets it back to initial state
ReleaseCurrentRow ( ) ;
_sequentialOrdinal = - 1 ; // sequentialBytesRead will reset when used
// making the check if (null != irowset) unnecessary
// if necessary, get next group of row handles
if ( IntPtr . Zero = = _rowFetchedCount ) { // starts at (-1 <= 0)
Debug . Assert ( 0 = = _currentRow , "incorrect state for _currentRow" ) ;
Debug . Assert ( 0 < = _metadata . Length , "incorrect state for fieldCount" ) ;
Debug . Assert ( 0 = = _nextAccessorForRetrieval , "incorrect state for nextAccessorForRetrieval" ) ;
Debug . Assert ( 0 = = _nextValueForRetrieval , "incorrect state for nextValueForRetrieval" ) ;
// @devnote: releasing row handles occurs next time user calls read, skip, or close
GetRowHandles ( /*skipCount*/ ) ;
}
return ( ( _currentRow < = ( int ) _rowFetchedCount ) & & _isRead ) ;
}
private void ReleaseCurrentRow ( ) {
Debug . Assert ( null ! = _irowset , "ReleaseCurrentRow: no rowset" ) ;
if ( 0 < ( int ) _rowFetchedCount ) {
// release the data in the current row
Bindings [ ] bindings = _bindings ;
Debug . Assert ( null ! = bindings , "ReleaseCurrentRow: null dbBindings" ) ;
for ( int i = 0 ; ( i < bindings . Length ) & & ( i < _nextAccessorForRetrieval ) ; + + i ) {
bindings [ i ] . CleanupBindings ( ) ;
}
_nextAccessorForRetrieval = 0 ;
_nextValueForRetrieval = 0 ;
_currentRow + + ;
if ( _currentRow = = ( int ) _rowFetchedCount ) {
ReleaseRowHandles ( ) ;
}
}
}
private void CreateAccessors ( bool allowMultipleAccessor ) {
Debug . Assert ( null = = _bindings , "CreateAccessor: dbBindings already exists" ) ;
Debug . Assert ( null ! = _irowset , "CreateAccessor: no IRowset available" ) ;
Debug . Assert ( null ! = _metadata & & 0 < _metadata . Length , "no columns" ) ;
Bindings [ ] dbBindings = CreateBindingsFromMetaData ( allowMultipleAccessor ) ;
UnsafeNativeMethods . IAccessor iaccessor = IAccessor ( ) ;
for ( int i = 0 ; i < dbBindings . Length ; + + i ) {
OleDbHResult hr = dbBindings [ i ] . CreateAccessor ( iaccessor , ODB . DBACCESSOR_ROWDATA ) ;
if ( hr < 0 ) {
ProcessResults ( hr ) ;
}
}
if ( IntPtr . Zero = = _rowHandleFetchCount ) {
_rowHandleFetchCount = new IntPtr ( 1 ) ;
object maxRows = GetPropertyValue ( ODB . DBPROP_MAXROWS ) ;
if ( maxRows is Int32 ) {
_rowHandleFetchCount = new IntPtr ( ( int ) maxRows ) ;
if ( ( ADP . PtrZero = = _rowHandleFetchCount ) | | ( 20 < = ( int ) _rowHandleFetchCount ) ) {
_rowHandleFetchCount = new IntPtr ( 20 ) ;
}
}
else if ( maxRows is Int64 ) {
_rowHandleFetchCount = new IntPtr ( ( long ) maxRows ) ;
if ( ( ADP . PtrZero = = _rowHandleFetchCount ) | | ( 20 < = ( long ) _rowHandleFetchCount ) ) {
_rowHandleFetchCount = new IntPtr ( 20 ) ;
}
}
}
if ( null = = _rowHandleNativeBuffer ) {
_rowHandleNativeBuffer = new RowHandleBuffer ( _rowHandleFetchCount ) ;
}
}
private Bindings [ ] CreateBindingsFromMetaData ( bool allowMultipleAccessor ) {
int bindingCount = 0 ;
int currentBindingIndex = 0 ;
MetaData [ ] metadata = _metadata ;
int [ ] indexToBinding = new int [ metadata . Length ] ;
int [ ] indexWithinBinding = new int [ metadata . Length ] ;
// walk through the schemaRows to determine the number of binding groups
if ( allowMultipleAccessor ) {
if ( null ! = _irowset ) {
for ( int i = 0 ; i < indexToBinding . Length ; + + i ) {
indexToBinding [ i ] = bindingCount ;
indexWithinBinding [ i ] = currentBindingIndex ;
#if false
// @denote: single/multiple Accessors
if ( ( bindingCount < 2 ) & & IsLong ( metadata [ i ] . flags ) ) {
bindingCount + + ;
currentBindingIndex = 0 ;
}
else {
currentBindingIndex + + ;
}
#elif false
// @devnote: one accessor per column option
bindingCount + + ;
currentBindingIndex = 0 ;
#else
// @devnote: one accessor only for IRowset
currentBindingIndex + + ;
#endif
}
if ( 0 < currentBindingIndex ) { // when blob is not the last column
bindingCount + + ;
}
}
else if ( null ! = _irow ) {
for ( int i = 0 ; i < indexToBinding . Length ; + + i ) {
indexToBinding [ i ] = i ;
indexWithinBinding [ i ] = 0 ;
}
bindingCount = metadata . Length ;
}
}
else {
for ( int i = 0 ; i < indexToBinding . Length ; + + i ) {
indexToBinding [ i ] = 0 ;
indexWithinBinding [ i ] = i ;
}
bindingCount = 1 ;
}
Bindings bindings ;
Bindings [ ] dbbindings = new Bindings [ bindingCount ] ;
bindingCount = 0 ;
// for every column, build tagDBBinding info
for ( int index = 0 ; index < metadata . Length ; + + index ) {
Debug . Assert ( indexToBinding [ index ] < dbbindings . Length , "bad indexToAccessor" ) ;
bindings = dbbindings [ indexToBinding [ index ] ] ;
if ( null = = bindings ) {
bindingCount = 0 ;
for ( int i = index ; ( i < metadata . Length ) & & ( bindingCount = = indexWithinBinding [ i ] ) ; + + i ) {
bindingCount + + ;
}
dbbindings [ indexToBinding [ index ] ] = bindings = new Bindings ( ( OleDbDataReader ) this , ( null ! = _irowset ) , bindingCount ) ;
// runningTotal is buffered to start values on 16-byte boundary
// the first columnCount * 8 bytes are for the length and status fields
//bindings.DataBufferSize = (bindingCount + (bindingCount % 2)) * sizeof_int64;
}
MetaData info = metadata [ index ] ;
int maxLen = info . type . fixlen ;
short getType = info . type . wType ;
Debug . Assert ( NativeDBType . STR ! = getType , "Should have bound as WSTR" ) ;
Debug . Assert ( ! NativeDBType . HasHighBit ( getType ) , "CreateAccessor - unexpected high bits on datatype" ) ;
if ( - 1 ! = info . size ) {
if ( info . type . islong ) {
maxLen = ADP . PtrSize ;
getType = ( short ) ( ( ushort ) getType | ( ushort ) NativeDBType . BYREF ) ;
}
else if ( - 1 = = maxLen ) {
// @devnote: not using provider owned memory for PDC, no one really supports it anyway.
/ * if ( ( ( null ! = connection ) & & connection . PropertyGetProviderOwnedMemory ( ) )
| | ( ( null ! = command ) & & command . Connection . PropertyGetProviderOwnedMemory ( ) ) ) {
bindings . MemOwner = DBMemOwner . ProviderOwned ;
bindings . MaxLen = ADP . PtrSize ;
bindings . DbType = ( short ) ( getType | DbType . BYREF ) ;
}
else * /
if ( ODB . LargeDataSize < info . size ) {
maxLen = ADP . PtrSize ;
getType = ( short ) ( ( ushort ) getType | ( ushort ) NativeDBType . BYREF ) ;
}
else if ( ( NativeDBType . WSTR = = getType ) & & ( - 1 ! = info . size ) ) { // WebData 99298
maxLen = info . size * 2 + 2 ;
}
else {
maxLen = info . size ;
}
}
}
else if ( maxLen < 0 ) {
// if variable length and no defined size we require this to be byref at this time
/ * if ( ( ( null ! = connection ) & & connection . PropertyGetProviderOwnedMemory ( ) )
| | ( ( null ! = command ) & & command . Connection . PropertyGetProviderOwnedMemory ( ) ) ) {
bindings . MemOwner = DBMemOwner . ProviderOwned ;
} * /
maxLen = ADP . PtrSize ;
getType = ( short ) ( ( ushort ) getType | ( ushort ) NativeDBType . BYREF ) ;
}
currentBindingIndex = indexWithinBinding [ index ] ;
bindings . CurrentIndex = currentBindingIndex ;
bindings . Ordinal = info . ordinal ;
bindings . Part = info . type . dbPart ;
bindings . Precision = ( byte ) info . precision ;
bindings . Scale = ( byte ) info . scale ;
bindings . DbType = ( short ) getType ;
bindings . MaxLen = maxLen ; // also increments databuffer size (uses DbType)
//bindings.ValueOffset = // set via MaxLen
//bindings.LengthOffset = // set via MaxLen
//bindings.StatusOffset = // set via MaxLen
//bindings.TypeInfoPtr = 0;
//bindings.ObjectPtr = 0;
//bindings.BindExtPtr = 0;
//bindings.MemOwner = /*DBMEMOWNER_CLIENTOWNED*/0;
//bindings.ParamIO = ODB.DBPARAMIO_NOTPARAM;
//bindings.Flags = 0;
if ( Bid . AdvancedOn ) {
Bid . Trace ( "<oledb.struct.tagDBBINDING|INFO|ADV> index=%d, columnName='%ls'\n" , index , info . columnName ) ; //, bindings.bindings[index]);
}
}
int count = 0 , indexStart = 0 ;
for ( int i = 0 ; i < dbbindings . Length ; + + i ) {
indexStart = dbbindings [ i ] . AllocateForAccessor ( this , indexStart , i ) ;
ColumnBinding [ ] columnBindings = dbbindings [ i ] . ColumnBindings ( ) ;
for ( int k = 0 ; k < columnBindings . Length ; + + k ) {
Debug . Assert ( count = = columnBindings [ k ] . Index , "column binding mismatch" ) ;
metadata [ count ] . columnBinding = columnBindings [ k ] ;
metadata [ count ] . bindings = dbbindings [ i ] ;
count + + ;
}
}
_bindings = dbbindings ;
return dbbindings ;
}
private void GetRowHandles ( /*int skipCount*/ ) {
Debug . Assert ( 0 < ( int ) _rowHandleFetchCount , "GetRowHandles: bad _rowHandleFetchCount" ) ;
Debug . Assert ( ! _isRead , "GetRowHandles: _isRead" ) ;
OleDbHResult hr = 0 ;
RowHandleBuffer rowHandleBuffer = _rowHandleNativeBuffer ;
bool mustRelease = false ;
RuntimeHelpers . PrepareConstrainedRegions ( ) ;
try {
rowHandleBuffer . DangerousAddRef ( ref mustRelease ) ;
IntPtr rowHandlesPtr = rowHandleBuffer . DangerousGetHandle ( ) ;
UnsafeNativeMethods . IRowset irowset = IRowset ( ) ;
try {
Bid . Trace ( "<oledb.IRowset.GetNextRows|API|OLEDB> %d#, Chapter=%Id, RowsRequested=%Id\n" , ObjectID , _chapterHandle . HChapter , _rowHandleFetchCount ) ;
hr = irowset . GetNextRows ( _chapterHandle . HChapter , /*skipCount*/ IntPtr . Zero , _rowHandleFetchCount , out _rowFetchedCount , ref rowHandlesPtr ) ;
Bid . Trace ( "<oledb.IRowset.GetNextRows|API|OLEDB|RET> %08X{HRESULT}, RowsObtained=%Id\n" , hr , _rowFetchedCount ) ;
Debug . Assert ( rowHandleBuffer . DangerousGetHandle ( ) = = rowHandlesPtr , "rowhandlebuffer changed" ) ;
}
catch ( System . InvalidCastException e ) { // MDAC 64320
throw ODB . ThreadApartmentState ( e ) ;
}
}
finally {
if ( mustRelease ) {
rowHandleBuffer . DangerousRelease ( ) ;
}
}
//if (/*DB_S_ROWLIMITEXCEEDED*/0x00040EC0 == hr) {
// _rowHandleFetchCount = 1;
// _isRead = true;
//} else
if ( hr < 0 ) {
// filter out the BadStartPosition due to the skipCount which
// maybe greater than the number of rows in the return rowset
//const int /*OLEDB_Error.*/DB_E_BADSTARTPOSITION = unchecked((int)0x80040E1E);
//if (DB_E_BADSTARTPOSITION != hr)
ProcessResults ( hr ) ;
}
_isRead = ( ( OleDbHResult . DB_S_ENDOFROWSET ! = hr ) | | ( 0 < ( int ) _rowFetchedCount ) ) ; // MDAC 59264
_rowFetchedCount = ( IntPtr ) Math . Max ( ( int ) _rowFetchedCount , 0 ) ;
}
private void GetRowDataFromHandle ( ) {
Debug . Assert ( null ! = _bindings , "GetRowDataFromHandle: null dbBindings" ) ;
Debug . Assert ( null ! = _rowHandleNativeBuffer , "GetRowDataFromHandle: null dbBindings" ) ;
OleDbHResult hr = 0 ;
UnsafeNativeMethods . IRowset irowset = IRowset ( ) ;
IntPtr rowHandle = _rowHandleNativeBuffer . GetRowHandle ( _currentRow ) ;
RowBinding rowBinding = _bindings [ _nextAccessorForRetrieval ] . RowBinding ( ) ;
IntPtr accessorHandle = rowBinding . DangerousGetAccessorHandle ( ) ;
bool mustRelease = false ;
RuntimeHelpers . PrepareConstrainedRegions ( ) ;
try {
rowBinding . DangerousAddRef ( ref mustRelease ) ;
rowBinding . StartDataBlock ( ) ;
IntPtr dataPtr = rowBinding . DangerousGetDataPtr ( ) ;
Bid . Trace ( "<oledb.IRowset.GetData|API|OLEDB> %d#, RowHandle=%Id, AccessorHandle=%Id\n" , ObjectID , rowHandle , accessorHandle ) ;
hr = irowset . GetData ( rowHandle , accessorHandle , dataPtr ) ;
Bid . Trace ( "<oledb.IRowset.GetData|API|OLEDB|RET> %08X{HRESULT}\n" , hr ) ;
}
finally {
if ( mustRelease ) {
rowBinding . DangerousRelease ( ) ;
}
}
//if (AdapterSwitches.DataError.TraceWarning) {
// DBBindings binding = _dbBindings[i];
// for (int k = 0; k < binding.Count; ++k) {
// binding.CurrentIndex = k;
// Debug.WriteLine("Status[" + k.ToString(CultureInfo.InvariantCulture) + "] = " + binding.StatusValue.ToString("G", CultureInfo.InvariantCulture));
// }
//}
_nextAccessorForRetrieval + + ;
if ( hr < 0 ) {
ProcessResults ( hr ) ;
}
}
private void ReleaseRowHandles ( ) {
Debug . Assert ( 0 < ( int ) _rowFetchedCount , "invalid _rowFetchedCount" ) ;
OleDbHResult hr ;
UnsafeNativeMethods . IRowset irowset = IRowset ( ) ;
Bid . Trace ( "<oledb.IRowset.ReleaseRows|API|OLEDB> %d#, Request=%Id\n" , ObjectID , _rowFetchedCount ) ;
hr = irowset . ReleaseRows ( _rowFetchedCount , _rowHandleNativeBuffer , ADP . PtrZero , ADP . PtrZero , ADP . PtrZero ) ;
Bid . Trace ( "<oledb.IRowset.ReleaseRows|API|OLEDB|RET> %08X{HRESULT}\n" , hr ) ;
if ( hr < 0 ) {
//ProcessFailure(hr);
//ProcessFailure(hr);
SafeNativeMethods . Wrapper . ClearErrorInfo ( ) ;
}
_rowFetchedCount = IntPtr . Zero ;
_currentRow = 0 ;
_isRead = false ; // MDAC 59264
}
private object GetPropertyValue ( int propertyId ) { // MDAC 72106
if ( null ! = _irowset ) {
return GetPropertyOnRowset ( OleDbPropertySetGuid . Rowset , propertyId ) ;
}
else if ( null ! = _command ) {
return _command . GetPropertyValue ( OleDbPropertySetGuid . Rowset , propertyId ) ;
}
return OleDbPropertyStatus . NotSupported ;
}
private object GetPropertyOnRowset ( Guid propertySet , int propertyID ) {
OleDbHResult hr ;
tagDBPROP [ ] dbprops ;
UnsafeNativeMethods . IRowsetInfo irowsetinfo = IRowsetInfo ( ) ;
using ( PropertyIDSet propidset = new PropertyIDSet ( propertySet , propertyID ) ) {
using ( DBPropSet propset = new DBPropSet ( irowsetinfo , propidset , out hr ) ) {
if ( hr < 0 ) {
// VSDD 621427: OLEDB Data Reader masks provider specific errors by raising "Internal .Net Framework Data Provider error 30."
// DBPropSet c-tor will register the exception and it will be raised at GetPropertySet call in case of failure
SafeNativeMethods . Wrapper . ClearErrorInfo ( ) ;
}
dbprops = propset . GetPropertySet ( 0 , out propertySet ) ;
}
}
if ( OleDbPropertyStatus . Ok = = dbprops [ 0 ] . dwStatus ) {
return dbprops [ 0 ] . vValue ;
}
return dbprops [ 0 ] . dwStatus ;
}
private void GetRowValue ( ) {
Debug . Assert ( null ! = _irow , "GetRowValue: null IRow" ) ;
Debug . Assert ( null ! = _metadata , "GetRowValue: null MetaData" ) ;
Bindings bindings = _bindings [ _nextAccessorForRetrieval ] ;
ColumnBinding [ ] columnBindings = bindings . ColumnBindings ( ) ;
RowBinding rowBinding = bindings . RowBinding ( ) ;
Debug . Assert ( _nextValueForRetrieval < = columnBindings [ 0 ] . Index , "backwards retrieval" ) ;
bool mustReleaseBinding = false ;
bool [ ] mustRelease = new bool [ columnBindings . Length ] ;
StringMemHandle [ ] sptr = new StringMemHandle [ columnBindings . Length ] ;
RuntimeHelpers . PrepareConstrainedRegions ( ) ;
try {
for ( int i = 0 ; i < columnBindings . Length ; + + i ) {
bindings . CurrentIndex = i ;
sptr [ i ] = null ;
MetaData info = _metadata [ columnBindings [ i ] . Index ] ;
if ( ( ODB . DBKIND_GUID_NAME = = info . kind ) | | ( ODB . DBKIND_NAME = = info . kind ) ) {
sptr [ i ] = new StringMemHandle ( info . idname ) ;
columnBindings [ i ] . _sptr = sptr [ i ] ;
}
sptr [ i ] . DangerousAddRef ( ref mustRelease [ i ] ) ;
IntPtr ulPropid = ( ( null ! = sptr [ i ] ) ? sptr [ i ] . DangerousGetHandle ( ) : info . propid ) ;
bindings . GuidKindName ( info . guid , info . kind , ulPropid ) ;
}
OleDbHResult hr ;
tagDBCOLUMNACCESS [ ] access = bindings . DBColumnAccess ;
rowBinding . DangerousAddRef ( ref mustReleaseBinding ) ;
rowBinding . StartDataBlock ( ) ;
UnsafeNativeMethods . IRow irow = IRow ( ) ;
Bid . Trace ( "<oledb.IRow.GetColumns|API|OLEDB> %d#\n" , ObjectID ) ;
hr = irow . GetColumns ( ( IntPtr ) access . Length , access ) ;
Bid . Trace ( "<oledb.IRow.GetColumns|API|OLEDB|RET> %08X{HRESULT}\n" , hr ) ;
}
finally {
if ( mustReleaseBinding ) {
rowBinding . DangerousRelease ( ) ;
}
for ( int i = 0 ; i < mustRelease . Length ; i + + ) {
if ( mustRelease [ i ] ) {
sptr [ i ] . DangerousRelease ( ) ;
}
}
}
_nextAccessorForRetrieval + + ;
}
private Int32 IndexOf ( Hashtable hash , string name ) { // MDAC 67385
// via case sensitive search, first match with lowest ordinal matches
object index = hash [ name ] ;
if ( null ! = index ) {
return ( int ) index ; // match via case-insensitive or by chance lowercase
}
// via case insensitive search, first match with lowest ordinal matches
string tmp = name . ToLower ( CultureInfo . InvariantCulture ) ;
index = hash [ tmp ] ; // match via lowercase
return ( ( null ! = index ) ? ( int ) index : - 1 ) ;
}
private void AppendSchemaInfo ( ) {
Debug . Assert ( null ! = _connection , "null connection" ) ;
Debug . Assert ( null ! = _metadata , "no _metadata" ) ;
if ( _metadata . Length < = 0 ) {
return ;
}
int keyCount = 0 ;
for ( int i = 0 ; i < _metadata . Length ; + + i ) {
if ( _metadata [ i ] . isKeyColumn & & ! _metadata [ i ] . isHidden ) { // MDAC 90411
keyCount + + ;
}
}
if ( 0 ! = keyCount ) /*|| _connection.IsServer_msdaora || _connection.IsServer_Microsoft_SQL)*/ { // MDAC 60109
return ;
}
string schemaName , catalogName ; // enforce single table
string baseSchemaName = null , baseCatalogName = null , baseTableName = null ;
for ( int i = 0 ; i < _metadata . Length ; + + i ) {
MetaData info = _metadata [ i ] ;
if ( ( null ! = info . baseTableName ) & & ( 0 < info . baseTableName . Length ) ) {
catalogName = ( ( null ! = info . baseCatalogName ) ? info . baseCatalogName : "" ) ; // MDAC 67249
schemaName = ( ( null ! = info . baseSchemaName ) ? info . baseSchemaName : "" ) ;
if ( null = = baseTableName ) {
baseSchemaName = schemaName ;
baseCatalogName = catalogName ;
baseTableName = info . baseTableName ;
}
else if ( ( 0 ! = ADP . SrcCompare ( baseTableName , info . baseTableName ) )
| | ( 0 ! = ADP . SrcCompare ( baseCatalogName , catalogName ) )
| | ( 0 ! = ADP . SrcCompare ( baseSchemaName , schemaName ) ) ) { // MDAC 71808
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "Multiple BaseTableName detected:"
+ " <" + baseCatalogName + "." + baseCatalogName + "." + baseTableName + ">"
+ " <" + info . baseCatalogName + "." + info . baseCatalogName + "." + info . baseTableName + ">" ) ;
}
#endif
baseTableName = null ;
break ;
}
}
}
if ( null = = baseTableName ) {
return ;
}
baseCatalogName = ADP . IsEmpty ( baseCatalogName ) ? null : baseCatalogName ;
baseSchemaName = ADP . IsEmpty ( baseSchemaName ) ? null : baseSchemaName ;
if ( null ! = _connection ) { // MDAC 67394
if ( ODB . DBPROPVAL_IC_SENSITIVE = = _connection . QuotedIdentifierCase ( ) ) {
string p = null , s = null ;
_connection . GetLiteralQuotes ( ADP . GetSchemaTable , out s , out p ) ;
if ( null = = s ) {
s = "" ;
}
if ( null = = p ) {
p = "" ;
}
baseTableName = s + baseTableName + p ;
}
}
Hashtable baseColumnNames = new Hashtable ( _metadata . Length * 2 ) ; // MDAC 67385
for ( int i = _metadata . Length - 1 ; 0 < = i ; - - i ) {
string basecolumname = _metadata [ i ] . baseColumnName ;
if ( ! ADP . IsEmpty ( basecolumname ) ) {
baseColumnNames [ basecolumname ] = i ;
}
}
for ( int i = 0 ; i < _metadata . Length ; + + i ) {
string basecolumname = _metadata [ i ] . baseColumnName ;
if ( ! ADP . IsEmpty ( basecolumname ) ) {
basecolumname = basecolumname . ToLower ( CultureInfo . InvariantCulture ) ;
if ( ! baseColumnNames . Contains ( basecolumname ) ) {
baseColumnNames [ basecolumname ] = i ;
}
}
}
// look for primary keys in the table
if ( _connection . SupportSchemaRowset ( OleDbSchemaGuid . Primary_Keys ) ) {
Object [ ] restrictions = new Object [ ] { baseCatalogName , baseSchemaName , baseTableName } ;
keyCount = AppendSchemaPrimaryKey ( baseColumnNames , restrictions ) ;
}
if ( 0 ! = keyCount ) {
return ;
}
// look for a single unique contraint that can be upgraded
if ( _connection . SupportSchemaRowset ( OleDbSchemaGuid . Indexes ) ) {
Object [ ] restrictions = new Object [ ] { baseCatalogName , baseSchemaName , null , null , baseTableName } ;
AppendSchemaUniqueIndexAsKey ( baseColumnNames , restrictions ) ;
}
}
private int AppendSchemaPrimaryKey ( Hashtable baseColumnNames , object [ ] restrictions ) {
int keyCount = 0 ;
bool partialPrimaryKey = false ;
DataTable table = null ;
try {
table = _connection . GetSchemaRowset ( OleDbSchemaGuid . Primary_Keys , restrictions ) ;
}
catch ( Exception e ) {
//
if ( ! ADP . IsCatchableExceptionType ( e ) ) {
throw ;
}
ADP . TraceExceptionWithoutRethrow ( e ) ;
}
if ( null ! = table ) {
DataColumnCollection dataColumns = table . Columns ;
int nameColumnIndex = dataColumns . IndexOf ( ODB . COLUMN_NAME ) ;
if ( - 1 ! = nameColumnIndex ) {
DataColumn nameColumn = dataColumns [ nameColumnIndex ] ;
foreach ( DataRow dataRow in table . Rows ) {
string name = ( string ) dataRow [ nameColumn , DataRowVersion . Default ] ;
int metaindex = IndexOf ( baseColumnNames , name ) ; // MDAC 67385
if ( 0 < = metaindex ) {
MetaData info = _metadata [ metaindex ] ;
info . isKeyColumn = true ;
info . flags & = ~ ODB . DBCOLUMNFLAGS_ISNULLABLE ;
keyCount + + ;
}
else {
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "PartialKeyColumn detected: <" + name + "> metaindex=" + metaindex ) ;
}
#endif
partialPrimaryKey = true ;
break ;
}
}
}
}
if ( partialPrimaryKey ) { // partial primary key detected
for ( int i = 0 ; i < _metadata . Length ; + + i ) {
_metadata [ i ] . isKeyColumn = false ;
}
return - 1 ;
}
return keyCount ;
}
private void AppendSchemaUniqueIndexAsKey ( Hashtable baseColumnNames , object [ ] restrictions ) {
bool partialPrimaryKey = false ;
DataTable table = null ;
try { // MDAC 66209
table = _connection . GetSchemaRowset ( OleDbSchemaGuid . Indexes , restrictions ) ;
}
catch ( Exception e ) {
//
if ( ! ADP . IsCatchableExceptionType ( e ) ) {
throw ;
}
ADP . TraceExceptionWithoutRethrow ( e ) ;
}
if ( null ! = table ) {
DataColumnCollection dataColumns = table . Columns ;
int indxIndex = dataColumns . IndexOf ( ODB . INDEX_NAME ) ;
int pkeyIndex = dataColumns . IndexOf ( ODB . PRIMARY_KEY ) ;
int uniqIndex = dataColumns . IndexOf ( ODB . UNIQUE ) ;
int nameIndex = dataColumns . IndexOf ( ODB . COLUMN_NAME ) ;
int nullIndex = dataColumns . IndexOf ( ODB . NULLS ) ;
if ( ( - 1 ! = indxIndex ) & & ( - 1 ! = pkeyIndex ) & & ( - 1 ! = uniqIndex ) & & ( - 1 ! = nameIndex ) ) {
DataColumn indxColumn = dataColumns [ indxIndex ] ;
DataColumn pkeyColumn = dataColumns [ pkeyIndex ] ;
DataColumn uniqCOlumn = dataColumns [ uniqIndex ] ;
DataColumn nameColumn = dataColumns [ nameIndex ] ;
DataColumn nulls = ( ( - 1 ! = nullIndex ) ? dataColumns [ nullIndex ] : null ) ;
bool [ ] keys = new bool [ _metadata . Length ] ;
bool [ ] uniq = new bool [ _metadata . Length ] ;
string uniqueIndexName = null ;
// match pkey name BaseColumnName
foreach ( DataRow dataRow in table . Rows ) {
bool isPKey = ( ! dataRow . IsNull ( pkeyColumn , DataRowVersion . Default ) & & ( bool ) dataRow [ pkeyColumn , DataRowVersion . Default ] ) ;
bool isUniq = ( ! dataRow . IsNull ( uniqCOlumn , DataRowVersion . Default ) & & ( bool ) dataRow [ uniqCOlumn , DataRowVersion . Default ] ) ;
bool nullsVal = ( null ! = nulls ) & & ( dataRow . IsNull ( nulls , DataRowVersion . Default ) | | ( ODB . DBPROPVAL_IN_ALLOWNULL = = Convert . ToInt32 ( dataRow [ nulls , DataRowVersion . Default ] , CultureInfo . InvariantCulture ) ) ) ;
if ( isPKey | | isUniq ) {
string name = ( string ) dataRow [ nameColumn , DataRowVersion . Default ] ;
int metaindex = IndexOf ( baseColumnNames , name ) ; // MDAC 67385
if ( 0 < = metaindex ) {
if ( isPKey ) {
keys [ metaindex ] = true ;
}
if ( isUniq & & ( null ! = uniq ) ) {
uniq [ metaindex ] = true ;
string indexname = ( string ) dataRow [ indxColumn , DataRowVersion . Default ] ;
if ( null = = uniqueIndexName ) {
uniqueIndexName = indexname ;
}
else if ( indexname ! = uniqueIndexName ) {
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "MultipleUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">" ) ;
}
#endif
uniq = null ;
}
}
}
else if ( isPKey ) {
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "PartialKeyColumn detected: " + name ) ;
}
#endif
partialPrimaryKey = true ;
break ;
}
else if ( null ! = uniqueIndexName ) {
string indexname = ( string ) dataRow [ indxColumn , DataRowVersion . Default ] ;
if ( indexname ! = uniqueIndexName ) {
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "PartialUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">" ) ;
}
#endif
uniq = null ;
}
}
}
}
if ( partialPrimaryKey ) {
for ( int i = 0 ; i < _metadata . Length ; + + i ) {
_metadata [ i ] . isKeyColumn = false ;
}
return ;
}
else if ( null ! = uniq ) {
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "upgrade single unique index to be a key: <" + uniqueIndexName + ">" ) ;
}
#endif
// upgrade single unique index to be a key
for ( int i = 0 ; i < _metadata . Length ; + + i ) {
_metadata [ i ] . isKeyColumn = uniq [ i ] ;
}
}
}
}
}
private MetaData FindMetaData ( string name ) {
int index = _fieldNameLookup . IndexOfName ( name ) ;
return ( ( - 1 ! = index ) ? _metadata [ index ] : null ) ;
}
internal void DumpToSchemaTable ( UnsafeNativeMethods . IRowset rowset ) {
List < MetaData > metainfo = new List < MetaData > ( ) ;
object hiddenColumns = null ;
using ( OleDbDataReader dataReader = new OleDbDataReader ( _connection , _command , Int32 . MinValue , 0 ) ) {
dataReader . InitializeIRowset ( rowset , ChapterHandle . DB_NULL_HCHAPTER , IntPtr . Zero ) ;
dataReader . BuildSchemaTableInfo ( rowset , true , false ) ;
hiddenColumns = GetPropertyValue ( ODB . DBPROP_HIDDENCOLUMNS ) ; // MDAC 55611, 72106
if ( 0 = = dataReader . FieldCount ) {
return ;
}
Debug . Assert ( null = = dataReader . _fieldNameLookup , "lookup already exists" ) ;
FieldNameLookup lookup = new FieldNameLookup ( dataReader , - 1 ) ;
dataReader . _fieldNameLookup = lookup ;
// This column, together with the DBCOLUMN_GUID and DBCOLUMN_PROPID
// columns, forms the ID of the column. One or more (but not all) of these columns
// will be NULL, depending on which elements of the DBID structure the provider uses.
MetaData columnidname = dataReader . FindMetaData ( ODB . DBCOLUMN_IDNAME ) ;
MetaData columnguid = dataReader . FindMetaData ( ODB . DBCOLUMN_GUID ) ;
MetaData columnpropid = dataReader . FindMetaData ( ODB . DBCOLUMN_PROPID ) ;
MetaData columnname = dataReader . FindMetaData ( ODB . DBCOLUMN_NAME ) ;
MetaData columnordinal = dataReader . FindMetaData ( ODB . DBCOLUMN_NUMBER ) ;
MetaData dbtype = dataReader . FindMetaData ( ODB . DBCOLUMN_TYPE ) ;
MetaData columnsize = dataReader . FindMetaData ( ODB . DBCOLUMN_COLUMNSIZE ) ;
MetaData numericprecision = dataReader . FindMetaData ( ODB . DBCOLUMN_PRECISION ) ;
MetaData numericscale = dataReader . FindMetaData ( ODB . DBCOLUMN_SCALE ) ;
MetaData columnflags = dataReader . FindMetaData ( ODB . DBCOLUMN_FLAGS ) ;
MetaData baseschemaname = dataReader . FindMetaData ( ODB . DBCOLUMN_BASESCHEMANAME ) ;
MetaData basecatalogname = dataReader . FindMetaData ( ODB . DBCOLUMN_BASECATALOGNAME ) ;
MetaData basetablename = dataReader . FindMetaData ( ODB . DBCOLUMN_BASETABLENAME ) ;
MetaData basecolumnname = dataReader . FindMetaData ( ODB . DBCOLUMN_BASECOLUMNNAME ) ;
MetaData isautoincrement = dataReader . FindMetaData ( ODB . DBCOLUMN_ISAUTOINCREMENT ) ;
MetaData isunique = dataReader . FindMetaData ( ODB . DBCOLUMN_ISUNIQUE ) ;
MetaData iskeycolumn = dataReader . FindMetaData ( ODB . DBCOLUMN_KEYCOLUMN ) ;
// @devnote: because we want to use the DBACCESSOR_OPTIMIZED bit,
// we are required to create the accessor before fetching any rows
dataReader . CreateAccessors ( false ) ;
ColumnBinding binding ;
while ( dataReader . ReadRowset ( ) ) {
dataReader . GetRowDataFromHandle ( ) ;
MetaData info = new MetaData ( ) ;
binding = columnidname . columnBinding ; // MDAC 72627
if ( ! binding . IsValueNull ( ) ) {
info . idname = ( string ) binding . Value ( ) ;
info . kind = ODB . DBKIND_NAME ;
}
binding = columnguid . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . guid = binding . Value_GUID ( ) ;
info . kind = ( ( ODB . DBKIND_NAME = = info . kind ) ? ODB . DBKIND_GUID_NAME : ODB . DBKIND_GUID ) ;
}
binding = columnpropid . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . propid = new IntPtr ( binding . Value_UI4 ( ) ) ;
info . kind = ( ( ODB . DBKIND_GUID = = info . kind ) ? ODB . DBKIND_GUID_PROPID : ODB . DBKIND_PROPID ) ;
}
binding = columnname . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . columnName = ( string ) binding . Value ( ) ;
}
else {
info . columnName = "" ;
}
if ( 4 = = ADP . PtrSize ) {
info . ordinal = ( IntPtr ) columnordinal . columnBinding . Value_UI4 ( ) ;
}
else {
info . ordinal = ( IntPtr ) columnordinal . columnBinding . Value_UI8 ( ) ;
}
short wType = unchecked ( ( short ) dbtype . columnBinding . Value_UI2 ( ) ) ;
if ( 4 = = ADP . PtrSize ) {
info . size = unchecked ( ( int ) columnsize . columnBinding . Value_UI4 ( ) ) ; // WebData 99298
}
else {
info . size = ADP . IntPtrToInt32 ( ( IntPtr ) unchecked ( ( long ) columnsize . columnBinding . Value_UI8 ( ) ) ) ;
}
binding = numericprecision . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . precision = ( byte ) binding . Value_UI2 ( ) ;
}
binding = numericscale . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . scale = ( byte ) binding . Value_I2 ( ) ;
}
info . flags = unchecked ( ( int ) columnflags . columnBinding . Value_UI4 ( ) ) ;
bool islong = OleDbDataReader . IsLong ( info . flags ) ;
bool isfixed = OleDbDataReader . IsFixed ( info . flags ) ;
NativeDBType dbType = NativeDBType . FromDBType ( wType , islong , isfixed ) ;
info . type = dbType ;
if ( null ! = isautoincrement ) {
binding = isautoincrement . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . isAutoIncrement = binding . Value_BOOL ( ) ;
}
}
if ( null ! = isunique ) {
binding = isunique . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . isUnique = binding . Value_BOOL ( ) ;
}
}
if ( null ! = iskeycolumn ) {
binding = iskeycolumn . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . isKeyColumn = binding . Value_BOOL ( ) ;
}
}
if ( null ! = baseschemaname ) {
binding = baseschemaname . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . baseSchemaName = binding . ValueString ( ) ;
}
}
if ( null ! = basecatalogname ) {
binding = basecatalogname . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . baseCatalogName = binding . ValueString ( ) ;
}
}
if ( null ! = basetablename ) {
binding = basetablename . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . baseTableName = binding . ValueString ( ) ;
}
}
if ( null ! = basecolumnname ) {
binding = basecolumnname . columnBinding ;
if ( ! binding . IsValueNull ( ) ) {
info . baseColumnName = binding . ValueString ( ) ;
}
}
metainfo . Add ( info ) ;
}
}
int visibleCount = metainfo . Count ;
if ( hiddenColumns is Int32 ) {
visibleCount - = ( int ) hiddenColumns ;
}
// VSTFDevDiv 479578:
// if one key column is invalidated, they all need to be invalidated. The SET is the key,
// and subsets likely are not accurate keys. Note the assumption that the two loops below
// will traverse the entire set of columns.
bool disallowKeyColumns = false ;
for ( int index = metainfo . Count - 1 ; visibleCount < = index ; - - index ) {
MetaData info = metainfo [ index ] ;
info . isHidden = true ;
if ( disallowKeyColumns ) {
info . isKeyColumn = false ;
}
else if ( info . guid . Equals ( ODB . DBCOL_SPECIALCOL ) ) { // MDAC 90827
info . isKeyColumn = false ;
// This is the first key column to be invalidated, scan back through the
// columns we already processed to make sure none of those are marked as keys.
disallowKeyColumns = true ;
for ( int index2 = metainfo . Count - 1 ; index < index2 ; - - index2 ) {
metainfo [ index2 ] . isKeyColumn = false ;
}
}
}
for ( int index = visibleCount - 1 ; 0 < = index ; - - index ) {
MetaData info = metainfo [ index ] ;
if ( disallowKeyColumns ) {
info . isKeyColumn = false ;
}
if ( info . guid . Equals ( ODB . DBCOL_SPECIALCOL ) ) { // MDAC 72390
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "Filtered Column: DBCOLUMN_GUID=DBCOL_SPECIALCOL DBCOLUMN_NAME=" + info . columnName + " DBCOLUMN_KEYCOLUMN=" + info . isKeyColumn ) ;
}
#endif
info . isHidden = true ;
visibleCount - - ;
}
#if WIN32
else if ( 0 > = ( int ) info . ordinal ) {
#else
else if ( 0 > = ( long ) info . ordinal ) {
#endif
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "Filtered Column: DBCOLUMN_NUMBER=" + info . ordinal . ToInt64 ( ) . ToString ( CultureInfo . InvariantCulture ) + " DBCOLUMN_NAME=" + info . columnName ) ;
}
#endif
info . isHidden = true ;
visibleCount - - ;
}
else if ( OleDbDataReader . DoColumnDropFilter ( info . flags ) ) {
#if DEBUG
if ( AdapterSwitches . DataSchema . TraceVerbose ) {
Debug . WriteLine ( "Filtered Column: DBCOLUMN_FLAGS=" + info . flags . ToString ( "X8" , ( System . IFormatProvider ) null ) + " DBCOLUMN_NAME=" + info . columnName ) ;
}
#endif
info . isHidden = true ;
visibleCount - - ;
}
}
//
metainfo . Sort ( ) ; // MDAC 68319
_visibleFieldCount = visibleCount ;
_metadata = metainfo . ToArray ( ) ;
}
static internal void GenerateSchemaTable ( OleDbDataReader dataReader , object handle , CommandBehavior behavior ) {
if ( 0 ! = ( CommandBehavior . KeyInfo & behavior ) ) {
dataReader . BuildSchemaTableRowset ( handle ) ; // tries IColumnsRowset first then IColumnsInfo
dataReader . AppendSchemaInfo ( ) ;
}
else {
dataReader . BuildSchemaTableInfo ( handle , false , false ) ; // only tries IColumnsInfo
}
MetaData [ ] metadata = dataReader . MetaData ;
if ( ( null ! = metadata ) & & ( 0 < metadata . Length ) ) {
dataReader . BuildSchemaTable ( metadata ) ;
}
}
static private bool DoColumnDropFilter ( int flags ) {
return ( 0 ! = ( ODB . DBCOLUMNFLAGS_ISBOOKMARK & flags ) ) ;
}
static private bool IsLong ( int flags ) {
return ( 0 ! = ( ODB . DBCOLUMNFLAGS_ISLONG & flags ) ) ;
}
static private bool IsFixed ( int flags ) {
return ( 0 ! = ( ODB . DBCOLUMNFLAGS_ISFIXEDLENGTH & flags ) ) ;
}
static private bool IsRowVersion ( int flags ) {
return ( 0 ! = ( ODB . DBCOLUMNFLAGS_ISROWID_DBCOLUMNFLAGS_ISROWVER & flags ) ) ;
}
static private bool AllowDBNull ( int flags ) {
return ( 0 ! = ( ODB . DBCOLUMNFLAGS_ISNULLABLE & flags ) ) ;
}
static private bool AllowDBNullMaybeNull ( int flags ) {
return ( 0 ! = ( ODB . DBCOLUMNFLAGS_ISNULLABLE_DBCOLUMNFLAGS_MAYBENULL & flags ) ) ;
}
static private bool IsReadOnly ( int flags ) {
return ( 0 = = ( ODB . DBCOLUMNFLAGS_WRITE_DBCOLUMNFLAGS_WRITEUNKNOWN & flags ) ) ;
}
}
sealed internal class MetaData : IComparable {
internal Bindings bindings ;
internal ColumnBinding columnBinding ;
internal string columnName ;
internal Guid guid ; // MDAC 72627
internal int kind ;
internal IntPtr propid ;
internal string idname ;
internal NativeDBType type ;
internal IntPtr ordinal ;
internal int size ;
internal int flags ;
internal byte precision ;
internal byte scale ;
internal bool isAutoIncrement ;
internal bool isUnique ;
internal bool isKeyColumn ;
internal bool isHidden ;
internal string baseSchemaName ;
internal string baseCatalogName ;
internal string baseTableName ;
internal string baseColumnName ;
int IComparable . CompareTo ( object obj ) { // MDAC 68319
if ( isHidden = = ( obj as MetaData ) . isHidden ) {
#if WIN32
return ( ( int ) ordinal - ( int ) ( obj as MetaData ) . ordinal ) ;
#else
long v = ( ( long ) ordinal - ( long ) ( obj as MetaData ) . ordinal ) ;
return ( ( 0 < v ) ? 1 : ( ( v < 0 ) ? - 1 : 0 ) ) ;
#endif
}
return ( isHidden ) ? 1 : - 1 ; // ensure that all hidden columns come after non-hidden columns
}
internal MetaData ( ) {
}
}
}