2004-03-10 Umadevi S (sumadevi@novell.com)
[mono.git] / mcs / class / System.Data / System.Data.OleDb / OleDbDataReader.cs
1 //
2 // System.Data.OleDb.OleDbDataReader
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Tim Coleman (tim@timcoleman.com)
7 //
8 // Copyright (C) Rodrigo Moya, 2002
9 // Copyright (C) Tim Coleman, 2002
10 //
11
12 using System.Collections;
13 using System.ComponentModel;
14 using System.Data;
15 using System.Data.Common;
16 using System.Runtime.InteropServices;
17
18 namespace System.Data.OleDb
19 {
20         public sealed class OleDbDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
21         {
22                 #region Fields
23                 
24                 private OleDbCommand command;
25                 private bool open;
26                 private ArrayList gdaResults;
27                 private int currentResult;
28                 private int currentRow;
29                 private bool disposed = false;
30
31                 #endregion
32
33                 #region Constructors
34
35                 internal OleDbDataReader (OleDbCommand command, ArrayList results) 
36                 {
37                         this.command = command;
38                         open = true;
39                         if (results != null)
40                                 gdaResults = results;
41                         else
42                                 gdaResults = new ArrayList ();
43                         currentResult = -1;
44                         currentRow = -1;
45                 }
46
47                 #endregion
48
49                 #region Properties
50
51                 public int Depth {
52                         get {
53                                 return 0; // no nested selects supported
54                         }
55                 }
56
57                 public int FieldCount {
58                         get {
59                                 if (currentResult < 0 ||
60                                     currentResult >= gdaResults.Count)
61                                         return 0;
62
63                                 return libgda.gda_data_model_get_n_columns (
64                                         (IntPtr) gdaResults[currentResult]);
65                         }
66                 }
67
68                 public bool IsClosed {
69                         get {
70                                 return !open;
71                         }
72                 }
73
74                 public object this[string name] {
75                         get {
76                                 int pos;
77
78                                 if (currentResult == -1)
79                                         throw new InvalidOperationException ();
80
81                                 pos = libgda.gda_data_model_get_column_position (
82                                         (IntPtr) gdaResults[currentResult],
83                                         name);
84                                 if (pos == -1)
85                                         throw new IndexOutOfRangeException ();
86
87                                 return this[pos];
88                         }
89                 }
90
91                 public object this[int index] {
92                         get {
93                                 return (object) GetValue (index);
94                         }
95                 }
96
97                 public int RecordsAffected {
98                         get {
99                                 int total_rows;
100                                 
101                                 if (currentResult < 0 ||
102                                     currentResult >= gdaResults.Count)
103                                         return 0;
104
105                                 total_rows = libgda.gda_data_model_get_n_rows (
106                                         (IntPtr) gdaResults[currentResult]);
107                                 if (total_rows > 0) {
108                                         if (FieldCount > 0) {
109                                                 // It's a SELECT statement
110                                                 return -1;
111                                         }
112                                 }
113
114                                 return FieldCount > 0 ? -1 : total_rows;
115                         }
116                 }
117
118                 #endregion
119
120                 #region Methods
121
122                 public void Close ()
123                 {
124                         for (int i = 0; i < gdaResults.Count; i++) {
125                                 IntPtr obj = (IntPtr) gdaResults[i];
126                                 libgda.FreeObject (obj);
127                         }
128
129                         gdaResults.Clear ();
130                         gdaResults = null;
131                         
132                         open = false;
133                         currentResult = -1;
134                         currentRow = -1;
135                 }
136
137                 public bool GetBoolean (int ordinal)
138                 {
139                         IntPtr value;
140
141                         if (currentResult == -1)
142                                 throw new InvalidCastException ();
143
144                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
145                                                                     ordinal, currentRow);
146                         if (value == IntPtr.Zero)
147                                 throw new InvalidCastException ();
148                         
149                         if (libgda.gda_value_get_type (value) != GdaValueType.Boolean)
150                                 throw new InvalidCastException ();
151                         return libgda.gda_value_get_boolean (value);
152                 }
153
154                 public byte GetByte (int ordinal)
155                 {
156                         IntPtr value;
157
158                         if (currentResult == -1)
159                                 throw new InvalidCastException ();
160
161                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
162                                                                     ordinal, currentRow);
163                         if (value == IntPtr.Zero)
164                                 throw new InvalidCastException ();
165                         
166                         if (libgda.gda_value_get_type (value) != GdaValueType.Tinyint)
167                                 throw new InvalidCastException ();
168                         return libgda.gda_value_get_tinyint (value);
169                 }
170
171                 [MonoTODO]
172                 public long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
173                 {
174                         throw new NotImplementedException ();
175                 }
176                 
177                 public char GetChar (int ordinal)
178                 {
179                         IntPtr value;
180
181                         if (currentResult == -1)
182                                 throw new InvalidCastException ();
183
184                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
185                                                                     ordinal, currentRow);
186                         if (value == IntPtr.Zero)
187                                 throw new InvalidCastException ();
188                         
189                         if (libgda.gda_value_get_type (value) != GdaValueType.Tinyint)
190                                 throw new InvalidCastException ();
191                         return (char) libgda.gda_value_get_tinyint (value);
192                 }
193
194                 [MonoTODO]
195                 public long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
196                 {
197                         throw new NotImplementedException ();
198                 }
199
200                 [MonoTODO]
201                 public OleDbDataReader GetData (int ordinal)
202                 {
203                         throw new NotImplementedException ();
204                 }
205
206                 public string GetDataTypeName (int index)
207                 {
208                         IntPtr attrs;
209                         GdaValueType type;
210
211                         if (currentResult == -1)
212                                 return "unknown";
213
214                         
215                         attrs = libgda.gda_data_model_describe_column ((IntPtr) gdaResults[currentResult],
216                                                                        index);
217                         if (attrs == IntPtr.Zero)
218                                 return "unknown";
219
220                         type = libgda.gda_field_attributes_get_gdatype (attrs);
221                         libgda.gda_field_attributes_free (attrs);
222                         
223                         return libgda.gda_type_to_string (type);
224                 }
225
226                 public DateTime GetDateTime (int ordinal)
227                 {
228                         IntPtr value;
229                         DateTime dt;
230
231                         if (currentResult == -1)
232                                 throw new InvalidCastException ();
233
234                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
235                                                                     ordinal, currentRow);
236                         if (value == IntPtr.Zero)
237                                 throw new InvalidCastException ();
238                         
239                         if (libgda.gda_value_get_type (value) == GdaValueType.Date) {
240                                 GdaDate gdt;
241
242                                 gdt = (GdaDate) Marshal.PtrToStructure (libgda.gda_value_get_date (value),
243                                                                         typeof (GdaDate));
244                                 return new DateTime ((int) gdt.year, (int) gdt.month, (int) gdt.day);
245                         } else if (libgda.gda_value_get_type (value) == GdaValueType.Time) {
246                                 GdaTime gdt;
247
248                                 gdt = (GdaTime) Marshal.PtrToStructure (libgda.gda_value_get_time (value),
249                                                                         typeof (GdaTime));
250                                 return new DateTime (0, 0, 0, (int) gdt.hour, (int) gdt.minute, (int) gdt.second, 0);
251                         } else if (libgda.gda_value_get_type (value) == GdaValueType.Timestamp) {
252                                 GdaTimestamp gdt;
253                                 
254                                 gdt = (GdaTimestamp) Marshal.PtrToStructure (libgda.gda_value_get_timestamp (value),
255                                                                              typeof (GdaTimestamp));
256
257                                 return new DateTime ((int) gdt.year, (int) gdt.month, (int) gdt.day,
258                                                      (int) gdt.hour, (int) gdt.minute, (int) gdt.second,
259                                                      (int) gdt.fraction);
260                         }
261
262                         throw new InvalidCastException ();
263                 }
264
265                 [MonoTODO]
266                 public decimal GetDecimal (int ordinal)
267                 {
268                         throw new NotImplementedException ();
269                 }
270
271                 public double GetDouble (int ordinal)
272                 {
273                         IntPtr value;
274
275                         if (currentResult == -1)
276                                 throw new InvalidCastException ();
277
278                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
279                                                                     ordinal, currentRow);
280                         if (value == IntPtr.Zero)
281                                 throw new InvalidCastException ();
282                         
283                         if (libgda.gda_value_get_type (value) != GdaValueType.Double)
284                                 throw new InvalidCastException ();
285                         return libgda.gda_value_get_double (value);
286                 }
287
288                 [MonoTODO]
289                 public Type GetFieldType (int index)
290                 {
291                         IntPtr value;
292                         GdaValueType type;
293
294                         if (currentResult == -1)
295                                 throw new IndexOutOfRangeException ();
296
297                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
298                                 index, currentRow);
299                         if (value == IntPtr.Zero)
300                                 throw new IndexOutOfRangeException ();
301
302                         type = libgda.gda_value_get_type (value);
303                         switch (type) {
304                         case GdaValueType.Bigint : return typeof (long);
305                         case GdaValueType.Boolean : return typeof (bool);
306                         case GdaValueType.Date : return typeof (DateTime);
307                         case GdaValueType.Double : return typeof (double);
308                         case GdaValueType.Integer : return typeof (int);
309                         case GdaValueType.Single : return typeof (float);
310                         case GdaValueType.Smallint : return typeof (byte);
311                         case GdaValueType.String : return typeof (string);
312                         case GdaValueType.Time : return typeof (DateTime);
313                         case GdaValueType.Timestamp : return typeof (DateTime);
314                         case GdaValueType.Tinyint : return typeof (byte);
315                         }
316
317                         return typeof(string); // default
318                 }
319
320                 public float GetFloat (int ordinal)
321                 {
322                         IntPtr value;
323
324                         if (currentResult == -1)
325                                 throw new InvalidCastException ();
326
327                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
328                                                                     ordinal, currentRow);
329                         if (value == IntPtr.Zero)
330                                 throw new InvalidCastException ();
331                         
332                         if (libgda.gda_value_get_type (value) != GdaValueType.Single)
333                                 throw new InvalidCastException ();
334                         return libgda.gda_value_get_single (value);
335                 }
336
337                 [MonoTODO]
338                 public Guid GetGuid (int ordinal)
339                 {
340                         throw new NotImplementedException ();
341                 }
342
343                 public short GetInt16 (int ordinal)
344                 {
345                         IntPtr value;
346
347                         if (currentResult == -1)
348                                 throw new InvalidCastException ();
349
350                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
351                                                                     ordinal, currentRow);
352                         if (value == IntPtr.Zero)
353                                 throw new InvalidCastException ();
354                         
355                         if (libgda.gda_value_get_type (value) != GdaValueType.Smallint)
356                                 throw new InvalidCastException ();
357                         return (short) libgda.gda_value_get_smallint (value);
358                 }
359
360                 public int GetInt32 (int ordinal)
361                 {
362                         IntPtr value;
363
364                         if (currentResult == -1)
365                                 throw new InvalidCastException ();
366
367                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
368                                                                     ordinal, currentRow);
369                         if (value == IntPtr.Zero)
370                                 throw new InvalidCastException ();
371                         
372                         if (libgda.gda_value_get_type (value) != GdaValueType.Integer)
373                                 throw new InvalidCastException ();
374                         return libgda.gda_value_get_integer (value);
375                 }
376
377                 public long GetInt64 (int ordinal)
378                 {
379                         IntPtr value;
380
381                         if (currentResult == -1)
382                                 throw new InvalidCastException ();
383
384                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
385                                                                     ordinal, currentRow);
386                         if (value == IntPtr.Zero)
387                                 throw new InvalidCastException ();
388                         
389                         if (libgda.gda_value_get_type (value) != GdaValueType.Bigint)
390                                 throw new InvalidCastException ();
391                         return libgda.gda_value_get_bigint (value);
392                 }
393
394                 public string GetName (int index)
395                 {
396                         if (currentResult == -1)
397                                 return null;
398
399                         return libgda.gda_data_model_get_column_title (
400                                 (IntPtr) gdaResults[currentResult], index);
401                 }
402
403                 public int GetOrdinal (string name)
404                 {
405                         if (currentResult == -1)
406                                 throw new IndexOutOfRangeException ();
407
408                         for (int i = 0; i < FieldCount; i++) {
409                                 if (GetName (i) == name)
410                                         return i;
411                         }
412
413                         throw new IndexOutOfRangeException ();
414                 }
415
416                 public DataTable GetSchemaTable ()
417                 {
418                         DataTable dataTableSchema = null;
419                         // Only Results from SQL SELECT Queries 
420                         // get a DataTable for schema of the result
421                         // otherwise, DataTable is null reference
422                         if(this.FieldCount > 0) {
423
424                                 IntPtr attrs;
425                                 GdaValueType gdaType;
426                                 long columnSize = 0;
427
428                                 if (currentResult == -1) {
429                                         // FIXME: throw an exception?
430                                         Console.WriteLine("Error: current result -1");
431                                         return null;
432                                 }
433                                                 
434                                 dataTableSchema = new DataTable ();
435                                 
436                                 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
437                                 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
438                                 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
439                                 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
440                                 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
441                                 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
442                                 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
443                                 DataColumn dc = dataTableSchema.Columns["IsKey"];
444                                 dc.AllowDBNull = true; // IsKey can have a DBNull
445                                 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
446                                 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
447                                 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
448                                 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
449                                 dataTableSchema.Columns.Add ("DataType", typeof(Type));
450                                 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
451                                 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
452                                 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
453                                 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
454                                 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
455                                 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
456                                 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
457                                 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
458                                 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
459                                 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
460 \r
461                                 DataRow schemaRow;
462                                 DbType dbType;
463                                 Type typ;
464                                                                 
465                                 for (int i = 0; i < this.FieldCount; i += 1 ) {
466                                         
467                                         schemaRow = dataTableSchema.NewRow ();
468
469                                         attrs = libgda.gda_data_model_describe_column ((IntPtr) gdaResults[currentResult],
470                                                 i);
471                                         if (attrs == IntPtr.Zero){
472                                                 // FIXME: throw exception
473                                                 Console.WriteLine("Error: attrs null");
474                                                 return null;
475                                         }
476
477                                         gdaType = libgda.gda_field_attributes_get_gdatype (attrs);
478                                         columnSize = libgda.gda_field_attributes_get_defined_size (attrs);
479                                         libgda.gda_field_attributes_free (attrs);
480                                                                                 
481                                         schemaRow["ColumnName"] = this.GetName(i);
482                                         schemaRow["ColumnOrdinal"] = i + 1;
483                                         
484                                         schemaRow["ColumnSize"] = (int) columnSize;
485                                         schemaRow["NumericPrecision"] = 0;
486                                         schemaRow["NumericScale"] = 0;
487                                         // TODO: need to get KeyInfo
488                                         //if((cmdBehavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) {
489                                                 // bool IsUnique, IsKey;
490                                                 // GetKeyInfo(field[i].Name, out IsUnique, out IsKey);
491                                         //}
492                                         //else {
493                                                 schemaRow["IsUnique"] = false;
494                                                 schemaRow["IsKey"] = DBNull.Value;
495                                         //}
496                                         schemaRow["BaseCatalogName"] = "";
497                                         
498                                         schemaRow["BaseColumnName"] = this.GetName(i);
499                                         schemaRow["BaseSchemaName"] = "";
500                                         schemaRow["BaseTableName"] = "";
501
502                                         schemaRow["DataType"] = this.GetFieldType(i);\r
503
504                                         schemaRow["AllowDBNull"] = false;
505                                         
506                                         schemaRow["ProviderType"] = (int) gdaType;
507                                         schemaRow["IsAliased"] = false;
508                                         schemaRow["IsExpression"] = false;
509                                         schemaRow["IsIdentity"] = false;
510                                         schemaRow["IsAutoIncrement"] = false;
511                                         schemaRow["IsRowVersion"] = false;
512                                         schemaRow["IsHidden"] = false;
513                                         schemaRow["IsLong"] = false;
514                                         schemaRow["IsReadOnly"] = false;
515                                         
516                                         schemaRow.AcceptChanges();
517                                         
518                                         dataTableSchema.Rows.Add (schemaRow);
519                                 }
520                                 
521 #if DEBUG_OleDbDataReader
522                                 Console.WriteLine("********** DEBUG Table Schema BEGIN ************");
523                                 foreach (DataRow myRow in dataTableSchema.Rows) {\r
524                                         foreach (DataColumn myCol in dataTableSchema.Columns)\r
525                                                 Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);\r
526                                         Console.WriteLine();\r
527                                 }
528                                 Console.WriteLine("********** DEBUG Table Schema END ************");
529 #endif // DEBUG_OleDbDataReader
530
531                         }
532                         
533                         return dataTableSchema;
534                 }
535
536                 public string GetString (int ordinal)
537                 {
538                         IntPtr value;
539
540                         if (currentResult == -1)
541                                 throw new InvalidCastException ();
542
543                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
544                                                                     ordinal, currentRow);
545                         if (value == IntPtr.Zero)
546                                 throw new InvalidCastException ();
547                         
548                         if (libgda.gda_value_get_type (value) != GdaValueType.String)
549                                 throw new InvalidCastException ();
550                         return libgda.gda_value_get_string (value);
551                 }
552
553                 [MonoTODO]
554                 public TimeSpan GetTimeSpan (int ordinal)
555                 {
556                         throw new NotImplementedException ();
557                 }
558
559                 public object GetValue (int ordinal)
560                 {
561                         IntPtr value;
562                         GdaValueType type;
563
564                         if (currentResult == -1)
565                                 throw new IndexOutOfRangeException ();
566
567                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
568                                                                     ordinal, currentRow);
569                         if (value == IntPtr.Zero)
570                                 throw new IndexOutOfRangeException ();
571
572                         type = libgda.gda_value_get_type (value);
573                         switch (type) {
574                         case GdaValueType.Bigint : return GetInt64 (ordinal);
575                         case GdaValueType.Boolean : return GetBoolean (ordinal);
576                         case GdaValueType.Date : return GetDateTime (ordinal);
577                         case GdaValueType.Double : return GetDouble (ordinal);
578                         case GdaValueType.Integer : return GetInt32 (ordinal);
579                         case GdaValueType.Single : return GetFloat (ordinal);
580                         case GdaValueType.Smallint : return GetByte (ordinal);
581                         case GdaValueType.String : return GetString (ordinal);
582                         case GdaValueType.Time : return GetDateTime (ordinal);
583                         case GdaValueType.Timestamp : return GetDateTime (ordinal);
584                         case GdaValueType.Tinyint : return GetByte (ordinal);
585                         }
586
587                         return (object) libgda.gda_value_stringify (value);
588                 }
589
590                 [MonoTODO]
591                 public int GetValues (object[] values)
592                 {
593                         throw new NotImplementedException ();
594                 }
595
596                 [MonoTODO]
597                 IDataReader IDataRecord.GetData (int ordinal)
598                 {
599                         throw new NotImplementedException ();
600                 }
601
602                 IEnumerator IEnumerable.GetEnumerator ()
603                 {
604                         return new DbEnumerator (this);
605                 }
606
607                 public bool IsDBNull (int ordinal)
608                 {
609                         IntPtr value;
610
611                         if (currentResult == -1)
612                                 throw new IndexOutOfRangeException ();
613
614                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
615                                                                     ordinal, currentRow);
616                         if (value == IntPtr.Zero)
617                                 throw new IndexOutOfRangeException ();
618
619                         return libgda.gda_value_is_null (value);
620                 }
621
622                 public bool NextResult ()
623                 {
624                         int i = currentResult + 1;
625                         if (i >= 0 && i < gdaResults.Count) {
626                                 currentResult++;
627                                 return true;
628                         }
629
630                         return false;
631                 }
632
633                 public bool Read ()
634                 {
635                         if (currentResult < 0 ||
636                             currentResult >= gdaResults.Count)
637                                 return false;
638
639                         currentRow++;
640                         if (currentRow <
641                             libgda.gda_data_model_get_n_rows ((IntPtr) gdaResults[currentResult]))
642                                 return true;
643
644                         return false;
645                 }
646
647                 #endregion
648
649                 #region Destructors
650
651                 private void Dispose (bool disposing) {
652                         if (!this.disposed) {
653                                 if (disposing) {
654                                         // release any managed resources
655                                         command = null;
656                                 }
657                                 // release any unmanaged resources
658                                 if (gdaResults != null) {
659                                         gdaResults.Clear ();
660                                         gdaResults = null;
661                                 }
662
663                                 // close any handles
664                                 if (open)
665                                         Close ();
666
667                                 this.disposed = true;   
668                         }
669                 }
670
671                 void IDisposable.Dispose() {
672                         Dispose (true);
673                 }
674
675                 ~OleDbDataReader() {
676                         Dispose (false);
677                 }
678
679                 #endregion // Destructors
680
681         }
682 }