SqlDataReaderSmi.cs 57 KB

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