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