2002-10-15 Daniel Morgan <danmorg@sc.rr.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
30                 #endregion
31
32                 #region Constructors
33
34                 internal OleDbDataReader (OleDbCommand command, ArrayList results) 
35                 {
36                         this.command = command;
37                         this.command.Connection.DataReader = this;
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                         this.command.Connection.DataReader = null;
137                 }
138
139                 ~OleDbDataReader ()
140                 {
141                         if (open)
142                                 Close ();
143                 }
144
145                 public bool GetBoolean (int ordinal)
146                 {
147                         IntPtr value;
148
149                         if (currentResult == -1)
150                                 throw new InvalidCastException ();
151
152                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
153                                                                     ordinal, currentRow);
154                         if (value == IntPtr.Zero)
155                                 throw new InvalidCastException ();
156                         
157                         if (libgda.gda_value_get_type (value) != GdaValueType.Boolean)
158                                 throw new InvalidCastException ();
159                         return libgda.gda_value_get_boolean (value);
160                 }
161
162                 public byte GetByte (int ordinal)
163                 {
164                         IntPtr value;
165
166                         if (currentResult == -1)
167                                 throw new InvalidCastException ();
168
169                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
170                                                                     ordinal, currentRow);
171                         if (value == IntPtr.Zero)
172                                 throw new InvalidCastException ();
173                         
174                         if (libgda.gda_value_get_type (value) != GdaValueType.Tinyint)
175                                 throw new InvalidCastException ();
176                         return libgda.gda_value_get_tinyint (value);
177                 }
178
179                 [MonoTODO]
180                 public long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
181                 {
182                         throw new NotImplementedException ();
183                 }
184                 
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                                         Console.WriteLine("Error: current result -1");
439                                         return null;
440                                 }
441                                                 
442                                 dataTableSchema = new DataTable ();
443                                 
444                                 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
445                                 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
446                                 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
447                                 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
448                                 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
449                                 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
450                                 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
451                                 DataColumn dc = dataTableSchema.Columns["IsKey"];
452                                 dc.AllowDBNull = true; // IsKey can have a DBNull
453                                 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
454                                 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
455                                 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
456                                 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
457                                 dataTableSchema.Columns.Add ("DataType", typeof(Type));
458                                 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
459                                 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
460                                 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
461                                 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
462                                 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
463                                 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
464                                 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
465                                 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
466                                 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
467                                 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
468 \r
469                                 DataRow schemaRow;
470                                 DbType dbType;
471                                 Type typ;
472                                                                 
473                                 for (int i = 0; i < this.FieldCount; i += 1 ) {
474                                         
475                                         schemaRow = dataTableSchema.NewRow ();
476
477                                         attrs = libgda.gda_data_model_describe_column ((IntPtr) gdaResults[currentResult],
478                                                 i);
479                                         if (attrs == IntPtr.Zero){
480                                                 // FIXME: throw exception
481                                                 Console.WriteLine("Error: attrs null");
482                                                 return null;
483                                         }
484
485                                         gdaType = libgda.gda_field_attributes_get_gdatype (attrs);
486                                         columnSize = libgda.gda_field_attributes_get_defined_size (attrs);
487                                         libgda.gda_field_attributes_free (attrs);
488                                                                                 
489                                         schemaRow["ColumnName"] = this.GetName(i);
490                                         schemaRow["ColumnOrdinal"] = i + 1;
491                                         
492                                         schemaRow["ColumnSize"] = (int) columnSize;
493                                         schemaRow["NumericPrecision"] = 0;
494                                         schemaRow["NumericScale"] = 0;
495                                         // TODO: need to get KeyInfo
496                                         //if((cmdBehavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) {
497                                                 // bool IsUnique, IsKey;
498                                                 // GetKeyInfo(field[i].Name, out IsUnique, out IsKey);
499                                         //}
500                                         //else {
501                                                 schemaRow["IsUnique"] = false;
502                                                 schemaRow["IsKey"] = DBNull.Value;
503                                         //}
504                                         schemaRow["BaseCatalogName"] = "";
505                                         
506                                         schemaRow["BaseColumnName"] = this.GetName(i);
507                                         schemaRow["BaseSchemaName"] = "";
508                                         schemaRow["BaseTableName"] = "";
509
510                                         schemaRow["DataType"] = this.GetFieldType(i);\r
511
512                                         schemaRow["AllowDBNull"] = false;
513                                         
514                                         schemaRow["ProviderType"] = (int) gdaType;
515                                         schemaRow["IsAliased"] = false;
516                                         schemaRow["IsExpression"] = false;
517                                         schemaRow["IsIdentity"] = false;
518                                         schemaRow["IsAutoIncrement"] = false;
519                                         schemaRow["IsRowVersion"] = false;
520                                         schemaRow["IsHidden"] = false;
521                                         schemaRow["IsLong"] = false;
522                                         schemaRow["IsReadOnly"] = false;
523                                         
524                                         schemaRow.AcceptChanges();
525                                         
526                                         dataTableSchema.Rows.Add (schemaRow);
527                                 }
528                                 
529 #if DEBUG_OleDbDataReader
530                                 Console.WriteLine("********** DEBUG Table Schema BEGIN ************");
531                                 foreach (DataRow myRow in dataTableSchema.Rows) {\r
532                                         foreach (DataColumn myCol in dataTableSchema.Columns)\r
533                                                 Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);\r
534                                         Console.WriteLine();\r
535                                 }
536                                 Console.WriteLine("********** DEBUG Table Schema END ************");
537 #endif // DEBUG_OleDbDataReader
538
539                         }
540                         
541                         return dataTableSchema;
542                 }
543
544                 public string GetString (int ordinal)
545                 {
546                         IntPtr value;
547
548                         if (currentResult == -1)
549                                 throw new InvalidCastException ();
550
551                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
552                                                                     ordinal, currentRow);
553                         if (value == IntPtr.Zero)
554                                 throw new InvalidCastException ();
555                         
556                         if (libgda.gda_value_get_type (value) != GdaValueType.String)
557                                 throw new InvalidCastException ();
558                         return libgda.gda_value_get_string (value);
559                 }
560
561                 [MonoTODO]
562                 public TimeSpan GetTimeSpan (int ordinal)
563                 {
564                         throw new NotImplementedException ();
565                 }
566
567                 public object GetValue (int ordinal)
568                 {
569                         IntPtr value;
570                         GdaValueType type;
571
572                         if (currentResult == -1)
573                                 throw new IndexOutOfRangeException ();
574
575                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
576                                                                     ordinal, currentRow);
577                         if (value == IntPtr.Zero)
578                                 throw new IndexOutOfRangeException ();
579
580                         type = libgda.gda_value_get_type (value);
581                         switch (type) {
582                         case GdaValueType.Bigint : return GetInt64 (ordinal);
583                         case GdaValueType.Boolean : return GetBoolean (ordinal);
584                         case GdaValueType.Date : return GetDateTime (ordinal);
585                         case GdaValueType.Double : return GetDouble (ordinal);
586                         case GdaValueType.Integer : return GetInt32 (ordinal);
587                         case GdaValueType.Single : return GetFloat (ordinal);
588                         case GdaValueType.Smallint : return GetByte (ordinal);
589                         case GdaValueType.String : return GetString (ordinal);
590                         case GdaValueType.Time : return GetDateTime (ordinal);
591                         case GdaValueType.Timestamp : return GetDateTime (ordinal);
592                         case GdaValueType.Tinyint : return GetByte (ordinal);
593                         }
594
595                         return (object) libgda.gda_value_stringify (value);
596                 }
597
598                 [MonoTODO]
599                 public int GetValues (object[] values)
600                 {
601                         throw new NotImplementedException ();
602                 }
603
604                 [MonoTODO]
605                 IDataReader IDataRecord.GetData (int ordinal)
606                 {
607                         throw new NotImplementedException ();
608                 }
609
610                 [MonoTODO]
611                 void IDisposable.Dispose ()
612                 {
613                         throw new NotImplementedException ();
614                 }
615
616                 [MonoTODO]
617                 IEnumerator IEnumerable.GetEnumerator ()
618                 {
619                         throw new NotImplementedException ();
620                 }
621
622                 public bool IsDBNull (int ordinal)
623                 {
624                         IntPtr value;
625
626                         if (currentResult == -1)
627                                 throw new IndexOutOfRangeException ();
628
629                         value = libgda.gda_data_model_get_value_at ((IntPtr) gdaResults[currentResult],
630                                                                     ordinal, currentRow);
631                         if (value == IntPtr.Zero)
632                                 throw new IndexOutOfRangeException ();
633
634                         return libgda.gda_value_is_null (value);
635                 }
636
637                 public bool NextResult ()
638                 {
639                         int i = currentResult + 1;
640                         if (i >= 0 && i < gdaResults.Count) {
641                                 currentResult++;
642                                 return true;
643                         }
644
645                         return false;
646                 }
647
648                 public bool Read ()
649                 {
650                         if (currentResult < 0 ||
651                             currentResult >= gdaResults.Count)
652                                 return false;
653
654                         currentRow++;
655                         if (currentRow <
656                             libgda.gda_data_model_get_n_rows ((IntPtr) gdaResults[currentResult]))
657                                 return true;
658
659                         return false;
660                 }
661
662                 #endregion
663         }
664 }