1 //------------------------------------------------------------------------------
2 // <copyright file="SqlDataReader.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.SqlClient {
11 using System.Collections;
12 using System.Collections.Specialized;
13 using System.ComponentModel;
15 using System.Data.Sql;
16 using System.Data.SqlTypes;
17 using System.Data.Common;
18 using System.Data.ProviderBase;
19 using System.Diagnostics;
20 using System.Globalization;
22 using System.Reflection;
23 using System.Runtime.CompilerServices;
24 using System.Threading;
27 using Microsoft.SqlServer.Server;
28 using System.Threading.Tasks;
30 public class SqlDataReader : DbDataReader, IDataReader {
32 private enum ALTROWSTATUS {
33 Null = 0, // default and after Done
34 AltRow, // after calling NextResult and the first AltRow is available for read
35 Done, // after consuming the value (GetValue -> GetValueInternal)
38 internal class SharedState { // parameters needed to execute cleanup from parser
39 internal int _nextColumnHeaderToRead;
40 internal int _nextColumnDataToRead;
41 internal long _columnDataBytesRemaining;
42 internal bool _dataReady; // ready to ProcessRow
45 internal SharedState _sharedState = new SharedState();
47 private TdsParser _parser; //
48 private TdsParserStateObject _stateObj;
49 private SqlCommand _command;
50 private SqlConnection _connection;
51 private int _defaultLCID;
52 private bool _haltRead; // bool to denote whether we have read first row for single row behavior
53 private bool _metaDataConsumed;
54 private bool _browseModeInfoConsumed;
55 private bool _isClosed;
56 private bool _isInitialized; // Webdata 104560
57 private bool _hasRows;
58 private ALTROWSTATUS _altRowStatus;
59 private int _recordsAffected = -1;
60 private long _defaultTimeoutMilliseconds;
61 private SqlConnectionString.TypeSystem _typeSystem;
63 // SQLStatistics support
64 private SqlStatistics _statistics;
65 private SqlBuffer[] _data; // row buffer, filled in by ReadColumnData()
66 private SqlStreamingXml _streamingXml; // Used by Getchars on an Xml column for sequential access
68 // buffers and metadata
69 private _SqlMetaDataSet _metaData; // current metaData for the stream, it is lazily loaded
70 private _SqlMetaDataSetCollection _altMetaDataSetCollection;
71 private FieldNameLookup _fieldNameLookup;
72 private CommandBehavior _commandBehavior;
74 private static int _objectTypeCount; // Bid counter
75 internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
78 // undone: we may still want to do this...it's nice to pass in an lpvoid (essentially) and just have the reader keep the state
79 // private object _context = null; // this is never looked at by the stream object. It is used by upper layers who wish
80 // to remain stateless
82 // metadata (no explicit table, use 'Table')
83 private MultiPartTableName[] _tableNames = null;
84 private string _resetOptionsString;
86 private int _lastColumnWithDataChunkRead;
87 private long _columnDataBytesRead; // last byte read by user
88 private long _columnDataCharsRead; // last char read by user
89 private char[] _columnDataChars;
90 private int _columnDataCharsIndex; // Column index that is currently loaded in _columnDataChars
92 private Task _currentTask;
93 private Snapshot _snapshot;
94 private CancellationTokenSource _cancelAsyncOnCloseTokenSource;
95 private CancellationToken _cancelAsyncOnCloseToken;
97 // Used for checking if the Type parameter provided to GetValue<T> is an INullable
98 internal static readonly Type _typeofINullable = typeof(INullable);
99 private static readonly Type _typeofSqlString = typeof(SqlString);
101 private SqlSequentialStream _currentStream;
102 private SqlSequentialTextReader _currentTextReader;
104 internal SqlDataReader(SqlCommand command, CommandBehavior behavior)
106 SqlConnection.VerifyExecutePermission();
109 _commandBehavior = behavior;
110 if (_command != null) {
111 _defaultTimeoutMilliseconds = (long)command.CommandTimeout * 1000L;
112 _connection = command.Connection;
113 if (_connection != null) {
114 _statistics = _connection.Statistics;
115 _typeSystem = _connection.TypeSystem;
118 _sharedState._dataReady = false;
119 _metaDataConsumed = false;
121 _browseModeInfoConsumed = false;
122 _currentStream = null;
123 _currentTextReader = null;
124 _cancelAsyncOnCloseTokenSource = new CancellationTokenSource();
125 _cancelAsyncOnCloseToken = _cancelAsyncOnCloseTokenSource.Token;
126 _columnDataCharsIndex = -1;
129 internal bool BrowseModeInfoConsumed {
131 _browseModeInfoConsumed = value;
135 internal SqlCommand Command {
141 protected SqlConnection Connection {
147 override public int Depth {
150 throw ADP.DataReaderClosed("Depth");
157 // fields/attributes collection
158 override public int FieldCount {
161 throw ADP.DataReaderClosed("FieldCount");
163 if (_currentTask != null) {
164 throw ADP.AsyncOperationPending();
167 if (MetaData == null) {
171 return _metaData.Length;
175 override public bool HasRows {
178 throw ADP.DataReaderClosed("HasRows");
180 if (_currentTask != null) {
181 throw ADP.AsyncOperationPending();
188 override public bool IsClosed {
194 internal bool IsInitialized {
196 return _isInitialized;
199 Debug.Assert(value, "attempting to uninitialize a data reader?");
200 _isInitialized = value;
204 // NOTE: For PLP values this indicates the amount of data left in the current chunk (or 0 if there are no more chunks left)
205 internal long ColumnDataBytesRemaining() {
206 // If there are an unknown (-1) number of bytes left for a PLP, read its size
207 if (-1 == _sharedState._columnDataBytesRemaining) {
208 _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj);
211 return _sharedState._columnDataBytesRemaining;
214 internal _SqlMetaDataSet MetaData {
217 throw ADP.DataReaderClosed("MetaData");
219 // metaData comes in pieces: colmetadata, tabname, colinfo, etc
220 // if we have any metaData, return it. If we have none,
222 if (_metaData == null && !_metaDataConsumed) {
223 if (_currentTask != null) {
224 throw SQL.PendingBeginXXXExists();
227 RuntimeHelpers.PrepareConstrainedRegions();
230 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
232 RuntimeHelpers.PrepareConstrainedRegions();
234 tdsReliabilitySection.Start();
239 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
240 if (!TryConsumeMetaData())
242 throw SQL.SynchronousCallMayNotPend();
247 tdsReliabilitySection.Stop();
251 catch (System.OutOfMemoryException e) {
253 if (null != _connection) {
254 _connection.Abort(e);
258 catch (System.StackOverflowException e) {
260 if (null != _connection) {
261 _connection.Abort(e);
265 catch (System.Threading.ThreadAbortException e) {
267 if (null != _connection) {
268 _connection.Abort(e);
277 internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() {
278 SmiExtendedMetaData[] metaDataReturn = null;
279 _SqlMetaDataSet metaData = this.MetaData;
281 if ( null != metaData && 0 < metaData.Length ) {
282 metaDataReturn = new SmiExtendedMetaData[metaData.visibleColumns];
284 for( int index=0; index < metaData.Length; index++ ) {
285 _SqlMetaData colMetaData = metaData[index];
287 if ( !colMetaData.isHidden ) {
288 SqlCollation collation = colMetaData.collation;
290 string typeSpecificNamePart1 = null;
291 string typeSpecificNamePart2 = null;
292 string typeSpecificNamePart3 = null;
294 if (SqlDbType.Xml == colMetaData.type) {
295 typeSpecificNamePart1 = colMetaData.xmlSchemaCollectionDatabase;
296 typeSpecificNamePart2 = colMetaData.xmlSchemaCollectionOwningSchema;
297 typeSpecificNamePart3 = colMetaData.xmlSchemaCollectionName;
299 else if (SqlDbType.Udt == colMetaData.type) {
300 Connection.CheckGetExtendedUDTInfo(colMetaData, true); // SQLBUDT #370593 ensure that colMetaData.udtType is set
302 typeSpecificNamePart1 = colMetaData.udtDatabaseName;
303 typeSpecificNamePart2 = colMetaData.udtSchemaName;
304 typeSpecificNamePart3 = colMetaData.udtTypeName;
307 int length = colMetaData.length;
308 if ( length > TdsEnums.MAXSIZE ) {
309 length = (int) SmiMetaData.UnlimitedMaxLengthIndicator;
311 else if (SqlDbType.NChar == colMetaData.type
312 ||SqlDbType.NVarChar == colMetaData.type) {
313 length /= ADP.CharSize;
316 metaDataReturn[index] = new SmiQueryMetaData(
319 colMetaData.precision,
321 (null != collation) ? collation.LCID : _defaultLCID,
322 (null != collation) ? collation.SqlCompareOptions : SqlCompareOptions.None,
324 false, // isMultiValued
325 null, // fieldmetadata
326 null, // extended properties
328 typeSpecificNamePart1,
329 typeSpecificNamePart2,
330 typeSpecificNamePart3,
331 colMetaData.isNullable,
332 colMetaData.serverName,
333 colMetaData.catalogName,
334 colMetaData.schemaName,
335 colMetaData.tableName,
336 colMetaData.baseColumn,
338 colMetaData.isIdentity,
339 0==colMetaData.updatability,
340 colMetaData.isExpression,
341 colMetaData.isDifferentName,
348 return metaDataReturn;
351 override public int RecordsAffected {
353 if (null != _command)
354 return _command.InternalRecordsAffected;
356 // cached locally for after Close() when command is nulled out
357 return _recordsAffected;
361 internal string ResetOptionsString {
363 _resetOptionsString = value;
367 private SqlStatistics Statistics {
373 internal MultiPartTableName[] TableNames {
382 override public int VisibleFieldCount {
385 throw ADP.DataReaderClosed("VisibleFieldCount");
387 _SqlMetaDataSet md = this.MetaData;
391 return (md.visibleColumns);
396 override public object this[int i] {
402 override public object this[string name] {
404 return GetValue(GetOrdinal(name));
408 internal void Bind(TdsParserStateObject stateObj) {
409 Debug.Assert(null != stateObj, "null stateobject");
411 Debug.Assert(null == _snapshot, "Should not change during execution of asynchronous command");
413 stateObj.Owner = this;
414 _stateObj = stateObj;
415 _parser = stateObj.Parser;
416 _defaultLCID = _parser.DefaultLCID;
419 // Fills in a schema table with meta data information. This function should only really be called by
422 internal DataTable BuildSchemaTable() {
423 _SqlMetaDataSet md = this.MetaData;
424 Debug.Assert(null != md, "BuildSchemaTable - unexpected null metadata information");
426 DataTable schemaTable = new DataTable("SchemaTable");
427 schemaTable.Locale = CultureInfo.InvariantCulture;
428 schemaTable.MinimumCapacity = md.Length;
430 DataColumn ColumnName = new DataColumn(SchemaTableColumn.ColumnName, typeof(System.String));
431 DataColumn Ordinal = new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(System.Int32));
432 DataColumn Size = new DataColumn(SchemaTableColumn.ColumnSize, typeof(System.Int32));
433 DataColumn Precision = new DataColumn(SchemaTableColumn.NumericPrecision, typeof(System.Int16));
434 DataColumn Scale = new DataColumn(SchemaTableColumn.NumericScale, typeof(System.Int16));
436 DataColumn DataType = new DataColumn(SchemaTableColumn.DataType, typeof(System.Type));
437 DataColumn ProviderSpecificDataType = new DataColumn(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(System.Type));
438 DataColumn NonVersionedProviderType = new DataColumn(SchemaTableColumn.NonVersionedProviderType, typeof(System.Int32));
439 DataColumn ProviderType = new DataColumn(SchemaTableColumn.ProviderType, typeof(System.Int32));
441 DataColumn IsLong = new DataColumn(SchemaTableColumn.IsLong, typeof(System.Boolean));
442 DataColumn AllowDBNull = new DataColumn(SchemaTableColumn.AllowDBNull, typeof(System.Boolean));
443 DataColumn IsReadOnly = new DataColumn(SchemaTableOptionalColumn.IsReadOnly, typeof(System.Boolean));
444 DataColumn IsRowVersion = new DataColumn(SchemaTableOptionalColumn.IsRowVersion, typeof(System.Boolean));
446 DataColumn IsUnique = new DataColumn(SchemaTableColumn.IsUnique, typeof(System.Boolean));
447 DataColumn IsKey = new DataColumn(SchemaTableColumn.IsKey, typeof(System.Boolean));
448 DataColumn IsAutoIncrement = new DataColumn(SchemaTableOptionalColumn.IsAutoIncrement, typeof(System.Boolean));
449 DataColumn IsHidden = new DataColumn(SchemaTableOptionalColumn.IsHidden, typeof(System.Boolean));
451 DataColumn BaseCatalogName = new DataColumn(SchemaTableOptionalColumn.BaseCatalogName, typeof(System.String));
452 DataColumn BaseSchemaName = new DataColumn(SchemaTableColumn.BaseSchemaName, typeof(System.String));
453 DataColumn BaseTableName = new DataColumn(SchemaTableColumn.BaseTableName, typeof(System.String));
454 DataColumn BaseColumnName = new DataColumn(SchemaTableColumn.BaseColumnName, typeof(System.String));
456 // unique to SqlClient
457 DataColumn BaseServerName = new DataColumn(SchemaTableOptionalColumn.BaseServerName, typeof(System.String));
458 DataColumn IsAliased = new DataColumn(SchemaTableColumn.IsAliased, typeof(System.Boolean));
459 DataColumn IsExpression = new DataColumn(SchemaTableColumn.IsExpression, typeof(System.Boolean));
460 DataColumn IsIdentity = new DataColumn("IsIdentity", typeof(System.Boolean));
461 DataColumn DataTypeName = new DataColumn("DataTypeName", typeof(System.String));
462 DataColumn UdtAssemblyQualifiedName = new DataColumn("UdtAssemblyQualifiedName", typeof(System.String));
463 // Xml metadata specific
464 DataColumn XmlSchemaCollectionDatabase = new DataColumn("XmlSchemaCollectionDatabase", typeof(System.String));
465 DataColumn XmlSchemaCollectionOwningSchema = new DataColumn("XmlSchemaCollectionOwningSchema", typeof(System.String));
466 DataColumn XmlSchemaCollectionName = new DataColumn("XmlSchemaCollectionName", typeof(System.String));
468 DataColumn IsColumnSet = new DataColumn("IsColumnSet", typeof(System.Boolean));
470 Ordinal.DefaultValue = 0;
471 IsLong.DefaultValue = false;
473 DataColumnCollection columns = schemaTable.Columns;
475 // must maintain order for backward compatibility
476 columns.Add(ColumnName);
477 columns.Add(Ordinal);
479 columns.Add(Precision);
481 columns.Add(IsUnique);
483 columns.Add(BaseServerName);
484 columns.Add(BaseCatalogName);
485 columns.Add(BaseColumnName);
486 columns.Add(BaseSchemaName);
487 columns.Add(BaseTableName);
488 columns.Add(DataType);
489 columns.Add(AllowDBNull);
490 columns.Add(ProviderType);
491 columns.Add(IsAliased);
492 columns.Add(IsExpression);
493 columns.Add(IsIdentity);
494 columns.Add(IsAutoIncrement);
495 columns.Add(IsRowVersion);
496 columns.Add(IsHidden);
498 columns.Add(IsReadOnly);
499 columns.Add(ProviderSpecificDataType);
500 columns.Add(DataTypeName);
501 columns.Add(XmlSchemaCollectionDatabase);
502 columns.Add(XmlSchemaCollectionOwningSchema);
503 columns.Add(XmlSchemaCollectionName);
504 columns.Add(UdtAssemblyQualifiedName);
505 columns.Add(NonVersionedProviderType);
506 columns.Add(IsColumnSet);
508 for (int i = 0; i < md.Length; i++) {
509 _SqlMetaData col = md[i];
510 DataRow schemaRow = schemaTable.NewRow();
512 schemaRow[ColumnName] = col.column;
513 schemaRow[Ordinal] = col.ordinal;
515 // be sure to return character count for string types, byte count otherwise
516 // col.length is always byte count so for unicode types, half the length
518 // For MAX and XML datatypes, we get 0x7fffffff from the server. Do not divide this.
519 if (col.cipherMD != null) {
520 Debug.Assert(col.baseTI != null && col.baseTI.metaType != null, "col.baseTI and col.baseTI.metaType should not be null.");
521 schemaRow[Size] = (col.baseTI.metaType.IsSizeInCharacters && (col.baseTI.length != 0x7fffffff)) ? (col.baseTI.length / 2) : col.baseTI.length;
524 schemaRow[Size] = (col.metaType.IsSizeInCharacters && (col.length != 0x7fffffff)) ? (col.length / 2) : col.length;
527 schemaRow[DataType] = GetFieldTypeInternal(col);
528 schemaRow[ProviderSpecificDataType] = GetProviderSpecificFieldTypeInternal(col);
529 schemaRow[NonVersionedProviderType] = (int) (col.cipherMD != null ? col.baseTI.type : col.type); // SqlDbType enum value - does not change with TypeSystem.
530 schemaRow[DataTypeName] = GetDataTypeNameInternal(col);
532 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType) {
533 schemaRow[ProviderType] = SqlDbType.NVarChar;
536 schemaRow[Size] = TdsEnums.WHIDBEY_DATE_LENGTH;
539 Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for Time column: " + col.scale);
540 schemaRow[Size] = TdsEnums.WHIDBEY_TIME_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale];
542 case SqlDbType.DateTime2:
543 Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for DateTime2 column: " + col.scale);
544 schemaRow[Size] = TdsEnums.WHIDBEY_DATETIME2_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale];
546 case SqlDbType.DateTimeOffset:
547 Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for DateTimeOffset column: " + col.scale);
548 schemaRow[Size] = TdsEnums.WHIDBEY_DATETIMEOFFSET_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale];
552 else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsLargeUdt) {
553 if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
554 schemaRow[ProviderType] = SqlDbType.VarBinary;
557 // TypeSystem.SQLServer2000
558 schemaRow[ProviderType] = SqlDbType.Image;
561 else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
562 // TypeSystem.SQLServer2005 and above
564 // SqlDbType enum value - always the actual type for SQLServer2005.
565 schemaRow[ProviderType] = (int) (col.cipherMD != null ? col.baseTI.type : col.type);
567 if (col.type == SqlDbType.Udt) { // Additional metadata for UDTs.
568 Debug.Assert(Connection.IsYukonOrNewer, "Invalid Column type received from the server");
569 schemaRow[UdtAssemblyQualifiedName] = col.udtAssemblyQualifiedName;
571 else if (col.type == SqlDbType.Xml) { // Additional metadata for Xml.
572 Debug.Assert(Connection.IsYukonOrNewer, "Invalid DataType (Xml) for the column");
573 schemaRow[XmlSchemaCollectionDatabase] = col.xmlSchemaCollectionDatabase;
574 schemaRow[XmlSchemaCollectionOwningSchema] = col.xmlSchemaCollectionOwningSchema;
575 schemaRow[XmlSchemaCollectionName] = col.xmlSchemaCollectionName;
579 // TypeSystem.SQLServer2000
581 // SqlDbType enum value - variable for certain types when SQLServer2000.
582 schemaRow[ProviderType] = GetVersionedMetaType(col.metaType).SqlDbType;
585 if (col.cipherMD != null) {
586 Debug.Assert(col.baseTI != null, @"col.baseTI should not be null.");
587 if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.baseTI.precision) {
588 schemaRow[Precision] = col.baseTI.precision;
591 schemaRow[Precision] = col.baseTI.metaType.Precision;
594 else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.precision) {
595 schemaRow[Precision] = col.precision;
598 schemaRow[Precision] = col.metaType.Precision;
601 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType) {
602 schemaRow[Scale] = MetaType.MetaNVarChar.Scale;
604 else if (col.cipherMD != null) {
605 Debug.Assert(col.baseTI != null, @"col.baseTI should not be null.");
606 if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.baseTI.scale) {
607 schemaRow[Scale] = col.baseTI.scale;
610 schemaRow[Scale] = col.baseTI.metaType.Scale;
613 else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale) {
614 schemaRow[Scale] = col.scale;
617 schemaRow[Scale] = col.metaType.Scale;
620 schemaRow[AllowDBNull] = col.isNullable;
622 // If no ColInfo token received, do not set value, leave as null.
623 if (_browseModeInfoConsumed) {
624 schemaRow[IsAliased] = col.isDifferentName;
625 schemaRow[IsKey] = col.isKey;
626 schemaRow[IsHidden] = col.isHidden;
627 schemaRow[IsExpression] = col.isExpression;
630 schemaRow[IsIdentity] = col.isIdentity;
631 schemaRow[IsAutoIncrement] = col.isIdentity;
633 if (col.cipherMD != null) {
634 Debug.Assert(col.baseTI != null, @"col.baseTI should not be null.");
635 Debug.Assert(col.baseTI.metaType != null, @"col.baseTI.metaType should not be null.");
636 schemaRow[IsLong] = col.baseTI.metaType.IsLong;
639 schemaRow[IsLong] = col.metaType.IsLong;
642 // mark unique for timestamp columns
643 if (SqlDbType.Timestamp == col.type) {
644 schemaRow[IsUnique] = true;
645 schemaRow[IsRowVersion] = true;
648 schemaRow[IsUnique] = false;
649 schemaRow[IsRowVersion] = false;
652 schemaRow[IsReadOnly] = (0 == col.updatability);
653 schemaRow[IsColumnSet] = col.isColumnSet;
655 if (!ADP.IsEmpty(col.serverName)) {
656 schemaRow[BaseServerName] = col.serverName;
658 if (!ADP.IsEmpty(col.catalogName)) {
659 schemaRow[BaseCatalogName] = col.catalogName;
661 if (!ADP.IsEmpty(col.schemaName)) {
662 schemaRow[BaseSchemaName] = col.schemaName;
664 if (!ADP.IsEmpty(col.tableName)) {
665 schemaRow[BaseTableName] = col.tableName;
667 if (!ADP.IsEmpty(col.baseColumn)) {
668 schemaRow[BaseColumnName] = col.baseColumn;
670 else if (!ADP.IsEmpty(col.column)) {
671 schemaRow[BaseColumnName] = col.column;
674 schemaTable.Rows.Add(schemaRow);
675 schemaRow.AcceptChanges();
678 // mark all columns as readonly
679 foreach(DataColumn column in columns) {
680 column.ReadOnly = true; // MDAC 70943
686 internal void Cancel(int objectID) {
687 TdsParserStateObject stateObj = _stateObj;
688 if (null != stateObj) {
689 stateObj.Cancel(objectID);
693 // wipe any data off the wire from a partial read
694 // and reset all pointers for sequential access
695 private bool TryCleanPartialRead() {
696 AssertReaderState(requireData: true, permitAsync: true);
698 // VSTS DEVDIV2 380446: It is possible that read attempt we are cleaning after ended with partially
699 // processed header (if it falls between network packets). In this case the first thing to do is to
700 // finish reading the header, otherwise code will start treating unread header as TDS payload.
701 if (_stateObj._partialHeaderBytesRead > 0) {
702 if (!_stateObj.TryProcessHeader()) {
707 // following cases for sequential read
708 // i. user called read but didn't fetch anything
709 // iia. user called read and fetched a subset of the columns
710 // iib. user called read and fetched a subset of the column data
712 // Wipe out any Streams or TextReaders
713 if (-1 != _lastColumnWithDataChunkRead) {
714 CloseActiveSequentialStreamAndTextReader();
717 // i. user called read but didn't fetch anything
718 if (0 == _sharedState._nextColumnHeaderToRead) {
719 if (!_stateObj.Parser.TrySkipRow(_metaData, _stateObj)) {
725 // iia. if we still have bytes left from a partially read column, skip
726 if (!TryResetBlobState()) {
731 // now read the remaining values off the wire for this row
732 if (!_stateObj.Parser.TrySkipRow(_metaData, _sharedState._nextColumnHeaderToRead, _stateObj)) {
738 if (_stateObj._pendingData) {
740 if (!_stateObj.TryPeekByte(out token)) {
744 Debug.Assert(TdsParser.IsValidTdsToken(token), string.Format("Invalid token after performing CleanPartialRead: {0,-2:X2}", token));
747 _sharedState._dataReady = false;
752 private void CleanPartialReadReliable() {
753 AssertReaderState(requireData: true, permitAsync: false);
755 RuntimeHelpers.PrepareConstrainedRegions();
758 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
760 RuntimeHelpers.PrepareConstrainedRegions();
762 tdsReliabilitySection.Start();
766 bool result = TryCleanPartialRead();
767 Debug.Assert(result, "Should not pend on sync call");
768 Debug.Assert(!_sharedState._dataReady, "_dataReady should be cleared");
772 tdsReliabilitySection.Stop();
776 catch (System.OutOfMemoryException e) {
778 if (_connection != null) {
779 _connection.Abort(e);
783 catch (System.StackOverflowException e) {
785 if (_connection != null) {
786 _connection.Abort(e);
790 catch (System.Threading.ThreadAbortException e) {
792 if (_connection != null) {
793 _connection.Abort(e);
799 override public void Close() {
800 SqlStatistics statistics = null;
802 Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.Close|API> %d#", ObjectID);
804 statistics = SqlStatistics.StartTimer(Statistics);
805 TdsParserStateObject stateObj = _stateObj;
807 // Request that the current task is stopped
808 _cancelAsyncOnCloseTokenSource.Cancel();
809 var currentTask = _currentTask;
810 if ((currentTask != null) && (!currentTask.IsCompleted)) {
812 // Wait for the task to complete
813 ((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne();
815 // Ensure that we've finished reading any pending data
816 var networkPacketTaskSource = stateObj._networkPacketTaskSource;
817 if (networkPacketTaskSource != null) {
818 ((IAsyncResult)networkPacketTaskSource.Task).AsyncWaitHandle.WaitOne();
822 // If we receive any exceptions while waiting, something has gone horribly wrong and we need to doom the connection and fast-fail the reader
823 _connection.InnerConnection.DoomThisConnection();
826 if (stateObj != null) {
838 // Close down any active Streams and TextReaders (this will also wait for them to finish their async tasks)
839 // NOTE: This must be done outside of the lock on the stateObj otherwise it will deadlock with CleanupAfterAsyncInvocation
840 CloseActiveSequentialStreamAndTextReader();
842 if (stateObj != null) {
844 // protect against concurrent close and cancel
847 if (_stateObj != null ) { // reader not closed while we waited for the lock
849 // TryCloseInternal will clear out the snapshot when it is done
850 if (_snapshot != null) {
852 // The stack trace for replays will differ since they weren't captured during close
853 stateObj._permitReplayStackTraceToDiffer = true;
855 PrepareForAsyncContinuation();
858 SetTimeout(_defaultTimeoutMilliseconds);
860 // Close can be called from async methods in error cases,
861 // in which case we need to switch to syncOverAsync
862 stateObj._syncOverAsync = true;
864 if (!TryCloseInternal(true /*closeReader*/)) {
865 throw SQL.SynchronousCallMayNotPend();
868 // DO NOT USE stateObj after this point - it has been returned to the TdsParser's session pool and potentially handed out to another thread
874 SqlStatistics.StopTimer(statistics);
875 Bid.ScopeLeave(ref hscp);
879 private bool TryCloseInternal(bool closeReader) {
880 TdsParser parser = _parser;
881 TdsParserStateObject stateObj = _stateObj;
882 bool closeConnection = (IsCommandBehavior(CommandBehavior.CloseConnection));
883 bool aborting = false;
884 bool cleanDataFailed = false;
886 RuntimeHelpers.PrepareConstrainedRegions();
889 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
891 RuntimeHelpers.PrepareConstrainedRegions();
893 tdsReliabilitySection.Start();
897 if ((!_isClosed) && (parser != null) && (stateObj != null) && (stateObj._pendingData)) {
899 // It is possible for this to be called during connection close on a
900 // broken connection, so check state first.
901 if (parser.State == TdsParserState.OpenLoggedIn) {
902 // if user called read but didn't fetch any values, skip the row
903 // same applies after NextResult on ALTROW because NextResult starts rowconsumption in that case ...
905 Debug.Assert(SniContext.Snix_Read==stateObj.SniContext, String.Format((IFormatProvider)null, "The SniContext should be Snix_Read but it actually is {0}", stateObj.SniContext));
907 if (_altRowStatus == ALTROWSTATUS.AltRow) {
908 _sharedState._dataReady = true; // set _sharedState._dataReady to not confuse CleanPartialRead
910 _stateObj._internalTimeout = false;
911 if (_sharedState._dataReady) {
912 cleanDataFailed = true;
913 if (TryCleanPartialRead()) {
914 cleanDataFailed = false;
923 if (!_stateObj.TryPeekByte(out token)) {
927 Debug.Assert(TdsParser.IsValidTdsToken(token), string.Format("DataReady is false, but next token is invalid: {0,-2:X2}", token));
933 if (!parser.TryRun(RunBehavior.Clean, _command, this, null, stateObj, out ignored)) {
939 RestoreServerSettings(parser, stateObj);
944 tdsReliabilitySection.Stop();
948 catch (System.OutOfMemoryException e) {
951 if (null != _connection) {
952 _connection.Abort(e);
956 catch (System.StackOverflowException e) {
959 if (null != _connection) {
960 _connection.Abort(e);
964 catch (System.Threading.ThreadAbortException e) {
967 if (null != _connection) {
968 _connection.Abort(e);
975 _command = null; // we are done at this point, don't allow navigation to the connection
981 else if (closeReader) {
982 bool wasClosed = _isClosed;
988 if (_snapshot != null) {
989 CleanupAfterAsyncInvocationInternal(stateObj);
992 // SQLBUDT #284712 - Note the order here is extremely important:
994 // (1) First, we remove the reader from the reference collection
995 // to prevent it from being forced closed by the parser if
996 // any future work occurs.
998 // (2) Next, we ensure that cancellation can no longer happen by
999 // calling CloseSession.
1001 if (Connection != null) {
1002 Connection.RemoveWeakReference(this); // This doesn't catch everything -- the connection may be closed, but it prevents dead readers from clogging the collection
1006 RuntimeHelpers.PrepareConstrainedRegions();
1009 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1011 RuntimeHelpers.PrepareConstrainedRegions();
1013 tdsReliabilitySection.Start();
1017 // IsClosed may be true if CloseReaderFromConnection was called - in which case, the session has already been closed
1018 if ((!wasClosed) && (null != stateObj)) {
1019 if (!cleanDataFailed) {
1020 stateObj.CloseSession();
1023 if (parser != null) {
1024 parser.State = TdsParserState.Broken; // We failed while draining data, so TDS pointer can be between tokens - cannot recover
1025 parser.PutSession(stateObj);
1026 parser.Connection.BreakConnection();
1031 // DO NOT USE stateObj after this point - it has been returned to the TdsParser's session pool and potentially handed out to another thread
1035 tdsReliabilitySection.Stop();
1039 catch (System.OutOfMemoryException e) {
1040 if (null != _connection) {
1041 _connection.Abort(e);
1045 catch (System.StackOverflowException e) {
1046 if (null != _connection) {
1047 _connection.Abort(e);
1051 catch (System.Threading.ThreadAbortException e) {
1052 if (null != _connection) {
1053 _connection.Abort(e);
1058 // do not retry here
1059 bool result = TrySetMetaData(null, false);
1060 Debug.Assert(result, "Should not pend a synchronous request");
1061 _fieldNameLookup = null;
1063 // if the user calls ExecuteReader(CommandBehavior.CloseConnection)
1064 // then we close down the connection when we are done reading results
1065 if (closeConnection) {
1066 if (Connection != null) {
1070 if (_command != null) {
1071 // cache recordsaffected to be returnable after DataReader.Close();
1072 _recordsAffected = _command.InternalRecordsAffected;
1075 _command = null; // we are done at this point, don't allow navigation to the connection
1082 virtual internal void CloseReaderFromConnection() {
1083 var parser = _parser;
1084 Debug.Assert(parser == null || parser.State != TdsParserState.OpenNotLoggedIn, "Reader on a connection that is not logged in");
1085 if ((parser != null) && (parser.State == TdsParserState.OpenLoggedIn)) {
1086 // Connection is ok - proper cleanup
1087 // NOTE: This is NOT thread-safe
1091 // Connection is broken - quick cleanup
1092 // NOTE: This MUST be thread-safe as a broken connection can happen at any time
1094 var stateObj = _stateObj;
1096 // Request that the current task is stopped
1097 _cancelAsyncOnCloseTokenSource.Cancel();
1098 if (stateObj != null) {
1099 var networkPacketTaskSource = stateObj._networkPacketTaskSource;
1100 if (networkPacketTaskSource != null) {
1101 // If the connection is closed or broken, this will never complete
1102 networkPacketTaskSource.TrySetException(ADP.ClosedConnectionError());
1104 if (_snapshot != null) {
1105 // CleanWire will do cleanup - so we don't really care about the snapshot
1106 CleanupAfterAsyncInvocationInternal(stateObj, resetNetworkPacketTaskSource: false);
1108 // Switch to sync to prepare for cleanwire
1109 stateObj._syncOverAsync = true;
1110 // Remove owner (this will allow the stateObj to be disposed after the connection is closed)
1111 stateObj.RemoveOwner();
1116 private bool TryConsumeMetaData() {
1117 // warning: Don't check the MetaData property within this function
1118 // warning: as it will be a reentrant call
1119 while (_parser != null && _stateObj != null && _stateObj._pendingData && !_metaDataConsumed) {
1120 if (_parser.State == TdsParserState.Broken || _parser.State == TdsParserState.Closed) {
1121 // Happened for DEVDIV2:180509 (SqlDataReader.ConsumeMetaData Hangs In 100% CPU Loop Forever When TdsParser._state == TdsParserState.Broken)
1122 // during request for DTC address.
1123 // NOTE: We doom connection for TdsParserState.Closed since it indicates that it is in some abnormal and unstable state, probably as a result of
1124 // closing from another thread. In general, TdsParserState.Closed does not necessitate dooming the connection.
1125 if (_parser.Connection != null)
1126 _parser.Connection.DoomThisConnection();
1127 throw SQL.ConnectionDoomed();
1130 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
1133 Debug.Assert(!ignored, "Parser read a row token while trying to read metadata");
1136 // we hide hidden columns from the user so build an internal map
1137 // that compacts all hidden columns from the array
1138 if (null != _metaData) {
1140 if (_snapshot != null && object.ReferenceEquals(_snapshot._metadata, _metaData)) {
1141 _metaData = (_SqlMetaDataSet)_metaData.Clone();
1144 _metaData.visibleColumns = 0;
1146 Debug.Assert(null == _metaData.indexMap, "non-null metaData indexmap");
1147 int[] indexMap = new int[_metaData.Length];
1148 for (int i = 0; i < indexMap.Length; ++i) {
1149 indexMap[i] = _metaData.visibleColumns;
1151 if (!(_metaData[i].isHidden)) {
1152 _metaData.visibleColumns++;
1155 _metaData.indexMap = indexMap;
1161 override public string GetDataTypeName(int i) {
1162 SqlStatistics statistics = null;
1164 statistics = SqlStatistics.StartTimer(Statistics);
1165 CheckMetaDataIsReady(columnIndex: i);
1167 return GetDataTypeNameInternal(_metaData[i]);
1170 SqlStatistics.StopTimer(statistics);
1174 private string GetDataTypeNameInternal(_SqlMetaData metaData) {
1175 string dataTypeName = null;
1177 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
1178 dataTypeName = MetaType.MetaNVarChar.TypeName;
1180 else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
1181 if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
1182 dataTypeName = MetaType.MetaMaxVarBinary.TypeName;
1185 // TypeSystem.SQLServer2000
1186 dataTypeName = MetaType.MetaImage.TypeName;
1189 else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
1190 // TypeSystem.SQLServer2005 and above
1192 if (metaData.type == SqlDbType.Udt) {
1193 Debug.Assert(Connection.IsYukonOrNewer, "Invalid Column type received from the server");
1194 dataTypeName = metaData.udtDatabaseName + "." + metaData.udtSchemaName + "." + metaData.udtTypeName;
1196 else { // For all other types, including Xml - use data in MetaType.
1197 if (metaData.cipherMD != null) {
1198 Debug.Assert(metaData.baseTI != null && metaData.baseTI.metaType != null, "metaData.baseTI and metaData.baseTI.metaType should not be null.");
1199 dataTypeName = metaData.baseTI.metaType.TypeName;
1202 dataTypeName = metaData.metaType.TypeName;
1207 // TypeSystem.SQLServer2000
1209 dataTypeName = GetVersionedMetaType(metaData.metaType).TypeName;
1212 return dataTypeName;
1215 virtual internal SqlBuffer.StorageType GetVariantInternalStorageType(int i) {
1216 Debug.Assert(null != _data, "Attempting to get variant internal storage type");
1217 Debug.Assert(i < _data.Length, "Reading beyond data length?");
1219 return _data[i].VariantInternalStorageType;
1222 override public IEnumerator GetEnumerator() {
1223 return new DbEnumerator(this, IsCommandBehavior(CommandBehavior.CloseConnection));
1226 override public Type GetFieldType(int i) {
1227 SqlStatistics statistics = null;
1229 statistics = SqlStatistics.StartTimer(Statistics);
1230 CheckMetaDataIsReady(columnIndex: i);
1232 return GetFieldTypeInternal(_metaData[i]);
1235 SqlStatistics.StopTimer(statistics);
1239 private Type GetFieldTypeInternal(_SqlMetaData metaData) {
1240 Type fieldType = null;
1242 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
1243 // Return katmai types as string
1244 fieldType = MetaType.MetaNVarChar.ClassType;
1246 else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
1247 if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
1248 fieldType = MetaType.MetaMaxVarBinary.ClassType;
1251 // TypeSystem.SQLServer2000
1252 fieldType = MetaType.MetaImage.ClassType;
1255 else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
1256 // TypeSystem.SQLServer2005 and above
1258 if (metaData.type == SqlDbType.Udt) {
1259 Debug.Assert(Connection.IsYukonOrNewer, "Invalid Column type received from the server");
1260 Connection.CheckGetExtendedUDTInfo(metaData, false);
1261 fieldType = metaData.udtType;
1263 else { // For all other types, including Xml - use data in MetaType.
1264 if (metaData.cipherMD != null) {
1265 Debug.Assert(metaData.baseTI != null && metaData.baseTI.metaType != null, "metaData.baseTI and metaData.baseTI.metaType should not be null.");
1266 fieldType = metaData.baseTI.metaType.ClassType;
1269 fieldType = metaData.metaType.ClassType; // Com+ type.
1274 // TypeSystem.SQLServer2000
1276 fieldType = GetVersionedMetaType(metaData.metaType).ClassType; // Com+ type.
1282 virtual internal int GetLocaleId(int i) {
1283 _SqlMetaData sqlMetaData = MetaData[i];
1286 if (sqlMetaData.collation != null) {
1287 lcid = sqlMetaData.collation.LCID;
1295 override public string GetName(int i) {
1296 CheckMetaDataIsReady(columnIndex: i);
1298 Debug.Assert(null != _metaData[i].column, "MDAC 66681");
1299 return _metaData[i].column;
1302 override public Type GetProviderSpecificFieldType(int i) {
1303 SqlStatistics statistics = null;
1305 statistics = SqlStatistics.StartTimer(Statistics);
1306 CheckMetaDataIsReady(columnIndex: i);
1308 return GetProviderSpecificFieldTypeInternal(_metaData[i]);
1311 SqlStatistics.StopTimer(statistics);
1315 private Type GetProviderSpecificFieldTypeInternal(_SqlMetaData metaData) {
1316 Type providerSpecificFieldType = null;
1318 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
1319 providerSpecificFieldType = MetaType.MetaNVarChar.SqlType;
1321 else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
1322 if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
1323 providerSpecificFieldType = MetaType.MetaMaxVarBinary.SqlType;
1326 // TypeSystem.SQLServer2000
1327 providerSpecificFieldType = MetaType.MetaImage.SqlType;
1330 else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
1331 // TypeSystem.SQLServer2005 and above
1333 if (metaData.type == SqlDbType.Udt) {
1334 Debug.Assert(Connection.IsYukonOrNewer, "Invalid Column type received from the server");
1335 Connection.CheckGetExtendedUDTInfo(metaData, false);
1336 providerSpecificFieldType = metaData.udtType;
1339 // For all other types, including Xml - use data in MetaType.
1340 if (metaData.cipherMD != null) {
1341 Debug.Assert(metaData.baseTI != null && metaData.baseTI.metaType != null,
1342 "metaData.baseTI and metaData.baseTI.metaType should not be null.");
1343 providerSpecificFieldType = metaData.baseTI.metaType.SqlType; // SqlType type.
1346 providerSpecificFieldType = metaData.metaType.SqlType; // SqlType type.
1351 // TypeSystem.SQLServer2000
1353 providerSpecificFieldType = GetVersionedMetaType(metaData.metaType).SqlType; // SqlType type.
1356 return providerSpecificFieldType;
1359 // named field access
1360 override public int GetOrdinal(string name) {
1361 SqlStatistics statistics = null;
1363 statistics = SqlStatistics.StartTimer(Statistics);
1364 if (null == _fieldNameLookup) {
1365 CheckMetaDataIsReady();
1366 _fieldNameLookup = new FieldNameLookup(this, _defaultLCID);
1368 return _fieldNameLookup.GetOrdinal(name); // MDAC 71470
1371 SqlStatistics.StopTimer(statistics);
1375 override public object GetProviderSpecificValue(int i) {
1376 return GetSqlValue(i);
1379 override public int GetProviderSpecificValues(object[] values) {
1380 return GetSqlValues(values);
1383 override public DataTable GetSchemaTable() {
1384 SqlStatistics statistics = null;
1386 Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.GetSchemaTable|API> %d#", ObjectID);
1388 statistics = SqlStatistics.StartTimer(Statistics);
1389 if (null == _metaData || null == _metaData.schemaTable) {
1390 if (null != this.MetaData) {
1392 _metaData.schemaTable = BuildSchemaTable();
1393 Debug.Assert(null != _metaData.schemaTable, "No schema information yet!");
1397 if (null != _metaData) {
1398 return _metaData.schemaTable;
1403 SqlStatistics.StopTimer(statistics);
1404 Bid.ScopeLeave(ref hscp);
1408 override public bool GetBoolean(int i) {
1410 return _data[i].Boolean;
1413 virtual public XmlReader GetXmlReader(int i) {
1414 // NOTE: sql_variant can not contain a XML data type: http://msdn.microsoft.com/en-us/library/ms173829.aspx
1415 // If this ever changes, the following code should be changed to be like GetStream\GetTextReader
1416 CheckDataIsReady(columnIndex: i, methodName: "GetXmlReader");
1418 MetaType mt = _metaData[i].metaType;
1420 // XmlReader only allowed on XML types
1421 if (mt.SqlDbType != SqlDbType.Xml) {
1422 throw SQL.XmlReaderNotSupportOnColumnType(_metaData[i].column);
1425 if (IsCommandBehavior(CommandBehavior.SequentialAccess)) {
1426 // Wrap the sequential stream in an XmlReader
1427 _currentStream = new SqlSequentialStream(this, i);
1428 _lastColumnWithDataChunkRead = i;
1429 return SqlXml.CreateSqlXmlReader(_currentStream, closeInput: true);
1432 // Need to call ReadColumn, since we want to access the internal data structures (i.e. SqlBinary) rather than calling anther Get*() method
1435 if (_data[i].IsNull) {
1437 return SqlXml.CreateSqlXmlReader(new MemoryStream(new byte[0], writable: false), closeInput: true);
1440 // Grab already read data
1441 return _data[i].SqlXml.CreateReader();
1446 override public Stream GetStream(int i) {
1447 CheckDataIsReady(columnIndex: i, methodName: "GetStream");
1449 // Streaming is not supported on encrypted columns.
1450 if (_metaData[i] != null && _metaData[i].cipherMD != null) {
1451 throw SQL.StreamNotSupportOnEncryptedColumn(_metaData[i].column);
1454 // Stream is only for Binary, Image, VarBinary, Udt and Xml types
1455 // NOTE: IsBinType also includes Timestamp for some reason...
1456 MetaType mt = _metaData[i].metaType;
1457 if (((!mt.IsBinType) || (mt.SqlDbType == SqlDbType.Timestamp)) && (mt.SqlDbType != SqlDbType.Variant)) {
1458 throw SQL.StreamNotSupportOnColumnType(_metaData[i].column);
1461 // For non-variant types with sequential access, we support proper streaming
1462 if ((mt.SqlDbType != SqlDbType.Variant) && (IsCommandBehavior(CommandBehavior.SequentialAccess))) {
1463 _currentStream = new SqlSequentialStream(this, i);
1464 _lastColumnWithDataChunkRead = i;
1465 return _currentStream;
1468 // Need to call ReadColumn, since we want to access the internal data structures (i.e. SqlBinary) rather than calling anther Get*() method
1472 if (_data[i].IsNull) {
1477 // Grab already read data
1478 data = _data[i].SqlBinary.Value;
1481 // If non-sequential then we just have a read-only MemoryStream
1482 return new MemoryStream(data, writable: false);
1486 override public byte GetByte(int i) {
1488 return _data[i].Byte;
1491 override public long GetBytes(int i, long dataIndex, byte[] buffer, int bufferIndex, int length) {
1492 SqlStatistics statistics = null;
1495 CheckDataIsReady(columnIndex: i, allowPartiallyReadColumn: true, methodName: "GetBytes");
1497 // don't allow get bytes on non-long or non-binary columns
1498 MetaType mt = _metaData[i].metaType;
1499 if (!(mt.IsLong || mt.IsBinType) || (SqlDbType.Xml == mt.SqlDbType)) {
1500 throw SQL.NonBlobColumn(_metaData[i].column);
1504 statistics = SqlStatistics.StartTimer(Statistics);
1505 SetTimeout(_defaultTimeoutMilliseconds);
1506 cbBytes = GetBytesInternal(i, dataIndex, buffer, bufferIndex, length);
1507 _lastColumnWithDataChunkRead = i;
1510 SqlStatistics.StopTimer(statistics);
1515 // Used (indirectly) by SqlCommand.CompleteXmlReader
1516 virtual internal long GetBytesInternal(int i, long dataIndex, byte[] buffer, int bufferIndex, int length) {
1517 if (_currentTask != null) {
1518 throw ADP.AsyncOperationPending();
1522 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
1523 bool result = TryGetBytesInternal(i, dataIndex, buffer, bufferIndex, length, out value);
1524 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
1528 private bool TryGetBytesInternal(int i, long dataIndex, byte[] buffer, int bufferIndex, int length, out long remaining) {
1531 RuntimeHelpers.PrepareConstrainedRegions();
1534 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1536 RuntimeHelpers.PrepareConstrainedRegions();
1538 tdsReliabilitySection.Start();
1543 AssertReaderState(requireData: true, permitAsync: true, columnIndex: i, enforceSequentialAccess: true);
1545 // sequential reading
1546 if (IsCommandBehavior(CommandBehavior.SequentialAccess)) {
1547 Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has an active Stream or TextReader");
1549 if (_metaData[i] != null && _metaData[i].cipherMD != null) {
1550 throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column);
1553 if (_sharedState._nextColumnHeaderToRead <= i) {
1554 if (!TryReadColumnHeader(i)) {
1559 // If data is null, ReadColumnHeader sets the data.IsNull bit.
1560 if (_data[i] != null && _data[i].IsNull) {
1561 throw new SqlNullValueException();
1564 // If there are an unknown (-1) number of bytes left for a PLP, read its size
1565 if ((-1 == _sharedState._columnDataBytesRemaining) && (_metaData[i].metaType.IsPlp)) {
1567 if (!_parser.TryPlpBytesLeft(_stateObj, out left)) {
1570 _sharedState._columnDataBytesRemaining = (long)left;
1573 if (0 == _sharedState._columnDataBytesRemaining) {
1574 return true; // We've read this column to the end
1577 // if no buffer is passed in, return the number total of bytes, or -1
1578 if (null == buffer) {
1579 if (_metaData[i].metaType.IsPlp) {
1580 remaining = (long) _parser.PlpBytesTotalLength(_stateObj);
1583 remaining = _sharedState._columnDataBytesRemaining;
1588 throw ADP.NegativeParameter("dataIndex");
1590 if (dataIndex < _columnDataBytesRead) {
1591 throw ADP.NonSeqByteAccess(dataIndex, _columnDataBytesRead, ADP.GetBytes);
1594 // if the dataIndex is not equal to bytes read, then we have to skip bytes
1595 long cb = dataIndex - _columnDataBytesRead;
1597 // if dataIndex is outside of the data range, return 0
1598 if ((cb > _sharedState._columnDataBytesRemaining) && !_metaData[i].metaType.IsPlp) {
1602 // if bad buffer index, throw
1603 if (bufferIndex < 0 || bufferIndex >= buffer.Length)
1604 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
1606 // if there is not enough room in the buffer for data
1607 if (length + bufferIndex > buffer.Length)
1608 throw ADP.InvalidBufferSizeOrIndex(length, bufferIndex);
1611 throw ADP.InvalidDataLength(length);
1615 if (_metaData[i].metaType.IsPlp) {
1617 if (!_parser.TrySkipPlpValue((ulong) cb, _stateObj, out skipped)) {
1620 _columnDataBytesRead += (long) skipped;
1623 if (!_stateObj.TrySkipLongBytes(cb)) {
1626 _columnDataBytesRead += cb;
1627 _sharedState._columnDataBytesRemaining -= cb;
1632 bool result = TryGetBytesInternalSequential(i, buffer, bufferIndex, length, out bytesRead);
1633 remaining = (int)bytesRead;
1637 // random access now!
1638 // note that since we are caching in an array, and arrays aren't 64 bit ready yet,
1639 // we need can cast to int if the dataIndex is in range
1641 throw ADP.NegativeParameter("dataIndex");
1643 if (dataIndex > Int32.MaxValue) {
1644 throw ADP.InvalidSourceBufferIndex(cbytes, dataIndex, "dataIndex");
1647 int ndataIndex = (int)dataIndex;
1650 // WebData 99342 - in the non-sequential case, we need to support
1651 // the use of GetBytes on string data columns, but
1652 // GetSqlBinary isn't supposed to. What we end up
1653 // doing isn't exactly pretty, but it does work.
1654 if (_metaData[i].metaType.IsBinType) {
1655 data = GetSqlBinary(i).Value;
1658 Debug.Assert(_metaData[i].metaType.IsLong, "non long type?");
1659 Debug.Assert(_metaData[i].metaType.IsCharType, "non-char type?");
1661 SqlString temp = GetSqlString(i);
1662 if (_metaData[i].metaType.IsNCharType) {
1663 data = temp.GetUnicodeBytes();
1666 data = temp.GetNonUnicodeBytes();
1670 cbytes = data.Length;
1672 // if no buffer is passed in, return the number of characters we have
1673 if (null == buffer) {
1678 // if dataIndex is outside of data range, return 0
1679 if (ndataIndex < 0 || ndataIndex >= cbytes) {
1683 if (ndataIndex < cbytes) {
1684 // help the user out in the case where there's less data than requested
1685 if ((ndataIndex + length) > cbytes)
1686 cbytes = cbytes - ndataIndex;
1691 Array.Copy(data, ndataIndex, buffer, bufferIndex, cbytes);
1693 catch (Exception e) {
1695 if (!ADP.IsCatchableExceptionType(e)) {
1698 cbytes = data.Length;
1701 throw ADP.InvalidDataLength(length);
1703 // if bad buffer index, throw
1704 if (bufferIndex < 0 || bufferIndex >= buffer.Length)
1705 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
1707 // if there is not enough room in the buffer for data
1708 if (cbytes + bufferIndex > buffer.Length)
1709 throw ADP.InvalidBufferSizeOrIndex(cbytes, bufferIndex);
1719 tdsReliabilitySection.Stop();
1723 catch (System.OutOfMemoryException e) {
1725 if (null != _connection) {
1726 _connection.Abort(e);
1730 catch (System.StackOverflowException e) {
1732 if (null != _connection) {
1733 _connection.Abort(e);
1737 catch (System.Threading.ThreadAbortException e) {
1739 if (null != _connection) {
1740 _connection.Abort(e);
1746 internal int GetBytesInternalSequential(int i, byte[] buffer, int index, int length, long? timeoutMilliseconds = null) {
1747 if (_currentTask != null) {
1748 throw ADP.AsyncOperationPending();
1752 SqlStatistics statistics = null;
1753 Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
1755 statistics = SqlStatistics.StartTimer(Statistics);
1756 SetTimeout(timeoutMilliseconds ?? _defaultTimeoutMilliseconds);
1758 bool result = TryReadColumnHeader(i);
1759 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
1761 result = TryGetBytesInternalSequential(i, buffer, index, length, out value);
1762 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
1765 SqlStatistics.StopTimer(statistics);
1771 // This is meant to be called from other internal methods once we are at the column to read
1772 // NOTE: This method must be retriable WITHOUT replaying a snapshot
1773 // Every time you call this method increment the index and decrease length by the value of bytesRead
1774 internal bool TryGetBytesInternalSequential(int i, byte[] buffer, int index, int length, out int bytesRead) {
1775 AssertReaderState(requireData: true, permitAsync: true, columnIndex: i, enforceSequentialAccess: true);
1776 Debug.Assert(_sharedState._nextColumnHeaderToRead == i + 1 && _sharedState._nextColumnDataToRead == i, "Non sequential access");
1777 Debug.Assert(buffer != null, "Null buffer");
1778 Debug.Assert(index >= 0, "Invalid index");
1779 Debug.Assert(length >= 0, "Invalid length");
1780 Debug.Assert(index + length <= buffer.Length, "Buffer too small");
1784 RuntimeHelpers.PrepareConstrainedRegions();
1788 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1790 RuntimeHelpers.PrepareConstrainedRegions();
1792 tdsReliabilitySection.Start();
1794 if ((_sharedState._columnDataBytesRemaining == 0) || (length == 0)) {
1795 // No data left or nothing requested, return 0
1800 // if plp columns, do partial reads. Don't read the entire value in one shot.
1801 if (_metaData[i].metaType.IsPlp) {
1803 bool result = _stateObj.TryReadPlpBytes(ref buffer, index, length, out bytesRead);
1804 _columnDataBytesRead += bytesRead;
1809 // Query for number of bytes left
1811 if (!_parser.TryPlpBytesLeft(_stateObj, out left)) {
1812 _sharedState._columnDataBytesRemaining = -1;
1815 _sharedState._columnDataBytesRemaining = (long)left;
1819 // Read data (not exceeding the total amount of data available)
1820 int bytesToRead = (int)Math.Min((long)length, _sharedState._columnDataBytesRemaining);
1821 bool result = _stateObj.TryReadByteArray(buffer, index, bytesToRead, out bytesRead);
1822 _columnDataBytesRead += bytesRead;
1823 _sharedState._columnDataBytesRemaining -= bytesRead;
1830 tdsReliabilitySection.Stop();
1834 catch (System.OutOfMemoryException e) {
1836 if (null != _connection) {
1837 _connection.Abort(e);
1841 catch (System.StackOverflowException e) {
1843 if (null != _connection) {
1844 _connection.Abort(e);
1848 catch (System.Threading.ThreadAbortException e) {
1850 if (null != _connection) {
1851 _connection.Abort(e);
1857 override public TextReader GetTextReader(int i) {
1858 CheckDataIsReady(columnIndex: i, methodName: "GetTextReader");
1860 // Xml type is not supported
1863 if (_metaData[i].cipherMD != null) {
1864 Debug.Assert(_metaData[i].baseTI != null, "_metaData[i].baseTI should not be null.");
1865 mt = _metaData[i].baseTI.metaType;
1868 mt = _metaData[i].metaType;
1871 Debug.Assert(mt != null, @"mt should not be null.");
1873 if (((!mt.IsCharType) && (mt.SqlDbType != SqlDbType.Variant)) || (mt.SqlDbType == SqlDbType.Xml)) {
1874 throw SQL.TextReaderNotSupportOnColumnType(_metaData[i].column);
1877 // For non-variant types with sequential access, we support proper streaming
1878 if ((mt.SqlDbType != SqlDbType.Variant) && (IsCommandBehavior(CommandBehavior.SequentialAccess))) {
1879 if (_metaData[i].cipherMD != null) {
1880 throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column);
1883 System.Text.Encoding encoding;
1886 // NChar types always use unicode
1887 encoding = SqlUnicodeEncoding.SqlUnicodeEncodingInstance;
1891 encoding = _metaData[i].encoding;
1894 _currentTextReader = new SqlSequentialTextReader(this, i, encoding);
1895 _lastColumnWithDataChunkRead = i;
1896 return _currentTextReader;
1899 // Need to call ReadColumn, since we want to access the internal data structures (i.e. SqlString) rather than calling anther Get*() method
1903 if (_data[i].IsNull) {
1905 data = string.Empty;
1908 // Grab already read data
1909 data = _data[i].SqlString.Value;
1912 // We've already read the data, so just wrap it in a StringReader
1913 return new StringReader(data);
1917 [ EditorBrowsableAttribute(EditorBrowsableState.Never) ] // MDAC 69508
1918 override public char GetChar(int i) {
1919 throw ADP.NotSupported();
1922 override public long GetChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) {
1923 SqlStatistics statistics = null;
1925 CheckMetaDataIsReady(columnIndex: i);
1927 if (_currentTask != null) {
1928 throw ADP.AsyncOperationPending();
1932 if (_metaData[i].cipherMD != null) {
1933 Debug.Assert(_metaData[i].baseTI != null, @"_metaData[i].baseTI should not be null.");
1934 mt = _metaData[i].baseTI.metaType;
1937 mt = _metaData[i].metaType;
1940 Debug.Assert(mt != null, "mt should not be null.");
1942 SqlDbType sqlDbType;
1943 if (_metaData[i].cipherMD != null) {
1944 Debug.Assert(_metaData[i].baseTI != null, @"_metaData[i].baseTI should not be null.");
1945 sqlDbType = _metaData[i].baseTI.type;
1948 sqlDbType = _metaData[i].type;
1952 statistics = SqlStatistics.StartTimer(Statistics);
1953 SetTimeout(_defaultTimeoutMilliseconds);
1955 (IsCommandBehavior(CommandBehavior.SequentialAccess)) ) {
1957 throw ADP.InvalidDataLength(length);
1960 if (_metaData[i].cipherMD != null) {
1961 throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column);
1964 // if bad buffer index, throw
1965 if ((bufferIndex < 0) || (buffer != null && bufferIndex >= buffer.Length)) {
1966 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
1969 // if there is not enough room in the buffer for data
1970 if (buffer != null && (length + bufferIndex > buffer.Length)) {
1971 throw ADP.InvalidBufferSizeOrIndex(length, bufferIndex);
1974 if ( sqlDbType == SqlDbType.Xml ) {
1976 CheckDataIsReady(columnIndex: i, allowPartiallyReadColumn: true, methodName: "GetChars");
1978 catch (Exception ex) {
1981 if (ADP.IsCatchableExceptionType(ex)) {
1982 throw new TargetInvocationException(ex);
1988 charsRead = GetStreamingXmlChars(i, dataIndex, buffer, bufferIndex, length);
1991 CheckDataIsReady(columnIndex: i, allowPartiallyReadColumn: true, methodName: "GetChars");
1992 charsRead = GetCharsFromPlpData(i, dataIndex, buffer, bufferIndex, length);
1994 _lastColumnWithDataChunkRead = i;
1998 // Did we start reading this value yet?
1999 if ((_sharedState._nextColumnDataToRead == (i+1)) && (_sharedState._nextColumnHeaderToRead == (i+1)) && (_columnDataChars != null) && (IsCommandBehavior(CommandBehavior.SequentialAccess)) && (dataIndex < _columnDataCharsRead)) {
2000 // Don't allow re-read of same chars in sequential access mode
2001 throw ADP.NonSeqByteAccess(dataIndex, _columnDataCharsRead, ADP.GetChars);
2004 if (_columnDataCharsIndex != i) {
2005 // if the object doesn't contain a char[] then the user will get an exception
2006 string s = GetSqlString(i).Value;
2008 _columnDataChars = s.ToCharArray();
2009 _columnDataCharsRead = 0;
2010 _columnDataCharsIndex = i;
2013 int cchars = _columnDataChars.Length;
2015 // note that since we are caching in an array, and arrays aren't 64 bit ready yet,
2016 // we need can cast to int if the dataIndex is in range
2017 if (dataIndex > Int32.MaxValue) {
2018 throw ADP.InvalidSourceBufferIndex(cchars, dataIndex, "dataIndex");
2020 int ndataIndex = (int)dataIndex;
2022 // if no buffer is passed in, return the number of characters we have
2026 // if dataIndex outside of data range, return 0
2027 if (ndataIndex < 0 || ndataIndex >= cchars)
2031 if (ndataIndex < cchars) {
2032 // help the user out in the case where there's less data than requested
2033 if ((ndataIndex + length) > cchars)
2034 cchars = cchars - ndataIndex;
2039 Array.Copy(_columnDataChars, ndataIndex, buffer, bufferIndex, cchars);
2040 _columnDataCharsRead += cchars;
2042 catch (Exception e) {
2044 if (!ADP.IsCatchableExceptionType(e)) {
2047 cchars = _columnDataChars.Length;
2050 throw ADP.InvalidDataLength(length);
2052 // if bad buffer index, throw
2053 if (bufferIndex < 0 || bufferIndex >= buffer.Length)
2054 throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
2056 // if there is not enough room in the buffer for data
2057 if (cchars + bufferIndex > buffer.Length)
2058 throw ADP.InvalidBufferSizeOrIndex(cchars, bufferIndex);
2066 SqlStatistics.StopTimer(statistics);
2070 private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int bufferIndex, int length) {
2071 RuntimeHelpers.PrepareConstrainedRegions();
2074 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
2076 RuntimeHelpers.PrepareConstrainedRegions();
2078 tdsReliabilitySection.Start();
2084 AssertReaderState(requireData: true, permitAsync: false, columnIndex: i, enforceSequentialAccess: true);
2085 Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has active Stream or TextReader");
2086 // don't allow get bytes on non-long or non-binary columns
2087 Debug.Assert(_metaData[i].metaType.IsPlp, "GetCharsFromPlpData called on a non-plp column!");
2088 // Must be sequential reading
2089 Debug.Assert (IsCommandBehavior(CommandBehavior.SequentialAccess), "GetCharsFromPlpData called for non-Sequential access");
2091 if (!_metaData[i].metaType.IsCharType) {
2092 throw SQL.NonCharColumn(_metaData[i].column);
2095 if (_sharedState._nextColumnHeaderToRead <= i) {
2096 ReadColumnHeader(i);
2099 // If data is null, ReadColumnHeader sets the data.IsNull bit.
2100 if (_data[i] != null && _data[i].IsNull) {
2101 throw new SqlNullValueException();
2104 if (dataIndex < _columnDataCharsRead) {
2105 // Don't allow re-read of same chars in sequential access mode
2106 throw ADP.NonSeqByteAccess(dataIndex, _columnDataCharsRead, ADP.GetChars);
2109 // If we start reading the new column, either dataIndex is 0 or
2110 // _columnDataCharsRead is 0 and dataIndex > _columnDataCharsRead is true below.
2111 // In both cases we will clean decoder
2113 _stateObj._plpdecoder = null;
2115 bool isUnicode = _metaData[i].metaType.IsNCharType;
2117 // If there are an unknown (-1) number of bytes left for a PLP, read its size
2118 if (-1 == _sharedState._columnDataBytesRemaining) {
2119 _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj);
2122 if (0 == _sharedState._columnDataBytesRemaining) {
2123 _stateObj._plpdecoder = null;
2124 return 0; // We've read this column to the end
2127 // if no buffer is passed in, return the total number of characters or -1
2129 if (null == buffer) {
2130 cch = (long) _parser.PlpBytesTotalLength(_stateObj);
2131 return (isUnicode && (cch > 0)) ? cch >> 1 : cch;
2133 if (dataIndex > _columnDataCharsRead) {
2136 // Clean decoder state: we do not reset it, but destroy to ensure
2137 // that we do not start decoding the column with decoder from the old one
2138 _stateObj._plpdecoder = null;
2142 cch = dataIndex - _columnDataCharsRead;
2143 cch = isUnicode ? (cch << 1 ) : cch;
2144 cch = (long) _parser.SkipPlpValue((ulong)(cch), _stateObj);
2145 _columnDataBytesRead += cch;
2146 _columnDataCharsRead += (isUnicode && (cch > 0)) ? cch >> 1 : cch;
2151 cch = (long) _parser.ReadPlpUnicodeChars(ref buffer, bufferIndex, length, _stateObj);
2152 _columnDataBytesRead += (cch << 1);
2155 cch = (long) _parser.ReadPlpAnsiChars(ref buffer, bufferIndex, length, _metaData[i], _stateObj);
2156 _columnDataBytesRead += cch << 1;
2158 _columnDataCharsRead += cch;
2159 _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj);
2164 tdsReliabilitySection.Stop();
2168 catch (System.OutOfMemoryException e) {
2170 if (null != _connection) {
2171 _connection.Abort(e);
2175 catch (System.StackOverflowException e) {
2177 if (null != _connection) {
2178 _connection.Abort(e);
2182 catch (System.Threading.ThreadAbortException e) {
2184 if (null != _connection) {
2185 _connection.Abort(e);
2191 internal long GetStreamingXmlChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) {
2192 SqlStreamingXml localSXml = null;
2193 if ((_streamingXml != null) && ( _streamingXml.ColumnOrdinal != i)) {
2194 _streamingXml.Close();
2195 _streamingXml = null;
2197 if (_streamingXml == null) {
2198 localSXml = new SqlStreamingXml(i, this);
2201 localSXml = _streamingXml;
2203 long cnt = localSXml.GetChars(dataIndex, buffer, bufferIndex, length);
2204 if (_streamingXml == null) {
2205 // Data is read through GetBytesInternal which may dispose _streamingXml if it has to advance the column ordinal.
2206 // Therefore save the new SqlStreamingXml class after the read succeeds.
2207 _streamingXml = localSXml;
2212 [ EditorBrowsableAttribute(EditorBrowsableState.Never) ] // MDAC 69508
2213 IDataReader IDataRecord.GetData(int i) {
2214 throw ADP.NotSupported();
2217 override public DateTime GetDateTime(int i) {
2220 DateTime dt = _data[i].DateTime;
2221 // This accessor can be called for regular DateTime column. In this case we should not throw
2222 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) {
2223 // TypeSystem.SQLServer2005 or less
2225 // If the above succeeds, then we received a valid DateTime instance, now we need to force
2226 // an InvalidCastException since DateTime is not exposed with the version knob in this setting.
2227 // To do so, we simply force the exception by casting the string representation of the value
2229 object temp = (object) _data[i].String;
2230 dt = (DateTime) temp;
2236 override public Decimal GetDecimal(int i) {
2238 return _data[i].Decimal;
2241 override public double GetDouble(int i) {
2243 return _data[i].Double;
2246 override public float GetFloat(int i) {
2248 return _data[i].Single;
2251 override public Guid GetGuid(int i) {
2253 return _data[i].SqlGuid.Value;
2256 override public Int16 GetInt16(int i) {
2258 return _data[i].Int16;
2261 override public Int32 GetInt32(int i) {
2263 return _data[i].Int32;
2266 override public Int64 GetInt64(int i) {
2268 return _data[i].Int64;
2271 virtual public SqlBoolean GetSqlBoolean(int i) {
2273 return _data[i].SqlBoolean;
2276 virtual public SqlBinary GetSqlBinary(int i) {
2277 ReadColumn(i, setTimeout: true, allowPartiallyReadColumn: true);
2278 return _data[i].SqlBinary;
2281 virtual public SqlByte GetSqlByte(int i) {
2283 return _data[i].SqlByte;
2286 virtual public SqlBytes GetSqlBytes(int i) {
2288 SqlBinary data = _data[i].SqlBinary;
2289 return new SqlBytes(data);
2292 virtual public SqlChars GetSqlChars(int i) {
2295 // Convert Katmai types to string
2296 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType)
2298 data = _data[i].KatmaiDateTimeSqlString;
2300 data = _data[i].SqlString;
2302 return new SqlChars(data);
2305 virtual public SqlDateTime GetSqlDateTime(int i) {
2307 return _data[i].SqlDateTime;
2310 virtual public SqlDecimal GetSqlDecimal(int i) {
2312 return _data[i].SqlDecimal;
2315 virtual public SqlGuid GetSqlGuid(int i) {
2317 return _data[i].SqlGuid;
2320 virtual public SqlDouble GetSqlDouble(int i) {
2322 return _data[i].SqlDouble;
2325 virtual public SqlInt16 GetSqlInt16(int i) {
2327 return _data[i].SqlInt16;
2330 virtual public SqlInt32 GetSqlInt32(int i) {
2332 return _data[i].SqlInt32;
2335 virtual public SqlInt64 GetSqlInt64(int i) {
2337 return _data[i].SqlInt64;
2340 virtual public SqlMoney GetSqlMoney(int i) {
2342 return _data[i].SqlMoney;
2345 virtual public SqlSingle GetSqlSingle(int i) {
2347 return _data[i].SqlSingle;
2351 virtual public SqlString GetSqlString(int i) {
2354 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) {
2355 return _data[i].KatmaiDateTimeSqlString;
2358 return _data[i].SqlString;
2361 virtual public SqlXml GetSqlXml(int i){
2365 if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
2366 // TypeSystem.SQLServer2005
2368 sx = _data[i].IsNull ? SqlXml.Null : _data[i].SqlCachedBuffer.ToSqlXml();
2371 // TypeSystem.SQLServer2000
2373 // First, attempt to obtain SqlXml value. If not SqlXml, we will throw the appropriate
2375 sx = _data[i].IsNull ? SqlXml.Null : _data[i].SqlCachedBuffer.ToSqlXml();
2377 // If the above succeeds, then we received a valid SqlXml instance, now we need to force
2378 // an InvalidCastException since SqlXml is not exposed with the version knob in this setting.
2379 // To do so, we simply force the exception by casting the string representation of the value
2381 object temp = (object) _data[i].String;
2388 virtual public object GetSqlValue(int i) {
2389 SqlStatistics statistics = null;
2391 statistics = SqlStatistics.StartTimer(Statistics);
2393 SetTimeout(_defaultTimeoutMilliseconds);
2394 return GetSqlValueInternal(i);
2397 SqlStatistics.StopTimer(statistics);
2401 private object GetSqlValueInternal(int i) {
2402 if (_currentTask != null) {
2403 throw ADP.AsyncOperationPending();
2406 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
2407 bool result = TryReadColumn(i, setTimeout: false);
2408 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
2410 return GetSqlValueFromSqlBufferInternal(_data[i], _metaData[i]);
2413 // NOTE: This method is called by the fast-paths in Async methods and, therefore, should be resilient to the DataReader being closed
2414 // Always make sure to take reference copies of anything set to null in TryCloseInternal()
2415 private object GetSqlValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData) {
2418 Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty");
2420 // Convert Katmai types to string
2421 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
2422 return data.KatmaiDateTimeSqlString;
2424 else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
2425 return data.SqlValue;
2427 else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
2428 // TypeSystem.SQLServer2005
2430 if (metaData.type == SqlDbType.Udt) {
2431 var connection = _connection;
2432 if (connection != null) {
2433 connection.CheckGetExtendedUDTInfo(metaData, true);
2434 return connection.GetUdtValue(data.Value, metaData, false);
2437 throw ADP.DataReaderClosed("GetSqlValueFromSqlBufferInternal");
2441 return data.SqlValue;
2445 // TypeSystem.SQLServer2000
2447 if (metaData.type == SqlDbType.Xml) {
2448 return data.SqlString;
2451 return data.SqlValue;
2456 virtual public int GetSqlValues(object[] values){
2457 SqlStatistics statistics = null;
2459 statistics = SqlStatistics.StartTimer(Statistics);
2461 if (null == values) {
2462 throw ADP.ArgumentNull("values");
2465 SetTimeout(_defaultTimeoutMilliseconds);
2467 int copyLen = (values.Length < _metaData.visibleColumns) ? values.Length : _metaData.visibleColumns;
2469 for (int i = 0; i < copyLen; i++) {
2470 values[_metaData.indexMap[i]] = GetSqlValueInternal(i);
2475 SqlStatistics.StopTimer(statistics);
2479 override public string GetString(int i) {
2482 // Convert katmai value to string if type system knob is 2005 or earlier
2483 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) {
2484 return _data[i].KatmaiDateTimeString;
2487 return _data[i].String;
2490 override public T GetFieldValue<T>(int i) {
2491 SqlStatistics statistics = null;
2493 statistics = SqlStatistics.StartTimer(Statistics);
2495 SetTimeout(_defaultTimeoutMilliseconds);
2496 return GetFieldValueInternal<T>(i);
2499 SqlStatistics.StopTimer(statistics);
2503 override public object GetValue(int i) {
2504 SqlStatistics statistics = null;
2506 statistics = SqlStatistics.StartTimer(Statistics);
2508 SetTimeout(_defaultTimeoutMilliseconds);
2509 return GetValueInternal(i);
2512 SqlStatistics.StopTimer(statistics);
2516 virtual public TimeSpan GetTimeSpan(int i) {
2519 TimeSpan t = _data[i].Time;
2521 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005) {
2522 // TypeSystem.SQLServer2005 or less
2524 // If the above succeeds, then we received a valid TimeSpan instance, now we need to force
2525 // an InvalidCastException since TimeSpan is not exposed with the version knob in this setting.
2526 // To do so, we simply force the exception by casting the string representation of the value
2528 object temp = (object) _data[i].String;
2529 t = (TimeSpan) temp;
2535 virtual public DateTimeOffset GetDateTimeOffset(int i) {
2538 DateTimeOffset dto = _data[i].DateTimeOffset;
2540 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005) {
2541 // TypeSystem.SQLServer2005 or less
2543 // If the above succeeds, then we received a valid DateTimeOffset instance, now we need to force
2544 // an InvalidCastException since DateTime is not exposed with the version knob in this setting.
2545 // To do so, we simply force the exception by casting the string representation of the value
2546 // To DateTimeOffset.
2547 object temp = (object) _data[i].String;
2548 dto = (DateTimeOffset) temp;
2554 private object GetValueInternal(int i) {
2555 if (_currentTask != null) {
2556 throw ADP.AsyncOperationPending();
2559 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
2560 bool result = TryReadColumn(i, setTimeout: false);
2561 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
2563 return GetValueFromSqlBufferInternal(_data[i], _metaData[i]);
2566 // NOTE: This method is called by the fast-paths in Async methods and, therefore, should be resilient to the DataReader being closed
2567 // Always make sure to take reference copies of anything set to null in TryCloseInternal()
2568 private object GetValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData) {
2571 Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty");
2573 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
2575 return DBNull.Value;
2578 return data.KatmaiDateTimeString;
2581 else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
2584 else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
2585 // TypeSystem.SQLServer2005
2587 if (metaData.type != SqlDbType.Udt) {
2591 var connection = _connection;
2592 if (connection != null) {
2593 connection.CheckGetExtendedUDTInfo(metaData, true);
2594 return connection.GetUdtValue(data.Value, metaData, true);
2597 throw ADP.DataReaderClosed("GetValueFromSqlBufferInternal");
2602 // TypeSystem.SQLServer2000
2607 private T GetFieldValueInternal<T>(int i) {
2608 if (_currentTask != null) {
2609 throw ADP.AsyncOperationPending();
2612 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
2613 bool result = TryReadColumn(i, setTimeout: false);
2614 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
2616 return GetFieldValueFromSqlBufferInternal<T>(_data[i], _metaData[i]);
2619 private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData metaData) {
2620 Type typeofT = typeof(T);
2621 if (_typeofINullable.IsAssignableFrom(typeofT)) {
2622 // If its a SQL Type or Nullable UDT
2623 object rawValue = GetSqlValueFromSqlBufferInternal(data, metaData);
2625 // Special case: User wants SqlString, but we have a SqlXml
2626 // SqlXml can not be typecast into a SqlString, but we need to support SqlString on XML Types - so do a manual conversion
2627 if (typeofT == _typeofSqlString) {
2628 SqlXml xmlValue = rawValue as SqlXml;
2629 if (xmlValue != null) {
2630 if (xmlValue.IsNull) {
2631 rawValue = SqlString.Null;
2634 rawValue = new SqlString(xmlValue.Value);
2642 // Otherwise Its a CLR or non-Nullable UDT
2644 return (T)GetValueFromSqlBufferInternal(data, metaData);
2646 catch (InvalidCastException) {
2648 // If the value was actually null, then we should throw a SqlNullValue instead
2649 throw SQL.SqlNullValue();
2652 // Legitmate InvalidCast, rethrow
2659 override public int GetValues(object[] values) {
2660 SqlStatistics statistics = null;
2661 bool sequentialAccess = IsCommandBehavior(CommandBehavior.SequentialAccess);
2664 statistics = SqlStatistics.StartTimer(Statistics);
2666 if (null == values) {
2667 throw ADP.ArgumentNull("values");
2670 CheckMetaDataIsReady();
2672 int copyLen = (values.Length < _metaData.visibleColumns) ? values.Length : _metaData.visibleColumns;
2673 int maximumColumn = copyLen - 1;
2675 SetTimeout(_defaultTimeoutMilliseconds);
2677 // Temporarily disable sequential access
2678 _commandBehavior &= ~CommandBehavior.SequentialAccess;
2680 // Read in all of the columns in one TryReadColumn call
2681 bool result = TryReadColumn(maximumColumn, setTimeout: false);
2682 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
2684 for (int i = 0; i < copyLen; i++) {
2685 // Get the usable, TypeSystem-compatible value from the iternal buffer
2686 values[_metaData.indexMap[i]] = GetValueFromSqlBufferInternal(_data[i], _metaData[i]);
2688 // If this is sequential access, then we need to wipe the internal buffer
2689 if ((sequentialAccess) && (i < maximumColumn)) {
2697 // Restore sequential access
2698 if (sequentialAccess) {
2699 _commandBehavior |= CommandBehavior.SequentialAccess;
2702 SqlStatistics.StopTimer(statistics);
2706 private MetaType GetVersionedMetaType(MetaType actualMetaType) {
2707 Debug.Assert(_typeSystem == SqlConnectionString.TypeSystem.SQLServer2000, "Should not be in this function under anything else but SQLServer2000");
2709 MetaType metaType = null;
2711 if (actualMetaType == MetaType.MetaUdt) {
2712 metaType = MetaType.MetaVarBinary;
2714 else if (actualMetaType == MetaType.MetaXml) {
2715 metaType = MetaType.MetaNText;
2717 else if (actualMetaType == MetaType.MetaMaxVarBinary) {
2718 metaType = MetaType.MetaImage;
2720 else if (actualMetaType == MetaType.MetaMaxVarChar) {
2721 metaType = MetaType.MetaText;
2723 else if (actualMetaType == MetaType.MetaMaxNVarChar) {
2724 metaType = MetaType.MetaNText;
2727 metaType = actualMetaType;
2733 private bool TryHasMoreResults(out bool moreResults) {
2734 if(null != _parser) {
2736 if (!TryHasMoreRows(out moreRows)) {
2737 moreResults = false;
2741 // When does this happen? This is only called from NextResult(), which loops until Read() false.
2742 moreResults = false;
2746 Debug.Assert(null != _command, "unexpected null command from the data reader!");
2748 while(_stateObj._pendingData) {
2750 if (!_stateObj.TryPeekByte(out token)) {
2751 moreResults = false;
2756 case TdsEnums.SQLALTROW:
2757 if(_altRowStatus == ALTROWSTATUS.Null) {
2758 // cache the regular metadata
2759 _altMetaDataSetCollection.metaDataSet = _metaData;
2763 Debug.Assert(_altRowStatus == ALTROWSTATUS.Done, "invalid AltRowStatus");
2765 _altRowStatus = ALTROWSTATUS.AltRow;
2769 case TdsEnums.SQLROW:
2770 case TdsEnums.SQLNBCROW:
2771 // always happens if there is a row following an altrow
2775 // VSTFDEVDIV 926281: DONEINPROC case is missing here; we have decided to reject this
2778 case TdsEnums.SQLDONE:
2779 Debug.Assert(_altRowStatus == ALTROWSTATUS.Done || _altRowStatus == ALTROWSTATUS.Null, "invalid AltRowStatus");
2780 _altRowStatus = ALTROWSTATUS.Null;
2782 _altMetaDataSetCollection = null;
2785 case TdsEnums.SQLCOLMETADATA:
2794 if (_parser.State == TdsParserState.Broken || _parser.State == TdsParserState.Closed) {
2795 throw ADP.ClosedConnectionError();
2799 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
2800 moreResults = false;
2805 moreResults = false;
2809 private bool TryHasMoreRows(out bool moreRows) {
2810 if (null != _parser) {
2811 if (_sharedState._dataReady) {
2816 // NextResult: previous call to NextResult started to process the altrowpackage, can't peek anymore
2817 // Read: Read prepared for final processing of altrow package, No more Rows until NextResult ...
2818 // Done: Done processing the altrow, no more rows until NextResult ...
2819 switch (_altRowStatus) {
2820 case ALTROWSTATUS.AltRow:
2823 case ALTROWSTATUS.Done:
2827 if (_stateObj._pendingData) {
2828 // Consume error's, info's, done's on HasMoreRows, so user obtains error on Read.
2840 // process any done, doneproc and doneinproc token streams and
2841 // any order, error or info token preceeding the first done, doneproc or doneinproc token stream
2843 if (!_stateObj.TryPeekByte(out b)) {
2847 bool ParsedDoneToken = false;
2849 while ( b == TdsEnums.SQLDONE ||
2850 b == TdsEnums.SQLDONEPROC ||
2851 b == TdsEnums.SQLDONEINPROC ||
2852 !ParsedDoneToken && (
2853 b == TdsEnums.SQLSESSIONSTATE ||
2854 b == TdsEnums.SQLENVCHANGE ||
2855 b == TdsEnums.SQLORDER ||
2856 b == TdsEnums.SQLERROR ||
2857 b == TdsEnums.SQLINFO ) ) {
2859 if (b == TdsEnums.SQLDONE ||
2860 b == TdsEnums.SQLDONEPROC ||
2861 b == TdsEnums.SQLDONEINPROC) {
2862 ParsedDoneToken = true;
2869 if (_parser.State == TdsParserState.Broken || _parser.State == TdsParserState.Closed) {
2870 throw ADP.ClosedConnectionError();
2874 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
2878 if ( _stateObj._pendingData) {
2879 if (!_stateObj.TryPeekByte(out b)) {
2889 // Only return true when we are positioned on a row token.
2890 if (IsRowToken(b)) {
2900 private bool IsRowToken(byte token) {
2901 return TdsEnums.SQLROW == token || TdsEnums.SQLNBCROW == token;
2904 override public bool IsDBNull(int i) {
2905 if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) && ((_sharedState._nextColumnHeaderToRead > i + 1) || (_lastColumnWithDataChunkRead > i))) {
2911 // To replicate this behavior we will skip CheckHeaderIsReady\ReadColumnHeader and instead just check that the reader is ready and the column is valid
2912 CheckMetaDataIsReady(columnIndex: i);
2915 CheckHeaderIsReady(columnIndex: i, methodName: "IsDBNull");
2917 SetTimeout(_defaultTimeoutMilliseconds);
2919 ReadColumnHeader(i); // header data only
2922 return _data[i].IsNull;
2925 protected internal bool IsCommandBehavior(CommandBehavior condition) {
2926 return (condition == (condition & _commandBehavior));
2929 override public bool NextResult() {
2930 if (_currentTask != null) {
2931 throw SQL.PendingBeginXXXExists();
2937 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
2938 result = TryNextResult(out more);
2940 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
2944 // recordset is automatically positioned on the first result set
2945 private bool TryNextResult(out bool more) {
2946 SqlStatistics statistics = null;
2948 Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.NextResult|API> %d#", ObjectID);
2950 RuntimeHelpers.PrepareConstrainedRegions();
2953 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
2955 RuntimeHelpers.PrepareConstrainedRegions();
2957 tdsReliabilitySection.Start();
2961 statistics = SqlStatistics.StartTimer(Statistics);
2963 SetTimeout(_defaultTimeoutMilliseconds);
2966 throw ADP.DataReaderClosed("NextResult");
2968 _fieldNameLookup = null;
2970 bool success = false; // WebData 100390
2971 _hasRows = false; // reset HasRows
2973 // if we are specifically only processing a single result, then read all the results off the wire and detach
2974 if (IsCommandBehavior(CommandBehavior.SingleResult)) {
2975 if (!TryCloseInternal(false /*closeReader*/)) {
2980 // In the case of not closing the reader, null out the metadata AFTER
2981 // CloseInternal finishes - since CloseInternal may go to the wire
2982 // and use the metadata.
2988 if (null != _parser) {
2989 // if there are more rows, then skip them, the user wants the next result
2990 bool moreRows = true;
2992 if (!TryReadInternal(false, out moreRows)) { // don't reset set the timeout value
2999 // we may be done, so continue only if we have not detached ourselves from the parser
3000 if (null != _parser) {
3002 if (!TryHasMoreResults(out moreResults)) {
3007 _metaDataConsumed = false;
3008 _browseModeInfoConsumed = false;
3010 switch (_altRowStatus) {
3011 case ALTROWSTATUS.AltRow:
3013 if (!_parser.TryGetAltRowId(_stateObj, out altRowId)) {
3017 _SqlMetaDataSet altMetaDataSet = _altMetaDataSetCollection.GetAltMetaData(altRowId);
3018 if (altMetaDataSet != null) {
3019 _metaData = altMetaDataSet;
3021 Debug.Assert ((_metaData != null), "Can't match up altrowmetadata");
3023 case ALTROWSTATUS.Done:
3024 // restore the row-metaData
3025 _metaData = _altMetaDataSetCollection.metaDataSet;
3026 Debug.Assert (_altRowStatus == ALTROWSTATUS.Done, "invalid AltRowStatus");
3027 _altRowStatus = ALTROWSTATUS.Null;
3030 if (!TryConsumeMetaData()) {
3034 if (_metaData == null) {
3044 // detach the parser from this reader now
3045 if (!TryCloseInternal(false /*closeReader*/)) {
3050 // In the case of not closing the reader, null out the metadata AFTER
3051 // CloseInternal finishes - since CloseInternal may go to the wire
3052 // and use the metadata.
3053 if (!TrySetMetaData(null, false)) {
3060 // Clear state in case of Read calling CloseInternal() then user calls NextResult()
3061 // MDAC 81986. Or, also the case where the Read() above will do essentially the same
3071 tdsReliabilitySection.Stop();
3075 catch (System.OutOfMemoryException e) {
3077 if (null != _connection) {
3078 _connection.Abort(e);
3082 catch (System.StackOverflowException e) {
3084 if (null != _connection) {
3085 _connection.Abort(e);
3089 catch (System.Threading.ThreadAbortException e) {
3091 if (null != _connection) {
3092 _connection.Abort(e);
3097 SqlStatistics.StopTimer(statistics);
3098 Bid.ScopeLeave(ref hscp);
3102 // user must call Read() to position on the first row
3103 override public bool Read() {
3104 if (_currentTask != null) {
3105 throw SQL.PendingBeginXXXExists();
3111 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
3112 result = TryReadInternal(true, out more);
3114 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
3118 // user must call Read() to position on the first row
3119 private bool TryReadInternal(bool setTimeout, out bool more) {
3120 SqlStatistics statistics = null;
3122 Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.Read|API> %d#", ObjectID);
3124 RuntimeHelpers.PrepareConstrainedRegions();
3127 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3129 RuntimeHelpers.PrepareConstrainedRegions();
3131 tdsReliabilitySection.Start();
3135 statistics = SqlStatistics.StartTimer(Statistics);
3137 if (null != _parser) {
3139 SetTimeout(_defaultTimeoutMilliseconds);
3141 if (_sharedState._dataReady) {
3142 if (!TryCleanPartialRead()) {
3148 // clear out our buffers
3149 SqlBuffer.Clear(_data);
3151 _sharedState._nextColumnHeaderToRead = 0;
3152 _sharedState._nextColumnDataToRead = 0;
3153 _sharedState._columnDataBytesRemaining = -1; // unknown
3154 _lastColumnWithDataChunkRead = -1;
3158 if (!TryHasMoreRows(out moreRows)) {
3163 // read the row from the backend (unless it's an altrow were the marker is already inside the altrow ...)
3164 while (_stateObj._pendingData) {
3165 if (_altRowStatus != ALTROWSTATUS.AltRow) {
3166 // if this is an ordinary row we let the run method consume the ROW token
3167 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out _sharedState._dataReady)) {
3171 if (_sharedState._dataReady) {
3176 // ALTROW token and AltrowId are already consumed ...
3177 Debug.Assert (_altRowStatus == ALTROWSTATUS.AltRow, "invalid AltRowStatus");
3178 _altRowStatus = ALTROWSTATUS.Done;
3179 _sharedState._dataReady = true;
3183 if (_sharedState._dataReady) {
3184 _haltRead = IsCommandBehavior(CommandBehavior.SingleRow);
3190 if (!_stateObj._pendingData) {
3191 if (!TryCloseInternal(false /*closeReader*/)) {
3198 // if we did not get a row and halt is true, clean off rows of result
3199 // success must be false - or else we could have just read off row and set
3202 if (!TryHasMoreRows(out moreRows)) {
3207 // if we are in SingleRow mode, and we've read the first row,
3208 // read the rest of the rows, if any
3209 while (_stateObj._pendingData && !_sharedState._dataReady) {
3210 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out _sharedState._dataReady)) {
3216 if (_sharedState._dataReady) {
3217 if (!TryCleanPartialRead()) {
3223 // clear out our buffers
3224 SqlBuffer.Clear(_data);
3226 _sharedState._nextColumnHeaderToRead = 0;
3228 if (!TryHasMoreRows(out moreRows)) {
3238 else if (IsClosed) {
3239 throw ADP.DataReaderClosed("Read");
3244 if ((!_sharedState._dataReady) && (_stateObj._pendingData)) {
3246 if (!_stateObj.TryPeekByte(out token)) {
3250 Debug.Assert(TdsParser.IsValidTdsToken(token), string.Format("DataReady is false, but next token is invalid: {0,-2:X2}", token));
3258 tdsReliabilitySection.Stop();
3262 catch (System.OutOfMemoryException e) {
3264 SqlConnection con = _connection;
3270 catch (System.StackOverflowException e) {
3272 SqlConnection con = _connection;
3278 catch (System.Threading.ThreadAbortException e) {
3280 SqlConnection con = _connection;
3287 SqlStatistics.StopTimer(statistics);
3288 Bid.ScopeLeave(ref hscp);
3292 private void ReadColumn(int i, bool setTimeout = true, bool allowPartiallyReadColumn = false) {
3293 if (_currentTask != null) {
3294 throw ADP.AsyncOperationPending();
3297 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
3298 bool result = TryReadColumn(i, setTimeout, allowPartiallyReadColumn);
3299 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
3302 private bool TryReadColumn(int i, bool setTimeout, bool allowPartiallyReadColumn = false) {
3303 CheckDataIsReady(columnIndex: i, permitAsync: true, allowPartiallyReadColumn: allowPartiallyReadColumn);
3305 RuntimeHelpers.PrepareConstrainedRegions();
3308 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3310 RuntimeHelpers.PrepareConstrainedRegions();
3312 tdsReliabilitySection.Start();
3316 Debug.Assert(_sharedState._nextColumnHeaderToRead <= _metaData.Length, "_sharedState._nextColumnHeaderToRead too large");
3317 Debug.Assert(_sharedState._nextColumnDataToRead <= _metaData.Length, "_sharedState._nextColumnDataToRead too large");
3320 SetTimeout(_defaultTimeoutMilliseconds);
3323 if (!TryReadColumnInternal(i, readHeaderOnly: false)) {
3327 Debug.Assert(null != _data[i], " data buffer is null?");
3331 tdsReliabilitySection.Stop();
3335 catch (System.OutOfMemoryException e) {
3337 if (null != _connection) {
3338 _connection.Abort(e);
3342 catch (System.StackOverflowException e) {
3344 if (null != _connection) {
3345 _connection.Abort(e);
3349 catch (System.Threading.ThreadAbortException e) {
3351 if (null != _connection) {
3352 _connection.Abort(e);
3360 private bool TryReadColumnData() {
3361 // If we've already read the value (because it was NULL) we don't
3362 // bother to read here.
3363 if (!_data[_sharedState._nextColumnDataToRead].IsNull) {
3364 _SqlMetaData columnMetaData = _metaData[_sharedState._nextColumnDataToRead];
3366 if (!_parser.TryReadSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, (int)_sharedState._columnDataBytesRemaining, _stateObj,
3367 _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting,
3368 columnMetaData.column)) { // will read UDTs as VARBINARY.
3371 _sharedState._columnDataBytesRemaining = 0;
3373 _sharedState._nextColumnDataToRead++;
3377 private void ReadColumnHeader(int i) {
3378 Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
3379 bool result = TryReadColumnHeader(i);
3380 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
3383 private bool TryReadColumnHeader(int i) {
3384 if (!_sharedState._dataReady) {
3385 throw SQL.InvalidRead();
3387 RuntimeHelpers.PrepareConstrainedRegions();
3390 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3392 RuntimeHelpers.PrepareConstrainedRegions();
3394 tdsReliabilitySection.Start();
3396 return TryReadColumnInternal(i, readHeaderOnly: true);
3400 tdsReliabilitySection.Stop();
3404 catch (System.OutOfMemoryException e) {
3406 if (null != _connection) {
3407 _connection.Abort(e);
3411 catch (System.StackOverflowException e) {
3413 if (null != _connection) {
3414 _connection.Abort(e);
3418 catch (System.Threading.ThreadAbortException e) {
3420 if (null != _connection) {
3421 _connection.Abort(e);
3427 private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) {
3428 AssertReaderState(requireData: true, permitAsync: true, columnIndex: i);
3430 // Check if we've already read the header already
3431 if (i < _sharedState._nextColumnHeaderToRead) {
3432 // Read the header, but we need to read the data
3433 if ((i == _sharedState._nextColumnDataToRead) && (!readHeaderOnly)) {
3434 return TryReadColumnData();
3436 // Else we've already read the data, or we're reading the header only
3438 // Ensure that, if we've read past the column, then we did store its data
3439 Debug.Assert(i == _sharedState._nextColumnDataToRead || // Either we haven't read the column yet
3440 ((i + 1 < _sharedState._nextColumnDataToRead) && (IsCommandBehavior(CommandBehavior.SequentialAccess))) || // Or we're in sequential mode and we've read way past the column (i.e. it was not the last column we read)
3441 (!_data[i].IsEmpty || _data[i].IsNull) || // Or we should have data stored for the column (unless the column was null)
3442 (_metaData[i].type == SqlDbType.Timestamp), // Or Dev11
3444 "Gone past column, be we have no data stored for it");
3449 Debug.Assert(_data[i].IsEmpty || _data[i].IsNull, "re-reading column value?");
3451 // If we're in sequential access mode, we can safely clear out any
3452 // data from the previous column.
3453 bool isSequentialAccess = IsCommandBehavior(CommandBehavior.SequentialAccess);
3454 if (isSequentialAccess) {
3455 if (0 < _sharedState._nextColumnDataToRead) {
3456 _data[_sharedState._nextColumnDataToRead - 1].Clear();
3459 // Only wipe out the blob objects if they aren't for a 'future' column (i.e. we haven't read up to them yet)
3460 if ((_lastColumnWithDataChunkRead > -1) && (i > _lastColumnWithDataChunkRead)) {
3461 CloseActiveSequentialStreamAndTextReader();
3464 else if (_sharedState._nextColumnDataToRead < _sharedState._nextColumnHeaderToRead) {
3465 // We read the header but not the column for the previous column
3466 if (!TryReadColumnData()) {
3469 Debug.Assert(_sharedState._nextColumnDataToRead == _sharedState._nextColumnHeaderToRead);
3472 // if we still have bytes left from the previous blob read, clear the wire and reset
3473 if (!TryResetBlobState()) {
3478 _SqlMetaData columnMetaData = _metaData[_sharedState._nextColumnHeaderToRead];
3480 if ((isSequentialAccess) && (_sharedState._nextColumnHeaderToRead < i)) {
3481 // SkipValue is no-op if the column appears in NBC bitmask
3482 // if not, it skips regular and PLP types
3483 if (!_parser.TrySkipValue(columnMetaData, _sharedState._nextColumnHeaderToRead, _stateObj)) {
3487 _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead;
3488 _sharedState._nextColumnHeaderToRead++;
3493 if (!_parser.TryProcessColumnHeader(columnMetaData, _stateObj, _sharedState._nextColumnHeaderToRead, out isNull, out dataLength)) {
3497 _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead;
3498 _sharedState._nextColumnHeaderToRead++; // We read this one
3500 if (isNull && columnMetaData.type != SqlDbType.Timestamp /* Maintain behavior for known */)
3502 TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead],
3504 _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting,
3505 _parser.Connection);
3507 if (!readHeaderOnly) {
3508 _sharedState._nextColumnDataToRead++;
3512 if ((i > _sharedState._nextColumnDataToRead) || (!readHeaderOnly)) {
3513 // If we're not in sequential access mode, we have to
3514 // save the data we skip over so that the consumer
3515 // can read it out of order
3516 if (!_parser.TryReadSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, (int)dataLength, _stateObj,
3517 _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting,
3518 columnMetaData.column)) { // will read UDTs as VARBINARY.
3521 _sharedState._nextColumnDataToRead++;
3524 _sharedState._columnDataBytesRemaining = (long)dataLength;
3529 if (_snapshot != null) {
3530 // reset snapshot to save memory use. We can safely do that here because all SqlDataReader values are stable.
3531 // The retry logic can use the current values to get back to the right state.
3533 PrepareAsyncInvocation(useSnapshot: true);
3535 } while (_sharedState._nextColumnHeaderToRead <= i);
3540 // Estimates if there is enough data available to read the number of columns requested
3541 private bool WillHaveEnoughData(int targetColumn, bool headerOnly = false) {
3542 AssertReaderState(requireData: true, permitAsync: true, columnIndex: targetColumn);
3544 if ((_lastColumnWithDataChunkRead == _sharedState._nextColumnDataToRead) && (_metaData[_lastColumnWithDataChunkRead].metaType.IsPlp)) {
3545 // In the middle of reading a Plp - no idea how much is left
3549 int bytesRemaining = Math.Min(checked(_stateObj._inBytesRead - _stateObj._inBytesUsed), _stateObj._inBytesPacket);
3551 // There are some parts of our code that peeks at the next token after doing its read
3552 // So we will make sure that there is always a spare byte for it to look at
3555 if ((targetColumn >= _sharedState._nextColumnDataToRead) && (_sharedState._nextColumnDataToRead < _sharedState._nextColumnHeaderToRead)) {
3556 if (_sharedState._columnDataBytesRemaining > bytesRemaining) {
3557 // The current column needs more data than we currently have
3558 // NOTE: Since the Long data types (TEXT, IMAGE, NTEXT) can have a size of Int32.MaxValue we cannot simply subtract
3559 // _columnDataBytesRemaining from bytesRemaining and then compare it to zero as this may lead to an overflow
3563 // Already read the header, so subtract actual data size
3564 bytesRemaining = checked(bytesRemaining - (int)_sharedState._columnDataBytesRemaining);
3568 // For each column that we need to read, subtract the size of its header and the size of its data
3569 int currentColumn = _sharedState._nextColumnHeaderToRead;
3570 while ((bytesRemaining >= 0) && (currentColumn <= targetColumn)) {
3572 if (!_stateObj.IsNullCompressionBitSet(currentColumn)) {
3574 // NOTE: This is mostly duplicated from TryProcessColumnHeaderNoNBC and TryGetTokenLength
3575 var metaType = _metaData[currentColumn].metaType;
3576 if ((metaType.IsLong) || (metaType.IsPlp) || (metaType.SqlDbType == SqlDbType.Udt) || (metaType.SqlDbType == SqlDbType.Structured)) {
3577 // Plp, Udt and TVP types have an unknownable size - so return that the estimate failed
3581 byte typeAndMask = (byte)(_metaData[currentColumn].tdsType & TdsEnums.SQLLenMask);
3582 if ((typeAndMask == TdsEnums.SQLVarLen) || (typeAndMask == TdsEnums.SQLVarCnt)) {
3583 if (0 != (_metaData[currentColumn].tdsType & 0x80)) {
3584 // UInt16 represents size
3587 else if (0 == (_metaData[currentColumn].tdsType & 0x0c)) {
3588 // UInt32 represents size
3592 // Byte represents size
3601 bytesRemaining = checked(bytesRemaining - maxHeaderSize);
3602 if ((currentColumn < targetColumn) || (!headerOnly)) {
3603 bytesRemaining = checked(bytesRemaining - _metaData[currentColumn].length);
3610 return (bytesRemaining >= 0);
3613 // clean remainder bytes for the column off the wire
3614 private bool TryResetBlobState() {
3615 Debug.Assert(null != _stateObj, "null state object"); // _parser may be null at this point
3616 AssertReaderState(requireData: true, permitAsync: true);
3617 Debug.Assert(_sharedState._nextColumnHeaderToRead <= _metaData.Length, "_sharedState._nextColumnHeaderToRead too large");
3619 // If we haven't already entirely read the column
3620 if (_sharedState._nextColumnDataToRead < _sharedState._nextColumnHeaderToRead) {
3621 if ((_sharedState._nextColumnHeaderToRead > 0) && (_metaData[_sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) {
3622 if (_stateObj._longlen != 0) {
3624 if (!_stateObj.Parser.TrySkipPlpValue(UInt64.MaxValue, _stateObj, out ignored)) {
3628 if (_streamingXml != null) {
3629 SqlStreamingXml localSXml = _streamingXml;
3630 _streamingXml = null;
3634 else if (0 < _sharedState._columnDataBytesRemaining) {
3635 if (!_stateObj.TrySkipLongBytes(_sharedState._columnDataBytesRemaining)) {
3642 Debug.Assert((_sharedState._columnDataBytesRemaining == 0 || _sharedState._columnDataBytesRemaining == -1) && _stateObj._longlen == 0, "Haven't read header yet, but column is partially read?");
3646 _sharedState._columnDataBytesRemaining = 0;
3647 _columnDataBytesRead = 0;
3648 _columnDataCharsRead = 0;
3649 _columnDataChars = null;
3650 _columnDataCharsIndex = -1;
3651 _stateObj._plpdecoder = null;
3656 private void CloseActiveSequentialStreamAndTextReader() {
3657 if (_currentStream != null) {
3658 _currentStream.SetClosed();
3659 _currentStream = null;
3661 if (_currentTextReader != null) {
3662 _currentTextReader.SetClosed();
3663 _currentStream = null;
3667 private void RestoreServerSettings(TdsParser parser, TdsParserStateObject stateObj) {
3668 // turn off any set options
3669 if (null != parser && null != _resetOptionsString) {
3670 // It is possible for this to be called during connection close on a
3671 // broken connection, so check state first.
3672 if (parser.State == TdsParserState.OpenLoggedIn) {
3673 Bid.CorrelationTrace("<sc.SqlDataReader.RestoreServerSettings|Info|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
3674 Task executeTask = parser.TdsExecuteSQLBatch(_resetOptionsString, (_command != null) ? _command.CommandTimeout : 0, null, stateObj, sync: true);
3675 Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes");
3677 // must execute this one synchronously as we can't retry
3678 parser.Run(RunBehavior.UntilDone, _command, this, null, stateObj);
3680 _resetOptionsString = null;
3684 internal bool TrySetAltMetaDataSet(_SqlMetaDataSet metaDataSet, bool metaDataConsumed) {
3685 if (_altMetaDataSetCollection == null) {
3686 _altMetaDataSetCollection = new _SqlMetaDataSetCollection();
3688 else if (_snapshot != null && object.ReferenceEquals(_snapshot._altMetaDataSetCollection, _altMetaDataSetCollection)) {
3689 _altMetaDataSetCollection = (_SqlMetaDataSetCollection)_altMetaDataSetCollection.Clone();
3691 _altMetaDataSetCollection.SetAltMetaData(metaDataSet);
3692 _metaDataConsumed = metaDataConsumed;
3693 if (_metaDataConsumed && null != _parser) {
3695 if (!_stateObj.TryPeekByte(out b)) {
3698 if (TdsEnums.SQLORDER == b) {
3700 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
3703 if (!_stateObj.TryPeekByte(out b)) {
3707 if (b == TdsEnums.SQLINFO) {
3709 _stateObj._accumulateInfoEvents = true;
3711 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, null, null, _stateObj, out ignored)) {
3716 _stateObj._accumulateInfoEvents = false;
3718 if (!_stateObj.TryPeekByte(out b)) {
3722 _hasRows = IsRowToken(b);
3724 if (metaDataSet != null) {
3725 if (_data == null || _data.Length<metaDataSet.Length) {
3726 _data = SqlBuffer.CreateBufferArray(metaDataSet.Length);
3732 private void ClearMetaData() {
3735 _fieldNameLookup = null;
3736 _metaDataConsumed = false;
3737 _browseModeInfoConsumed = false;
3740 internal bool TrySetMetaData(_SqlMetaDataSet metaData, bool moreInfo) {
3741 _metaData = metaData;
3743 // get rid of cached metadata info as well
3745 if (_metaData != null) {
3746 _metaData.schemaTable = null;
3747 _data = SqlBuffer.CreateBufferArray(metaData.Length);
3750 _fieldNameLookup = null;
3752 if (null != metaData) {
3753 // we are done consuming metadata only if there is no moreInfo
3755 _metaDataConsumed = true;
3757 if (_parser != null) { // There is a valid case where parser is null
3758 // Peek, and if row token present, set _hasRows true since there is a
3759 // row in the result
3761 if (!_stateObj.TryPeekByte(out b)) {
3768 // simply rip the order token off the wire
3769 if (b == TdsEnums.SQLORDER) { // same logic as SetAltMetaDataSet
3770 // Devnote: That's not the right place to process TDS
3771 // Can this result in Reentrance to Run?
3774 if (!_parser.TryRun(RunBehavior.ReturnImmediately, null, null, null, _stateObj, out ignored)) {
3777 if (!_stateObj.TryPeekByte(out b)) {
3781 if (b == TdsEnums.SQLINFO)
3784 // We are accumulating informational events and fire them at next
3785 // TdsParser.Run purely to avoid breaking change
3787 _stateObj._accumulateInfoEvents = true;
3789 if (!_parser.TryRun(RunBehavior.ReturnImmediately, null, null, null, _stateObj, out ignored)) {
3794 _stateObj._accumulateInfoEvents = false;
3796 if (!_stateObj.TryPeekByte(out b)) {
3800 _hasRows = IsRowToken(b);
3801 if (TdsEnums.SQLALTMETADATA == b)
3803 _metaDataConsumed = false;
3809 _metaDataConsumed = false;
3812 _browseModeInfoConsumed = false;
3816 private void SetTimeout(long timeoutMilliseconds) {
3817 // WebData 111653,112003 -- we now set timeouts per operation, not
3818 // per command (it's not supposed to be a cumulative per command).
3819 TdsParserStateObject stateObj = _stateObj;
3820 if (null != stateObj) {
3821 stateObj.SetTimeoutMilliseconds(timeoutMilliseconds);
3825 private bool HasActiveStreamOrTextReaderOnColumn(int columnIndex) {
3826 bool active = false;
3828 active |= ((_currentStream != null) && (_currentStream.ColumnIndex == columnIndex));
3829 active |= ((_currentTextReader != null) && (_currentTextReader.ColumnIndex == columnIndex));
3834 private void CheckMetaDataIsReady() {
3835 if (_currentTask != null) {
3836 throw ADP.AsyncOperationPending();
3838 if (MetaData == null) {
3839 throw SQL.InvalidRead();
3843 private void CheckMetaDataIsReady(int columnIndex, bool permitAsync = false) {
3844 if ((!permitAsync) && (_currentTask != null)) {
3845 throw ADP.AsyncOperationPending();
3847 if (MetaData == null) {
3848 throw SQL.InvalidRead();
3850 if ((columnIndex < 0) || (columnIndex >= _metaData.Length)) {
3851 throw ADP.IndexOutOfRange();
3855 private void CheckDataIsReady() {
3856 if (_currentTask != null) {
3857 throw ADP.AsyncOperationPending();
3859 Debug.Assert(!_sharedState._dataReady || _metaData != null, "Data is ready, but there is no metadata?");
3860 if ((!_sharedState._dataReady) || (_metaData == null)) {
3861 throw SQL.InvalidRead();
3865 private void CheckHeaderIsReady(int columnIndex, bool permitAsync = false, string methodName = null) {
3867 throw ADP.DataReaderClosed(methodName ?? "CheckHeaderIsReady");
3869 if ((!permitAsync) && (_currentTask != null)) {
3870 throw ADP.AsyncOperationPending();
3872 Debug.Assert(!_sharedState._dataReady || _metaData != null, "Data is ready, but there is no metadata?");
3873 if ((!_sharedState._dataReady) || (_metaData == null)) {
3874 throw SQL.InvalidRead();
3876 if ((columnIndex < 0) || (columnIndex >= _metaData.Length)) {
3877 throw ADP.IndexOutOfRange();
3879 if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) && // Only for sequential access
3880 ((_sharedState._nextColumnHeaderToRead > columnIndex + 1) || (_lastColumnWithDataChunkRead > columnIndex))) { // Read past column
3881 throw ADP.NonSequentialColumnAccess(columnIndex, Math.Max(_sharedState._nextColumnHeaderToRead - 1, _lastColumnWithDataChunkRead));
3885 private void CheckDataIsReady(int columnIndex, bool allowPartiallyReadColumn = false, bool permitAsync = false, string methodName = null) {
3887 throw ADP.DataReaderClosed(methodName ?? "CheckDataIsReady");
3889 if ((!permitAsync) && (_currentTask != null)) {
3890 throw ADP.AsyncOperationPending();
3892 Debug.Assert(!_sharedState._dataReady || _metaData != null, "Data is ready, but there is no metadata?");
3893 if ((!_sharedState._dataReady) || (_metaData == null)) {
3894 throw SQL.InvalidRead();
3896 if ((columnIndex < 0) || (columnIndex >= _metaData.Length)) {
3897 throw ADP.IndexOutOfRange();
3899 if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) && // Only for sequential access
3900 ((_sharedState._nextColumnDataToRead > columnIndex) || (_lastColumnWithDataChunkRead > columnIndex) || // Read past column
3901 ((!allowPartiallyReadColumn) && (_lastColumnWithDataChunkRead == columnIndex)) || // Partially read column
3902 ((allowPartiallyReadColumn) && (HasActiveStreamOrTextReaderOnColumn(columnIndex))))) { // Has a Stream or TextReader on a partially-read column
3903 throw ADP.NonSequentialColumnAccess(columnIndex, Math.Max(_sharedState._nextColumnDataToRead, _lastColumnWithDataChunkRead + 1));
3907 [Conditional("DEBUG")]
3908 private void AssertReaderState(bool requireData, bool permitAsync, int? columnIndex = null, bool enforceSequentialAccess = false) {
3909 Debug.Assert(!_sharedState._dataReady || _metaData != null, "Data is ready, but there is no metadata?");
3910 Debug.Assert(permitAsync || _currentTask == null, "Call while async operation is pending");
3911 Debug.Assert(_metaData != null, "_metaData is null, check MetaData before calling this method");
3912 Debug.Assert(!requireData || _sharedState._dataReady, "No data is ready to be read");
3913 if (columnIndex.HasValue) {
3914 Debug.Assert(columnIndex.Value >= 0 && columnIndex.Value < _metaData.Length, "Invalid column index");
3915 Debug.Assert((!enforceSequentialAccess) || (!IsCommandBehavior(CommandBehavior.SequentialAccess)) || ((_sharedState._nextColumnDataToRead <= columnIndex) && (_lastColumnWithDataChunkRead <= columnIndex)), "Already read past column");
3919 public override Task<bool> NextResultAsync(CancellationToken cancellationToken) {
3921 Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.NextResultAsync|API> %d#", ObjectID);
3924 TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
3927 source.SetException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("NextResultAsync")));
3931 IDisposable registration = null;
3932 if (cancellationToken.CanBeCanceled) {
3933 if (cancellationToken.IsCancellationRequested) {
3934 source.SetCanceled();
3937 registration = cancellationToken.Register(_command.CancelIgnoreFailure);
3940 Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
3941 if (original != null) {
3942 source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
3946 // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
3947 if (_cancelAsyncOnCloseToken.IsCancellationRequested) {
3948 source.SetCanceled();
3949 _currentTask = null;
3953 PrepareAsyncInvocation(useSnapshot: true);
3955 Func<Task, Task<bool>> moreFunc = null;
3959 Bid.Trace("<sc.SqlDataReader.NextResultAsync> attempt retry %d#\n", ObjectID);
3960 PrepareForAsyncContinuation();
3964 if (TryNextResult(out more)) {
3966 return more ? ADP.TrueTask : ADP.FalseTask;
3969 return ContinueRetryable(moreFunc);
3972 return InvokeRetryable(moreFunc, source, registration);
3975 Bid.ScopeLeave(ref hscp);
3979 // NOTE: This will return null if it completed sequentially
3980 // If this returns null, then you can use bytesRead to see how many bytes were read - otherwise bytesRead should be ignored
3981 internal Task<int> GetBytesAsync(int i, byte[] buffer, int index, int length, int timeout, CancellationToken cancellationToken, out int bytesRead) {
3982 AssertReaderState(requireData: true, permitAsync: true, columnIndex: i, enforceSequentialAccess: true);
3983 Debug.Assert(IsCommandBehavior(CommandBehavior.SequentialAccess));
3987 TaskCompletionSource<int> source = new TaskCompletionSource<int>();
3988 source.SetException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("GetBytesAsync")));
3992 if (_currentTask != null) {
3993 TaskCompletionSource<int> source = new TaskCompletionSource<int>();
3994 source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
3998 if (cancellationToken.CanBeCanceled) {
3999 if (cancellationToken.IsCancellationRequested) {
4004 // Check if we need to skip columns
4005 Debug.Assert(_sharedState._nextColumnDataToRead <= _lastColumnWithDataChunkRead, "Non sequential access");
4006 if ((_sharedState._nextColumnHeaderToRead <= _lastColumnWithDataChunkRead) || (_sharedState._nextColumnDataToRead < _lastColumnWithDataChunkRead)) {
4007 TaskCompletionSource<int> source = new TaskCompletionSource<int>();
4008 Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
4009 if (original != null) {
4010 source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4014 PrepareAsyncInvocation(useSnapshot: true);
4016 Func<Task, Task<int>> moreFunc = null;
4019 CancellationToken timeoutToken = CancellationToken.None;
4020 CancellationTokenSource timeoutCancellationSource = null;
4022 timeoutCancellationSource = new CancellationTokenSource();
4023 timeoutCancellationSource.CancelAfter(timeout);
4024 timeoutToken = timeoutCancellationSource.Token;
4029 Bid.Trace("<sc.SqlDataReader.GetBytesAsync> attempt retry %d#\n", ObjectID);
4030 PrepareForAsyncContinuation();
4033 // Prepare for stateObj timeout
4034 SetTimeout(_defaultTimeoutMilliseconds);
4036 if (TryReadColumnHeader(i)) {
4037 // Only once we have read upto where we need to be can we check the cancellation tokens (otherwise we will be in an unknown state)
4039 if (cancellationToken.IsCancellationRequested) {
4040 // User requested cancellation
4041 return ADP.CreatedTaskWithCancellation<int>();
4043 else if (timeoutToken.IsCancellationRequested) {
4045 return ADP.CreatedTaskWithException<int>(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout())));
4048 // Upto the correct column - continue to read
4049 SwitchToAsyncWithoutSnapshot();
4051 var readTask = GetBytesAsyncReadDataStage(i, buffer, index, length, timeout, true, cancellationToken, timeoutToken, out totalBytesRead);
4052 if (readTask == null) {
4053 // Completed synchronously
4054 return Task.FromResult<int>(totalBytesRead);
4062 return ContinueRetryable(moreFunc);
4066 return InvokeRetryable(moreFunc, source, timeoutCancellationSource);
4069 // We're already at the correct column, just read the data
4072 PrepareAsyncInvocation(useSnapshot: false);
4075 return GetBytesAsyncReadDataStage(i, buffer, index, length, timeout, false, cancellationToken, CancellationToken.None, out bytesRead);
4078 CleanupAfterAsyncInvocation();
4084 private Task<int> GetBytesAsyncReadDataStage(int i, byte[] buffer, int index, int length, int timeout, bool isContinuation, CancellationToken cancellationToken, CancellationToken timeoutToken, out int bytesRead) {
4085 _lastColumnWithDataChunkRead = i;
4086 TaskCompletionSource<int> source = null;
4087 CancellationTokenSource timeoutCancellationSource = null;
4089 // Prepare for stateObj timeout
4090 SetTimeout(_defaultTimeoutMilliseconds);
4092 // Try to read without any continuations (all the data may already be in the stateObj's buffer)
4093 if (!TryGetBytesInternalSequential(i, buffer, index, length, out bytesRead)) {
4094 // This will be the 'state' for the callback
4095 int totalBytesRead = bytesRead;
4097 if (!isContinuation) {
4098 // This is the first async operation which is happening - setup the _currentTask and timeout
4099 source = new TaskCompletionSource<int>();
4100 Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
4101 if (original != null) {
4102 source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4106 // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
4107 if (_cancelAsyncOnCloseToken.IsCancellationRequested) {
4108 source.SetCanceled();
4109 _currentTask = null;
4114 Debug.Assert(timeoutToken == CancellationToken.None, "TimeoutToken is set when GetBytesAsyncReadDataStage is not a continuation");
4116 timeoutCancellationSource = new CancellationTokenSource();
4117 timeoutCancellationSource.CancelAfter(timeout);
4118 timeoutToken = timeoutCancellationSource.Token;
4122 Func<Task, Task<int>> moreFunc = null;
4124 PrepareForAsyncContinuation();
4126 if (cancellationToken.IsCancellationRequested) {
4127 // User requested cancellation
4128 return ADP.CreatedTaskWithCancellation<int>();
4130 else if (timeoutToken.IsCancellationRequested) {
4132 return ADP.CreatedTaskWithException<int>(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout())));
4135 // Prepare for stateObj timeout
4136 SetTimeout(_defaultTimeoutMilliseconds);
4138 int bytesReadThisIteration;
4139 bool result = TryGetBytesInternalSequential(i, buffer, index + totalBytesRead, length - totalBytesRead, out bytesReadThisIteration);
4140 totalBytesRead += bytesReadThisIteration;
4141 Debug.Assert(totalBytesRead <= length, "Read more bytes than required");
4144 return Task.FromResult<int>(totalBytesRead);
4147 return ContinueRetryable(moreFunc);
4152 Task<int> retryTask = ContinueRetryable(moreFunc);
4153 if (isContinuation) {
4154 // Let the caller handle cleanup\completing
4158 // setup for cleanup\completing
4159 retryTask.ContinueWith((t) => CompleteRetryable(t, source, timeoutCancellationSource), TaskScheduler.Default);
4164 if (!isContinuation) {
4165 // If this is the first async op, we need to cleanup
4166 CleanupAfterAsyncInvocation();
4168 // Completed synchronously, return null
4172 public override Task<bool> ReadAsync(CancellationToken cancellationToken) {
4174 Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.ReadAsync|API> %d#", ObjectID);
4178 return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("ReadAsync")));
4181 // If user's token is canceled, return a canceled task
4182 if (cancellationToken.IsCancellationRequested) {
4183 return ADP.CreatedTaskWithCancellation<bool>();
4186 // Check for existing async
4187 if (_currentTask != null) {
4188 return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
4191 // These variables will be captured in moreFunc so that we can skip searching for a row token once one has been read
4192 bool rowTokenRead = false;
4195 // Shortcut, do we have enough data to immediately do the ReadAsync?
4197 // First, check if we can finish reading the current row
4198 // NOTE: If we are in SingleRow mode and we've read that single row (i.e. _haltRead == true), then skip the shortcut
4199 if ((!_haltRead) && ((!_sharedState._dataReady) || (WillHaveEnoughData(_metaData.Length - 1)))) {
4203 _stateObj._shouldHaveEnoughData = true;
4205 if (_sharedState._dataReady) {
4206 // Clean off current row
4207 CleanPartialReadReliable();
4210 // If there a ROW token ready (as well as any metadata for the row)
4211 if (_stateObj.IsRowTokenReady()) {
4212 // Read the ROW token
4213 bool result = TryReadInternal(true, out more);
4214 Debug.Assert(result, "Should not have run out of data");
4216 rowTokenRead = true;
4218 // Sequential mode, nothing left to do
4219 if (IsCommandBehavior(CommandBehavior.SequentialAccess)) {
4220 return ADP.TrueTask;
4222 // For non-sequential, check if we can read the row data now
4223 else if (WillHaveEnoughData(_metaData.Length - 1)) {
4225 result = TryReadColumn(_metaData.Length - 1, setTimeout: true);
4226 Debug.Assert(result, "Should not have run out of data");
4227 return ADP.TrueTask;
4231 // No data left, return
4232 return ADP.FalseTask;
4238 _stateObj._shouldHaveEnoughData = false;
4243 catch (Exception ex) {
4244 if (!ADP.IsCatchableExceptionType(ex)) {
4247 return ADP.CreatedTaskWithException<bool>(ex);
4250 TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
4251 Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
4252 if (original != null) {
4253 source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
4257 // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
4258 if (_cancelAsyncOnCloseToken.IsCancellationRequested) {
4259 source.SetCanceled();
4260 _currentTask = null;
4264 IDisposable registration = null;
4265 if (cancellationToken.CanBeCanceled) {
4266 registration = cancellationToken.Register(_command.CancelIgnoreFailure);
4269 PrepareAsyncInvocation(useSnapshot: true);
4271 Func<Task, Task<bool>> moreFunc = null;
4274 Bid.Trace("<sc.SqlDataReader.ReadAsync> attempt retry %d#\n", ObjectID);
4275 PrepareForAsyncContinuation();
4278 if (rowTokenRead || TryReadInternal(true, out more)) {
4280 // If there are no more rows, or this is Sequential Access, then we are done
4281 if (!more || (_commandBehavior & CommandBehavior.SequentialAccess) == CommandBehavior.SequentialAccess) {
4283 return more ? ADP.TrueTask : ADP.FalseTask;
4286 // First time reading the row token - update the snapshot
4287 if (!rowTokenRead) {
4288 rowTokenRead = true;
4290 PrepareAsyncInvocation(useSnapshot: true);
4293 // if non-sequentialaccess then read entire row before returning
4294 if (TryReadColumn(_metaData.Length - 1, true)) {
4296 return ADP.TrueTask;
4301 return ContinueRetryable(moreFunc);
4304 return InvokeRetryable(moreFunc, source, registration);
4307 Bid.ScopeLeave(ref hscp);
4311 override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationToken) {
4314 CheckHeaderIsReady(columnIndex: i, methodName: "IsDBNullAsync");
4316 catch (Exception ex) {
4317 if (!ADP.IsCatchableExceptionType(ex)) {
4320 return ADP.CreatedTaskWithException<bool>(ex);
4323 // Shortcut - if there are no issues and the data is already read, then just return the value
4324 if ((_sharedState._nextColumnHeaderToRead > i) && (!cancellationToken.IsCancellationRequested) && (_currentTask == null)) {
4327 return data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
4330 // Reader was closed between the CheckHeaderIsReady and accessing _data - throw closed exception
4331 return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("IsDBNullAsync")));
4335 // Throw if there is any current task
4336 if (_currentTask != null) {
4337 return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4340 // If user's token is canceled, return a canceled task
4341 if (cancellationToken.IsCancellationRequested) {
4342 return ADP.CreatedTaskWithCancellation<bool>();
4345 // Shortcut - if we have enough data, then run sync
4347 if (WillHaveEnoughData(i, headerOnly: true)) {
4350 _stateObj._shouldHaveEnoughData = true;
4352 ReadColumnHeader(i);
4353 return _data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
4357 _stateObj._shouldHaveEnoughData = false;
4362 catch (Exception ex) {
4363 if (!ADP.IsCatchableExceptionType(ex)) {
4366 return ADP.CreatedTaskWithException<bool>(ex);
4369 // Setup and check for pending task
4370 TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
4371 Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
4372 if (original != null) {
4373 source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4377 // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
4378 if (_cancelAsyncOnCloseToken.IsCancellationRequested) {
4379 source.SetCanceled();
4380 _currentTask = null;
4384 // Setup cancellations
4385 IDisposable registration = null;
4386 if (cancellationToken.CanBeCanceled) {
4387 registration = cancellationToken.Register(_command.CancelIgnoreFailure);
4391 PrepareAsyncInvocation(useSnapshot: true);
4393 // Setup the retryable function
4394 Func<Task, Task<bool>> moreFunc = null;
4397 PrepareForAsyncContinuation();
4400 if (TryReadColumnHeader(i)) {
4401 return _data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
4404 return ContinueRetryable(moreFunc);
4409 return InvokeRetryable(moreFunc, source, registration);
4413 override public Task<T> GetFieldValueAsync<T>(int i, CancellationToken cancellationToken) {
4416 CheckDataIsReady(columnIndex: i, methodName: "GetFieldValueAsync");
4418 // Shortcut - if there are no issues and the data is already read, then just return the value
4419 if ((!IsCommandBehavior(CommandBehavior.SequentialAccess)) && (_sharedState._nextColumnDataToRead > i) && (!cancellationToken.IsCancellationRequested) && (_currentTask == null)) {
4421 var metaData =_metaData;
4422 if ((data != null) && (metaData != null)) {
4423 return Task.FromResult<T>(GetFieldValueFromSqlBufferInternal<T>(data[i], metaData[i]));
4426 // Reader was closed between the CheckDataIsReady and accessing _data\_metaData - throw closed exception
4427 return ADP.CreatedTaskWithException<T>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("GetFieldValueAsync")));
4430 } catch (Exception ex) {
4431 if (!ADP.IsCatchableExceptionType(ex)) {
4434 return ADP.CreatedTaskWithException<T>(ex);
4437 // Throw if there is any current task
4438 if (_currentTask != null) {
4439 return ADP.CreatedTaskWithException<T>(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4442 // If user's token is canceled, return a canceled task
4443 if (cancellationToken.IsCancellationRequested) {
4444 return ADP.CreatedTaskWithCancellation<T>();
4447 // Shortcut - if we have enough data, then run sync
4449 if (WillHaveEnoughData(i)) {
4452 _stateObj._shouldHaveEnoughData = true;
4454 return Task.FromResult(GetFieldValueInternal<T>(i));
4458 _stateObj._shouldHaveEnoughData = false;
4463 catch (Exception ex) {
4464 if (!ADP.IsCatchableExceptionType(ex)) {
4467 return ADP.CreatedTaskWithException<T>(ex);
4470 // Setup and check for pending task
4471 TaskCompletionSource<T> source = new TaskCompletionSource<T>();
4472 Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
4473 if (original != null) {
4474 source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4478 // Check if cancellation due to close is requested (this needs to be done after setting _currentTask)
4479 if (_cancelAsyncOnCloseToken.IsCancellationRequested) {
4480 source.SetCanceled();
4481 _currentTask = null;
4485 // Setup cancellations
4486 IDisposable registration = null;
4487 if (cancellationToken.CanBeCanceled) {
4488 registration = cancellationToken.Register(_command.CancelIgnoreFailure);
4492 PrepareAsyncInvocation(useSnapshot: true);
4494 // Setup the retryable function
4495 Func<Task, Task<T>> moreFunc = null;
4498 PrepareForAsyncContinuation();
4501 if (TryReadColumn(i, setTimeout: false)) {
4502 return Task.FromResult<T>(GetFieldValueFromSqlBufferInternal<T>(_data[i], _metaData[i]));
4505 return ContinueRetryable(moreFunc);
4510 return InvokeRetryable(moreFunc, source, registration);
4515 internal void CompletePendingReadWithSuccess(bool resetForcePendingReadsToWait) {
4516 var stateObj = _stateObj;
4517 if (stateObj != null) {
4518 stateObj.CompletePendingReadWithSuccess(resetForcePendingReadsToWait);
4522 internal void CompletePendingReadWithFailure(int errorCode, bool resetForcePendingReadsToWait) {
4523 var stateObj = _stateObj;
4524 if (stateObj != null) {
4525 stateObj.CompletePendingReadWithFailure(errorCode, resetForcePendingReadsToWait);
4532 public bool _dataReady;
4533 public bool _haltRead;
4534 public bool _metaDataConsumed;
4535 public bool _browseModeInfoConsumed;
4536 public bool _hasRows;
4537 public ALTROWSTATUS _altRowStatus;
4538 public int _nextColumnDataToRead;
4539 public int _nextColumnHeaderToRead;
4540 public long _columnDataBytesRead;
4541 public long _columnDataBytesRemaining;
4543 public _SqlMetaDataSet _metadata;
4544 public _SqlMetaDataSetCollection _altMetaDataSetCollection;
4545 public MultiPartTableName[] _tableNames;
4547 public SqlSequentialStream _currentStream;
4548 public SqlSequentialTextReader _currentTextReader;
4551 private Task<T> ContinueRetryable<T>(Func<Task, Task<T>> moreFunc) {
4552 // _networkPacketTaskSource could be null if the connection was closed
4553 // while an async invocation was outstanding.
4554 TaskCompletionSource<object> completionSource = _stateObj._networkPacketTaskSource;
4555 if (_cancelAsyncOnCloseToken.IsCancellationRequested || completionSource == null) {
4556 // Cancellation requested due to datareader being closed
4557 TaskCompletionSource<T> source = new TaskCompletionSource<T>();
4558 source.TrySetException(ADP.ExceptionWithStackTrace(ADP.ClosedConnectionError()));
4562 return completionSource.Task.ContinueWith((retryTask) => {
4563 if (retryTask.IsFaulted) {
4564 // Somehow the network task faulted - return the exception
4565 TaskCompletionSource<T> exceptionSource = new TaskCompletionSource<T>();
4566 exceptionSource.TrySetException(retryTask.Exception.InnerException);
4567 return exceptionSource.Task;
4569 else if (!_cancelAsyncOnCloseToken.IsCancellationRequested) {
4570 TdsParserStateObject stateObj = _stateObj;
4571 if (stateObj != null) {
4572 // protect continuations against concurrent
4575 if (_stateObj != null) { // reader not closed while we waited for the lock
4576 if (retryTask.IsCanceled) {
4577 if (_parser != null) {
4578 _parser.State = TdsParserState.Broken; // We failed to respond to attention, we have to quit!
4579 _parser.Connection.BreakConnection();
4580 _parser.ThrowExceptionAndWarning(_stateObj);
4586 return moreFunc(retryTask);
4589 CleanupAfterAsyncInvocation();
4598 // if stateObj is null, or we closed the connection or the connection was already closed,
4599 // then mark this operation as cancelled.
4600 TaskCompletionSource<T> source = new TaskCompletionSource<T>();
4601 source.SetException(ADP.ExceptionWithStackTrace(ADP.ClosedConnectionError()));
4603 }, TaskScheduler.Default).Unwrap();
4607 private Task<T> InvokeRetryable<T>(Func<Task, Task<T>> moreFunc, TaskCompletionSource<T> source, IDisposable objectToDispose = null) {
4611 task = moreFunc(null);
4613 catch (Exception ex) {
4614 task = ADP.CreatedTaskWithException<T>(ex);
4617 if (task.IsCompleted) {
4618 // If we've completed sync, then don't bother handling the TaskCompletionSource - we'll just return the completed task
4619 CompleteRetryable(task, source, objectToDispose);
4623 task.ContinueWith((t) => CompleteRetryable(t, source, objectToDispose), TaskScheduler.Default);
4626 catch (AggregateException e) {
4627 source.TrySetException(e.InnerException);
4629 catch (Exception e) {
4630 source.TrySetException(e);
4633 // Fall through for exceptions\completing async
4637 private void CompleteRetryable<T>(Task<T> task, TaskCompletionSource<T> source, IDisposable objectToDispose) {
4638 if (objectToDispose != null) {
4639 objectToDispose.Dispose();
4642 // If something has forced us to switch to SyncOverAsync mode while in an async task then we need to guarantee that we do the cleanup
4643 // This avoids us replaying non-replayable data (such as DONE or ENV_CHANGE tokens)
4644 var stateObj = _stateObj;
4645 bool ignoreCloseToken = ((stateObj != null) && (stateObj._syncOverAsync));
4646 CleanupAfterAsyncInvocation(ignoreCloseToken);
4648 Task current = Interlocked.CompareExchange(ref _currentTask, null, source.Task);
4649 Debug.Assert(current == source.Task, "Should not be able to change the _currentTask while an asynchronous operation is pending");
4651 if (task.IsFaulted) {
4652 Exception e = task.Exception.InnerException;
4653 source.TrySetException(e);
4655 else if (task.IsCanceled) {
4656 source.TrySetCanceled();
4659 source.TrySetResult(task.Result);
4663 private void PrepareAsyncInvocation(bool useSnapshot) {
4664 // if there is already a snapshot, then the previous async command
4665 // completed with exception or cancellation. We need to continue
4666 // with the old snapshot.
4668 Debug.Assert(!_stateObj._asyncReadWithoutSnapshot, "Can't prepare async invocation with snapshot if doing async without snapshots");
4670 if (_snapshot == null) {
4671 _snapshot = new Snapshot {
4672 _dataReady = _sharedState._dataReady,
4673 _haltRead = _haltRead,
4674 _metaDataConsumed = _metaDataConsumed,
4675 _browseModeInfoConsumed = _browseModeInfoConsumed,
4676 _hasRows = _hasRows,
4677 _altRowStatus = _altRowStatus,
4678 _nextColumnDataToRead = _sharedState._nextColumnDataToRead,
4679 _nextColumnHeaderToRead = _sharedState._nextColumnHeaderToRead,
4680 _columnDataBytesRead = _columnDataBytesRead,
4681 _columnDataBytesRemaining = _sharedState._columnDataBytesRemaining,
4683 // _metadata and _altaMetaDataSetCollection must be Cloned
4684 // before they are updated
4685 _metadata = _metaData,
4686 _altMetaDataSetCollection = _altMetaDataSetCollection,
4687 _tableNames = _tableNames,
4689 _currentStream = _currentStream,
4690 _currentTextReader = _currentTextReader,
4693 _stateObj.SetSnapshot();
4697 Debug.Assert(_snapshot == null, "Can prepare async invocation without snapshot if there is currently a snapshot");
4698 _stateObj._asyncReadWithoutSnapshot = true;
4701 _stateObj._syncOverAsync = false;
4702 _stateObj._executionContext = ExecutionContext.Capture();
4705 private void CleanupAfterAsyncInvocation(bool ignoreCloseToken = false) {
4706 var stateObj = _stateObj;
4707 if (stateObj != null) {
4708 // If close requested cancellation and we have a snapshot, then it will deal with cleaning up
4709 // NOTE: There are some cases where we wish to ignore the close token, such as when we've read some data that is not replayable (e.g. DONE or ENV_CHANGE token)
4710 if ((ignoreCloseToken) || (!_cancelAsyncOnCloseToken.IsCancellationRequested) || (stateObj._asyncReadWithoutSnapshot)) {
4711 // Prevent race condition between the DataReader being closed (e.g. when another MARS thread has an error)
4713 if (_stateObj != null) { // reader not closed while we waited for the lock
4714 CleanupAfterAsyncInvocationInternal(_stateObj);
4715 Debug.Assert(_snapshot == null && !_stateObj._asyncReadWithoutSnapshot, "Snapshot not null or async without snapshot still enabled after cleaning async state");
4722 // This function is called directly if calling function already closed the reader, so _stateObj is null,
4723 // in other cases parameterless version should be called
4724 private void CleanupAfterAsyncInvocationInternal(TdsParserStateObject stateObj, bool resetNetworkPacketTaskSource = true)
4726 if (resetNetworkPacketTaskSource) {
4727 stateObj._networkPacketTaskSource = null;
4729 stateObj.ResetSnapshot();
4730 stateObj._syncOverAsync = true;
4731 stateObj._executionContext = null;
4732 stateObj._asyncReadWithoutSnapshot = false;
4734 stateObj._permitReplayStackTraceToDiffer = false;
4737 // We are setting this to null inside the if-statement because stateObj==null means that the reader hasn't been initialized or has been closed (either way _snapshot should already be null)
4741 private void PrepareForAsyncContinuation() {
4742 Debug.Assert(((_snapshot != null) || (_stateObj._asyncReadWithoutSnapshot)), "Can not prepare for an async continuation if no async if setup");
4743 if (_snapshot != null) {
4744 _sharedState._dataReady = _snapshot._dataReady;
4745 _haltRead = _snapshot._haltRead;
4746 _metaDataConsumed = _snapshot._metaDataConsumed;
4747 _browseModeInfoConsumed = _snapshot._browseModeInfoConsumed;
4748 _hasRows = _snapshot._hasRows;
4749 _altRowStatus = _snapshot._altRowStatus;
4750 _sharedState._nextColumnDataToRead = _snapshot._nextColumnDataToRead;
4751 _sharedState._nextColumnHeaderToRead = _snapshot._nextColumnHeaderToRead;
4752 _columnDataBytesRead = _snapshot._columnDataBytesRead;
4753 _sharedState._columnDataBytesRemaining = _snapshot._columnDataBytesRemaining;
4755 _metaData = _snapshot._metadata;
4756 _altMetaDataSetCollection = _snapshot._altMetaDataSetCollection;
4757 _tableNames = _snapshot._tableNames;
4759 _currentStream = _snapshot._currentStream;
4760 _currentTextReader = _snapshot._currentTextReader;
4762 _stateObj.PrepareReplaySnapshot();
4765 _stateObj._executionContext = ExecutionContext.Capture();
4768 private void SwitchToAsyncWithoutSnapshot() {
4769 Debug.Assert(_snapshot != null, "Should currently have a snapshot");
4770 Debug.Assert(_stateObj != null && !_stateObj._asyncReadWithoutSnapshot, "Already in async without snapshot");
4773 _stateObj.ResetSnapshot();
4774 _stateObj._asyncReadWithoutSnapshot = true;