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