2 // System.Data.SqlClient.SqlDataReader.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Daniel Morgan (danmorg@sc.rr.com)
7 // Tim Coleman (tim@timcoleman.com)
9 // (C) Ximian, Inc 2002
10 // (C) Daniel Morgan 2002
11 // Copyright (C) Tim Coleman, 2002
14 using Mono.Data.TdsClient.Internal;
16 using System.Collections;
17 using System.ComponentModel;
19 using System.Data.Common;
20 using System.Data.SqlTypes;
22 namespace System.Data.SqlClient {
23 public sealed class SqlDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
28 ArrayList dataTypeNames;
29 bool disposed = false;
36 DataTable schemaTable;
42 internal SqlDataReader (SqlCommand command)
44 this.command = command;
45 schemaTable = ConstructSchemaTable ();
49 isSelect = (command.CommandText.Trim ().ToUpper ().StartsWith ("SELECT"));
50 command.Tds.RecordsAffected = 0;
54 #endregion // Constructors
62 public int FieldCount {
63 get { return fieldCount; }
66 public bool IsClosed {
67 get { return isClosed; }
70 public object this [int i] {
71 get { return GetValue (i); }
74 public object this [string name] {
75 get { return GetValue (GetOrdinal (name)); }
78 public int RecordsAffected {
83 return command.Tds.RecordsAffected;
87 #endregion // Properties
94 command.CloseDataReader (moreResults);
97 private static DataTable ConstructSchemaTable ()
99 Type booleanType = Type.GetType ("System.Boolean");
100 Type stringType = Type.GetType ("System.String");
101 Type intType = Type.GetType ("System.Int32");
102 Type typeType = Type.GetType ("System.Type");
103 Type shortType = Type.GetType ("System.Int16");
105 DataTable schemaTable = new DataTable ("SchemaTable");
106 schemaTable.Columns.Add ("ColumnName", stringType);
107 schemaTable.Columns.Add ("ColumnOrdinal", intType);
108 schemaTable.Columns.Add ("ColumnSize", intType);
109 schemaTable.Columns.Add ("NumericPrecision", shortType);
110 schemaTable.Columns.Add ("NumericScale", shortType);
111 schemaTable.Columns.Add ("IsUnique", booleanType);
112 schemaTable.Columns.Add ("IsKey", booleanType);
113 schemaTable.Columns.Add ("BaseServerName", stringType);
114 schemaTable.Columns.Add ("BaseCatalogName", stringType);
115 schemaTable.Columns.Add ("BaseColumnName", stringType);
116 schemaTable.Columns.Add ("BaseSchemaName", stringType);
117 schemaTable.Columns.Add ("BaseTableName", stringType);
118 schemaTable.Columns.Add ("DataType", typeType);
119 schemaTable.Columns.Add ("AllowDBNull", booleanType);
120 schemaTable.Columns.Add ("ProviderType", intType);
121 schemaTable.Columns.Add ("IsAliased", booleanType);
122 schemaTable.Columns.Add ("IsExpression", booleanType);
123 schemaTable.Columns.Add ("IsIdentity", booleanType);
124 schemaTable.Columns.Add ("IsAutoIncrement", booleanType);
125 schemaTable.Columns.Add ("IsRowVersion", booleanType);
126 schemaTable.Columns.Add ("IsHidden", booleanType);
127 schemaTable.Columns.Add ("IsLong", booleanType);
128 schemaTable.Columns.Add ("IsReadOnly", booleanType);
133 private void Dispose (bool disposing)
137 schemaTable.Dispose ();
144 public bool GetBoolean (int i)
146 object value = GetValue (i);
147 if (!(value is bool))
148 throw new InvalidCastException ();
152 public byte GetByte (int i)
154 object value = GetValue (i);
155 if (!(value is byte))
156 throw new InvalidCastException ();
160 public long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
162 object value = GetValue (i);
163 if (!(value is byte []))
164 throw new InvalidCastException ();
165 Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
166 return ((byte []) value).Length - dataIndex;
169 public char GetChar (int i)
171 object value = GetValue (i);
172 if (!(value is char))
173 throw new InvalidCastException ();
177 public long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
179 object value = GetValue (i);
180 if (!(value is char[]))
181 throw new InvalidCastException ();
182 Array.Copy ((char []) value, (int) dataIndex, buffer, bufferIndex, length);
183 return ((char []) value).Length - dataIndex;
186 [MonoTODO ("Implement GetData")]
187 public IDataReader GetData (int i)
189 throw new NotImplementedException ();
192 public string GetDataTypeName (int i)
194 return (string) dataTypeNames [i];
197 public DateTime GetDateTime (int i)
199 object value = GetValue (i);
200 if (!(value is DateTime))
201 throw new InvalidCastException ();
202 return (DateTime) value;
205 public decimal GetDecimal (int i)
207 object value = GetValue (i);
208 if (!(value is decimal))
209 throw new InvalidCastException ();
210 return (decimal) value;
213 public double GetDouble (int i)
215 object value = GetValue (i);
216 if (!(value is double))
217 throw new InvalidCastException ();
218 return (double) value;
221 public Type GetFieldType (int i)
223 return (Type) schemaTable.Rows[i]["DataType"];
226 public float GetFloat (int i)
228 object value = GetValue (i);
229 if (!(value is float))
230 throw new InvalidCastException ();
231 return (float) value;
234 public Guid GetGuid (int i)
236 object value = GetValue (i);
237 if (!(value is Guid))
238 throw new InvalidCastException ();
242 public short GetInt16 (int i)
244 object value = GetValue (i);
245 if (!(value is short))
246 throw new InvalidCastException ();
247 return (short) value;
250 public int GetInt32 (int i)
252 object value = GetValue (i);
254 throw new InvalidCastException ();
258 public long GetInt64 (int i)
260 object value = GetValue (i);
261 if (!(value is long))
262 throw new InvalidCastException ();
266 public string GetName (int i)
268 return (string) schemaTable.Rows[i]["ColumnName"];
271 public int GetOrdinal (string name)
273 foreach (DataRow schemaRow in schemaTable.Rows)
274 if (((string) schemaRow ["ColumnName"]).Equals (name))
275 return (int) schemaRow ["ColumnOrdinal"];
276 foreach (DataRow schemaRow in schemaTable.Rows)
277 if (String.Compare (((string) schemaRow ["ColumnName"]), name, true) == 0)
278 return (int) schemaRow ["ColumnOrdinal"];
279 throw new IndexOutOfRangeException ();
282 public DataTable GetSchemaTable ()
284 if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
292 dataTypeNames = new ArrayList ();
294 foreach (TdsSchemaInfo schema in command.Tds.Schema) {
295 DataRow row = schemaTable.NewRow ();
297 row ["ColumnName"] = GetSchemaValue (schema, "ColumnName");
298 row ["ColumnSize"] = GetSchemaValue (schema, "ColumnSize");
299 row ["ColumnOrdinal"] = GetSchemaValue (schema, "ColumnOrdinal");
300 row ["NumericPrecision"] = GetSchemaValue (schema, "NumericPrecision");
301 row ["NumericScale"] = GetSchemaValue (schema, "NumericScale");
302 row ["IsUnique"] = GetSchemaValue (schema, "IsUnique");
303 row ["IsKey"] = GetSchemaValue (schema, "IsKey");
304 row ["BaseServerName"] = GetSchemaValue (schema, "BaseServerName");
305 row ["BaseCatalogName"] = GetSchemaValue (schema, "BaseCatalogName");
306 row ["BaseColumnName"] = GetSchemaValue (schema, "BaseColumnName");
307 row ["BaseSchemaName"] = GetSchemaValue (schema, "BaseSchemaName");
308 row ["BaseTableName"] = GetSchemaValue (schema, "BaseTableName");
309 row ["AllowDBNull"] = GetSchemaValue (schema, "AllowDBNull");
310 row ["IsAliased"] = GetSchemaValue (schema, "IsAliased");
311 row ["IsExpression"] = GetSchemaValue (schema, "IsExpression");
312 row ["IsIdentity"] = GetSchemaValue (schema, "IsIdentity");
313 row ["IsAutoIncrement"] = GetSchemaValue (schema, "IsAutoIncrement");
314 row ["IsRowVersion"] = GetSchemaValue (schema, "IsRowVersion");
315 row ["IsHidden"] = GetSchemaValue (schema, "IsHidden");
316 row ["IsReadOnly"] = GetSchemaValue (schema, "IsReadOnly");
318 // We don't always get the base column name.
319 if (row ["BaseColumnName"] == DBNull.Value)
320 row ["BaseColumnName"] = row ["ColumnName"];
322 switch ((TdsColumnType) schema ["ColumnType"]) {
323 case TdsColumnType.Image :
324 dataTypeNames.Add ("image");
325 row ["ProviderType"] = (int) SqlDbType.Image;
326 row ["DataType"] = typeof (byte[]);
327 row ["IsLong"] = true;
329 case TdsColumnType.Text :
330 dataTypeNames.Add ("text");
331 row ["ProviderType"] = (int) SqlDbType.Text;
332 row ["DataType"] = typeof (string);
333 row ["IsLong"] = true;
335 case TdsColumnType.UniqueIdentifier :
336 dataTypeNames.Add ("uniqueidentifier");
337 row ["ProviderType"] = (int) SqlDbType.UniqueIdentifier;
338 row ["DataType"] = typeof (Guid);
339 row ["IsLong"] = false;
341 case TdsColumnType.VarBinary :
342 case TdsColumnType.BigVarBinary :
343 dataTypeNames.Add ("varbinary");
344 row ["ProviderType"] = (int) SqlDbType.VarBinary;
345 row ["DataType"] = typeof (byte[]);
346 row ["IsLong"] = true;
348 case TdsColumnType.IntN :
349 case TdsColumnType.Int4 :
350 dataTypeNames.Add ("int");
351 row ["ProviderType"] = (int) SqlDbType.Int;
352 row ["DataType"] = typeof (int);
353 row ["IsLong"] = false;
355 case TdsColumnType.VarChar :
356 case TdsColumnType.BigVarChar :
357 dataTypeNames.Add ("varchar");
358 row ["ProviderType"] = (int) SqlDbType.VarChar;
359 row ["DataType"] = typeof (string);
360 row ["IsLong"] = false;
362 case TdsColumnType.Binary :
363 case TdsColumnType.BigBinary :
364 dataTypeNames.Add ("binary");
365 row ["ProviderType"] = (int) SqlDbType.Binary;
366 row ["DataType"] = typeof (byte[]);
367 row ["IsLong"] = true;
369 case TdsColumnType.Char :
370 case TdsColumnType.BigChar :
371 dataTypeNames.Add ("char");
372 row ["ProviderType"] = (int) SqlDbType.Char;
373 row ["DataType"] = typeof (string);
374 row ["IsLong"] = false;
376 case TdsColumnType.Int1 :
377 dataTypeNames.Add ("tinyint");
378 row ["ProviderType"] = (int) SqlDbType.TinyInt;
379 row ["DataType"] = typeof (byte);
380 row ["IsLong"] = false;
382 case TdsColumnType.Bit :
383 case TdsColumnType.BitN :
384 dataTypeNames.Add ("bit");
385 row ["ProviderType"] = (int) SqlDbType.Bit;
386 row ["DataType"] = typeof (bool);
387 row ["IsLong"] = false;
389 case TdsColumnType.Int2 :
390 dataTypeNames.Add ("smallint");
391 row ["ProviderType"] = (int) SqlDbType.SmallInt;
392 row ["DataType"] = typeof (short);
393 row ["IsLong"] = false;
395 case TdsColumnType.DateTime4 :
396 case TdsColumnType.DateTime :
397 case TdsColumnType.DateTimeN :
398 dataTypeNames.Add ("datetime");
399 row ["ProviderType"] = (int) SqlDbType.DateTime;
400 row ["DataType"] = typeof (DateTime);
401 row ["IsLong"] = false;
403 case TdsColumnType.Real :
404 dataTypeNames.Add ("real");
405 row ["ProviderType"] = (int) SqlDbType.Real;
406 row ["DataType"] = typeof (float);
408 case TdsColumnType.Money :
409 case TdsColumnType.MoneyN :
410 case TdsColumnType.Money4 :
411 dataTypeNames.Add ("money");
412 row ["ProviderType"] = (int) SqlDbType.Money;
413 row ["DataType"] = typeof (decimal);
414 row ["IsLong"] = false;
416 case TdsColumnType.Float8 :
417 case TdsColumnType.FloatN :
418 dataTypeNames.Add ("float");
419 row ["ProviderType"] = (int) SqlDbType.Float;
420 row ["DataType"] = typeof (double);
421 row ["IsLong"] = false;
423 case TdsColumnType.NText :
424 dataTypeNames.Add ("ntext");
425 row ["ProviderType"] = (int) SqlDbType.NText;
426 row ["DataType"] = typeof (string);
427 row ["IsLong"] = true;
429 case TdsColumnType.NVarChar :
430 dataTypeNames.Add ("nvarchar");
431 row ["ProviderType"] = (int) SqlDbType.NVarChar;
432 row ["DataType"] = typeof (string);
433 row ["IsLong"] = false;
435 case TdsColumnType.Decimal :
436 case TdsColumnType.Numeric :
437 dataTypeNames.Add ("decimal");
438 row ["ProviderType"] = (int) SqlDbType.Decimal;
439 row ["DataType"] = typeof (decimal);
440 row ["IsLong"] = false;
442 case TdsColumnType.NChar :
443 dataTypeNames.Add ("nchar");
444 row ["ProviderType"] = (int) SqlDbType.NChar;
445 row ["DataType"] = typeof (string);
446 row ["IsLong"] = false;
448 case TdsColumnType.SmallMoney :
449 dataTypeNames.Add ("smallmoney");
450 row ["ProviderType"] = (int) SqlDbType.SmallMoney;
451 row ["DataType"] = typeof (decimal);
452 row ["IsLong"] = false;
455 dataTypeNames.Add ("variant");
456 row ["ProviderType"] = (int) SqlDbType.Variant;
457 row ["DataType"] = typeof (object);
458 row ["IsLong"] = false;
462 schemaTable.Rows.Add (row);
469 private static object GetSchemaValue (TdsSchemaInfo schema, object key)
471 if (schema.ContainsKey (key) && schema [key] != null)
476 public SqlBinary GetSqlBinary (int i)
478 throw new NotImplementedException ();
481 public SqlBoolean GetSqlBoolean (int i)
483 object value = GetSqlValue (i);
484 if (!(value is SqlBoolean))
485 throw new InvalidCastException ();
486 return (SqlBoolean) value;
489 public SqlByte GetSqlByte (int i)
491 object value = GetSqlValue (i);
492 if (!(value is SqlByte))
493 throw new InvalidCastException ();
494 return (SqlByte) value;
497 public SqlDateTime GetSqlDateTime (int i)
499 object value = GetSqlValue (i);
500 if (!(value is SqlDateTime))
501 throw new InvalidCastException ();
502 return (SqlDateTime) value;
505 public SqlDecimal GetSqlDecimal (int i)
507 object value = GetSqlValue (i);
508 if (!(value is SqlDecimal))
509 throw new InvalidCastException ();
510 return (SqlDecimal) value;
513 public SqlDouble GetSqlDouble (int i)
515 object value = GetSqlValue (i);
516 if (!(value is SqlDouble))
517 throw new InvalidCastException ();
518 return (SqlDouble) value;
521 public SqlGuid GetSqlGuid (int i)
523 object value = GetSqlValue (i);
524 if (!(value is SqlGuid))
525 throw new InvalidCastException ();
526 return (SqlGuid) value;
529 public SqlInt16 GetSqlInt16 (int i)
531 object value = GetSqlValue (i);
532 if (!(value is SqlInt16))
533 throw new InvalidCastException ();
534 return (SqlInt16) value;
537 public SqlInt32 GetSqlInt32 (int i)
539 object value = GetSqlValue (i);
540 if (!(value is SqlInt32))
541 throw new InvalidCastException ();
542 return (SqlInt32) value;
545 public SqlInt64 GetSqlInt64 (int i)
547 object value = GetSqlValue (i);
548 if (!(value is SqlInt64))
549 throw new InvalidCastException ();
550 return (SqlInt64) value;
553 public SqlMoney GetSqlMoney (int i)
555 object value = GetSqlValue (i);
556 if (!(value is SqlMoney))
557 throw new InvalidCastException ();
558 return (SqlMoney) value;
561 public SqlSingle GetSqlSingle (int i)
563 object value = GetSqlValue (i);
564 if (!(value is SqlSingle))
565 throw new InvalidCastException ();
566 return (SqlSingle) value;
569 public SqlString GetSqlString (int i)
571 object value = GetSqlValue (i);
572 if (!(value is SqlString))
573 throw new InvalidCastException ();
574 return (SqlString) value;
577 [MonoTODO ("Implement TdsBigDecimal conversion. SqlDbType.Real fails tests?")]
578 public object GetSqlValue (int i)
580 SqlDbType type = (SqlDbType) (schemaTable.Rows [i]["ProviderType"]);
581 object value = GetValue (i);
584 case SqlDbType.BigInt:
586 return SqlInt64.Null;
587 return (SqlInt64) ((long) value);
588 case SqlDbType.Binary:
589 case SqlDbType.Image:
590 case SqlDbType.VarBinary:
591 case SqlDbType.Timestamp:
593 return SqlBinary.Null;
594 return (SqlBinary) ((byte[]) value);
597 return SqlBoolean.Null;
598 return (SqlBoolean) ((bool) value);
600 case SqlDbType.NChar:
601 case SqlDbType.NText:
602 case SqlDbType.NVarChar:
604 case SqlDbType.VarChar:
606 return SqlString.Null;
607 return (SqlString) ((string) value);
608 case SqlDbType.DateTime:
609 case SqlDbType.SmallDateTime:
611 return SqlDateTime.Null;
612 return (SqlDateTime) ((DateTime) value);
613 case SqlDbType.Decimal:
615 return SqlDecimal.Null;
616 if (value is TdsBigDecimal)
617 return SqlDecimal.FromTdsBigDecimal ((TdsBigDecimal) value);
618 return (SqlDecimal) ((decimal) value);
619 case SqlDbType.Float:
621 return SqlDouble.Null;
622 return (SqlDouble) ((double) value);
625 return SqlInt32.Null;
626 return (SqlInt32) ((int) value);
627 case SqlDbType.Money:
628 case SqlDbType.SmallMoney:
630 return SqlMoney.Null;
631 return (SqlMoney) ((decimal) value);
634 return SqlSingle.Null;
635 return (SqlSingle) ((float) value);
636 case SqlDbType.UniqueIdentifier:
639 return (SqlGuid) ((Guid) value);
640 case SqlDbType.SmallInt:
642 return SqlInt16.Null;
643 return (SqlInt16) ((short) value);
644 case SqlDbType.TinyInt:
647 return (SqlByte) ((byte) value);
650 throw new InvalidOperationException ("The type of this column is unknown.");
653 public int GetSqlValues (object[] values)
656 int columnCount = schemaTable.Rows.Count;
657 int arrayCount = values.Length;
659 if (arrayCount > columnCount)
664 for (int i = 0; i < count; i += 1)
665 values [i] = GetSqlValue (i);
670 public string GetString (int i)
672 object value = GetValue (i);
673 if (!(value is string))
674 throw new InvalidCastException ();
675 return (string) value;
678 public object GetValue (int i)
680 return command.Tds.ColumnValues [i];
683 public int GetValues (object[] values)
685 int len = values.Length;
686 int bigDecimalIndex = command.Tds.ColumnValues.BigDecimalIndex;
688 // If a four-byte decimal is stored, then we can't convert to
689 // a native type. Throw an OverflowException.
690 if (bigDecimalIndex >= 0 && bigDecimalIndex < len)
691 throw new OverflowException ();
693 command.Tds.ColumnValues.CopyTo (0, values, 0, len);
694 return (len > FieldCount ? len : FieldCount);
697 void IDisposable.Dispose ()
700 GC.SuppressFinalize (this);
703 IEnumerator IEnumerable.GetEnumerator ()
705 return new DbEnumerator (this);
708 public bool IsDBNull (int i)
710 return GetValue (i) == null;
713 public bool NextResult ()
715 if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0)
717 if (command.Tds.DoneProc)
720 schemaTable.Rows.Clear ();
722 moreResults = command.Tds.NextResult ();
732 if ((command.CommandBehavior & CommandBehavior.SingleRow) != 0 && rowsRead > 0)
734 if ((command.CommandBehavior & CommandBehavior.SchemaOnly) != 0)
739 bool result = command.Tds.NextRow ();
746 #endregion // Methods