Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / OleDb / OleDbDataReader.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="OleDbDataReader.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.OleDb {
10
11     using System;
12     using System.Collections;
13     using System.Collections.Generic;
14     using System.ComponentModel;
15     using System.Data;
16     using System.Data.Common;
17     using System.Data.ProviderBase;
18     using System.Diagnostics;
19     using System.Globalization;
20     using System.IO;
21     using System.Runtime.CompilerServices;
22     using System.Runtime.InteropServices;
23     using System.Text;
24
25     public sealed class OleDbDataReader : DbDataReader {
26
27         private CommandBehavior     _commandBehavior;
28
29         private static int          _objectTypeCount; // Bid counter
30         internal readonly int       ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
31
32         // object model interaction
33         private OleDbConnection _connection;
34         private OleDbCommand _command;
35
36         // DataReader owns the parameter bindings until CloseDataReader
37         // this allows OleDbCommand.Dispose to not require OleDbDataReader.Dispose
38         private Bindings _parameterBindings;
39
40         // OLEDB interfaces
41         private UnsafeNativeMethods.IMultipleResults _imultipleResults;
42         private UnsafeNativeMethods.IRowset _irowset;
43         private UnsafeNativeMethods.IRow _irow;
44
45         private ChapterHandle _chapterHandle = ChapterHandle.DB_NULL_HCHAPTER;
46
47         private int _depth;
48         private bool _isClosed, _isRead, _hasRows, _hasRowsReadCheck;
49
50         long _sequentialBytesRead;
51         int _sequentialOrdinal;
52
53         private Bindings[] _bindings; // _metdata contains the ColumnBinding
54
55         // do we need to jump to the next accessor
56         private int _nextAccessorForRetrieval;
57
58         // must increment the counter before retrieving value so that
59         // if an exception is thrown, user can continue without erroring again
60         private int _nextValueForRetrieval;
61
62         // record affected for the current dataset
63         private IntPtr _recordsAffected = ADP.RecordsUnaffected;
64         private bool _useIColumnsRowset;
65         private bool _sequentialAccess;
66         private bool _singleRow;
67
68         // cached information for Reading (rowhandles/status)
69         private IntPtr _rowHandleFetchCount; // MDAC 60111 (>1 fails against jet)
70         private RowHandleBuffer _rowHandleNativeBuffer;
71
72         private IntPtr _rowFetchedCount;
73         private int _currentRow;
74
75         private DataTable _dbSchemaTable;
76
77         private int _visibleFieldCount;
78         private MetaData[] _metadata;
79         private FieldNameLookup _fieldNameLookup;
80
81         // ctor for an ICommandText, IMultipleResults, IRowset, IRow
82         // ctor for an ADODB.Recordset, ADODB.Record or Hierarchial resultset
83         internal OleDbDataReader(OleDbConnection connection, OleDbCommand command, int depth, CommandBehavior commandBehavior) {
84             OleDbConnection.VerifyExecutePermission();
85             
86             _connection = connection;
87             _command = command;
88             _commandBehavior = commandBehavior;
89
90             if ((null != command) && (0 == _depth)) {
91                 _parameterBindings = command.TakeBindingOwnerShip();
92             }
93             _depth = depth;
94         }
95
96         private void Initialize() {
97             CommandBehavior behavior = _commandBehavior;
98             _useIColumnsRowset = (0 != (CommandBehavior.KeyInfo & behavior));
99             _sequentialAccess  = (0 != (CommandBehavior.SequentialAccess & behavior)); // MDAC 60296
100             if (0 == _depth) { // MDAC 70886
101                 _singleRow     = (0 != (CommandBehavior.SingleRow & behavior));
102             }
103         }
104
105         internal void InitializeIMultipleResults(object result) {
106             Initialize();
107             _imultipleResults = (UnsafeNativeMethods.IMultipleResults) result; // maybe null if no results
108         }
109         internal void InitializeIRowset(object result, ChapterHandle chapterHandle, IntPtr recordsAffected) {
110             // if from ADODB, connection will be null
111             if ((null == _connection) || (ChapterHandle.DB_NULL_HCHAPTER != chapterHandle)) { // MDAC 59629
112                 _rowHandleFetchCount = new IntPtr(1);
113             }
114
115             Initialize();
116             _recordsAffected = recordsAffected;
117             _irowset = (UnsafeNativeMethods.IRowset) result; // maybe null if no results
118             _chapterHandle = chapterHandle;
119         }
120
121         internal void InitializeIRow(object result, IntPtr recordsAffected) {
122             Initialize();
123             Debug.Assert(_singleRow, "SingleRow not already set");
124             _singleRow = true;
125             _recordsAffected = recordsAffected;
126             _irow = (UnsafeNativeMethods.IRow) result; // maybe null if no results
127             _hasRows = (null != _irow);
128         }
129
130         internal OleDbCommand Command {
131             get {
132                 return _command;
133             }
134         }
135
136         override public int Depth {
137             get {
138                 Bid.Trace("<oledb.OleDbDataReader.get_Depth|API> %d#\n", ObjectID);
139                 if (IsClosed) { // MDAC 63669
140                     throw ADP.DataReaderClosed("Depth");
141                 }
142                 return _depth;
143             }
144         }
145
146         override public Int32 FieldCount {
147             get {
148                 Bid.Trace("<oledb.OleDbDataReader.get_FieldCount|API> %d#\n", ObjectID);
149                 if (IsClosed) { // MDAC 63669
150                     throw ADP.DataReaderClosed("FieldCount");
151                 }
152                 MetaData[] metadata = MetaData;
153                 return ((null != metadata) ? metadata.Length : 0);
154             }
155         }
156
157         override public bool HasRows { // MDAC 78405
158             get {
159                 Bid.Trace("<oledb.OleDbDataReader.get_HasRows|API> %d#\n", ObjectID);
160                 if (IsClosed) { // MDAC 63669
161                     throw ADP.DataReaderClosed("HasRows");
162                 }
163                 return _hasRows;
164             }
165         }
166
167         override public Boolean IsClosed {
168             get { // if we have a rowset or multipleresults, we may have more to read
169                 Bid.Trace("<oledb.OleDbDataReader.get_IsClosed|API> %d#\n", ObjectID);
170                 Debug.Assert((_singleRow && !_isClosed && !_isRead && (null == _irow) && (null == _irowset)) ||
171                              _isClosed == ((null == _irow) && (null == _irowset) && (null == _imultipleResults)
172                                            && (null == _dbSchemaTable) && (null == _connection) && (null == _command)), // MDAC 59536
173                                            "IsClosed mismatch");
174                 return _isClosed;
175             }
176         }
177
178         private MetaData[] MetaData {
179             get { return _metadata; }
180         }
181
182         override public int RecordsAffected {
183             get {
184                 Bid.Trace("<oledb.OleDbDataReader.get_RecordsAffected|API> %d#\n", ObjectID);
185                 return ADP.IntPtrToInt32(_recordsAffected);
186             }
187         }
188
189         /*
190         internal long RecordsAffectedLong {
191             get {
192                 return (long)_recordsAffected;
193             }
194         }*/
195
196         override public object this[Int32 index] {
197             get {
198                 return GetValue(index);
199             }
200         }
201
202         override public object this[String name] {
203             get {
204                 int ordinal = GetOrdinal(name);
205                 return GetValue(ordinal);
206             }
207         }
208
209         // grouping the native OLE DB casts togther by required interfaces and optional interfaces
210         // want these to be methods, not properties otherwise they appear in VS7 managed debugger which attempts to evaluate them
211
212         // required interface, safe cast
213         private UnsafeNativeMethods.IAccessor IAccessor() {
214             Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|rowset> %d#, IAccessor\n", ObjectID);
215             return (UnsafeNativeMethods.IAccessor) IRowset();
216         }
217
218         // required interface, safe cast
219         private UnsafeNativeMethods.IRowsetInfo IRowsetInfo() {
220             Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|rowset> %d#, IRowsetInfo\n", ObjectID);
221             return (UnsafeNativeMethods.IRowsetInfo) IRowset();
222         }
223
224         private UnsafeNativeMethods.IRowset IRowset() {
225             UnsafeNativeMethods.IRowset irowset = _irowset;
226             if (null == irowset) {
227                 Debug.Assert(false, "object is disposed");
228                 throw new ObjectDisposedException(GetType().Name);
229             }
230             return irowset;
231         }
232
233         private UnsafeNativeMethods.IRow IRow() {
234             UnsafeNativeMethods.IRow irow = _irow;
235             if (null == irow) {
236                 Debug.Assert(false, "object is disposed");
237                 throw new ObjectDisposedException(GetType().Name);
238             }
239             return irow;
240         }
241
242         override public DataTable GetSchemaTable() {
243             IntPtr hscp;
244             Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.GetSchemaTable|API> %d#\n", ObjectID);
245             try {
246                 DataTable schemaTable = _dbSchemaTable;
247                 if (null == schemaTable) {
248                     MetaData[] metadata = MetaData;
249                     if ((null != metadata) && (0 < metadata.Length)) {
250                         if ((0 < metadata.Length) && _useIColumnsRowset && (null != _connection)) {
251                             AppendSchemaInfo();
252                         }
253                         schemaTable = BuildSchemaTable(metadata);
254                     }
255                     else if (IsClosed) { // MDAC 68331
256                         throw ADP.DataReaderClosed("GetSchemaTable");
257                     }
258                     //GetSchemaTable() is defined to return null after NextResult returns false
259                     //throw ADP.DataReaderNoData();
260                 }
261                 return schemaTable;
262             }
263             finally {
264                 Bid.ScopeLeave(ref hscp);
265             }
266         }
267
268         internal void BuildMetaInfo() {
269             Debug.Assert(null == _metadata, "BuildMetaInfo: already built, by _metadata");
270
271             if (null != _irowset) {
272                 if (_useIColumnsRowset) {
273                     BuildSchemaTableRowset(_irowset);
274                 }
275                 else {
276                     BuildSchemaTableInfo(_irowset, false, false);
277                 }
278                 if (null != _metadata && 0 < _metadata.Length) {
279                     // @devnote: because we want to use the DBACCESSOR_OPTIMIZED bit,
280                     // we are required to create the accessor before fetching any rows
281                     CreateAccessors(true);
282                     Debug.Assert(null != _bindings, "unexpected dbBindings");
283                 }
284             }
285             else if (null != _irow) {
286                 BuildSchemaTableInfo(_irow, false, false);
287                 if (null != _metadata && 0 < _metadata.Length) {
288                     CreateBindingsFromMetaData(true);
289                 }
290             }
291             if (null == _metadata) {
292                 _hasRows = false;
293                 _visibleFieldCount = 0;
294                 _metadata = new MetaData[0];
295             }
296         }
297
298         private DataTable BuildSchemaTable(MetaData[] metadata) {
299             Debug.Assert(null == _dbSchemaTable, "BuildSchemaTable: schema table already exists");
300             Debug.Assert(null != metadata, "BuildSchemaTable: no _metadata");
301
302             DataTable schemaTable = new DataTable("SchemaTable");
303             schemaTable.Locale = CultureInfo.InvariantCulture;
304             schemaTable.MinimumCapacity = metadata.Length;
305
306             DataColumn name       = new DataColumn("ColumnName",       typeof(System.String));
307             DataColumn ordinal    = new DataColumn("ColumnOrdinal",    typeof(System.Int32));
308             DataColumn size       = new DataColumn("ColumnSize",       typeof(System.Int32));
309             DataColumn precision  = new DataColumn("NumericPrecision", typeof(System.Int16));
310             DataColumn scale      = new DataColumn("NumericScale",     typeof(System.Int16));
311
312             DataColumn dataType   = new DataColumn("DataType",         typeof(System.Type));
313             DataColumn providerType = new DataColumn("ProviderType",   typeof(System.Int32));
314
315             DataColumn isLong        = new DataColumn("IsLong",           typeof(System.Boolean));
316             DataColumn allowDBNull   = new DataColumn("AllowDBNull",      typeof(System.Boolean));
317             DataColumn isReadOnly    = new DataColumn("IsReadOnly",       typeof(System.Boolean));
318             DataColumn isRowVersion  = new DataColumn("IsRowVersion",     typeof(System.Boolean));
319
320             DataColumn isUnique        = new DataColumn("IsUnique",        typeof(System.Boolean));
321             DataColumn isKey           = new DataColumn("IsKey",           typeof(System.Boolean));
322             DataColumn isAutoIncrement = new DataColumn("IsAutoIncrement", typeof(System.Boolean));
323             DataColumn isHidden        = new DataColumn("IsHidden",        typeof(System.Boolean));
324
325             DataColumn baseSchemaName  = new DataColumn("BaseSchemaName",  typeof(System.String));
326             DataColumn baseCatalogName = new DataColumn("BaseCatalogName", typeof(System.String));
327             DataColumn baseTableName   = new DataColumn("BaseTableName",   typeof(System.String));
328             DataColumn baseColumnName  = new DataColumn("BaseColumnName",  typeof(System.String));
329
330             ordinal.DefaultValue = 0;
331             isLong.DefaultValue = false;
332
333             DataColumnCollection columns = schemaTable.Columns;
334
335             columns.Add(name);
336             columns.Add(ordinal);
337             columns.Add(size);
338             columns.Add(precision);
339             columns.Add(scale);
340
341             columns.Add(dataType);
342             columns.Add(providerType);
343
344             columns.Add(isLong);
345             columns.Add(allowDBNull);
346             columns.Add(isReadOnly);
347             columns.Add(isRowVersion);
348
349             columns.Add(isUnique);
350             columns.Add(isKey);
351             columns.Add(isAutoIncrement);
352             if (_visibleFieldCount < metadata.Length) {
353                 columns.Add(isHidden);
354             }
355
356             columns.Add(baseSchemaName);
357             columns.Add(baseCatalogName);
358             columns.Add(baseTableName);
359             columns.Add(baseColumnName);
360
361             for (int i = 0; i < metadata.Length; ++i) {
362                 MetaData info = metadata[i];
363
364                 DataRow newRow = schemaTable.NewRow();
365                 newRow[name] = info.columnName;
366                 newRow[ordinal] = i; // MDAC 68319
367                 // @devnote: size is count of characters for WSTR or STR, bytes otherwise
368                 // @devnote: see OLEDB spec under IColumnsInfo::GetColumnInfo
369                 newRow[size] = ((info.type.enumOleDbType != OleDbType.BSTR) ? info.size : -1); // MDAC 72653
370                 newRow[precision] = info.precision; // MDAC 72800
371                 newRow[scale] = info.scale;
372
373                 newRow[dataType] = info.type.dataType;
374                 newRow[providerType] = info.type.enumOleDbType;
375                 newRow[isLong] = OleDbDataReader.IsLong(info.flags);
376                 if (info.isKeyColumn) {
377                     newRow[allowDBNull] = OleDbDataReader.AllowDBNull(info.flags);
378                 }
379                 else {
380                     newRow[allowDBNull] = OleDbDataReader.AllowDBNullMaybeNull(info.flags);
381                 }
382                 newRow[isReadOnly] = OleDbDataReader.IsReadOnly(info.flags);
383                 newRow[isRowVersion] = OleDbDataReader.IsRowVersion(info.flags);
384
385                 newRow[isUnique] = info.isUnique;
386                 newRow[isKey] = info.isKeyColumn;
387                 newRow[isAutoIncrement] = info.isAutoIncrement;
388                 if (_visibleFieldCount < metadata.Length) {
389                     newRow[isHidden] = info.isHidden;
390                 }
391
392                 if (null != info.baseSchemaName) {
393                     newRow[baseSchemaName] = info.baseSchemaName;
394                 }
395                 if (null != info.baseCatalogName) {
396                     newRow[baseCatalogName] = info.baseCatalogName;
397                 }
398                 if (null != info.baseTableName) {
399                     newRow[baseTableName] = info.baseTableName;
400                 }
401                 if (null != info.baseColumnName) {
402                     newRow[baseColumnName] = info.baseColumnName;
403                 }
404
405                 schemaTable.Rows.Add(newRow);
406                 newRow.AcceptChanges();
407             }
408
409             // mark all columns as readonly
410             int count = columns.Count;
411             for (int i=0; i < count; i++) {
412                 columns[i].ReadOnly = true; // MDAC 70943
413             }
414
415             _dbSchemaTable = schemaTable;
416             return schemaTable;
417         }
418
419         private void BuildSchemaTableInfo(object handle, bool filterITypeInfo, bool filterChapters) {
420             Debug.Assert(null == _dbSchemaTable, "non-null SchemaTable");
421             Debug.Assert(null == _metadata, "non-null metadata");
422             Debug.Assert(null != handle, "unexpected null rowset");
423
424             Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|rowset_row> %d#, IColumnsInfo\n", ObjectID);
425             UnsafeNativeMethods.IColumnsInfo icolumnsInfo = (handle as UnsafeNativeMethods.IColumnsInfo);
426             if (null == icolumnsInfo) {
427                  Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|RET> %08X{HRESULT}\n", OleDbHResult.E_NOINTERFACE);
428                 _dbSchemaTable = null;
429 #if DEBUG
430                 if (handle is UnsafeNativeMethods.IRow) {
431                     Debug.Assert(false, "bad IRow - IColumnsInfo not available");
432                 }
433                 else {
434                     Debug.Assert(handle is UnsafeNativeMethods.IRowset, "bad IRowset - IColumnsInfo not available");
435                 }
436 #endif
437                 return;
438             }
439
440             OleDbHResult hr;
441             IntPtr columnCount = ADP.PtrZero; // column count
442             IntPtr columnInfos = ADP.PtrZero; // ptr to byvalue tagDBCOLUMNINFO[]
443
444             using(DualCoTaskMem safehandle = new DualCoTaskMem(icolumnsInfo, out columnCount, out columnInfos, out hr)) {
445                 if (hr < 0) {
446                     ProcessResults(hr);
447                 }
448                 if (0 < (int)columnCount) {
449                     BuildSchemaTableInfoTable(columnCount.ToInt32(), columnInfos, filterITypeInfo, filterChapters);
450                 }
451             }
452         }
453
454         // create DataColumns
455         // add DataColumns to DataTable
456         // add schema information to DataTable
457         // generate unique column names
458         private void BuildSchemaTableInfoTable(int columnCount, IntPtr columnInfos, bool filterITypeInfo, bool filterChapters) {
459             Debug.Assert(0 < columnCount, "BuildSchemaTableInfoTable - no column");
460
461             int rowCount = 0;
462             MetaData[] metainfo = new MetaData[columnCount];
463
464             // for every column, build an equivalent to tagDBCOLUMNINFO
465             tagDBCOLUMNINFO dbColumnInfo = new tagDBCOLUMNINFO();
466             for (int i = 0, offset = 0; i < columnCount; ++i, offset += ODB.SizeOf_tagDBCOLUMNINFO) {
467                 Marshal.PtrToStructure(ADP.IntPtrOffset(columnInfos, offset), dbColumnInfo);
468 #if WIN32
469                 if (0 >= (int) dbColumnInfo.iOrdinal) {
470 #else
471                 if (0 >= (long) dbColumnInfo.iOrdinal) {
472 #endif
473                     continue;
474                 }
475                 if (OleDbDataReader.DoColumnDropFilter(dbColumnInfo.dwFlags)) {
476                     continue;
477                 }
478
479                 if (null == dbColumnInfo.pwszName) {
480                     dbColumnInfo.pwszName = "";
481                 }
482                 if (filterITypeInfo && (ODB.DBCOLUMN_TYPEINFO == dbColumnInfo.pwszName)) { // MDAC 65306
483                     continue;
484                 }
485                 if (filterChapters && (NativeDBType.HCHAPTER == dbColumnInfo.wType)) {
486                     continue;  // filter chapters in IRowset from IDBSchemaRowset for DumpToTable
487                 }
488
489                 bool islong  = OleDbDataReader.IsLong(dbColumnInfo.dwFlags);
490                 bool isfixed = OleDbDataReader.IsFixed(dbColumnInfo.dwFlags);
491                 NativeDBType dbType = NativeDBType.FromDBType(dbColumnInfo.wType, islong, isfixed);
492
493                 MetaData info = new MetaData();
494                 info.columnName = dbColumnInfo.pwszName;
495                 info.type = dbType;
496                 info.ordinal = dbColumnInfo.iOrdinal;
497 #if WIN32
498                     info.size = (int)dbColumnInfo.ulColumnSize;
499 #else
500                     long maxsize = (long) dbColumnInfo.ulColumnSize;
501                     info.size = (((maxsize < 0) || (Int32.MaxValue < maxsize)) ? Int32.MaxValue : (int)maxsize);
502 #endif
503                 info.flags = dbColumnInfo.dwFlags;
504                 info.precision = dbColumnInfo.bPrecision;
505                 info.scale = dbColumnInfo.bScale;
506
507                 info.kind = dbColumnInfo.columnid.eKind;
508                 switch(dbColumnInfo.columnid.eKind) {
509                     case ODB.DBKIND_GUID_NAME:
510                     case ODB.DBKIND_GUID_PROPID:
511                     case ODB.DBKIND_GUID:
512                         info.guid = dbColumnInfo.columnid.uGuid;
513                         break;
514                     default:
515                         Debug.Assert(ODB.DBKIND_PGUID_NAME != dbColumnInfo.columnid.eKind, "OLE DB providers never return pGuid-style bindings.");
516                         Debug.Assert(ODB.DBKIND_PGUID_PROPID != dbColumnInfo.columnid.eKind, "OLE DB providers never return pGuid-style bindings.");
517                         info.guid = Guid.Empty;
518                         break;
519                 }
520                 switch(dbColumnInfo.columnid.eKind) {
521                     case ODB.DBKIND_GUID_PROPID:
522                     case ODB.DBKIND_PROPID:
523                         info.propid = dbColumnInfo.columnid.ulPropid;
524                         break;
525                     case ODB.DBKIND_GUID_NAME:
526                     case ODB.DBKIND_NAME:
527                         if (ADP.PtrZero != dbColumnInfo.columnid.ulPropid) {
528                             info.idname = Marshal.PtrToStringUni(dbColumnInfo.columnid.ulPropid);
529                         }
530                         else {
531                             info.idname = null;
532                         }
533                         break;
534                     default:
535                         info.propid = ADP.PtrZero;
536                         break;
537                 }
538                 metainfo[rowCount] = info;
539
540 #if DEBUG
541                 if (AdapterSwitches.DataSchema.TraceVerbose) {
542                     Debug.WriteLine("OleDbDataReader[" + info.ordinal.ToInt64().ToString(CultureInfo.InvariantCulture) + ", " + dbColumnInfo.pwszName + "]=" + dbType.enumOleDbType.ToString() + "," + dbType.dataSourceType + ", " + dbType.wType);
543                 }
544 #endif
545                 rowCount++;
546             }
547             if (rowCount < columnCount) { // shorten names array appropriately
548                 MetaData[] tmpinfo = new MetaData[rowCount];
549                 for (int i = 0; i < rowCount; ++i) {
550                     tmpinfo[i] = metainfo[i];
551                 }
552                 metainfo = tmpinfo;
553             }
554             _visibleFieldCount = rowCount;
555             _metadata = metainfo;
556         }
557
558         private void BuildSchemaTableRowset(object handle) {
559             Debug.Assert(null == _dbSchemaTable, "BuildSchemaTableRowset - non-null SchemaTable");
560             Debug.Assert(null != handle, "BuildSchemaTableRowset(object) - unexpected null handle");
561
562             Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|rowset_row> %d, IColumnsRowset\n", ObjectID);
563             UnsafeNativeMethods.IColumnsRowset icolumnsRowset = (handle as UnsafeNativeMethods.IColumnsRowset);
564
565             if (null != icolumnsRowset) {
566                 UnsafeNativeMethods.IRowset rowset = null;
567                 IntPtr cOptColumns;
568                 OleDbHResult hr;
569
570                 using(DualCoTaskMem prgOptColumns = new DualCoTaskMem(icolumnsRowset, out cOptColumns, out hr)) {
571                     Debug.Assert((0 == hr) || prgOptColumns.IsInvalid, "GetAvailableCOlumns: unexpected return");
572
573                     Bid.Trace("<oledb.IColumnsRowset.GetColumnsRowset|API|OLEDB> %d#, IID_IRowset\n", ObjectID);
574                     hr = icolumnsRowset.GetColumnsRowset(ADP.PtrZero, cOptColumns, prgOptColumns, ref ODB.IID_IRowset, 0, ADP.PtrZero, out rowset);
575                     Bid.Trace("<oledb.IColumnsRowset.GetColumnsRowset|API|OLEDB|RET> %08X{HRESULT}\n", hr);
576                 }
577
578                 Debug.Assert((0 <= hr) || (null == rowset), "if GetColumnsRowset failed, rowset should be null");
579                 if (hr < 0) {
580                     ProcessResults(hr);
581                 }
582                 DumpToSchemaTable(rowset);
583
584                 // VSTFDEVDIV 479576: release the rowset to avoid race condition between the GC and the user code causing
585                 // "Connection is busy with results for another command" exception
586                 if (null != rowset) {
587                     Marshal.ReleaseComObject(rowset);
588                 }
589             }
590             else {
591                 Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|RET> %08X{HRESULT}\n", OleDbHResult.E_NOINTERFACE);
592                 _useIColumnsRowset = false; // MDAC 72653
593                 BuildSchemaTableInfo(handle, false, false);
594             }
595         }
596
597         override public void Close() {
598             IntPtr hscp;
599             Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.Close|API> %d#\n", ObjectID);
600             try {
601                 OleDbConnection con = _connection;
602                 OleDbCommand cmd = _command;
603                 Bindings bindings = _parameterBindings;
604                 _connection = null;
605                 _command = null;
606                 _parameterBindings = null;
607
608                 _isClosed = true;
609
610                 DisposeOpenResults();
611                 _hasRows = false;
612
613                 if ((null != cmd) && cmd.canceling) { // MDAC 68964                
614                     DisposeNativeMultipleResults();
615                     
616                     if (null != bindings) {
617                         bindings.CloseFromConnection();
618                         bindings = null;
619                     }
620                 }
621                 else {
622                     UnsafeNativeMethods.IMultipleResults multipleResults = _imultipleResults;
623                     _imultipleResults = null;
624
625                     if (null != multipleResults) {
626                         // if we don't have a cmd, same as a cancel (don't call NextResults) which is ADODB behavior
627
628                         try {
629                             // tricky code path is an exception is thrown
630                             // causing connection to do a ResetState and connection.Close
631                             // resulting in OleDbCommand.CloseFromConnection
632                             if ((null != cmd) && !cmd.canceling) { // MDAC 71435
633                                 IntPtr affected = IntPtr.Zero;
634                                 OleDbException nextResultsFailure = NextResults(multipleResults, null, cmd, out affected);
635                                 _recordsAffected = AddRecordsAffected(_recordsAffected, affected);
636                                 if (null != nextResultsFailure) {
637                                     throw nextResultsFailure;
638                                 }
639                             }
640                         }
641                         finally {
642                             if (null != multipleResults) {
643                                 Marshal.ReleaseComObject(multipleResults);
644                             }
645                         }
646                     }
647                 }
648
649                 if ((null != cmd) && (0 == _depth)) {
650                     // return bindings back to the cmd after closure of root DataReader
651                     cmd.CloseFromDataReader(bindings); // MDAC 52283
652                 }
653
654                 if (null != con) {
655                     con.RemoveWeakReference(this);
656
657                     // if the DataReader is Finalized it will not close the connection
658                     if (IsCommandBehavior(CommandBehavior.CloseConnection)) {
659                         con.Close();
660                     }
661                 }
662
663                 // release unmanaged objects
664                 RowHandleBuffer rowHandleNativeBuffer = _rowHandleNativeBuffer;
665                 _rowHandleNativeBuffer = null;
666                 if (null != rowHandleNativeBuffer) {
667                     rowHandleNativeBuffer.Dispose();
668                 }
669             }
670             finally {
671                 Bid.ScopeLeave(ref hscp);
672             }
673         }
674
675         internal void CloseReaderFromConnection(bool canceling) {
676             // being called from the connection, we will have a command. that command
677             // may be Disposed, but it doesn't matter since another command can't execute
678             // until all DataReader are closed
679             if (null != _command) { // 
680                 _command.canceling = canceling;
681             }
682
683             // called from the connection which will remove this from its ReferenceCollection
684             // we want the NextResult behavior, but no errors
685             _connection = null;
686
687             Close();
688         }
689
690         private void DisposeManagedRowset() {
691             //not cleared after last rowset
692             //_hasRows = false;
693
694             _isRead = false;
695             _hasRowsReadCheck = false;
696
697             _nextAccessorForRetrieval = 0;
698             _nextValueForRetrieval = 0;
699
700             Bindings[] bindings = _bindings;
701             _bindings = null;
702
703             if (null != bindings) {
704                 for (int i = 0; i < bindings.Length; ++i) {
705                     if (null != bindings[i]) { // MDAC 77007
706                         bindings[i].Dispose();
707                     }
708                 }
709             }
710
711             _currentRow = 0;
712             _rowFetchedCount = IntPtr.Zero;
713
714             _dbSchemaTable = null;
715             _visibleFieldCount = 0;
716             _metadata = null;
717             _fieldNameLookup = null;
718         }
719
720         private void DisposeNativeMultipleResults() {
721             UnsafeNativeMethods.IMultipleResults imultipleResults = _imultipleResults;
722             _imultipleResults = null;
723
724             if (null != imultipleResults) {
725                 Marshal.ReleaseComObject(imultipleResults);
726             }
727         }
728
729         private void DisposeNativeRowset() {
730             UnsafeNativeMethods.IRowset irowset = _irowset;
731             _irowset = null;
732
733             ChapterHandle chapter = _chapterHandle;
734             _chapterHandle = ChapterHandle.DB_NULL_HCHAPTER;
735
736             if (ChapterHandle.DB_NULL_HCHAPTER != chapter) { // MDAC 81441
737                 chapter.Dispose();
738             }
739
740             if (null != irowset) {
741                 Marshal.ReleaseComObject(irowset);
742             }
743         }
744
745         private void DisposeNativeRow() {
746             UnsafeNativeMethods.IRow irow = _irow;
747             _irow = null;
748
749             if (null != irow) {
750                 Marshal.ReleaseComObject(irow);
751             }
752         }
753
754         private void DisposeOpenResults() {
755             DisposeManagedRowset();
756
757             DisposeNativeRow();
758             DisposeNativeRowset();
759         }
760
761         override public Boolean GetBoolean(int ordinal) {
762             ColumnBinding binding = GetColumnBinding(ordinal);
763             return binding.ValueBoolean();
764         }
765
766         override public Byte GetByte(int ordinal) {
767             ColumnBinding binding = GetColumnBinding(ordinal);
768             return binding.ValueByte();
769         }
770
771         private ColumnBinding DoSequentialCheck(int ordinal, Int64 dataIndex, string method) {
772             ColumnBinding binding = GetColumnBinding(ordinal);
773
774             if (dataIndex > Int32.MaxValue) {
775                 throw ADP.InvalidSourceBufferIndex(0, dataIndex, "dataIndex");
776             }
777             if (_sequentialOrdinal != ordinal) {
778                 _sequentialOrdinal = ordinal;
779                 _sequentialBytesRead = 0;
780             }
781             else if (_sequentialAccess && (_sequentialBytesRead < dataIndex)) {
782                 throw ADP.NonSeqByteAccess(dataIndex, _sequentialBytesRead, method);
783             }
784             // getting the value doesn't really belong, but it's common to both
785             // callers GetBytes and GetChars so we might as well have the code here
786             return binding;
787         }
788
789         override public Int64 GetBytes(int ordinal, Int64 dataIndex, byte[] buffer, Int32 bufferIndex, Int32 length) {
790             ColumnBinding binding = DoSequentialCheck(ordinal, dataIndex, ADP.GetBytes);
791             byte[] value = binding.ValueByteArray();
792
793             if (null == buffer) {
794                 return value.Length;
795             }
796             int srcIndex = (int) dataIndex;
797             int byteCount = Math.Min(value.Length - srcIndex, length);
798             if (srcIndex < 0) { // MDAC 72830
799                 throw ADP.InvalidSourceBufferIndex(value.Length, srcIndex, "dataIndex");
800             }
801             else if ((bufferIndex < 0) || (bufferIndex >= buffer.Length)) { // MDAC 71013
802                 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
803             }
804             if (0 < byteCount) {
805                 // @usernote: user may encounter ArgumentException from Buffer.BlockCopy
806                 Buffer.BlockCopy(value, srcIndex, buffer, bufferIndex, byteCount);
807                 _sequentialBytesRead = srcIndex + byteCount; // MDAC 71013
808             }
809             else if (length < 0) { // MDAC 71007
810                 throw ADP.InvalidDataLength(length);
811             }
812             else {
813                 byteCount = 0;
814             }
815             return byteCount;
816         }
817
818         override public Int64 GetChars(int ordinal, Int64 dataIndex, char[] buffer, Int32 bufferIndex, Int32 length) {
819             ColumnBinding binding = DoSequentialCheck(ordinal, dataIndex, ADP.GetChars);
820             string value =  binding.ValueString();
821
822             if (null == buffer) {
823                 return value.Length;
824             }
825
826             int srcIndex = (int) dataIndex;
827             int charCount = Math.Min(value.Length - srcIndex, length);
828             if (srcIndex < 0) { // MDAC 72830
829                 throw ADP.InvalidSourceBufferIndex(value.Length, srcIndex, "dataIndex");
830             }
831             else if ((bufferIndex < 0) || (bufferIndex >= buffer.Length)) { // MDAC 71013
832                 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
833             }
834             if (0 < charCount) {
835                 // @usernote: user may encounter ArgumentException from String.CopyTo
836                 value.CopyTo(srcIndex, buffer, bufferIndex, charCount);
837                 _sequentialBytesRead = srcIndex + charCount; // MDAC 71013
838             }
839             else if (length < 0) { // MDAC 71007
840                 throw ADP.InvalidDataLength(length);
841             }
842             else {
843                 charCount = 0;
844             }
845             return charCount;
846         }
847
848         [ EditorBrowsableAttribute(EditorBrowsableState.Never) ] // MDAC 69508
849         override public Char GetChar(int ordinal) {
850             throw ADP.NotSupported();
851         }
852
853         [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ]
854         new public OleDbDataReader GetData(int ordinal) {
855             ColumnBinding binding = GetColumnBinding(ordinal);
856             return binding.ValueChapter();
857         }
858
859         override protected DbDataReader GetDbDataReader(int ordinal) {
860             return GetData(ordinal);
861         }
862
863         internal OleDbDataReader ResetChapter(int bindingIndex, int index, RowBinding rowbinding, int valueOffset) {
864             return GetDataForReader(_metadata[bindingIndex + index].ordinal, rowbinding, valueOffset);
865         }
866
867         private OleDbDataReader GetDataForReader(IntPtr ordinal, RowBinding rowbinding, int valueOffset) {
868             UnsafeNativeMethods.IRowsetInfo rowsetInfo = IRowsetInfo();
869             UnsafeNativeMethods.IRowset result;
870             OleDbHResult hr;
871
872             Bid.Trace("<oledb.IRowsetInfo.GetReferencedRowset|API|OLEDB> %d#, ColumnOrdinal=%Id\n", ObjectID, ordinal);
873             hr = rowsetInfo.GetReferencedRowset((IntPtr)ordinal, ref ODB.IID_IRowset, out result);
874             Bid.Trace("<oledb.IRowsetInfo.GetReferencedRowset|API|OLEDB|RET> %08X{HRESULT}\n", hr);
875
876             ProcessResults(hr);
877
878             OleDbDataReader reader = null;
879             if (null != result) {
880                 // only when the first datareader is closed will the connection close
881                 ChapterHandle chapterHandle = ChapterHandle.CreateChapterHandle(result, rowbinding, valueOffset);
882                 reader = new OleDbDataReader(_connection, _command, 1+Depth, _commandBehavior & ~CommandBehavior.CloseConnection);
883                 reader.InitializeIRowset(result, chapterHandle, ADP.RecordsUnaffected);
884                 reader.BuildMetaInfo();
885                 reader.HasRowsRead();
886
887                 if (null != _connection) {
888                     // connection tracks all readers to prevent cmd from executing
889                     // until all readers (including nested) are closed
890                     _connection.AddWeakReference(reader, OleDbReferenceCollection.DataReaderTag);
891                 }
892             }
893             return reader;
894         }
895
896         override public String GetDataTypeName(int index) {
897             if (null != _metadata) {
898                 return _metadata[index].type.dataSourceType;
899             }
900             throw ADP.DataReaderNoData();
901         }
902
903         override public DateTime GetDateTime(int ordinal) {
904             ColumnBinding binding = GetColumnBinding(ordinal);
905             return binding.ValueDateTime();
906         }
907
908         override public Decimal GetDecimal(int ordinal) {
909             ColumnBinding binding = GetColumnBinding(ordinal);
910             return binding.ValueDecimal();
911         }
912
913         override public Double GetDouble(int ordinal) {
914             ColumnBinding binding = GetColumnBinding(ordinal);
915             return binding.ValueDouble();
916         }
917
918         override public IEnumerator GetEnumerator() {
919             return new DbEnumerator((IDataReader)this, IsCommandBehavior(CommandBehavior.CloseConnection));
920         }
921         
922         override public Type GetFieldType(int index) {
923             if (null != _metadata) {
924                 return _metadata[index].type.dataType;
925             }
926             throw ADP.DataReaderNoData();
927         }
928
929         override public Single GetFloat(int ordinal) {
930             ColumnBinding binding = GetColumnBinding(ordinal);
931             return binding.ValueSingle();
932         }
933
934         override public Guid GetGuid(int ordinal) {
935             ColumnBinding binding = GetColumnBinding(ordinal);
936             return binding.ValueGuid();
937         }
938
939         override public Int16 GetInt16(int ordinal) {
940             ColumnBinding binding = GetColumnBinding(ordinal);
941             return binding.ValueInt16();
942         }
943
944         override public Int32 GetInt32(int ordinal) {
945             ColumnBinding binding = GetColumnBinding(ordinal);
946             return binding.ValueInt32();
947         }
948
949         override public Int64 GetInt64(int ordinal) {
950             ColumnBinding binding = GetColumnBinding(ordinal);
951             return binding.ValueInt64();
952         }
953
954         override public String GetName(int index) {
955             if (null != _metadata) {
956                 Debug.Assert(null != _metadata[index].columnName, "MDAC 66681");
957                 return _metadata[index].columnName;
958             }
959             throw ADP.DataReaderNoData();
960         }
961
962         override public Int32 GetOrdinal(String name) {
963             if (null == _fieldNameLookup) {
964                 if (null == _metadata) {
965                     throw ADP.DataReaderNoData();
966                 }
967                 _fieldNameLookup = new FieldNameLookup(this, -1);
968             }
969             return _fieldNameLookup.GetOrdinal(name); // MDAC 71470
970         }
971
972         override public String GetString(int ordinal) {
973             ColumnBinding binding = GetColumnBinding(ordinal);
974             return binding.ValueString();
975         }
976
977         public TimeSpan GetTimeSpan(int ordinal) {
978             return (TimeSpan) GetValue(ordinal);
979         }
980
981         private MetaData DoValueCheck(int ordinal) {
982             if (!_isRead) {
983                 // Read hasn't been called yet or no more data
984                 throw ADP.DataReaderNoData();
985             }
986             else if (_sequentialAccess && (ordinal < _nextValueForRetrieval)) {
987                 throw ADP.NonSequentialColumnAccess(ordinal, _nextValueForRetrieval);
988             }
989             // @usernote: user may encounter the IndexOutOfRangeException
990             MetaData info = _metadata[ordinal];
991             return info;
992         }
993
994         private ColumnBinding GetColumnBinding(int ordinal) {
995             MetaData info = DoValueCheck(ordinal);
996             return GetValueBinding(info);
997         }
998
999         private ColumnBinding GetValueBinding(MetaData info) {
1000             ColumnBinding binding = info.columnBinding;
1001             Debug.Assert(null != binding, "null binding");
1002
1003             // do we need to jump to the next accessor
1004             for (int i = _nextAccessorForRetrieval; i <= binding.IndexForAccessor; ++i) {
1005                 Debug.Assert(_nextAccessorForRetrieval <= binding.IndexForAccessor, "backwards index for accessor");
1006                 Debug.Assert(_nextAccessorForRetrieval == i, "failed to increment");
1007
1008                 if (_sequentialAccess) {
1009                     if (_nextValueForRetrieval != binding.Index) { // release old value
1010                         _metadata[_nextValueForRetrieval].columnBinding.ResetValue();
1011                     }
1012                     _nextAccessorForRetrieval = binding.IndexForAccessor;
1013                 }
1014
1015                 if (null != _irowset) {
1016                     GetRowDataFromHandle(); // will increment _nextAccessorForRetrieval
1017                 }
1018                 else if (null != _irow) {
1019                     GetRowValue(); // will increment _nextAccessorForRetrieval
1020                 }
1021                 else {
1022                     throw ADP.DataReaderNoData();
1023                 }
1024             }
1025
1026             // to enforce sequential access
1027             _nextValueForRetrieval = binding.Index;
1028             return binding;
1029         }
1030
1031         override public object GetValue(int ordinal) {
1032             ColumnBinding binding = GetColumnBinding(ordinal);
1033             object value = binding.Value();
1034             return value;
1035         }
1036
1037         override public Int32 GetValues(object[] values) {
1038             if (null == values) {
1039                 throw ADP.ArgumentNull("values");
1040             }
1041             MetaData info = DoValueCheck(0);
1042             int count = Math.Min(values.Length, _visibleFieldCount);
1043             for (int i = 0; (i < _metadata.Length) && (i < count); ++i) {
1044                 ColumnBinding binding = GetValueBinding(_metadata[i]);
1045                 values[i] = binding.Value();
1046             }
1047             return count;
1048         }
1049
1050         private bool IsCommandBehavior(CommandBehavior condition) {
1051             return (condition == (condition & _commandBehavior));
1052         }
1053         
1054         override public Boolean IsDBNull(int ordinal) {
1055             ColumnBinding binding = GetColumnBinding(ordinal);
1056             return binding.IsValueNull();
1057         }
1058
1059         private void ProcessResults(OleDbHResult hr) {
1060             Exception e;
1061             if (null != _command) {
1062                 e = OleDbConnection.ProcessResults(hr, _connection, _command);
1063             }
1064             else {
1065                 e = OleDbConnection.ProcessResults(hr, _connection, _connection);
1066             }
1067             if (null != e) { throw e; }
1068         }
1069
1070
1071         static private IntPtr AddRecordsAffected(IntPtr recordsAffected, IntPtr affected) { // MDAC 65374
1072 #if WIN32
1073             if (0 <= (int)affected) {
1074                 if (0 <= (int)recordsAffected) {
1075                     return (IntPtr)((int)recordsAffected + (int)affected);
1076 #else
1077             if (0 <= (long)affected) {
1078                 if (0 <= (long)recordsAffected) {
1079                     return (IntPtr)((long)recordsAffected + (long)affected);
1080 #endif
1081                 }
1082                 return affected;
1083             }
1084             return recordsAffected;
1085         }
1086
1087         override public int VisibleFieldCount {
1088             get {
1089                 Bid.Trace("<oledb.OleDbDataReader.get_VisibleFieldCount|API> %d#\n", ObjectID);
1090                 if (IsClosed) {
1091                     throw ADP.DataReaderClosed("VisibleFieldCount");
1092                 }
1093                 return _visibleFieldCount;
1094             }
1095         }
1096
1097         internal void HasRowsRead() { // MDAC 78405
1098             Debug.Assert(!_hasRowsReadCheck, "_hasRowsReadCheck not reset");
1099             bool flag = Read();
1100             _hasRows = flag;
1101             _hasRowsReadCheck = true;
1102             _isRead = false;
1103         }
1104
1105         static internal OleDbException NextResults(UnsafeNativeMethods.IMultipleResults imultipleResults, OleDbConnection connection, OleDbCommand command, out IntPtr recordsAffected) {
1106             recordsAffected = ADP.RecordsUnaffected;
1107             List<OleDbException> exceptions = null; // WebData 104564
1108             if (null != imultipleResults) {
1109                 object result;
1110                 IntPtr affected;
1111                 OleDbHResult hr;
1112
1113
1114                 // MSOLAP provider doesn't move onto the next result when calling GetResult with IID_NULL, but does return S_OK with 0 affected records.
1115                 // we want to break out of that infinite loop for ExecuteNonQuery and the multiple result Close scenarios
1116                 for (int loop = 0;; ++loop) {
1117                     if ((null != command) && command.canceling) { // MDAC 68964
1118                         break;
1119                     }
1120
1121                     Bid.Trace("<oledb.IMultipleResults.GetResult|API|OLEDB> DBRESULTFLAG_DEFAULT, IID_NULL\n");
1122                     hr = imultipleResults.GetResult(ADP.PtrZero, ODB.DBRESULTFLAG_DEFAULT, ref ODB.IID_NULL, out affected, out result);
1123                     Bid.Trace("<oledb.IMultipleResults.GetResult|API|OLEDB|RET> %08X{HRESULT}, RecordAffected=%Id\n", hr, affected);
1124
1125                     // If a provider doesn't support IID_NULL and returns E_NOINTERFACE we want to break out
1126                     // of the loop without throwing an exception.  Our behavior will match ADODB in that scenario
1127                     // where Recordset.Close just releases the interfaces without proccessing remaining results
1128                     if ((OleDbHResult.DB_S_NORESULT == hr) || (OleDbHResult.E_NOINTERFACE == hr)) { // MDAC 70874
1129                         break;
1130                     }
1131                     if (null != connection) {
1132                         Exception e = OleDbConnection.ProcessResults(hr, connection, command);
1133                         if (null != e) {
1134                             OleDbException excep = (e as OleDbException);
1135                             if (null != excep) {
1136                                 if (null == exceptions) {
1137                                     exceptions = new List<OleDbException>();
1138                                 }
1139                                 exceptions.Add(excep);
1140                             }
1141                             else {
1142                                 Debug.Assert(OleDbHResult.DB_E_OBJECTOPEN == hr, "unexpected");
1143                                 throw e; // we don't expect to be here, but it could happen
1144                             }
1145                         }
1146                     }
1147                     else if (hr < 0) {
1148                         SafeNativeMethods.Wrapper.ClearErrorInfo();
1149                         break; // MDAC 72694
1150                     }
1151                     recordsAffected = AddRecordsAffected(recordsAffected, affected);
1152
1153                     if (0 != (int)affected) {
1154                         loop = 0;
1155                     }
1156                     else if (2000 <= loop) { // MDAC 72126 (reason for more than 1000 iterations)
1157                         NextResultsInfinite(); // MDAC 72738
1158                         break;
1159                     }
1160                 }
1161             }
1162             if (null != exceptions) {
1163                 return OleDbException.CombineExceptions(exceptions);
1164             }
1165             return null;
1166         }
1167
1168         static private void NextResultsInfinite() { // MDAC 72738
1169             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");
1170
1171             // Microsoft's suggestion is that we debug assert so that users will learn of MSOLAP's misbehavior and not call ExecuteNonQuery
1172             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");
1173         }
1174
1175         override public bool NextResult() {
1176             IntPtr hscp;
1177             Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.NextResult|API> %d#\n", ObjectID);
1178             try {
1179                 bool retflag = false;
1180                 if (IsClosed) {
1181                     throw ADP.DataReaderClosed("NextResult");
1182                 }
1183                 _fieldNameLookup = null;
1184
1185                 OleDbCommand command = _command;
1186                 UnsafeNativeMethods.IMultipleResults imultipleResults = _imultipleResults;
1187                 if (null != imultipleResults) {
1188                     DisposeOpenResults();
1189                     _hasRows = false;
1190                     
1191                     for (;;) {
1192                         Debug.Assert(null == _irow, "NextResult: row loop check");
1193                         Debug.Assert(null == _irowset, "NextResult: rowset loop check");
1194
1195                         object result = null;
1196                         OleDbHResult hr;
1197                         IntPtr affected;
1198
1199                         if ((null != command) && command.canceling) { // MDAC 69986
1200                             Close();
1201                             break;
1202                         }
1203
1204                         Bid.Trace("<oledb.IMultipleResults.GetResult|API|OLEDB> %d#, IID_IRowset\n", ObjectID);
1205                         hr = imultipleResults.GetResult(ADP.PtrZero, ODB.DBRESULTFLAG_DEFAULT, ref ODB.IID_IRowset, out affected, out result);
1206                         Bid.Trace("<oledb.IMultipleResults.GetResult|API|OLEDB|RET> %08X{HRESULT}, RecordAffected=%Id\n", hr, affected);
1207
1208                         if ((0 <= hr) && (null != result)) {
1209                             Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|RowSet> %d#, IRowset\n", ObjectID);
1210                             _irowset = (UnsafeNativeMethods.IRowset) result;
1211                         }
1212                         _recordsAffected = AddRecordsAffected(_recordsAffected, affected);
1213
1214                         if (OleDbHResult.DB_S_NORESULT == hr) {
1215                             DisposeNativeMultipleResults();
1216                             break;
1217                         }
1218                         // @devnote: infomessage events may be fired from here
1219                         ProcessResults(hr);
1220
1221                         if (null != _irowset) {
1222                             BuildMetaInfo();
1223                             HasRowsRead();
1224                             retflag = true;
1225                             break;
1226                         }
1227                     }
1228                 }
1229                 else {
1230                     DisposeOpenResults(); // MDAC 70934
1231                     _hasRows = false; // MDAC 85850
1232                 }
1233                 return retflag;
1234             }
1235             finally {
1236                 Bid.ScopeLeave(ref hscp);
1237             }
1238         }
1239
1240         override public bool Read() {
1241             IntPtr hscp;
1242             Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.Read|API> %d#\n", ObjectID);
1243             try {
1244                 bool retflag = false;
1245                 OleDbCommand command = _command;
1246                 if ((null != command) && command.canceling) { // MDAC 69986
1247                     DisposeOpenResults();
1248                 }
1249                 else if (null != _irowset) {
1250                     if (_hasRowsReadCheck) {
1251                         _isRead = retflag = _hasRows;
1252                         _hasRowsReadCheck = false;
1253                     }
1254                     else if (_singleRow && _isRead) {
1255                         DisposeOpenResults(); // MDAC 66109
1256                     }
1257                     else {
1258                         retflag = ReadRowset();
1259                     }
1260                 }
1261                 else if (null != _irow) {
1262                     retflag = ReadRow();
1263                 }
1264                 else if (IsClosed) {
1265                     throw ADP.DataReaderClosed("Read");
1266                 }
1267                 return retflag;
1268             }
1269             finally {
1270                 Bid.ScopeLeave(ref hscp);
1271             }
1272         }
1273
1274         private bool ReadRow() {
1275             if (_isRead) {
1276                 _isRead = false; // for DoValueCheck
1277
1278                 DisposeNativeRow();
1279
1280                 _sequentialOrdinal = -1; // sequentialBytesRead will reset when used
1281             }
1282             else {
1283                 _isRead = true;
1284                 return (0 < _metadata.Length);
1285             }
1286             return false;
1287         }
1288
1289         private bool ReadRowset() {
1290             Debug.Assert(null != _irowset, "ReadRow: null IRowset");
1291             Debug.Assert(0 <= _metadata.Length, "incorrect state for fieldCount");
1292
1293             // releases bindings as necessary
1294             // bumps current row, else resets it back to initial state
1295             ReleaseCurrentRow();
1296
1297             _sequentialOrdinal = -1; // sequentialBytesRead will reset when used
1298
1299             // making the check if (null != irowset) unnecessary
1300             // if necessary, get next group of row handles
1301             if (IntPtr.Zero == _rowFetchedCount) { // starts at (-1 <= 0)
1302                 Debug.Assert(0 == _currentRow, "incorrect state for _currentRow");
1303                 Debug.Assert(0 <= _metadata.Length, "incorrect state for fieldCount");
1304                 Debug.Assert(0 == _nextAccessorForRetrieval, "incorrect state for nextAccessorForRetrieval");
1305                 Debug.Assert(0 == _nextValueForRetrieval, "incorrect state for nextValueForRetrieval");
1306
1307                 // @devnote: releasing row handles occurs next time user calls read, skip, or close
1308                 GetRowHandles(/*skipCount*/);
1309             }
1310             return ((_currentRow <= (int)_rowFetchedCount) && _isRead);
1311         }
1312
1313         private void ReleaseCurrentRow() {
1314             Debug.Assert(null != _irowset, "ReleaseCurrentRow: no rowset");
1315
1316             if (0 < (int)_rowFetchedCount) {
1317
1318                 // release the data in the current row
1319                 Bindings[] bindings = _bindings;
1320                 Debug.Assert(null != bindings, "ReleaseCurrentRow: null dbBindings");
1321                 for (int i = 0; (i < bindings.Length) && (i < _nextAccessorForRetrieval); ++i) {
1322                     bindings[i].CleanupBindings();
1323                 }
1324                 _nextAccessorForRetrieval = 0;
1325                 _nextValueForRetrieval = 0;
1326
1327                 _currentRow++;
1328                 if (_currentRow == (int)_rowFetchedCount) {
1329                     ReleaseRowHandles();
1330                 }
1331             }
1332         }
1333
1334         private void CreateAccessors(bool allowMultipleAccessor) {
1335             Debug.Assert(null == _bindings, "CreateAccessor: dbBindings already exists");
1336             Debug.Assert(null != _irowset, "CreateAccessor: no IRowset available");
1337             Debug.Assert(null != _metadata && 0 < _metadata.Length, "no columns");
1338
1339             Bindings[] dbBindings = CreateBindingsFromMetaData(allowMultipleAccessor);
1340
1341             UnsafeNativeMethods.IAccessor iaccessor = IAccessor();
1342             for (int i = 0; i < dbBindings.Length; ++i) {
1343                 OleDbHResult hr = dbBindings[i].CreateAccessor(iaccessor, ODB.DBACCESSOR_ROWDATA);
1344                 if (hr < 0) {
1345                     ProcessResults(hr);
1346                 }
1347             }
1348
1349             if (IntPtr.Zero == _rowHandleFetchCount) {
1350                 _rowHandleFetchCount = new IntPtr(1);
1351
1352                 object maxRows = GetPropertyValue(ODB.DBPROP_MAXROWS);
1353                 if (maxRows is Int32) {
1354                     _rowHandleFetchCount = new IntPtr((int) maxRows);
1355                     if ((ADP.PtrZero == _rowHandleFetchCount) || (20 <= (int)_rowHandleFetchCount)) {
1356                         _rowHandleFetchCount = new IntPtr(20);
1357                     }
1358                 }
1359                 else if (maxRows is Int64) {
1360                     _rowHandleFetchCount = new IntPtr((long) maxRows);
1361                     if ((ADP.PtrZero == _rowHandleFetchCount) || (20 <= (long)_rowHandleFetchCount)) {
1362                         _rowHandleFetchCount = new IntPtr(20);
1363                     }
1364                 }
1365             }
1366             if (null == _rowHandleNativeBuffer) {
1367                 _rowHandleNativeBuffer = new RowHandleBuffer(_rowHandleFetchCount);
1368             }
1369         }
1370
1371         private Bindings[] CreateBindingsFromMetaData(bool allowMultipleAccessor) {
1372             int bindingCount = 0;
1373             int currentBindingIndex = 0;
1374
1375             MetaData[] metadata = _metadata;
1376
1377             int[] indexToBinding = new int[metadata.Length];
1378             int[] indexWithinBinding = new int[metadata.Length];
1379
1380             // walk through the schemaRows to determine the number of binding groups
1381             if (allowMultipleAccessor) {
1382                 if (null != _irowset) {
1383                     for (int i = 0; i < indexToBinding.Length; ++i) {
1384                         indexToBinding[i] = bindingCount;
1385                         indexWithinBinding[i] = currentBindingIndex;
1386 #if false
1387                         // @denote: single/multiple Accessors
1388                         if ((bindingCount < 2) && IsLong(metadata[i].flags)) {
1389                             bindingCount++;
1390                             currentBindingIndex = 0;
1391                         }
1392                         else {
1393                             currentBindingIndex++;
1394                         }
1395 #elif false
1396                         // @devnote: one accessor per column option
1397                         bindingCount++;
1398                         currentBindingIndex = 0;
1399 #else
1400                         // @devnote: one accessor only for IRowset
1401                         currentBindingIndex++;
1402 #endif
1403                     }
1404                     if (0 < currentBindingIndex) { // when blob is not the last column
1405                         bindingCount++;
1406                     }
1407                 }
1408                 else if (null != _irow) {
1409                     for (int i = 0; i < indexToBinding.Length; ++i) {
1410                         indexToBinding[i] = i;
1411                         indexWithinBinding[i] = 0;
1412                     }
1413                     bindingCount = metadata.Length;
1414                 }
1415             }
1416             else {
1417                 for (int i = 0; i < indexToBinding.Length; ++i) {
1418                     indexToBinding[i] = 0;
1419                     indexWithinBinding[i] = i;
1420                 }
1421                 bindingCount = 1;
1422             }
1423
1424             Bindings bindings;
1425             Bindings[] dbbindings = new Bindings[bindingCount];
1426             bindingCount = 0;
1427
1428             // for every column, build tagDBBinding info
1429             for (int index = 0; index < metadata.Length; ++index) {
1430                 Debug.Assert(indexToBinding[index] < dbbindings.Length, "bad indexToAccessor");
1431                 bindings = dbbindings[indexToBinding[index]];
1432                 if (null == bindings) {
1433                     bindingCount = 0;
1434                     for (int i = index; (i < metadata.Length) && (bindingCount == indexWithinBinding[i]); ++i) {
1435                         bindingCount++;
1436                     }
1437                     dbbindings[indexToBinding[index]] = bindings = new Bindings((OleDbDataReader)this, (null != _irowset), bindingCount);
1438
1439                     // runningTotal is buffered to start values on 16-byte boundary
1440                     // the first columnCount * 8 bytes are for the length and status fields
1441                     //bindings.DataBufferSize = (bindingCount + (bindingCount % 2)) * sizeof_int64;
1442                 }
1443                 MetaData info = metadata[index];
1444
1445                 int maxLen = info.type.fixlen;
1446                 short getType = info.type.wType;
1447
1448                 Debug.Assert(NativeDBType.STR != getType, "Should have bound as WSTR");
1449                 Debug.Assert(!NativeDBType.HasHighBit(getType), "CreateAccessor - unexpected high bits on datatype");
1450
1451                 if (-1 != info.size) {
1452                     if (info.type.islong) {
1453                         maxLen = ADP.PtrSize;
1454                         getType = (short)((ushort) getType | (ushort) NativeDBType.BYREF);
1455                     }
1456                     else if (-1 == maxLen) {
1457                         // @devnote: not using provider owned memory for PDC, no one really supports it anyway.
1458                         /*if (((null != connection) && connection.PropertyGetProviderOwnedMemory())
1459                             || ((null != command) && command.Connection.PropertyGetProviderOwnedMemory())) {
1460                             bindings.MemOwner = DBMemOwner.ProviderOwned;
1461
1462                             bindings.MaxLen = ADP.PtrSize;
1463                             bindings.DbType = (short) (getType | DbType.BYREF);
1464                         }
1465                         else*/
1466
1467                         if (ODB.LargeDataSize < info.size) {
1468                             maxLen = ADP.PtrSize;
1469                             getType = (short)((ushort) getType | (ushort)NativeDBType.BYREF);
1470                         }
1471                         else if ((NativeDBType.WSTR == getType) && (-1 != info.size)) { // WebData 99298
1472                             maxLen = info.size * 2 + 2;
1473                         }
1474                         else {
1475                             maxLen = info.size;
1476                         }
1477                     }
1478                 }
1479                 else if (maxLen < 0) {
1480                     // if variable length and no defined size we require this to be byref at this time
1481                     /*if (((null != connection) && connection.PropertyGetProviderOwnedMemory())
1482                         || ((null != command) && command.Connection.PropertyGetProviderOwnedMemory())) {
1483                         bindings.MemOwner = DBMemOwner.ProviderOwned;
1484                     }*/
1485                     maxLen = ADP.PtrSize;
1486                     getType = (short)((ushort) getType | (ushort)NativeDBType.BYREF);
1487                 }
1488
1489                 currentBindingIndex = indexWithinBinding[index];
1490                 bindings.CurrentIndex = currentBindingIndex;
1491
1492                 bindings.Ordinal      = info.ordinal;
1493                 bindings.Part         = info.type.dbPart;
1494                 bindings.Precision    = (byte) info.precision;
1495                 bindings.Scale        = (byte) info.scale;
1496                 bindings.DbType       = (short) getType;
1497                 bindings.MaxLen       = maxLen; // also increments databuffer size (uses DbType)
1498
1499               //bindings.ValueOffset  = // set via MaxLen
1500               //bindings.LengthOffset = // set via MaxLen
1501               //bindings.StatusOffset = // set via MaxLen
1502               //bindings.TypeInfoPtr  = 0;
1503               //bindings.ObjectPtr    = 0;
1504               //bindings.BindExtPtr   = 0;
1505               //bindings.MemOwner     = /*DBMEMOWNER_CLIENTOWNED*/0;
1506               //bindings.ParamIO      = ODB.DBPARAMIO_NOTPARAM;
1507               //bindings.Flags        = 0;
1508
1509                 if (Bid.AdvancedOn) {
1510                     Bid.Trace("<oledb.struct.tagDBBINDING|INFO|ADV> index=%d, columnName='%ls'\n", index, info.columnName);//, bindings.bindings[index]);
1511                 }
1512             }
1513
1514             int count = 0, indexStart = 0;
1515             for (int i = 0; i < dbbindings.Length; ++i) {
1516                 indexStart = dbbindings[i].AllocateForAccessor(this, indexStart, i);
1517
1518                 ColumnBinding[] columnBindings = dbbindings[i].ColumnBindings();
1519                 for (int k = 0; k < columnBindings.Length; ++k) {
1520                     Debug.Assert(count == columnBindings[k].Index, "column binding mismatch");
1521                     metadata[count].columnBinding = columnBindings[k];
1522                     metadata[count].bindings = dbbindings[i];
1523                     count++;
1524                 }
1525             }
1526
1527             _bindings = dbbindings;
1528             return dbbindings;
1529         }
1530
1531         private void GetRowHandles(/*int skipCount*/) {
1532             Debug.Assert(0 < (int)_rowHandleFetchCount, "GetRowHandles: bad _rowHandleFetchCount");
1533             Debug.Assert(!_isRead, "GetRowHandles: _isRead");
1534
1535             OleDbHResult hr = 0;
1536
1537             RowHandleBuffer rowHandleBuffer = _rowHandleNativeBuffer;
1538             bool mustRelease = false;
1539
1540             RuntimeHelpers.PrepareConstrainedRegions();
1541             try {
1542                 rowHandleBuffer.DangerousAddRef(ref mustRelease);
1543
1544                 IntPtr rowHandlesPtr = rowHandleBuffer.DangerousGetHandle();
1545                 UnsafeNativeMethods.IRowset irowset = IRowset();
1546                 try {
1547                     Bid.Trace("<oledb.IRowset.GetNextRows|API|OLEDB> %d#, Chapter=%Id, RowsRequested=%Id\n", ObjectID, _chapterHandle.HChapter, _rowHandleFetchCount);
1548                     hr = irowset.GetNextRows(_chapterHandle.HChapter, /*skipCount*/IntPtr.Zero, _rowHandleFetchCount, out _rowFetchedCount, ref rowHandlesPtr);
1549                     Bid.Trace("<oledb.IRowset.GetNextRows|API|OLEDB|RET> %08X{HRESULT}, RowsObtained=%Id\n", hr, _rowFetchedCount);
1550                     Debug.Assert(rowHandleBuffer.DangerousGetHandle() == rowHandlesPtr, "rowhandlebuffer changed");
1551                 }
1552                 catch(System.InvalidCastException e) { // MDAC 64320
1553                     throw ODB.ThreadApartmentState(e);
1554                 }
1555             }
1556             finally {
1557                 if (mustRelease) {
1558                     rowHandleBuffer.DangerousRelease();
1559                 }
1560             }
1561
1562             //if (/*DB_S_ROWLIMITEXCEEDED*/0x00040EC0 == hr) {
1563             //    _rowHandleFetchCount = 1;
1564             //    _isRead = true;
1565             //} else
1566             if (hr < 0) {
1567                 // filter out the BadStartPosition due to the skipCount which
1568                 // maybe greater than the number of rows in the return rowset
1569                 //const int /*OLEDB_Error.*/DB_E_BADSTARTPOSITION = unchecked((int)0x80040E1E);
1570                 //if (DB_E_BADSTARTPOSITION != hr)
1571                 ProcessResults(hr);
1572             }
1573             _isRead = ((OleDbHResult.DB_S_ENDOFROWSET != hr) || (0 < (int)_rowFetchedCount)); // MDAC 59264
1574             _rowFetchedCount = (IntPtr)Math.Max((int)_rowFetchedCount, 0);
1575         }
1576
1577         private void GetRowDataFromHandle() {
1578             Debug.Assert(null != _bindings, "GetRowDataFromHandle: null dbBindings");
1579             Debug.Assert(null != _rowHandleNativeBuffer, "GetRowDataFromHandle: null dbBindings");
1580
1581             OleDbHResult hr = 0;
1582             UnsafeNativeMethods.IRowset irowset = IRowset();
1583
1584             IntPtr rowHandle = _rowHandleNativeBuffer.GetRowHandle(_currentRow);
1585
1586             RowBinding rowBinding = _bindings[_nextAccessorForRetrieval].RowBinding();
1587             IntPtr accessorHandle = rowBinding.DangerousGetAccessorHandle();
1588
1589             bool mustRelease = false;
1590             RuntimeHelpers.PrepareConstrainedRegions();
1591             try {
1592                 rowBinding.DangerousAddRef(ref mustRelease);
1593                 rowBinding.StartDataBlock();
1594                 
1595                 IntPtr dataPtr = rowBinding.DangerousGetDataPtr();
1596
1597                 Bid.Trace("<oledb.IRowset.GetData|API|OLEDB> %d#, RowHandle=%Id, AccessorHandle=%Id\n", ObjectID, rowHandle, accessorHandle);
1598                 hr = irowset.GetData(rowHandle, accessorHandle, dataPtr);
1599                 Bid.Trace("<oledb.IRowset.GetData|API|OLEDB|RET> %08X{HRESULT}\n", hr);
1600             }
1601             finally {
1602                 if (mustRelease) {
1603                     rowBinding.DangerousRelease();
1604                 }
1605             }
1606
1607             //if (AdapterSwitches.DataError.TraceWarning) {
1608             //    DBBindings binding = _dbBindings[i];
1609             //    for (int k = 0; k < binding.Count; ++k) {
1610             //        binding.CurrentIndex = k;
1611             //        Debug.WriteLine("Status[" + k.ToString(CultureInfo.InvariantCulture) + "] = " + binding.StatusValue.ToString("G", CultureInfo.InvariantCulture));
1612             //    }
1613             //}
1614
1615             _nextAccessorForRetrieval++;
1616             if (hr < 0) {
1617                 ProcessResults(hr);
1618             }
1619         }
1620
1621         private void ReleaseRowHandles() {
1622             Debug.Assert(0 < (int)_rowFetchedCount, "invalid _rowFetchedCount");
1623
1624             OleDbHResult hr;
1625             UnsafeNativeMethods.IRowset irowset = IRowset();
1626
1627             Bid.Trace("<oledb.IRowset.ReleaseRows|API|OLEDB> %d#, Request=%Id\n", ObjectID, _rowFetchedCount);
1628             hr = irowset.ReleaseRows(_rowFetchedCount, _rowHandleNativeBuffer, ADP.PtrZero, ADP.PtrZero, ADP.PtrZero);
1629             Bid.Trace("<oledb.IRowset.ReleaseRows|API|OLEDB|RET> %08X{HRESULT}\n", hr);
1630
1631             if (hr < 0) {
1632                 //ProcessFailure(hr);
1633                 //ProcessFailure(hr);
1634                 SafeNativeMethods.Wrapper.ClearErrorInfo();
1635             }
1636             _rowFetchedCount = IntPtr.Zero;
1637             _currentRow = 0;
1638             _isRead = false; // MDAC 59264
1639         }
1640
1641         private object GetPropertyValue(int propertyId) { // MDAC 72106
1642             if (null != _irowset) {
1643                 return GetPropertyOnRowset(OleDbPropertySetGuid.Rowset, propertyId);
1644             }
1645             else if (null != _command) {
1646                 return _command.GetPropertyValue(OleDbPropertySetGuid.Rowset, propertyId);
1647             }
1648             return OleDbPropertyStatus.NotSupported;
1649         }
1650
1651         private object GetPropertyOnRowset(Guid propertySet, int propertyID) {
1652             OleDbHResult hr;
1653             tagDBPROP[] dbprops;
1654             UnsafeNativeMethods.IRowsetInfo irowsetinfo = IRowsetInfo();
1655
1656             using(PropertyIDSet propidset = new PropertyIDSet(propertySet, propertyID)) {
1657
1658                 using(DBPropSet propset = new DBPropSet(irowsetinfo, propidset, out hr)) {
1659                     if (hr < 0) {
1660                         // VSDD 621427: OLEDB Data Reader masks provider specific errors by raising "Internal .Net Framework Data Provider error 30."
1661                         // DBPropSet c-tor will register the exception and it will be raised at GetPropertySet call in case of failure
1662                         SafeNativeMethods.Wrapper.ClearErrorInfo();
1663                     }
1664                     dbprops = propset.GetPropertySet(0, out propertySet);
1665                 }
1666             }
1667             if (OleDbPropertyStatus.Ok == dbprops[0].dwStatus) {
1668                 return dbprops[0].vValue;
1669             }
1670             return dbprops[0].dwStatus;
1671         }
1672
1673         private void GetRowValue() {
1674             Debug.Assert(null != _irow, "GetRowValue: null IRow");
1675             Debug.Assert(null != _metadata, "GetRowValue: null MetaData");
1676
1677             Bindings bindings = _bindings[_nextAccessorForRetrieval];
1678             ColumnBinding[] columnBindings = bindings.ColumnBindings();
1679             RowBinding rowBinding = bindings.RowBinding();
1680             Debug.Assert(_nextValueForRetrieval <= columnBindings[0].Index, "backwards retrieval");
1681
1682             bool mustReleaseBinding = false;
1683             bool[] mustRelease = new bool[columnBindings.Length];
1684             StringMemHandle[] sptr = new StringMemHandle[columnBindings.Length];
1685
1686             RuntimeHelpers.PrepareConstrainedRegions();
1687             try {
1688                 for (int i = 0; i < columnBindings.Length; ++i) {
1689                     bindings.CurrentIndex = i;
1690
1691                     sptr[i] = null;
1692                     MetaData info = _metadata[columnBindings[i].Index];
1693                     if ((ODB.DBKIND_GUID_NAME == info.kind) || (ODB.DBKIND_NAME == info.kind)) {
1694                         sptr[i] = new StringMemHandle(info.idname);
1695                         columnBindings[i]._sptr = sptr[i];
1696                     }
1697
1698                     sptr[i].DangerousAddRef(ref mustRelease[i]);
1699
1700                     IntPtr ulPropid = ((null != sptr[i]) ? sptr[i].DangerousGetHandle() : info.propid);
1701                     bindings.GuidKindName(info.guid, info.kind, ulPropid);
1702                 }
1703
1704                 OleDbHResult hr;
1705                 tagDBCOLUMNACCESS[] access = bindings.DBColumnAccess;
1706
1707                 rowBinding.DangerousAddRef(ref mustReleaseBinding);
1708                 rowBinding.StartDataBlock();                
1709
1710                 UnsafeNativeMethods.IRow irow = IRow();
1711
1712                 Bid.Trace("<oledb.IRow.GetColumns|API|OLEDB> %d#\n", ObjectID);                
1713                 hr = irow.GetColumns((IntPtr)access.Length, access);
1714                 Bid.Trace("<oledb.IRow.GetColumns|API|OLEDB|RET> %08X{HRESULT}\n", hr);
1715             }
1716             finally {
1717                 if (mustReleaseBinding) {
1718                     rowBinding.DangerousRelease();
1719                 }
1720                 for (int i = 0; i < mustRelease.Length; i++) {
1721                     if (mustRelease[i]) {
1722                         sptr[i].DangerousRelease();
1723                     }
1724                 }
1725             }
1726             _nextAccessorForRetrieval++;
1727         }
1728
1729         private Int32 IndexOf(Hashtable hash, string name) { // MDAC 67385
1730             // via case sensitive search, first match with lowest ordinal matches
1731             object index = hash[name];
1732             if (null != index) {
1733                 return (int) index; // match via case-insensitive or by chance lowercase
1734             }
1735
1736             // via case insensitive search, first match with lowest ordinal matches
1737             string tmp = name.ToLower(CultureInfo.InvariantCulture);
1738             index = hash[tmp]; // match via lowercase
1739             return ((null != index) ? (int) index : -1);
1740         }
1741
1742         private void AppendSchemaInfo() {
1743             Debug.Assert(null != _connection, "null connection");
1744             Debug.Assert(null != _metadata, "no _metadata");
1745
1746             if (_metadata.Length <= 0) {
1747                 return;
1748             }
1749
1750             int keyCount = 0;
1751             for (int i = 0; i < _metadata.Length; ++i) {
1752                 if (_metadata[i].isKeyColumn && !_metadata[i].isHidden) { // MDAC 90411
1753                     keyCount++;
1754                 }
1755             }
1756             if (0 != keyCount) /*|| _connection.IsServer_msdaora || _connection.IsServer_Microsoft_SQL)*/ { // MDAC 60109
1757                 return;
1758             }
1759
1760             string schemaName, catalogName; // enforce single table
1761             string baseSchemaName = null, baseCatalogName = null, baseTableName = null;
1762             for (int i = 0; i < _metadata.Length; ++i) {
1763                 MetaData info = _metadata[i];
1764                 if ((null != info.baseTableName) && (0 < info.baseTableName.Length)) {
1765                     catalogName = ((null != info.baseCatalogName) ? info.baseCatalogName : ""); // MDAC 67249
1766                     schemaName = ((null != info.baseSchemaName) ? info.baseSchemaName : "");
1767                     if (null == baseTableName) {
1768                         baseSchemaName = schemaName;
1769                         baseCatalogName = catalogName;
1770                         baseTableName = info.baseTableName;
1771                     }
1772                     else if ((0 != ADP.SrcCompare(baseTableName, info.baseTableName))
1773                             || (0 != ADP.SrcCompare(baseCatalogName, catalogName))
1774                             || (0 != ADP.SrcCompare(baseSchemaName, schemaName))) { // MDAC 71808
1775 #if DEBUG
1776                         if (AdapterSwitches.DataSchema.TraceVerbose) {
1777                             Debug.WriteLine("Multiple BaseTableName detected:"
1778                                 +" <"+baseCatalogName+"."+baseCatalogName+"."+baseTableName+">"
1779                                 +" <"+info.baseCatalogName+"."+info.baseCatalogName+"."+info.baseTableName+">");
1780                         }
1781 #endif
1782                         baseTableName = null;
1783                         break;
1784                     }
1785                 }
1786             }
1787             if (null == baseTableName) {
1788                 return;
1789             }
1790             baseCatalogName = ADP.IsEmpty(baseCatalogName) ? null : baseCatalogName;
1791             baseSchemaName = ADP.IsEmpty(baseSchemaName) ? null : baseSchemaName;
1792
1793             if (null != _connection) { // MDAC 67394
1794                 if (ODB.DBPROPVAL_IC_SENSITIVE == _connection.QuotedIdentifierCase()) {
1795                     string p = null, s = null;
1796                     _connection.GetLiteralQuotes(ADP.GetSchemaTable, out s, out p);
1797                     if (null == s) {
1798                         s = "";
1799                     }
1800                     if (null == p) {
1801                         p = "";
1802                     }
1803                     baseTableName = s + baseTableName + p;
1804                 }
1805             }
1806
1807             Hashtable baseColumnNames = new Hashtable(_metadata.Length * 2); // MDAC 67385
1808
1809             for (int i = _metadata.Length-1; 0 <= i; --i) {
1810                 string basecolumname = _metadata[i].baseColumnName;
1811                 if (!ADP.IsEmpty(basecolumname)) {
1812                     baseColumnNames[basecolumname] = i;
1813                 }
1814             }
1815             for (int i = 0; i < _metadata.Length; ++i) {
1816                 string basecolumname = _metadata[i].baseColumnName;
1817                 if (!ADP.IsEmpty(basecolumname)) {
1818                     basecolumname = basecolumname.ToLower(CultureInfo.InvariantCulture);
1819                     if (!baseColumnNames.Contains(basecolumname)) {
1820                         baseColumnNames[basecolumname] = i;
1821                     }
1822                 }
1823             }
1824
1825             // look for primary keys in the table
1826             if (_connection.SupportSchemaRowset(OleDbSchemaGuid.Primary_Keys)) {
1827                 Object[] restrictions = new Object[] { baseCatalogName, baseSchemaName, baseTableName };
1828                 keyCount = AppendSchemaPrimaryKey(baseColumnNames, restrictions);
1829             }
1830             if (0 != keyCount) {
1831                 return;
1832             }
1833
1834             // look for a single unique contraint that can be upgraded
1835             if (_connection.SupportSchemaRowset(OleDbSchemaGuid.Indexes)) {
1836                 Object[] restrictions = new Object[] { baseCatalogName, baseSchemaName, null, null, baseTableName };
1837                 AppendSchemaUniqueIndexAsKey(baseColumnNames, restrictions);
1838             }
1839         }
1840
1841         private int AppendSchemaPrimaryKey(Hashtable baseColumnNames, object[] restrictions) {
1842             int keyCount = 0;
1843             bool partialPrimaryKey = false;
1844             DataTable table = null;
1845             try {
1846                 table = _connection.GetSchemaRowset(OleDbSchemaGuid.Primary_Keys, restrictions);
1847             }
1848             catch(Exception e) {
1849                 // 
1850                 if (!ADP.IsCatchableExceptionType(e)) {
1851                     throw;
1852                 }
1853
1854                 ADP.TraceExceptionWithoutRethrow(e);
1855             }
1856             if (null != table) {
1857                 DataColumnCollection dataColumns = table.Columns;
1858                 int nameColumnIndex = dataColumns.IndexOf(ODB.COLUMN_NAME);
1859
1860                 if (-1 != nameColumnIndex) {
1861                     DataColumn nameColumn = dataColumns[nameColumnIndex];
1862                     foreach(DataRow dataRow in table.Rows) {
1863                         string name = (string) dataRow[nameColumn, DataRowVersion.Default];
1864
1865                         int metaindex = IndexOf(baseColumnNames, name); // MDAC 67385
1866                         if (0 <= metaindex) {
1867                             MetaData info = _metadata[metaindex];
1868                             info.isKeyColumn = true;
1869                             info.flags &= ~ODB.DBCOLUMNFLAGS_ISNULLABLE;
1870                             keyCount++;
1871                         }
1872                         else {
1873 #if DEBUG
1874                             if (AdapterSwitches.DataSchema.TraceVerbose) {
1875                                 Debug.WriteLine("PartialKeyColumn detected: <" + name + "> metaindex=" + metaindex);
1876                             }
1877 #endif
1878                             partialPrimaryKey = true;
1879                             break;
1880                         }
1881                     }
1882                 }
1883             }
1884             if (partialPrimaryKey) { // partial primary key detected
1885                 for (int i = 0; i < _metadata.Length; ++i) {
1886                     _metadata[i].isKeyColumn = false;
1887                 }
1888                 return -1;
1889             }
1890             return keyCount;
1891         }
1892
1893         private void AppendSchemaUniqueIndexAsKey(Hashtable baseColumnNames, object[] restrictions) {
1894             bool partialPrimaryKey = false;
1895             DataTable table = null;
1896             try { // MDAC 66209
1897                 table = _connection.GetSchemaRowset(OleDbSchemaGuid.Indexes, restrictions);
1898             }
1899             catch(Exception e) {
1900                 // 
1901                 if (!ADP.IsCatchableExceptionType(e)) {
1902                     throw;
1903                 }
1904
1905                 ADP.TraceExceptionWithoutRethrow(e);
1906             }
1907             if (null != table) {
1908                 DataColumnCollection dataColumns = table.Columns;
1909
1910                 int indxIndex = dataColumns.IndexOf(ODB.INDEX_NAME);
1911                 int pkeyIndex = dataColumns.IndexOf(ODB.PRIMARY_KEY);
1912                 int uniqIndex = dataColumns.IndexOf(ODB.UNIQUE);
1913                 int nameIndex = dataColumns.IndexOf(ODB.COLUMN_NAME);
1914                 int nullIndex = dataColumns.IndexOf(ODB.NULLS);
1915
1916                 if ((-1 != indxIndex) && (-1 != pkeyIndex) && (-1 != uniqIndex) && (-1 != nameIndex)) {
1917
1918                     DataColumn indxColumn = dataColumns[indxIndex];
1919                     DataColumn pkeyColumn = dataColumns[pkeyIndex];
1920                     DataColumn uniqCOlumn = dataColumns[uniqIndex];
1921                     DataColumn nameColumn = dataColumns[nameIndex];
1922                     DataColumn nulls = ((-1 != nullIndex) ? dataColumns[nullIndex] : null);
1923
1924                     bool[] keys = new bool[_metadata.Length];
1925                     bool[] uniq = new bool[_metadata.Length];
1926                     string uniqueIndexName = null;
1927
1928                     // match pkey name BaseColumnName
1929                     foreach(DataRow dataRow in table.Rows) {
1930
1931                         bool isPKey = (!dataRow.IsNull(pkeyColumn, DataRowVersion.Default) && (bool)dataRow[pkeyColumn, DataRowVersion.Default]);
1932                         bool isUniq = (!dataRow.IsNull(uniqCOlumn, DataRowVersion.Default) && (bool)dataRow[uniqCOlumn, DataRowVersion.Default]);
1933                         bool nullsVal = (null != nulls) && (dataRow.IsNull(nulls, DataRowVersion.Default) || (ODB.DBPROPVAL_IN_ALLOWNULL == Convert.ToInt32(dataRow[nulls, DataRowVersion.Default], CultureInfo.InvariantCulture)));
1934
1935                         if (isPKey || isUniq) {
1936                             string name = (string) dataRow[nameColumn, DataRowVersion.Default];
1937
1938                             int metaindex = IndexOf(baseColumnNames, name); // MDAC 67385
1939                             if (0 <= metaindex) {
1940                                 if (isPKey) {
1941                                     keys[metaindex] = true;
1942                                 }
1943                                 if (isUniq && (null != uniq)) {
1944                                     uniq[metaindex] = true;
1945
1946                                     string indexname = (string) dataRow[indxColumn, DataRowVersion.Default];
1947                                     if (null == uniqueIndexName) {
1948                                         uniqueIndexName = indexname;
1949                                     }
1950                                     else if (indexname != uniqueIndexName) {
1951 #if DEBUG
1952                                         if (AdapterSwitches.DataSchema.TraceVerbose) {
1953                                             Debug.WriteLine("MultipleUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">");
1954                                         }
1955 #endif
1956                                         uniq = null;
1957                                     }
1958                                 }
1959                             }
1960                             else if (isPKey) {
1961 #if DEBUG
1962                                 if (AdapterSwitches.DataSchema.TraceVerbose) {
1963                                     Debug.WriteLine("PartialKeyColumn detected: " + name);
1964                                 }
1965 #endif
1966                                 partialPrimaryKey = true;
1967                                 break;
1968                             }
1969                             else if (null != uniqueIndexName) {
1970                                 string indexname = (string) dataRow[indxColumn, DataRowVersion.Default];
1971
1972                                 if (indexname != uniqueIndexName) {
1973 #if DEBUG
1974                                     if (AdapterSwitches.DataSchema.TraceVerbose) {
1975                                         Debug.WriteLine("PartialUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">");
1976                                     }
1977 #endif
1978                                     uniq = null;
1979                                 }
1980                             }
1981                         }
1982                     }
1983                     if (partialPrimaryKey) {
1984                         for (int i = 0; i < _metadata.Length; ++i) {
1985                             _metadata[i].isKeyColumn = false;
1986                         }
1987                         return;
1988                     }
1989                     else if (null != uniq) {
1990 #if DEBUG
1991                         if (AdapterSwitches.DataSchema.TraceVerbose) {
1992                             Debug.WriteLine("upgrade single unique index to be a key: <" + uniqueIndexName + ">");
1993                         }
1994 #endif
1995                         // upgrade single unique index to be a key
1996                         for (int i = 0; i < _metadata.Length; ++i) {
1997                             _metadata[i].isKeyColumn = uniq[i];
1998                         }
1999                     }
2000                 }
2001             }
2002         }
2003
2004         private MetaData FindMetaData(string name) {
2005             int index = _fieldNameLookup.IndexOfName(name);
2006             return ((-1 != index) ? _metadata[index] : null);
2007         }
2008
2009         internal void DumpToSchemaTable(UnsafeNativeMethods.IRowset rowset) {
2010             List<MetaData> metainfo = new List<MetaData>();
2011
2012             object hiddenColumns = null;
2013             using (OleDbDataReader dataReader = new OleDbDataReader(_connection, _command, Int32.MinValue, 0)) {
2014                 dataReader.InitializeIRowset(rowset, ChapterHandle.DB_NULL_HCHAPTER, IntPtr.Zero);
2015                 dataReader.BuildSchemaTableInfo(rowset, true, false);
2016
2017                 hiddenColumns = GetPropertyValue(ODB.DBPROP_HIDDENCOLUMNS); // MDAC 55611, 72106
2018                 if (0 == dataReader.FieldCount) {
2019                     return;
2020                 }
2021
2022                 Debug.Assert(null == dataReader._fieldNameLookup, "lookup already exists");
2023                 FieldNameLookup lookup = new FieldNameLookup(dataReader, -1);
2024                 dataReader._fieldNameLookup = lookup;
2025
2026                 // This column, together with the DBCOLUMN_GUID and DBCOLUMN_PROPID
2027                 // columns, forms the ID of the column. One or more (but not all) of these columns
2028                 // will be NULL, depending on which elements of the DBID structure the provider uses.
2029                 MetaData columnidname = dataReader.FindMetaData(ODB.DBCOLUMN_IDNAME);
2030                 MetaData columnguid = dataReader.FindMetaData(ODB.DBCOLUMN_GUID);
2031                 MetaData columnpropid = dataReader.FindMetaData(ODB.DBCOLUMN_PROPID);
2032
2033                 MetaData columnname = dataReader.FindMetaData(ODB.DBCOLUMN_NAME);
2034                 MetaData columnordinal = dataReader.FindMetaData(ODB.DBCOLUMN_NUMBER);
2035                 MetaData dbtype = dataReader.FindMetaData(ODB.DBCOLUMN_TYPE);
2036                 MetaData columnsize = dataReader.FindMetaData(ODB.DBCOLUMN_COLUMNSIZE);
2037                 MetaData numericprecision = dataReader.FindMetaData(ODB.DBCOLUMN_PRECISION);
2038                 MetaData numericscale = dataReader.FindMetaData(ODB.DBCOLUMN_SCALE);
2039                 MetaData columnflags = dataReader.FindMetaData(ODB.DBCOLUMN_FLAGS);
2040                 MetaData baseschemaname = dataReader.FindMetaData(ODB.DBCOLUMN_BASESCHEMANAME);
2041                 MetaData basecatalogname = dataReader.FindMetaData(ODB.DBCOLUMN_BASECATALOGNAME);
2042                 MetaData basetablename = dataReader.FindMetaData(ODB.DBCOLUMN_BASETABLENAME);
2043                 MetaData basecolumnname = dataReader.FindMetaData(ODB.DBCOLUMN_BASECOLUMNNAME);
2044                 MetaData isautoincrement = dataReader.FindMetaData(ODB.DBCOLUMN_ISAUTOINCREMENT);
2045                 MetaData isunique = dataReader.FindMetaData(ODB.DBCOLUMN_ISUNIQUE);
2046                 MetaData iskeycolumn = dataReader.FindMetaData(ODB.DBCOLUMN_KEYCOLUMN);
2047
2048                 // @devnote: because we want to use the DBACCESSOR_OPTIMIZED bit,
2049                 // we are required to create the accessor before fetching any rows
2050                 dataReader.CreateAccessors(false);
2051
2052                 ColumnBinding binding;
2053                 while (dataReader.ReadRowset()) {
2054                     dataReader.GetRowDataFromHandle();
2055
2056                     MetaData info = new MetaData();
2057
2058                     binding = columnidname.columnBinding; // MDAC 72627
2059                     if (!binding.IsValueNull()) {
2060                         info.idname = (string)binding.Value();
2061                         info.kind = ODB.DBKIND_NAME;
2062                     }
2063
2064                     binding = columnguid.columnBinding;
2065                     if (!binding.IsValueNull()) {
2066                         info.guid = binding.Value_GUID();
2067                         info.kind = ((ODB.DBKIND_NAME == info.kind) ? ODB.DBKIND_GUID_NAME : ODB.DBKIND_GUID);
2068                     }
2069
2070                     binding = columnpropid.columnBinding;
2071                     if (!binding.IsValueNull()) {
2072                         info.propid = new IntPtr(binding.Value_UI4());
2073                         info.kind = ((ODB.DBKIND_GUID == info.kind) ? ODB.DBKIND_GUID_PROPID : ODB.DBKIND_PROPID);
2074                     }
2075
2076                     binding = columnname.columnBinding;
2077                     if (!binding.IsValueNull()) {
2078                         info.columnName = (string)binding.Value();
2079                     }
2080                     else {
2081                         info.columnName = "";
2082                     }
2083
2084                     if (4 == ADP.PtrSize) {
2085                         info.ordinal = (IntPtr)columnordinal.columnBinding.Value_UI4();
2086                     }
2087                     else {
2088                         info.ordinal = (IntPtr)columnordinal.columnBinding.Value_UI8();
2089                     }
2090                     short wType = unchecked((short) dbtype.columnBinding.Value_UI2());
2091
2092                     if (4 == ADP.PtrSize) {
2093                         info.size = unchecked((int) columnsize.columnBinding.Value_UI4()); // WebData 99298
2094                     }
2095                     else {
2096                         info.size = ADP.IntPtrToInt32((IntPtr)unchecked((long)columnsize.columnBinding.Value_UI8()));
2097                     }
2098
2099                     binding = numericprecision.columnBinding;
2100                     if (!binding.IsValueNull()) {
2101                         info.precision = (byte)binding.Value_UI2();
2102                     }
2103
2104                     binding = numericscale.columnBinding;
2105                     if (!binding.IsValueNull()) {
2106                         info.scale = (byte)binding.Value_I2();
2107                     }
2108
2109                     info.flags = unchecked((int) columnflags.columnBinding.Value_UI4());
2110
2111                     bool islong = OleDbDataReader.IsLong(info.flags);
2112                     bool isfixed = OleDbDataReader.IsFixed(info.flags);
2113                     NativeDBType dbType = NativeDBType.FromDBType(wType, islong, isfixed);
2114
2115                     info.type = dbType;
2116
2117                     if (null != isautoincrement) {
2118                         binding = isautoincrement.columnBinding;
2119                         if (!binding.IsValueNull()) {
2120                             info.isAutoIncrement = binding.Value_BOOL();
2121                         }
2122                     }
2123                     if (null != isunique) {
2124                         binding = isunique.columnBinding;
2125                         if (!binding.IsValueNull()) {
2126                             info.isUnique = binding.Value_BOOL();
2127                         }
2128                     }
2129                     if (null != iskeycolumn) {
2130                         binding = iskeycolumn.columnBinding;
2131                         if (!binding.IsValueNull()) {
2132                             info.isKeyColumn = binding.Value_BOOL();
2133                         }
2134                     }
2135                     if (null != baseschemaname) {
2136                         binding = baseschemaname.columnBinding;
2137                         if (!binding.IsValueNull()) {
2138                             info.baseSchemaName = binding.ValueString();
2139                         }
2140                     }
2141                     if (null != basecatalogname) {
2142                         binding = basecatalogname.columnBinding;
2143                         if (!binding.IsValueNull()) {
2144                             info.baseCatalogName = binding.ValueString();
2145                         }
2146                     }
2147                     if (null != basetablename) {
2148                         binding = basetablename.columnBinding;
2149                         if (!binding.IsValueNull()) {
2150                             info.baseTableName = binding.ValueString();
2151                         }
2152                     }
2153                     if (null != basecolumnname) {
2154                         binding = basecolumnname.columnBinding;
2155                         if (!binding.IsValueNull()) {
2156                             info.baseColumnName = binding.ValueString();
2157                         }
2158                     }
2159                     metainfo.Add(info);
2160                 }
2161             }
2162
2163             int visibleCount = metainfo.Count;
2164             if (hiddenColumns is Int32) {
2165                 visibleCount -= (int)hiddenColumns;
2166             }
2167
2168             // VSTFDevDiv 479578: 
2169             //  if one key column is invalidated, they all need to be invalidated. The SET is the key,
2170             //  and subsets likely are not accurate keys. Note the assumption that the two loops below
2171             //  will traverse the entire set of columns.
2172             bool disallowKeyColumns = false;
2173
2174             for (int index = metainfo.Count - 1; visibleCount <= index; --index) {
2175                 MetaData info = metainfo[index];
2176
2177                 info.isHidden = true;
2178
2179                 if (disallowKeyColumns) {
2180                     info.isKeyColumn = false;
2181                 }
2182                 else if (info.guid.Equals(ODB.DBCOL_SPECIALCOL)) { // MDAC 90827
2183                     info.isKeyColumn = false;
2184
2185                     // This is the first key column to be invalidated, scan back through the 
2186                     //  columns we already processed to make sure none of those are marked as keys.
2187                     disallowKeyColumns = true;
2188                     for (int index2 = metainfo.Count-1; index < index2; --index2) {
2189                         metainfo[index2].isKeyColumn = false;
2190                     }
2191                 }
2192             }
2193
2194             for (int index = visibleCount - 1; 0 <= index; --index) {
2195                 MetaData info = metainfo[index];
2196
2197                 if (disallowKeyColumns) {
2198                     info.isKeyColumn = false;
2199                 }
2200
2201                 if (info.guid.Equals(ODB.DBCOL_SPECIALCOL)) { // MDAC 72390
2202 #if DEBUG
2203                     if (AdapterSwitches.DataSchema.TraceVerbose) {
2204                         Debug.WriteLine("Filtered Column: DBCOLUMN_GUID=DBCOL_SPECIALCOL DBCOLUMN_NAME=" + info.columnName + " DBCOLUMN_KEYCOLUMN=" + info.isKeyColumn);
2205                     }
2206 #endif
2207                     info.isHidden = true;
2208                     visibleCount--;
2209                 }
2210 #if WIN32
2211                 else if (0 >= (int)info.ordinal) {
2212 #else
2213                 else if (0 >= (long)info.ordinal) {
2214 #endif
2215 #if DEBUG
2216                     if (AdapterSwitches.DataSchema.TraceVerbose) {
2217                         Debug.WriteLine("Filtered Column: DBCOLUMN_NUMBER=" + info.ordinal.ToInt64().ToString(CultureInfo.InvariantCulture) + " DBCOLUMN_NAME=" + info.columnName);
2218                     }
2219 #endif
2220                     info.isHidden = true;
2221                     visibleCount--;
2222                 }
2223                 else if (OleDbDataReader.DoColumnDropFilter(info.flags)) {
2224 #if DEBUG
2225                     if (AdapterSwitches.DataSchema.TraceVerbose) {
2226                         Debug.WriteLine("Filtered Column: DBCOLUMN_FLAGS=" + info.flags.ToString("X8", (System.IFormatProvider)null) + " DBCOLUMN_NAME=" + info.columnName);
2227                     }
2228 #endif
2229                     info.isHidden = true;
2230                     visibleCount--;
2231                 }
2232             }
2233
2234             // 
2235             metainfo.Sort(); // MDAC 68319
2236             _visibleFieldCount = visibleCount;
2237             _metadata = metainfo.ToArray();
2238         }
2239
2240         static internal void GenerateSchemaTable(OleDbDataReader dataReader, object handle, CommandBehavior behavior) {
2241             if (0 != (CommandBehavior.KeyInfo & behavior)) {
2242                 dataReader.BuildSchemaTableRowset(handle); // tries IColumnsRowset first then IColumnsInfo
2243                 dataReader.AppendSchemaInfo();
2244             }
2245             else {
2246                 dataReader.BuildSchemaTableInfo(handle, false, false); // only tries IColumnsInfo
2247             }
2248             MetaData[] metadata = dataReader.MetaData;
2249             if ((null != metadata) && (0 < metadata.Length)) {
2250                 dataReader.BuildSchemaTable(metadata);
2251             }
2252         }
2253
2254         static private bool DoColumnDropFilter(int flags) {
2255             return (0 != (ODB.DBCOLUMNFLAGS_ISBOOKMARK & flags));
2256         }
2257         static private bool IsLong(int flags) {
2258             return (0 != (ODB.DBCOLUMNFLAGS_ISLONG & flags));
2259         }
2260         static private bool IsFixed(int flags) {
2261             return (0 != (ODB.DBCOLUMNFLAGS_ISFIXEDLENGTH & flags));
2262         }
2263         static private bool IsRowVersion(int flags) {
2264             return (0 != (ODB.DBCOLUMNFLAGS_ISROWID_DBCOLUMNFLAGS_ISROWVER & flags));
2265         }
2266         static private bool AllowDBNull(int flags) {
2267             return (0 != (ODB.DBCOLUMNFLAGS_ISNULLABLE & flags));
2268         }
2269         static private bool AllowDBNullMaybeNull(int flags) {
2270             return (0 != (ODB.DBCOLUMNFLAGS_ISNULLABLE_DBCOLUMNFLAGS_MAYBENULL & flags));
2271         }
2272         static private bool IsReadOnly(int flags) {
2273             return (0 == (ODB.DBCOLUMNFLAGS_WRITE_DBCOLUMNFLAGS_WRITEUNKNOWN & flags));
2274         }
2275     }
2276
2277     sealed internal class MetaData : IComparable {
2278
2279         internal Bindings bindings;
2280         internal ColumnBinding columnBinding;
2281
2282         internal string columnName;
2283
2284         internal Guid guid; // MDAC 72627
2285         internal int kind;
2286         internal IntPtr propid;
2287         internal string idname;
2288
2289         internal NativeDBType type;
2290
2291         internal IntPtr ordinal;
2292         internal int size;
2293
2294         internal int flags;
2295
2296         internal byte precision;
2297         internal byte scale;
2298
2299         internal bool isAutoIncrement;
2300         internal bool isUnique;
2301         internal bool isKeyColumn;
2302         internal bool isHidden;
2303
2304         internal string baseSchemaName;
2305         internal string baseCatalogName;
2306         internal string baseTableName;
2307         internal string baseColumnName;
2308
2309         int IComparable.CompareTo(object obj) { // MDAC 68319
2310             if (isHidden == (obj as MetaData).isHidden) {
2311 #if WIN32
2312                 return ((int)ordinal - (int)(obj as MetaData).ordinal);
2313 #else
2314                 long v = ((long)ordinal - (long)(obj as MetaData).ordinal);
2315                 return ((0 < v) ? 1 : ((v < 0) ? -1 : 0));
2316 #endif
2317
2318             }
2319             return (isHidden) ? 1 : -1; // ensure that all hidden columns come after non-hidden columns
2320         }
2321
2322         internal MetaData() {
2323         }
2324     }
2325 }