2002-11-04 Tim Coleman <tim@timcoleman.com>
[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 using Mono.Data.TdsClient.Internal;
15 using System;
16 using System.Collections;
17 using System.ComponentModel;
18 using System.Data;
19 using System.Data.Common;
20 using System.Data.SqlTypes;
21
22 namespace System.Data.SqlClient {
23         public sealed class SqlDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
24         {
25                 #region Fields
26
27                 int fieldCount;
28                 bool isClosed;
29                 int recordsAffected;
30                 bool moreResults;
31
32                 int resultsRead;
33                 int rowsRead;
34
35                 SqlCommand command;
36                 DataTable schemaTable;
37
38                 ArrayList dataTypeNames;
39                 ArrayList dataTypes;
40
41                 #endregion // Fields
42
43                 #region Constructors
44
45                 internal SqlDataReader (SqlCommand command)
46                 {
47                         schemaTable = ConstructSchemaTable ();
48                         this.resultsRead = 0;
49                         this.command = command;
50                         this.fieldCount = 0;
51                         this.isClosed = false;
52
53                         NextResult ();
54                 }
55
56                 #endregion
57
58                 #region Properties
59
60                 public int Depth {
61                         get { return 0; }
62                 }
63
64                 public int FieldCount {
65                         get { return fieldCount; }
66                 }
67
68                 public bool IsClosed {
69                         get { return isClosed; }
70                 }
71
72                 public object this [int i] {
73                         get { return GetValue (i); }
74                 }
75
76                 public object this [string name] {
77                         get { return GetValue (GetOrdinal (name)); }
78                 }
79                 
80                 public int RecordsAffected {
81                         get { return recordsAffected; }
82                 }
83
84                 #endregion // Properties
85
86                 #region Methods
87
88                 public void Close()
89                 {
90                         isClosed = true;
91                         command.CloseDataReader (moreResults);
92                 }
93
94                 private static DataTable ConstructSchemaTable ()
95                 {
96                         Type booleanType = Type.GetType ("System.Boolean");
97                         Type stringType = Type.GetType ("System.String");
98                         Type intType = Type.GetType ("System.Int32");
99                         Type typeType = Type.GetType ("System.Type");
100                         Type shortType = Type.GetType ("System.Int16");
101
102                         DataTable schemaTable = new DataTable ("SchemaTable");
103                         schemaTable.Columns.Add ("ColumnName", stringType);
104                         schemaTable.Columns.Add ("ColumnOrdinal", intType);
105                         schemaTable.Columns.Add ("ColumnSize", intType);
106                         schemaTable.Columns.Add ("NumericPrecision", shortType);
107                         schemaTable.Columns.Add ("NumericScale", shortType);
108                         schemaTable.Columns.Add ("IsUnique", booleanType);
109                         schemaTable.Columns.Add ("IsKey", booleanType);
110                         schemaTable.Columns.Add ("BaseServerName", stringType);
111                         schemaTable.Columns.Add ("BaseCatalogName", stringType);
112                         schemaTable.Columns.Add ("BaseColumnName", stringType);
113                         schemaTable.Columns.Add ("BaseSchemaName", stringType);
114                         schemaTable.Columns.Add ("BaseTableName", stringType);
115                         schemaTable.Columns.Add ("DataType", typeType);
116                         schemaTable.Columns.Add ("AllowDBNull", booleanType);
117                         schemaTable.Columns.Add ("ProviderType", intType);
118                         schemaTable.Columns.Add ("IsAliased", booleanType);
119                         schemaTable.Columns.Add ("IsExpression", booleanType);
120                         schemaTable.Columns.Add ("IsIdentity", booleanType);
121                         schemaTable.Columns.Add ("IsAutoIncrement", booleanType);
122                         schemaTable.Columns.Add ("IsRowVersion", booleanType);
123                         schemaTable.Columns.Add ("IsHidden", booleanType);
124                         schemaTable.Columns.Add ("IsLong", booleanType);
125                         schemaTable.Columns.Add ("IsReadOnly", booleanType);
126
127                         return schemaTable;
128                 }
129
130                 public bool GetBoolean (int i)
131                 {
132                         object value = GetValue (i);
133                         if (!(value is bool))
134                                 throw new InvalidCastException ();
135                         return (bool) value;
136                 }
137
138                 public byte GetByte (int i)
139                 {
140                         object value = GetValue (i);
141                         if (!(value is byte))
142                                 throw new InvalidCastException ();
143                         return (byte) value;
144                 }
145
146                 public long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
147                 {
148                         object value = GetValue (i);
149                         if (!(value is byte []))
150                                 throw new InvalidCastException ();
151                         Array.Copy ((byte []) value, (int) dataIndex, buffer, bufferIndex, length);
152                         return ((byte []) value).Length - dataIndex;
153                 }
154
155                 [MonoTODO]
156                 public char GetChar (int i)
157                 {
158                         throw new NotImplementedException ();
159                 }
160
161                 public long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
162                 {
163                         object value = GetValue (i);
164                         if (!(value is char []))
165                                 throw new InvalidCastException ();
166                         Array.Copy ((char []) value, (int) dataIndex, buffer, bufferIndex, length);
167                         return ((char []) value).Length - dataIndex;
168                 }
169
170                 [MonoTODO]
171                 public IDataReader GetData (int i)
172                 {
173                         throw new NotImplementedException ();
174                 }
175
176                 public string GetDataTypeName (int i)
177                 {
178                         return (string) dataTypeNames [i];
179                 }
180
181                 public DateTime GetDateTime (int i)
182                 {
183                         object value = GetValue (i);
184                         if (!(value is DateTime))
185                                 throw new InvalidCastException ();
186                         return (DateTime) value;
187                 }
188
189                 public decimal GetDecimal (int i)
190                 {
191                         object value = GetValue (i);
192                         if (!(value is TdsBigDecimal))
193                                 throw new InvalidCastException ();
194                         int[] bits = ((TdsBigDecimal) value).Data;
195                         if (bits[3] != 0)
196                                 throw new OverflowException ();
197                         byte scale = ((TdsBigDecimal) value).Scale;
198                         bool isNegative = ((TdsBigDecimal) value).IsNegative;
199                         return new Decimal (bits[0], bits[1], bits[2], isNegative, scale);
200                 }
201
202                 private TdsBigDecimal GetDecimalImpl (int i)
203                 {
204                         object value = GetValue (i);
205                         if (!(value is TdsBigDecimal))
206                                 throw new InvalidCastException ();
207                         return (TdsBigDecimal) value;
208                 }
209
210                 public double GetDouble (int i)
211                 {
212                         object value = GetValue (i);
213                         if (!(value is double))
214                                 throw new InvalidCastException ();
215                         return (double) value;
216                 }
217
218                 public Type GetFieldType (int i)
219                 {
220                         return (Type) schemaTable.Rows[i]["DataType"];
221                 }
222
223                 public float GetFloat (int i)
224                 {
225                         object value = GetValue (i);
226                         if (!(value is float))
227                                 throw new InvalidCastException ();
228                         return (float) value;
229                 }
230
231                 [MonoTODO]
232                 public Guid GetGuid (int i)
233                 {
234                         throw new NotImplementedException ();
235                 }
236
237                 public short GetInt16 (int i)
238                 {
239                         object value = GetValue (i);
240                         if (!(value is short))
241                                 throw new InvalidCastException ();
242                         return (short) value;
243                 }
244
245                 public int GetInt32 (int i)
246                 {
247                         object value = GetValue (i);
248                         if (!(value is int))
249                                 throw new InvalidCastException ();
250                         return (int) value;
251                 }
252
253                 public long GetInt64 (int i)
254                 {
255                         object value = GetValue (i);
256                         if (!(value is long))
257                                 throw new InvalidCastException ();
258                         return (long) value;
259                 }
260
261                 public string GetName (int i)
262                 {
263                         return (string) schemaTable.Rows[i]["ColumnName"];
264                 }
265
266                 [MonoTODO]
267                 public int GetOrdinal (string name)
268                 {
269                         foreach (DataRow schemaRow in schemaTable.Rows)
270                                 if (((string) schemaRow ["ColumnName"]).Equals (name))
271                                         return (int) schemaRow ["ColumnOrdinal"];
272                         foreach (DataRow schemaRow in schemaTable.Rows)
273                                 if (String.Compare (((string) schemaRow ["ColumnName"]), name, true) == 0)
274                                         return (int) schemaRow ["ColumnOrdinal"];
275                         throw new IndexOutOfRangeException ();
276                 }
277
278                 public DataTable GetSchemaTable ()
279                 {
280                         if (schemaTable.Rows != null && schemaTable.Rows.Count > 0)
281                                 return schemaTable;
282
283                         if (!moreResults)
284                                 return null;
285
286                         fieldCount = 0;
287
288                         dataTypeNames = new ArrayList ();
289                         dataTypes = new ArrayList ();
290
291                         foreach (TdsSchemaInfo schema in command.Tds.Schema) {
292                                 DataRow row = schemaTable.NewRow ();
293
294
295                                 switch (schema.ColumnType) {
296                                         case TdsColumnType.Image :
297                                                 dataTypeNames.Add ("image");
298                                                 row ["ProviderType"] = (int) SqlDbType.Image;
299                                                 row ["DataType"] = typeof (byte[]);
300                                                 break;
301                                         case TdsColumnType.Text :
302                                                 dataTypes.Add (typeof (string));
303                                                 dataTypeNames.Add ("text");
304                                                 row ["ProviderType"] = (int) SqlDbType.Text;
305                                                 row ["DataType"] = typeof (string);
306                                                 break;
307                                         case TdsColumnType.UniqueIdentifier :
308                                                 dataTypeNames.Add ("uniqueidentifier");
309                                                 row ["ProviderType"] = (int) SqlDbType.UniqueIdentifier;
310                                                 row ["DataType"] = typeof (Guid);
311                                                 break;
312                                         case TdsColumnType.VarBinary :
313                                         case TdsColumnType.BigVarBinary :
314                                                 dataTypeNames.Add ("varbinary");
315                                                 row ["ProviderType"] = (int) SqlDbType.VarBinary;
316                                                 row ["DataType"] = typeof (byte[]);
317                                                 break;
318                                         case TdsColumnType.IntN :
319                                         case TdsColumnType.Int4 :
320                                                 dataTypeNames.Add ("int");
321                                                 row ["ProviderType"] = (int) SqlDbType.Int;
322                                                 row ["DataType"] = typeof (int);
323                                                 break;
324                                         case TdsColumnType.VarChar :
325                                         case TdsColumnType.BigVarChar :
326                                                 dataTypeNames.Add ("varchar");
327                                                 row ["ProviderType"] = (int) SqlDbType.VarChar;
328                                                 row ["DataType"] = typeof (string);
329                                                 break;
330                                         case TdsColumnType.Binary :
331                                         case TdsColumnType.BigBinary :
332                                                 dataTypeNames.Add ("binary");
333                                                 row ["ProviderType"] = (int) SqlDbType.Binary;
334                                                 row ["DataType"] = typeof (byte[]);
335                                                 break;
336                                         case TdsColumnType.Char :
337                                         case TdsColumnType.BigChar :
338                                                 dataTypeNames.Add ("char");
339                                                 row ["ProviderType"] = (int) SqlDbType.Char;
340                                                 row ["DataType"] = typeof (string);
341                                                 break;
342                                         case TdsColumnType.Int1 :
343                                                 dataTypeNames.Add ("tinyint");
344                                                 row ["ProviderType"] = (int) SqlDbType.TinyInt;
345                                                 row ["DataType"] = typeof (byte);
346                                                 break;
347                                         case TdsColumnType.Bit :
348                                         case TdsColumnType.BitN :
349                                                 dataTypeNames.Add ("bit");
350                                                 row ["ProviderType"] = (int) SqlDbType.Bit;
351                                                 row ["DataType"] = typeof (bool);
352                                                 break;
353                                         case TdsColumnType.Int2 :
354                                                 dataTypeNames.Add ("smallint");
355                                                 row ["ProviderType"] = (int) SqlDbType.SmallInt;
356                                                 row ["DataType"] = typeof (short);
357                                                 break;
358                                         case TdsColumnType.DateTime4 :
359                                         case TdsColumnType.DateTime :
360                                         case TdsColumnType.DateTimeN :
361                                                 dataTypeNames.Add ("datetime");
362                                                 row ["ProviderType"] = (int) SqlDbType.DateTime;
363                                                 row ["DataType"] = typeof (DateTime);
364                                                 break;
365                                         case TdsColumnType.Real :
366                                                 dataTypeNames.Add ("real");
367                                                 row ["ProviderType"] = (int) SqlDbType.Real;
368                                                 row ["DataType"] = typeof (float);
369                                                 break;
370                                         case TdsColumnType.Money :
371                                         case TdsColumnType.MoneyN :
372                                         case TdsColumnType.Money4 :
373                                                 dataTypeNames.Add ("money");
374                                                 row ["ProviderType"] = (int) SqlDbType.Money;
375                                                 row ["DataType"] = typeof (decimal);
376                                                 break;
377                                         case TdsColumnType.Float8 :
378                                         case TdsColumnType.FloatN :
379                                                 dataTypeNames.Add ("float");
380                                                 row ["ProviderType"] = (int) SqlDbType.Float;
381                                                 row ["DataType"] = typeof (double);
382                                                 break;
383                                         case TdsColumnType.NText :
384                                                 dataTypeNames.Add ("ntext");
385                                                 row ["ProviderType"] = (int) SqlDbType.NText;
386                                                 row ["DataType"] = typeof (string);
387                                                 break;
388                                         case TdsColumnType.NVarChar :
389                                                 dataTypeNames.Add ("nvarchar");
390                                                 row ["ProviderType"] = (int) SqlDbType.NVarChar;
391                                                 row ["DataType"] = typeof (string);
392                                                 break;
393                                         case TdsColumnType.Decimal :
394                                         case TdsColumnType.Numeric :
395                                                 dataTypeNames.Add ("decimal");
396                                                 row ["ProviderType"] = (int) SqlDbType.Decimal;
397                                                 row ["DataType"] = typeof (decimal);
398                                                 break;
399                                         case TdsColumnType.NChar :
400                                                 dataTypeNames.Add ("nchar");
401                                                 row ["ProviderType"] = (int) SqlDbType.Char;
402                                                 row ["DataType"] = typeof (string);
403                                                 break;
404                                         case TdsColumnType.SmallMoney :
405                                                 dataTypeNames.Add ("smallmoney");
406                                                 row ["ProviderType"] = (int) SqlDbType.SmallMoney;
407                                                 row ["DataType"] = typeof (decimal);
408                                                 break;
409                                         default :
410                                                 dataTypeNames.Add ("variant");
411                                                 row ["ProviderType"] = (int) SqlDbType.Variant;
412                                                 row ["DataType"] = typeof (object);
413                                                 break;
414                                 }
415
416                                 // set default values
417                                 row ["AllowDBNull"] = true;
418                                 row ["BaseCatalogName"] = DBNull.Value;
419                                 row ["BaseColumnName"] = DBNull.Value;
420                                 row ["BaseSchemaName"] = DBNull.Value;
421                                 row ["BaseTableName"] = DBNull.Value;
422                                 row ["ColumnName"] = DBNull.Value;
423                                 row ["IsUnique"] = false;
424                                 row ["NumericPrecision"] = DBNull.Value;
425                                 row ["NumericScale"] = DBNull.Value;
426
427                                 // load schema values
428                                 row ["ColumnOrdinal"] = schema.ColumnOrdinal;
429                                 row ["ColumnSize"] = schema.ColumnSize;
430                                 row ["AllowDBNull"] = schema.AllowDBNull;
431                                 row ["IsExpression"] = schema.IsExpression;
432                                 row ["IsIdentity"] = schema.IsIdentity;
433                                 row ["IsReadOnly"] = schema.IsReadOnly;
434                                 row ["IsKey"] = schema.IsKey;
435
436                                 if (schema.BaseColumnName != null)
437                                         row ["BaseColumnName"] = schema.BaseColumnName;
438
439                                 if (schema.ColumnName != null)
440                                         row ["ColumnName"] = schema.ColumnName;
441
442                                 if (schema.BaseTableName != null)
443                                         row ["BaseTableName"] = schema.BaseTableName;
444
445                                 if (schema.NumericScale != 0)
446                                         row ["NumericPrecision"] = schema.NumericPrecision;
447
448                                 if (schema.NumericScale == 0)
449                                         row ["NumericScale"] = schema.NumericScale;
450
451                                 schemaTable.Rows.Add (row);
452
453                                 fieldCount += 1;
454                         }
455                         return schemaTable;
456                 }               
457
458                 public SqlBinary GetSqlBinary (int i)
459                 {
460                         throw new NotImplementedException ();
461                 }
462
463                 public SqlBoolean GetSqlBoolean (int i) 
464                 {
465                         object value = GetValue (i);
466                         if (value == null)
467                                 return SqlBoolean.Null;
468                         if (!(value is bool))
469                                 throw new InvalidCastException ();
470                         return (bool) value;
471                 }
472
473                 public SqlByte GetSqlByte (int i)
474                 {
475                         object value = GetValue (i);
476                         if (value == null)
477                                 return SqlByte.Null;
478                         if (!(value is byte))
479                                 throw new InvalidCastException ();
480                         return (byte) value;
481                 }
482
483                 public SqlDateTime GetSqlDateTime (int i)
484                 {
485                         object value = GetValue (i);
486                         if (value == null)
487                                 return SqlDateTime.Null;
488                         if (!(value is DateTime))
489                                 throw new InvalidCastException ();
490                         return (DateTime) value;
491                 }
492
493                 public SqlDecimal GetSqlDecimal (int i)
494                 {
495                         object value = GetValue (i);
496                         if (value == null)
497                                 return SqlDecimal.Null;
498                         if (!(value is TdsBigDecimal))
499                                 throw new InvalidCastException ();
500                         return SqlDecimal.FromTdsBigDecimal ((TdsBigDecimal) value);
501                 }
502
503                 public SqlDouble GetSqlDouble (int i)
504                 {
505                         object value = GetValue (i);
506                         if (value == null)
507                                 return SqlDouble.Null;
508                         if (!(value is double))
509                                 throw new InvalidCastException ();
510                         return (double) value;
511                 }
512
513                 public SqlGuid GetSqlGuid (int i)
514                 {
515                         object value = GetValue (i);
516                         if (value == null)
517                                 return SqlGuid.Null;
518                         if (!(value is Guid))
519                                 throw new InvalidCastException ();
520                         return (Guid) value;
521                 }
522
523                 public SqlInt16 GetSqlInt16 (int i)
524                 {
525                         object value = GetValue (i);
526                         if (value == null)
527                                 return SqlInt16.Null;
528                         if (!(value is short))
529                                 throw new InvalidCastException ();
530                         return (short) value;
531                 }
532
533                 public SqlInt32 GetSqlInt32 (int i)
534                 {
535                         object value = GetValue (i);
536                         if (value == null)
537                                 return SqlInt32.Null;
538                         if (!(value is int))
539                                 throw new InvalidCastException ();
540                         return (int) value;
541                 }
542
543                 public SqlInt64 GetSqlInt64 (int i)
544                 {
545                         object value = GetValue (i);
546                         if (value == null)
547                                 return SqlInt64.Null;
548                         if (!(value is long))
549                                 throw new InvalidCastException ();
550                         return (long) value;
551                 }
552
553                 public SqlMoney GetSqlMoney (int i)
554                 {
555                         object value = GetValue (i);
556                         if (value == null)
557                                 return SqlMoney.Null;
558                         if (!(value is TdsBigDecimal))
559                                 throw new InvalidCastException ();
560                         return (SqlMoney) (SqlDecimal) value;
561                 }
562
563                 public SqlSingle GetSqlSingle (int i)
564                 {
565                         object value = GetValue (i);
566                         if (value == null)
567                                 return SqlSingle.Null;
568                         if (!(value is float))
569                                 throw new InvalidCastException ();
570                         return (float) value;
571                 }
572
573                 public SqlString GetSqlString (int i)
574                 {
575                         object value = GetValue (i);
576                         if (value == null)
577                                 return SqlString.Null;
578                         if (!(value is string))
579                                 throw new InvalidCastException ();
580                         return (string) value;
581                 }
582
583                 [MonoTODO]
584                 public object GetSqlValue (int i)
585                 {
586                         object value = GetValue (i);
587                         if (value == null)
588                                 return DBNull.Value; 
589                         throw new NotImplementedException ();
590                 }
591
592                 [MonoTODO]
593                 public int GetSqlValues (object[] values)
594                 {
595                         throw new NotImplementedException ();
596                 }
597
598                 public string GetString (int i)
599                 {
600                         object value = GetValue (i);
601                         if (!(value is string))
602                                 throw new InvalidCastException ();
603                         return (string) value;
604                 }
605
606                 public object GetValue (int i)
607                 {
608                         return command.Tds.ColumnValues[i];
609                 }
610
611                 public int GetValues (object[] values)
612                 {
613                         int len = values.Length;
614                         command.Tds.ColumnValues.CopyTo (0, values, 0, len);
615                         return (len > FieldCount ? len : FieldCount);
616                 }
617
618                 [MonoTODO]
619                 void IDisposable.Dispose ()
620                 {
621                         throw new NotImplementedException ();
622                 }
623
624                 IEnumerator IEnumerable.GetEnumerator ()
625                 {
626                         return new DbEnumerator (this);
627                 }
628
629                 public bool IsDBNull (int i)
630                 {
631                         return GetValue (i) == null;
632                 }
633
634                 public bool NextResult ()
635                 {
636                         if ((command.CommandBehavior & CommandBehavior.SingleResult) != 0 && resultsRead > 0)
637                                 return false;
638                         if (command.Tds.DoneProc)
639                                 return false;
640
641                         schemaTable.Rows.Clear ();
642
643                         moreResults = command.Tds.NextResult ();
644                         rowsRead = 0;
645                         resultsRead += 1;
646                         return moreResults;
647                 }
648
649                 public bool Read ()
650                 {
651                         if ((command.CommandBehavior & CommandBehavior.SingleRow) != 0 && rowsRead > 0)
652                                 return false;
653                         if ((command.CommandBehavior & CommandBehavior.SchemaOnly) != 0)
654                                 return false;
655                         if (!moreResults)
656                                 return false;
657
658                         bool result = command.Tds.NextRow ();
659
660                         rowsRead += 1;
661
662                         return result;
663                 }
664
665                 #endregion // Methods
666         }
667 }