Bump corlib version for MonoIO coop handle changes.
[mono-project.git] / mcs / class / System.Data / System.Data.SqlClient / SqlDataReader.cs
blob9cb4b8a6eeae73078ce759004903398213075241
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
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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.
37 using Mono.Data.Tds.Protocol;
38 using System;
39 using System.IO;
40 using System.Text;
41 using System.Threading;
42 using System.Threading.Tasks;
43 using System.Collections;
44 using System.ComponentModel;
45 using System.Data;
46 using System.Data.Common;
47 using System.Data.SqlTypes;
48 using System.Globalization;
49 using System.Xml;
51 namespace System.Data.SqlClient
53 public class SqlDataReader : DbDataReader, IDataReader, IDisposable, IDataRecord
55 #region Fields
57 SqlCommand command;
58 bool disposed;
59 bool isClosed;
60 bool moreResults;
61 int resultsRead;
62 int rowsRead;
63 DataTable schemaTable;
64 bool haveRead;
65 bool readResult;
66 bool readResultUsed;
67 int visibleFieldCount;
69 #endregion // Fields
71 const int COLUMN_NAME_IDX = 0;
72 const int COLUMN_ORDINAL_IDX = 1;
73 const int COLUMN_SIZE_IDX = 2;
74 const int NUMERIC_PRECISION_IDX = 3;
75 const int NUMERIC_SCALE_IDX = 4;
76 const int IS_UNIQUE_IDX = 5;
77 const int IS_KEY_IDX = 6;
78 const int BASE_SERVER_NAME_IDX = 7;
79 const int BASE_CATALOG_NAME_IDX = 8;
80 const int BASE_COLUMN_NAME_IDX = 9;
81 const int BASE_SCHEMA_NAME_IDX = 10;
82 const int BASE_TABLE_NAME_IDX = 11;
83 const int DATA_TYPE_IDX = 12;
84 const int ALLOW_DBNULL_IDX = 13;
85 const int PROVIDER_TYPE_IDX = 14;
86 const int IS_ALIASED_IDX = 15;
87 const int IS_EXPRESSION_IDX = 16;
88 const int IS_IDENTITY_IDX = 17;
89 const int IS_AUTO_INCREMENT_IDX = 18;
90 const int IS_ROW_VERSION_IDX = 19;
91 const int IS_HIDDEN_IDX = 20;
92 const int IS_LONG_IDX = 21;
93 const int IS_READ_ONLY_IDX = 22;
94 const int PROVIDER_SPECIFIC_TYPE_IDX = 23;
95 const int DATA_TYPE_NAME_IDX = 24;
96 const int XML_SCHEMA_COLLCTN_DB_IDX = 25;
97 const int XML_SCHEMA_COLLCTN_OWN_SCHEMA_IDX = 26;
98 const int XML_SCHEMA_COLLCTN_NAME_IDX = 27;
99 const int UDT_ASMBLY_QUALIFIED_NAME_IDX = 28;
100 const int NON_VER_PROVIDER_TYPE_IDX = 29;
101 const int IS_COLUMN_SET = 30;
103 #region Constructors
105 internal SqlDataReader (SqlCommand command)
107 this.command = command;
108 command.Tds.RecordsAffected = -1;
109 NextResult ();
112 #endregion // Constructors
114 #region Properties
116 public
117 override
118 int Depth {
119 get { return 0; }
122 public
123 override
124 int FieldCount {
125 get {
126 ValidateState ();
127 return command.Tds.Columns.Count;
131 public
132 override
133 bool IsClosed {
134 get { return isClosed; }
137 public
138 override
139 object this [int i] {
140 get { return GetValue (i); }
143 public
144 override
145 object this [string name] {
146 get { return GetValue (GetOrdinal (name)); }
149 public
150 override
151 int RecordsAffected {
152 get {
153 return command.Tds.RecordsAffected;
157 public
158 override
159 bool HasRows {
160 get {
161 ValidateState ();
163 if (rowsRead > 0)
164 return true;
165 if (!haveRead)
166 readResult = ReadRecord ();
167 return readResult;
170 public override int VisibleFieldCount {
171 get { return visibleFieldCount; }
174 protected SqlConnection Connection {
175 get { return command.Connection; }
178 protected bool IsCommandBehavior (CommandBehavior condition) {
179 return condition == command.CommandBehavior;
182 #endregion // Properties
184 #region Methods
186 public
187 override
188 void Close ()
190 if (IsClosed)
191 return;
192 // skip to end & read output parameters.
193 while (NextResult ())
195 isClosed = true;
196 command.CloseDataReader ();
199 private static DataTable ConstructSchemaTable ()
201 Type booleanType = typeof (bool);
202 Type stringType = typeof (string);
203 Type intType = typeof (int);
204 Type typeType = typeof (Type);
205 Type shortType = typeof (short);
207 DataTable schemaTable = new DataTable ("SchemaTable");
208 schemaTable.Columns.Add ("ColumnName", stringType);
209 schemaTable.Columns.Add ("ColumnOrdinal", intType);
210 schemaTable.Columns.Add ("ColumnSize", intType);
211 schemaTable.Columns.Add ("NumericPrecision", shortType);
212 schemaTable.Columns.Add ("NumericScale", shortType);
213 schemaTable.Columns.Add ("IsUnique", booleanType);
214 schemaTable.Columns.Add ("IsKey", booleanType);
215 schemaTable.Columns.Add ("BaseServerName", stringType);
216 schemaTable.Columns.Add ("BaseCatalogName", stringType);
217 schemaTable.Columns.Add ("BaseColumnName", stringType);
218 schemaTable.Columns.Add ("BaseSchemaName", stringType);
219 schemaTable.Columns.Add ("BaseTableName", stringType);
220 schemaTable.Columns.Add ("DataType", typeType);
221 schemaTable.Columns.Add ("AllowDBNull", booleanType);
222 schemaTable.Columns.Add ("ProviderType", intType);
223 schemaTable.Columns.Add ("IsAliased", booleanType);
224 schemaTable.Columns.Add ("IsExpression", booleanType);
225 schemaTable.Columns.Add ("IsIdentity", booleanType);
226 schemaTable.Columns.Add ("IsAutoIncrement", booleanType);
227 schemaTable.Columns.Add ("IsRowVersion", booleanType);
228 schemaTable.Columns.Add ("IsHidden", booleanType);
229 schemaTable.Columns.Add ("IsLong", booleanType);
230 schemaTable.Columns.Add ("IsReadOnly", booleanType);
231 schemaTable.Columns.Add ("ProviderSpecificDataType", typeType);
232 schemaTable.Columns.Add ("DataTypeName", stringType);
233 schemaTable.Columns.Add ("XmlSchemaCollectionDatabase", stringType);
234 schemaTable.Columns.Add ("XmlSchemaCollectionOwningSchema", stringType);
235 schemaTable.Columns.Add ("XmlSchemaCollectionName", stringType);
236 schemaTable.Columns.Add ("UdtAssemblyQualifiedName", stringType);
237 schemaTable.Columns.Add ("NonVersionedProviderType", intType);
238 schemaTable.Columns.Add ("IsColumnSet", booleanType);
240 return schemaTable;
243 private string GetSchemaRowTypeName (TdsColumnType ctype, int csize, short precision, short scale)
245 int dbType;
246 bool isLong;
247 Type fieldType;
249 string typeName;
250 GetSchemaRowType (ctype, csize, precision, scale,
251 out dbType, out fieldType, out isLong,
252 out typeName);
253 return typeName;
256 private Type GetSchemaRowFieldType (TdsColumnType ctype, int csize, short precision, short scale)
258 int dbType;
259 bool isLong;
260 Type fieldType;
261 string typeName;
263 GetSchemaRowType (ctype, csize, precision, scale,
264 out dbType, out fieldType, out isLong,
265 out typeName);
266 return fieldType;
269 SqlDbType GetSchemaRowDbType (int ordinal)
271 int csize;
272 short precision, scale;
273 TdsColumnType ctype;
274 TdsDataColumn column;
276 if (ordinal < 0 || ordinal >= command.Tds.Columns.Count)
277 throw new IndexOutOfRangeException ();
279 column = command.Tds.Columns [ordinal];
280 ctype = (TdsColumnType) column.ColumnType;
281 csize = (int) column.ColumnSize;
282 precision = (short) (column.NumericPrecision ?? 0);
283 scale = (short) (column.NumericScale ?? 0);
284 return GetSchemaRowDbType (ctype, csize, precision, scale);
287 private SqlDbType GetSchemaRowDbType (TdsColumnType ctype, int csize, short precision, short scale)
289 Type fieldType;
290 bool isLong;
291 string typeName;
292 int dbType;
294 GetSchemaRowType (ctype, csize, precision, scale,
295 out dbType, out fieldType, out isLong,
296 out typeName);
297 return (SqlDbType) dbType;
300 private void GetSchemaRowType (TdsColumnType ctype, int csize,
301 short precision, short scale,
302 out int dbType, out Type fieldType,
303 out bool isLong, out string typeName)
305 dbType = -1;
306 typeName = string.Empty;
307 isLong = false;
308 fieldType = typeof (Type);
310 switch (ctype) {
311 case TdsColumnType.Int1:
312 case TdsColumnType.Int2:
313 case TdsColumnType.Int4:
314 case TdsColumnType.IntN:
315 case TdsColumnType.BigInt:
316 switch (csize) {
317 case 1:
318 typeName = "tinyint";
319 dbType = (int) SqlDbType.TinyInt;
320 fieldType = typeof (byte);
321 isLong = false;
322 break;
323 case 2:
324 typeName = "smallint";
325 dbType = (int) SqlDbType.SmallInt;
326 fieldType = typeof (short);
327 isLong = false;
328 break;
329 case 4:
330 typeName = "int";
331 dbType = (int) SqlDbType.Int;
332 fieldType = typeof (int);
333 isLong = false;
334 break;
335 case 8:
336 typeName = "bigint";
337 dbType = (int) SqlDbType.BigInt;
338 fieldType = typeof (long);
339 isLong = false;
340 break;
342 break;
343 case TdsColumnType.Real:
344 case TdsColumnType.Float8:
345 case TdsColumnType.FloatN:
346 switch (csize) {
347 case 4:
348 typeName = "real";
349 dbType = (int) SqlDbType.Real;
350 fieldType = typeof (float);
351 isLong = false;
352 break;
353 case 8:
354 typeName = "float";
355 dbType = (int) SqlDbType.Float;
356 fieldType = typeof (double);
357 isLong = false;
358 break;
360 break;
361 case TdsColumnType.Image :
362 typeName = "image";
363 dbType = (int) SqlDbType.Image;
364 fieldType = typeof (byte[]);
365 isLong = true;
366 break;
367 case TdsColumnType.Text :
368 typeName = "text";
369 dbType = (int) SqlDbType.Text;
370 fieldType = typeof (string);
371 isLong = true;
372 break;
373 case TdsColumnType.UniqueIdentifier :
374 typeName = "uniqueidentifier";
375 dbType = (int) SqlDbType.UniqueIdentifier;
376 fieldType = typeof (Guid);
377 isLong = false;
378 break;
379 case TdsColumnType.VarBinary :
380 case TdsColumnType.BigVarBinary :
381 typeName = "varbinary";
382 dbType = (int) SqlDbType.VarBinary;
383 fieldType = typeof (byte[]);
384 isLong = false;
385 break;
386 case TdsColumnType.VarChar :
387 case TdsColumnType.BigVarChar :
388 typeName = "varchar";
389 dbType = (int) SqlDbType.VarChar;
390 fieldType = typeof (string);
391 isLong = false;
392 break;
393 case TdsColumnType.Binary :
394 case TdsColumnType.BigBinary :
395 typeName = "binary";
396 dbType = (int) SqlDbType.Binary;
397 fieldType = typeof (byte[]);
398 isLong = false;
399 break;
400 case TdsColumnType.Char :
401 case TdsColumnType.BigChar :
402 typeName = "char";
403 dbType = (int) SqlDbType.Char;
404 fieldType = typeof (string);
405 isLong = false;
406 break;
407 case TdsColumnType.Bit :
408 case TdsColumnType.BitN :
409 typeName = "bit";
410 dbType = (int) SqlDbType.Bit;
411 fieldType = typeof (bool);
412 isLong = false;
413 break;
414 case TdsColumnType.DateTime4 :
415 case TdsColumnType.DateTime :
416 case TdsColumnType.DateTimeN :
417 switch (csize) {
418 case 4:
419 typeName = "smalldatetime";
420 dbType = (int) SqlDbType.SmallDateTime;
421 fieldType = typeof (DateTime);
422 isLong = false;
423 break;
424 case 8:
425 typeName = "datetime";
426 dbType = (int) SqlDbType.DateTime;
427 fieldType = typeof (DateTime);
428 isLong = false;
429 break;
431 break;
432 case TdsColumnType.Money :
433 case TdsColumnType.MoneyN :
434 case TdsColumnType.Money4 :
435 switch (csize) {
436 case 4:
437 typeName = "smallmoney";
438 dbType = (int) SqlDbType.SmallMoney;
439 fieldType = typeof (decimal);
440 isLong = false;
441 break;
442 case 8:
443 typeName = "money";
444 dbType = (int) SqlDbType.Money;
445 fieldType = typeof (decimal);
446 isLong = false;
447 break;
449 break;
450 case TdsColumnType.NText :
451 typeName = "ntext";
452 dbType = (int) SqlDbType.NText;
453 fieldType = typeof (string);
454 isLong = true;
455 break;
456 case TdsColumnType.NVarChar :
457 typeName = "nvarchar";
458 dbType = (int) SqlDbType.NVarChar;
459 fieldType = typeof (string);
460 isLong = false;
461 break;
462 case TdsColumnType.Decimal :
463 case TdsColumnType.Numeric :
464 // TDS 7.0 returns bigint as decimal(19,0)
465 if (precision == 19 && scale == 0) {
466 typeName = "bigint";
467 dbType = (int) SqlDbType.BigInt;
468 fieldType = typeof (long);
469 } else {
470 typeName = "decimal";
471 dbType = (int) SqlDbType.Decimal;
472 fieldType = typeof (decimal);
474 isLong = false;
475 break;
476 case TdsColumnType.NChar :
477 typeName = "nchar";
478 dbType = (int) SqlDbType.NChar;
479 fieldType = typeof (string);
480 isLong = false;
481 break;
482 case TdsColumnType.SmallMoney :
483 typeName = "smallmoney";
484 dbType = (int) SqlDbType.SmallMoney;
485 fieldType = typeof (decimal);
486 isLong = false;
487 break;
488 default :
489 typeName = "variant";
490 dbType = (int) SqlDbType.Variant;
491 fieldType = typeof (object);
492 isLong = false;
493 break;
498 void Dispose (bool disposing)
500 if (!disposed) {
501 if (disposing) {
502 if (schemaTable != null)
503 schemaTable.Dispose ();
504 Close ();
505 command = null;
507 disposed = true;
511 public
512 override
513 bool GetBoolean (int i)
515 object value = GetValue (i);
516 if (!(value is bool)) {
517 if (value is DBNull) throw new SqlNullValueException ();
518 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
520 return (bool) value;
523 public
524 override
525 byte GetByte (int i)
527 object value = GetValue (i);
528 if (!(value is byte)) {
529 if (value is DBNull) throw new SqlNullValueException ();
530 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
532 return (byte) value;
535 public
536 override
537 long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
539 if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
540 ValidateState ();
541 EnsureDataAvailable ();
543 try {
544 long len = ((Tds)command.Tds).GetSequentialColumnValue (i, dataIndex, buffer, bufferIndex, length);
545 if (len == -1)
546 throw CreateGetBytesOnInvalidColumnTypeException (i);
547 if (len == -2)
548 throw new SqlNullValueException ();
549 return len;
550 } catch (TdsInternalException ex) {
551 command.Connection.Close ();
552 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
556 object value = GetValue (i);
557 if (!(value is byte [])) {
558 SqlDbType type = GetSchemaRowDbType (i);
559 switch (type) {
560 case SqlDbType.Image:
561 if (value is DBNull)
562 throw new SqlNullValueException ();
563 break;
564 case SqlDbType.Text:
565 string text = value as string;
566 if (text != null)
567 value = Encoding.Default.GetBytes (text);
568 else
569 value = null;
570 break;
571 case SqlDbType.NText:
572 string ntext = value as string;
573 if (ntext != null)
574 value = Encoding.Unicode.GetBytes (ntext);
575 else
576 value = null;
577 break;
578 default:
579 throw CreateGetBytesOnInvalidColumnTypeException (i);
583 if (buffer == null)
584 return ((byte []) value).Length; // Return length of data
586 // Copy data into buffer
587 int availLen = (int) ( ( (byte []) value).Length - dataIndex);
588 if (availLen < length)
589 length = availLen;
590 if (dataIndex < 0)
591 return 0;
593 Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
594 return length; // return actual read count
597 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
598 public
599 override
600 char GetChar (int i)
602 throw new NotSupportedException ();
605 public
606 override
607 long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
609 if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
610 ValidateState ();
611 EnsureDataAvailable ();
613 if (i < 0 || i >= command.Tds.Columns.Count)
614 throw new IndexOutOfRangeException ();
616 Encoding encoding = null;
617 byte mul = 1;
618 TdsColumnType colType = (TdsColumnType) command.Tds.Columns[i]["ColumnType"];
619 switch (colType) {
620 case TdsColumnType.Text :
621 case TdsColumnType.VarChar:
622 case TdsColumnType.Char:
623 case TdsColumnType.BigVarChar:
624 encoding = Encoding.ASCII;
625 break;
626 case TdsColumnType.NText :
627 case TdsColumnType.NVarChar:
628 case TdsColumnType.NChar:
629 encoding = Encoding.Unicode;
630 mul = 2;
631 break;
632 default :
633 return -1;
636 long count = 0;
637 if (buffer == null) {
638 count = GetBytes (i,0,(byte[]) null,0,0);
639 return (count/mul);
642 length *= mul;
643 byte[] arr = new byte [length];
644 count = GetBytes (i, dataIndex, arr, 0, length);
645 if (count == -1)
646 throw new InvalidCastException ("Specified cast is not valid");
648 Char[] val = encoding.GetChars (arr, 0, (int)count);
649 val.CopyTo (buffer, bufferIndex);
650 return val.Length;
653 char [] valueBuffer;
654 object value = GetValue (i);
656 if (value is char[])
657 valueBuffer = (char[])value;
658 else if (value is string)
659 valueBuffer = ((string)value).ToCharArray();
660 else {
661 if (value is DBNull) throw new SqlNullValueException ();
662 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
665 if ( buffer == null ) {
666 // Return length of data
667 return valueBuffer.Length;
669 else {
670 // Copy data into buffer
671 Array.Copy (valueBuffer, (int) dataIndex, buffer, bufferIndex, length);
672 return valueBuffer.Length - dataIndex;
677 public
678 override
679 string GetDataTypeName (int i)
681 TdsDataColumn column;
682 TdsColumnType ctype;
683 int csize;
684 short precision;
685 short scale;
687 ValidateState ();
689 if (i < 0 || i >= command.Tds.Columns.Count)
690 throw new IndexOutOfRangeException ();
692 column = command.Tds.Columns [i];
693 ctype = (TdsColumnType) column.ColumnType;
694 csize = (int) column.ColumnSize;
695 precision = (short) (column.NumericPrecision ?? 0);
696 scale = (short) (column.NumericScale ?? 0);
697 return GetSchemaRowTypeName (ctype, csize, precision, scale);
700 public
701 override
702 DateTime GetDateTime (int i)
704 object value = GetValue (i);
705 if (!(value is DateTime)) {
706 if (value is DBNull) throw new SqlNullValueException ();
707 else throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
709 return (DateTime) value;
712 [MonoTODO]
713 public virtual DateTimeOffset GetDateTimeOffset (int i)
715 throw new NotImplementedException ();
718 [MonoTODO]
719 public virtual TimeSpan GetTimeSpan (int i)
721 throw new NotImplementedException ();
724 [MonoTODO]
725 public virtual SqlChars GetSqlChars (int i)
727 throw new NotImplementedException ();
730 public
731 override
732 decimal GetDecimal (int i)
734 object value = GetValue (i);
735 if (!(value is decimal)) {
736 if (value is DBNull) throw new SqlNullValueException ();
737 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
739 return (decimal) value;
742 public
743 override
744 double GetDouble (int i)
746 object value = GetValue (i);
747 if (!(value is double)) {
748 if (value is DBNull) throw new SqlNullValueException ();
749 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
751 return (double) value;
754 public
755 override
756 Type GetFieldType (int i)
758 TdsDataColumn column;
759 TdsColumnType ctype;
760 int csize;
761 short precision;
762 short scale;
764 ValidateState ();
766 if (i < 0 || i >= command.Tds.Columns.Count)
767 throw new IndexOutOfRangeException ();
769 column = command.Tds.Columns [i];
770 ctype = (TdsColumnType) column.ColumnType;
771 csize = (int) column.ColumnSize;
772 precision = (short) (column.NumericPrecision ?? 0);
773 scale = (short) (column.NumericScale ?? 0);
774 return GetSchemaRowFieldType (ctype, csize, precision,
775 scale);
778 public
779 override
780 float GetFloat (int i)
782 object value = GetValue (i);
783 if (!(value is float)) {
784 if (value is DBNull) throw new SqlNullValueException ();
785 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
787 return (float) value;
790 public
791 override
792 Guid GetGuid (int i)
794 object value = GetValue (i);
795 if (!(value is Guid)) {
796 if (value is DBNull) throw new SqlNullValueException ();
797 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
799 return (Guid) value;
802 public
803 override
804 short GetInt16 (int i)
806 object value = GetValue (i);
807 if (!(value is short)) {
808 if (value is DBNull) throw new SqlNullValueException ();
809 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
811 return (short) value;
814 public
815 override
816 int GetInt32 (int i)
818 object value = GetValue (i);
819 if (!(value is int)) {
820 if (value is DBNull) throw new SqlNullValueException ();
821 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
823 return (int) value;
826 public
827 override
828 long GetInt64 (int i)
830 object value = GetValue (i);
831 if (!(value is long)) {
832 if (value is DBNull) throw new SqlNullValueException ();
833 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
835 return (long) value;
838 public
839 override
840 string GetName (int i)
842 ValidateState ();
844 if (i < 0 || i >= command.Tds.Columns.Count)
845 throw new IndexOutOfRangeException ();
846 return (string) command.Tds.Columns[i].ColumnName;
849 public
850 override
851 int GetOrdinal (string name)
853 ValidateState ();
855 if (name == null)
856 throw new ArgumentNullException ("fieldName");
858 string colName;
859 foreach (TdsDataColumn schema in command.Tds.Columns) {
860 colName = schema.ColumnName;
861 if (colName.Equals (name) || String.Compare (colName, name, true) == 0)
862 return (int) schema.ColumnOrdinal;
864 throw new IndexOutOfRangeException ();
867 public
868 override
869 DataTable GetSchemaTable ()
871 ValidateState ();
873 if (schemaTable == null)
874 schemaTable = ConstructSchemaTable ();
876 if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
877 return schemaTable;
879 if (!moreResults)
880 return null;
882 foreach (TdsDataColumn schema in command.Tds.Columns) {
883 DataRow row = schemaTable.NewRow ();
885 row [COLUMN_NAME_IDX] = GetSchemaValue (schema.ColumnName);
886 row [COLUMN_ORDINAL_IDX] = GetSchemaValue (schema.ColumnOrdinal);
887 row [IS_UNIQUE_IDX] = GetSchemaValue (schema.IsUnique);
888 row [IS_AUTO_INCREMENT_IDX] = GetSchemaValue (schema.IsAutoIncrement);
889 row [IS_ROW_VERSION_IDX] = GetSchemaValue (schema.IsRowVersion);
890 row [IS_HIDDEN_IDX] = GetSchemaValue (schema.IsHidden);
891 row [IS_IDENTITY_IDX] = GetSchemaValue (schema.IsIdentity);
892 row [NUMERIC_PRECISION_IDX] = GetSchemaValue (schema.NumericPrecision);
893 row [IS_KEY_IDX] = GetSchemaValue (schema.IsKey);
894 row [IS_ALIASED_IDX] = GetSchemaValue (schema.IsAliased);
895 row [IS_EXPRESSION_IDX] = GetSchemaValue (schema.IsExpression);
896 row [IS_READ_ONLY_IDX] = GetSchemaValue (schema.IsReadOnly);
897 row [BASE_SERVER_NAME_IDX] = GetSchemaValue (schema.BaseServerName);
898 row [BASE_CATALOG_NAME_IDX] = GetSchemaValue (schema.BaseCatalogName);
899 row [BASE_COLUMN_NAME_IDX] = GetSchemaValue (schema.BaseColumnName);
900 row [BASE_SCHEMA_NAME_IDX] = GetSchemaValue (schema.BaseSchemaName);
901 row [BASE_TABLE_NAME_IDX] = GetSchemaValue (schema.BaseTableName);
902 row [ALLOW_DBNULL_IDX] = GetSchemaValue (schema.AllowDBNull);
903 row [PROVIDER_SPECIFIC_TYPE_IDX] = DBNull.Value;
904 row [DATA_TYPE_NAME_IDX] = GetSchemaValue (schema.DataTypeName);
905 row [XML_SCHEMA_COLLCTN_DB_IDX] = DBNull.Value;
906 row [XML_SCHEMA_COLLCTN_OWN_SCHEMA_IDX] = DBNull.Value;
907 row [XML_SCHEMA_COLLCTN_NAME_IDX] = DBNull.Value;
908 row [UDT_ASMBLY_QUALIFIED_NAME_IDX] = DBNull.Value;
909 row [NON_VER_PROVIDER_TYPE_IDX] = DBNull.Value;
910 row [IS_COLUMN_SET] = DBNull.Value;
911 // We don't always get the base column name.
912 if (row [BASE_COLUMN_NAME_IDX] == DBNull.Value)
913 row [BASE_COLUMN_NAME_IDX] = row [COLUMN_NAME_IDX];
915 TdsColumnType ctype;
916 int csize, dbType;
917 Type fieldType;
918 bool isLong;
919 string typeName;
920 short precision;
921 short scale;
922 ctype = (TdsColumnType) schema.ColumnType;
923 csize = (int) schema.ColumnSize;
924 precision = (short) GetSchemaValue (schema.NumericPrecision);
925 scale = (short) GetSchemaValue (schema.NumericScale);
927 GetSchemaRowType (ctype, csize, precision, scale,
928 out dbType, out fieldType, out isLong,
929 out typeName);
931 row [COLUMN_SIZE_IDX] = csize;
932 row [NUMERIC_PRECISION_IDX] = precision;
933 row [NUMERIC_SCALE_IDX] = scale;
934 row [PROVIDER_TYPE_IDX] = dbType;
935 row [DATA_TYPE_IDX] = fieldType;
936 row [IS_LONG_IDX] = isLong;
937 if ((bool)row [IS_HIDDEN_IDX] == false)
938 visibleFieldCount += 1;
940 schemaTable.Rows.Add (row);
942 return schemaTable;
945 private static object GetSchemaValue (TdsDataColumn schema, string key)
947 object val = schema [key];
948 if (val != null)
949 return val;
950 else
951 return DBNull.Value;
954 static object GetSchemaValue (object value)
956 if (value == null)
957 return DBNull.Value;
959 return value;
962 public
963 virtual
964 SqlBinary GetSqlBinary (int i)
966 object value = GetSqlValue (i);
967 if (!(value is SqlBinary))
968 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
969 return (SqlBinary) value;
972 public
973 virtual
974 SqlBoolean GetSqlBoolean (int i)
976 object value = GetSqlValue (i);
977 if (!(value is SqlBoolean))
978 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
979 return (SqlBoolean) value;
982 public
983 virtual
984 SqlByte GetSqlByte (int i)
986 object value = GetSqlValue (i);
987 if (!(value is SqlByte))
988 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
989 return (SqlByte) value;
992 public
993 virtual
994 SqlDateTime GetSqlDateTime (int i)
996 object value = GetSqlValue (i);
997 if (!(value is SqlDateTime))
998 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
999 return (SqlDateTime) value;
1002 public
1003 virtual
1004 SqlDecimal GetSqlDecimal (int i)
1006 object value = GetSqlValue (i);
1007 if (!(value is SqlDecimal))
1008 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1009 return (SqlDecimal) value;
1012 public
1013 virtual
1014 SqlDouble GetSqlDouble (int i)
1016 object value = GetSqlValue (i);
1017 if (!(value is SqlDouble))
1018 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1019 return (SqlDouble) value;
1022 public
1023 virtual
1024 SqlGuid GetSqlGuid (int i)
1026 object value = GetSqlValue (i);
1027 if (!(value is SqlGuid))
1028 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1029 return (SqlGuid) value;
1032 public
1033 virtual
1034 SqlInt16 GetSqlInt16 (int i)
1036 object value = GetSqlValue (i);
1037 if (!(value is SqlInt16))
1038 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1039 return (SqlInt16) value;
1042 public
1043 virtual
1044 SqlInt32 GetSqlInt32 (int i)
1046 object value = GetSqlValue (i);
1047 if (!(value is SqlInt32))
1048 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1049 return (SqlInt32) value;
1052 public
1053 virtual
1054 SqlInt64 GetSqlInt64 (int i)
1056 object value = GetSqlValue (i);
1057 if (!(value is SqlInt64))
1058 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1059 return (SqlInt64) value;
1062 public
1063 virtual
1064 SqlMoney GetSqlMoney (int i)
1066 object value = GetSqlValue (i);
1067 if (!(value is SqlMoney))
1068 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1069 return (SqlMoney) value;
1072 public
1073 virtual
1074 SqlSingle GetSqlSingle (int i)
1076 object value = GetSqlValue (i);
1077 if (!(value is SqlSingle))
1078 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1079 return (SqlSingle) value;
1082 public
1083 virtual
1084 SqlString GetSqlString (int i)
1086 object value = GetSqlValue (i);
1087 if (!(value is SqlString))
1088 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1089 return (SqlString) value;
1092 public virtual SqlXml GetSqlXml (int i)
1094 object value = GetSqlValue (i);
1095 if (!(value is SqlXml)) {
1096 if (value is DBNull) {
1097 throw new SqlNullValueException ();
1098 } else if (command.Tds.TdsVersion <= TdsVersion.tds80 && value is SqlString) {
1099 // Workaround for TDS 7/8/8.1 clients
1100 // Xml column types are supported only from Sql Server 2005 / TDS 9, however
1101 // when a TDS 7/8/8.1 client requests for Xml column data, Sql Server 2005 returns
1102 // it as NTEXT
1103 MemoryStream stream = null;
1104 if (!((SqlString) value).IsNull)
1105 stream = new MemoryStream (Encoding.Unicode.GetBytes (value.ToString()));
1106 value = new SqlXml (stream);
1107 } else {
1108 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1111 return (SqlXml) value;
1114 public
1115 virtual
1116 object GetSqlValue (int i)
1118 object value = GetValue (i);
1119 //Console.WriteLine ("Type of value: {0}", value.GetType ());
1121 SqlDbType type = GetSchemaRowDbType (i);
1122 switch (type) {
1123 case SqlDbType.BigInt:
1124 if (value == DBNull.Value)
1125 return SqlInt64.Null;
1126 return (SqlInt64) ((long) value);
1127 case SqlDbType.Binary:
1128 case SqlDbType.Image:
1129 case SqlDbType.VarBinary:
1130 case SqlDbType.Timestamp:
1131 if (value == DBNull.Value)
1132 return SqlBinary.Null;
1133 return (SqlBinary) ((byte[]) value);
1134 case SqlDbType.Bit:
1135 if (value == DBNull.Value)
1136 return SqlBoolean.Null;
1137 return (SqlBoolean) ((bool) value);
1138 case SqlDbType.Char:
1139 case SqlDbType.NChar:
1140 case SqlDbType.NText:
1141 case SqlDbType.NVarChar:
1142 case SqlDbType.Text:
1143 case SqlDbType.VarChar:
1144 if (value == DBNull.Value)
1145 return SqlString.Null;
1146 return (SqlString) ((string) value);
1147 case SqlDbType.DateTime:
1148 case SqlDbType.SmallDateTime:
1149 if (value == DBNull.Value)
1150 return SqlDateTime.Null;
1151 return (SqlDateTime) ((DateTime) value);
1152 case SqlDbType.Decimal:
1153 if (value == DBNull.Value)
1154 return SqlDecimal.Null;
1155 if (value is TdsBigDecimal)
1156 return SqlDecimalExtensions.FromTdsBigDecimal ((TdsBigDecimal) value);
1157 if (value is Int64)
1158 return (SqlDecimal)((long) value);
1159 return (SqlDecimal) ((decimal) value);
1160 case SqlDbType.Float:
1161 if (value == DBNull.Value)
1162 return SqlDouble.Null;
1163 return (SqlDouble) ((double) value);
1164 case SqlDbType.Int:
1165 if (value == DBNull.Value)
1166 return SqlInt32.Null;
1167 return (SqlInt32) ((int) value);
1168 case SqlDbType.Money:
1169 case SqlDbType.SmallMoney:
1170 if (value == DBNull.Value)
1171 return SqlMoney.Null;
1172 return (SqlMoney) ((decimal) value);
1173 case SqlDbType.Real:
1174 if (value == DBNull.Value)
1175 return SqlSingle.Null;
1176 return (SqlSingle) ((float) value);
1177 case SqlDbType.UniqueIdentifier:
1178 if (value == DBNull.Value)
1179 return SqlGuid.Null;
1180 return (SqlGuid) ((Guid) value);
1181 case SqlDbType.SmallInt:
1182 if (value == DBNull.Value)
1183 return SqlInt16.Null;
1184 return (SqlInt16) ((short) value);
1185 case SqlDbType.TinyInt:
1186 if (value == DBNull.Value)
1187 return SqlByte.Null;
1188 return (SqlByte) ((byte) value);
1189 case SqlDbType.Xml:
1190 if (value == DBNull.Value)
1191 return SqlByte.Null;
1192 return (SqlXml) value;
1195 throw new InvalidOperationException ("The type of this column is unknown.");
1198 public
1199 virtual
1200 int GetSqlValues (object[] values)
1202 ValidateState ();
1203 EnsureDataAvailable ();
1205 if (values == null)
1206 throw new ArgumentNullException ("values");
1208 int count = 0;
1209 int columnCount = command.Tds.Columns.Count;
1210 int arrayCount = values.Length;
1212 if (arrayCount > columnCount)
1213 count = columnCount;
1214 else
1215 count = arrayCount;
1217 for (int i = 0; i < count; i += 1)
1218 values [i] = GetSqlValue (i);
1220 return count;
1223 public
1224 override
1225 string GetString (int i)
1227 object value = GetValue (i);
1228 if (!(value is string)) {
1229 if (value is DBNull) throw new SqlNullValueException ();
1230 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1232 return (string) value;
1235 public
1236 override
1237 object GetValue (int i)
1239 ValidateState ();
1240 EnsureDataAvailable ();
1242 if (i < 0 || i >= command.Tds.Columns.Count)
1243 throw new IndexOutOfRangeException ();
1245 try {
1246 if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
1247 return ((Tds)command.Tds).GetSequentialColumnValue (i);
1249 } catch (TdsInternalException ex) {
1250 command.Connection.Close ();
1251 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1254 return command.Tds.ColumnValues [i];
1257 public
1258 override
1259 int GetValues (object[] values)
1261 ValidateState ();
1262 EnsureDataAvailable ();
1264 if (values == null)
1265 throw new ArgumentNullException ("values");
1267 int len = values.Length;
1268 var tds = command.Tds;
1269 int columns = Math.Min (len, tds.Columns.Count);
1271 if ((command.CommandBehavior & CommandBehavior.SequentialAccess) != 0) {
1272 for (int i = 0; i < columns; ++i) {
1273 values [i] = tds.GetSequentialColumnValue (i);
1275 } else {
1276 int bigDecimalIndex = tds.ColumnValues.BigDecimalIndex;
1278 // If a four-byte decimal is stored, then we can't convert to
1279 // a native type. Throw an OverflowException.
1280 if (bigDecimalIndex >= 0 && bigDecimalIndex < len)
1281 throw new OverflowException ();
1282 try {
1283 tds.ColumnValues.CopyTo (0, values, 0, columns);
1284 } catch (TdsInternalException ex) {
1285 command.Connection.Close ();
1286 throw SqlException.FromTdsInternalException ((TdsInternalException)ex);
1290 return columns;
1294 public override IEnumerator GetEnumerator ()
1296 return new DbEnumerator (this);
1299 public
1300 override
1301 bool IsDBNull (int i)
1303 return GetValue (i) == DBNull.Value;
1306 public
1307 override
1308 bool NextResult ()
1310 ValidateState ();
1312 if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0) {
1313 moreResults = false;
1314 rowsRead = 0;
1315 haveRead = false;
1316 return false;
1319 try {
1320 moreResults = command.Tds.NextResult ();
1321 } catch (TdsInternalException ex) {
1322 command.Connection.Close ();
1323 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1325 if (!moreResults)
1326 command.GetOutputParameters ();
1327 else {
1328 // new schema - don't do anything except reset schemaTable as command.Tds.Columns is already updated
1329 schemaTable = null;
1332 rowsRead = 0;
1333 haveRead = false;
1334 resultsRead += 1;
1335 return moreResults;
1338 public
1339 override
1340 bool Read ()
1342 ValidateState ();
1344 if (!haveRead || readResultUsed)
1345 readResult = ReadRecord ();
1346 readResultUsed = true;
1347 return readResult;
1350 internal bool ReadRecord ()
1352 readResultUsed = false;
1354 if ((command.CommandBehavior & CommandBehavior.SingleRow) != 0 && haveRead)
1355 return false;
1356 if ((command.CommandBehavior & CommandBehavior.SchemaOnly) != 0)
1357 return false;
1358 if (!moreResults)
1359 return false;
1361 try {
1362 bool result = command.Tds.NextRow ();
1363 if (result)
1364 rowsRead++;
1365 haveRead = true;
1366 return result;
1367 } catch (TdsInternalException ex) {
1368 command.Connection.Close ();
1369 throw SqlException.FromTdsInternalException ((TdsInternalException) ex);
1373 void ValidateState ()
1375 if (IsClosed)
1376 throw new InvalidOperationException ("Invalid attempt to read data when reader is closed");
1379 void EnsureDataAvailable ()
1381 if (!readResult || !haveRead || !readResultUsed)
1382 throw new InvalidOperationException ("No data available.");
1385 InvalidCastException CreateGetBytesOnInvalidColumnTypeException (int ordinal)
1387 string message = string.Format (CultureInfo.InvariantCulture,
1388 "Invalid attempt to GetBytes on column '{0}'." +
1389 "The GetBytes function can only be used on " +
1390 "columns of type Text, NText, or Image.",
1391 GetName (ordinal));
1392 return new InvalidCastException (message);
1395 public override Type GetProviderSpecificFieldType (int i)
1397 return (GetSqlValue (i).GetType());
1400 public override object GetProviderSpecificValue (int i)
1402 return (GetSqlValue (i));
1405 public override int GetProviderSpecificValues (object [] values)
1407 return (GetSqlValues (values));
1410 public virtual SqlBytes GetSqlBytes (int i)
1412 //object value = GetSqlValue (i);
1413 //if (!(value is SqlBinary))
1414 // throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
1415 Byte[] val = (byte[])GetValue(i);
1416 SqlBytes sb = new SqlBytes (val);
1417 return (sb);
1420 public override T GetFieldValue<T> (int i)
1422 return (T)GetValue(i);
1425 [MonoTODO]
1426 public virtual XmlReader GetXmlReader (int i)
1428 throw new NotImplementedException ();
1431 override public Task<T> GetFieldValueAsync<T> (int i, CancellationToken cancellationToken)
1433 return base.GetFieldValueAsync<T> (i, cancellationToken);
1436 override public Stream GetStream (int i)
1438 return base.GetStream (i);
1440 override public TextReader GetTextReader (int i)
1442 return base.GetTextReader (i);
1445 override public Task<bool> IsDBNullAsync (int i, CancellationToken cancellationToken)
1447 return base.IsDBNullAsync (i, cancellationToken);
1450 #endregion // Methods