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