1 //------------------------------------------------------------------------------
2 // <copyright file="OleDbDataReader.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data.OleDb {
12 using System.Collections;
13 using System.Collections.Generic;
14 using System.ComponentModel;
16 using System.Data.Common;
17 using System.Data.ProviderBase;
18 using System.Diagnostics;
19 using System.Globalization;
21 using System.Runtime.CompilerServices;
22 using System.Runtime.InteropServices;
25 public sealed class OleDbDataReader : DbDataReader {
27 private CommandBehavior _commandBehavior;
29 private static int _objectTypeCount; // Bid counter
30 internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
32 // object model interaction
33 private OleDbConnection _connection;
34 private OleDbCommand _command;
36 // DataReader owns the parameter bindings until CloseDataReader
37 // this allows OleDbCommand.Dispose to not require OleDbDataReader.Dispose
38 private Bindings _parameterBindings;
41 private UnsafeNativeMethods.IMultipleResults _imultipleResults;
42 private UnsafeNativeMethods.IRowset _irowset;
43 private UnsafeNativeMethods.IRow _irow;
45 private ChapterHandle _chapterHandle = ChapterHandle.DB_NULL_HCHAPTER;
48 private bool _isClosed, _isRead, _hasRows, _hasRowsReadCheck;
50 long _sequentialBytesRead;
51 int _sequentialOrdinal;
53 private Bindings[] _bindings; // _metdata contains the ColumnBinding
55 // do we need to jump to the next accessor
56 private int _nextAccessorForRetrieval;
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;
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;
68 // cached information for Reading (rowhandles/status)
69 private IntPtr _rowHandleFetchCount; // MDAC 60111 (>1 fails against jet)
70 private RowHandleBuffer _rowHandleNativeBuffer;
72 private IntPtr _rowFetchedCount;
73 private int _currentRow;
75 private DataTable _dbSchemaTable;
77 private int _visibleFieldCount;
78 private MetaData[] _metadata;
79 private FieldNameLookup _fieldNameLookup;
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();
86 _connection = connection;
88 _commandBehavior = commandBehavior;
90 if ((null != command) && (0 == _depth)) {
91 _parameterBindings = command.TakeBindingOwnerShip();
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));
105 internal void InitializeIMultipleResults(object result) {
107 _imultipleResults = (UnsafeNativeMethods.IMultipleResults) result; // maybe null if no results
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);
116 _recordsAffected = recordsAffected;
117 _irowset = (UnsafeNativeMethods.IRowset) result; // maybe null if no results
118 _chapterHandle = chapterHandle;
121 internal void InitializeIRow(object result, IntPtr recordsAffected) {
123 Debug.Assert(_singleRow, "SingleRow not already set");
125 _recordsAffected = recordsAffected;
126 _irow = (UnsafeNativeMethods.IRow) result; // maybe null if no results
127 _hasRows = (null != _irow);
130 internal OleDbCommand Command {
136 override public int Depth {
138 Bid.Trace("<oledb.OleDbDataReader.get_Depth|API> %d#\n", ObjectID);
139 if (IsClosed) { // MDAC 63669
140 throw ADP.DataReaderClosed("Depth");
146 override public Int32 FieldCount {
148 Bid.Trace("<oledb.OleDbDataReader.get_FieldCount|API> %d#\n", ObjectID);
149 if (IsClosed) { // MDAC 63669
150 throw ADP.DataReaderClosed("FieldCount");
152 MetaData[] metadata = MetaData;
153 return ((null != metadata) ? metadata.Length : 0);
157 override public bool HasRows { // MDAC 78405
159 Bid.Trace("<oledb.OleDbDataReader.get_HasRows|API> %d#\n", ObjectID);
160 if (IsClosed) { // MDAC 63669
161 throw ADP.DataReaderClosed("HasRows");
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");
178 private MetaData[] MetaData {
179 get { return _metadata; }
182 override public int RecordsAffected {
184 Bid.Trace("<oledb.OleDbDataReader.get_RecordsAffected|API> %d#\n", ObjectID);
185 return ADP.IntPtrToInt32(_recordsAffected);
190 internal long RecordsAffectedLong {
192 return (long)_recordsAffected;
196 override public object this[Int32 index] {
198 return GetValue(index);
202 override public object this[String name] {
204 int ordinal = GetOrdinal(name);
205 return GetValue(ordinal);
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
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();
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();
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);
233 private UnsafeNativeMethods.IRow IRow() {
234 UnsafeNativeMethods.IRow irow = _irow;
236 Debug.Assert(false, "object is disposed");
237 throw new ObjectDisposedException(GetType().Name);
242 override public DataTable GetSchemaTable() {
244 Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.GetSchemaTable|API> %d#\n", ObjectID);
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)) {
253 schemaTable = BuildSchemaTable(metadata);
255 else if (IsClosed) { // MDAC 68331
256 throw ADP.DataReaderClosed("GetSchemaTable");
258 //GetSchemaTable() is defined to return null after NextResult returns false
259 //throw ADP.DataReaderNoData();
264 Bid.ScopeLeave(ref hscp);
268 internal void BuildMetaInfo() {
269 Debug.Assert(null == _metadata, "BuildMetaInfo: already built, by _metadata");
271 if (null != _irowset) {
272 if (_useIColumnsRowset) {
273 BuildSchemaTableRowset(_irowset);
276 BuildSchemaTableInfo(_irowset, false, false);
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");
285 else if (null != _irow) {
286 BuildSchemaTableInfo(_irow, false, false);
287 if (null != _metadata && 0 < _metadata.Length) {
288 CreateBindingsFromMetaData(true);
291 if (null == _metadata) {
293 _visibleFieldCount = 0;
294 _metadata = new MetaData[0];
298 private DataTable BuildSchemaTable(MetaData[] metadata) {
299 Debug.Assert(null == _dbSchemaTable, "BuildSchemaTable: schema table already exists");
300 Debug.Assert(null != metadata, "BuildSchemaTable: no _metadata");
302 DataTable schemaTable = new DataTable("SchemaTable");
303 schemaTable.Locale = CultureInfo.InvariantCulture;
304 schemaTable.MinimumCapacity = metadata.Length;
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));
312 DataColumn dataType = new DataColumn("DataType", typeof(System.Type));
313 DataColumn providerType = new DataColumn("ProviderType", typeof(System.Int32));
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));
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));
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));
330 ordinal.DefaultValue = 0;
331 isLong.DefaultValue = false;
333 DataColumnCollection columns = schemaTable.Columns;
336 columns.Add(ordinal);
338 columns.Add(precision);
341 columns.Add(dataType);
342 columns.Add(providerType);
345 columns.Add(allowDBNull);
346 columns.Add(isReadOnly);
347 columns.Add(isRowVersion);
349 columns.Add(isUnique);
351 columns.Add(isAutoIncrement);
352 if (_visibleFieldCount < metadata.Length) {
353 columns.Add(isHidden);
356 columns.Add(baseSchemaName);
357 columns.Add(baseCatalogName);
358 columns.Add(baseTableName);
359 columns.Add(baseColumnName);
361 for (int i = 0; i < metadata.Length; ++i) {
362 MetaData info = metadata[i];
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;
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);
380 newRow[allowDBNull] = OleDbDataReader.AllowDBNullMaybeNull(info.flags);
382 newRow[isReadOnly] = OleDbDataReader.IsReadOnly(info.flags);
383 newRow[isRowVersion] = OleDbDataReader.IsRowVersion(info.flags);
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;
392 if (null != info.baseSchemaName) {
393 newRow[baseSchemaName] = info.baseSchemaName;
395 if (null != info.baseCatalogName) {
396 newRow[baseCatalogName] = info.baseCatalogName;
398 if (null != info.baseTableName) {
399 newRow[baseTableName] = info.baseTableName;
401 if (null != info.baseColumnName) {
402 newRow[baseColumnName] = info.baseColumnName;
405 schemaTable.Rows.Add(newRow);
406 newRow.AcceptChanges();
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
415 _dbSchemaTable = schemaTable;
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");
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;
430 if (handle is UnsafeNativeMethods.IRow) {
431 Debug.Assert(false, "bad IRow - IColumnsInfo not available");
434 Debug.Assert(handle is UnsafeNativeMethods.IRowset, "bad IRowset - IColumnsInfo not available");
441 IntPtr columnCount = ADP.PtrZero; // column count
442 IntPtr columnInfos = ADP.PtrZero; // ptr to byvalue tagDBCOLUMNINFO[]
444 using(DualCoTaskMem safehandle = new DualCoTaskMem(icolumnsInfo, out columnCount, out columnInfos, out hr)) {
448 if (0 < (int)columnCount) {
449 BuildSchemaTableInfoTable(columnCount.ToInt32(), columnInfos, filterITypeInfo, filterChapters);
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");
462 MetaData[] metainfo = new MetaData[columnCount];
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);
469 if (0 >= (int) dbColumnInfo.iOrdinal) {
471 if (0 >= (long) dbColumnInfo.iOrdinal) {
475 if (OleDbDataReader.DoColumnDropFilter(dbColumnInfo.dwFlags)) {
479 if (null == dbColumnInfo.pwszName) {
480 dbColumnInfo.pwszName = "";
482 if (filterITypeInfo && (ODB.DBCOLUMN_TYPEINFO == dbColumnInfo.pwszName)) { // MDAC 65306
485 if (filterChapters && (NativeDBType.HCHAPTER == dbColumnInfo.wType)) {
486 continue; // filter chapters in IRowset from IDBSchemaRowset for DumpToTable
489 bool islong = OleDbDataReader.IsLong(dbColumnInfo.dwFlags);
490 bool isfixed = OleDbDataReader.IsFixed(dbColumnInfo.dwFlags);
491 NativeDBType dbType = NativeDBType.FromDBType(dbColumnInfo.wType, islong, isfixed);
493 MetaData info = new MetaData();
494 info.columnName = dbColumnInfo.pwszName;
496 info.ordinal = dbColumnInfo.iOrdinal;
498 info.size = (int)dbColumnInfo.ulColumnSize;
500 long maxsize = (long) dbColumnInfo.ulColumnSize;
501 info.size = (((maxsize < 0) || (Int32.MaxValue < maxsize)) ? Int32.MaxValue : (int)maxsize);
503 info.flags = dbColumnInfo.dwFlags;
504 info.precision = dbColumnInfo.bPrecision;
505 info.scale = dbColumnInfo.bScale;
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;
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;
520 switch(dbColumnInfo.columnid.eKind) {
521 case ODB.DBKIND_GUID_PROPID:
522 case ODB.DBKIND_PROPID:
523 info.propid = dbColumnInfo.columnid.ulPropid;
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);
535 info.propid = ADP.PtrZero;
538 metainfo[rowCount] = info;
541 if (AdapterSwitches.DataSchema.TraceVerbose) {
542 Debug.WriteLine("OleDbDataReader[" + info.ordinal.ToInt64().ToString(CultureInfo.InvariantCulture) + ", " + dbColumnInfo.pwszName + "]=" + dbType.enumOleDbType.ToString() + "," + dbType.dataSourceType + ", " + dbType.wType);
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];
554 _visibleFieldCount = rowCount;
555 _metadata = metainfo;
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");
562 Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|rowset_row> %d, IColumnsRowset\n", ObjectID);
563 UnsafeNativeMethods.IColumnsRowset icolumnsRowset = (handle as UnsafeNativeMethods.IColumnsRowset);
565 if (null != icolumnsRowset) {
566 UnsafeNativeMethods.IRowset rowset = null;
570 using(DualCoTaskMem prgOptColumns = new DualCoTaskMem(icolumnsRowset, out cOptColumns, out hr)) {
571 Debug.Assert((0 == hr) || prgOptColumns.IsInvalid, "GetAvailableCOlumns: unexpected return");
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);
578 Debug.Assert((0 <= hr) || (null == rowset), "if GetColumnsRowset failed, rowset should be null");
582 DumpToSchemaTable(rowset);
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);
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);
597 override public void Close() {
599 Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.Close|API> %d#\n", ObjectID);
601 OleDbConnection con = _connection;
602 OleDbCommand cmd = _command;
603 Bindings bindings = _parameterBindings;
606 _parameterBindings = null;
610 DisposeOpenResults();
613 if ((null != cmd) && cmd.canceling) { // MDAC 68964
614 DisposeNativeMultipleResults();
616 if (null != bindings) {
617 bindings.CloseFromConnection();
622 UnsafeNativeMethods.IMultipleResults multipleResults = _imultipleResults;
623 _imultipleResults = null;
625 if (null != multipleResults) {
626 // if we don't have a cmd, same as a cancel (don't call NextResults) which is ADODB behavior
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;
642 if (null != multipleResults) {
643 Marshal.ReleaseComObject(multipleResults);
649 if ((null != cmd) && (0 == _depth)) {
650 // return bindings back to the cmd after closure of root DataReader
651 cmd.CloseFromDataReader(bindings); // MDAC 52283
655 con.RemoveWeakReference(this);
657 // if the DataReader is Finalized it will not close the connection
658 if (IsCommandBehavior(CommandBehavior.CloseConnection)) {
663 // release unmanaged objects
664 RowHandleBuffer rowHandleNativeBuffer = _rowHandleNativeBuffer;
665 _rowHandleNativeBuffer = null;
666 if (null != rowHandleNativeBuffer) {
667 rowHandleNativeBuffer.Dispose();
671 Bid.ScopeLeave(ref hscp);
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;
683 // called from the connection which will remove this from its ReferenceCollection
684 // we want the NextResult behavior, but no errors
690 private void DisposeManagedRowset() {
691 //not cleared after last rowset
695 _hasRowsReadCheck = false;
697 _nextAccessorForRetrieval = 0;
698 _nextValueForRetrieval = 0;
700 Bindings[] bindings = _bindings;
703 if (null != bindings) {
704 for (int i = 0; i < bindings.Length; ++i) {
705 if (null != bindings[i]) { // MDAC 77007
706 bindings[i].Dispose();
712 _rowFetchedCount = IntPtr.Zero;
714 _dbSchemaTable = null;
715 _visibleFieldCount = 0;
717 _fieldNameLookup = null;
720 private void DisposeNativeMultipleResults() {
721 UnsafeNativeMethods.IMultipleResults imultipleResults = _imultipleResults;
722 _imultipleResults = null;
724 if (null != imultipleResults) {
725 Marshal.ReleaseComObject(imultipleResults);
729 private void DisposeNativeRowset() {
730 UnsafeNativeMethods.IRowset irowset = _irowset;
733 ChapterHandle chapter = _chapterHandle;
734 _chapterHandle = ChapterHandle.DB_NULL_HCHAPTER;
736 if (ChapterHandle.DB_NULL_HCHAPTER != chapter) { // MDAC 81441
740 if (null != irowset) {
741 Marshal.ReleaseComObject(irowset);
745 private void DisposeNativeRow() {
746 UnsafeNativeMethods.IRow irow = _irow;
750 Marshal.ReleaseComObject(irow);
754 private void DisposeOpenResults() {
755 DisposeManagedRowset();
758 DisposeNativeRowset();
761 override public Boolean GetBoolean(int ordinal) {
762 ColumnBinding binding = GetColumnBinding(ordinal);
763 return binding.ValueBoolean();
766 override public Byte GetByte(int ordinal) {
767 ColumnBinding binding = GetColumnBinding(ordinal);
768 return binding.ValueByte();
771 private ColumnBinding DoSequentialCheck(int ordinal, Int64 dataIndex, string method) {
772 ColumnBinding binding = GetColumnBinding(ordinal);
774 if (dataIndex > Int32.MaxValue) {
775 throw ADP.InvalidSourceBufferIndex(0, dataIndex, "dataIndex");
777 if (_sequentialOrdinal != ordinal) {
778 _sequentialOrdinal = ordinal;
779 _sequentialBytesRead = 0;
781 else if (_sequentialAccess && (_sequentialBytesRead < dataIndex)) {
782 throw ADP.NonSeqByteAccess(dataIndex, _sequentialBytesRead, method);
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
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();
793 if (null == buffer) {
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");
801 else if ((bufferIndex < 0) || (bufferIndex >= buffer.Length)) { // MDAC 71013
802 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
805 // @usernote: user may encounter ArgumentException from Buffer.BlockCopy
806 Buffer.BlockCopy(value, srcIndex, buffer, bufferIndex, byteCount);
807 _sequentialBytesRead = srcIndex + byteCount; // MDAC 71013
809 else if (length < 0) { // MDAC 71007
810 throw ADP.InvalidDataLength(length);
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();
822 if (null == buffer) {
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");
831 else if ((bufferIndex < 0) || (bufferIndex >= buffer.Length)) { // MDAC 71013
832 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
835 // @usernote: user may encounter ArgumentException from String.CopyTo
836 value.CopyTo(srcIndex, buffer, bufferIndex, charCount);
837 _sequentialBytesRead = srcIndex + charCount; // MDAC 71013
839 else if (length < 0) { // MDAC 71007
840 throw ADP.InvalidDataLength(length);
848 [ EditorBrowsableAttribute(EditorBrowsableState.Never) ] // MDAC 69508
849 override public Char GetChar(int ordinal) {
850 throw ADP.NotSupported();
853 [ EditorBrowsableAttribute(EditorBrowsableState.Advanced) ]
854 new public OleDbDataReader GetData(int ordinal) {
855 ColumnBinding binding = GetColumnBinding(ordinal);
856 return binding.ValueChapter();
859 override protected DbDataReader GetDbDataReader(int ordinal) {
860 return GetData(ordinal);
863 internal OleDbDataReader ResetChapter(int bindingIndex, int index, RowBinding rowbinding, int valueOffset) {
864 return GetDataForReader(_metadata[bindingIndex + index].ordinal, rowbinding, valueOffset);
867 private OleDbDataReader GetDataForReader(IntPtr ordinal, RowBinding rowbinding, int valueOffset) {
868 UnsafeNativeMethods.IRowsetInfo rowsetInfo = IRowsetInfo();
869 UnsafeNativeMethods.IRowset result;
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);
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();
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);
896 override public String GetDataTypeName(int index) {
897 if (null != _metadata) {
898 return _metadata[index].type.dataSourceType;
900 throw ADP.DataReaderNoData();
903 override public DateTime GetDateTime(int ordinal) {
904 ColumnBinding binding = GetColumnBinding(ordinal);
905 return binding.ValueDateTime();
908 override public Decimal GetDecimal(int ordinal) {
909 ColumnBinding binding = GetColumnBinding(ordinal);
910 return binding.ValueDecimal();
913 override public Double GetDouble(int ordinal) {
914 ColumnBinding binding = GetColumnBinding(ordinal);
915 return binding.ValueDouble();
918 override public IEnumerator GetEnumerator() {
919 return new DbEnumerator((IDataReader)this, IsCommandBehavior(CommandBehavior.CloseConnection));
922 override public Type GetFieldType(int index) {
923 if (null != _metadata) {
924 return _metadata[index].type.dataType;
926 throw ADP.DataReaderNoData();
929 override public Single GetFloat(int ordinal) {
930 ColumnBinding binding = GetColumnBinding(ordinal);
931 return binding.ValueSingle();
934 override public Guid GetGuid(int ordinal) {
935 ColumnBinding binding = GetColumnBinding(ordinal);
936 return binding.ValueGuid();
939 override public Int16 GetInt16(int ordinal) {
940 ColumnBinding binding = GetColumnBinding(ordinal);
941 return binding.ValueInt16();
944 override public Int32 GetInt32(int ordinal) {
945 ColumnBinding binding = GetColumnBinding(ordinal);
946 return binding.ValueInt32();
949 override public Int64 GetInt64(int ordinal) {
950 ColumnBinding binding = GetColumnBinding(ordinal);
951 return binding.ValueInt64();
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;
959 throw ADP.DataReaderNoData();
962 override public Int32 GetOrdinal(String name) {
963 if (null == _fieldNameLookup) {
964 if (null == _metadata) {
965 throw ADP.DataReaderNoData();
967 _fieldNameLookup = new FieldNameLookup(this, -1);
969 return _fieldNameLookup.GetOrdinal(name); // MDAC 71470
972 override public String GetString(int ordinal) {
973 ColumnBinding binding = GetColumnBinding(ordinal);
974 return binding.ValueString();
977 public TimeSpan GetTimeSpan(int ordinal) {
978 return (TimeSpan) GetValue(ordinal);
981 private MetaData DoValueCheck(int ordinal) {
983 // Read hasn't been called yet or no more data
984 throw ADP.DataReaderNoData();
986 else if (_sequentialAccess && (ordinal < _nextValueForRetrieval)) {
987 throw ADP.NonSequentialColumnAccess(ordinal, _nextValueForRetrieval);
989 // @usernote: user may encounter the IndexOutOfRangeException
990 MetaData info = _metadata[ordinal];
994 private ColumnBinding GetColumnBinding(int ordinal) {
995 MetaData info = DoValueCheck(ordinal);
996 return GetValueBinding(info);
999 private ColumnBinding GetValueBinding(MetaData info) {
1000 ColumnBinding binding = info.columnBinding;
1001 Debug.Assert(null != binding, "null binding");
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");
1008 if (_sequentialAccess) {
1009 if (_nextValueForRetrieval != binding.Index) { // release old value
1010 _metadata[_nextValueForRetrieval].columnBinding.ResetValue();
1012 _nextAccessorForRetrieval = binding.IndexForAccessor;
1015 if (null != _irowset) {
1016 GetRowDataFromHandle(); // will increment _nextAccessorForRetrieval
1018 else if (null != _irow) {
1019 GetRowValue(); // will increment _nextAccessorForRetrieval
1022 throw ADP.DataReaderNoData();
1026 // to enforce sequential access
1027 _nextValueForRetrieval = binding.Index;
1031 override public object GetValue(int ordinal) {
1032 ColumnBinding binding = GetColumnBinding(ordinal);
1033 object value = binding.Value();
1037 override public Int32 GetValues(object[] values) {
1038 if (null == values) {
1039 throw ADP.ArgumentNull("values");
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();
1050 private bool IsCommandBehavior(CommandBehavior condition) {
1051 return (condition == (condition & _commandBehavior));
1054 override public Boolean IsDBNull(int ordinal) {
1055 ColumnBinding binding = GetColumnBinding(ordinal);
1056 return binding.IsValueNull();
1059 private void ProcessResults(OleDbHResult hr) {
1061 if (null != _command) {
1062 e = OleDbConnection.ProcessResults(hr, _connection, _command);
1065 e = OleDbConnection.ProcessResults(hr, _connection, _connection);
1067 if (null != e) { throw e; }
1071 static private IntPtr AddRecordsAffected(IntPtr recordsAffected, IntPtr affected) { // MDAC 65374
1073 if (0 <= (int)affected) {
1074 if (0 <= (int)recordsAffected) {
1075 return (IntPtr)((int)recordsAffected + (int)affected);
1077 if (0 <= (long)affected) {
1078 if (0 <= (long)recordsAffected) {
1079 return (IntPtr)((long)recordsAffected + (long)affected);
1084 return recordsAffected;
1087 override public int VisibleFieldCount {
1089 Bid.Trace("<oledb.OleDbDataReader.get_VisibleFieldCount|API> %d#\n", ObjectID);
1091 throw ADP.DataReaderClosed("VisibleFieldCount");
1093 return _visibleFieldCount;
1097 internal void HasRowsRead() { // MDAC 78405
1098 Debug.Assert(!_hasRowsReadCheck, "_hasRowsReadCheck not reset");
1101 _hasRowsReadCheck = true;
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) {
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
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);
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
1131 if (null != connection) {
1132 Exception e = OleDbConnection.ProcessResults(hr, connection, command);
1134 OleDbException excep = (e as OleDbException);
1135 if (null != excep) {
1136 if (null == exceptions) {
1137 exceptions = new List<OleDbException>();
1139 exceptions.Add(excep);
1142 Debug.Assert(OleDbHResult.DB_E_OBJECTOPEN == hr, "unexpected");
1143 throw e; // we don't expect to be here, but it could happen
1148 SafeNativeMethods.Wrapper.ClearErrorInfo();
1149 break; // MDAC 72694
1151 recordsAffected = AddRecordsAffected(recordsAffected, affected);
1153 if (0 != (int)affected) {
1156 else if (2000 <= loop) { // MDAC 72126 (reason for more than 1000 iterations)
1157 NextResultsInfinite(); // MDAC 72738
1162 if (null != exceptions) {
1163 return OleDbException.CombineExceptions(exceptions);
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");
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");
1175 override public bool NextResult() {
1177 Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.NextResult|API> %d#\n", ObjectID);
1179 bool retflag = false;
1181 throw ADP.DataReaderClosed("NextResult");
1183 _fieldNameLookup = null;
1185 OleDbCommand command = _command;
1186 UnsafeNativeMethods.IMultipleResults imultipleResults = _imultipleResults;
1187 if (null != imultipleResults) {
1188 DisposeOpenResults();
1192 Debug.Assert(null == _irow, "NextResult: row loop check");
1193 Debug.Assert(null == _irowset, "NextResult: rowset loop check");
1195 object result = null;
1199 if ((null != command) && command.canceling) { // MDAC 69986
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);
1208 if ((0 <= hr) && (null != result)) {
1209 Bid.Trace("<oledb.IUnknown.QueryInterface|API|OLEDB|RowSet> %d#, IRowset\n", ObjectID);
1210 _irowset = (UnsafeNativeMethods.IRowset) result;
1212 _recordsAffected = AddRecordsAffected(_recordsAffected, affected);
1214 if (OleDbHResult.DB_S_NORESULT == hr) {
1215 DisposeNativeMultipleResults();
1218 // @devnote: infomessage events may be fired from here
1221 if (null != _irowset) {
1230 DisposeOpenResults(); // MDAC 70934
1231 _hasRows = false; // MDAC 85850
1236 Bid.ScopeLeave(ref hscp);
1240 override public bool Read() {
1242 Bid.ScopeEnter(out hscp, "<oledb.OleDbDataReader.Read|API> %d#\n", ObjectID);
1244 bool retflag = false;
1245 OleDbCommand command = _command;
1246 if ((null != command) && command.canceling) { // MDAC 69986
1247 DisposeOpenResults();
1249 else if (null != _irowset) {
1250 if (_hasRowsReadCheck) {
1251 _isRead = retflag = _hasRows;
1252 _hasRowsReadCheck = false;
1254 else if (_singleRow && _isRead) {
1255 DisposeOpenResults(); // MDAC 66109
1258 retflag = ReadRowset();
1261 else if (null != _irow) {
1262 retflag = ReadRow();
1264 else if (IsClosed) {
1265 throw ADP.DataReaderClosed("Read");
1270 Bid.ScopeLeave(ref hscp);
1274 private bool ReadRow() {
1276 _isRead = false; // for DoValueCheck
1280 _sequentialOrdinal = -1; // sequentialBytesRead will reset when used
1284 return (0 < _metadata.Length);
1289 private bool ReadRowset() {
1290 Debug.Assert(null != _irowset, "ReadRow: null IRowset");
1291 Debug.Assert(0 <= _metadata.Length, "incorrect state for fieldCount");
1293 // releases bindings as necessary
1294 // bumps current row, else resets it back to initial state
1295 ReleaseCurrentRow();
1297 _sequentialOrdinal = -1; // sequentialBytesRead will reset when used
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");
1307 // @devnote: releasing row handles occurs next time user calls read, skip, or close
1308 GetRowHandles(/*skipCount*/);
1310 return ((_currentRow <= (int)_rowFetchedCount) && _isRead);
1313 private void ReleaseCurrentRow() {
1314 Debug.Assert(null != _irowset, "ReleaseCurrentRow: no rowset");
1316 if (0 < (int)_rowFetchedCount) {
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();
1324 _nextAccessorForRetrieval = 0;
1325 _nextValueForRetrieval = 0;
1328 if (_currentRow == (int)_rowFetchedCount) {
1329 ReleaseRowHandles();
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");
1339 Bindings[] dbBindings = CreateBindingsFromMetaData(allowMultipleAccessor);
1341 UnsafeNativeMethods.IAccessor iaccessor = IAccessor();
1342 for (int i = 0; i < dbBindings.Length; ++i) {
1343 OleDbHResult hr = dbBindings[i].CreateAccessor(iaccessor, ODB.DBACCESSOR_ROWDATA);
1349 if (IntPtr.Zero == _rowHandleFetchCount) {
1350 _rowHandleFetchCount = new IntPtr(1);
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);
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);
1366 if (null == _rowHandleNativeBuffer) {
1367 _rowHandleNativeBuffer = new RowHandleBuffer(_rowHandleFetchCount);
1371 private Bindings[] CreateBindingsFromMetaData(bool allowMultipleAccessor) {
1372 int bindingCount = 0;
1373 int currentBindingIndex = 0;
1375 MetaData[] metadata = _metadata;
1377 int[] indexToBinding = new int[metadata.Length];
1378 int[] indexWithinBinding = new int[metadata.Length];
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;
1387 // @denote: single/multiple Accessors
1388 if ((bindingCount < 2) && IsLong(metadata[i].flags)) {
1390 currentBindingIndex = 0;
1393 currentBindingIndex++;
1396 // @devnote: one accessor per column option
1398 currentBindingIndex = 0;
1400 // @devnote: one accessor only for IRowset
1401 currentBindingIndex++;
1404 if (0 < currentBindingIndex) { // when blob is not the last column
1408 else if (null != _irow) {
1409 for (int i = 0; i < indexToBinding.Length; ++i) {
1410 indexToBinding[i] = i;
1411 indexWithinBinding[i] = 0;
1413 bindingCount = metadata.Length;
1417 for (int i = 0; i < indexToBinding.Length; ++i) {
1418 indexToBinding[i] = 0;
1419 indexWithinBinding[i] = i;
1425 Bindings[] dbbindings = new Bindings[bindingCount];
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) {
1434 for (int i = index; (i < metadata.Length) && (bindingCount == indexWithinBinding[i]); ++i) {
1437 dbbindings[indexToBinding[index]] = bindings = new Bindings((OleDbDataReader)this, (null != _irowset), bindingCount);
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;
1443 MetaData info = metadata[index];
1445 int maxLen = info.type.fixlen;
1446 short getType = info.type.wType;
1448 Debug.Assert(NativeDBType.STR != getType, "Should have bound as WSTR");
1449 Debug.Assert(!NativeDBType.HasHighBit(getType), "CreateAccessor - unexpected high bits on datatype");
1451 if (-1 != info.size) {
1452 if (info.type.islong) {
1453 maxLen = ADP.PtrSize;
1454 getType = (short)((ushort) getType | (ushort) NativeDBType.BYREF);
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;
1462 bindings.MaxLen = ADP.PtrSize;
1463 bindings.DbType = (short) (getType | DbType.BYREF);
1467 if (ODB.LargeDataSize < info.size) {
1468 maxLen = ADP.PtrSize;
1469 getType = (short)((ushort) getType | (ushort)NativeDBType.BYREF);
1471 else if ((NativeDBType.WSTR == getType) && (-1 != info.size)) { // WebData 99298
1472 maxLen = info.size * 2 + 2;
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;
1485 maxLen = ADP.PtrSize;
1486 getType = (short)((ushort) getType | (ushort)NativeDBType.BYREF);
1489 currentBindingIndex = indexWithinBinding[index];
1490 bindings.CurrentIndex = currentBindingIndex;
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)
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;
1509 if (Bid.AdvancedOn) {
1510 Bid.Trace("<oledb.struct.tagDBBINDING|INFO|ADV> index=%d, columnName='%ls'\n", index, info.columnName);//, bindings.bindings[index]);
1514 int count = 0, indexStart = 0;
1515 for (int i = 0; i < dbbindings.Length; ++i) {
1516 indexStart = dbbindings[i].AllocateForAccessor(this, indexStart, i);
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];
1527 _bindings = dbbindings;
1531 private void GetRowHandles(/*int skipCount*/) {
1532 Debug.Assert(0 < (int)_rowHandleFetchCount, "GetRowHandles: bad _rowHandleFetchCount");
1533 Debug.Assert(!_isRead, "GetRowHandles: _isRead");
1535 OleDbHResult hr = 0;
1537 RowHandleBuffer rowHandleBuffer = _rowHandleNativeBuffer;
1538 bool mustRelease = false;
1540 RuntimeHelpers.PrepareConstrainedRegions();
1542 rowHandleBuffer.DangerousAddRef(ref mustRelease);
1544 IntPtr rowHandlesPtr = rowHandleBuffer.DangerousGetHandle();
1545 UnsafeNativeMethods.IRowset irowset = IRowset();
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");
1552 catch(System.InvalidCastException e) { // MDAC 64320
1553 throw ODB.ThreadApartmentState(e);
1558 rowHandleBuffer.DangerousRelease();
1562 //if (/*DB_S_ROWLIMITEXCEEDED*/0x00040EC0 == hr) {
1563 // _rowHandleFetchCount = 1;
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)
1573 _isRead = ((OleDbHResult.DB_S_ENDOFROWSET != hr) || (0 < (int)_rowFetchedCount)); // MDAC 59264
1574 _rowFetchedCount = (IntPtr)Math.Max((int)_rowFetchedCount, 0);
1577 private void GetRowDataFromHandle() {
1578 Debug.Assert(null != _bindings, "GetRowDataFromHandle: null dbBindings");
1579 Debug.Assert(null != _rowHandleNativeBuffer, "GetRowDataFromHandle: null dbBindings");
1581 OleDbHResult hr = 0;
1582 UnsafeNativeMethods.IRowset irowset = IRowset();
1584 IntPtr rowHandle = _rowHandleNativeBuffer.GetRowHandle(_currentRow);
1586 RowBinding rowBinding = _bindings[_nextAccessorForRetrieval].RowBinding();
1587 IntPtr accessorHandle = rowBinding.DangerousGetAccessorHandle();
1589 bool mustRelease = false;
1590 RuntimeHelpers.PrepareConstrainedRegions();
1592 rowBinding.DangerousAddRef(ref mustRelease);
1593 rowBinding.StartDataBlock();
1595 IntPtr dataPtr = rowBinding.DangerousGetDataPtr();
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);
1603 rowBinding.DangerousRelease();
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));
1615 _nextAccessorForRetrieval++;
1621 private void ReleaseRowHandles() {
1622 Debug.Assert(0 < (int)_rowFetchedCount, "invalid _rowFetchedCount");
1625 UnsafeNativeMethods.IRowset irowset = IRowset();
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);
1632 //ProcessFailure(hr);
1633 //ProcessFailure(hr);
1634 SafeNativeMethods.Wrapper.ClearErrorInfo();
1636 _rowFetchedCount = IntPtr.Zero;
1638 _isRead = false; // MDAC 59264
1641 private object GetPropertyValue(int propertyId) { // MDAC 72106
1642 if (null != _irowset) {
1643 return GetPropertyOnRowset(OleDbPropertySetGuid.Rowset, propertyId);
1645 else if (null != _command) {
1646 return _command.GetPropertyValue(OleDbPropertySetGuid.Rowset, propertyId);
1648 return OleDbPropertyStatus.NotSupported;
1651 private object GetPropertyOnRowset(Guid propertySet, int propertyID) {
1653 tagDBPROP[] dbprops;
1654 UnsafeNativeMethods.IRowsetInfo irowsetinfo = IRowsetInfo();
1656 using(PropertyIDSet propidset = new PropertyIDSet(propertySet, propertyID)) {
1658 using(DBPropSet propset = new DBPropSet(irowsetinfo, propidset, out hr)) {
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();
1664 dbprops = propset.GetPropertySet(0, out propertySet);
1667 if (OleDbPropertyStatus.Ok == dbprops[0].dwStatus) {
1668 return dbprops[0].vValue;
1670 return dbprops[0].dwStatus;
1673 private void GetRowValue() {
1674 Debug.Assert(null != _irow, "GetRowValue: null IRow");
1675 Debug.Assert(null != _metadata, "GetRowValue: null MetaData");
1677 Bindings bindings = _bindings[_nextAccessorForRetrieval];
1678 ColumnBinding[] columnBindings = bindings.ColumnBindings();
1679 RowBinding rowBinding = bindings.RowBinding();
1680 Debug.Assert(_nextValueForRetrieval <= columnBindings[0].Index, "backwards retrieval");
1682 bool mustReleaseBinding = false;
1683 bool[] mustRelease = new bool[columnBindings.Length];
1684 StringMemHandle[] sptr = new StringMemHandle[columnBindings.Length];
1686 RuntimeHelpers.PrepareConstrainedRegions();
1688 for (int i = 0; i < columnBindings.Length; ++i) {
1689 bindings.CurrentIndex = i;
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];
1698 sptr[i].DangerousAddRef(ref mustRelease[i]);
1700 IntPtr ulPropid = ((null != sptr[i]) ? sptr[i].DangerousGetHandle() : info.propid);
1701 bindings.GuidKindName(info.guid, info.kind, ulPropid);
1705 tagDBCOLUMNACCESS[] access = bindings.DBColumnAccess;
1707 rowBinding.DangerousAddRef(ref mustReleaseBinding);
1708 rowBinding.StartDataBlock();
1710 UnsafeNativeMethods.IRow irow = IRow();
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);
1717 if (mustReleaseBinding) {
1718 rowBinding.DangerousRelease();
1720 for (int i = 0; i < mustRelease.Length; i++) {
1721 if (mustRelease[i]) {
1722 sptr[i].DangerousRelease();
1726 _nextAccessorForRetrieval++;
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
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);
1742 private void AppendSchemaInfo() {
1743 Debug.Assert(null != _connection, "null connection");
1744 Debug.Assert(null != _metadata, "no _metadata");
1746 if (_metadata.Length <= 0) {
1751 for (int i = 0; i < _metadata.Length; ++i) {
1752 if (_metadata[i].isKeyColumn && !_metadata[i].isHidden) { // MDAC 90411
1756 if (0 != keyCount) /*|| _connection.IsServer_msdaora || _connection.IsServer_Microsoft_SQL)*/ { // MDAC 60109
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;
1772 else if ((0 != ADP.SrcCompare(baseTableName, info.baseTableName))
1773 || (0 != ADP.SrcCompare(baseCatalogName, catalogName))
1774 || (0 != ADP.SrcCompare(baseSchemaName, schemaName))) { // MDAC 71808
1776 if (AdapterSwitches.DataSchema.TraceVerbose) {
1777 Debug.WriteLine("Multiple BaseTableName detected:"
1778 +" <"+baseCatalogName+"."+baseCatalogName+"."+baseTableName+">"
1779 +" <"+info.baseCatalogName+"."+info.baseCatalogName+"."+info.baseTableName+">");
1782 baseTableName = null;
1787 if (null == baseTableName) {
1790 baseCatalogName = ADP.IsEmpty(baseCatalogName) ? null : baseCatalogName;
1791 baseSchemaName = ADP.IsEmpty(baseSchemaName) ? null : baseSchemaName;
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);
1803 baseTableName = s + baseTableName + p;
1807 Hashtable baseColumnNames = new Hashtable(_metadata.Length * 2); // MDAC 67385
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;
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;
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);
1830 if (0 != keyCount) {
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);
1841 private int AppendSchemaPrimaryKey(Hashtable baseColumnNames, object[] restrictions) {
1843 bool partialPrimaryKey = false;
1844 DataTable table = null;
1846 table = _connection.GetSchemaRowset(OleDbSchemaGuid.Primary_Keys, restrictions);
1848 catch(Exception e) {
1850 if (!ADP.IsCatchableExceptionType(e)) {
1854 ADP.TraceExceptionWithoutRethrow(e);
1856 if (null != table) {
1857 DataColumnCollection dataColumns = table.Columns;
1858 int nameColumnIndex = dataColumns.IndexOf(ODB.COLUMN_NAME);
1860 if (-1 != nameColumnIndex) {
1861 DataColumn nameColumn = dataColumns[nameColumnIndex];
1862 foreach(DataRow dataRow in table.Rows) {
1863 string name = (string) dataRow[nameColumn, DataRowVersion.Default];
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;
1874 if (AdapterSwitches.DataSchema.TraceVerbose) {
1875 Debug.WriteLine("PartialKeyColumn detected: <" + name + "> metaindex=" + metaindex);
1878 partialPrimaryKey = true;
1884 if (partialPrimaryKey) { // partial primary key detected
1885 for (int i = 0; i < _metadata.Length; ++i) {
1886 _metadata[i].isKeyColumn = false;
1893 private void AppendSchemaUniqueIndexAsKey(Hashtable baseColumnNames, object[] restrictions) {
1894 bool partialPrimaryKey = false;
1895 DataTable table = null;
1897 table = _connection.GetSchemaRowset(OleDbSchemaGuid.Indexes, restrictions);
1899 catch(Exception e) {
1901 if (!ADP.IsCatchableExceptionType(e)) {
1905 ADP.TraceExceptionWithoutRethrow(e);
1907 if (null != table) {
1908 DataColumnCollection dataColumns = table.Columns;
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);
1916 if ((-1 != indxIndex) && (-1 != pkeyIndex) && (-1 != uniqIndex) && (-1 != nameIndex)) {
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);
1924 bool[] keys = new bool[_metadata.Length];
1925 bool[] uniq = new bool[_metadata.Length];
1926 string uniqueIndexName = null;
1928 // match pkey name BaseColumnName
1929 foreach(DataRow dataRow in table.Rows) {
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)));
1935 if (isPKey || isUniq) {
1936 string name = (string) dataRow[nameColumn, DataRowVersion.Default];
1938 int metaindex = IndexOf(baseColumnNames, name); // MDAC 67385
1939 if (0 <= metaindex) {
1941 keys[metaindex] = true;
1943 if (isUniq && (null != uniq)) {
1944 uniq[metaindex] = true;
1946 string indexname = (string) dataRow[indxColumn, DataRowVersion.Default];
1947 if (null == uniqueIndexName) {
1948 uniqueIndexName = indexname;
1950 else if (indexname != uniqueIndexName) {
1952 if (AdapterSwitches.DataSchema.TraceVerbose) {
1953 Debug.WriteLine("MultipleUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">");
1962 if (AdapterSwitches.DataSchema.TraceVerbose) {
1963 Debug.WriteLine("PartialKeyColumn detected: " + name);
1966 partialPrimaryKey = true;
1969 else if (null != uniqueIndexName) {
1970 string indexname = (string) dataRow[indxColumn, DataRowVersion.Default];
1972 if (indexname != uniqueIndexName) {
1974 if (AdapterSwitches.DataSchema.TraceVerbose) {
1975 Debug.WriteLine("PartialUniqueIndexes detected: <" + uniqueIndexName + "> <" + indexname + ">");
1983 if (partialPrimaryKey) {
1984 for (int i = 0; i < _metadata.Length; ++i) {
1985 _metadata[i].isKeyColumn = false;
1989 else if (null != uniq) {
1991 if (AdapterSwitches.DataSchema.TraceVerbose) {
1992 Debug.WriteLine("upgrade single unique index to be a key: <" + uniqueIndexName + ">");
1995 // upgrade single unique index to be a key
1996 for (int i = 0; i < _metadata.Length; ++i) {
1997 _metadata[i].isKeyColumn = uniq[i];
2004 private MetaData FindMetaData(string name) {
2005 int index = _fieldNameLookup.IndexOfName(name);
2006 return ((-1 != index) ? _metadata[index] : null);
2009 internal void DumpToSchemaTable(UnsafeNativeMethods.IRowset rowset) {
2010 List<MetaData> metainfo = new List<MetaData>();
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);
2017 hiddenColumns = GetPropertyValue(ODB.DBPROP_HIDDENCOLUMNS); // MDAC 55611, 72106
2018 if (0 == dataReader.FieldCount) {
2022 Debug.Assert(null == dataReader._fieldNameLookup, "lookup already exists");
2023 FieldNameLookup lookup = new FieldNameLookup(dataReader, -1);
2024 dataReader._fieldNameLookup = lookup;
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);
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);
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);
2052 ColumnBinding binding;
2053 while (dataReader.ReadRowset()) {
2054 dataReader.GetRowDataFromHandle();
2056 MetaData info = new MetaData();
2058 binding = columnidname.columnBinding; // MDAC 72627
2059 if (!binding.IsValueNull()) {
2060 info.idname = (string)binding.Value();
2061 info.kind = ODB.DBKIND_NAME;
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);
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);
2076 binding = columnname.columnBinding;
2077 if (!binding.IsValueNull()) {
2078 info.columnName = (string)binding.Value();
2081 info.columnName = "";
2084 if (4 == ADP.PtrSize) {
2085 info.ordinal = (IntPtr)columnordinal.columnBinding.Value_UI4();
2088 info.ordinal = (IntPtr)columnordinal.columnBinding.Value_UI8();
2090 short wType = unchecked((short) dbtype.columnBinding.Value_UI2());
2092 if (4 == ADP.PtrSize) {
2093 info.size = unchecked((int) columnsize.columnBinding.Value_UI4()); // WebData 99298
2096 info.size = ADP.IntPtrToInt32((IntPtr)unchecked((long)columnsize.columnBinding.Value_UI8()));
2099 binding = numericprecision.columnBinding;
2100 if (!binding.IsValueNull()) {
2101 info.precision = (byte)binding.Value_UI2();
2104 binding = numericscale.columnBinding;
2105 if (!binding.IsValueNull()) {
2106 info.scale = (byte)binding.Value_I2();
2109 info.flags = unchecked((int) columnflags.columnBinding.Value_UI4());
2111 bool islong = OleDbDataReader.IsLong(info.flags);
2112 bool isfixed = OleDbDataReader.IsFixed(info.flags);
2113 NativeDBType dbType = NativeDBType.FromDBType(wType, islong, isfixed);
2117 if (null != isautoincrement) {
2118 binding = isautoincrement.columnBinding;
2119 if (!binding.IsValueNull()) {
2120 info.isAutoIncrement = binding.Value_BOOL();
2123 if (null != isunique) {
2124 binding = isunique.columnBinding;
2125 if (!binding.IsValueNull()) {
2126 info.isUnique = binding.Value_BOOL();
2129 if (null != iskeycolumn) {
2130 binding = iskeycolumn.columnBinding;
2131 if (!binding.IsValueNull()) {
2132 info.isKeyColumn = binding.Value_BOOL();
2135 if (null != baseschemaname) {
2136 binding = baseschemaname.columnBinding;
2137 if (!binding.IsValueNull()) {
2138 info.baseSchemaName = binding.ValueString();
2141 if (null != basecatalogname) {
2142 binding = basecatalogname.columnBinding;
2143 if (!binding.IsValueNull()) {
2144 info.baseCatalogName = binding.ValueString();
2147 if (null != basetablename) {
2148 binding = basetablename.columnBinding;
2149 if (!binding.IsValueNull()) {
2150 info.baseTableName = binding.ValueString();
2153 if (null != basecolumnname) {
2154 binding = basecolumnname.columnBinding;
2155 if (!binding.IsValueNull()) {
2156 info.baseColumnName = binding.ValueString();
2163 int visibleCount = metainfo.Count;
2164 if (hiddenColumns is Int32) {
2165 visibleCount -= (int)hiddenColumns;
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;
2174 for (int index = metainfo.Count - 1; visibleCount <= index; --index) {
2175 MetaData info = metainfo[index];
2177 info.isHidden = true;
2179 if (disallowKeyColumns) {
2180 info.isKeyColumn = false;
2182 else if (info.guid.Equals(ODB.DBCOL_SPECIALCOL)) { // MDAC 90827
2183 info.isKeyColumn = false;
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;
2194 for (int index = visibleCount - 1; 0 <= index; --index) {
2195 MetaData info = metainfo[index];
2197 if (disallowKeyColumns) {
2198 info.isKeyColumn = false;
2201 if (info.guid.Equals(ODB.DBCOL_SPECIALCOL)) { // MDAC 72390
2203 if (AdapterSwitches.DataSchema.TraceVerbose) {
2204 Debug.WriteLine("Filtered Column: DBCOLUMN_GUID=DBCOL_SPECIALCOL DBCOLUMN_NAME=" + info.columnName + " DBCOLUMN_KEYCOLUMN=" + info.isKeyColumn);
2207 info.isHidden = true;
2211 else if (0 >= (int)info.ordinal) {
2213 else if (0 >= (long)info.ordinal) {
2216 if (AdapterSwitches.DataSchema.TraceVerbose) {
2217 Debug.WriteLine("Filtered Column: DBCOLUMN_NUMBER=" + info.ordinal.ToInt64().ToString(CultureInfo.InvariantCulture) + " DBCOLUMN_NAME=" + info.columnName);
2220 info.isHidden = true;
2223 else if (OleDbDataReader.DoColumnDropFilter(info.flags)) {
2225 if (AdapterSwitches.DataSchema.TraceVerbose) {
2226 Debug.WriteLine("Filtered Column: DBCOLUMN_FLAGS=" + info.flags.ToString("X8", (System.IFormatProvider)null) + " DBCOLUMN_NAME=" + info.columnName);
2229 info.isHidden = true;
2235 metainfo.Sort(); // MDAC 68319
2236 _visibleFieldCount = visibleCount;
2237 _metadata = metainfo.ToArray();
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();
2246 dataReader.BuildSchemaTableInfo(handle, false, false); // only tries IColumnsInfo
2248 MetaData[] metadata = dataReader.MetaData;
2249 if ((null != metadata) && (0 < metadata.Length)) {
2250 dataReader.BuildSchemaTable(metadata);
2254 static private bool DoColumnDropFilter(int flags) {
2255 return (0 != (ODB.DBCOLUMNFLAGS_ISBOOKMARK & flags));
2257 static private bool IsLong(int flags) {
2258 return (0 != (ODB.DBCOLUMNFLAGS_ISLONG & flags));
2260 static private bool IsFixed(int flags) {
2261 return (0 != (ODB.DBCOLUMNFLAGS_ISFIXEDLENGTH & flags));
2263 static private bool IsRowVersion(int flags) {
2264 return (0 != (ODB.DBCOLUMNFLAGS_ISROWID_DBCOLUMNFLAGS_ISROWVER & flags));
2266 static private bool AllowDBNull(int flags) {
2267 return (0 != (ODB.DBCOLUMNFLAGS_ISNULLABLE & flags));
2269 static private bool AllowDBNullMaybeNull(int flags) {
2270 return (0 != (ODB.DBCOLUMNFLAGS_ISNULLABLE_DBCOLUMNFLAGS_MAYBENULL & flags));
2272 static private bool IsReadOnly(int flags) {
2273 return (0 == (ODB.DBCOLUMNFLAGS_WRITE_DBCOLUMNFLAGS_WRITEUNKNOWN & flags));
2277 sealed internal class MetaData : IComparable {
2279 internal Bindings bindings;
2280 internal ColumnBinding columnBinding;
2282 internal string columnName;
2284 internal Guid guid; // MDAC 72627
2286 internal IntPtr propid;
2287 internal string idname;
2289 internal NativeDBType type;
2291 internal IntPtr ordinal;
2296 internal byte precision;
2297 internal byte scale;
2299 internal bool isAutoIncrement;
2300 internal bool isUnique;
2301 internal bool isKeyColumn;
2302 internal bool isHidden;
2304 internal string baseSchemaName;
2305 internal string baseCatalogName;
2306 internal string baseTableName;
2307 internal string baseColumnName;
2309 int IComparable.CompareTo(object obj) { // MDAC 68319
2310 if (isHidden == (obj as MetaData).isHidden) {
2312 return ((int)ordinal - (int)(obj as MetaData).ordinal);
2314 long v = ((long)ordinal - (long)(obj as MetaData).ordinal);
2315 return ((0 < v) ? 1 : ((v < 0) ? -1 : 0));
2319 return (isHidden) ? 1 : -1; // ensure that all hidden columns come after non-hidden columns
2322 internal MetaData() {