Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlDataReaderSmi.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.Data;
12     using System.Data.Sql;
13     using System.Data.SqlTypes;
14     using System.IO;
15     using System.Runtime.InteropServices;
16     using System.Threading;
17     using System.Diagnostics;        // for Conditional compilation
18     using System.Diagnostics.CodeAnalysis;
19     using System.Xml;
20     using Microsoft.SqlServer.Server;
21     using System.Data.ProviderBase;
22     using System.Data.Common;
23     using System.Threading.Tasks;
24
25         // SqlServer provider's implementation of ISqlReader.
26     //    Supports ISqlReader and ISqlResultSet objects.
27     //
28     //    User should never be able to create one of these themselves, nor subclass.
29     //        This is accomplished by having no public override constructors.
30     internal sealed class SqlDataReaderSmi : SqlDataReader {
31
32     
33     //
34     // IDBRecord properties
35     //
36         public override int FieldCount {
37             get {
38                 ThrowIfClosed( "FieldCount" );
39                 return InternalFieldCount;
40             }
41         }
42
43         public override int VisibleFieldCount {
44             get {
45                 ThrowIfClosed("VisibleFieldCount");
46
47                 if (FNotInResults()) {
48                     return 0;
49                 }
50
51                 return _visibleColumnCount;
52             }
53         }
54
55     //
56     // IDBRecord Metadata Methods
57     //
58         public override String GetName(int ordinal) {
59             EnsureCanGetMetaData( "GetName" );
60             return _currentMetaData[ordinal].Name;
61         }
62
63         public override String GetDataTypeName(int ordinal) {
64             EnsureCanGetMetaData( "GetDataTypeName" );
65             SmiExtendedMetaData md = _currentMetaData[ordinal];
66             if ( SqlDbType.Udt == md.SqlDbType ) {
67                 return md.TypeSpecificNamePart1 + "." + md.TypeSpecificNamePart2 + "." + md.TypeSpecificNamePart3;
68             }
69             else {
70                 return md.TypeName;
71             }
72         }
73
74         public override Type GetFieldType(int ordinal) {
75             EnsureCanGetMetaData( "GetFieldType" );
76             if (SqlDbType.Udt == _currentMetaData[ordinal].SqlDbType) {
77                 return _currentMetaData[ordinal].Type;
78             }
79             else {
80                 return MetaType.GetMetaTypeFromSqlDbType(
81                     _currentMetaData[ordinal].SqlDbType, _currentMetaData[ordinal].IsMultiValued).ClassType ;
82             }
83         }
84
85         override public Type GetProviderSpecificFieldType(int ordinal) {
86             EnsureCanGetMetaData( "GetProviderSpecificFieldType" );
87
88             if (SqlDbType.Udt == _currentMetaData[ordinal].SqlDbType) {
89                 return _currentMetaData[ordinal].Type;
90             }
91             else {
92                 return MetaType.GetMetaTypeFromSqlDbType(
93                     _currentMetaData[ordinal].SqlDbType, _currentMetaData[ordinal].IsMultiValued).SqlType ;
94             }
95         }
96
97         public override int Depth {
98             get{
99                 ThrowIfClosed( "Depth" );
100                 return 0;
101             }
102         } // 
103
104         public override Object GetValue(int ordinal) {
105             EnsureCanGetCol( "GetValue", ordinal);
106             SmiQueryMetaData metaData = _currentMetaData[ordinal];
107             if (_currentConnection.IsKatmaiOrNewer) {
108                 return ValueUtilsSmi.GetValue200(_readerEventSink, (SmiTypedGetterSetter)_currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext);
109             }
110             else {
111                 return ValueUtilsSmi.GetValue(_readerEventSink, _currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext);
112             }
113         }
114
115         public override T GetFieldValue<T>(int ordinal) {
116             EnsureCanGetCol( "GetFieldValue<T>", ordinal);
117             SmiQueryMetaData metaData = _currentMetaData[ordinal];
118
119             if (_typeofINullable.IsAssignableFrom(typeof(T))) {
120                 // If its a SQL Type or Nullable UDT
121                 if (_currentConnection.IsKatmaiOrNewer) {
122                     return (T)ValueUtilsSmi.GetSqlValue200(_readerEventSink, (SmiTypedGetterSetter)_currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext);
123                 }
124                 else {
125                     return (T)ValueUtilsSmi.GetSqlValue(_readerEventSink, _currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext);
126                 }
127             }
128             else {
129                 // Otherwise Its a CLR or non-Nullable UDT
130                 if (_currentConnection.IsKatmaiOrNewer) {
131                     return (T)ValueUtilsSmi.GetValue200(_readerEventSink, (SmiTypedGetterSetter)_currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext);
132                 }
133                 else {
134                     return (T)ValueUtilsSmi.GetValue(_readerEventSink, _currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext);
135                 }
136             }
137         }
138
139         public override Task<T> GetFieldValueAsync<T>(int ordinal, CancellationToken cancellationToken) {
140             // As per Async spec, Context Connections do not support async
141             return ADP.CreatedTaskWithException<T>(ADP.ExceptionWithStackTrace(SQL.NotAvailableOnContextConnection()));
142         }
143
144         override internal SqlBuffer.StorageType GetVariantInternalStorageType(int ordinal) {
145             Debug.Assert(null != _currentColumnValuesV3, "Attempting to get variant internal storage type without calling GetValue first");
146             if (IsDBNull(ordinal))
147                 return SqlBuffer.StorageType.Empty;
148
149             SmiMetaData valueMetaData = _currentColumnValuesV3.GetVariantType(_readerEventSink, ordinal);
150             if (valueMetaData == null)
151                 return SqlBuffer.StorageType.Empty;
152             else
153                 return ValueUtilsSmi.SqlDbTypeToStorageType(valueMetaData.SqlDbType);
154         }
155
156         public override int GetValues(object[] values) {
157             EnsureCanGetCol( "GetValues", 0);
158             if (null == values) {
159                 throw ADP.ArgumentNull("values");
160             }
161
162             int copyLength = (values.Length < _visibleColumnCount) ? values.Length : _visibleColumnCount;
163             for(int i=0; i<copyLength; i++) {
164                 values[_indexMap[i]] = GetValue(i);
165             }
166             return copyLength;
167         }
168
169         public override int GetOrdinal(string name) {
170             EnsureCanGetMetaData( "GetOrdinal" );
171             if (null == _fieldNameLookup) {
172                 _fieldNameLookup = new FieldNameLookup( (IDataReader) this, -1 ); // 
173             }
174             return _fieldNameLookup.GetOrdinal(name); // MDAC 71470
175         }
176
177         // Generic array access by column index (accesses column value)
178         public override object this[int ordinal] {
179             get {
180                 return GetValue( ordinal );
181             }
182         }
183
184         // Generic array access by column name (accesses column value)
185         public override object this[string strName] {
186             get {
187                 return GetValue( GetOrdinal( strName ) );
188             }
189         }
190
191     //
192     // IDataRecord Data Access methods
193     //
194         public override bool IsDBNull(int ordinal) {
195             EnsureCanGetCol( "IsDBNull", ordinal);
196             return ValueUtilsSmi.IsDBNull(_readerEventSink, _currentColumnValuesV3, ordinal);
197         }
198
199         public override Task<bool> IsDBNullAsync(int ordinal, CancellationToken cancellationToken) {
200             // As per Async spec, Context Connections do not support async
201             return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(SQL.NotAvailableOnContextConnection()));
202         }
203
204         public override bool GetBoolean(int ordinal) {
205             EnsureCanGetCol( "GetBoolean", ordinal);
206             return ValueUtilsSmi.GetBoolean(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
207         }
208
209         public override byte GetByte(int ordinal) {
210             EnsureCanGetCol( "GetByte", ordinal);
211             return ValueUtilsSmi.GetByte(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
212         }
213
214         public override long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) {
215             EnsureCanGetCol( "GetBytes", ordinal);
216             return ValueUtilsSmi.GetBytes(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], fieldOffset, buffer, bufferOffset, length, true);
217         }
218
219         // XmlReader support code calls this method.
220         internal override long GetBytesInternal(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length) {
221             EnsureCanGetCol( "GetBytes", ordinal);
222             return ValueUtilsSmi.GetBytesInternal(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], fieldOffset, buffer, bufferOffset, length, false);
223         }
224
225         public override char GetChar(int ordinal) {
226             throw ADP.NotSupported();
227         }
228
229         public override long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length) {
230             EnsureCanGetCol( "GetChars", ordinal);
231             SmiExtendedMetaData metaData = _currentMetaData[ordinal];
232             if (IsCommandBehavior(CommandBehavior.SequentialAccess)) {
233                 if (metaData.SqlDbType == SqlDbType.Xml) {
234                     return GetStreamingXmlChars(ordinal, fieldOffset, buffer, bufferOffset, length);
235                 }
236             }
237             return ValueUtilsSmi.GetChars(_readerEventSink, _currentColumnValuesV3, ordinal, metaData, fieldOffset, buffer, bufferOffset, length);
238         }
239
240         public override Guid GetGuid(int ordinal) {
241             EnsureCanGetCol( "GetGuid", ordinal);
242             return ValueUtilsSmi.GetGuid(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
243         }
244
245         public override Int16 GetInt16(int ordinal) {
246             EnsureCanGetCol( "GetInt16", ordinal);
247             return ValueUtilsSmi.GetInt16(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
248         }
249
250         public override Int32 GetInt32(int ordinal) {
251             EnsureCanGetCol( "GetInt32", ordinal);
252             return ValueUtilsSmi.GetInt32(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
253         }
254
255         public override Int64 GetInt64(int ordinal) {
256             EnsureCanGetCol( "GetInt64", ordinal);
257             return ValueUtilsSmi.GetInt64(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
258         }
259
260         public override Single GetFloat(int ordinal) {
261             EnsureCanGetCol( "GetFloat", ordinal);
262             return ValueUtilsSmi.GetSingle(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
263         }
264
265         public override Double GetDouble(int ordinal) {
266             EnsureCanGetCol( "GetDouble", ordinal);
267             return ValueUtilsSmi.GetDouble(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
268         }
269
270         public override String GetString(int ordinal) {
271             EnsureCanGetCol( "GetString", ordinal);
272             return ValueUtilsSmi.GetString(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
273         }
274
275         public override Decimal GetDecimal(int ordinal) {
276             EnsureCanGetCol( "GetDecimal", ordinal);
277             return ValueUtilsSmi.GetDecimal(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
278         }
279
280         public override DateTime GetDateTime(int ordinal) {
281             EnsureCanGetCol( "GetDateTime", ordinal);
282             return ValueUtilsSmi.GetDateTime(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
283         }
284
285     //
286     // IDataReader properties
287     //
288         // Logically closed test. I.e. is this object closed as far as external access is concerned?
289         public override bool IsClosed {
290             get { 
291                 return IsReallyClosed(); 
292             }
293         }
294
295         public override int RecordsAffected {
296             get {
297                 return base.Command.InternalRecordsAffected;
298             }
299         }
300
301     //
302     // IDataReader methods
303     //
304         internal override void CloseReaderFromConnection() {
305             // Context Connections do not support async - so there is no threading issues with closing from the connection
306             CloseInternal(closeConnection: false);
307         }
308
309         public override void Close() {
310             // Connection should be open at this point, so we can do multiple checks of HasEvents, and we may need to close the connection afterwards
311             CloseInternal(closeConnection: IsCommandBehavior(CommandBehavior.CloseConnection));
312         }
313
314         private void CloseInternal(bool closeConnection) {
315             IntPtr hscp;
316             Bid.ScopeEnter(out hscp, "<sc.SqlDataReaderSmi.Close|API> %d#", ObjectID);
317             bool processFinallyBlock = true;
318             try  {
319                 if(!IsClosed) {
320                     _hasRows = false;
321
322                     // Process the remaining events. This makes sure that environment changes are applied and any errors are picked up.
323                     while(_eventStream.HasEvents) {
324                         _eventStream.ProcessEvent( _readerEventSink );
325                         _readerEventSink.ProcessMessagesAndThrow(true);
326                     }
327
328                     // Close the request executor
329                     _requestExecutor.Close(_readerEventSink);
330                     _readerEventSink.ProcessMessagesAndThrow(true);
331                 }
332             }
333             catch (Exception e) {
334                 processFinallyBlock = ADP.IsCatchableExceptionType(e);
335                 throw;
336             }
337             finally {
338                 if (processFinallyBlock) {
339                     _isOpen = false;
340
341                     if ((closeConnection) && (Connection != null)) {
342                         Connection.Close();
343                     }
344
345                     Bid.ScopeLeave(ref hscp);
346                 }
347             }
348         }
349
350         // Move to the next resultset
351         public override unsafe bool NextResult() {
352             ThrowIfClosed( "NextResult" );
353
354             bool hasAnotherResult = InternalNextResult(false);
355
356             return hasAnotherResult;
357         }
358         
359         public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
360         {
361             // Async not supported on Context Connections
362             return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(SQL.NotAvailableOnContextConnection()));
363         }
364
365         internal unsafe bool InternalNextResult(bool ignoreNonFatalMessages) {
366             IntPtr hscp = IntPtr.Zero;
367             if (Bid.AdvancedOn) {
368                 Bid.ScopeEnter(out hscp, "<sc.SqlDataReaderSmi.InternalNextResult|ADV> %d#", ObjectID);
369             }
370             try {
371                 _hasRows = false;
372
373                 if( PositionState.AfterResults != _currentPosition )
374                 {
375                     // Consume any remaning rows in the current result. 
376                     
377                     while( InternalRead(ignoreNonFatalMessages) ) {
378                         // This space intentionally left blank
379                     }
380
381                     // reset resultset metadata - it will be created again if there is a pending resultset
382                     ResetResultSet();
383
384                     // Process the events until metadata is found or all of the
385                     // available events have been consumed. If there is another
386                     // result, the metadata for it will be available after the last
387                     // read on the prior result.
388
389                     while(null == _currentMetaData && _eventStream.HasEvents) {
390                         _eventStream.ProcessEvent( _readerEventSink );
391                         _readerEventSink.ProcessMessagesAndThrow(ignoreNonFatalMessages);
392                     }
393                 }
394
395                 return PositionState.AfterResults != _currentPosition;
396             }
397             finally {
398                 if (Bid.AdvancedOn) {
399                     Bid.ScopeLeave(ref hscp);
400                 }
401             }
402         }
403
404         public override bool Read() {
405             ThrowIfClosed( "Read" );
406             bool hasAnotherRow = InternalRead(false);
407
408             return hasAnotherRow;
409         }
410
411         public override Task<bool> ReadAsync(CancellationToken cancellationToken)
412         {
413             // Async not supported on Context Connections
414             return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(SQL.NotAvailableOnContextConnection()));
415         }
416
417         internal unsafe bool InternalRead(bool ignoreNonFatalErrors) {
418             IntPtr hscp = IntPtr.Zero;
419             if (Bid.AdvancedOn) {
420                 Bid.ScopeEnter(out hscp, "<sc.SqlDataReaderSmi.InternalRead|ADV> %d#", ObjectID);
421             }
422             try {
423                 // Don't move unless currently in results.
424                 if( FInResults() ) {
425                 
426                     // Set current row to null so we can see if we get a new one
427                     _currentColumnValues = null;
428                     _currentColumnValuesV3 = null;
429                     
430                     // Reset blobs
431                     if (_currentStream != null) {
432                         _currentStream.SetClosed();
433                         _currentStream = null;
434                     }
435                     if (_currentTextReader != null) {
436                         _currentTextReader.SetClosed();
437                         _currentTextReader = null;
438                     }
439
440                     // NOTE: SQLBUDT #386118 -- may indicate that we want to break this loop when we get a MessagePosted callback, but we can't prove that.
441                     while(  null == _currentColumnValues &&                         // Did we find a row?
442                             null == _currentColumnValuesV3 &&                       // Did we find a V3 row?
443                             FInResults() &&                         // Was the batch terminated due to a serious error?
444                             PositionState.AfterRows != _currentPosition &&              // Have we seen a statement completed event?
445                             _eventStream.HasEvents ) {              // Have we processed all events?
446                         _eventStream.ProcessEvent( _readerEventSink );
447                         _readerEventSink.ProcessMessagesAndThrow(ignoreNonFatalErrors);
448                     }
449                 }
450                 
451                 return PositionState.OnRow == _currentPosition;
452             }
453             finally {
454                 if (Bid.AdvancedOn) {
455                     Bid.ScopeLeave(ref hscp);
456                 }
457             }
458         }    
459
460         public override DataTable GetSchemaTable() {
461             ThrowIfClosed( "GetSchemaTable" );
462
463             if ( null == _schemaTable && FInResults() )
464                 {
465
466                 DataTable schemaTable = new DataTable( "SchemaTable" );
467                 schemaTable.Locale = System.Globalization.CultureInfo.InvariantCulture;
468                 schemaTable.MinimumCapacity = InternalFieldCount;
469
470                 DataColumn ColumnName                       = new DataColumn(SchemaTableColumn.ColumnName,                       typeof(System.String));
471                 DataColumn Ordinal                          = new DataColumn(SchemaTableColumn.ColumnOrdinal,                    typeof(System.Int32));
472                 DataColumn Size                             = new DataColumn(SchemaTableColumn.ColumnSize,                       typeof(System.Int32));
473                 DataColumn Precision                        = new DataColumn(SchemaTableColumn.NumericPrecision,                 typeof(System.Int16));
474                 DataColumn Scale                            = new DataColumn(SchemaTableColumn.NumericScale,                     typeof(System.Int16));
475
476                 DataColumn DataType                         = new DataColumn(SchemaTableColumn.DataType,                         typeof(System.Type));
477                 DataColumn ProviderSpecificDataType         = new DataColumn(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(System.Type));
478                 DataColumn ProviderType                     = new DataColumn(SchemaTableColumn.ProviderType,                     typeof(System.Int32));
479                 DataColumn NonVersionedProviderType         = new DataColumn(SchemaTableColumn.NonVersionedProviderType,         typeof(System.Int32));
480
481                 DataColumn IsLong                           = new DataColumn(SchemaTableColumn.IsLong,                           typeof(System.Boolean));
482                 DataColumn AllowDBNull                      = new DataColumn(SchemaTableColumn.AllowDBNull,                      typeof(System.Boolean));
483                 DataColumn IsReadOnly                       = new DataColumn(SchemaTableOptionalColumn.IsReadOnly,               typeof(System.Boolean));
484                 DataColumn IsRowVersion                     = new DataColumn(SchemaTableOptionalColumn.IsRowVersion,             typeof(System.Boolean));
485
486                 DataColumn IsUnique                         = new DataColumn(SchemaTableColumn.IsUnique,                         typeof(System.Boolean));
487                 DataColumn IsKey                            = new DataColumn(SchemaTableColumn.IsKey,                            typeof(System.Boolean));
488                 DataColumn IsAutoIncrement                  = new DataColumn(SchemaTableOptionalColumn.IsAutoIncrement,          typeof(System.Boolean));
489                 DataColumn IsHidden                         = new DataColumn(SchemaTableOptionalColumn.IsHidden,                 typeof(System.Boolean));
490
491                 DataColumn BaseCatalogName                  = new DataColumn(SchemaTableOptionalColumn.BaseCatalogName,          typeof(System.String));
492                 DataColumn BaseSchemaName                   = new DataColumn(SchemaTableColumn.BaseSchemaName,                   typeof(System.String));
493                 DataColumn BaseTableName                    = new DataColumn(SchemaTableColumn.BaseTableName,                    typeof(System.String));
494                 DataColumn BaseColumnName                   = new DataColumn(SchemaTableColumn.BaseColumnName,                   typeof(System.String));
495
496                 // unique to SqlClient
497                 DataColumn BaseServerName                   = new DataColumn(SchemaTableOptionalColumn.BaseServerName,           typeof(System.String));
498                 DataColumn IsAliased                        = new DataColumn(SchemaTableColumn.IsAliased,                        typeof(System.Boolean));
499                 DataColumn IsExpression                     = new DataColumn(SchemaTableColumn.IsExpression,                     typeof(System.Boolean));
500                 DataColumn IsIdentity                       = new DataColumn("IsIdentity",                                       typeof(System.Boolean));
501                 // UDT specific. Holds UDT typename ONLY if the type of the column is UDT, otherwise the data type
502                 DataColumn DataTypeName                     = new DataColumn("DataTypeName",                                     typeof(System.String));
503                 DataColumn UdtAssemblyQualifiedName         = new DataColumn("UdtAssemblyQualifiedName",                         typeof(System.String));
504                 // Xml metadata specific
505                 DataColumn XmlSchemaCollectionDatabase      = new DataColumn("XmlSchemaCollectionDatabase",                      typeof(System.String));
506                 DataColumn XmlSchemaCollectionOwningSchema  = new DataColumn("XmlSchemaCollectionOwningSchema",                  typeof(System.String));
507                 DataColumn XmlSchemaCollectionName          = new DataColumn("XmlSchemaCollectionName",                          typeof(System.String));
508                 // SparseColumnSet
509                 DataColumn IsColumnSet = new DataColumn("IsColumnSet", typeof(System.Boolean));
510
511                 Ordinal.DefaultValue = 0;
512                 IsLong.DefaultValue = false;
513
514                 DataColumnCollection columns = schemaTable.Columns;
515
516                 // must maintain order for backward compatibility
517                 columns.Add(ColumnName);
518                 columns.Add(Ordinal);
519                 columns.Add(Size);
520                 columns.Add(Precision);
521                 columns.Add(Scale);
522                 columns.Add(IsUnique);
523                 columns.Add(IsKey);
524                 columns.Add(BaseServerName);
525                 columns.Add(BaseCatalogName);
526                 columns.Add(BaseColumnName);
527                 columns.Add(BaseSchemaName);
528                 columns.Add(BaseTableName);
529                 columns.Add(DataType);
530                 columns.Add(AllowDBNull);
531                 columns.Add(ProviderType);
532                 columns.Add(IsAliased);
533                 columns.Add(IsExpression);
534                 columns.Add(IsIdentity);
535                 columns.Add(IsAutoIncrement);
536                 columns.Add(IsRowVersion);
537                 columns.Add(IsHidden);
538                 columns.Add(IsLong);
539                 columns.Add(IsReadOnly);
540                 columns.Add(ProviderSpecificDataType);
541                 columns.Add(DataTypeName);
542                 columns.Add(XmlSchemaCollectionDatabase);
543                 columns.Add(XmlSchemaCollectionOwningSchema);
544                 columns.Add(XmlSchemaCollectionName);
545                 columns.Add(UdtAssemblyQualifiedName);
546                 columns.Add(NonVersionedProviderType);
547                 columns.Add(IsColumnSet);
548
549                 for (int i = 0; i < InternalFieldCount; i++) {
550                     SmiQueryMetaData colMetaData = _currentMetaData[i];
551
552                     long maxLength = colMetaData.MaxLength;
553                         
554                     MetaType metaType = MetaType.GetMetaTypeFromSqlDbType(colMetaData.SqlDbType, colMetaData.IsMultiValued);
555                     if ( SmiMetaData.UnlimitedMaxLengthIndicator == maxLength ) {
556                         metaType = MetaType.GetMaxMetaTypeFromMetaType( metaType );
557                         maxLength = (metaType.IsSizeInCharacters && !metaType.IsPlp) ? (0x7fffffff / 2) : 0x7fffffff;
558                     }
559
560                     DataRow schemaRow = schemaTable.NewRow();
561
562                     // NOTE: there is an impedence mismatch here - the server always 
563                     // treats numeric data as variable length and sends a maxLength
564                     // based upon the precision, whereas TDS always sends 17 for 
565                     // the max length; rather than push this logic into the server,
566                     // I've elected to make a fixup here instead.
567                     if (SqlDbType.Decimal == colMetaData.SqlDbType) {
568                         // 
569                         maxLength = TdsEnums.MAX_NUMERIC_LEN;   // SQLBUDT 339686
570                     }
571                     else if (SqlDbType.Variant == colMetaData.SqlDbType) {
572                         // 
573                         maxLength = 8009;   // SQLBUDT 340726
574                     }
575
576                     schemaRow[ColumnName]   = colMetaData.Name;
577                     schemaRow[Ordinal]      = i;
578                     schemaRow[Size]         = maxLength;
579                     
580                     schemaRow[ProviderType] = (int) colMetaData.SqlDbType; // SqlDbType
581                     schemaRow[NonVersionedProviderType] = (int) colMetaData.SqlDbType; // SqlDbType
582
583                     if (colMetaData.SqlDbType != SqlDbType.Udt) {
584                         schemaRow[DataType]                 = metaType.ClassType; // com+ type
585                         schemaRow[ProviderSpecificDataType] = metaType.SqlType;
586                     }
587                     else {
588                         schemaRow[UdtAssemblyQualifiedName] = colMetaData.Type.AssemblyQualifiedName;
589                         schemaRow[DataType]                 = colMetaData.Type;
590                         schemaRow[ProviderSpecificDataType] = colMetaData.Type;
591                     }
592
593                     // NOTE: there is also an impedence mismatch here - the server 
594                     // has different ideas about what the precision value should be
595                     // than does the client bits.  I tried fixing up the default
596                     // meta data values in SmiMetaData, however, it caused the 
597                     // server suites to fall over dead.  Rather than attempt to 
598                     // bake it into the server, I'm fixing it up in the client.
599                     byte precision = 0xff;  // default for everything, except certain numeric types.
600                     
601                     // 
602                     switch (colMetaData.SqlDbType) {
603                         case SqlDbType.BigInt:
604                         case SqlDbType.DateTime:
605                         case SqlDbType.Decimal:
606                         case SqlDbType.Int:
607                         case SqlDbType.Money:
608                         case SqlDbType.SmallDateTime:
609                         case SqlDbType.SmallInt:
610                         case SqlDbType.SmallMoney:
611                         case SqlDbType.TinyInt:
612                             precision = colMetaData.Precision;  
613                             break;
614                         case SqlDbType.Float:       
615                             precision = 15;  
616                             break;
617                         case SqlDbType.Real:        
618                             precision = 7;  
619                             break;
620                         default:
621                             precision = 0xff;   // everything else is unknown;
622                             break;
623                     }
624
625                     schemaRow[Precision]        = precision;
626                     
627                     // 
628                     if ( SqlDbType.Decimal == colMetaData.SqlDbType ||
629                         SqlDbType.Time == colMetaData.SqlDbType ||
630                         SqlDbType.DateTime2 == colMetaData.SqlDbType ||
631                         SqlDbType.DateTimeOffset == colMetaData.SqlDbType) {
632                         schemaRow[Scale]            = colMetaData.Scale;
633                     }
634                     else {
635                         schemaRow[Scale]            = MetaType.GetMetaTypeFromSqlDbType(
636                                                         colMetaData.SqlDbType, colMetaData.IsMultiValued).Scale;
637                     }
638                     
639                     schemaRow[AllowDBNull]      = colMetaData.AllowsDBNull;
640                     if ( !( colMetaData.IsAliased.IsNull ) ) {
641                         schemaRow[IsAliased]        = colMetaData.IsAliased.Value;
642                     }
643
644                     if ( !( colMetaData.IsKey.IsNull ) ) {
645                         schemaRow[IsKey]            = colMetaData.IsKey.Value;
646                     }
647
648                     if ( !( colMetaData.IsHidden.IsNull ) ) {
649                         schemaRow[IsHidden]         = colMetaData.IsHidden.Value;
650                     }
651
652                     if ( !( colMetaData.IsExpression.IsNull ) ) {
653                         schemaRow[IsExpression]     = colMetaData.IsExpression.Value;
654                     }
655
656                     schemaRow[IsReadOnly]       = colMetaData.IsReadOnly;
657                     schemaRow[IsIdentity]       = colMetaData.IsIdentity;
658                     schemaRow[IsColumnSet]      = colMetaData.IsColumnSet;
659                     schemaRow[IsAutoIncrement]  = colMetaData.IsIdentity;
660                     schemaRow[IsLong]           = metaType.IsLong;
661
662                     // mark unique for timestamp columns
663                     if ( SqlDbType.Timestamp == colMetaData.SqlDbType ) {
664                         schemaRow[IsUnique]         = true;
665                         schemaRow[IsRowVersion]     = true;
666                     }
667                     else {
668                         schemaRow[IsUnique]         = false;
669                         schemaRow[IsRowVersion]     = false;
670                     }
671
672                     if ( !ADP.IsEmpty( colMetaData.ColumnName ) ) {
673                         schemaRow[BaseColumnName]   = colMetaData.ColumnName;
674                     }
675                     else if (!ADP.IsEmpty( colMetaData.Name)) {
676                         // Use projection name if base column name is not present
677                         schemaRow[BaseColumnName]   = colMetaData.Name;
678                     }
679
680                     if ( !ADP.IsEmpty(colMetaData.TableName ) ) {
681                         schemaRow[BaseTableName]    = colMetaData.TableName;
682                     }
683
684                     if (!ADP.IsEmpty(colMetaData.SchemaName)) {
685                         schemaRow[BaseSchemaName]   = colMetaData.SchemaName;
686                     }
687
688                     if (!ADP.IsEmpty(colMetaData.CatalogName)) {
689                         schemaRow[BaseCatalogName]  = colMetaData.CatalogName;
690                     }
691
692                     if (!ADP.IsEmpty(colMetaData.ServerName)) {
693                         schemaRow[BaseServerName]   = colMetaData.ServerName;
694                     }
695
696                     if ( SqlDbType.Udt == colMetaData.SqlDbType ) {
697                         schemaRow[DataTypeName] = colMetaData.TypeSpecificNamePart1 + "." + colMetaData.TypeSpecificNamePart2 + "." + colMetaData.TypeSpecificNamePart3;
698                     }
699                     else {                        
700                         schemaRow[DataTypeName] = metaType.TypeName;  
701                     }
702
703                     // Add Xml metadata
704                     if ( SqlDbType.Xml == colMetaData.SqlDbType ) {
705                         schemaRow[XmlSchemaCollectionDatabase]      = colMetaData.TypeSpecificNamePart1;
706                         schemaRow[XmlSchemaCollectionOwningSchema]  = colMetaData.TypeSpecificNamePart2;
707                         schemaRow[XmlSchemaCollectionName]          = colMetaData.TypeSpecificNamePart3;
708                     }
709
710                     schemaTable.Rows.Add(schemaRow);
711                     schemaRow.AcceptChanges();
712                 }
713
714                 // mark all columns as readonly
715                 foreach(DataColumn column in columns) {
716                     column.ReadOnly = true; // MDAC 70943
717                 }
718
719                 _schemaTable = schemaTable;
720             }
721
722             return _schemaTable;
723         }
724
725     //
726     //    ISqlRecord methods
727     //
728         public override SqlBinary GetSqlBinary(int ordinal) {
729             EnsureCanGetCol( "GetSqlBinary", ordinal);
730             return ValueUtilsSmi.GetSqlBinary(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
731         }
732
733         public override SqlBoolean GetSqlBoolean(int ordinal) {
734             EnsureCanGetCol( "GetSqlBoolean", ordinal);
735             return ValueUtilsSmi.GetSqlBoolean(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
736         }
737
738         public override SqlByte GetSqlByte(int ordinal) {
739             EnsureCanGetCol( "GetSqlByte", ordinal);
740             return ValueUtilsSmi.GetSqlByte(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
741         }
742
743         public override SqlInt16 GetSqlInt16(int ordinal) {
744             EnsureCanGetCol( "GetSqlInt16", ordinal);
745             return ValueUtilsSmi.GetSqlInt16(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
746         }
747
748         public override SqlInt32 GetSqlInt32(int ordinal) {
749             EnsureCanGetCol( "GetSqlInt32", ordinal);
750             return ValueUtilsSmi.GetSqlInt32(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
751         }
752
753         public override SqlInt64 GetSqlInt64(int ordinal) {
754             EnsureCanGetCol( "GetSqlInt64", ordinal);
755             return ValueUtilsSmi.GetSqlInt64(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
756         }
757
758         public override SqlSingle GetSqlSingle(int ordinal) {
759             EnsureCanGetCol( "GetSqlSingle", ordinal);
760             return ValueUtilsSmi.GetSqlSingle(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
761         }
762
763         public override SqlDouble GetSqlDouble(int ordinal) {
764             EnsureCanGetCol( "GetSqlDouble", ordinal);
765             return ValueUtilsSmi.GetSqlDouble(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
766         }
767
768         public override SqlMoney GetSqlMoney(int ordinal) {
769             EnsureCanGetCol( "GetSqlMoney", ordinal);
770             return ValueUtilsSmi.GetSqlMoney(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
771         }
772
773         public override SqlDateTime GetSqlDateTime(int ordinal) {
774             EnsureCanGetCol( "GetSqlDateTime", ordinal);
775             return ValueUtilsSmi.GetSqlDateTime(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
776         }
777
778
779         public override SqlDecimal GetSqlDecimal(int ordinal) {
780             EnsureCanGetCol( "GetSqlDecimal", ordinal);
781             return ValueUtilsSmi.GetSqlDecimal(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
782         }
783
784         public override SqlString GetSqlString(int ordinal) {
785             EnsureCanGetCol( "GetSqlString", ordinal);
786             return ValueUtilsSmi.GetSqlString(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
787         }
788
789         public override SqlGuid GetSqlGuid(int ordinal) {
790             EnsureCanGetCol( "GetSqlGuid", ordinal);
791             return ValueUtilsSmi.GetSqlGuid(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal]);
792         }
793
794         public override SqlChars GetSqlChars(int ordinal) {
795             EnsureCanGetCol( "GetSqlChars", ordinal);
796             return ValueUtilsSmi.GetSqlChars(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.InternalContext);
797         }
798
799         public override SqlBytes GetSqlBytes(int ordinal) {
800             EnsureCanGetCol( "GetSqlBytes", ordinal);
801             return ValueUtilsSmi.GetSqlBytes(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.InternalContext);
802         }
803
804         public override SqlXml GetSqlXml(int ordinal) {
805             EnsureCanGetCol( "GetSqlXml", ordinal);
806             return ValueUtilsSmi.GetSqlXml(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.InternalContext);
807         }
808
809         public override TimeSpan GetTimeSpan(int ordinal) {
810             EnsureCanGetCol("GetTimeSpan", ordinal);
811             return ValueUtilsSmi.GetTimeSpan(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.IsKatmaiOrNewer);
812         }
813
814         public override DateTimeOffset GetDateTimeOffset(int ordinal) {
815             EnsureCanGetCol("GetDateTimeOffset", ordinal);
816             return ValueUtilsSmi.GetDateTimeOffset(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], _currentConnection.IsKatmaiOrNewer);
817         }
818
819         public override object GetSqlValue(int ordinal) {
820             EnsureCanGetCol( "GetSqlValue", ordinal);
821
822             SmiMetaData metaData = _currentMetaData[ordinal];
823             if (_currentConnection.IsKatmaiOrNewer) {
824                 return ValueUtilsSmi.GetSqlValue200(_readerEventSink, (SmiTypedGetterSetter)_currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext);
825             }
826             return ValueUtilsSmi.GetSqlValue(_readerEventSink, _currentColumnValuesV3, ordinal, metaData, _currentConnection.InternalContext); ;
827         }
828
829         public override int GetSqlValues(object[] values) {
830             EnsureCanGetCol( "GetSqlValues", 0);
831
832             if (null == values) {
833                 throw ADP.ArgumentNull("values");
834             }
835
836             int copyLength = (values.Length < _visibleColumnCount) ? values.Length : _visibleColumnCount;
837             for(int i=0; i<copyLength; i++) {
838                 values[_indexMap[i]] = GetSqlValue(i);
839             }
840
841             return copyLength;
842         }
843
844     //
845     //    ISqlReader methods/properties
846     //
847         public override bool HasRows {
848             get {return _hasRows;}
849         }
850
851     //
852     //    SqlDataReader method/properties
853     //
854         public override Stream GetStream(int ordinal) {
855             EnsureCanGetCol("GetStream", ordinal);
856
857             SmiQueryMetaData metaData = _currentMetaData[ordinal];
858
859             // For non-null, non-variant types with sequential access, we support proper streaming
860             if ((metaData.SqlDbType != SqlDbType.Variant) && (IsCommandBehavior(CommandBehavior.SequentialAccess)) && (!ValueUtilsSmi.IsDBNull(_readerEventSink, _currentColumnValuesV3, ordinal))) {
861                 if (HasActiveStreamOrTextReaderOnColumn(ordinal)) {
862                     throw ADP.NonSequentialColumnAccess(ordinal, ordinal + 1);
863                 }
864                 _currentStream = ValueUtilsSmi.GetSequentialStream(_readerEventSink, _currentColumnValuesV3, ordinal, metaData);
865                 return _currentStream;
866             }
867             else {
868                 return ValueUtilsSmi.GetStream(_readerEventSink, _currentColumnValuesV3, ordinal, metaData);
869             }
870         }
871
872         public override TextReader GetTextReader(int ordinal) {
873             EnsureCanGetCol("GetTextReader", ordinal);
874             
875             SmiQueryMetaData metaData = _currentMetaData[ordinal];
876
877             // For non-variant types with sequential access, we support proper streaming
878             if ((metaData.SqlDbType != SqlDbType.Variant) && (IsCommandBehavior(CommandBehavior.SequentialAccess)) && (!ValueUtilsSmi.IsDBNull(_readerEventSink, _currentColumnValuesV3, ordinal))) {
879                 if (HasActiveStreamOrTextReaderOnColumn(ordinal)) {
880                     throw ADP.NonSequentialColumnAccess(ordinal, ordinal + 1);
881                 }
882                 _currentTextReader = ValueUtilsSmi.GetSequentialTextReader(_readerEventSink, _currentColumnValuesV3, ordinal, metaData);
883                 return _currentTextReader;
884             }
885             else {
886                 return ValueUtilsSmi.GetTextReader(_readerEventSink, _currentColumnValuesV3, ordinal, metaData);
887             }
888         }
889
890         public override XmlReader GetXmlReader(int ordinal) {
891             // NOTE: sql_variant can not contain a XML data type: http://msdn.microsoft.com/en-us/library/ms173829.aspx
892             
893             EnsureCanGetCol("GetXmlReader", ordinal);
894             if (_currentMetaData[ordinal].SqlDbType != SqlDbType.Xml) {
895                 throw ADP.InvalidCast();
896             }
897
898             Stream stream = null;
899             if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) && (!ValueUtilsSmi.IsDBNull(_readerEventSink, _currentColumnValuesV3, ordinal))) {
900                 if (HasActiveStreamOrTextReaderOnColumn(ordinal)) {
901                     throw ADP.NonSequentialColumnAccess(ordinal, ordinal + 1);
902                 }
903                 // Need to bypass the type check since streams are not usually allowed on XML types
904                 _currentStream = ValueUtilsSmi.GetSequentialStream(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], bypassTypeCheck: true);
905                 stream = _currentStream;
906             }
907             else {
908                 stream = ValueUtilsSmi.GetStream(_readerEventSink, _currentColumnValuesV3, ordinal, _currentMetaData[ordinal], bypassTypeCheck: true);
909             }
910
911             return SqlXml.CreateSqlXmlReader(stream);
912         }
913
914     //
915     //    Internal reader state
916     //
917
918         // Logical state of reader/resultset as viewed by the client
919         //    Does not necessarily match up with server state.
920         internal enum PositionState
921             {
922             BeforeResults,            // Before all resultset in request
923             BeforeRows,                // Before all rows in current resultset
924             OnRow,                    // On a valid row in the current resultset
925             AfterRows,                // After all rows in current resultset
926             AfterResults            // After all resultsets in request
927             }
928         private PositionState _currentPosition;        // Where is the reader relative to incoming results?
929
930
931     //
932     //    Fields
933     //
934         private bool                        _isOpen;                    // Is the reader open?
935         private SmiQueryMetaData[]          _currentMetaData;           // Metadata for current resultset
936         private int[]                       _indexMap;                  // map of indices for visible column
937         private int                         _visibleColumnCount;        // number of visible columns
938         private DataTable                   _schemaTable;               // Cache of user-visible extended metadata while in results.
939         private ITypedGetters               _currentColumnValues;       // Unmanaged-managed data marshalers/cache
940         private ITypedGettersV3             _currentColumnValuesV3;     // Unmanaged-managed data marshalers/cache for SMI V3
941         private bool                        _hasRows;                   // Are there any rows in the current resultset?  Must be able to say before moving to first row.
942         private SmiEventStream              _eventStream;               // The event buffer that receives the events from the execution engine.
943         private SmiRequestExecutor          _requestExecutor;           // The used to request actions from the execution engine.
944         private SqlInternalConnectionSmi    _currentConnection;
945         private ReaderEventSink             _readerEventSink;           // The event sink that will process events from the event buffer.
946         private FieldNameLookup             _fieldNameLookup;           // cached lookup object to improve access time based on field name
947         private SqlSequentialStreamSmi      _currentStream;             // The stream on the current column (if any)
948         private SqlSequentialTextReaderSmi  _currentTextReader;         // The text reader on the current column (if any)
949
950     //
951     // Internal methods for use by other classes in project
952     //
953         // Constructor
954         //
955         //  Assumes that if there were any results, the first chunk of them are in the data stream
956         //      (up to the first actual row or the end of the resultsets).
957         unsafe internal SqlDataReaderSmi (
958                 SmiEventStream              eventStream,        // the event stream that receives the events from the execution engine
959                 SqlCommand                  parent,             // command that owns reader
960                 CommandBehavior             behavior,           // behavior specified for this execution
961                 SqlInternalConnectionSmi    connection,         // connection that owns everybody
962                 SmiEventSink                parentSink,         // Event sink of parent command
963                 SmiRequestExecutor          requestExecutor
964             ) : base( parent, behavior ) {  // 
965             _eventStream = eventStream;
966             _currentConnection = connection;
967             _readerEventSink = new ReaderEventSink( this, parentSink );
968             _currentPosition = PositionState.BeforeResults;
969             _isOpen = true;
970             _indexMap = null;
971             _visibleColumnCount = 0;
972             _currentStream = null;
973             _currentTextReader = null;
974             _requestExecutor = requestExecutor;
975         }
976
977         internal override SmiExtendedMetaData[] GetInternalSmiMetaData() {
978             if (null == _currentMetaData || _visibleColumnCount == this.InternalFieldCount) {
979                 return _currentMetaData;
980             }
981             else {
982 #if DEBUG
983                 // DEVNOTE: Interpretation of returned array currently depends on hidden columns
984                 //  always appearing at the end, since there currently is no access to the index map
985                 //  outside of this class.  In Debug code, we check this assumption.
986                 bool sawHiddenColumn = false;
987 #endif
988                 SmiExtendedMetaData[] visibleMetaData = new SmiExtendedMetaData[_visibleColumnCount];
989                 for(int i=0; i<_visibleColumnCount; i++) {
990 #if DEBUG
991                     if (_currentMetaData[_indexMap[i]].IsHidden.IsTrue) {
992                         sawHiddenColumn = true;
993                     }
994                     else {
995                         Debug.Assert(!sawHiddenColumn);
996                     }
997 #endif
998                     visibleMetaData[i] = _currentMetaData[_indexMap[i]];
999                 }
1000
1001                 return visibleMetaData;
1002             }
1003         }
1004
1005         internal override int GetLocaleId(int ordinal) {
1006             EnsureCanGetMetaData( "GetLocaleId" );
1007             return (int)_currentMetaData[ordinal].LocaleId;
1008         }
1009     
1010         //
1011         // Private implementation methods
1012         //
1013
1014         private int InternalFieldCount {
1015             get {
1016                 if ( FNotInResults() ) {
1017                     return 0;
1018                 }
1019                 else {
1020                     return _currentMetaData.Length;
1021                 }
1022             }
1023         }
1024
1025         // Have we cleaned up internal resources?
1026         private bool IsReallyClosed() {
1027             return !_isOpen;
1028         }
1029
1030         // Central checkpoint for closed recordset.
1031         //    Any code that requires an open recordset should call this method first!
1032         //    Especially any code that accesses unmanaged memory structures whose lifetime
1033         //      matches the lifetime of the unmanaged recordset.
1034         internal void ThrowIfClosed( string operationName ) {
1035             if (IsClosed)
1036                 throw ADP.DataReaderClosed( operationName );
1037         }
1038
1039         // Central checkpoint to ensure the requested column can be accessed.
1040         //    Calling this function serves to notify that it has been accessed by the user.
1041         [SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")] // for future compatibility
1042         private void EnsureCanGetCol( string operationName, int ordinal) {
1043             EnsureOnRow( operationName );
1044         }
1045
1046         internal void EnsureOnRow( string operationName ) {
1047             ThrowIfClosed( operationName );
1048             if (_currentPosition != PositionState.OnRow) {
1049                 throw SQL.InvalidRead();
1050             }
1051         }
1052
1053         internal void EnsureCanGetMetaData( string operationName ) {
1054             ThrowIfClosed( operationName );
1055             if (FNotInResults()) {
1056                 throw SQL.InvalidRead(); // 
1057             }
1058         }
1059
1060         private bool FInResults() {
1061             return !FNotInResults();
1062         }
1063
1064         private bool FNotInResults() {
1065             return (PositionState.AfterResults == _currentPosition || PositionState.BeforeResults == _currentPosition);
1066         }
1067
1068         private void MetaDataAvailable( SmiQueryMetaData[] md, bool nextEventIsRow ) {
1069             Debug.Assert( _currentPosition != PositionState.AfterResults );
1070             
1071             _currentMetaData = md;
1072             _hasRows = nextEventIsRow;
1073             _fieldNameLookup = null;
1074             _schemaTable = null; // will be rebuilt based on new metadata
1075             _currentPosition = PositionState.BeforeRows;
1076
1077             // calculate visible column indices
1078             _indexMap = new int[_currentMetaData.Length];
1079             int i;
1080             int visibleCount = 0;
1081             for(i=0; i<_currentMetaData.Length; i++) {
1082                 if (!_currentMetaData[i].IsHidden.IsTrue) {
1083                     _indexMap[visibleCount] = i;
1084                     visibleCount++;
1085                 }
1086             }
1087             _visibleColumnCount = visibleCount;
1088         }
1089
1090         private bool HasActiveStreamOrTextReaderOnColumn(int columnIndex) {
1091             bool active = false;
1092
1093             active |= ((_currentStream != null) && (_currentStream.ColumnIndex == columnIndex));
1094             active |= ((_currentTextReader != null) && (_currentTextReader.ColumnIndex == columnIndex));
1095
1096             return active;
1097         }
1098
1099         // Obsolete V2- method
1100         private void RowAvailable( ITypedGetters row ) {
1101             Debug.Assert( _currentPosition != PositionState.AfterResults );
1102
1103             _currentColumnValues = row;
1104             _currentPosition = PositionState.OnRow;
1105         }
1106
1107         private void RowAvailable( ITypedGettersV3 row ) {
1108             Debug.Assert( _currentPosition != PositionState.AfterResults );
1109
1110             _currentColumnValuesV3 = row;
1111             _currentPosition = PositionState.OnRow;
1112         }
1113
1114         private void StatementCompleted( ) {
1115             Debug.Assert( _currentPosition != PositionState.AfterResults );
1116
1117             _currentPosition = PositionState.AfterRows;
1118         }
1119
1120         private void ResetResultSet() {
1121             _currentMetaData = null;
1122             _visibleColumnCount = 0;
1123             _schemaTable = null;
1124         }
1125
1126         private void BatchCompleted() {
1127             Debug.Assert( _currentPosition != PositionState.AfterResults );
1128
1129             ResetResultSet();
1130
1131             _currentPosition = PositionState.AfterResults;
1132             _eventStream.Close( _readerEventSink );
1133         }
1134
1135         // An implementation of the IEventSink interface that either performs
1136         // the required enviornment changes or forwards the events on to the
1137         // corresponding reader instance. Having the event sink be a separate
1138         // class keeps the IEventSink methods out of SqlDataReader's inteface.
1139
1140         private sealed class ReaderEventSink : SmiEventSink_Default {
1141             private readonly SqlDataReaderSmi reader;
1142
1143             internal ReaderEventSink( SqlDataReaderSmi reader, SmiEventSink parent )
1144                 : base( parent ) {
1145                 this.reader = reader;
1146             }
1147
1148             internal override void MetaDataAvailable( SmiQueryMetaData[] md, bool nextEventIsRow ) {
1149                 if (Bid.AdvancedOn) {
1150                     Bid.Trace("<sc.SqlDataReaderSmi.ReaderEventSink.MetaDataAvailable|ADV> %d#, md.Length=%d nextEventIsRow=%d.\n", reader.ObjectID, (null != md) ? md.Length : -1, nextEventIsRow);
1151                 
1152                     if (null != md) {
1153                         for (int i=0; i < md.Length; i++) {
1154                             Bid.Trace("<sc.SqlDataReaderSmi.ReaderEventSink.MetaDataAvailable|ADV> %d#, metaData[%d] is %ls%ls\n",
1155                                             reader.ObjectID, i, md[i].GetType().ToString(), md[i].TraceString());
1156                         }
1157                     }
1158                 }
1159                 this.reader.MetaDataAvailable( md, nextEventIsRow );
1160             }
1161
1162             // Obsolete V2- method
1163             internal override void RowAvailable( ITypedGetters row ) {
1164                 if (Bid.AdvancedOn) {
1165                     Bid.Trace("<sc.SqlDataReaderSmi.ReaderEventSink.RowAvailable|ADV> %d# (v2).\n", reader.ObjectID);
1166                 }
1167                 this.reader.RowAvailable( row );
1168             }
1169
1170             internal override void RowAvailable( ITypedGettersV3 row ) {
1171                 if (Bid.AdvancedOn) {
1172                     Bid.Trace("<sc.SqlDataReaderSmi.ReaderEventSink.RowAvailable|ADV> %d# (ITypedGettersV3).\n", reader.ObjectID);
1173                 }
1174                 this.reader.RowAvailable( row );
1175             }
1176
1177             internal override void RowAvailable(SmiTypedGetterSetter rowData) {
1178                 if (Bid.AdvancedOn) {
1179                     Bid.Trace("<sc.SqlDataReaderSmi.ReaderEventSink.RowAvailable|ADV> %d# (SmiTypedGetterSetter).\n", reader.ObjectID);
1180                 }
1181                 this.reader.RowAvailable(rowData);
1182             }
1183
1184             internal override void StatementCompleted( int recordsAffected ) {
1185                 if (Bid.AdvancedOn) {
1186                     Bid.Trace("<sc.SqlDataReaderSmi.ReaderEventSink.StatementCompleted|ADV> %d# recordsAffected=%d.\n", reader.ObjectID, recordsAffected);
1187                 }
1188
1189                 // devnote: relies on SmiEventSink_Default to pass event to parent
1190                 // Both command and reader care about StatementCompleted, but for different reasons.
1191
1192                 base.StatementCompleted( recordsAffected );
1193                 this.reader.StatementCompleted( );
1194             }
1195
1196             internal override void BatchCompleted() {
1197                 if (Bid.AdvancedOn) {
1198                     Bid.Trace("<sc.SqlDataReaderSmi.ReaderEventSink.BatchCompleted|ADV> %d#.\n", reader.ObjectID);
1199                 }
1200
1201                 // devnote: relies on SmiEventSink_Default to pass event to parent
1202                 //  parent's callback *MUST* come before reader's BatchCompleted, since
1203                 //  reader will close the event stream during this call, and parent wants
1204                 //  to extract parameter values before that happens.
1205
1206                 base.BatchCompleted();
1207                 this.reader.BatchCompleted();
1208             }
1209         }
1210         
1211     }
1212 }
1213