* SqlException.cs: Do not hide Message on 2.0 profile. Fixes bug
[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 #if NET_2_0
49         public class SqlDataReader : DbDataReader, IDataReader, IDisposable, IDataRecord
50 #else
51         public sealed class SqlDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
52 #endif // NET_2_0
53         {
54                 #region Fields
55
56                 SqlCommand command;
57                 ArrayList dataTypeNames;
58                 bool disposed = false;
59                 int fieldCount;
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                 #region Constructors
76
77                 internal SqlDataReader (SqlCommand command) 
78                 {
79                         readResult = false;
80                         haveRead = false;
81                         readResultUsed = false;
82                         this.command = command;
83                         schemaTable = ConstructSchemaTable ();
84                         resultsRead = 0;
85                         fieldCount = 0;
86                         isClosed = false;
87                         command.Tds.RecordsAffected = -1;
88 #if NET_2_0
89                         visibleFieldCount = 0;
90 #endif
91                         NextResult ();
92                 }
93
94                 #endregion // Constructors
95
96                 #region Properties
97
98                 public 
99 #if NET_2_0
100                 override
101 #endif // NET_2_0
102                 int Depth {
103                         get { return 0; }
104                 }
105
106                 public 
107 #if NET_2_0
108                 override
109 #endif // NET_2_0
110                 int FieldCount {
111                         get { return fieldCount; }
112                 }
113
114                 public 
115 #if NET_2_0
116                 override
117 #endif // NET_2_0
118                 bool IsClosed {
119                         get { return isClosed; }
120                 }
121
122                 public 
123 #if NET_2_0
124                 override
125 #endif // NET_2_0
126                 object this [int i] {
127                         get { return GetValue (i); }
128                 }
129
130                 public 
131 #if NET_2_0
132                 override
133 #endif // NET_2_0
134                 object this [string name] {
135                         get { return GetValue (GetOrdinal (name)); }
136                 }
137         
138                 public 
139 #if NET_2_0
140                 override
141 #endif // NET_2_0
142                 int RecordsAffected {
143                         get { 
144                                 return command.Tds.RecordsAffected; 
145                         }
146                 }
147
148                 public 
149 #if NET_2_0
150                 override
151 #endif // NET_2_0
152                 bool HasRows {
153                         get {
154                                 if (haveRead) 
155                                         return readResult;
156                         
157                                 haveRead = true;
158                                 readResult = ReadRecord ();
159                                 return readResult;
160                         }
161                 }
162 #if NET_2_0
163                 public override int VisibleFieldCount { 
164                         get { return visibleFieldCount; }
165                 }
166
167                 protected SqlConnection Connection { 
168                         get { return command.Connection; }
169                 }
170
171                 protected bool IsCommandBehavior (CommandBehavior condition) { 
172                         return condition == command.CommandBehavior;
173                 }
174 #endif
175
176                 #endregion // Properties
177
178                 #region Methods
179
180                 public 
181 #if NET_2_0
182                 override
183 #endif // NET_2_0
184                 void Close ()
185                 {
186                         if (IsClosed)
187                                 return;
188                         // skip to end & read output parameters.
189                         while (NextResult ())
190                                 ;
191                         isClosed = true;
192                         command.CloseDataReader (moreResults);
193                 }
194
195                 private static DataTable ConstructSchemaTable ()
196                 {
197                         Type booleanType = Type.GetType ("System.Boolean");
198                         Type stringType = Type.GetType ("System.String");
199                         Type intType = Type.GetType ("System.Int32");
200                         Type typeType = Type.GetType ("System.Type");
201                         Type shortType = Type.GetType ("System.Int16");
202
203                         DataTable schemaTable = new DataTable ("SchemaTable");
204                         schemaTable.Columns.Add ("ColumnName", stringType);
205                         schemaTable.Columns.Add ("ColumnOrdinal", intType);
206                         schemaTable.Columns.Add ("ColumnSize", intType);
207                         schemaTable.Columns.Add ("NumericPrecision", shortType);
208                         schemaTable.Columns.Add ("NumericScale", shortType);
209                         schemaTable.Columns.Add ("IsUnique", booleanType);
210                         schemaTable.Columns.Add ("IsKey", booleanType);
211                         schemaTable.Columns.Add ("BaseServerName", stringType);
212                         schemaTable.Columns.Add ("BaseCatalogName", stringType);
213                         schemaTable.Columns.Add ("BaseColumnName", stringType);
214                         schemaTable.Columns.Add ("BaseSchemaName", stringType);
215                         schemaTable.Columns.Add ("BaseTableName", stringType);
216                         schemaTable.Columns.Add ("DataType", typeType);
217                         schemaTable.Columns.Add ("AllowDBNull", booleanType);
218                         schemaTable.Columns.Add ("ProviderType", intType);
219                         schemaTable.Columns.Add ("IsAliased", booleanType);
220                         schemaTable.Columns.Add ("IsExpression", booleanType);
221                         schemaTable.Columns.Add ("IsIdentity", booleanType);
222                         schemaTable.Columns.Add ("IsAutoIncrement", booleanType);
223                         schemaTable.Columns.Add ("IsRowVersion", booleanType);
224                         schemaTable.Columns.Add ("IsHidden", booleanType);
225                         schemaTable.Columns.Add ("IsLong", booleanType);
226                         schemaTable.Columns.Add ("IsReadOnly", booleanType);
227
228                         return schemaTable;
229                 }
230 #if NET_2_0
231                 new
232 #endif
233                 void Dispose (bool disposing) 
234                 {
235                         if (!disposed) {
236                                 if (disposing) {
237                                         schemaTable.Dispose ();
238                                         Close ();
239                                         command = null;
240                                 }
241                                 disposed = true;
242                         }
243                 }
244
245                 public 
246 #if NET_2_0
247                 override
248 #endif // NET_2_0
249                 bool GetBoolean (int i)
250                 {
251                         object value = GetValue (i);
252                         if (!(value is bool)) {
253                                 if (value is DBNull) throw new SqlNullValueException ();
254                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
255                         }
256                         return (bool) value;
257                 }
258
259                 public 
260 #if NET_2_0
261                 override
262 #endif // NET_2_0
263                 byte GetByte (int i)
264                 {
265                         object value = GetValue (i);
266                         if (!(value is byte)) {
267                                 if (value is DBNull) throw new SqlNullValueException ();
268                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
269                         }
270                         return (byte) value;
271                 }
272
273                 public 
274 #if NET_2_0
275                 override
276 #endif // NET_2_0
277                 long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
278                 {
279                         if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
280                                 try {
281                                         long len = ((Tds)command.Tds).GetSequentialColumnValue (i, dataIndex, buffer, bufferIndex, length);
282                                         if (len == -1)
283                                                 throw new InvalidCastException ("Invalid attempt to GetBytes on column "
284                                                                                 + "'" + command.Tds.Columns[i]["ColumnName"] +
285                                                                                 "'." + "The GetBytes function"
286                                                                                 + " can only be used on columns of type Text, NText, or Image");
287                                         return len;
288                                 } catch (TdsInternalException ex) {
289                                         command.Connection.Close ();
290                                         throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
291                                 }
292                         }
293
294                         object value = GetValue (i);
295                         if (!(value is byte [])) {
296                                 if (value is DBNull) throw new SqlNullValueException ();
297                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
298                         }
299                         
300                         if ( buffer == null )
301                                 return ((byte []) value).Length; // Return length of data
302
303                         // Copy data into buffer
304                         int availLen = (int) ( ( (byte []) value).Length - dataIndex);
305                         if (availLen < length)
306                                 length = availLen;
307                         Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
308                         return length; // return actual read count
309                 }
310
311                 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
312                 public 
313 #if NET_2_0
314                 override
315 #endif // NET_2_0
316                 char GetChar (int i)
317                 {
318                         object value = GetValue (i);
319                         if (!(value is char)) {
320                                 if (value is DBNull) throw new SqlNullValueException ();
321                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
322                         }
323                         return (char) value;
324                 }
325
326                 public 
327 #if NET_2_0
328                 override
329 #endif // NET_2_0
330                 long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
331                 {
332                         if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
333                                 Encoding encoding = null;
334                                 byte mul = 1;
335                                 TdsColumnType colType = (TdsColumnType) command.Tds.Columns[i]["ColumnType"];
336                                 switch (colType) {
337                                         case TdsColumnType.Text :
338                                         case TdsColumnType.VarChar:
339                                         case TdsColumnType.Char:
340                                         case TdsColumnType.BigVarChar:
341                                                 encoding = Encoding.ASCII;
342                                                 break;
343                                         case TdsColumnType.NText :
344                                         case TdsColumnType.NVarChar:
345                                         case TdsColumnType.NChar:
346                                                 encoding = Encoding.Unicode;
347                                                 mul = 2 ;
348                                                 break;
349                                         default :
350                                                 return -1;
351                                 }
352
353                                 long count = 0;
354                                 if (buffer == null) {
355                                         count = GetBytes (i,0,(byte[]) null,0,0);
356                                         return (count/mul);
357                                 }
358
359                                 length *= mul;
360                                 byte[] arr = new byte [length];
361                                 count = GetBytes (i, dataIndex, arr, 0, length);
362                                 if (count == -1)
363                                         throw new InvalidCastException ("Specified cast is not valid");
364
365                                 Char[] val = encoding.GetChars (arr, 0, (int)count);
366                                 val.CopyTo (buffer, bufferIndex);
367                                 return val.Length;
368                         }
369
370                         char [] valueBuffer;
371                         object value = GetValue (i);
372                         
373                         if (value is char[])
374                                 valueBuffer = (char[])value;
375                         else if (value is string)
376                                 valueBuffer = ((string)value).ToCharArray();
377                         else {
378                                 if (value is DBNull) throw new SqlNullValueException ();
379                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
380                         }
381                         
382                         if ( buffer == null ) {
383                                 // Return length of data
384                                 return valueBuffer.Length;
385                         }
386                         else {
387                                 // Copy data into buffer
388                                 Array.Copy (valueBuffer, (int) dataIndex, buffer, bufferIndex, length);
389                                 return valueBuffer.Length - dataIndex;
390                         }
391                 }
392                 
393 #if !NET_2_0
394                 [EditorBrowsableAttribute (EditorBrowsableState.Never)] 
395                 public new IDataReader GetData (int i)
396                 {
397                         return ((IDataReader) this [i]);
398                 }
399 #endif
400
401                 public 
402 #if NET_2_0
403                 override
404 #endif // NET_2_0
405                 string GetDataTypeName (int i)
406                 {
407                         if (i < 0 || i >= dataTypeNames.Count)
408                                 throw new IndexOutOfRangeException ();
409                         return (string) dataTypeNames [i];
410                 }
411
412                 public 
413 #if NET_2_0
414                 override
415 #endif // NET_2_0
416                 DateTime GetDateTime (int i)
417                 {
418                         object value = GetValue (i);
419                         if (!(value is DateTime)) {
420                                 if (value is DBNull) throw new SqlNullValueException ();
421                                 else throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
422                         }
423                         return (DateTime) value;
424                 }
425
426                 public 
427 #if NET_2_0
428                 override
429 #endif // NET_2_0
430                 decimal GetDecimal (int i)
431                 {
432                         object value = GetValue (i);
433                         if (!(value is decimal)) {
434                                 if (value is DBNull) throw new SqlNullValueException ();
435                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
436                         }
437                         return (decimal) value;
438                 }
439
440                 public 
441 #if NET_2_0
442                 override
443 #endif // NET_2_0
444                 double GetDouble (int i)
445                 {
446                         object value = GetValue (i);
447                         if (!(value is double)) {
448                                 if (value is DBNull) throw new SqlNullValueException ();
449                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
450                         }
451                         return (double) value;
452                 }
453
454                 public 
455 #if NET_2_0
456                 override
457 #endif // NET_2_0
458                 Type GetFieldType (int i)
459                 {
460                         if (i < 0 || i >= schemaTable.Rows.Count)
461                                 throw new IndexOutOfRangeException ();
462                         return (Type) schemaTable.Rows[i]["DataType"];
463                 }
464
465                 public 
466 #if NET_2_0
467                 override
468 #endif // NET_2_0
469                 float GetFloat (int i)
470                 {
471                         object value = GetValue (i);
472                         if (!(value is float)) {
473                                 if (value is DBNull) throw new SqlNullValueException ();
474                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
475                         }
476                         return (float) value;
477                 }
478
479                 public 
480 #if NET_2_0
481                 override
482 #endif // NET_2_0
483                 Guid GetGuid (int i)
484                 {
485                         object value = GetValue (i);
486                         if (!(value is Guid)) {
487                                 if (value is DBNull) throw new SqlNullValueException ();
488                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
489                         }
490                         return (Guid) value;
491                 }
492
493                 public 
494 #if NET_2_0
495                 override
496 #endif // NET_2_0
497                 short GetInt16 (int i)
498                 {
499                         object value = GetValue (i);
500                         if (!(value is short)) {
501                                 if (value is DBNull) throw new SqlNullValueException ();
502                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
503                         }
504                         return (short) value;
505                 }
506
507                 public 
508 #if NET_2_0
509                 override
510 #endif // NET_2_0
511                 int GetInt32 (int i)
512                 {
513                         object value = GetValue (i);
514                         if (!(value is int)) {
515                                 if (value is DBNull) throw new SqlNullValueException ();
516                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
517                         }
518                         return (int) value;
519                 }
520
521                 public 
522 #if NET_2_0
523                 override
524 #endif // NET_2_0
525                 long GetInt64 (int i)
526                 {
527                         object value = GetValue (i);
528                         // TDS 7.0 returns bigint as decimal(19,0)
529                         if (value is decimal) {
530                                 TdsDataColumn schema = command.Tds.Columns[i];
531                                 if ((byte)schema["NumericPrecision"] == 19 && (byte)schema["NumericScale"] == 0)
532                                         value = (long) (decimal) value;
533                         }
534                         if (!(value is long)) {
535                                 if (value is DBNull) throw new SqlNullValueException ();
536                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
537                         }
538                         return (long) value;
539                 }
540
541                 public 
542 #if NET_2_0
543                 override
544 #endif // NET_2_0
545                 string GetName (int i)
546                 {
547                         return (string) schemaTable.Rows[i]["ColumnName"];
548                 }
549
550                 public 
551 #if NET_2_0
552                 override
553 #endif // NET_2_0
554                 int GetOrdinal (string name)
555                 {
556                         foreach (DataRow schemaRow in schemaTable.Rows)
557                                 if (((string) schemaRow ["ColumnName"]).Equals (name))
558                                         return (int) schemaRow ["ColumnOrdinal"];
559                         foreach (DataRow schemaRow in schemaTable.Rows)
560                                 if (String.Compare (((string) schemaRow ["ColumnName"]), name, true) == 0)
561                                         return (int) schemaRow ["ColumnOrdinal"];
562                         throw new IndexOutOfRangeException ();
563                 }
564
565                 public 
566 #if NET_2_0
567                 override
568 #endif // NET_2_0
569                 DataTable GetSchemaTable ()
570                 {
571                         ValidateState ();
572
573                         if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
574                                 return schemaTable;
575
576                         if (!moreResults)
577                                 return null;
578
579                         fieldCount = 0;
580
581                         dataTypeNames = new ArrayList ();
582
583                         foreach (TdsDataColumn schema in command.Tds.Columns) {
584                                 DataRow row = schemaTable.NewRow ();
585
586                                 row ["ColumnName"]              = GetSchemaValue (schema, "ColumnName");
587                                 row ["ColumnSize"]              = GetSchemaValue (schema, "ColumnSize"); 
588                                 row ["ColumnOrdinal"]           = GetSchemaValue (schema, "ColumnOrdinal");
589                                 row ["NumericPrecision"]        = GetSchemaValue (schema, "NumericPrecision");
590                                 row ["NumericScale"]            = GetSchemaValue (schema, "NumericScale");
591                                 row ["IsUnique"]                = GetSchemaValue (schema, "IsUnique");
592                                 row ["IsKey"]                   = GetSchemaValue (schema, "IsKey");
593                                 row ["BaseServerName"]          = GetSchemaValue (schema, "BaseServerName");
594                                 row ["BaseCatalogName"]         = GetSchemaValue (schema, "BaseCatalogName");
595                                 row ["BaseColumnName"]          = GetSchemaValue (schema, "BaseColumnName");
596                                 row ["BaseSchemaName"]          = GetSchemaValue (schema, "BaseSchemaName");
597                                 row ["BaseTableName"]           = GetSchemaValue (schema, "BaseTableName");
598                                 row ["AllowDBNull"]             = GetSchemaValue (schema, "AllowDBNull");
599                                 row ["IsAliased"]               = GetSchemaValue (schema, "IsAliased");
600                                 row ["IsExpression"]            = GetSchemaValue (schema, "IsExpression");
601                                 row ["IsIdentity"]              = GetSchemaValue (schema, "IsIdentity");
602                                 row ["IsAutoIncrement"]         = GetSchemaValue (schema, "IsAutoIncrement");
603                                 row ["IsRowVersion"]            = GetSchemaValue (schema, "IsRowVersion");
604                                 row ["IsHidden"]                = GetSchemaValue (schema, "IsHidden");
605                                 row ["IsReadOnly"]              = GetSchemaValue (schema, "IsReadOnly");
606
607                                 // We don't always get the base column name.
608                                 if (row ["BaseColumnName"] == DBNull.Value)
609                                         row ["BaseColumnName"] = row ["ColumnName"];
610
611                                 switch ((TdsColumnType) schema ["ColumnType"]) {
612                                         case TdsColumnType.Int1:
613                                         case TdsColumnType.Int2:
614                                         case TdsColumnType.Int4:
615                                         case TdsColumnType.IntN:
616                                                 switch ((int) schema ["ColumnSize"]) {
617                                                 case 1:
618                                                         dataTypeNames.Add ("tinyint");
619                                                         row ["ProviderType"] = (int) SqlDbType.TinyInt;
620                                                         row ["DataType"] = typeof (byte);
621                                                         row ["IsLong"] = false;
622                                                         break;
623                                                 case 2:
624                                                         dataTypeNames.Add ("smallint");
625                                                         row ["ProviderType"] = (int) SqlDbType.SmallInt;
626                                                         row ["DataType"] = typeof (short);
627                                                         row ["IsLong"] = false;
628                                                         break;
629                                                 case 4:
630                                                         dataTypeNames.Add ("int");
631                                                         row ["ProviderType"] = (int) SqlDbType.Int;
632                                                         row ["DataType"] = typeof (int);
633                                                         row ["IsLong"] = false;
634                                                         break;
635                                                 case 8:
636                                                         dataTypeNames.Add ("bigint");
637                                                         row ["ProviderType"] = (int) SqlDbType.BigInt;
638                                                         row ["DataType"] = typeof (long);
639                                                         row ["IsLong"] = false;
640                                                         break;
641                                                 }
642                                                 break;
643                                         case TdsColumnType.Real:
644                                         case TdsColumnType.Float8:
645                                         case TdsColumnType.FloatN:
646                                                 switch ((int) schema ["ColumnSize"]) {
647                                                 case 4:
648                                                         dataTypeNames.Add ("real");
649                                                         row ["ProviderType"] = (int) SqlDbType.Real;
650                                                         row ["DataType"] = typeof (float);
651                                                         row ["IsLong"] = false;
652                                                         break;
653                                                 case 8:
654                                                         dataTypeNames.Add ("float");
655                                                         row ["ProviderType"] = (int) SqlDbType.Float;
656                                                         row ["DataType"] = typeof (double);
657                                                         row ["IsLong"] = false;
658                                                         break;
659                                                 }
660                                                 break;
661                                         case TdsColumnType.Image :
662                                                 dataTypeNames.Add ("image");
663                                                 row ["ProviderType"] = (int) SqlDbType.Image;
664                                                 row ["DataType"] = typeof (byte[]);
665                                                 row ["IsLong"] = true;
666                                                 break;
667                                         case TdsColumnType.Text :
668                                                 dataTypeNames.Add ("text");
669                                                 row ["ProviderType"] = (int) SqlDbType.Text;
670                                                 row ["DataType"] = typeof (string);
671                                                 row ["IsLong"] = true;
672                                                 break;
673                                         case TdsColumnType.UniqueIdentifier :
674                                                 dataTypeNames.Add ("uniqueidentifier");
675                                                 row ["ProviderType"] = (int) SqlDbType.UniqueIdentifier;
676                                                 row ["DataType"] = typeof (Guid);
677                                                 row ["IsLong"] = false;
678                                                 break;
679                                         case TdsColumnType.VarBinary :
680                                         case TdsColumnType.BigVarBinary :
681                                                 dataTypeNames.Add ("varbinary");
682                                                 row ["ProviderType"] = (int) SqlDbType.VarBinary;
683                                                 row ["DataType"] = typeof (byte[]);
684                                                 row ["IsLong"] = true;
685                                                 break;
686                                         case TdsColumnType.VarChar :
687                                         case TdsColumnType.BigVarChar :
688                                                 dataTypeNames.Add ("varchar");
689                                                 row ["ProviderType"] = (int) SqlDbType.VarChar;
690                                                 row ["DataType"] = typeof (string);
691                                                 row ["IsLong"] = false;
692                                                 break;
693                                         case TdsColumnType.Binary :
694                                         case TdsColumnType.BigBinary :
695                                                 dataTypeNames.Add ("binary");
696                                                 row ["ProviderType"] = (int) SqlDbType.Binary;
697                                                 row ["DataType"] = typeof (byte[]);
698                                                 row ["IsLong"] = true;
699                                                 break;
700                                         case TdsColumnType.Char :
701                                         case TdsColumnType.BigChar :
702                                                 dataTypeNames.Add ("char");
703                                                 row ["ProviderType"] = (int) SqlDbType.Char;
704                                                 row ["DataType"] = typeof (string);
705                                                 row ["IsLong"] = false;
706                                                 break;
707                                         case TdsColumnType.Bit :
708                                         case TdsColumnType.BitN :
709                                                 dataTypeNames.Add ("bit");
710                                                 row ["ProviderType"] = (int) SqlDbType.Bit;
711                                                 row ["DataType"] = typeof (bool);
712                                                 row ["IsLong"] = false;
713                                                 break;
714                                         case TdsColumnType.DateTime4 :
715                                         case TdsColumnType.DateTime :
716                                         case TdsColumnType.DateTimeN :
717                                                 dataTypeNames.Add ("datetime");
718                                                 row ["ProviderType"] = (int) SqlDbType.DateTime;
719                                                 row ["DataType"] = typeof (DateTime);
720                                                 row ["IsLong"] = false;
721                                                 break;
722                                         case TdsColumnType.Money :
723                                         case TdsColumnType.MoneyN :
724                                         case TdsColumnType.Money4 :
725                                                 dataTypeNames.Add ("money");
726                                                 row ["ProviderType"] = (int) SqlDbType.Money;
727                                                 row ["DataType"] = typeof (decimal);
728                                                 row ["IsLong"] = false;
729                                                 break;
730                                         case TdsColumnType.NText :
731                                                 dataTypeNames.Add ("ntext");
732                                                 row ["ProviderType"] = (int) SqlDbType.NText;
733                                                 row ["DataType"] = typeof (string);
734                                                 row ["IsLong"] = true;
735                                                 break;
736                                         case TdsColumnType.NVarChar :
737                                                 dataTypeNames.Add ("nvarchar");
738                                                 row ["ProviderType"] = (int) SqlDbType.NVarChar;
739                                                 row ["DataType"] = typeof (string);
740                                                 row ["IsLong"] = false;
741                                                 break;
742                                         case TdsColumnType.Decimal :
743                                         case TdsColumnType.Numeric :
744                                                 dataTypeNames.Add ("decimal");
745                                                 row ["ProviderType"] = (int) SqlDbType.Decimal;
746                                                 row ["DataType"] = typeof (decimal);
747                                                 row ["IsLong"] = false;
748                                                 break;
749                                         case TdsColumnType.NChar :
750                                                 dataTypeNames.Add ("nchar");
751                                                 row ["ProviderType"] = (int) SqlDbType.NChar;
752                                                 row ["DataType"] = typeof (string);
753                                                 row ["IsLong"] = false;
754                                                 break;
755                                         case TdsColumnType.SmallMoney :
756                                                 dataTypeNames.Add ("smallmoney");
757                                                 row ["ProviderType"] = (int) SqlDbType.SmallMoney;
758                                                 row ["DataType"] = typeof (decimal);
759                                                 row ["IsLong"] = false;
760                                                 break;
761                                         default :
762                                                 dataTypeNames.Add ("variant");
763                                                 row ["ProviderType"] = (int) SqlDbType.Variant;
764                                                 row ["DataType"] = typeof (object);
765                                                 row ["IsLong"] = false;
766                                                 break;
767                                 }
768 #if NET_2_0
769                                 if ((bool)row ["IsHidden"] == false)
770                                         visibleFieldCount += 1;
771 #endif
772
773                                 schemaTable.Rows.Add (row);
774
775                                 fieldCount += 1;
776                         }
777                         return schemaTable;
778                 }
779
780                 private static object GetSchemaValue (TdsDataColumn schema, object key)
781                 {
782                         if (schema.ContainsKey (key) && schema [key] != null)
783                                 return schema [key];
784                         return DBNull.Value;
785                 }
786
787                 public
788 #if NET_2_0
789                 virtual
790 #endif
791                 SqlBinary GetSqlBinary (int i)
792                 {
793                         object value = GetSqlValue (i);
794                         if (!(value is SqlBinary))
795                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
796                         return (SqlBinary) value;
797                 }
798
799                 public
800 #if NET_2_0
801                 virtual
802 #endif
803                 SqlBoolean GetSqlBoolean (int i) 
804                 {
805                         object value = GetSqlValue (i);
806                         if (!(value is SqlBoolean))
807                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
808                         return (SqlBoolean) value;
809                 }
810
811                 public
812 #if NET_2_0
813                 virtual
814 #endif
815                 SqlByte GetSqlByte (int i)
816                 {
817                         object value = GetSqlValue (i);
818                         if (!(value is SqlByte))
819                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
820                         return (SqlByte) value;
821                 }
822
823                 public
824 #if NET_2_0
825                 virtual
826 #endif
827                 SqlDateTime GetSqlDateTime (int i)
828                 {
829                         object value = GetSqlValue (i);
830                         if (!(value is SqlDateTime))
831                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
832                         return (SqlDateTime) value;
833                 }
834
835                 public
836 #if NET_2_0
837                 virtual
838 #endif
839                 SqlDecimal GetSqlDecimal (int i)
840                 {
841                         object value = GetSqlValue (i);
842                         if (!(value is SqlDecimal))
843                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
844                         return (SqlDecimal) value;
845                 }
846
847                 public
848 #if NET_2_0
849                 virtual
850 #endif
851                 SqlDouble GetSqlDouble (int i)
852                 {
853                         object value = GetSqlValue (i);
854                         if (!(value is SqlDouble))
855                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
856                         return (SqlDouble) value;
857                 }
858
859                 public
860 #if NET_2_0
861                 virtual
862 #endif
863                 SqlGuid GetSqlGuid (int i)
864                 {
865                         object value = GetSqlValue (i);
866                         if (!(value is SqlGuid))
867                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
868                         return (SqlGuid) value;
869                 }
870
871                 public
872 #if NET_2_0
873                 virtual
874 #endif
875                 SqlInt16 GetSqlInt16 (int i)
876                 {
877                         object value = GetSqlValue (i);
878                         if (!(value is SqlInt16))
879                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
880                         return (SqlInt16) value;
881                 }
882
883                 public
884 #if NET_2_0
885                 virtual
886 #endif
887                 SqlInt32 GetSqlInt32 (int i)
888                 {
889                         object value = GetSqlValue (i);
890                         if (!(value is SqlInt32))
891                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
892                         return (SqlInt32) value;
893                 }
894
895                 public
896 #if NET_2_0
897                 virtual
898 #endif
899                 SqlInt64 GetSqlInt64 (int i)
900                 {
901                         object value = GetSqlValue (i);
902                         // TDS 7.0 returns bigint as decimal(19,0)
903                         if (value is SqlDecimal) {
904                                 TdsDataColumn schema = command.Tds.Columns[i];
905                                 if ((byte)schema["NumericPrecision"] == 19 && (byte)schema["NumericScale"] == 0)
906                                         value = (SqlInt64) (SqlDecimal) value;
907                         }
908                         if (!(value is SqlInt64))
909                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
910                         return (SqlInt64) value;
911                 }
912
913                 public
914 #if NET_2_0
915                 virtual
916 #endif
917                 SqlMoney GetSqlMoney (int i)
918                 {
919                         object value = GetSqlValue (i);
920                         if (!(value is SqlMoney))
921                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
922                         return (SqlMoney) value;
923                 }
924
925                 public
926 #if NET_2_0
927                 virtual
928 #endif
929                 SqlSingle GetSqlSingle (int i)
930                 {
931                         object value = GetSqlValue (i);
932                         if (!(value is SqlSingle))
933                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
934                         return (SqlSingle) value;
935                 }
936
937                 public
938 #if NET_2_0
939                 virtual
940 #endif
941                 SqlString GetSqlString (int i)
942                 {
943                         object value = GetSqlValue (i);
944                         if (!(value is SqlString))
945                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
946                         return (SqlString) value;
947                 }
948
949 #if NET_2_0
950                 public virtual SqlXml GetSqlXml (int i)
951                 {
952                         object value = GetSqlValue (i);
953                         if (!(value is SqlXml)) {
954                                 if (value is DBNull) throw new SqlNullValueException ();
955                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
956                         }
957                         return (SqlXml) value;
958                 }
959 #endif // NET_2_0
960
961                 public
962 #if NET_2_0
963                 virtual
964 #endif
965                 object GetSqlValue (int i)
966                 {
967                         SqlDbType type = (SqlDbType) (schemaTable.Rows [i]["ProviderType"]);
968                         object value = GetValue (i);
969
970                         switch (type) {
971                         case SqlDbType.BigInt:
972                                 if (value == DBNull.Value)
973                                         return SqlInt64.Null;
974                                 return (SqlInt64) ((long) value);
975                         case SqlDbType.Binary:
976                         case SqlDbType.Image:
977                         case SqlDbType.VarBinary:
978                         case SqlDbType.Timestamp:
979                                 if (value == DBNull.Value)
980                                         return SqlBinary.Null;
981                                 return (SqlBinary) ((byte[]) value);
982                         case SqlDbType.Bit:
983                                 if (value == DBNull.Value)
984                                         return SqlBoolean.Null;
985                                 return (SqlBoolean) ((bool) value);
986                         case SqlDbType.Char:
987                         case SqlDbType.NChar:
988                         case SqlDbType.NText:
989                         case SqlDbType.NVarChar:
990                         case SqlDbType.Text:
991                         case SqlDbType.VarChar:
992                                 if (value == DBNull.Value)
993                                         return SqlString.Null;
994                                 return (SqlString) ((string) value);
995                         case SqlDbType.DateTime:
996                         case SqlDbType.SmallDateTime:
997                                 if (value == DBNull.Value)
998                                         return SqlDateTime.Null;
999                                 return (SqlDateTime) ((DateTime) value);
1000                         case SqlDbType.Decimal:
1001                                 if (value == DBNull.Value)
1002                                         return SqlDecimal.Null;
1003                                 if (value is TdsBigDecimal)
1004                                         return SqlDecimal.FromTdsBigDecimal ((TdsBigDecimal) value);
1005                                 return (SqlDecimal) ((decimal) value);
1006                         case SqlDbType.Float:
1007                                 if (value == DBNull.Value)
1008                                         return SqlDouble.Null;
1009                                 return (SqlDouble) ((double) value);
1010                         case SqlDbType.Int:
1011                                 if (value == DBNull.Value)
1012                                         return SqlInt32.Null;
1013                                 return (SqlInt32) ((int) value);
1014                         case SqlDbType.Money:
1015                         case SqlDbType.SmallMoney:
1016                                 if (value == DBNull.Value)
1017                                         return SqlMoney.Null;
1018                                 return (SqlMoney) ((decimal) value);
1019                         case SqlDbType.Real:
1020                                 if (value == DBNull.Value)
1021                                         return SqlSingle.Null;
1022                                 return (SqlSingle) ((float) value);
1023                         case SqlDbType.UniqueIdentifier:
1024                                 if (value == DBNull.Value)
1025                                         return SqlGuid.Null;
1026                                 return (SqlGuid) ((Guid) value);
1027                         case SqlDbType.SmallInt:
1028                                 if (value == DBNull.Value)
1029                                         return SqlInt16.Null;
1030                                 return (SqlInt16) ((short) value);
1031                         case SqlDbType.TinyInt:
1032                                 if (value == DBNull.Value)
1033                                         return SqlByte.Null;
1034                                 return (SqlByte) ((byte) value);
1035 #if NET_2_0
1036                         case SqlDbType.Xml:
1037                                 if (value == DBNull.Value)
1038                                         return SqlByte.Null;
1039                                 return (SqlXml) value;
1040 #endif
1041                         }
1042
1043                         throw new InvalidOperationException ("The type of this column is unknown.");
1044                 }
1045
1046                 public
1047 #if NET_2_0
1048                 virtual
1049 #endif
1050                 int GetSqlValues (object[] values)
1051                 {
1052                         int count = 0;
1053                         int columnCount = schemaTable.Rows.Count;
1054                         int arrayCount = values.Length;
1055
1056                         if (arrayCount > columnCount)
1057                                 count = columnCount;
1058                         else
1059                                 count = arrayCount;
1060
1061                         for (int i = 0; i < count; i += 1) 
1062                                 values [i] = GetSqlValue (i);
1063
1064                         return count;
1065                 }
1066
1067                 public 
1068 #if NET_2_0
1069                 override
1070 #endif // NET_2_0
1071                 string GetString (int i)
1072                 {
1073                         object value = GetValue (i);
1074                         if (!(value is string)) {
1075                                 if (value is DBNull) throw new SqlNullValueException ();
1076                                 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1077                         }
1078                         return (string) value;
1079                 }
1080
1081                 public 
1082 #if NET_2_0
1083                 override
1084 #endif // NET_2_0
1085                 object GetValue (int i)
1086                 {
1087                         if (i < 0 || i >= command.Tds.Columns.Count)
1088                                 throw new IndexOutOfRangeException ();
1089
1090                         try {
1091                                 if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
1092                                         return ((Tds)command.Tds).GetSequentialColumnValue (i);
1093                                 }
1094                         } catch (TdsInternalException ex) {
1095                                 command.Connection.Close ();
1096                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1097                         }
1098
1099                         return command.Tds.ColumnValues [i];
1100                 }
1101
1102                 public 
1103 #if NET_2_0
1104                 override
1105 #endif // NET_2_0
1106                 int GetValues (object[] values)
1107                 {
1108                         int len = values.Length;
1109                         int bigDecimalIndex = command.Tds.ColumnValues.BigDecimalIndex;
1110
1111                         // If a four-byte decimal is stored, then we can't convert to
1112                         // a native type.  Throw an OverflowException.
1113                         if (bigDecimalIndex >= 0 && bigDecimalIndex < len)
1114                                 throw new OverflowException ();
1115                         try {
1116                                 command.Tds.ColumnValues.CopyTo (0, values, 0,
1117                                                                  len > command.Tds.ColumnValues.Count ? command.Tds.ColumnValues.Count : len);
1118                         } catch (TdsInternalException ex) {
1119                                 command.Connection.Close ();
1120                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1121                         }
1122                         return (len < FieldCount ? len : FieldCount);
1123                 }
1124
1125 #if !NET_2_0
1126                 void IDisposable.Dispose ()
1127                 {
1128                         Dispose (true);
1129                         GC.SuppressFinalize (this);
1130                 }
1131 #endif
1132
1133 #if NET_2_0
1134                 public override IEnumerator GetEnumerator ()
1135 #else
1136                 IEnumerator IEnumerable.GetEnumerator ()
1137 #endif
1138                 {
1139                         return new DbEnumerator (this);
1140                 }
1141
1142                 public 
1143 #if NET_2_0
1144                 override
1145 #endif // NET_2_0
1146                 bool IsDBNull (int i)
1147                 {
1148                         return GetValue (i) == DBNull.Value;
1149                 }
1150
1151                 public 
1152 #if NET_2_0
1153                 override
1154 #endif // NET_2_0
1155                 bool NextResult ()
1156                 {
1157                         ValidateState ();
1158
1159                         if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0)
1160                                 return false;
1161
1162                         try {
1163                                 moreResults = command.Tds.NextResult ();
1164                         } catch (TdsInternalException ex) {
1165                                 command.Connection.Close ();
1166                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1167                         }
1168                         if (!moreResults)
1169                                 command.GetOutputParameters ();
1170                         else {
1171                                 //new schema
1172                                 schemaTable = ConstructSchemaTable ();
1173                                 GetSchemaTable ();
1174                         }
1175
1176                         rowsRead = 0;
1177                         resultsRead += 1;
1178                         return moreResults;
1179                 }
1180
1181                 public 
1182 #if NET_2_0
1183                 override
1184 #endif // NET_2_0
1185                 bool Read ()
1186                 {
1187                         ValidateState ();
1188
1189                         if ((command.CommandBehavior & CommandBehavior.SingleRow) != 0 && rowsRead > 0)
1190                                 return false;
1191                         if ((command.CommandBehavior & CommandBehavior.SchemaOnly) != 0)
1192                                 return false;
1193                         if (!moreResults)
1194                                 return false;
1195         
1196                         if ((haveRead) && (!readResultUsed))
1197                         {
1198                                 readResultUsed = true;
1199                                 return true;
1200                         }
1201                         return (ReadRecord ());
1202                 }
1203
1204                 internal bool ReadRecord ()
1205                 {
1206                         try {
1207                                 bool result = command.Tds.NextRow ();
1208                         
1209                                 rowsRead += 1;
1210                                 return result;
1211                         } catch (TdsInternalException ex) {
1212                                 command.Connection.Close ();
1213                                 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1214                         }
1215                 }
1216                 
1217                 void ValidateState ()
1218                 {
1219                         if (IsClosed)
1220                                 throw new InvalidOperationException ("Invalid attempt to read data when reader is closed");
1221                 }
1222                 
1223 #if NET_2_0
1224
1225                 public override Type GetProviderSpecificFieldType (int position)
1226                 {
1227                         return (GetSqlValue (position).GetType());
1228                 }
1229
1230                 public override object GetProviderSpecificValue (int position)
1231                 {
1232                         return (GetSqlValue (position));
1233                 }
1234
1235                 public override int GetProviderSpecificValues (object [] values)
1236                 {
1237                         return (GetSqlValues (values));
1238                 }
1239
1240                 public virtual SqlBytes GetSqlBytes (int i)
1241                 {
1242                         //object value = GetSqlValue (i);
1243                         //if (!(value is SqlBinary))
1244                         //      throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1245                         Byte[] val = (byte[])GetValue(i);
1246                         SqlBytes sb = new SqlBytes (val);
1247                         return (sb);
1248                 }
1249 #endif // NET_2_0
1250
1251                 #endregion // Methods
1252         }
1253 }