New tests.
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlDataReader.cs
1 //
2 // System.Data.SqlClient.SqlDataReader.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //   Tim Coleman (tim@timcoleman.com)
8 //
9 // (C) Ximian, Inc 2002
10 // (C) Daniel Morgan 2002
11 // Copyright (C) Tim Coleman, 2002
12 //
13
14 //
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using Mono.Data.Tds.Protocol;
38 using System;
39 using System.IO;
40 using System.Text;
41 using System.Collections;
42 using System.ComponentModel;
43 using System.Data;
44 using System.Data.Common;
45 using System.Data.SqlTypes;
46
47 namespace System.Data.SqlClient
48 {
49 #if NET_2_0
50         public class SqlDataReader : DbDataReader, IDataReader, IDisposable, IDataRecord
51 #else
52         public sealed class SqlDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
53 #endif // NET_2_0
54         {
55                 #region Fields
56
57                 SqlCommand command;
58                 ArrayList dataTypeNames;
59                 bool disposed;
60                 bool isClosed;
61                 bool moreResults;
62                 int resultsRead;
63                 int rowsRead;
64                 DataTable schemaTable;
65                 bool hasRows;
66                 bool haveRead;
67                 bool readResult;
68                 bool readResultUsed;
69 #if NET_2_0
70                 int visibleFieldCount;
71 #endif
72
73                 #endregion // Fields
74
75                 const int COLUMN_NAME_IDX = 0;
76                 const int COLUMN_ORDINAL_IDX = 1;
77                 const int COLUMN_SIZE_IDX = 2;
78                 const int NUMERIC_PRECISION_IDX = 3;
79                 const int NUMERIC_SCALE_IDX = 4;
80                 const int IS_UNIQUE_IDX = 5;
81                 const int IS_KEY_IDX = 6;
82                 const int BASE_SERVER_NAME_IDX = 7;
83                 const int BASE_CATALOG_NAME_IDX = 8;
84                 const int BASE_COLUMN_NAME_IDX = 9;
85                 const int BASE_SCHEMA_NAME_IDX = 10;
86                 const int BASE_TABLE_NAME_IDX = 11;
87                 const int DATA_TYPE_IDX = 12;
88                 const int ALLOW_DBNULL_IDX = 13;
89                 const int PROVIDER_TYPE_IDX = 14;
90                 const int IS_ALIASED_IDX = 15;
91                 const int IS_EXPRESSION_IDX = 16;
92                 const int IS_IDENTITY_IDX = 17;
93                 const int IS_AUTO_INCREMENT_IDX = 18;
94                 const int IS_ROW_VERSION_IDX = 19;
95                 const int IS_HIDDEN_IDX = 20;
96                 const int IS_LONG_IDX = 21;
97                 const int IS_READ_ONLY_IDX = 22;
98
99                 #region Constructors
100
101                 internal SqlDataReader (SqlCommand command)
102                 {
103                         readResult = false;
104                         haveRead = false;
105                         readResultUsed = false;
106                         this.command = command;
107                         resultsRead = 0;
108                         isClosed = false;
109                         command.Tds.RecordsAffected = -1;
110 #if NET_2_0
111                         visibleFieldCount = 0;
112 #endif
113                         NextResult ();
114                 }
115
116                 #endregion // Constructors
117
118                 #region Properties
119
120                 public
121 #if NET_2_0
122                 override
123 #endif // NET_2_0
124                 int Depth {
125                         get { return 0; }
126                 }
127
128                 public
129 #if NET_2_0
130                 override
131 #endif // NET_2_0
132                 int FieldCount {
133                         get { return command.Tds.Columns.Count; }
134                 }
135
136                 public
137 #if NET_2_0
138                 override
139 #endif // NET_2_0
140                 bool IsClosed {
141                         get { return isClosed; }
142                 }
143
144                 public
145 #if NET_2_0
146                 override
147 #endif // NET_2_0
148                 object this [int i] {
149                         get { return GetValue (i); }
150                 }
151
152                 public
153 #if NET_2_0
154                 override
155 #endif // NET_2_0
156                 object this [string name] {
157                         get { return GetValue (GetOrdinal (name)); }
158                 }
159         
160                 public
161 #if NET_2_0
162                 override
163 #endif // NET_2_0
164                 int RecordsAffected {
165                         get {
166                                 return command.Tds.RecordsAffected; 
167                         }
168                 }
169
170                 public
171 #if NET_2_0
172                 override
173 #endif // NET_2_0
174                 bool HasRows {
175                         get {
176                                 if (haveRead) 
177                                         return readResult;
178                         
179                                 haveRead = true;
180                                 readResult = ReadRecord ();
181                                 return readResult;
182                         }
183                 }
184 #if NET_2_0
185                 public override int VisibleFieldCount {
186                         get { return visibleFieldCount; }
187                 }
188
189                 protected SqlConnection Connection {
190                         get { return command.Connection; }
191                 }
192
193                 protected bool IsCommandBehavior (CommandBehavior condition) {
194                         return condition == command.CommandBehavior;
195                 }
196 #endif
197
198                 #endregion // Properties
199
200                 #region Methods
201
202                 public
203 #if NET_2_0
204                 override
205 #endif // NET_2_0
206                 void Close ()
207                 {
208                         if (IsClosed)
209                                 return;
210                         // skip to end & read output parameters.
211                         while (NextResult ())
212                                 ;
213                         isClosed = true;
214                         command.CloseDataReader ();
215                 }
216
217                 private static DataTable ConstructSchemaTable ()
218                 {
219                         Type booleanType = typeof (bool);
220                         Type stringType = typeof (string);
221                         Type intType = typeof (int);
222                         Type typeType = typeof (Type);
223                         Type shortType = typeof (short);
224
225                         DataTable schemaTable = new DataTable ("SchemaTable");
226                         schemaTable.Columns.Add ("ColumnName", stringType);
227                         schemaTable.Columns.Add ("ColumnOrdinal", intType);
228                         schemaTable.Columns.Add ("ColumnSize", intType);
229                         schemaTable.Columns.Add ("NumericPrecision", shortType);
230                         schemaTable.Columns.Add ("NumericScale", shortType);
231                         schemaTable.Columns.Add ("IsUnique", booleanType);
232                         schemaTable.Columns.Add ("IsKey", booleanType);
233                         schemaTable.Columns.Add ("BaseServerName", stringType);
234                         schemaTable.Columns.Add ("BaseCatalogName", stringType);
235                         schemaTable.Columns.Add ("BaseColumnName", stringType);
236                         schemaTable.Columns.Add ("BaseSchemaName", stringType);
237                         schemaTable.Columns.Add ("BaseTableName", stringType);
238                         schemaTable.Columns.Add ("DataType", typeType);
239                         schemaTable.Columns.Add ("AllowDBNull", booleanType);
240                         schemaTable.Columns.Add ("ProviderType", intType);
241                         schemaTable.Columns.Add ("IsAliased", booleanType);
242                         schemaTable.Columns.Add ("IsExpression", booleanType);
243                         schemaTable.Columns.Add ("IsIdentity", booleanType);
244                         schemaTable.Columns.Add ("IsAutoIncrement", booleanType);
245                         schemaTable.Columns.Add ("IsRowVersion", booleanType);
246                         schemaTable.Columns.Add ("IsHidden", booleanType);
247                         schemaTable.Columns.Add ("IsLong", booleanType);
248                         schemaTable.Columns.Add ("IsReadOnly", booleanType);
249
250                         return schemaTable;
251                 }
252                 
253                 private void GetSchemaRowTypeName (TdsColumnType ctype, int csize, out string typeName) 
254                 {
255                         int dbType;
256                         bool isLong;
257                         Type fieldType;
258                         
259                         GetSchemaRowType (ctype, csize, out dbType, out fieldType, out isLong, out typeName);
260                 }
261
262                 private void GetSchemaRowFieldType (TdsColumnType ctype, int csize, out Type fieldType) 
263                 {
264                         int dbType;
265                         bool isLong;
266                         string typeName;
267                         
268                         GetSchemaRowType (ctype, csize, out dbType, out fieldType, out isLong, out typeName);
269                 }
270
271                 private void GetSchemaRowDbType (TdsColumnType ctype, int csize, out int dbType) 
272                 {
273                         Type fieldType;
274                         bool isLong;
275                         string typeName;
276                         
277                         GetSchemaRowType (ctype, csize, out dbType, out fieldType, out isLong, out typeName);
278                 }
279                 
280                 private void GetSchemaRowType (TdsColumnType ctype, int csize, 
281                                                out int dbType, out Type fieldType, 
282                                                out bool isLong, out string typeName)
283                 {
284                         dbType = -1;
285                         typeName = string.Empty;
286                         isLong = false;
287                         fieldType = typeof (Type);
288                         
289                         switch (ctype) {
290                                 case TdsColumnType.Int1:
291                                 case TdsColumnType.Int2:
292                                 case TdsColumnType.Int4:
293                                 case TdsColumnType.IntN:
294                                         switch (csize) {
295                                         case 1:
296                                                 typeName = "tinyint";
297                                                 dbType = (int) SqlDbType.TinyInt;
298                                                 fieldType = typeof (byte);
299                                                 isLong = false;
300                                                 break;
301                                         case 2:
302                                                 typeName = "smallint";
303                                                 dbType = (int) SqlDbType.SmallInt;
304                                                 fieldType = typeof (short);
305                                                 isLong = false;
306                                                 break;
307                                         case 4:
308                                                 typeName = "int";
309                                                 dbType = (int) SqlDbType.Int;
310                                                 fieldType = typeof (int);
311                                                 isLong = false;
312                                                 break;
313                                         case 8:
314                                                 typeName = "bigint";
315                                                 dbType = (int) SqlDbType.BigInt;
316                                                 fieldType = typeof (long);
317                                                 isLong = false;
318                                                 break;
319                                         }
320                                         break;
321                                 case TdsColumnType.Real:
322                                 case TdsColumnType.Float8:
323                                 case TdsColumnType.FloatN:
324                                         switch (csize) {
325                                         case 4:
326                                                 typeName = "real";
327                                                 dbType = (int) SqlDbType.Real;
328                                                 fieldType = typeof (float);
329                                                 isLong = false;
330                                                 break;
331                                         case 8:
332                                                 typeName = "float";
333                                                 dbType = (int) SqlDbType.Float;
334                                                 fieldType = typeof (double);
335                                                 isLong = false;
336                                                 break;
337                                         }
338                                         break;
339                                 case TdsColumnType.Image :
340                                         typeName = "image";
341                                         dbType = (int) SqlDbType.Image;
342                                         fieldType = typeof (byte[]);
343                                         isLong = true;
344                                         break;
345                                 case TdsColumnType.Text :
346                                         typeName = "text";
347                                         dbType = (int) SqlDbType.Text;
348                                         fieldType = typeof (string);
349                                         isLong = true;
350                                         break;
351                                 case TdsColumnType.UniqueIdentifier :
352                                         typeName = "uniqueidentifier";
353                                         dbType = (int) SqlDbType.UniqueIdentifier;
354                                         fieldType = typeof (Guid);
355                                         isLong = false;
356                                         break;
357                                 case TdsColumnType.VarBinary :
358                                 case TdsColumnType.BigVarBinary :
359                                         typeName = "varbinary";
360                                         dbType = (int) SqlDbType.VarBinary;
361                                         fieldType = typeof (byte[]);
362                                         isLong = true;
363                                         break;
364                                 case TdsColumnType.VarChar :
365                                 case TdsColumnType.BigVarChar :
366                                         typeName = "varchar";
367                                         dbType = (int) SqlDbType.VarChar;
368                                         fieldType = typeof (string);
369                                         isLong = false;
370                                         break;
371                                 case TdsColumnType.Binary :
372                                 case TdsColumnType.BigBinary :
373                                         typeName = "binary";
374                                         dbType = (int) SqlDbType.Binary;
375                                         fieldType = typeof (byte[]);
376                                         isLong = true;
377                                         break;
378                                 case TdsColumnType.Char :
379                                 case TdsColumnType.BigChar :
380                                         typeName = "char";
381                                         dbType = (int) SqlDbType.Char;
382                                         fieldType = typeof (string);
383                                         isLong = false;
384                                         break;
385                                 case TdsColumnType.Bit :
386                                 case TdsColumnType.BitN :
387                                         typeName = "bit";
388                                         dbType = (int) SqlDbType.Bit;
389                                         fieldType = typeof (bool);
390                                         isLong = false;
391                                         break;
392                                 case TdsColumnType.DateTime4 :
393                                 case TdsColumnType.DateTime :
394                                 case TdsColumnType.DateTimeN :
395                                         typeName = "datetime";
396                                         dbType = (int) SqlDbType.DateTime;
397                                         fieldType = typeof (DateTime);
398                                         isLong = false;
399                                         break;
400                                 case TdsColumnType.Money :
401                                 case TdsColumnType.MoneyN :
402                                 case TdsColumnType.Money4 :
403                                         typeName = "money";
404                                         dbType = (int) SqlDbType.Money;
405                                         fieldType = typeof (decimal);
406                                         isLong = false;
407                                         break;
408                                 case TdsColumnType.NText :
409                                         typeName = "ntext";
410                                         dbType = (int) SqlDbType.NText;
411                                         fieldType = typeof (string);
412                                         isLong = true;
413                                         break;
414                                 case TdsColumnType.NVarChar :
415                                         typeName = "nvarchar";
416                                         dbType = (int) SqlDbType.NVarChar;
417                                         fieldType = typeof (string);
418                                         isLong = false;
419                                         break;
420                                 case TdsColumnType.Decimal :
421                                 case TdsColumnType.Numeric :
422                                         typeName = "decimal";
423                                         dbType = (int) SqlDbType.Decimal;
424                                         fieldType = typeof (decimal);
425                                         isLong = false;
426                                         break;
427                                 case TdsColumnType.NChar :
428                                         typeName = "nchar";
429                                         dbType = (int) SqlDbType.NChar;
430                                         fieldType = typeof (string);
431                                         isLong = false;
432                                         break;
433                                 case TdsColumnType.SmallMoney :
434                                         typeName = "smallmoney";
435                                         dbType = (int) SqlDbType.SmallMoney;
436                                         fieldType = typeof (decimal);
437                                         isLong = false;
438                                         break;
439                                 default :
440                                         typeName = "variant";
441                                         dbType = (int) SqlDbType.Variant;
442                                         fieldType = typeof (object);
443                                         isLong = false;
444                                         break;
445                         }
446                 }
447
448 #if NET_2_0
449                 new
450 #endif
451                 void Dispose (bool disposing)
452                 {
453                         if (!disposed) {
454                                 if (disposing) {
455                                         if (schemaTable != null)
456                                                 schemaTable.Dispose ();
457                                         Close ();
458                                         command = null;
459                                 }
460                                 disposed = true;
461                         }
462                 }
463
464                 public 
465 #if NET_2_0
466                 override
467 #endif // NET_2_0
468                 bool GetBoolean (int i)
469                 {
470                         object value = GetValue (i);
471                         if (!(value is bool)) {
472                                 if (value is DBNull) throw new SqlNullValueException ();
473                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
474                         }
475                         return (bool) value;
476                 }
477
478                 public
479 #if NET_2_0
480                 override
481 #endif // NET_2_0
482                 byte GetByte (int i)
483                 {
484                         object value = GetValue (i);
485                         if (!(value is byte)) {
486                                 if (value is DBNull) throw new SqlNullValueException ();
487                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
488                         }
489                         return (byte) value;
490                 }
491
492                 public
493 #if NET_2_0
494                 override
495 #endif // NET_2_0
496                 long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
497                 {
498                         if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
499                                 try {
500                                         long len = ((Tds)command.Tds).GetSequentialColumnValue (i, dataIndex, buffer, bufferIndex, length);
501                                         if (len == -1)
502                                                 throw new InvalidCastException ("Invalid attempt to GetBytes on column "
503                                                                                 + "'" + command.Tds.Columns[i]["ColumnName"] +
504                                                                                 "'." + "The GetBytes function"
505                                                                                 + " can only be used on columns of type Text, NText, or Image");
506                                         return len;
507                                 } catch (TdsInternalException ex) {
508                                         command.Connection.Close ();
509                                         throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
510                                 }
511                         }
512
513                         object value = GetValue (i);
514                         if (!(value is byte [])) {
515                                 if (value is DBNull) throw new SqlNullValueException ();
516                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
517                         }
518                         
519                         if ( buffer == null )
520                                 return ((byte []) value).Length; // Return length of data
521
522                         // Copy data into buffer
523                         int availLen = (int) ( ( (byte []) value).Length - dataIndex);
524                         if (availLen < length)
525                                 length = availLen;
526                         Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
527                         return length; // return actual read count
528                 }
529
530                 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
531                 public
532 #if NET_2_0
533                 override
534 #endif // NET_2_0
535                 char GetChar (int i)
536                 {
537                         object value = GetValue (i);
538                         if (!(value is char)) {
539                                 if (value is DBNull) throw new SqlNullValueException ();
540                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
541                         }
542                         return (char) value;
543                 }
544
545                 public
546 #if NET_2_0
547                 override
548 #endif // NET_2_0
549                 long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
550                 {
551                         if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
552                                 Encoding encoding = null;
553                                 byte mul = 1;
554                                 TdsColumnType colType = (TdsColumnType) command.Tds.Columns[i]["ColumnType"];
555                                 switch (colType) {
556                                         case TdsColumnType.Text :
557                                         case TdsColumnType.VarChar:
558                                         case TdsColumnType.Char:
559                                         case TdsColumnType.BigVarChar:
560                                                 encoding = Encoding.ASCII;
561                                                 break;
562                                         case TdsColumnType.NText :
563                                         case TdsColumnType.NVarChar:
564                                         case TdsColumnType.NChar:
565                                                 encoding = Encoding.Unicode;
566                                                 mul = 2;
567                                                 break;
568                                         default :
569                                                 return -1;
570                                 }
571
572                                 long count = 0;
573                                 if (buffer == null) {
574                                         count = GetBytes (i,0,(byte[]) null,0,0);
575                                         return (count/mul);
576                                 }
577
578                                 length *= mul;
579                                 byte[] arr = new byte [length];
580                                 count = GetBytes (i, dataIndex, arr, 0, length);
581                                 if (count == -1)
582                                         throw new InvalidCastException ("Specified cast is not valid");
583
584                                 Char[] val = encoding.GetChars (arr, 0, (int)count);
585                                 val.CopyTo (buffer, bufferIndex);
586                                 return val.Length;
587                         }
588
589                         char [] valueBuffer;
590                         object value = GetValue (i);
591                         
592                         if (value is char[])
593                                 valueBuffer = (char[])value;
594                         else if (value is string)
595                                 valueBuffer = ((string)value).ToCharArray();
596                         else {
597                                 if (value is DBNull) throw new SqlNullValueException ();
598                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
599                         }
600                         
601                         if ( buffer == null ) {
602                                 // Return length of data
603                                 return valueBuffer.Length;
604                         }
605                         else {
606                                 // Copy data into buffer
607                                 Array.Copy (valueBuffer, (int) dataIndex, buffer, bufferIndex, length);
608                                 return valueBuffer.Length - dataIndex;
609                         }
610                 }
611                 
612 #if !NET_2_0
613                 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
614                 public new IDataReader GetData (int i)
615                 {
616                         return ((IDataReader) this [i]);
617                 }
618 #endif
619
620                 public
621 #if NET_2_0
622                 override
623 #endif // NET_2_0
624                 string GetDataTypeName (int i)
625                 {
626                         TdsColumnType ctype;
627                         string datatypeName;
628                         int csize;
629                         
630                         if (i < 0 || i >= command.Tds.Columns.Count)
631                                 throw new IndexOutOfRangeException ();
632 #if NET_2_0
633                         ctype = (TdsColumnType) command.Tds.Columns[i].ColumnType;
634                         csize = (int) command.Tds.Columns[i].ColumnSize;
635 #else
636                         ctype = (TdsColumnType) command.Tds.Columns[i]["ColumnType"];
637                         csize = (int) command.Tds.Columns[i]["ColumnSize"];
638 #endif
639                         GetSchemaRowTypeName (ctype, csize, out datatypeName);
640                         return datatypeName;
641                 }
642
643                 public
644 #if NET_2_0
645                 override
646 #endif // NET_2_0
647                 DateTime GetDateTime (int i)
648                 {
649                         object value = GetValue (i);
650                         if (!(value is DateTime)) {
651                                 if (value is DBNull) throw new SqlNullValueException ();
652                                 else throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
653                         }
654                         return (DateTime) value;
655                 }
656
657                 public
658 #if NET_2_0
659                 override
660 #endif // NET_2_0
661                 decimal GetDecimal (int i)
662                 {
663                         object value = GetValue (i);
664                         if (!(value is decimal)) {
665                                 if (value is DBNull) throw new SqlNullValueException ();
666                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
667                         }
668                         return (decimal) value;
669                 }
670
671                 public
672 #if NET_2_0
673                 override
674 #endif // NET_2_0
675                 double GetDouble (int i)
676                 {
677                         object value = GetValue (i);
678                         if (!(value is double)) {
679                                 if (value is DBNull) throw new SqlNullValueException ();
680                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
681                         }
682                         return (double) value;
683                 }
684
685                 public
686 #if NET_2_0
687                 override
688 #endif // NET_2_0
689                 Type GetFieldType (int i)
690                 {
691                         TdsColumnType ctype;
692                         Type fieldType;
693                         int csize;
694                         
695                         if (i < 0 || i >= command.Tds.Columns.Count)
696                                 throw new IndexOutOfRangeException ();
697 #if NET_2_0
698                         ctype = (TdsColumnType) command.Tds.Columns[i].ColumnType;
699                         csize = (int) command.Tds.Columns[i].ColumnSize;
700 #else
701                         ctype = (TdsColumnType) command.Tds.Columns[i]["ColumnType"];
702                         csize = (int) command.Tds.Columns[i]["ColumnSize"];
703 #endif
704                         GetSchemaRowFieldType (ctype, csize, out fieldType);                    
705                         return fieldType;
706                 }
707
708                 public
709 #if NET_2_0
710                 override
711 #endif // NET_2_0
712                 float GetFloat (int i)
713                 {
714                         object value = GetValue (i);
715                         if (!(value is float)) {
716                                 if (value is DBNull) throw new SqlNullValueException ();
717                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
718                         }
719                         return (float) value;
720                 }
721
722                 public
723 #if NET_2_0
724                 override
725 #endif // NET_2_0
726                 Guid GetGuid (int i)
727                 {
728                         object value = GetValue (i);
729                         if (!(value is Guid)) {
730                                 if (value is DBNull) throw new SqlNullValueException ();
731                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
732                         }
733                         return (Guid) value;
734                 }
735
736                 public
737 #if NET_2_0
738                 override
739 #endif // NET_2_0
740                 short GetInt16 (int i)
741                 {
742                         object value = GetValue (i);
743                         if (!(value is short)) {
744                                 if (value is DBNull) throw new SqlNullValueException ();
745                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
746                         }
747                         return (short) value;
748                 }
749
750                 public
751 #if NET_2_0
752                 override
753 #endif // NET_2_0
754                 int GetInt32 (int i)
755                 {
756                         object value = GetValue (i);
757                         if (!(value is int)) {
758                                 if (value is DBNull) throw new SqlNullValueException ();
759                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
760                         }
761                         return (int) value;
762                 }
763
764                 public
765 #if NET_2_0
766                 override
767 #endif // NET_2_0
768                 long GetInt64 (int i)
769                 {
770                         object value = GetValue (i);
771                         if (!(value is long)) {
772                                 if (value is DBNull) throw new SqlNullValueException ();
773                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
774                         }
775                         return (long) value;
776                 }
777
778                 public
779 #if NET_2_0
780                 override
781 #endif // NET_2_0
782                 string GetName (int i)
783                 {
784                         if (i < 0 || i >= command.Tds.Columns.Count)
785                                 throw new IndexOutOfRangeException ();
786 #if NET_2_0
787                         return (string) command.Tds.Columns[i].ColumnName;
788 #else
789                         return (string) command.Tds.Columns[i]["ColumnName"];
790 #endif
791                 }
792
793                 public
794 #if NET_2_0
795                 override
796 #endif // NET_2_0
797                 int GetOrdinal (string name)
798                 {
799                         string colName;
800                         foreach (TdsDataColumn schema in command.Tds.Columns) {
801 #if NET_2_0
802                                 colName = schema.ColumnName;
803                                 if (colName.Equals (name) || String.Compare (colName, name, true) == 0)
804                                         return (int) schema.ColumnOrdinal;
805 #else
806                                 colName = (string) schema["ColumnName"];
807                                 if (colName.Equals (name) || String.Compare (colName, name, true) == 0)
808                                         return (int) schema["ColumnOrdinal"];
809 #endif                                          
810                         }                       
811                         throw new IndexOutOfRangeException ();
812                 }
813
814                 public
815 #if NET_2_0
816                 override
817 #endif // NET_2_0
818                 DataTable GetSchemaTable ()
819                 {
820                         ValidateState ();
821
822                         if (schemaTable == null)
823                                 schemaTable = ConstructSchemaTable ();
824
825                         if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
826                                 return schemaTable;
827
828                         if (!moreResults)
829                                 return null;
830
831                         dataTypeNames = new ArrayList (command.Tds.Columns.Count);
832
833                         foreach (TdsDataColumn schema in command.Tds.Columns) {
834                                 DataRow row = schemaTable.NewRow ();
835
836 #if NET_2_0
837                                 row [COLUMN_NAME_IDX]           = GetSchemaValue (schema.ColumnName);
838                                 row [COLUMN_ORDINAL_IDX]                = GetSchemaValue (schema.ColumnOrdinal);
839                                 row [IS_UNIQUE_IDX]             = GetSchemaValue (schema.IsUnique);
840                                 row [IS_AUTO_INCREMENT_IDX]             = GetSchemaValue (schema.IsAutoIncrement);
841                                 row [IS_ROW_VERSION_IDX]                = GetSchemaValue (schema.IsRowVersion);
842                                 row [IS_HIDDEN_IDX]             = GetSchemaValue (schema.IsHidden);
843                                 row [IS_IDENTITY_IDX]           = GetSchemaValue (schema.IsIdentity);
844                                 row [COLUMN_SIZE_IDX]           = GetSchemaValue (schema.ColumnSize);
845                                 row [NUMERIC_PRECISION_IDX]     = GetSchemaValue (schema.NumericPrecision);
846                                 row [NUMERIC_SCALE_IDX]         = GetSchemaValue (schema.NumericScale);
847                                 row [IS_KEY_IDX]                        = GetSchemaValue (schema.IsKey);
848                                 row [IS_ALIASED_IDX]            = GetSchemaValue (schema.IsAliased);
849                                 row [IS_EXPRESSION_IDX]         = GetSchemaValue (schema.IsExpression);
850                                 row [IS_READ_ONLY_IDX]          = GetSchemaValue (schema.IsReadOnly);
851                                 row [BASE_SERVER_NAME_IDX]              = GetSchemaValue (schema.BaseServerName);
852                                 row [BASE_CATALOG_NAME_IDX]             = GetSchemaValue (schema.BaseCatalogName);
853                                 row [BASE_COLUMN_NAME_IDX]              = GetSchemaValue (schema.BaseColumnName);
854                                 row [BASE_SCHEMA_NAME_IDX]              = GetSchemaValue (schema.BaseSchemaName);
855                                 row [BASE_TABLE_NAME_IDX]               = GetSchemaValue (schema.BaseTableName);
856                                 row [ALLOW_DBNULL_IDX]          = GetSchemaValue (schema.AllowDBNull);
857 #else
858                                 row ["ColumnName"]              = GetSchemaValue (schema, "ColumnName");
859                                 row ["ColumnOrdinal"]           = GetSchemaValue (schema, "ColumnOrdinal");
860                                 row ["IsUnique"]                = GetSchemaValue (schema, "IsUnique");
861                                 row ["IsAutoIncrement"]         = GetSchemaValue (schema, "IsAutoIncrement");
862                                 row ["IsRowVersion"]            = GetSchemaValue (schema, "IsRowVersion");
863                                 row ["IsHidden"]                = GetSchemaValue (schema, "IsHidden");
864                                 row ["IsIdentity"]              = GetSchemaValue (schema, "IsIdentity");
865                                 row ["ColumnSize"]              = GetSchemaValue (schema, "ColumnSize");
866                                 row ["NumericPrecision"]        = GetSchemaValue (schema, "NumericPrecision");
867                                 row ["NumericScale"]            = GetSchemaValue (schema, "NumericScale");
868                                 row ["IsKey"]                   = GetSchemaValue (schema, "IsKey");
869                                 row ["IsAliased"]               = GetSchemaValue (schema, "IsAliased");
870                                 row ["IsExpression"]            = GetSchemaValue (schema, "IsExpression");
871                                 row ["IsReadOnly"]              = GetSchemaValue (schema, "IsReadOnly");
872                                 row ["BaseServerName"]          = GetSchemaValue (schema, "BaseServerName");
873                                 row ["BaseCatalogName"]         = GetSchemaValue (schema, "BaseCatalogName");
874                                 row ["BaseColumnName"]          = GetSchemaValue (schema, "BaseColumnName");
875                                 row ["BaseSchemaName"]          = GetSchemaValue (schema, "BaseSchemaName");
876                                 row ["BaseTableName"]           = GetSchemaValue (schema, "BaseTableName");
877                                 row ["AllowDBNull"]             = GetSchemaValue (schema, "AllowDBNull");
878 #endif
879                                 // We don't always get the base column name.
880                                 if (row [BASE_COLUMN_NAME_IDX] == DBNull.Value)
881                                         row [BASE_COLUMN_NAME_IDX] = row [COLUMN_NAME_IDX];
882
883                                 TdsColumnType ctype;
884                                 int csize, dbType;                              
885                                 Type fieldType;
886                                 bool isLong;
887                                 string typeName;
888 #if NET_2_0
889                                 ctype = (TdsColumnType) schema.ColumnType;
890                                 csize = (int) schema.ColumnSize;
891 #else
892                                 ctype = (TdsColumnType) schema ["ColumnType"];
893                                 csize = (int) schema ["ColumnSize"];
894 #endif
895
896                                 GetSchemaRowType (ctype, csize, out dbType, 
897                                                                         out fieldType, out isLong, out typeName);
898                                 
899                                 dataTypeNames.Add (typeName);
900                                 row [PROVIDER_TYPE_IDX] = dbType;
901                                 row [DATA_TYPE_IDX] = fieldType;
902                                 row [IS_LONG_IDX] = isLong;                     
903 #if NET_2_0
904                                 if ((bool)row [IS_HIDDEN_IDX] == false)
905                                         visibleFieldCount += 1;
906 #endif
907
908                                 schemaTable.Rows.Add (row);
909                         }
910                         return schemaTable;
911                 }
912
913                 private static object GetSchemaValue (TdsDataColumn schema, string key)
914                 {
915                         object val = schema [key];
916                         if (val != null)
917                                 return val;
918                         else
919                                 return DBNull.Value;
920                 }
921
922                 static object GetSchemaValue (object value)
923                 {
924                         if (value == null)
925                                 return DBNull.Value;
926
927                         return value;
928                 }
929                 
930                 public
931 #if NET_2_0
932                 virtual
933 #endif
934                 SqlBinary GetSqlBinary (int i)
935                 {
936                         object value = GetSqlValue (i);
937                         if (!(value is SqlBinary))
938                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
939                         return (SqlBinary) value;
940                 }
941
942                 public
943 #if NET_2_0
944                 virtual
945 #endif
946                 SqlBoolean GetSqlBoolean (int i) 
947                 {
948                         object value = GetSqlValue (i);
949                         if (!(value is SqlBoolean))
950                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
951                         return (SqlBoolean) value;
952                 }
953
954                 public
955 #if NET_2_0
956                 virtual
957 #endif
958                 SqlByte GetSqlByte (int i)
959                 {
960                         object value = GetSqlValue (i);
961                         if (!(value is SqlByte))
962                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
963                         return (SqlByte) value;
964                 }
965
966                 public
967 #if NET_2_0
968                 virtual
969 #endif
970                 SqlDateTime GetSqlDateTime (int i)
971                 {
972                         object value = GetSqlValue (i);
973                         if (!(value is SqlDateTime))
974                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
975                         return (SqlDateTime) value;
976                 }
977
978                 public
979 #if NET_2_0
980                 virtual
981 #endif
982                 SqlDecimal GetSqlDecimal (int i)
983                 {
984                         object value = GetSqlValue (i);
985                         if (!(value is SqlDecimal))
986                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
987                         return (SqlDecimal) value;
988                 }
989
990                 public
991 #if NET_2_0
992                 virtual
993 #endif
994                 SqlDouble GetSqlDouble (int i)
995                 {
996                         object value = GetSqlValue (i);
997                         if (!(value is SqlDouble))
998                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
999                         return (SqlDouble) value;
1000                 }
1001
1002                 public
1003 #if NET_2_0
1004                 virtual
1005 #endif
1006                 SqlGuid GetSqlGuid (int i)
1007                 {
1008                         object value = GetSqlValue (i);
1009                         if (!(value is SqlGuid))
1010                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1011                         return (SqlGuid) value;
1012                 }
1013
1014                 public
1015 #if NET_2_0
1016                 virtual
1017 #endif
1018                 SqlInt16 GetSqlInt16 (int i)
1019                 {
1020                         object value = GetSqlValue (i);
1021                         if (!(value is SqlInt16))
1022                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1023                         return (SqlInt16) value;
1024                 }
1025
1026                 public
1027 #if NET_2_0
1028                 virtual
1029 #endif
1030                 SqlInt32 GetSqlInt32 (int i)
1031                 {
1032                         object value = GetSqlValue (i);
1033                         if (!(value is SqlInt32))
1034                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1035                         return (SqlInt32) value;
1036                 }
1037
1038                 public
1039 #if NET_2_0
1040                 virtual
1041 #endif
1042                 SqlInt64 GetSqlInt64 (int i)
1043                 {
1044                         object value = GetSqlValue (i);
1045                         // TDS 7.0 returns bigint as decimal(19,0)
1046                         if (value is SqlDecimal) {
1047                                 TdsDataColumn schema = command.Tds.Columns[i];
1048                                 byte precision, scale;
1049
1050 #if NET_2_0
1051                                 precision = (byte)schema.NumericPrecision;
1052                                 scale = (byte)schema.NumericScale;
1053 #else
1054                                 precision = (byte)schema["NumericPrecision"];
1055                                 scale = (byte)schema["NumericScale"];
1056 #endif
1057                                 if (precision == 19 && scale == 0)
1058                                         value = (SqlInt64) (SqlDecimal) value;
1059                         }
1060                         if (!(value is SqlInt64))
1061                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1062                         return (SqlInt64) value;
1063                 }
1064
1065                 public
1066 #if NET_2_0
1067                 virtual
1068 #endif
1069                 SqlMoney GetSqlMoney (int i)
1070                 {
1071                         object value = GetSqlValue (i);
1072                         if (!(value is SqlMoney))
1073                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1074                         return (SqlMoney) value;
1075                 }
1076
1077                 public
1078 #if NET_2_0
1079                 virtual
1080 #endif
1081                 SqlSingle GetSqlSingle (int i)
1082                 {
1083                         object value = GetSqlValue (i);
1084                         if (!(value is SqlSingle))
1085                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1086                         return (SqlSingle) value;
1087                 }
1088
1089                 public
1090 #if NET_2_0
1091                 virtual
1092 #endif
1093                 SqlString GetSqlString (int i)
1094                 {
1095                         object value = GetSqlValue (i);
1096                         if (!(value is SqlString))
1097                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1098                         return (SqlString) value;
1099                 }
1100
1101 #if NET_2_0
1102                 public virtual SqlXml GetSqlXml (int i)
1103                 {
1104                         object value = GetSqlValue (i);
1105                         if (!(value is SqlXml)) {
1106                                 if (value is DBNull) throw new SqlNullValueException ();
1107                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1108                         }
1109                         return (SqlXml) value;
1110                 }
1111 #endif // NET_2_0
1112
1113                 public
1114 #if NET_2_0
1115                 virtual
1116 #endif
1117                 object GetSqlValue (int i)
1118                 {
1119                         int dbType, csize;
1120                         TdsColumnType ctype;
1121                         
1122                         if (i < 0 || i >= command.Tds.Columns.Count)
1123                                 throw new IndexOutOfRangeException ();
1124 #if NET_2_0
1125                         ctype = (TdsColumnType) command.Tds.Columns[i].ColumnType;
1126                         csize = (int) command.Tds.Columns[i].ColumnSize;
1127 #else
1128                         ctype = (TdsColumnType) command.Tds.Columns[i]["ColumnType"];
1129                         csize = (int) command.Tds.Columns[i]["ColumnSize"];
1130 #endif
1131                         GetSchemaRowDbType (ctype, csize, out dbType);
1132                         SqlDbType type = (SqlDbType) dbType;
1133                         object value = GetValue (i);
1134
1135                         switch (type) {
1136                         case SqlDbType.BigInt:
1137                                 if (value == DBNull.Value)
1138                                         return SqlInt64.Null;
1139                                 return (SqlInt64) ((long) value);
1140                         case SqlDbType.Binary:
1141                         case SqlDbType.Image:
1142                         case SqlDbType.VarBinary:
1143                         case SqlDbType.Timestamp:
1144                                 if (value == DBNull.Value)
1145                                         return SqlBinary.Null;
1146                                 return (SqlBinary) ((byte[]) value);
1147                         case SqlDbType.Bit:
1148                                 if (value == DBNull.Value)
1149                                         return SqlBoolean.Null;
1150                                 return (SqlBoolean) ((bool) value);
1151                         case SqlDbType.Char:
1152                         case SqlDbType.NChar:
1153                         case SqlDbType.NText:
1154                         case SqlDbType.NVarChar:
1155                         case SqlDbType.Text:
1156                         case SqlDbType.VarChar:
1157                                 if (value == DBNull.Value)
1158                                         return SqlString.Null;
1159                                 return (SqlString) ((string) value);
1160                         case SqlDbType.DateTime:
1161                         case SqlDbType.SmallDateTime:
1162                                 if (value == DBNull.Value)
1163                                         return SqlDateTime.Null;
1164                                 return (SqlDateTime) ((DateTime) value);
1165                         case SqlDbType.Decimal:
1166                                 if (value == DBNull.Value)
1167                                         return SqlDecimal.Null;
1168                                 if (value is TdsBigDecimal)
1169                                         return SqlDecimal.FromTdsBigDecimal ((TdsBigDecimal) value);
1170                                 return (SqlDecimal) ((decimal) value);
1171                         case SqlDbType.Float:
1172                                 if (value == DBNull.Value)
1173                                         return SqlDouble.Null;
1174                                 return (SqlDouble) ((double) value);
1175                         case SqlDbType.Int:
1176                                 if (value == DBNull.Value)
1177                                         return SqlInt32.Null;
1178                                 return (SqlInt32) ((int) value);
1179                         case SqlDbType.Money:
1180                         case SqlDbType.SmallMoney:
1181                                 if (value == DBNull.Value)
1182                                         return SqlMoney.Null;
1183                                 return (SqlMoney) ((decimal) value);
1184                         case SqlDbType.Real:
1185                                 if (value == DBNull.Value)
1186                                         return SqlSingle.Null;
1187                                 return (SqlSingle) ((float) value);
1188                         case SqlDbType.UniqueIdentifier:
1189                                 if (value == DBNull.Value)
1190                                         return SqlGuid.Null;
1191                                 return (SqlGuid) ((Guid) value);
1192                         case SqlDbType.SmallInt:
1193                                 if (value == DBNull.Value)
1194                                         return SqlInt16.Null;
1195                                 return (SqlInt16) ((short) value);
1196                         case SqlDbType.TinyInt:
1197                                 if (value == DBNull.Value)
1198                                         return SqlByte.Null;
1199                                 return (SqlByte) ((byte) value);
1200 #if NET_2_0
1201                         case SqlDbType.Xml:
1202                                 if (value == DBNull.Value)
1203                                         return SqlByte.Null;
1204                                 return (SqlXml) value;
1205 #endif
1206                         }
1207
1208                         throw new InvalidOperationException ("The type of this column is unknown.");
1209                 }
1210
1211                 public
1212 #if NET_2_0
1213                 virtual
1214 #endif
1215                 int GetSqlValues (object[] values)
1216                 {
1217                         int count = 0;
1218                         int columnCount = command.Tds.Columns.Count;
1219                         int arrayCount = values.Length;
1220
1221                         if (arrayCount > columnCount)
1222                                 count = columnCount;
1223                         else
1224                                 count = arrayCount;
1225
1226                         for (int i = 0; i < count; i += 1) 
1227                                 values [i] = GetSqlValue (i);
1228
1229                         return count;
1230                 }
1231
1232                 public
1233 #if NET_2_0
1234                 override
1235 #endif // NET_2_0
1236                 string GetString (int i)
1237                 {
1238                         object value = GetValue (i);
1239                         if (!(value is string)) {
1240                                 if (value is DBNull) throw new SqlNullValueException ();
1241                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1242                         }
1243                         return (string) value;
1244                 }
1245
1246                 public
1247 #if NET_2_0
1248                 override
1249 #endif // NET_2_0
1250                 object GetValue (int i)
1251                 {
1252                         if (i < 0 || i >= command.Tds.Columns.Count)
1253                                 throw new IndexOutOfRangeException ();
1254
1255                         try {
1256                                 if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
1257                                         return ((Tds)command.Tds).GetSequentialColumnValue (i);
1258                                 }
1259                         } catch (TdsInternalException ex) {
1260                                 command.Connection.Close ();
1261                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1262                         }
1263
1264                         return command.Tds.ColumnValues [i];
1265                 }
1266
1267                 public
1268 #if NET_2_0
1269                 override
1270 #endif // NET_2_0
1271                 int GetValues (object[] values)
1272                 {
1273                         int len = values.Length;
1274                         int bigDecimalIndex = command.Tds.ColumnValues.BigDecimalIndex;
1275
1276                         // If a four-byte decimal is stored, then we can't convert to
1277                         // a native type.  Throw an OverflowException.
1278                         if (bigDecimalIndex >= 0 && bigDecimalIndex < len)
1279                                 throw new OverflowException ();
1280                         try {
1281                                 command.Tds.ColumnValues.CopyTo (0, values, 0,
1282                                                                  len > command.Tds.ColumnValues.Count ? command.Tds.ColumnValues.Count : len);
1283                         } catch (TdsInternalException ex) {
1284                                 command.Connection.Close ();
1285                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1286                         }
1287                         return (len < FieldCount ? len : FieldCount);
1288                 }
1289
1290 #if !NET_2_0
1291                 void IDisposable.Dispose ()
1292                 {
1293                         Dispose (true);
1294                         GC.SuppressFinalize (this);
1295                 }
1296 #endif
1297
1298 #if NET_2_0
1299                 public override IEnumerator GetEnumerator ()
1300 #else
1301                 IEnumerator IEnumerable.GetEnumerator ()
1302 #endif
1303                 {
1304                         return new DbEnumerator (this);
1305                 }
1306
1307                 public
1308 #if NET_2_0
1309                 override
1310 #endif // NET_2_0
1311                 bool IsDBNull (int i)
1312                 {
1313                         return GetValue (i) == DBNull.Value;
1314                 }
1315
1316                 public
1317 #if NET_2_0
1318                 override
1319 #endif // NET_2_0
1320                 bool NextResult ()
1321                 {
1322                         ValidateState ();
1323
1324                         if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0)
1325                                 return false;
1326
1327                         try {
1328                                 moreResults = command.Tds.NextResult ();
1329                         } catch (TdsInternalException ex) {
1330                                 command.Connection.Close ();
1331                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1332                         }
1333                         if (!moreResults)
1334                                 command.GetOutputParameters ();
1335                         else {
1336                                 // new schema - don't do anything except reset schemaTable as command.Tds.Columns is already updated
1337                                 schemaTable = null;
1338                                 dataTypeNames = null;
1339                         }
1340
1341                         rowsRead = 0;
1342                         resultsRead += 1;
1343                         return moreResults;
1344                 }
1345
1346                 public
1347 #if NET_2_0
1348                 override
1349 #endif // NET_2_0
1350                 bool Read ()
1351                 {
1352                         ValidateState ();
1353
1354                         if ((command.CommandBehavior & CommandBehavior.SingleRow) != 0 && rowsRead > 0)
1355                                 return false;
1356                         if ((command.CommandBehavior & CommandBehavior.SchemaOnly) != 0)
1357                                 return false;
1358                         if (!moreResults)
1359                                 return false;
1360         
1361                         if ((haveRead) && (!readResultUsed))
1362                         {
1363                                 readResultUsed = true;
1364                                 return true;
1365                         }
1366                         return (ReadRecord ());
1367                 }
1368
1369                 internal bool ReadRecord ()
1370                 {
1371                         try {
1372                                 bool result = command.Tds.NextRow ();
1373                         
1374                                 rowsRead += 1;
1375                                 return result;
1376                         } catch (TdsInternalException ex) {
1377                                 command.Connection.Close ();
1378                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1379                         }
1380                 }
1381                 
1382                 void ValidateState ()
1383                 {
1384                         if (IsClosed)
1385                                 throw new InvalidOperationException ("Invalid attempt to read data when reader is closed");
1386                 }
1387                 
1388 #if NET_2_0
1389                 public override Type GetProviderSpecificFieldType (int i)
1390                 {
1391                         return (GetSqlValue (i).GetType());
1392                 }
1393
1394                 public override object GetProviderSpecificValue (int i)
1395                 {
1396                         return (GetSqlValue (i));
1397                 }
1398
1399                 public override int GetProviderSpecificValues (object [] values)
1400                 {
1401                         return (GetSqlValues (values));
1402                 }
1403
1404                 public virtual SqlBytes GetSqlBytes (int i)
1405                 {
1406                         //object value = GetSqlValue (i);
1407                         //if (!(value is SqlBinary))
1408                         //      throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1409                         Byte[] val = (byte[])GetValue(i);
1410                         SqlBytes sb = new SqlBytes (val);
1411                         return (sb);
1412                 }
1413 #endif // NET_2_0
1414
1415                 #endregion // Methods
1416         }
1417 }