ed353411d0d2c3421f822acfc827123d5853592d
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlDataReader.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlDataReader.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.SqlClient {
10     using System;
11     using System.Collections;
12     using System.Collections.Specialized;
13     using System.ComponentModel;
14     using System.Data;
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;
21     using System.IO;
22     using System.Reflection;
23     using System.Runtime.CompilerServices;
24     using System.Threading;
25     using System.Xml;
26     
27     using Microsoft.SqlServer.Server;
28     using System.Threading.Tasks;
29
30     public class SqlDataReader : DbDataReader, IDataReader {
31
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)
36         }
37
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
43         }
44
45         internal SharedState _sharedState = new SharedState();
46
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;
62
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
67
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;
73
74         private  static int   _objectTypeCount; // Bid counter
75         internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
76
77         // context
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
81
82         // metadata (no explicit table, use 'Table')
83         private MultiPartTableName[] _tableNames = null;
84         private string               _resetOptionsString;
85
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 
91
92         private Task _currentTask;
93         private Snapshot _snapshot;
94         private CancellationTokenSource _cancelAsyncOnCloseTokenSource;
95         private CancellationToken _cancelAsyncOnCloseToken;
96
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);
100
101         private SqlSequentialStream _currentStream;
102         private SqlSequentialTextReader _currentTextReader;
103         
104         internal SqlDataReader(SqlCommand command, CommandBehavior behavior)
105         {
106             SqlConnection.VerifyExecutePermission();
107             
108             _command = command;
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;
116                 }
117             }
118             _sharedState._dataReady = false;
119             _metaDataConsumed = false;
120             _hasRows = false;
121             _browseModeInfoConsumed = false;
122             _currentStream = null;
123             _currentTextReader = null;
124             _cancelAsyncOnCloseTokenSource = new CancellationTokenSource();
125             _cancelAsyncOnCloseToken = _cancelAsyncOnCloseTokenSource.Token;
126             _columnDataCharsIndex = -1;
127         }
128         
129         internal bool BrowseModeInfoConsumed {
130             set {
131                 _browseModeInfoConsumed = value;
132             }
133         }
134
135         internal SqlCommand Command {
136             get {
137                 return _command;
138             }
139         }
140
141         protected SqlConnection Connection {
142             get {
143                 return _connection;
144             }
145         }
146
147         override public int Depth {
148             get {
149                 if (this.IsClosed) {
150                     throw ADP.DataReaderClosed("Depth");
151                 }
152
153                 return 0;
154             }
155         }
156
157         // fields/attributes collection
158         override public int FieldCount {
159             get {
160                 if (this.IsClosed) {
161                     throw ADP.DataReaderClosed("FieldCount");
162                 }
163                 if (_currentTask != null) {
164                     throw ADP.AsyncOperationPending();
165                 }
166
167                 if (MetaData == null) {
168                     return 0;
169                 }
170
171                 return _metaData.Length;
172             }
173         }
174
175         override public bool HasRows {
176             get {
177                 if (this.IsClosed) {
178                     throw ADP.DataReaderClosed("HasRows");
179                 }
180                 if (_currentTask != null) {
181                     throw ADP.AsyncOperationPending();
182                 }
183
184                 return _hasRows;
185             }
186         }
187
188         override public bool IsClosed {
189             get {
190                 return _isClosed;
191             }
192         }
193
194         internal bool IsInitialized {
195             get {
196                 return _isInitialized;
197             }
198             set {
199                 Debug.Assert(value, "attempting to uninitialize a data reader?");
200                 _isInitialized = value;
201             }
202         }
203
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);
209             }
210
211             return _sharedState._columnDataBytesRemaining;
212         }
213
214         internal _SqlMetaDataSet MetaData {
215             get {
216                 if (IsClosed) {
217                     throw ADP.DataReaderClosed("MetaData");
218                 }
219                 // metaData comes in pieces: colmetadata, tabname, colinfo, etc
220                 // if we have any metaData, return it.  If we have none,
221                 // then fetch it
222                 if (_metaData == null && !_metaDataConsumed) {
223                     if (_currentTask != null) {
224                         throw SQL.PendingBeginXXXExists();
225                     }
226
227                     RuntimeHelpers.PrepareConstrainedRegions();
228                     try {
229 #if DEBUG
230                         TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
231
232                         RuntimeHelpers.PrepareConstrainedRegions();
233                         try {
234                             tdsReliabilitySection.Start();
235 #else
236                         {
237 #endif //DEBUG
238
239                             Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
240                             if (!TryConsumeMetaData())
241                             {
242                                 throw SQL.SynchronousCallMayNotPend();
243                             }
244                         }
245 #if DEBUG
246                         finally {
247                             tdsReliabilitySection.Stop();
248                         }
249 #endif //DEBUG
250                     }
251                     catch (System.OutOfMemoryException e) {
252                         _isClosed = true;
253                         if (null != _connection) {
254                             _connection.Abort(e);
255                         }
256                         throw;
257                     }
258                     catch (System.StackOverflowException e) {
259                         _isClosed = true;
260                         if (null != _connection) {
261                             _connection.Abort(e);
262                         }
263                         throw;
264                     }
265                     catch (System.Threading.ThreadAbortException e)  {
266                         _isClosed = true;
267                         if (null != _connection) {
268                             _connection.Abort(e);
269                         }
270                         throw;
271                     }
272                 }
273                 return _metaData;
274             }
275         }
276
277         internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() {
278             SmiExtendedMetaData[] metaDataReturn = null;
279             _SqlMetaDataSet metaData = this.MetaData;
280
281             if ( null != metaData && 0 < metaData.Length ) {
282                 metaDataReturn = new SmiExtendedMetaData[metaData.visibleColumns];
283
284                 for( int index=0; index < metaData.Length; index++ ) {
285                     _SqlMetaData colMetaData = metaData[index];
286
287                     if ( !colMetaData.isHidden ) {
288                         SqlCollation collation = colMetaData.collation;
289
290                         string typeSpecificNamePart1 = null;
291                         string typeSpecificNamePart2 = null;
292                         string typeSpecificNamePart3 = null;
293
294                         if (SqlDbType.Xml == colMetaData.type) {
295                             typeSpecificNamePart1 = colMetaData.xmlSchemaCollectionDatabase;
296                             typeSpecificNamePart2 = colMetaData.xmlSchemaCollectionOwningSchema;
297                             typeSpecificNamePart3 = colMetaData.xmlSchemaCollectionName;
298                         }
299                         else if (SqlDbType.Udt == colMetaData.type) {
300                             Connection.CheckGetExtendedUDTInfo(colMetaData, true);    // SQLBUDT #370593 ensure that colMetaData.udtType is set
301
302                             typeSpecificNamePart1 = colMetaData.udtDatabaseName;
303                             typeSpecificNamePart2 = colMetaData.udtSchemaName;
304                             typeSpecificNamePart3 = colMetaData.udtTypeName;
305                         }
306
307                         int length = colMetaData.length;
308                         if ( length > TdsEnums.MAXSIZE ) {
309                             length = (int) SmiMetaData.UnlimitedMaxLengthIndicator;
310                         }
311                         else if (SqlDbType.NChar == colMetaData.type
312                                 ||SqlDbType.NVarChar == colMetaData.type) {
313                             length /= ADP.CharSize;
314                         }
315
316                         metaDataReturn[index] = new SmiQueryMetaData( 
317                                                         colMetaData.type, 
318                                                         length,
319                                                         colMetaData.precision, 
320                                                         colMetaData.scale, 
321                                                         (null != collation) ? collation.LCID : _defaultLCID,
322                                                         (null != collation) ? collation.SqlCompareOptions : SqlCompareOptions.None,
323                                                         colMetaData.udtType, 
324                                                         false,  // isMultiValued
325                                                         null,   // fieldmetadata
326                                                         null,   // extended properties
327                                                         colMetaData.column, 
328                                                         typeSpecificNamePart1, 
329                                                         typeSpecificNamePart2, 
330                                                         typeSpecificNamePart3,
331                                                         colMetaData.isNullable,
332                                                         colMetaData.serverName,
333                                                         colMetaData.catalogName,
334                                                         colMetaData.schemaName,
335                                                         colMetaData.tableName,
336                                                         colMetaData.baseColumn,
337                                                         colMetaData.isKey,
338                                                         colMetaData.isIdentity,
339                                                         0==colMetaData.updatability,
340                                                         colMetaData.isExpression,
341                                                         colMetaData.isDifferentName,
342                                                         colMetaData.isHidden
343                                                         );
344                     }
345                 }
346             }
347
348             return metaDataReturn;
349         }
350
351         override public int RecordsAffected {
352             get {
353                 if (null != _command)
354                     return _command.InternalRecordsAffected;
355
356                 // cached locally for after Close() when command is nulled out
357                 return _recordsAffected;
358             }
359         }
360
361         internal string ResetOptionsString {
362             set {
363                 _resetOptionsString = value;
364             }
365         }
366
367         private SqlStatistics Statistics {
368             get {
369                 return _statistics;
370             }
371         }
372
373         internal MultiPartTableName[] TableNames {
374             get {
375                 return _tableNames;
376             }
377             set {
378                 _tableNames = value;
379             }
380         }
381
382         override public int VisibleFieldCount {
383             get {
384                 if (this.IsClosed) {
385                     throw ADP.DataReaderClosed("VisibleFieldCount");
386                 }
387                 _SqlMetaDataSet md = this.MetaData;
388                 if (md == null) {
389                     return 0;
390                 }
391                 return (md.visibleColumns);
392             }
393         }
394
395         // this operator
396         override public object this[int i] {
397             get {
398                 return GetValue(i);
399             }
400         }
401
402         override public object this[string name] {
403             get {
404                 return GetValue(GetOrdinal(name));
405             }
406         }
407
408         internal void Bind(TdsParserStateObject stateObj) {
409             Debug.Assert(null != stateObj, "null stateobject");
410
411             Debug.Assert(null == _snapshot, "Should not change during execution of asynchronous command");
412
413             stateObj.Owner = this;
414             _stateObj    = stateObj;
415             _parser      = stateObj.Parser;
416             _defaultLCID = _parser.DefaultLCID;
417         }
418
419         // Fills in a schema table with meta data information.  This function should only really be called by
420         // 
421
422         internal DataTable BuildSchemaTable() {
423             _SqlMetaDataSet md = this.MetaData;
424             Debug.Assert(null != md, "BuildSchemaTable - unexpected null metadata information");
425
426             DataTable schemaTable = new DataTable("SchemaTable");
427             schemaTable.Locale = CultureInfo.InvariantCulture;
428             schemaTable.MinimumCapacity = md.Length;
429
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));
435
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));
440
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));
445
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));
450
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));
455
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));
467             // SparseColumnSet
468             DataColumn IsColumnSet                      = new DataColumn("IsColumnSet",                                      typeof(System.Boolean));
469
470             Ordinal.DefaultValue = 0;
471             IsLong.DefaultValue = false;
472
473             DataColumnCollection columns = schemaTable.Columns;
474
475             // must maintain order for backward compatibility
476             columns.Add(ColumnName);
477             columns.Add(Ordinal);
478             columns.Add(Size);
479             columns.Add(Precision);
480             columns.Add(Scale);
481             columns.Add(IsUnique);
482             columns.Add(IsKey);
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);
497             columns.Add(IsLong);
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);
507
508             for (int i = 0; i < md.Length; i++) {
509                 _SqlMetaData col = md[i];
510                 DataRow schemaRow = schemaTable.NewRow();
511
512                 schemaRow[ColumnName] = col.column;
513                 schemaRow[Ordinal]    = col.ordinal;
514                 //
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
517                 //
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;
522                 }
523                 else {
524                     schemaRow[Size] = (col.metaType.IsSizeInCharacters && (col.length != 0x7fffffff)) ? (col.length / 2) : col.length;
525                 }
526
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);
531
532                 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType) {
533                     schemaRow[ProviderType] = SqlDbType.NVarChar;
534                     switch (col.type) {
535                         case SqlDbType.Date:
536                             schemaRow[Size] = TdsEnums.WHIDBEY_DATE_LENGTH;
537                             break;
538                         case SqlDbType.Time:
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];
541                             break;
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];
545                             break;
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];
549                             break;
550                     }
551                 }
552                 else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsLargeUdt) {
553                     if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
554                         schemaRow[ProviderType] = SqlDbType.VarBinary;
555                     }
556                     else {
557                         // TypeSystem.SQLServer2000
558                         schemaRow[ProviderType] = SqlDbType.Image;
559                     }
560                 }
561                 else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
562                     // TypeSystem.SQLServer2005 and above
563
564                     // SqlDbType enum value - always the actual type for SQLServer2005.
565                     schemaRow[ProviderType] = (int) (col.cipherMD != null ? col.baseTI.type : col.type);
566
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;
570                     }
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;
576                     }
577                 }
578                 else {
579                     // TypeSystem.SQLServer2000
580             
581                     // SqlDbType enum value - variable for certain types when SQLServer2000.
582                     schemaRow[ProviderType] = GetVersionedMetaType(col.metaType).SqlDbType; 
583                 }
584     
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;
589                     }
590                     else {
591                         schemaRow[Precision] = col.baseTI.metaType.Precision;
592                     }
593                 }
594                 else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.precision) {
595                     schemaRow[Precision] = col.precision;
596                 }
597                 else {
598                     schemaRow[Precision] = col.metaType.Precision;
599                 }
600
601                 if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && col.IsNewKatmaiDateTimeType) {
602                     schemaRow[Scale] = MetaType.MetaNVarChar.Scale;
603                 }
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;
608                     }
609                     else {
610                         schemaRow[Scale] = col.baseTI.metaType.Scale;
611                     }
612                 }
613                 else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale) {
614                     schemaRow[Scale] = col.scale;
615                 }
616                 else {
617                     schemaRow[Scale] = col.metaType.Scale;
618                 }
619
620                 schemaRow[AllowDBNull] = col.isNullable;
621
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;
628                 }
629
630                 schemaRow[IsIdentity] = col.isIdentity;
631                 schemaRow[IsAutoIncrement] = col.isIdentity;
632
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;
637                 }
638                 else {
639                     schemaRow[IsLong] = col.metaType.IsLong;
640                 }
641
642                 // mark unique for timestamp columns
643                 if (SqlDbType.Timestamp == col.type) {
644                     schemaRow[IsUnique] = true;
645                     schemaRow[IsRowVersion] = true;
646                 }
647                 else {
648                     schemaRow[IsUnique] = false;
649                     schemaRow[IsRowVersion] = false;
650                 }
651
652                 schemaRow[IsReadOnly] = (0 == col.updatability);
653                 schemaRow[IsColumnSet] = col.isColumnSet;
654
655                 if (!ADP.IsEmpty(col.serverName)) {
656                     schemaRow[BaseServerName] = col.serverName;
657                 }
658                 if (!ADP.IsEmpty(col.catalogName)) {
659                     schemaRow[BaseCatalogName] = col.catalogName;
660                 }
661                 if (!ADP.IsEmpty(col.schemaName)) {
662                     schemaRow[BaseSchemaName] = col.schemaName;
663                 }
664                 if (!ADP.IsEmpty(col.tableName)) {
665                     schemaRow[BaseTableName] = col.tableName;
666                 }
667                 if (!ADP.IsEmpty(col.baseColumn)) {
668                     schemaRow[BaseColumnName] = col.baseColumn;
669                 }
670                 else if (!ADP.IsEmpty(col.column)) {
671                     schemaRow[BaseColumnName] = col.column;
672                 }
673
674                 schemaTable.Rows.Add(schemaRow);
675                 schemaRow.AcceptChanges();
676             }
677
678             // mark all columns as readonly
679             foreach(DataColumn column in columns) {
680                 column.ReadOnly = true; // MDAC 70943
681             }
682
683             return schemaTable;
684         }
685
686         internal void Cancel(int objectID) {
687             TdsParserStateObject stateObj = _stateObj;
688             if (null != stateObj) {
689                 stateObj.Cancel(objectID);
690             }
691         }
692
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);
697
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()) {
703                     return false;
704                 }
705             }
706
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
711
712             // Wipe out any Streams or TextReaders
713             if (-1 != _lastColumnWithDataChunkRead) {
714                 CloseActiveSequentialStreamAndTextReader();
715             }
716
717             // i. user called read but didn't fetch anything
718             if (0 == _sharedState._nextColumnHeaderToRead) {
719                 if (!_stateObj.Parser.TrySkipRow(_metaData, _stateObj)) {
720                     return false;
721                 }
722             }
723             else {
724
725                 // iia.  if we still have bytes left from a partially read column, skip
726                 if (!TryResetBlobState()) {
727                     return false;
728                 }
729
730                 // iib.
731                 // now read the remaining values off the wire for this row
732                 if (!_stateObj.Parser.TrySkipRow(_metaData, _sharedState._nextColumnHeaderToRead, _stateObj)) {
733                     return false;
734                 }
735             }
736
737 #if DEBUG
738                 if (_stateObj._pendingData) {
739                     byte token;
740                     if (!_stateObj.TryPeekByte(out token)) {
741                         return false;
742                     }
743
744                     Debug.Assert(TdsParser.IsValidTdsToken(token), string.Format("Invalid token after performing CleanPartialRead: {0,-2:X2}", token));
745                 }
746 #endif            
747             _sharedState._dataReady = false;
748
749             return true;
750         }
751
752         private void CleanPartialReadReliable() {
753             AssertReaderState(requireData: true, permitAsync: false);
754             
755             RuntimeHelpers.PrepareConstrainedRegions();
756             try {
757 #if DEBUG
758                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
759
760                 RuntimeHelpers.PrepareConstrainedRegions();
761                 try {
762                     tdsReliabilitySection.Start();
763 #else
764                 {
765 #endif //DEBUG
766                     bool result = TryCleanPartialRead();
767                     Debug.Assert(result, "Should not pend on sync call");
768                     Debug.Assert(!_sharedState._dataReady, "_dataReady should be cleared");
769                 }
770 #if DEBUG
771                 finally {
772                     tdsReliabilitySection.Stop();
773                 }
774 #endif //DEBUG
775             }
776             catch (System.OutOfMemoryException e) {
777                 _isClosed = true;
778                 if (_connection != null) {
779                     _connection.Abort(e);
780                 }
781                 throw;
782             }
783             catch (System.StackOverflowException e) {
784                 _isClosed = true;
785                 if (_connection != null) {
786                     _connection.Abort(e);
787                 }
788                 throw;
789             }
790             catch (System.Threading.ThreadAbortException e)  {
791                 _isClosed = true;
792                 if (_connection != null) {
793                     _connection.Abort(e);
794                 }
795                 throw;
796             }
797         }
798
799         override public void Close() {
800             SqlStatistics statistics = null;
801             IntPtr hscp;
802             Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.Close|API> %d#", ObjectID);
803             try {
804                 statistics = SqlStatistics.StartTimer(Statistics);
805                 TdsParserStateObject stateObj = _stateObj;
806
807                 // Request that the current task is stopped
808                 _cancelAsyncOnCloseTokenSource.Cancel();
809                 var currentTask = _currentTask;
810                 if ((currentTask != null) && (!currentTask.IsCompleted)) {
811                     try {
812                         // Wait for the task to complete
813                         ((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne();
814
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();
819                         }
820                     }
821                     catch (Exception) {
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();
824                         _isClosed = true;
825
826                         if (stateObj != null) {
827                             lock (stateObj) {
828                                 _stateObj = null;
829                                 _command = null;
830                                 _connection = null;
831                             }
832                         }
833
834                         throw;
835                     }
836                 }
837                 
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();
841
842                 if (stateObj != null) {
843
844                     // protect against concurrent close and cancel
845                     lock (stateObj) {
846                         
847                         if (_stateObj != null ) {  // reader not closed while we waited for the lock
848
849                             // TryCloseInternal will clear out the snapshot when it is done
850                             if (_snapshot != null) {
851 #if DEBUG
852                                 // The stack trace for replays will differ since they weren't captured during close                                
853                                 stateObj._permitReplayStackTraceToDiffer = true;
854 #endif
855                                 PrepareForAsyncContinuation();
856                             }
857
858                             SetTimeout(_defaultTimeoutMilliseconds);
859
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;
863
864                             if (!TryCloseInternal(true /*closeReader*/)) {
865                                 throw SQL.SynchronousCallMayNotPend();
866                             }
867
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
869                         }
870                     }
871                 }
872             }
873             finally {
874                 SqlStatistics.StopTimer(statistics);
875                 Bid.ScopeLeave(ref hscp);
876             }
877         }
878
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;
885             
886             RuntimeHelpers.PrepareConstrainedRegions();            
887             try {
888 #if DEBUG
889                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
890
891                 RuntimeHelpers.PrepareConstrainedRegions();
892                 try {
893                     tdsReliabilitySection.Start();
894 #else
895                 {
896 #endif //DEBUG
897                     if ((!_isClosed) && (parser != null) && (stateObj != null) && (stateObj._pendingData)) {
898
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 ...
904
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));
906
907                             if (_altRowStatus == ALTROWSTATUS.AltRow) {
908                                 _sharedState._dataReady = true;      // set _sharedState._dataReady to not confuse CleanPartialRead
909                             }
910                             _stateObj._internalTimeout = false;
911                             if (_sharedState._dataReady) {
912                                 cleanDataFailed = true;
913                                 if (TryCleanPartialRead()) {
914                                     cleanDataFailed = false;
915                                 }
916                                 else {
917                                     return false;
918                                 }
919                             }
920 #if DEBUG
921                             else {
922                                 byte token;
923                                 if (!_stateObj.TryPeekByte(out token)) {
924                                     return false;
925                                 }
926
927                                 Debug.Assert(TdsParser.IsValidTdsToken(token), string.Format("DataReady is false, but next token is invalid: {0,-2:X2}", token));
928                             }
929 #endif
930
931
932                             bool ignored;
933                             if (!parser.TryRun(RunBehavior.Clean, _command, this, null, stateObj, out ignored)) {
934                                 return false;
935                             }
936                         }
937                     }
938
939                     RestoreServerSettings(parser, stateObj);
940                     return true;
941                 }
942 #if DEBUG
943                 finally {
944                     tdsReliabilitySection.Stop();
945                 }
946 #endif //DEBUG
947             }
948             catch (System.OutOfMemoryException e) {
949                 _isClosed = true;
950                 aborting = true;
951                 if (null != _connection) {
952                     _connection.Abort(e);
953                 }
954                 throw;
955             }
956             catch (System.StackOverflowException e) {
957                 _isClosed = true;
958                 aborting = true;
959                 if (null != _connection) {
960                     _connection.Abort(e);
961                 }
962                 throw;
963             }
964             catch (System.Threading.ThreadAbortException e)  {
965                 _isClosed = true;
966                 aborting = true;
967                 if (null != _connection) {
968                     _connection.Abort(e);
969                 }
970                 throw;
971             }
972             finally {
973                 if (aborting) {
974                     _isClosed = true;
975                     _command = null; // we are done at this point, don't allow navigation to the connection
976                     _connection = null;
977                     _statistics = null;
978                     _stateObj = null;
979                     _parser = null;
980                 }
981                 else if (closeReader) {
982                     bool wasClosed = _isClosed;
983                     _isClosed = true;
984                     _parser = null;
985                     _stateObj = null;
986                     _data = null;
987
988                     if (_snapshot != null) {
989                         CleanupAfterAsyncInvocationInternal(stateObj);
990                     }
991
992                     // SQLBUDT #284712 - Note the order here is extremely important:
993                     //
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.
997                     //
998                     // (2) Next, we ensure that cancellation can no longer happen by
999                     //     calling CloseSession.
1000
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
1003                     }
1004
1005
1006                     RuntimeHelpers.PrepareConstrainedRegions();
1007                     try {
1008 #if DEBUG
1009                         TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1010
1011                         RuntimeHelpers.PrepareConstrainedRegions();
1012                         try {
1013                             tdsReliabilitySection.Start();
1014 #else
1015                         {
1016 #endif //DEBUG
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();
1021                                 }
1022                                 else {
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();
1027                                     }
1028                                 }
1029                             }
1030
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
1032                         }
1033 #if DEBUG
1034                         finally {
1035                             tdsReliabilitySection.Stop();
1036                         }
1037 #endif //DEBUG
1038                     }
1039                     catch (System.OutOfMemoryException e) {
1040                         if (null != _connection) {
1041                             _connection.Abort(e);
1042                         }
1043                         throw;
1044                     }
1045                     catch (System.StackOverflowException e) {
1046                         if (null != _connection) {
1047                             _connection.Abort(e);
1048                         }
1049                         throw;
1050                     }
1051                     catch (System.Threading.ThreadAbortException e)  {
1052                         if (null != _connection) {
1053                             _connection.Abort(e);
1054                         }
1055                         throw;
1056                     }
1057
1058                     // do not retry here
1059                     bool result = TrySetMetaData(null, false);
1060                     Debug.Assert(result, "Should not pend a synchronous request");
1061                     _fieldNameLookup = null;
1062
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) {
1067                             Connection.Close();
1068                         }
1069                     }
1070                     if (_command != null) {
1071                         // cache recordsaffected to be returnable after DataReader.Close();
1072                         _recordsAffected = _command.InternalRecordsAffected;
1073                     }
1074
1075                     _command = null; // we are done at this point, don't allow navigation to the connection
1076                     _connection = null;
1077                     _statistics = null;
1078                 }
1079             }
1080         }
1081
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
1088                 Close();
1089             }
1090             else {
1091                 // Connection is broken - quick cleanup
1092                 // NOTE: This MUST be thread-safe as a broken connection can happen at any time
1093
1094                 var stateObj = _stateObj;
1095                 _isClosed = true;
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());
1103                     }
1104                     if (_snapshot != null) {
1105                         // CleanWire will do cleanup - so we don't really care about the snapshot
1106                         CleanupAfterAsyncInvocationInternal(stateObj, resetNetworkPacketTaskSource: false);
1107                     }
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();
1112                 }
1113             }
1114         }
1115
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();
1128                 }
1129                 bool ignored;
1130                 if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
1131                     return false;
1132                 }
1133                 Debug.Assert(!ignored, "Parser read a row token while trying to read metadata");
1134             }
1135
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) {
1139
1140                 if (_snapshot != null && object.ReferenceEquals(_snapshot._metadata, _metaData)) {
1141                     _metaData = (_SqlMetaDataSet)_metaData.Clone();
1142                 }
1143
1144                 _metaData.visibleColumns = 0;
1145
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;
1150
1151                     if (!(_metaData[i].isHidden)) {
1152                         _metaData.visibleColumns++;
1153                     }
1154                 }
1155                 _metaData.indexMap = indexMap;
1156             }
1157
1158             return true;
1159         }
1160
1161         override public string GetDataTypeName(int i) {
1162             SqlStatistics statistics = null;
1163             try {
1164                 statistics = SqlStatistics.StartTimer(Statistics);
1165                 CheckMetaDataIsReady(columnIndex: i);
1166
1167                 return GetDataTypeNameInternal(_metaData[i]);
1168             }
1169             finally {
1170                 SqlStatistics.StopTimer(statistics);
1171             }
1172         }
1173
1174         private string GetDataTypeNameInternal(_SqlMetaData metaData) {
1175             string dataTypeName = null;
1176
1177             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
1178                 dataTypeName = MetaType.MetaNVarChar.TypeName;
1179             }
1180             else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
1181                 if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
1182                     dataTypeName = MetaType.MetaMaxVarBinary.TypeName;
1183                 }
1184                 else {
1185                     // TypeSystem.SQLServer2000
1186                     dataTypeName = MetaType.MetaImage.TypeName;
1187                 }
1188             }
1189             else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
1190                 // TypeSystem.SQLServer2005 and above
1191
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;
1195                 }
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;
1200                         }
1201                         else {
1202                             dataTypeName = metaData.metaType.TypeName;  
1203                         }
1204                 }
1205             }
1206             else {
1207                 // TypeSystem.SQLServer2000
1208
1209                     dataTypeName = GetVersionedMetaType(metaData.metaType).TypeName;
1210             }
1211
1212             return dataTypeName;
1213         }
1214
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?");
1218
1219             return _data[i].VariantInternalStorageType;
1220         }
1221
1222         override public IEnumerator GetEnumerator() {
1223             return new DbEnumerator(this, IsCommandBehavior(CommandBehavior.CloseConnection));
1224         }
1225         
1226         override public Type GetFieldType(int i) {
1227             SqlStatistics statistics = null;
1228             try {
1229                 statistics = SqlStatistics.StartTimer(Statistics);
1230                 CheckMetaDataIsReady(columnIndex: i);
1231
1232                 return GetFieldTypeInternal(_metaData[i]);
1233             }
1234             finally {
1235                 SqlStatistics.StopTimer(statistics);
1236             }
1237         }
1238
1239         private Type GetFieldTypeInternal(_SqlMetaData metaData) {
1240             Type fieldType = null;
1241
1242             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
1243                 // Return katmai types as string
1244                 fieldType = MetaType.MetaNVarChar.ClassType;
1245             }
1246             else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
1247                 if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
1248                     fieldType = MetaType.MetaMaxVarBinary.ClassType;
1249                 }
1250                 else {
1251                     // TypeSystem.SQLServer2000
1252                     fieldType = MetaType.MetaImage.ClassType;
1253                 }
1254             }
1255             else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
1256                 // TypeSystem.SQLServer2005 and above
1257
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;
1262                 }
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;
1267                     }
1268                     else {
1269                         fieldType = metaData.metaType.ClassType; // Com+ type.
1270                     }
1271                 }
1272             }
1273             else {
1274                 // TypeSystem.SQLServer2000
1275         
1276                 fieldType = GetVersionedMetaType(metaData.metaType).ClassType; // Com+ type.
1277             }    
1278             
1279             return fieldType;
1280         }
1281
1282         virtual internal int GetLocaleId(int i) {
1283             _SqlMetaData sqlMetaData = MetaData[i];
1284             int lcid;
1285             
1286             if (sqlMetaData.collation != null) {
1287                 lcid = sqlMetaData.collation.LCID;
1288             }
1289             else {
1290                 lcid = 0;
1291             }
1292             return lcid;
1293         }
1294         
1295         override public string GetName(int i) {
1296             CheckMetaDataIsReady(columnIndex: i);
1297
1298             Debug.Assert(null != _metaData[i].column, "MDAC 66681");
1299             return _metaData[i].column;
1300         }
1301
1302         override public Type GetProviderSpecificFieldType(int i) {
1303             SqlStatistics statistics = null;
1304             try {
1305                 statistics = SqlStatistics.StartTimer(Statistics);
1306                 CheckMetaDataIsReady(columnIndex: i);
1307
1308                 return GetProviderSpecificFieldTypeInternal(_metaData[i]);
1309             }
1310             finally {
1311                 SqlStatistics.StopTimer(statistics);
1312             }
1313         }
1314
1315         private Type GetProviderSpecificFieldTypeInternal(_SqlMetaData metaData) {
1316             Type providerSpecificFieldType = null;
1317
1318             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
1319                 providerSpecificFieldType = MetaType.MetaNVarChar.SqlType;
1320             }
1321             else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
1322                 if (_typeSystem == SqlConnectionString.TypeSystem.SQLServer2005) {
1323                     providerSpecificFieldType = MetaType.MetaMaxVarBinary.SqlType;
1324                 }
1325                 else {
1326                     // TypeSystem.SQLServer2000
1327                     providerSpecificFieldType = MetaType.MetaImage.SqlType;
1328                 }
1329             }
1330             else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
1331                 // TypeSystem.SQLServer2005 and above
1332
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;
1337                 }
1338                 else {
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.
1344                     }
1345                     else {
1346                         providerSpecificFieldType = metaData.metaType.SqlType; // SqlType type.
1347                     }
1348                 }
1349             }
1350             else {
1351                 // TypeSystem.SQLServer2000
1352         
1353                 providerSpecificFieldType = GetVersionedMetaType(metaData.metaType).SqlType; // SqlType type.
1354             }
1355
1356             return providerSpecificFieldType;
1357         }
1358     
1359         // named field access
1360         override public int GetOrdinal(string name) {
1361             SqlStatistics statistics = null;
1362             try {
1363                 statistics = SqlStatistics.StartTimer(Statistics);
1364                 if (null == _fieldNameLookup) {
1365                     CheckMetaDataIsReady();
1366                     _fieldNameLookup = new FieldNameLookup(this, _defaultLCID);
1367                 }
1368                 return _fieldNameLookup.GetOrdinal(name); // MDAC 71470
1369             }
1370             finally {
1371                 SqlStatistics.StopTimer(statistics);
1372             }
1373         }
1374
1375         override public object GetProviderSpecificValue(int i) {
1376             return GetSqlValue(i);
1377         }
1378
1379         override public int GetProviderSpecificValues(object[] values) {
1380             return GetSqlValues(values);
1381         }
1382
1383         override public DataTable GetSchemaTable() {
1384             SqlStatistics statistics = null;
1385             IntPtr hscp;
1386             Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.GetSchemaTable|API> %d#", ObjectID);
1387             try {
1388                 statistics = SqlStatistics.StartTimer(Statistics);
1389                 if (null == _metaData || null == _metaData.schemaTable) {
1390                     if (null != this.MetaData) {
1391
1392                         _metaData.schemaTable = BuildSchemaTable();
1393                         Debug.Assert(null != _metaData.schemaTable, "No schema information yet!");
1394                         // filter table?
1395                     }
1396                 }
1397                 if (null != _metaData) {
1398                     return _metaData.schemaTable;
1399                 }
1400                 return null;
1401             }
1402             finally {
1403                 SqlStatistics.StopTimer(statistics);
1404                 Bid.ScopeLeave(ref hscp);
1405             }
1406         }
1407
1408         override public bool GetBoolean(int i) {
1409             ReadColumn(i);
1410             return _data[i].Boolean;
1411         }
1412
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");
1417
1418             MetaType mt = _metaData[i].metaType;
1419
1420             // XmlReader only allowed on XML types
1421             if (mt.SqlDbType != SqlDbType.Xml) {
1422                 throw SQL.XmlReaderNotSupportOnColumnType(_metaData[i].column);
1423             }
1424
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);
1430             }
1431             else {
1432                 // Need to call ReadColumn, since we want to access the internal data structures (i.e. SqlBinary) rather than calling anther Get*() method
1433                 ReadColumn(i);
1434
1435                 if (_data[i].IsNull) {
1436                     // A 'null' stream
1437                     return SqlXml.CreateSqlXmlReader(new MemoryStream(new byte[0], writable: false), closeInput: true);
1438                 }
1439                 else {
1440                     // Grab already read data    
1441                     return _data[i].SqlXml.CreateReader();
1442                 }                
1443             }
1444         }
1445
1446         override public Stream GetStream(int i) {
1447             CheckDataIsReady(columnIndex: i, methodName: "GetStream");
1448
1449             // Streaming is not supported on encrypted columns.
1450             if (_metaData[i] != null && _metaData[i].cipherMD != null) {
1451                 throw SQL.StreamNotSupportOnEncryptedColumn(_metaData[i].column);
1452             }
1453
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);
1459             }
1460
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;
1466             }
1467             else {
1468                 // Need to call ReadColumn, since we want to access the internal data structures (i.e. SqlBinary) rather than calling anther Get*() method
1469                 ReadColumn(i);
1470
1471                 byte[] data;
1472                 if (_data[i].IsNull) {
1473                     // A 'null' stream
1474                     data = new byte[0];
1475                 }
1476                 else {
1477                     // Grab already read data    
1478                     data = _data[i].SqlBinary.Value;
1479                 }
1480
1481                 // If non-sequential then we just have a read-only MemoryStream
1482                 return new MemoryStream(data, writable: false);
1483             }
1484         }
1485
1486         override public byte GetByte(int i) {
1487             ReadColumn(i);
1488             return _data[i].Byte;
1489         }
1490
1491         override public long GetBytes(int i, long dataIndex, byte[] buffer, int bufferIndex, int length) {
1492             SqlStatistics statistics = null;
1493             long  cbBytes = 0;
1494
1495             CheckDataIsReady(columnIndex: i, allowPartiallyReadColumn: true, methodName: "GetBytes");
1496
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);
1501             }
1502                       
1503             try {
1504                 statistics = SqlStatistics.StartTimer(Statistics);
1505                 SetTimeout(_defaultTimeoutMilliseconds);
1506                 cbBytes = GetBytesInternal(i, dataIndex, buffer, bufferIndex, length);
1507                 _lastColumnWithDataChunkRead = i;
1508             }
1509             finally {
1510                 SqlStatistics.StopTimer(statistics);
1511             }
1512             return cbBytes;
1513         }
1514
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();
1519             }
1520
1521             long value;
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(); }
1525             return value;
1526         }
1527
1528         private bool TryGetBytesInternal(int i, long dataIndex, byte[] buffer, int bufferIndex, int length, out long remaining) {
1529             remaining = 0;
1530
1531             RuntimeHelpers.PrepareConstrainedRegions();
1532             try {
1533 #if DEBUG
1534                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1535
1536                 RuntimeHelpers.PrepareConstrainedRegions();
1537                 try {
1538                     tdsReliabilitySection.Start();
1539 #else
1540                 {
1541 #endif //DEBUG
1542                     int cbytes = 0;
1543                     AssertReaderState(requireData: true, permitAsync: true, columnIndex: i, enforceSequentialAccess: true);
1544
1545                     // sequential reading
1546                     if (IsCommandBehavior(CommandBehavior.SequentialAccess)) {
1547                         Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has an active Stream or TextReader");
1548
1549                         if (_metaData[i] != null && _metaData[i].cipherMD != null) {
1550                             throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column);
1551                         }
1552
1553                         if (_sharedState._nextColumnHeaderToRead <= i) {
1554                             if (!TryReadColumnHeader(i)) {
1555                                 return false;
1556                             }
1557                         }
1558
1559                         // If data is null, ReadColumnHeader sets the data.IsNull bit.
1560                         if (_data[i] != null && _data[i].IsNull) {
1561                             throw new SqlNullValueException();
1562                         }    
1563
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)) {
1566                             ulong left;
1567                             if (!_parser.TryPlpBytesLeft(_stateObj, out left)) {
1568                                 return false;
1569                             }
1570                             _sharedState._columnDataBytesRemaining = (long)left;
1571                         }
1572
1573                         if (0 == _sharedState._columnDataBytesRemaining) {
1574                             return true; // We've read this column to the end
1575                         }
1576
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);
1581                                 return true;
1582                             }
1583                             remaining = _sharedState._columnDataBytesRemaining;
1584                             return true;
1585                         }
1586                         
1587                         if (dataIndex < 0)
1588                             throw ADP.NegativeParameter("dataIndex");
1589                         
1590                         if (dataIndex < _columnDataBytesRead) {
1591                             throw ADP.NonSeqByteAccess(dataIndex, _columnDataBytesRead, ADP.GetBytes);
1592                         }
1593
1594                         // if the dataIndex is not equal to bytes read, then we have to skip bytes
1595                         long cb = dataIndex - _columnDataBytesRead;
1596
1597                         // if dataIndex is outside of the data range, return 0
1598                         if ((cb > _sharedState._columnDataBytesRemaining) && !_metaData[i].metaType.IsPlp) {
1599                             return true;
1600                         }
1601                         
1602                         // if bad buffer index, throw
1603                         if (bufferIndex < 0 || bufferIndex >= buffer.Length)
1604                             throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
1605
1606                         // if there is not enough room in the buffer for data
1607                         if (length + bufferIndex > buffer.Length)
1608                             throw ADP.InvalidBufferSizeOrIndex(length, bufferIndex);
1609
1610                         if (length < 0)
1611                             throw ADP.InvalidDataLength(length);
1612
1613                         // Skip if needed
1614                         if (cb > 0) {
1615                             if (_metaData[i].metaType.IsPlp) {
1616                                     ulong skipped;
1617                                     if (!_parser.TrySkipPlpValue((ulong) cb, _stateObj, out skipped)) {
1618                                         return false;
1619                                     }
1620                                     _columnDataBytesRead += (long) skipped;
1621                             }
1622                             else {
1623                                 if (!_stateObj.TrySkipLongBytes(cb)) {
1624                                     return false;
1625                                 }
1626                                 _columnDataBytesRead += cb;
1627                                 _sharedState._columnDataBytesRemaining -= cb;
1628                             }
1629                         }
1630
1631                         int bytesRead;
1632                         bool result = TryGetBytesInternalSequential(i, buffer, bufferIndex, length, out bytesRead);
1633                         remaining = (int)bytesRead;
1634                         return result;
1635                     }
1636
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
1640                     if (dataIndex < 0)
1641                         throw ADP.NegativeParameter("dataIndex");
1642                     
1643                     if (dataIndex > Int32.MaxValue) {
1644                         throw ADP.InvalidSourceBufferIndex(cbytes, dataIndex, "dataIndex");
1645                     }
1646                     
1647                     int ndataIndex = (int)dataIndex;
1648                     byte[] data;
1649
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;
1656                     }
1657                     else {
1658                         Debug.Assert(_metaData[i].metaType.IsLong, "non long type?");
1659                         Debug.Assert(_metaData[i].metaType.IsCharType, "non-char type?");
1660
1661                         SqlString temp = GetSqlString(i);
1662                         if (_metaData[i].metaType.IsNCharType) {
1663                             data = temp.GetUnicodeBytes();
1664                         }
1665                         else {
1666                             data = temp.GetNonUnicodeBytes();
1667                         }
1668                     }
1669
1670                     cbytes = data.Length;
1671
1672                     // if no buffer is passed in, return the number of characters we have
1673                     if (null == buffer) {
1674                         remaining = cbytes;
1675                         return true;
1676                     }
1677
1678                     // if dataIndex is outside of data range, return 0
1679                     if (ndataIndex < 0 || ndataIndex >= cbytes) {
1680                         return true;
1681                     }
1682                     try {
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;
1687                             else
1688                                 cbytes = length;
1689                         }
1690
1691                         Array.Copy(data, ndataIndex, buffer, bufferIndex, cbytes);
1692                     }
1693                     catch (Exception e) {
1694                         // 
1695                         if (!ADP.IsCatchableExceptionType(e)) {
1696                             throw;
1697                         }
1698                         cbytes = data.Length;
1699
1700                         if (length < 0)
1701                             throw ADP.InvalidDataLength(length);
1702
1703                         // if bad buffer index, throw
1704                         if (bufferIndex < 0 || bufferIndex >= buffer.Length)
1705                             throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
1706
1707                         // if there is not enough room in the buffer for data
1708                         if (cbytes + bufferIndex > buffer.Length)
1709                             throw ADP.InvalidBufferSizeOrIndex(cbytes, bufferIndex);
1710
1711                         throw;
1712                     }
1713
1714                     remaining = cbytes;
1715                     return true;
1716                 }
1717 #if DEBUG
1718                 finally {
1719                     tdsReliabilitySection.Stop();
1720                 }
1721 #endif //DEBUG
1722             }
1723             catch (System.OutOfMemoryException e) {
1724                 _isClosed = true;
1725                 if (null != _connection) {
1726                     _connection.Abort(e);
1727                 }
1728                 throw;
1729             }
1730             catch (System.StackOverflowException e) {
1731                 _isClosed = true;
1732                 if (null != _connection) {
1733                     _connection.Abort(e);
1734                 }
1735                 throw;
1736             }
1737             catch (System.Threading.ThreadAbortException e)  {
1738                 _isClosed = true;
1739                 if (null != _connection) {
1740                     _connection.Abort(e);
1741                 }
1742                 throw;
1743             }
1744         }
1745
1746         internal int GetBytesInternalSequential(int i, byte[] buffer, int index, int length, long? timeoutMilliseconds = null) {
1747             if (_currentTask != null) {
1748                 throw ADP.AsyncOperationPending();
1749             }
1750
1751             int value;
1752             SqlStatistics statistics = null;
1753             Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
1754             try {
1755                 statistics = SqlStatistics.StartTimer(Statistics);
1756                 SetTimeout(timeoutMilliseconds ?? _defaultTimeoutMilliseconds);
1757
1758                 bool result = TryReadColumnHeader(i);
1759                 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
1760
1761                 result = TryGetBytesInternalSequential(i, buffer, index, length, out value);
1762                 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
1763             }
1764             finally {
1765                 SqlStatistics.StopTimer(statistics);
1766             }
1767             
1768             return value;
1769         }
1770
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");
1781             
1782             bytesRead = 0;
1783
1784             RuntimeHelpers.PrepareConstrainedRegions();
1785             try
1786             {
1787 #if DEBUG
1788                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
1789
1790                 RuntimeHelpers.PrepareConstrainedRegions();
1791                 try {
1792                     tdsReliabilitySection.Start();
1793 #endif //DEBUG
1794                     if ((_sharedState._columnDataBytesRemaining == 0) || (length == 0)) {
1795                         // No data left or nothing requested, return 0
1796                         bytesRead = 0;
1797                         return true;
1798                     }
1799                     else {
1800                         // if plp columns, do partial reads. Don't read the entire value in one shot.
1801                         if (_metaData[i].metaType.IsPlp) {
1802                             // Read in data
1803                             bool result = _stateObj.TryReadPlpBytes(ref buffer, index, length, out bytesRead);
1804                             _columnDataBytesRead += bytesRead;
1805                             if (!result) {
1806                                 return false;
1807                             }
1808
1809                             // Query for number of bytes left
1810                             ulong left;
1811                             if (!_parser.TryPlpBytesLeft(_stateObj, out left)) {
1812                                 _sharedState._columnDataBytesRemaining = -1;
1813                                 return false;
1814                             }
1815                             _sharedState._columnDataBytesRemaining = (long)left;
1816                             return true;
1817                         }
1818                         else {
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;
1824                             return result;
1825                         }
1826                     }
1827 #if DEBUG
1828                 }
1829                 finally {
1830                     tdsReliabilitySection.Stop();
1831                 }
1832 #endif //DEBUG
1833             }
1834             catch (System.OutOfMemoryException e) {
1835                 _isClosed = true;
1836                 if (null != _connection) {
1837                     _connection.Abort(e);
1838                 }
1839                 throw;
1840             }
1841             catch (System.StackOverflowException e) {
1842                 _isClosed = true;
1843                 if (null != _connection) {
1844                     _connection.Abort(e);
1845                 }
1846                 throw;
1847             }
1848             catch (System.Threading.ThreadAbortException e)  {
1849                 _isClosed = true;
1850                 if (null != _connection) {
1851                     _connection.Abort(e);
1852                 }
1853                 throw;
1854             }
1855         }
1856
1857         override public TextReader GetTextReader(int i) {
1858             CheckDataIsReady(columnIndex: i, methodName: "GetTextReader");
1859             
1860             // Xml type is not supported
1861             MetaType mt = null;
1862
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;
1866             }
1867             else {
1868                 mt = _metaData[i].metaType;
1869             }
1870
1871             Debug.Assert(mt != null, @"mt should not be null.");
1872
1873             if (((!mt.IsCharType) && (mt.SqlDbType != SqlDbType.Variant)) || (mt.SqlDbType == SqlDbType.Xml)) {
1874                 throw SQL.TextReaderNotSupportOnColumnType(_metaData[i].column);
1875             }
1876
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);
1881                 }
1882
1883                 System.Text.Encoding encoding;
1884                 if (mt.IsNCharType)
1885                 {
1886                     // NChar types always use unicode
1887                     encoding = SqlUnicodeEncoding.SqlUnicodeEncodingInstance;
1888                 }
1889                 else
1890                 {
1891                     encoding = _metaData[i].encoding;
1892                 }
1893                         
1894                 _currentTextReader = new SqlSequentialTextReader(this, i, encoding);
1895                 _lastColumnWithDataChunkRead = i;
1896                 return _currentTextReader;
1897             }
1898             else {
1899                 // Need to call ReadColumn, since we want to access the internal data structures (i.e. SqlString) rather than calling anther Get*() method
1900                 ReadColumn(i);
1901
1902                 string data;
1903                 if (_data[i].IsNull) {
1904                     // A 'null' stream
1905                     data = string.Empty;
1906                 }
1907                 else {
1908                     // Grab already read data    
1909                     data = _data[i].SqlString.Value;
1910                 }
1911
1912                 // We've already read the data, so just wrap it in a StringReader
1913                 return new StringReader(data);
1914             }
1915         }
1916
1917         [ EditorBrowsableAttribute(EditorBrowsableState.Never) ] // MDAC 69508
1918         override public char GetChar(int i) {
1919             throw ADP.NotSupported();
1920         }
1921
1922         override public long GetChars(int i, long dataIndex, char[] buffer, int bufferIndex, int length) {
1923             SqlStatistics statistics = null;
1924
1925             CheckMetaDataIsReady(columnIndex: i);
1926
1927             if (_currentTask != null) {
1928                 throw ADP.AsyncOperationPending();
1929             }
1930
1931             MetaType mt = null;
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;
1935             }
1936             else {
1937                 mt = _metaData[i].metaType;
1938             }
1939
1940             Debug.Assert(mt != null, "mt should not be null.");
1941
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;
1946             }
1947             else {
1948                 sqlDbType = _metaData[i].type;
1949             }
1950             
1951             try {
1952                 statistics = SqlStatistics.StartTimer(Statistics);
1953                 SetTimeout(_defaultTimeoutMilliseconds);
1954                 if ((mt.IsPlp) &&
1955                     (IsCommandBehavior(CommandBehavior.SequentialAccess)) ) {
1956                     if (length < 0) {
1957                         throw ADP.InvalidDataLength(length);
1958                     }
1959
1960                     if (_metaData[i].cipherMD != null) {
1961                         throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column);
1962                     }
1963
1964                     // if bad buffer index, throw
1965                     if ((bufferIndex < 0) || (buffer != null && bufferIndex >= buffer.Length)) {
1966                         throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
1967                     }
1968                     
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);
1972                     }
1973                     long charsRead = 0;
1974                     if ( sqlDbType == SqlDbType.Xml ) {
1975                         try {
1976                             CheckDataIsReady(columnIndex: i, allowPartiallyReadColumn: true, methodName: "GetChars");
1977                         }
1978                         catch (Exception ex) {
1979                             // Dev11 
1980
1981                             if (ADP.IsCatchableExceptionType(ex)) {
1982                                 throw new TargetInvocationException(ex);
1983                             }
1984                             else {
1985                                 throw;
1986                             }
1987                         }
1988                         charsRead = GetStreamingXmlChars(i, dataIndex, buffer, bufferIndex, length);
1989                     }
1990                     else {
1991                         CheckDataIsReady(columnIndex: i, allowPartiallyReadColumn: true, methodName: "GetChars");
1992                         charsRead = GetCharsFromPlpData(i, dataIndex, buffer, bufferIndex, length);
1993                     }
1994                     _lastColumnWithDataChunkRead = i;
1995                     return charsRead;
1996                 }
1997
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);
2002                 }
2003
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;
2007
2008                     _columnDataChars = s.ToCharArray();
2009                     _columnDataCharsRead = 0;
2010                     _columnDataCharsIndex = i;
2011                 }
2012
2013                 int cchars = _columnDataChars.Length;
2014
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");
2019                 }
2020                 int ndataIndex = (int)dataIndex;
2021
2022                 // if no buffer is passed in, return the number of characters we have
2023                 if (null == buffer)
2024                     return cchars;
2025
2026                 // if dataIndex outside of data range, return 0
2027                 if (ndataIndex < 0 || ndataIndex >= cchars)
2028                     return 0;
2029
2030                 try {
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;
2035                         else
2036                             cchars = length;
2037                     }
2038
2039                     Array.Copy(_columnDataChars, ndataIndex, buffer, bufferIndex, cchars);
2040                     _columnDataCharsRead += cchars;
2041                 }
2042                 catch (Exception e) {
2043                     // 
2044                     if (!ADP.IsCatchableExceptionType(e)) {
2045                         throw;
2046                     }
2047                     cchars = _columnDataChars.Length;
2048
2049                     if (length < 0)
2050                        throw ADP.InvalidDataLength(length);
2051
2052                     // if bad buffer index, throw
2053                     if (bufferIndex < 0 || bufferIndex >= buffer.Length)
2054                         throw ADP.InvalidDestinationBufferIndex(buffer.Length, bufferIndex, "bufferIndex");
2055
2056                     // if there is not enough room in the buffer for data
2057                     if (cchars + bufferIndex > buffer.Length)
2058                         throw ADP.InvalidBufferSizeOrIndex(cchars, bufferIndex);
2059
2060                     throw;
2061                 }
2062
2063                 return cchars;
2064             }
2065             finally {
2066                 SqlStatistics.StopTimer(statistics);
2067             }
2068         }
2069
2070         private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int bufferIndex, int length) {
2071             RuntimeHelpers.PrepareConstrainedRegions();
2072             try {
2073 #if DEBUG
2074                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
2075
2076                 RuntimeHelpers.PrepareConstrainedRegions();
2077                 try {
2078                     tdsReliabilitySection.Start();
2079 #else
2080                 {
2081 #endif //DEBUG
2082                     long cch;
2083
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");
2090                     
2091                     if (!_metaData[i].metaType.IsCharType) {
2092                         throw SQL.NonCharColumn(_metaData[i].column);
2093                     }
2094                     
2095                     if (_sharedState._nextColumnHeaderToRead <= i) {
2096                         ReadColumnHeader(i);
2097                     }
2098                     
2099                     // If data is null, ReadColumnHeader sets the data.IsNull bit.
2100                     if (_data[i] != null && _data[i].IsNull) {
2101                         throw new SqlNullValueException();
2102                     }    
2103
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);
2107                     }
2108                     
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
2112                     if (dataIndex == 0) 
2113                         _stateObj._plpdecoder = null;
2114
2115                     bool isUnicode = _metaData[i].metaType.IsNCharType;
2116
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);
2120                     }
2121
2122                     if (0 == _sharedState._columnDataBytesRemaining) {
2123                         _stateObj._plpdecoder = null;
2124                         return 0; // We've read this column to the end
2125                     }
2126
2127                     // if no buffer is passed in, return the total number of characters or -1
2128                     // 
2129                     if (null == buffer) {
2130                         cch = (long) _parser.PlpBytesTotalLength(_stateObj);
2131                         return (isUnicode && (cch > 0)) ? cch >> 1 : cch;
2132                     }
2133                     if (dataIndex > _columnDataCharsRead) {
2134                         // Skip chars
2135
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; 
2139
2140                         // 
2141
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;
2147                     }
2148                     cch = length;
2149                     
2150                     if (isUnicode) {
2151                         cch = (long) _parser.ReadPlpUnicodeChars(ref buffer, bufferIndex, length, _stateObj);
2152                         _columnDataBytesRead += (cch << 1);
2153                     }
2154                     else {
2155                         cch = (long) _parser.ReadPlpAnsiChars(ref buffer, bufferIndex, length, _metaData[i], _stateObj);
2156                         _columnDataBytesRead += cch << 1;
2157                     }
2158                     _columnDataCharsRead += cch;
2159                     _sharedState._columnDataBytesRemaining = (long)_parser.PlpBytesLeft(_stateObj);
2160                     return cch;
2161                 }
2162 #if DEBUG
2163                 finally {
2164                     tdsReliabilitySection.Stop();
2165                 }
2166 #endif //DEBUG
2167             }
2168             catch (System.OutOfMemoryException e) {
2169                 _isClosed = true;
2170                 if (null != _connection) {
2171                     _connection.Abort(e);
2172                 }
2173                 throw;
2174             }
2175             catch (System.StackOverflowException e) {
2176                 _isClosed = true;
2177                 if (null != _connection) {
2178                     _connection.Abort(e);
2179                 }
2180                 throw;
2181             }
2182             catch (System.Threading.ThreadAbortException e)  {
2183                 _isClosed = true;
2184                 if (null != _connection) {
2185                     _connection.Abort(e);
2186                 }
2187                 throw;
2188             }
2189         }
2190
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;
2196            }
2197             if (_streamingXml == null) {
2198                 localSXml = new SqlStreamingXml(i, this);
2199             }
2200             else {
2201                 localSXml = _streamingXml;
2202             }
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;
2208             }
2209             return cnt;            
2210         }
2211
2212         [ EditorBrowsableAttribute(EditorBrowsableState.Never) ] // MDAC 69508
2213         IDataReader IDataRecord.GetData(int i) {
2214             throw ADP.NotSupported();
2215         }
2216
2217         override public DateTime GetDateTime(int i) {
2218             ReadColumn(i);
2219
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
2224
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
2228                 // To DateTime.
2229                 object temp = (object) _data[i].String;
2230                 dt = (DateTime) temp;
2231             }
2232
2233             return dt;
2234         }
2235
2236         override public Decimal GetDecimal(int i) {
2237             ReadColumn(i);
2238             return _data[i].Decimal;
2239         }
2240
2241         override public double GetDouble(int i) {
2242             ReadColumn(i);
2243             return _data[i].Double;
2244         }
2245
2246         override public float GetFloat(int i) {
2247             ReadColumn(i);
2248             return _data[i].Single;
2249         }
2250
2251         override public Guid GetGuid(int i) {
2252             ReadColumn(i);
2253             return _data[i].SqlGuid.Value;
2254         }
2255
2256         override public Int16 GetInt16(int i) {
2257             ReadColumn(i);
2258             return _data[i].Int16;
2259         }
2260
2261         override public Int32 GetInt32(int i) {
2262             ReadColumn(i);
2263             return _data[i].Int32;
2264         }
2265
2266         override public Int64 GetInt64(int i) {
2267             ReadColumn(i);
2268             return _data[i].Int64;
2269         }
2270
2271         virtual public SqlBoolean GetSqlBoolean(int i) {
2272             ReadColumn(i);
2273             return _data[i].SqlBoolean;
2274         }
2275
2276         virtual public SqlBinary GetSqlBinary(int i) {
2277             ReadColumn(i, setTimeout: true, allowPartiallyReadColumn: true);
2278             return _data[i].SqlBinary;
2279         }
2280
2281         virtual public SqlByte GetSqlByte(int i) {
2282             ReadColumn(i);
2283             return _data[i].SqlByte;
2284         }
2285
2286         virtual public SqlBytes GetSqlBytes(int i) {
2287             ReadColumn(i);
2288             SqlBinary data = _data[i].SqlBinary;
2289             return new SqlBytes(data);
2290         }
2291
2292         virtual public SqlChars GetSqlChars(int i) {
2293             ReadColumn(i);
2294             SqlString data;            
2295             // Convert Katmai types to string
2296             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType)
2297             {
2298                 data = _data[i].KatmaiDateTimeSqlString;
2299             } else {
2300                 data = _data[i].SqlString;
2301             }
2302             return new SqlChars(data);
2303         }
2304
2305         virtual public SqlDateTime GetSqlDateTime(int i) {
2306             ReadColumn(i);
2307             return _data[i].SqlDateTime;
2308         }
2309
2310         virtual public SqlDecimal GetSqlDecimal(int i) {
2311             ReadColumn(i);
2312             return _data[i].SqlDecimal;
2313         }
2314
2315         virtual public SqlGuid GetSqlGuid(int i) {
2316             ReadColumn(i);
2317             return _data[i].SqlGuid;
2318         }
2319
2320         virtual public SqlDouble GetSqlDouble(int i) {
2321             ReadColumn(i);
2322             return _data[i].SqlDouble;
2323         }
2324
2325         virtual public SqlInt16 GetSqlInt16(int i) {
2326             ReadColumn(i);
2327             return _data[i].SqlInt16;
2328         }
2329
2330         virtual public SqlInt32 GetSqlInt32(int i) {
2331             ReadColumn(i);
2332             return _data[i].SqlInt32;
2333         }
2334
2335         virtual public SqlInt64 GetSqlInt64(int i) {
2336             ReadColumn(i);
2337             return _data[i].SqlInt64;
2338         }
2339
2340         virtual public SqlMoney GetSqlMoney(int i) {
2341             ReadColumn(i);
2342             return _data[i].SqlMoney;
2343         }
2344
2345         virtual public SqlSingle GetSqlSingle(int i) {
2346             ReadColumn(i);
2347             return _data[i].SqlSingle;
2348         }
2349
2350         // 
2351         virtual public SqlString GetSqlString(int i) {
2352             ReadColumn(i);
2353
2354             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && _metaData[i].IsNewKatmaiDateTimeType) {
2355                 return _data[i].KatmaiDateTimeSqlString;
2356             }
2357
2358             return _data[i].SqlString;
2359         }
2360
2361         virtual public SqlXml GetSqlXml(int i){
2362             ReadColumn(i);
2363             SqlXml sx = null;
2364
2365             if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
2366                 // TypeSystem.SQLServer2005
2367
2368                 sx = _data[i].IsNull ? SqlXml.Null : _data[i].SqlCachedBuffer.ToSqlXml();
2369             }
2370             else {
2371                 // TypeSystem.SQLServer2000
2372
2373                 // First, attempt to obtain SqlXml value.  If not SqlXml, we will throw the appropriate
2374                 // cast exception.
2375                 sx = _data[i].IsNull ? SqlXml.Null : _data[i].SqlCachedBuffer.ToSqlXml();
2376
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
2380                 // To SqlXml.
2381                 object temp = (object) _data[i].String;
2382                 sx = (SqlXml) temp;
2383             }            
2384
2385             return sx;
2386         }
2387
2388         virtual public object GetSqlValue(int i) {
2389             SqlStatistics statistics = null;
2390             try {
2391                 statistics = SqlStatistics.StartTimer(Statistics);
2392
2393                 SetTimeout(_defaultTimeoutMilliseconds);
2394                 return GetSqlValueInternal(i);
2395             }
2396             finally {
2397                 SqlStatistics.StopTimer(statistics);
2398             }
2399         }
2400
2401         private object GetSqlValueInternal(int i) {
2402             if (_currentTask != null) {
2403                 throw ADP.AsyncOperationPending();
2404             }
2405
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(); }
2409
2410             return GetSqlValueFromSqlBufferInternal(_data[i], _metaData[i]);
2411         }
2412
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) {
2416             // Dev11 
2417
2418             Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty");
2419
2420             // Convert Katmai types to string
2421             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
2422                 return data.KatmaiDateTimeSqlString;
2423             }
2424             else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
2425                 return data.SqlValue;
2426             }
2427             else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
2428                 // TypeSystem.SQLServer2005
2429
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);
2435                     }
2436                     else {
2437                         throw ADP.DataReaderClosed("GetSqlValueFromSqlBufferInternal");
2438                     }
2439                 }
2440                 else {
2441                     return data.SqlValue;
2442                 }
2443             }
2444             else {
2445                 // TypeSystem.SQLServer2000
2446
2447                 if (metaData.type == SqlDbType.Xml) {
2448                     return data.SqlString;
2449                 }
2450                 else {
2451                     return data.SqlValue;
2452                 }
2453             }
2454         }
2455
2456         virtual public int GetSqlValues(object[] values){
2457             SqlStatistics statistics = null;
2458             try {
2459                 statistics = SqlStatistics.StartTimer(Statistics);
2460                 CheckDataIsReady();
2461                 if (null == values) {
2462                     throw ADP.ArgumentNull("values");
2463                 }
2464
2465                 SetTimeout(_defaultTimeoutMilliseconds);
2466
2467                 int copyLen = (values.Length < _metaData.visibleColumns) ? values.Length : _metaData.visibleColumns;
2468
2469                 for (int i = 0; i < copyLen; i++) {
2470                     values[_metaData.indexMap[i]] = GetSqlValueInternal(i);
2471                 }
2472                 return copyLen;
2473             }
2474             finally {
2475                 SqlStatistics.StopTimer(statistics);
2476             }
2477         }
2478
2479         override public string GetString(int i) {
2480             ReadColumn(i);
2481
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;
2485             }
2486
2487             return _data[i].String;
2488         }
2489         
2490         override public T GetFieldValue<T>(int i) {
2491             SqlStatistics statistics = null;
2492             try {
2493                 statistics = SqlStatistics.StartTimer(Statistics);
2494
2495                 SetTimeout(_defaultTimeoutMilliseconds);
2496                 return GetFieldValueInternal<T>(i);
2497             }
2498             finally {
2499                 SqlStatistics.StopTimer(statistics);
2500             }
2501         }
2502
2503         override public object GetValue(int i) {
2504             SqlStatistics statistics = null;
2505             try {
2506                 statistics = SqlStatistics.StartTimer(Statistics);
2507
2508                 SetTimeout(_defaultTimeoutMilliseconds);
2509                 return GetValueInternal(i);
2510             }
2511             finally {
2512                 SqlStatistics.StopTimer(statistics);
2513             }
2514         }
2515
2516         virtual public TimeSpan GetTimeSpan(int i) {
2517             ReadColumn(i);
2518
2519             TimeSpan t = _data[i].Time;
2520
2521             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005) {
2522                 // TypeSystem.SQLServer2005 or less
2523
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
2527                 // To TimeSpan.
2528                 object temp = (object) _data[i].String;
2529                 t = (TimeSpan) temp;
2530             }
2531
2532             return t;
2533         }
2534
2535         virtual public DateTimeOffset GetDateTimeOffset(int i) {
2536             ReadColumn(i);
2537
2538             DateTimeOffset dto = _data[i].DateTimeOffset;
2539
2540             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005) {
2541                 // TypeSystem.SQLServer2005 or less
2542
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;
2549             }
2550
2551             return dto;
2552         }
2553
2554         private object GetValueInternal(int i) {
2555             if (_currentTask != null) {
2556                 throw ADP.AsyncOperationPending();
2557             }
2558
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(); }
2562
2563             return GetValueFromSqlBufferInternal(_data[i], _metaData[i]);
2564         }
2565
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) {
2569             // Dev11 
2570
2571             Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty");
2572
2573             if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsNewKatmaiDateTimeType) {
2574                 if (data.IsNull) {
2575                     return DBNull.Value;
2576                 }
2577                 else {
2578                     return data.KatmaiDateTimeString;
2579                 }
2580             }
2581             else if (_typeSystem <= SqlConnectionString.TypeSystem.SQLServer2005 && metaData.IsLargeUdt) {
2582                 return data.Value;
2583             }
2584             else if (_typeSystem != SqlConnectionString.TypeSystem.SQLServer2000) {
2585                 // TypeSystem.SQLServer2005
2586
2587                 if (metaData.type != SqlDbType.Udt) {
2588                     return data.Value;
2589                 }
2590                 else {
2591                     var connection = _connection;
2592                     if (connection != null) {
2593                         connection.CheckGetExtendedUDTInfo(metaData, true);
2594                         return connection.GetUdtValue(data.Value, metaData, true);
2595                     }
2596                     else {
2597                         throw ADP.DataReaderClosed("GetValueFromSqlBufferInternal");
2598                     }
2599                 }
2600             }
2601             else {
2602                 // TypeSystem.SQLServer2000
2603                 return data.Value;
2604             }
2605         }
2606
2607         private T GetFieldValueInternal<T>(int i) {
2608             if (_currentTask != null) {
2609                 throw ADP.AsyncOperationPending();
2610             }
2611
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(); }
2615
2616             return GetFieldValueFromSqlBufferInternal<T>(_data[i], _metaData[i]);
2617         }
2618
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);
2624
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;
2632                         }
2633                         else {
2634                             rawValue = new SqlString(xmlValue.Value);
2635                         }
2636                     }
2637                 }
2638
2639                 return (T)rawValue;
2640             }
2641             else {
2642                 // Otherwise Its a CLR or non-Nullable UDT
2643                 try {
2644                     return (T)GetValueFromSqlBufferInternal(data, metaData);
2645                 }
2646                 catch (InvalidCastException) {
2647                     if (data.IsNull) {
2648                         // If the value was actually null, then we should throw a SqlNullValue instead
2649                         throw SQL.SqlNullValue();
2650                     }
2651                     else {
2652                         // Legitmate InvalidCast, rethrow
2653                         throw;
2654                     }
2655                 }
2656             }
2657         }
2658
2659         override public int GetValues(object[] values) {
2660             SqlStatistics statistics = null;
2661             bool sequentialAccess = IsCommandBehavior(CommandBehavior.SequentialAccess);
2662
2663             try {
2664                 statistics = SqlStatistics.StartTimer(Statistics);
2665
2666                 if (null == values) {
2667                     throw ADP.ArgumentNull("values");
2668                 }
2669
2670                 CheckMetaDataIsReady();
2671
2672                 int copyLen = (values.Length < _metaData.visibleColumns) ? values.Length : _metaData.visibleColumns;
2673                 int maximumColumn = copyLen - 1;
2674
2675                 SetTimeout(_defaultTimeoutMilliseconds);
2676
2677                 // Temporarily disable sequential access
2678                 _commandBehavior &= ~CommandBehavior.SequentialAccess;
2679
2680                 // Read in all of the columns in one TryReadColumn call
2681                 bool result = TryReadColumn(maximumColumn, setTimeout: false);
2682                 if (!result) { throw SQL.SynchronousCallMayNotPend(); }
2683
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]);
2687
2688                     // If this is sequential access, then we need to wipe the internal buffer
2689                     if ((sequentialAccess) && (i < maximumColumn)) {
2690                         _data[i].Clear();                        
2691                     }
2692                 }
2693
2694                 return copyLen;
2695             }
2696             finally {
2697                 // Restore sequential access
2698                 if (sequentialAccess) {
2699                     _commandBehavior |= CommandBehavior.SequentialAccess;
2700                 }
2701
2702                 SqlStatistics.StopTimer(statistics);
2703             }
2704         }
2705
2706         private MetaType GetVersionedMetaType(MetaType actualMetaType) {
2707             Debug.Assert(_typeSystem == SqlConnectionString.TypeSystem.SQLServer2000, "Should not be in this function under anything else but SQLServer2000");
2708
2709             MetaType metaType = null;
2710
2711             if      (actualMetaType == MetaType.MetaUdt) {
2712                 metaType = MetaType.MetaVarBinary;
2713             }
2714             else if (actualMetaType == MetaType.MetaXml) {
2715                 metaType = MetaType.MetaNText;
2716             }
2717             else if (actualMetaType == MetaType.MetaMaxVarBinary) {
2718                 metaType = MetaType.MetaImage;
2719             }
2720             else if (actualMetaType == MetaType.MetaMaxVarChar) {
2721                 metaType = MetaType.MetaText;
2722             }
2723             else if (actualMetaType == MetaType.MetaMaxNVarChar) {
2724                 metaType = MetaType.MetaNText;
2725             }
2726             else {
2727                 metaType = actualMetaType;
2728             }
2729
2730             return metaType;
2731         }
2732
2733         private bool TryHasMoreResults(out bool moreResults) {
2734             if(null != _parser) {
2735                 bool moreRows;
2736                 if (!TryHasMoreRows(out moreRows)) {
2737                     moreResults = false;
2738                     return false;
2739                 }
2740                 if(moreRows) {
2741                     // When does this happen?  This is only called from NextResult(), which loops until Read() false.
2742                     moreResults = false;
2743                     return true;
2744                 }
2745
2746                 Debug.Assert(null != _command, "unexpected null command from the data reader!");
2747
2748                 while(_stateObj._pendingData) {
2749                     byte token;
2750                     if (!_stateObj.TryPeekByte(out token)) {
2751                         moreResults = false;
2752                         return false;
2753                     }
2754
2755                     switch(token) {
2756                         case TdsEnums.SQLALTROW:
2757                             if(_altRowStatus == ALTROWSTATUS.Null) {
2758                                 // cache the regular metadata
2759                                 _altMetaDataSetCollection.metaDataSet = _metaData;
2760                                 _metaData = null;
2761                             }
2762                             else {
2763                                 Debug.Assert(_altRowStatus == ALTROWSTATUS.Done, "invalid AltRowStatus");
2764                             }
2765                             _altRowStatus = ALTROWSTATUS.AltRow;
2766                             _hasRows = true;
2767                             moreResults = true;
2768                             return true;
2769                         case TdsEnums.SQLROW:
2770                         case TdsEnums.SQLNBCROW:
2771                             // always happens if there is a row following an altrow
2772                             moreResults = true;
2773                             return true;
2774                         
2775                         // VSTFDEVDIV 926281: DONEINPROC case is missing here; we have decided to reject this 
2776
2777
2778                         case TdsEnums.SQLDONE:
2779                             Debug.Assert(_altRowStatus == ALTROWSTATUS.Done || _altRowStatus == ALTROWSTATUS.Null, "invalid AltRowStatus");
2780                             _altRowStatus = ALTROWSTATUS.Null;
2781                             _metaData = null;
2782                             _altMetaDataSetCollection = null;
2783                             moreResults = true;
2784                             return true;
2785                         case TdsEnums.SQLCOLMETADATA:
2786                             moreResults = true;
2787                             return true;
2788                     }
2789
2790                     // Dev11 
2791
2792
2793
2794                     if (_parser.State == TdsParserState.Broken || _parser.State == TdsParserState.Closed) {
2795                         throw ADP.ClosedConnectionError();
2796                     }
2797
2798                     bool ignored;
2799                     if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
2800                         moreResults = false;
2801                         return false;
2802                     }
2803                 }
2804             }
2805             moreResults = false;
2806             return true;
2807         }
2808
2809         private bool TryHasMoreRows(out bool moreRows) {
2810             if (null != _parser) {
2811                 if (_sharedState._dataReady) {
2812                     moreRows = true;
2813                     return true;
2814                 }
2815
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:
2821                         moreRows = true;
2822                         return true;
2823                     case ALTROWSTATUS.Done:
2824                         moreRows = false;
2825                         return true;
2826                 }
2827                 if (_stateObj._pendingData) {
2828                     // Consume error's, info's, done's on HasMoreRows, so user obtains error on Read.
2829                     // Previous 
2830
2831
2832                     // 
2833
2834
2835
2836
2837
2838
2839
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
2842                     byte b;
2843                     if (!_stateObj.TryPeekByte(out b)) {
2844                         moreRows = false;
2845                         return false;
2846                     }
2847                     bool ParsedDoneToken = false;
2848
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 ) ) {
2858
2859                         if (b == TdsEnums.SQLDONE ||
2860                             b == TdsEnums.SQLDONEPROC   ||
2861                             b == TdsEnums.SQLDONEINPROC) {
2862                             ParsedDoneToken = true;
2863                         }
2864
2865                         // Dev11 
2866
2867
2868
2869                         if (_parser.State == TdsParserState.Broken || _parser.State == TdsParserState.Closed) {
2870                             throw ADP.ClosedConnectionError();
2871                         }
2872
2873                         bool ignored;
2874                         if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
2875                             moreRows = false;
2876                             return false;
2877                         }
2878                         if ( _stateObj._pendingData) {
2879                             if (!_stateObj.TryPeekByte(out b)) {
2880                                 moreRows = false;
2881                                 return false;
2882                             }
2883                         }
2884                         else {
2885                             break;
2886                         }
2887                     }
2888
2889                     // Only return true when we are positioned on a row token.
2890                     if (IsRowToken(b)) {
2891                         moreRows = true;
2892                         return true;
2893                     }
2894                 }
2895             }
2896             moreRows = false;
2897             return true;
2898         }
2899         
2900         private bool IsRowToken(byte token) {
2901             return TdsEnums.SQLROW == token || TdsEnums.SQLNBCROW == token;
2902         }
2903
2904         override public bool IsDBNull(int i) {
2905             if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) && ((_sharedState._nextColumnHeaderToRead > i + 1) || (_lastColumnWithDataChunkRead > i))) {
2906                 // 
2907
2908
2909
2910
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);
2913             }
2914             else {
2915                 CheckHeaderIsReady(columnIndex: i, methodName: "IsDBNull");
2916             
2917                 SetTimeout(_defaultTimeoutMilliseconds);
2918
2919                 ReadColumnHeader(i);    // header data only
2920             }
2921
2922             return _data[i].IsNull;
2923         }
2924
2925         protected internal bool IsCommandBehavior(CommandBehavior condition) {
2926             return (condition == (condition & _commandBehavior));
2927         }
2928
2929         override public bool NextResult() {
2930             if (_currentTask != null) {
2931                 throw SQL.PendingBeginXXXExists();
2932             }
2933
2934             bool more;
2935             bool result;
2936
2937             Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
2938             result = TryNextResult(out more);
2939
2940             if (!result) { throw SQL.SynchronousCallMayNotPend(); }
2941             return more;
2942         }
2943
2944         // recordset is automatically positioned on the first result set
2945         private bool TryNextResult(out bool more) {
2946             SqlStatistics statistics = null;
2947             IntPtr hscp;
2948             Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.NextResult|API> %d#", ObjectID);
2949
2950             RuntimeHelpers.PrepareConstrainedRegions();
2951             try {
2952 #if DEBUG
2953                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
2954
2955                 RuntimeHelpers.PrepareConstrainedRegions();
2956                 try {
2957                     tdsReliabilitySection.Start();
2958 #else
2959                 {
2960 #endif //DEBUG
2961                     statistics = SqlStatistics.StartTimer(Statistics);
2962
2963                     SetTimeout(_defaultTimeoutMilliseconds);
2964                     
2965                     if (IsClosed) {
2966                         throw ADP.DataReaderClosed("NextResult");
2967                     }
2968                     _fieldNameLookup = null;
2969
2970                     bool success = false; // WebData 100390
2971                     _hasRows = false; // reset HasRows
2972
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*/)) {
2976                             more = false;
2977                             return false;
2978                         }
2979
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.
2983                         ClearMetaData();
2984                         more = success;
2985                         return true;
2986                     }
2987
2988                     if (null != _parser) {
2989                         // if there are more rows, then skip them, the user wants the next result
2990                         bool moreRows = true;
2991                         while (moreRows) { 
2992                             if (!TryReadInternal(false, out moreRows)) { // don't reset set the timeout value
2993                                 more = false;
2994                                 return false;
2995                             }
2996                         }
2997                     }
2998
2999                     // we may be done, so continue only if we have not detached ourselves from the parser
3000                     if (null != _parser) {
3001                         bool moreResults;
3002                         if (!TryHasMoreResults(out moreResults)) {
3003                             more = false;
3004                             return false;
3005                         }
3006                         if (moreResults) {
3007                             _metaDataConsumed = false;
3008                             _browseModeInfoConsumed = false;
3009
3010                             switch (_altRowStatus) {
3011                                 case ALTROWSTATUS.AltRow:
3012                                     int altRowId;
3013                                     if (!_parser.TryGetAltRowId(_stateObj, out altRowId)) {
3014                                         more = false;
3015                                         return false;
3016                                     }
3017                                     _SqlMetaDataSet altMetaDataSet = _altMetaDataSetCollection.GetAltMetaData(altRowId);
3018                                     if (altMetaDataSet != null) {
3019                                         _metaData = altMetaDataSet;
3020                                     }
3021                                     Debug.Assert ((_metaData != null), "Can't match up altrowmetadata");
3022                                     break;
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;
3028                                     break;
3029                                 default:
3030                                     if (!TryConsumeMetaData()) {
3031                                         more = false;
3032                                         return false;
3033                                     }
3034                                     if (_metaData == null) {
3035                                         more = false;
3036                                         return true;
3037                                     }
3038                                     break;
3039                             }
3040
3041                             success = true;
3042                         }
3043                         else {
3044                             // detach the parser from this reader now
3045                             if (!TryCloseInternal(false /*closeReader*/)) {
3046                                 more = false;
3047                                 return false;
3048                             }
3049
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)) {
3054                                 more = false;
3055                                 return false;
3056                             }
3057                         }
3058                     }
3059                     else {
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
3062                         // thing.
3063                         ClearMetaData();
3064                     }
3065
3066                     more = success;
3067                     return true;
3068                 }
3069 #if DEBUG
3070                 finally {
3071                     tdsReliabilitySection.Stop();
3072                 }
3073 #endif //DEBUG
3074             }
3075             catch (System.OutOfMemoryException e) {
3076                 _isClosed = true;
3077                 if (null != _connection) {
3078                     _connection.Abort(e);
3079                 }
3080                 throw;
3081             }
3082             catch (System.StackOverflowException e) {
3083                 _isClosed = true;
3084                 if (null != _connection) {
3085                     _connection.Abort(e);
3086                 }
3087                 throw;
3088             }
3089             catch (System.Threading.ThreadAbortException e)  {
3090                 _isClosed = true;
3091                 if (null != _connection) {
3092                     _connection.Abort(e);
3093                 }
3094                 throw;
3095             }
3096             finally {
3097                 SqlStatistics.StopTimer(statistics);
3098                 Bid.ScopeLeave(ref hscp);
3099             }
3100         }
3101
3102         // user must call Read() to position on the first row
3103         override public bool Read() {
3104             if (_currentTask != null) {
3105                 throw SQL.PendingBeginXXXExists();
3106             }
3107
3108             bool more;
3109             bool result;
3110
3111             Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
3112             result = TryReadInternal(true, out more);
3113
3114             if (!result) { throw SQL.SynchronousCallMayNotPend(); }
3115             return more;
3116         }
3117
3118         // user must call Read() to position on the first row
3119         private bool TryReadInternal(bool setTimeout, out bool more) {
3120             SqlStatistics statistics = null;
3121             IntPtr hscp;
3122             Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.Read|API> %d#", ObjectID);
3123
3124             RuntimeHelpers.PrepareConstrainedRegions();
3125             try {
3126 #if DEBUG
3127                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3128
3129                 RuntimeHelpers.PrepareConstrainedRegions();
3130                 try {
3131                     tdsReliabilitySection.Start();
3132 #else
3133                 {
3134 #endif //DEBUG
3135                     statistics = SqlStatistics.StartTimer(Statistics);
3136
3137                     if (null != _parser) {
3138                         if (setTimeout) {
3139                             SetTimeout(_defaultTimeoutMilliseconds);
3140                         }
3141                         if (_sharedState._dataReady) {
3142                             if (!TryCleanPartialRead()) {
3143                                 more = false;
3144                                 return false;
3145                             }
3146                         }
3147                         
3148                         // clear out our buffers
3149                         SqlBuffer.Clear(_data);
3150
3151                         _sharedState._nextColumnHeaderToRead = 0;
3152                         _sharedState._nextColumnDataToRead = 0;
3153                         _sharedState._columnDataBytesRemaining = -1; // unknown
3154                         _lastColumnWithDataChunkRead = -1;
3155
3156                         if (!_haltRead) {
3157                             bool moreRows;
3158                             if (!TryHasMoreRows(out moreRows)) {
3159                                 more = false;
3160                                 return false;
3161                             }
3162                             if (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)) {
3168                                             more = false;
3169                                             return false;
3170                                         }
3171                                         if (_sharedState._dataReady) {
3172                                             break;
3173                                         }
3174                                     }
3175                                     else {
3176                                         // ALTROW token and AltrowId are already consumed ...
3177                                         Debug.Assert (_altRowStatus == ALTROWSTATUS.AltRow, "invalid AltRowStatus");
3178                                         _altRowStatus = ALTROWSTATUS.Done;
3179                                         _sharedState._dataReady = true;
3180                                         break;
3181                                     }
3182                                 }
3183                                 if (_sharedState._dataReady) {
3184                                     _haltRead = IsCommandBehavior(CommandBehavior.SingleRow);
3185                                     more = true;
3186                                     return true;
3187                                 }
3188                             }
3189
3190                             if (!_stateObj._pendingData) {
3191                                 if (!TryCloseInternal(false /*closeReader*/)) {
3192                                     more = false;
3193                                     return false;
3194                                 }
3195                             }
3196                         }
3197                         else {
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
3200                             // halt to true
3201                             bool moreRows;
3202                             if (!TryHasMoreRows(out moreRows)) {
3203                                 more = false;
3204                                 return false;
3205                             }
3206                             while (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)) {
3211                                         more = false;
3212                                         return false;
3213                                     }
3214                                 }
3215
3216                                 if (_sharedState._dataReady) {
3217                                     if (!TryCleanPartialRead()) {
3218                                         more = false;
3219                                         return false;
3220                                     }
3221                                 }
3222
3223                                 // clear out our buffers
3224                                 SqlBuffer.Clear(_data);
3225
3226                                 _sharedState._nextColumnHeaderToRead = 0;
3227
3228                                 if (!TryHasMoreRows(out moreRows)) {
3229                                     more = false;
3230                                     return false;
3231                                 }
3232                             }
3233
3234                             // reset haltRead
3235                             _haltRead = false;
3236                          }
3237                     }
3238                     else if (IsClosed) {
3239                         throw ADP.DataReaderClosed("Read");
3240                     }
3241                     more = false;
3242
3243 #if DEBUG
3244                     if ((!_sharedState._dataReady) && (_stateObj._pendingData)) {
3245                         byte token;
3246                         if (!_stateObj.TryPeekByte(out token)) {
3247                             return false;
3248                         }
3249
3250                         Debug.Assert(TdsParser.IsValidTdsToken(token), string.Format("DataReady is false, but next token is invalid: {0,-2:X2}", token));
3251                     }
3252 #endif
3253
3254                     return true;
3255                 }
3256 #if DEBUG
3257                 finally {
3258                     tdsReliabilitySection.Stop();
3259                 }
3260 #endif //DEBUG
3261             }
3262             catch (System.OutOfMemoryException e) {
3263                 _isClosed = true;
3264                 SqlConnection con = _connection;
3265                 if (con != null) {
3266                     con.Abort(e);
3267                 }
3268                 throw;
3269             }
3270             catch (System.StackOverflowException e) {
3271                 _isClosed = true;
3272                 SqlConnection con = _connection;
3273                 if (con != null) {
3274                     con.Abort(e);
3275                 }
3276                 throw;
3277             }
3278             catch (System.Threading.ThreadAbortException e)  {
3279                _isClosed = true;
3280                 SqlConnection con = _connection;
3281                 if (con != null) {
3282                     con.Abort(e);
3283                 }
3284                 throw;
3285             }
3286             finally {
3287                 SqlStatistics.StopTimer(statistics);
3288                 Bid.ScopeLeave(ref hscp);
3289             }
3290         }
3291
3292         private void ReadColumn(int i, bool setTimeout = true, bool allowPartiallyReadColumn = false) {
3293             if (_currentTask != null) {
3294                 throw ADP.AsyncOperationPending();
3295             }
3296
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(); }
3300         }
3301
3302         private bool TryReadColumn(int i, bool setTimeout, bool allowPartiallyReadColumn = false) {
3303             CheckDataIsReady(columnIndex: i, permitAsync: true, allowPartiallyReadColumn: allowPartiallyReadColumn);
3304             
3305             RuntimeHelpers.PrepareConstrainedRegions();
3306             try {
3307 #if DEBUG
3308                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3309
3310                 RuntimeHelpers.PrepareConstrainedRegions();
3311                 try {
3312                     tdsReliabilitySection.Start();
3313 #else
3314                 {
3315 #endif //DEBUG
3316                     Debug.Assert(_sharedState._nextColumnHeaderToRead <= _metaData.Length, "_sharedState._nextColumnHeaderToRead too large");
3317                     Debug.Assert(_sharedState._nextColumnDataToRead <= _metaData.Length, "_sharedState._nextColumnDataToRead too large");
3318
3319                     if (setTimeout) {
3320                         SetTimeout(_defaultTimeoutMilliseconds);
3321                     }
3322                     
3323                     if (!TryReadColumnInternal(i, readHeaderOnly: false)) {
3324                         return false;
3325                     }
3326                     
3327                     Debug.Assert(null != _data[i], " data buffer is null?");
3328                 }
3329 #if DEBUG
3330                 finally {
3331                     tdsReliabilitySection.Stop();
3332                 }
3333 #endif //DEBUG
3334             }
3335             catch (System.OutOfMemoryException e) {
3336                 _isClosed = true;
3337                 if (null != _connection) {
3338                     _connection.Abort(e);
3339                 }
3340                 throw;
3341             }
3342             catch (System.StackOverflowException e) {
3343                 _isClosed = true;
3344                 if (null != _connection) {
3345                     _connection.Abort(e);
3346                 }
3347                 throw;
3348             }
3349             catch (System.Threading.ThreadAbortException e)  {
3350                 _isClosed = true;
3351                 if (null != _connection) {
3352                     _connection.Abort(e);
3353                 }
3354                 throw;
3355             }
3356
3357             return true;
3358         }
3359
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];
3365
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.
3369                     return false;
3370                 }
3371                 _sharedState._columnDataBytesRemaining = 0;
3372             }
3373             _sharedState._nextColumnDataToRead++;
3374             return true;
3375         }
3376
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(); }
3381         }
3382
3383         private bool TryReadColumnHeader(int i) {
3384             if (!_sharedState._dataReady) {
3385                 throw SQL.InvalidRead();
3386             }
3387             RuntimeHelpers.PrepareConstrainedRegions();
3388             try {
3389 #if DEBUG
3390                 TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
3391
3392                 RuntimeHelpers.PrepareConstrainedRegions();
3393                 try {
3394                     tdsReliabilitySection.Start();
3395 #endif //DEBUG
3396                     return TryReadColumnInternal(i, readHeaderOnly: true);
3397 #if DEBUG
3398                 }
3399                 finally {
3400                     tdsReliabilitySection.Stop();
3401                 }
3402 #endif //DEBUG
3403             }
3404             catch (System.OutOfMemoryException e) {
3405                 _isClosed = true;
3406                 if (null != _connection) {
3407                     _connection.Abort(e);
3408                 }
3409                 throw;
3410             }
3411             catch (System.StackOverflowException e) {
3412                 _isClosed = true;
3413                 if (null != _connection) {
3414                     _connection.Abort(e);
3415                 }
3416                 throw;
3417             }
3418             catch (System.Threading.ThreadAbortException e)  {
3419                 _isClosed = true;
3420                 if (null != _connection) {
3421                     _connection.Abort(e);
3422                 }
3423                 throw;
3424             }
3425         }
3426
3427         private bool TryReadColumnInternal(int i, bool readHeaderOnly = false) {
3428             AssertReaderState(requireData: true, permitAsync: true, columnIndex: i);
3429
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();
3435                 }
3436                 // Else we've already read the data, or we're reading the header only
3437                 else {
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 
3443                                                                                                                         //    Due to a 
3444                         "Gone past column, be we have no data stored for it");
3445                     return true;
3446                 }
3447             }
3448
3449             Debug.Assert(_data[i].IsEmpty || _data[i].IsNull, "re-reading column value?");
3450             
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();
3457                 }
3458
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();
3462                 }
3463             }
3464             else if (_sharedState._nextColumnDataToRead < _sharedState._nextColumnHeaderToRead) {
3465                 // We read the header but not the column for the previous column
3466                 if (!TryReadColumnData()) {
3467                     return false;
3468                 }
3469                 Debug.Assert(_sharedState._nextColumnDataToRead == _sharedState._nextColumnHeaderToRead);
3470             }
3471
3472             // if we still have bytes left from the previous blob read, clear the wire and reset
3473             if (!TryResetBlobState()) {
3474                 return false;
3475             }
3476
3477             do {
3478                 _SqlMetaData columnMetaData = _metaData[_sharedState._nextColumnHeaderToRead];
3479
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)) {
3484                         return false;
3485                     }
3486
3487                     _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead;
3488                     _sharedState._nextColumnHeaderToRead++;
3489                 }
3490                 else {
3491                     bool isNull;
3492                     ulong dataLength;
3493                     if (!_parser.TryProcessColumnHeader(columnMetaData, _stateObj, _sharedState._nextColumnHeaderToRead, out isNull, out dataLength)) {
3494                         return false;
3495                     }
3496
3497                     _sharedState._nextColumnDataToRead = _sharedState._nextColumnHeaderToRead;
3498                     _sharedState._nextColumnHeaderToRead++;  // We read this one
3499
3500                     if (isNull && columnMetaData.type != SqlDbType.Timestamp /* Maintain behavior for known */)
3501                     {
3502                         TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], 
3503                             columnMetaData,
3504                             _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting,
3505                             _parser.Connection);
3506                         
3507                         if (!readHeaderOnly) {
3508                             _sharedState._nextColumnDataToRead++;
3509                         }                        
3510                     }
3511                     else {
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.
3519                                 return false;
3520                             }
3521                             _sharedState._nextColumnDataToRead++;
3522                         }
3523                         else {
3524                             _sharedState._columnDataBytesRemaining = (long)dataLength;
3525                         }
3526                     }
3527                 }
3528
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.
3532                     _snapshot = null;
3533                     PrepareAsyncInvocation(useSnapshot: true);
3534                 }
3535             } while (_sharedState._nextColumnHeaderToRead <= i);
3536
3537             return true;
3538         }
3539
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);
3543                        
3544             if ((_lastColumnWithDataChunkRead == _sharedState._nextColumnDataToRead) && (_metaData[_lastColumnWithDataChunkRead].metaType.IsPlp)) {
3545                 // In the middle of reading a Plp - no idea how much is left
3546                 return false;
3547             }
3548
3549             int bytesRemaining = Math.Min(checked(_stateObj._inBytesRead - _stateObj._inBytesUsed), _stateObj._inBytesPacket);
3550
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
3553             bytesRemaining--;
3554             
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
3560                     return false;
3561                 }
3562                 else {
3563                     // Already read the header, so subtract actual data size
3564                     bytesRemaining = checked(bytesRemaining - (int)_sharedState._columnDataBytesRemaining);
3565                 }
3566             }
3567             
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)) {
3571                 // Check NBC first
3572                 if (!_stateObj.IsNullCompressionBitSet(currentColumn)) {
3573
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
3578                         return false;
3579                     }            
3580                     int maxHeaderSize;
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
3585                             maxHeaderSize = 2;
3586                         }
3587                         else if (0 == (_metaData[currentColumn].tdsType & 0x0c)) {
3588                             // UInt32 represents size
3589                             maxHeaderSize = 4;
3590                         }
3591                         else {
3592                             // Byte represents size
3593                             maxHeaderSize = 1;
3594                         }
3595                     }
3596                     else
3597                     {
3598                         maxHeaderSize = 0;
3599                     }
3600
3601                     bytesRemaining = checked(bytesRemaining - maxHeaderSize);
3602                     if ((currentColumn < targetColumn) || (!headerOnly)) {
3603                         bytesRemaining = checked(bytesRemaining - _metaData[currentColumn].length);
3604                     }
3605                 }
3606
3607                 currentColumn++;
3608             }
3609
3610             return (bytesRemaining >= 0);
3611         }
3612
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");
3618
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) {
3623                         ulong ignored;
3624                         if (!_stateObj.Parser.TrySkipPlpValue(UInt64.MaxValue, _stateObj, out ignored)) {
3625                             return false;
3626                         }
3627                     }
3628                     if (_streamingXml != null) {
3629                         SqlStreamingXml localSXml = _streamingXml;
3630                         _streamingXml = null;
3631                         localSXml.Close();
3632                     }
3633                 }
3634                 else if (0 < _sharedState._columnDataBytesRemaining) {
3635                     if (!_stateObj.TrySkipLongBytes(_sharedState._columnDataBytesRemaining)) {
3636                         return false;
3637                     }
3638                 }
3639             }
3640 #if DEBUG
3641             else {
3642                 Debug.Assert((_sharedState._columnDataBytesRemaining == 0 || _sharedState._columnDataBytesRemaining == -1) && _stateObj._longlen == 0, "Haven't read header yet, but column is partially read?");
3643             }
3644 #endif
3645
3646             _sharedState._columnDataBytesRemaining = 0;
3647             _columnDataBytesRead = 0;
3648             _columnDataCharsRead = 0;
3649             _columnDataChars = null;
3650             _columnDataCharsIndex = -1;
3651             _stateObj._plpdecoder = null;
3652
3653             return true;
3654         }
3655
3656         private void CloseActiveSequentialStreamAndTextReader() {
3657             if (_currentStream != null) {
3658                 _currentStream.SetClosed();
3659                 _currentStream = null;
3660             }
3661             if (_currentTextReader != null) {
3662                 _currentTextReader.SetClosed();
3663                 _currentStream = null;
3664             }
3665         }
3666
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");
3676                     
3677                     // must execute this one synchronously as we can't retry
3678                     parser.Run(RunBehavior.UntilDone, _command, this, null, stateObj);
3679                 }
3680                 _resetOptionsString = null;
3681             }
3682         }
3683
3684         internal bool TrySetAltMetaDataSet(_SqlMetaDataSet metaDataSet, bool metaDataConsumed) {
3685             if (_altMetaDataSetCollection == null) {
3686                 _altMetaDataSetCollection = new _SqlMetaDataSetCollection();
3687             } 
3688             else if (_snapshot != null && object.ReferenceEquals(_snapshot._altMetaDataSetCollection, _altMetaDataSetCollection)) {
3689                 _altMetaDataSetCollection = (_SqlMetaDataSetCollection)_altMetaDataSetCollection.Clone();
3690             }
3691             _altMetaDataSetCollection.SetAltMetaData(metaDataSet);
3692             _metaDataConsumed = metaDataConsumed;
3693             if (_metaDataConsumed && null != _parser) {
3694                 byte b;
3695                 if (!_stateObj.TryPeekByte(out b)) {
3696                     return false;
3697                 }
3698                 if (TdsEnums.SQLORDER == b) {
3699                     bool ignored;
3700                     if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, this, null, _stateObj, out ignored)) {
3701                         return false;
3702                     }
3703                     if (!_stateObj.TryPeekByte(out b)) {
3704                         return false;
3705                     }
3706                 }
3707                 if (b == TdsEnums.SQLINFO) {
3708                     try {
3709                         _stateObj._accumulateInfoEvents = true;
3710                         bool ignored;
3711                         if (!_parser.TryRun(RunBehavior.ReturnImmediately, _command, null, null, _stateObj, out ignored)) {
3712                             return false;
3713                         }
3714                     }
3715                     finally {
3716                         _stateObj._accumulateInfoEvents = false;
3717                     }
3718                     if (!_stateObj.TryPeekByte(out b)) {
3719                         return false;
3720                     }
3721                 }
3722                 _hasRows = IsRowToken(b);
3723             }
3724             if (metaDataSet != null) {
3725                 if (_data == null || _data.Length<metaDataSet.Length) {
3726                     _data = SqlBuffer.CreateBufferArray(metaDataSet.Length);
3727                 }
3728             }
3729             return true;
3730         }
3731
3732         private void ClearMetaData() {
3733             _metaData = null;
3734             _tableNames = null;
3735             _fieldNameLookup = null;
3736             _metaDataConsumed = false;
3737             _browseModeInfoConsumed = false;
3738         }
3739
3740         internal bool TrySetMetaData(_SqlMetaDataSet metaData, bool moreInfo) {
3741             _metaData = metaData;
3742
3743             // get rid of cached metadata info as well
3744             _tableNames = null;
3745             if (_metaData != null) {
3746                 _metaData.schemaTable = null;
3747                 _data = SqlBuffer.CreateBufferArray(metaData.Length);
3748             }
3749
3750             _fieldNameLookup = null;
3751
3752             if (null != metaData) {
3753                 // we are done consuming metadata only if there is no moreInfo
3754                 if (!moreInfo) {
3755                     _metaDataConsumed = true;
3756
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
3760                         byte b;
3761                         if (!_stateObj.TryPeekByte(out b)) {
3762                             return false;
3763                         }
3764
3765                         // 
3766
3767
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?
3772 //
3773                             bool ignored;
3774                             if (!_parser.TryRun(RunBehavior.ReturnImmediately, null, null, null, _stateObj, out ignored)) {
3775                                 return false;
3776                             }
3777                             if (!_stateObj.TryPeekByte(out b)) {
3778                                 return false;
3779                             }
3780                         }
3781                         if (b == TdsEnums.SQLINFO)
3782                         {
3783                             // VSTFDEVDIV713926
3784                             // We are accumulating informational events and fire them at next
3785                             // TdsParser.Run purely to avoid breaking change
3786                             try {
3787                                 _stateObj._accumulateInfoEvents = true;
3788                                 bool ignored;
3789                                 if (!_parser.TryRun(RunBehavior.ReturnImmediately, null, null, null, _stateObj, out ignored)) {
3790                                     return false;
3791                                 }
3792                             }
3793                             finally {
3794                                 _stateObj._accumulateInfoEvents = false;
3795                             }                       
3796                             if (!_stateObj.TryPeekByte(out b)) {
3797                                 return false;
3798                             }
3799                         }
3800                         _hasRows = IsRowToken(b);
3801                         if (TdsEnums.SQLALTMETADATA == b)
3802                         {
3803                             _metaDataConsumed = false;
3804                         }
3805                     }
3806                 }
3807             }
3808             else {
3809                 _metaDataConsumed = false;
3810             }
3811
3812             _browseModeInfoConsumed = false;
3813             return true;
3814         }
3815
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);
3822             }
3823         }
3824
3825         private bool HasActiveStreamOrTextReaderOnColumn(int columnIndex) {
3826             bool active = false;
3827
3828             active |= ((_currentStream != null) && (_currentStream.ColumnIndex == columnIndex));
3829             active |= ((_currentTextReader != null) && (_currentTextReader.ColumnIndex == columnIndex));
3830
3831             return active;
3832         }
3833
3834         private void CheckMetaDataIsReady() {
3835             if (_currentTask != null) {
3836                 throw ADP.AsyncOperationPending();
3837             }
3838             if (MetaData == null) {
3839                 throw SQL.InvalidRead();
3840             }
3841         }
3842
3843         private void CheckMetaDataIsReady(int columnIndex, bool permitAsync = false) {
3844             if ((!permitAsync) && (_currentTask != null)) {
3845                 throw ADP.AsyncOperationPending();
3846             }
3847             if (MetaData == null) {
3848                 throw SQL.InvalidRead();
3849             }
3850             if ((columnIndex < 0) || (columnIndex >= _metaData.Length)) {
3851                 throw ADP.IndexOutOfRange();
3852             }
3853         }
3854
3855         private void CheckDataIsReady() {
3856             if (_currentTask != null) {
3857                 throw ADP.AsyncOperationPending();
3858             }
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();
3862             }
3863         }
3864         
3865         private void CheckHeaderIsReady(int columnIndex, bool permitAsync = false, string methodName = null) {
3866             if (_isClosed) {
3867                 throw ADP.DataReaderClosed(methodName ?? "CheckHeaderIsReady");
3868             }
3869             if ((!permitAsync) && (_currentTask != null)) {
3870                 throw ADP.AsyncOperationPending();
3871             }
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();
3875             }
3876             if ((columnIndex < 0) || (columnIndex >= _metaData.Length)) {
3877                 throw ADP.IndexOutOfRange();
3878             }
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));
3882             }
3883         }
3884
3885         private void CheckDataIsReady(int columnIndex, bool allowPartiallyReadColumn = false, bool permitAsync = false, string methodName = null) {
3886             if (_isClosed) {
3887                 throw ADP.DataReaderClosed(methodName ?? "CheckDataIsReady");
3888             }
3889             if ((!permitAsync) && (_currentTask != null)) {
3890                 throw ADP.AsyncOperationPending();
3891             }
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();
3895             }
3896             if ((columnIndex < 0) || (columnIndex >= _metaData.Length)) {
3897                 throw ADP.IndexOutOfRange();
3898             }
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));
3904             }
3905         }
3906
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");
3916             }
3917         }
3918
3919         public override Task<bool> NextResultAsync(CancellationToken cancellationToken) {
3920             IntPtr hscp;
3921             Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.NextResultAsync|API> %d#", ObjectID);
3922
3923             try {
3924                 TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
3925
3926                 if (IsClosed) {
3927                     source.SetException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("NextResultAsync")));
3928                     return source.Task;
3929                 }
3930
3931                 IDisposable registration = null;
3932                 if (cancellationToken.CanBeCanceled) {
3933                     if (cancellationToken.IsCancellationRequested) {
3934                         source.SetCanceled();
3935                         return source.Task;
3936                     }
3937                     registration = cancellationToken.Register(_command.CancelIgnoreFailure);
3938                 }
3939
3940                 Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
3941                 if (original != null) {
3942                     source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
3943                     return source.Task;
3944                 }
3945
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;
3950                     return source.Task;
3951                 }
3952
3953                 PrepareAsyncInvocation(useSnapshot: true);
3954
3955                 Func<Task, Task<bool>> moreFunc = null;
3956
3957                 moreFunc = (t) => {
3958                     if (t != null) {
3959                         Bid.Trace("<sc.SqlDataReader.NextResultAsync> attempt retry %d#\n", ObjectID);
3960                         PrepareForAsyncContinuation();
3961                     }
3962
3963                     bool more;
3964                     if (TryNextResult(out more)) {
3965                         // completed 
3966                         return more ? ADP.TrueTask : ADP.FalseTask;
3967                     }
3968
3969                     return ContinueRetryable(moreFunc);
3970                 };
3971
3972                 return InvokeRetryable(moreFunc, source, registration);
3973             }
3974             finally {
3975                 Bid.ScopeLeave(ref hscp);
3976             }
3977         }
3978
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));
3984
3985             bytesRead = 0;
3986             if (IsClosed) {
3987                 TaskCompletionSource<int> source = new TaskCompletionSource<int>();
3988                 source.SetException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("GetBytesAsync")));
3989                 return source.Task;
3990             }
3991
3992             if (_currentTask != null) {
3993                 TaskCompletionSource<int> source = new TaskCompletionSource<int>();
3994                 source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
3995                 return source.Task;
3996             }
3997                 
3998             if (cancellationToken.CanBeCanceled) {
3999                 if (cancellationToken.IsCancellationRequested) {
4000                     return null;
4001                 }
4002             }
4003
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()));
4011                     return source.Task;
4012                 }
4013
4014                 PrepareAsyncInvocation(useSnapshot: true);
4015
4016                 Func<Task, Task<int>> moreFunc = null;
4017
4018                 // Timeout
4019                 CancellationToken timeoutToken = CancellationToken.None;
4020                 CancellationTokenSource timeoutCancellationSource = null;
4021                 if (timeout > 0) {
4022                     timeoutCancellationSource = new CancellationTokenSource();
4023                     timeoutCancellationSource.CancelAfter(timeout);
4024                     timeoutToken = timeoutCancellationSource.Token;
4025                 }
4026
4027                 moreFunc = (t) => {
4028                     if (t != null) {
4029                         Bid.Trace("<sc.SqlDataReader.GetBytesAsync> attempt retry %d#\n", ObjectID);
4030                         PrepareForAsyncContinuation();
4031                     }
4032
4033                     // Prepare for stateObj timeout
4034                     SetTimeout(_defaultTimeoutMilliseconds);
4035
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)
4038
4039                         if (cancellationToken.IsCancellationRequested) {
4040                             // User requested cancellation
4041                             return ADP.CreatedTaskWithCancellation<int>();
4042                         }
4043                         else if (timeoutToken.IsCancellationRequested) {
4044                             // Timeout
4045                             return ADP.CreatedTaskWithException<int>(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout())));
4046                         }
4047                         else {
4048                             // Upto the correct column - continue to read
4049                             SwitchToAsyncWithoutSnapshot();
4050                             int totalBytesRead;
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);
4055                             }
4056                             else {
4057                                 return readTask;
4058                             }
4059                         }
4060                     }
4061                     else {
4062                         return ContinueRetryable(moreFunc);
4063                     }
4064                 };
4065
4066                 return InvokeRetryable(moreFunc, source, timeoutCancellationSource);
4067             }
4068             else {
4069                 // We're already at the correct column, just read the data
4070
4071                 // Switch to async
4072                 PrepareAsyncInvocation(useSnapshot: false);
4073
4074                 try {
4075                     return GetBytesAsyncReadDataStage(i, buffer, index, length, timeout, false, cancellationToken, CancellationToken.None, out bytesRead);
4076                 }
4077                 catch {
4078                     CleanupAfterAsyncInvocation();
4079                     throw;
4080                 }
4081             }
4082         }
4083
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;
4088             
4089             // Prepare for stateObj timeout
4090             SetTimeout(_defaultTimeoutMilliseconds);
4091
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;
4096
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()));
4103                         return source.Task;
4104                     }
4105
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;
4110                         return source.Task;
4111                     }
4112
4113                     // Timeout
4114                      Debug.Assert(timeoutToken == CancellationToken.None, "TimeoutToken is set when GetBytesAsyncReadDataStage is not a continuation");
4115                     if (timeout > 0) {
4116                         timeoutCancellationSource = new CancellationTokenSource();
4117                         timeoutCancellationSource.CancelAfter(timeout);
4118                         timeoutToken = timeoutCancellationSource.Token;
4119                     }
4120                 }
4121                     
4122                 Func<Task, Task<int>> moreFunc = null;
4123                 moreFunc = (_ => {
4124                     PrepareForAsyncContinuation();
4125
4126                     if (cancellationToken.IsCancellationRequested) {
4127                         // User requested cancellation
4128                         return ADP.CreatedTaskWithCancellation<int>();
4129                     }
4130                     else if (timeoutToken.IsCancellationRequested) {
4131                         // Timeout
4132                         return ADP.CreatedTaskWithException<int>(ADP.ExceptionWithStackTrace(ADP.IO(SQLMessage.Timeout())));
4133                     }
4134                     else {
4135                         // Prepare for stateObj timeout
4136                         SetTimeout(_defaultTimeoutMilliseconds);
4137
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");
4142
4143                         if (result) {
4144                             return Task.FromResult<int>(totalBytesRead);
4145                         }
4146                         else {
4147                             return ContinueRetryable(moreFunc);
4148                         }
4149                     }
4150                 });
4151
4152                 Task<int> retryTask = ContinueRetryable(moreFunc);
4153                 if (isContinuation) {
4154                     // Let the caller handle cleanup\completing
4155                     return retryTask;
4156                 }
4157                 else {
4158                     // setup for cleanup\completing
4159                     retryTask.ContinueWith((t) => CompleteRetryable(t, source, timeoutCancellationSource), TaskScheduler.Default);
4160                     return source.Task;
4161                 }
4162             }
4163
4164             if (!isContinuation) {
4165                 // If this is the first async op, we need to cleanup
4166                 CleanupAfterAsyncInvocation();
4167             }
4168             // Completed synchronously, return null
4169             return null;
4170         }
4171
4172         public override Task<bool> ReadAsync(CancellationToken cancellationToken) {
4173             IntPtr hscp;
4174             Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.ReadAsync|API> %d#", ObjectID);
4175
4176             try {
4177                 if (IsClosed) {
4178                     return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("ReadAsync")));
4179                 }
4180
4181                 // If user's token is canceled, return a canceled task
4182                 if (cancellationToken.IsCancellationRequested) {
4183                     return ADP.CreatedTaskWithCancellation<bool>();
4184                 }
4185
4186                 // Check for existing async
4187                 if (_currentTask != null) {
4188                     return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
4189                 }
4190                 
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;
4193                 bool more = false;
4194
4195                 // Shortcut, do we have enough data to immediately do the ReadAsync?
4196                 try {
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)))) {
4200
4201 #if DEBUG
4202                         try {
4203                             _stateObj._shouldHaveEnoughData = true;
4204 #endif
4205                             if (_sharedState._dataReady) {
4206                                 // Clean off current row
4207                                 CleanPartialReadReliable();
4208                             }
4209
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");
4215
4216                                 rowTokenRead = true;
4217                                 if (more) {
4218                                     // Sequential mode, nothing left to do
4219                                     if (IsCommandBehavior(CommandBehavior.SequentialAccess)) {
4220                                         return ADP.TrueTask;
4221                                     }
4222                                     // For non-sequential, check if we can read the row data now
4223                                     else if (WillHaveEnoughData(_metaData.Length - 1)) {
4224                                         // Read row data
4225                                         result = TryReadColumn(_metaData.Length - 1, setTimeout: true);
4226                                         Debug.Assert(result, "Should not have run out of data");
4227                                         return ADP.TrueTask;
4228                                     }
4229                                 }
4230                                 else {
4231                                     // No data left, return
4232                                     return ADP.FalseTask;
4233                                 }
4234                             }
4235 #if DEBUG
4236                         }
4237                         finally {
4238                             _stateObj._shouldHaveEnoughData = false;
4239                         }
4240 #endif
4241                     }
4242                 }
4243                 catch (Exception ex) {
4244                     if (!ADP.IsCatchableExceptionType(ex)) {
4245                         throw;
4246                     }
4247                     return ADP.CreatedTaskWithException<bool>(ex);
4248                 }
4249
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()));
4254                     return source.Task;
4255                 }
4256
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;
4261                     return source.Task;
4262                 }
4263
4264                 IDisposable registration = null;
4265                 if (cancellationToken.CanBeCanceled) {
4266                     registration = cancellationToken.Register(_command.CancelIgnoreFailure);
4267                 }
4268
4269                 PrepareAsyncInvocation(useSnapshot: true);
4270
4271                 Func<Task, Task<bool>> moreFunc = null;
4272                 moreFunc = (t) => {
4273                     if (t != null) {
4274                         Bid.Trace("<sc.SqlDataReader.ReadAsync> attempt retry %d#\n", ObjectID);
4275                         PrepareForAsyncContinuation();
4276                     }
4277
4278                     if (rowTokenRead || TryReadInternal(true, out more)) {
4279
4280                         // If there are no more rows, or this is Sequential Access, then we are done
4281                         if (!more || (_commandBehavior & CommandBehavior.SequentialAccess) == CommandBehavior.SequentialAccess) {
4282                             // completed 
4283                             return more ? ADP.TrueTask : ADP.FalseTask;
4284                         }
4285                         else {
4286                             // First time reading the row token - update the snapshot
4287                             if (!rowTokenRead) {
4288                                 rowTokenRead = true;
4289                                 _snapshot = null;
4290                                 PrepareAsyncInvocation(useSnapshot: true);
4291                             }
4292
4293                             // if non-sequentialaccess then read entire row before returning
4294                             if (TryReadColumn(_metaData.Length - 1, true)) {
4295                                 // completed 
4296                                 return ADP.TrueTask;
4297                             }
4298                         }
4299                     }
4300
4301                     return ContinueRetryable(moreFunc);
4302                 };
4303
4304                 return InvokeRetryable(moreFunc, source, registration);
4305             }
4306             finally {
4307                 Bid.ScopeLeave(ref hscp);
4308             }
4309         }
4310
4311         override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationToken) {
4312
4313             try {
4314                 CheckHeaderIsReady(columnIndex: i, methodName: "IsDBNullAsync");
4315             }
4316             catch (Exception ex) {
4317                 if (!ADP.IsCatchableExceptionType(ex)) {
4318                     throw;
4319                 }
4320                 return ADP.CreatedTaskWithException<bool>(ex);
4321             }
4322
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)) {
4325                 var data = _data;
4326                 if (data != null) {
4327                     return data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
4328                 }
4329                 else {
4330                     // Reader was closed between the CheckHeaderIsReady and accessing _data - throw closed exception
4331                     return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("IsDBNullAsync")));
4332                 }
4333             }
4334             else {
4335                 // Throw if there is any current task
4336                 if (_currentTask != null) {
4337                     return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4338                 }
4339                 
4340                 // If user's token is canceled, return a canceled task
4341                 if (cancellationToken.IsCancellationRequested) {
4342                     return ADP.CreatedTaskWithCancellation<bool>();
4343                 }
4344                 
4345                 // Shortcut - if we have enough data, then run sync
4346                 try {
4347                     if (WillHaveEnoughData(i, headerOnly: true)) {
4348 #if DEBUG
4349                     try {
4350                         _stateObj._shouldHaveEnoughData = true;
4351 #endif
4352                     ReadColumnHeader(i);
4353                     return _data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
4354 #if DEBUG
4355                     }
4356                     finally {
4357                         _stateObj._shouldHaveEnoughData = false;
4358                     }
4359 #endif
4360                     }
4361                 }
4362                 catch (Exception ex) {
4363                     if (!ADP.IsCatchableExceptionType(ex)) {
4364                         throw;
4365                     }
4366                     return ADP.CreatedTaskWithException<bool>(ex);
4367                 }
4368                 
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()));
4374                     return source.Task;
4375                 }
4376
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;
4381                     return source.Task;
4382                 }
4383
4384                 // Setup cancellations
4385                 IDisposable registration = null;
4386                 if (cancellationToken.CanBeCanceled) {
4387                     registration = cancellationToken.Register(_command.CancelIgnoreFailure);
4388                 }                
4389
4390                 // Setup async
4391                 PrepareAsyncInvocation(useSnapshot: true);
4392
4393                 // Setup the retryable function
4394                 Func<Task, Task<bool>> moreFunc = null;
4395                 moreFunc = (t) => {
4396                     if (t != null) {
4397                         PrepareForAsyncContinuation();
4398                     }
4399
4400                     if (TryReadColumnHeader(i)) {
4401                         return _data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
4402                     }
4403                     else {
4404                         return ContinueRetryable(moreFunc);
4405                     }
4406                 };
4407
4408                 // Go!
4409                 return InvokeRetryable(moreFunc, source, registration);
4410             }
4411         }
4412
4413         override public Task<T> GetFieldValueAsync<T>(int i, CancellationToken cancellationToken) {
4414
4415             try {
4416                 CheckDataIsReady(columnIndex: i, methodName: "GetFieldValueAsync");
4417
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)) {
4420                     var data = _data;
4421                     var metaData =_metaData;
4422                     if ((data != null) && (metaData != null)) {
4423                         return Task.FromResult<T>(GetFieldValueFromSqlBufferInternal<T>(data[i], metaData[i]));
4424                     }
4425                     else {
4426                         // Reader was closed between the CheckDataIsReady and accessing _data\_metaData - throw closed exception
4427                         return ADP.CreatedTaskWithException<T>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("GetFieldValueAsync")));
4428                     }
4429                 }
4430             } catch (Exception ex) {
4431                 if (!ADP.IsCatchableExceptionType(ex)) {
4432                     throw;
4433                 }
4434                 return ADP.CreatedTaskWithException<T>(ex);
4435             }
4436
4437             // Throw if there is any current task
4438             if (_currentTask != null) {
4439                 return ADP.CreatedTaskWithException<T>(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
4440             }
4441
4442             // If user's token is canceled, return a canceled task
4443             if (cancellationToken.IsCancellationRequested) {
4444                 return ADP.CreatedTaskWithCancellation<T>();
4445             }
4446
4447             // Shortcut - if we have enough data, then run sync
4448             try {
4449                 if (WillHaveEnoughData(i)) {
4450 #if DEBUG
4451                     try {
4452                         _stateObj._shouldHaveEnoughData = true;
4453 #endif
4454                     return Task.FromResult(GetFieldValueInternal<T>(i));
4455 #if DEBUG
4456                     }
4457                     finally {
4458                         _stateObj._shouldHaveEnoughData = false;
4459                     }
4460 #endif
4461                 }
4462             }
4463             catch (Exception ex) {
4464                 if (!ADP.IsCatchableExceptionType(ex)) {
4465                     throw;
4466                 }
4467                 return ADP.CreatedTaskWithException<T>(ex);
4468             }
4469
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()));
4475                 return source.Task;
4476             }
4477
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;
4482                 return source.Task;
4483             }
4484
4485             // Setup cancellations
4486             IDisposable registration = null;
4487             if (cancellationToken.CanBeCanceled) {
4488                 registration = cancellationToken.Register(_command.CancelIgnoreFailure);
4489             }
4490
4491             // Setup async
4492             PrepareAsyncInvocation(useSnapshot: true);
4493
4494             // Setup the retryable function
4495             Func<Task, Task<T>> moreFunc = null;
4496             moreFunc = (t) => {
4497                 if (t != null) {
4498                     PrepareForAsyncContinuation();
4499                 }
4500
4501                 if (TryReadColumn(i, setTimeout: false)) {
4502                     return Task.FromResult<T>(GetFieldValueFromSqlBufferInternal<T>(_data[i], _metaData[i]));
4503                 }
4504                 else {
4505                     return ContinueRetryable(moreFunc);
4506                 }
4507             };
4508
4509             // Go!
4510             return InvokeRetryable(moreFunc, source, registration);
4511         }
4512
4513 #if DEBUG
4514
4515         internal void CompletePendingReadWithSuccess(bool resetForcePendingReadsToWait) {
4516             var stateObj = _stateObj;
4517             if (stateObj != null) {
4518                 stateObj.CompletePendingReadWithSuccess(resetForcePendingReadsToWait);
4519             }
4520         }
4521
4522         internal void CompletePendingReadWithFailure(int errorCode, bool resetForcePendingReadsToWait) {
4523             var stateObj = _stateObj;
4524             if (stateObj != null) {
4525                 stateObj.CompletePendingReadWithFailure(errorCode, resetForcePendingReadsToWait);
4526             }
4527         }
4528
4529 #endif
4530
4531         class Snapshot {
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;
4542
4543             public _SqlMetaDataSet _metadata;
4544             public _SqlMetaDataSetCollection _altMetaDataSetCollection;
4545             public MultiPartTableName[] _tableNames;
4546
4547             public SqlSequentialStream _currentStream;
4548             public SqlSequentialTextReader _currentTextReader;
4549         }
4550
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()));
4559                 return source.Task;
4560             }
4561             else {
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;
4568                     }
4569                     else if (!_cancelAsyncOnCloseToken.IsCancellationRequested) {
4570                         TdsParserStateObject stateObj = _stateObj;
4571                         if (stateObj != null) {
4572                             // protect continuations against concurrent
4573                             // close and cancel
4574                             lock (stateObj) {
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);
4581                                         }
4582                                     }
4583                                     else {
4584                                         if (!IsClosed) {
4585                                             try {
4586                                                 return moreFunc(retryTask);
4587                                             }
4588                                             catch (Exception) {
4589                                                 CleanupAfterAsyncInvocation();
4590                                                 throw;
4591                                             }
4592                                         }
4593                                     }
4594                                 }
4595                             }
4596                         }
4597                     }
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()));
4602                     return source.Task;
4603                 }, TaskScheduler.Default).Unwrap();
4604             }
4605         }
4606
4607         private Task<T> InvokeRetryable<T>(Func<Task, Task<T>> moreFunc, TaskCompletionSource<T> source, IDisposable objectToDispose = null) {
4608             try {
4609                 Task<T> task;
4610                 try {
4611                     task = moreFunc(null);
4612                 }
4613                 catch (Exception ex) {
4614                     task = ADP.CreatedTaskWithException<T>(ex);
4615                 }
4616
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);
4620                     return task;
4621                 }
4622                 else {
4623                     task.ContinueWith((t) => CompleteRetryable(t, source, objectToDispose), TaskScheduler.Default);
4624                 }
4625             }
4626             catch (AggregateException e) {
4627                 source.TrySetException(e.InnerException);
4628             }
4629             catch (Exception e) {
4630                 source.TrySetException(e);
4631             }
4632
4633             // Fall through for exceptions\completing async
4634             return source.Task;
4635         }
4636         
4637         private void CompleteRetryable<T>(Task<T> task, TaskCompletionSource<T> source, IDisposable objectToDispose) {
4638             if (objectToDispose != null) {
4639                 objectToDispose.Dispose();
4640             }
4641
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);
4647
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");
4650
4651             if (task.IsFaulted) {
4652                 Exception e = task.Exception.InnerException;
4653                 source.TrySetException(e);
4654             }
4655             else if (task.IsCanceled) {
4656                 source.TrySetCanceled();
4657             }
4658             else {
4659                 source.TrySetResult(task.Result);
4660             }
4661         }
4662
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.
4667             if (useSnapshot) {
4668                 Debug.Assert(!_stateObj._asyncReadWithoutSnapshot, "Can't prepare async invocation with snapshot if doing async without snapshots");
4669
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,
4682
4683                         // _metadata and _altaMetaDataSetCollection must be Cloned
4684                         // before they are updated
4685                         _metadata = _metaData,
4686                         _altMetaDataSetCollection = _altMetaDataSetCollection,
4687                         _tableNames = _tableNames,
4688                     
4689                         _currentStream = _currentStream,
4690                         _currentTextReader = _currentTextReader,
4691                     };
4692
4693                     _stateObj.SetSnapshot();
4694                 }
4695             }
4696             else {
4697                 Debug.Assert(_snapshot == null, "Can prepare async invocation without snapshot if there is currently a snapshot");
4698                 _stateObj._asyncReadWithoutSnapshot = true;
4699             }
4700
4701             _stateObj._syncOverAsync = false;
4702             _stateObj._executionContext = ExecutionContext.Capture();
4703         }
4704
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)
4712                     lock(stateObj) {
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");
4716                         }
4717                     }
4718                 }
4719             }
4720         }
4721
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)
4725         {
4726             if (resetNetworkPacketTaskSource) {
4727                 stateObj._networkPacketTaskSource = null;
4728             }
4729             stateObj.ResetSnapshot();
4730             stateObj._syncOverAsync = true;
4731             stateObj._executionContext = null;
4732             stateObj._asyncReadWithoutSnapshot = false;
4733 #if DEBUG
4734             stateObj._permitReplayStackTraceToDiffer = false;
4735 #endif
4736
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)
4738             _snapshot = null;
4739         }
4740
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;
4754
4755                 _metaData = _snapshot._metadata;
4756                 _altMetaDataSetCollection = _snapshot._altMetaDataSetCollection;
4757                 _tableNames = _snapshot._tableNames;
4758
4759                 _currentStream = _snapshot._currentStream;
4760                 _currentTextReader = _snapshot._currentTextReader;
4761
4762                 _stateObj.PrepareReplaySnapshot();
4763             }
4764
4765             _stateObj._executionContext = ExecutionContext.Capture();
4766         }
4767
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");
4771
4772             _snapshot = null;
4773             _stateObj.ResetSnapshot();
4774             _stateObj._asyncReadWithoutSnapshot = true;
4775         }
4776
4777     }// SqlDataReader
4778 }// namespace