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