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