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