Ignore dll's
[mono.git] / mcs / class / Mono.Data.MySql / Mono.Data.MySql / MySqlDataReader.cs
1 //
2 // Mono.Data.MySql.MySqlDataReader.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //
8 // (C) Ximian, Inc 2002
9 // (C) Daniel Morgan 2002
10 //
11
12 // *** uncomment #define to get debug messages, comment for production ***
13 //#define DEBUG_MySqlDataReader
14
15 using System;
16 using System.Collections;
17 using System.ComponentModel;
18 using System.Data;
19 using System.Runtime.InteropServices;
20
21 namespace Mono.Data.MySql {
22         /// <summary>
23         /// Provides a means of reading one or more forward-only streams
24         /// of result sets obtained by executing a command 
25         /// at a SQL database.
26         /// </summary>
27         //public sealed class MySqlDataReader : MarshalByRefObject,
28         //      IEnumerable, IDataReader, IDisposable, IDataRecord
29         public sealed class MySqlDataReader : IEnumerable, 
30                 IDataReader, IDataRecord {
31                 
32                 #region Fields
33
34                 private MySqlCommand cmd;
35                 private DataTable table = null;
36
37                 // field meta data
38                 string[] fieldName;
39                 int[] fieldType; // MySQL data type
40                 DbType[] fieldDbType; // DbType translated from MySQL type
41                 uint[] fieldLength;
42                 uint[] fieldMaxLength;
43                 uint[] fieldFlags;
44                 // field data value
45                 private object[] dataValue;
46                                                 
47                 private bool open = false;
48
49                 private int recordsAffected = -1; 
50                 private int currentQuery = 0;
51
52                 private int currentRow = -1;
53                 IntPtr res;
54
55                 private int numFields;
56                 private int numRows;
57
58                 private CommandBehavior cmdBehavior;
59
60                 #endregion // Fields
61
62                 #region Constructors
63
64                 internal MySqlDataReader (MySqlCommand sqlCmd, CommandBehavior behavior) {
65
66                         cmd = sqlCmd;
67                         open = true;
68                         // cmd.OpenReader(this);
69                         cmdBehavior = behavior;
70                 }
71
72                 #endregion // Fields
73
74                 #region Public Methods
75
76                 [MonoTODO]
77                 public void Close() {
78                         open = false;
79                         
80                         // free MySqlDataReader resources in SqlCommand
81                         // and allow SqlConnection to be used again
82                         //cmd.CloseReader();
83
84                         // TODO: get parameters from result
85
86                         // clear unmanaged MySQL result set
87                         if(res != IntPtr.Zero) {
88                                 MySql.FreeResult(res);
89                                 res = IntPtr.Zero;
90                         }
91                 }
92
93                 [MonoTODO]
94                 public DataTable GetSchemaTable() {     
95
96                         DataTable dataTableSchema = null;
97                         // Only Results from SQL SELECT Queries 
98                         // get a DataTable for schema of the result
99                         // otherwise, DataTable is null reference
100                         if(numFields > 0) {
101                                 
102                                 dataTableSchema = new DataTable ();
103                                 
104                                 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
105                                 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
106                                 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
107                                 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
108                                 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
109                                 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
110                                 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
111                                 DataColumn dc = dataTableSchema.Columns["IsKey"];
112                                 dc.AllowDBNull = true; // IsKey can have a DBNull
113                                 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
114                                 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
115                                 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
116                                 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
117                                 dataTableSchema.Columns.Add ("DataType", typeof(Type));
118                                 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
119                                 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
120                                 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
121                                 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
122                                 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
123                                 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
124                                 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
125                                 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
126                                 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
127                                 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
128 \r
129                                 DataRow schemaRow;
130                                 DbType dbType;
131                                 Type typ;
132                                                                 
133                                 for (int i = 0; i < numFields; i += 1 ) {
134                                         
135                                         schemaRow = dataTableSchema.NewRow ();
136                                                                                 
137                                         schemaRow["ColumnName"] = fieldName[i];
138                                         schemaRow["ColumnOrdinal"] = i + 1;
139                                         
140                                         schemaRow["ColumnSize"] = (int) fieldMaxLength[i];
141                                         schemaRow["NumericPrecision"] = 0;
142                                         schemaRow["NumericScale"] = 0;
143                                         // TODO: need to get KeyInfo
144                                         if((cmdBehavior & CommandBehavior.KeyInfo) == CommandBehavior.KeyInfo) {
145                                                 // bool IsUnique, IsKey;
146                                                 // GetKeyInfo(field[i].Name, out IsUnique, out IsKey);
147                                         }
148                                         else {
149                                                 schemaRow["IsUnique"] = false;
150                                                 schemaRow["IsKey"] = DBNull.Value;
151                                         }
152                                         schemaRow["BaseCatalogName"] = "";
153                                         
154                                         schemaRow["BaseColumnName"] = fieldName[i];
155                                         schemaRow["BaseSchemaName"] = "";
156                                         schemaRow["BaseTableName"] = "";
157
158                                         // do translation from MySQL type 
159                                         // to .NET Type and then convert the result
160                                         // to a string
161                                         MySqlEnumFieldTypes fieldEnum;
162                                         
163                                         fieldEnum = (MySqlEnumFieldTypes) fieldType[i];
164                                         dbType = MySqlHelper.MySqlTypeToDbType(fieldEnum);
165                                         typ = MySqlHelper.DbTypeToSystemType (dbType);
166                                         string st = typ.ToString();\r
167                                         schemaRow["DataType"] = typ;\r
168
169                                         schemaRow["AllowDBNull"] = false;
170                                         
171                                         schemaRow["ProviderType"] = (int) fieldType[i];
172                                         schemaRow["IsAliased"] = false;
173                                         schemaRow["IsExpression"] = false;
174                                         schemaRow["IsIdentity"] = false;
175                                         schemaRow["IsAutoIncrement"] = false;
176                                         schemaRow["IsRowVersion"] = false;
177                                         schemaRow["IsHidden"] = false;
178                                         schemaRow["IsLong"] = false;
179                                         schemaRow["IsReadOnly"] = false;
180                                         
181                                         schemaRow.AcceptChanges();
182                                         
183                                         dataTableSchema.Rows.Add (schemaRow);
184                                 }
185                                 
186 #if DEBUG_MySqlCommand
187                                 Console.WriteLine("********** DEBUG Table Schema BEGIN ************");
188                                 foreach (DataRow myRow in dataTableSchema.Rows) {\r
189                                         foreach (DataColumn myCol in dataTableSchema.Columns)\r
190                                                 Console.WriteLine(myCol.ColumnName + " = " + myRow[myCol]);\r
191                                         Console.WriteLine();\r
192                                 }
193                                 Console.WriteLine("********** DEBUG Table Schema END ************");
194 #endif // DEBUG_MySqlCommand
195
196                         }
197                         
198                         return dataTableSchema;
199                 }
200
201                 [MonoTODO]
202                 public bool NextResult() {
203                         currentRow = -1;
204                         bool resultReturned = false;            
205
206                         // reset
207                         table = null;
208                         recordsAffected = -1;
209                         
210                         // store the result set
211                         res = MySql.StoreResult(cmd.Connection.NativeMySqlInitStruct);
212
213                         if(res.Equals(IntPtr.Zero)) {
214                                 // no result set returned
215                                 recordsAffected = (int) MySql.AffectedRows(cmd.Connection.NativeMySqlInitStruct);
216
217                                 numRows = 0;
218                                 numFields = 0;
219                                 fieldName = null;
220                                 fieldType = null;
221                                 fieldLength = null;
222                                 fieldMaxLength = null;
223                                 fieldFlags = null;
224                                 dataValue = null;
225                         }
226                         else {
227                                 dataValue = null;
228
229                                 // get meta data about result set
230                                 numRows = MySql.NumRows(res);
231                                 numFields = MySql.NumFields(res);                                               
232                                 // get meta data about each field
233                                 fieldName = new string[numFields];
234                                 fieldType = new int[numFields];
235                                 fieldLength = new uint[numFields];
236                                 fieldMaxLength = new uint[numFields];
237                                 fieldFlags = new uint[numFields];
238
239                                 fieldDbType = new DbType[numFields];
240                                                                 
241                                 // marshal each meta data field
242                                 // into field* arrays
243                                 for (int i = 0; i < numFields; i++) {
244                                         // marshal field
245                                         MySqlMarshalledField marshField = null;
246                                         marshField = (MySqlMarshalledField) Marshal.PtrToStructure(MySql.FetchField(res), 
247                                                 typeof(MySqlMarshalledField));
248                                         
249                                         // copy memebers in marshalField to fields[i]
250                                         fieldName[i] = marshField.Name;
251                                         fieldType[i] = marshField.FieldType;
252                                         fieldLength[i] = marshField.Length;
253                                         fieldMaxLength[i] = marshField.MaxLength;
254                                         fieldFlags[i] = marshField.Flags;
255
256                                         fieldDbType[i] = MySqlHelper.MySqlTypeToDbType((MySqlEnumFieldTypes)fieldType[i]);
257                                 }
258
259                                 // TODO: for CommandBehavior.SingleRow\r
260                                 //       use IRow, otherwise, IRowset\r
261                                 if(numFields > 0)\r
262                                         if((cmdBehavior & CommandBehavior.SingleRow) == CommandBehavior.SingleRow)\r
263                                                 numFields = 1;\r
264
265                                 // TODO: for CommandBehavior.SchemaInfo
266                                 if((cmdBehavior & CommandBehavior.SchemaOnly) == CommandBehavior.SchemaOnly)
267                                         numFields = 0;
268
269                                 // TODO: for CommandBehavior.SingleResult
270                                 if((cmdBehavior & CommandBehavior.SingleResult) == CommandBehavior.SingleResult)
271                                         if(currentQuery > 0)
272                                                 numFields = 0;
273
274                                 // TODO: for CommandBehavior.SequentialAccess - used for reading Large OBjects
275                                 //if((cmdBehavior & CommandBehavior.SequentialAccess) == CommandBehavior.SequentialAccess) {
276                                 //}
277
278                         }
279                         return resultReturned;
280                 }
281
282                 [MonoTODO]
283                 public bool Read() {            
284                         
285                         if(currentRow < numRows - 1)  {
286                                 
287                                 currentRow++;
288
289                                 dataValue = null;
290
291                                 dataValue = new object[numFields];
292                                                                         
293                                 IntPtr row;
294                                 row = MySql.FetchRow(res);
295                                 if(row.Equals(IntPtr.Zero)) {
296                                         MySql.FreeResult(res);
297                                         res = IntPtr.Zero;
298                                         return false; // EOF
299                                 }
300                                 else {
301                                         for (int i = 0; i < numFields; i++) {
302                                                 // marshal column data value
303                                                 string objValue = cmd.GetColumnData(row, i);
304                                                 
305                                                 // tranlate from native MySql c type
306                                                 // to a .NET type here
307                                                 dataValue[i] = MySqlHelper.ConvertDbTypeToSystem (fieldDbType[i], objValue);
308                                         }
309                                 }
310                                 return true;
311                         }
312                         return false;
313                 }
314
315                 [MonoTODO]
316                 public byte GetByte(int i) {
317                         throw new NotImplementedException ();
318                 }
319
320                 [MonoTODO]
321                 public long GetBytes(int i, long fieldOffset, 
322                         byte[] buffer, int bufferOffset, 
323                         int length) {
324
325                         throw new NotImplementedException ();
326                 }
327
328                 [MonoTODO]
329                 public char GetChar(int i) {
330                         throw new NotImplementedException ();
331                 }
332
333                 [MonoTODO]
334                 public long GetChars(int i, long fieldOffset, 
335                         char[] buffer, int bufferOffset, 
336                         int length) {
337
338                         throw new NotImplementedException ();
339                 }
340
341                 [MonoTODO]
342                 public IDataReader GetData(int i) {
343                         throw new NotImplementedException ();
344                 }
345
346                 [MonoTODO]
347                 public string GetDataTypeName(int i) {
348                         return MySqlHelper.GetMySqlTypeName((MySqlEnumFieldTypes)fieldType[i]);
349                 }
350
351                 [MonoTODO]
352                 public DateTime GetDateTime(int i) {
353                         return (DateTime) dataValue[i];
354                 }
355
356                 [MonoTODO]
357                 public decimal GetDecimal(int i) {
358                         return (decimal) dataValue[i];
359                 }
360
361                 [MonoTODO]
362                 public double GetDouble(int i) {
363                         return (double) dataValue[i];
364                 }
365
366                 [MonoTODO]
367                 public Type GetFieldType(int i) {
368                         MySqlEnumFieldTypes fieldEnum;
369                         DbType dbType;
370                         Type typ;
371
372                         fieldEnum = (MySqlEnumFieldTypes) fieldType[i];         
373                         dbType = MySqlHelper.MySqlTypeToDbType(fieldEnum);
374                         typ = MySqlHelper.DbTypeToSystemType (dbType);
375
376                         return typ;
377                 }
378
379                 [MonoTODO]
380                 public float GetFloat(int i) {
381                         return (float) dataValue[i];
382                 }
383
384                 [MonoTODO]
385                 public Guid GetGuid(int i) {
386                         throw new NotImplementedException ();
387                 }
388
389                 [MonoTODO]
390                 public short GetInt16(int i) {
391                         return (short) dataValue[i];
392                 }
393
394                 [MonoTODO]
395                 public int GetInt32(int i) {
396                         return (int) dataValue[i];
397                 }
398
399                 [MonoTODO]
400                 public long GetInt64(int i) {
401                         return (long) dataValue[i];
402                 }
403
404                 [MonoTODO]
405                 public string GetName(int i) {
406                         return fieldName[i];
407                 }
408
409                 [MonoTODO]
410                 public int GetOrdinal(string name) {
411
412                         int i;
413                         
414                         for(i = 0; i < numFields; i++) {
415                                 if(fieldName[i].Equals(name))
416                                         return i;
417                         }
418
419                         for(i = 0; i < numFields; i++) {
420                                 string ta;
421                                 string n;
422                                                                 
423                                 ta = fieldName[i].ToUpper();
424                                 n = name.ToUpper();
425                                                 
426                                 if(ta.Equals(n)) {
427                                         return i;
428                                 }
429                         }
430                         
431                         throw new MissingFieldException("Missing field: " + name);
432                 }
433
434                 [MonoTODO]
435                 public string GetString(int i) {
436                         return (string) dataValue[i];
437                 }
438
439                 [MonoTODO]
440                 public object GetValue(int i) {
441                         // FIXME: this returns a native type
442                         //        need to return a .NET type
443                         if(MySqlFieldHelper.IsNotNull(fieldFlags[i]) == true)
444                                 return dataValue[i];
445                         else
446                                 return DBNull.Value;
447                 }
448
449                 [MonoTODO]
450                 public int GetValues(object[] values) 
451                 {
452                         Array.Copy (dataValue, values, dataValue.Length);
453                         return dataValue.Length;
454                 }
455
456                 [MonoTODO]
457                 public bool IsDBNull(int i) {
458                         return !MySqlFieldHelper.IsNotNull(fieldFlags[i]);
459                 }
460
461                 [MonoTODO]
462                 public bool GetBoolean(int i) {
463                         return (bool) dataValue[i];
464                 }
465
466                 [MonoTODO]
467                 public IEnumerator GetEnumerator() {
468                         throw new NotImplementedException ();
469                 }
470
471                 #endregion // Public Methods
472
473                 #region Destructors
474
475                 [MonoTODO]
476                 public void Dispose () {
477                 }
478
479                 //[MonoTODO]
480                 //~MySqlDataReader() {
481                 //}
482
483                 #endregion // Destructors
484
485                 #region Properties
486
487                 public int Depth {
488                         [MonoTODO]
489                         get { 
490                                 return 0; // always return zero, unless
491                                           // this provider will allow
492                                           // nesting of a row
493                         }
494                 }
495
496                 public bool IsClosed {
497                         [MonoTODO]
498                         get {
499                                 if(open == false)
500                                         return true;
501                                 else
502                                         return false;
503                         }
504                 }
505
506                 public int RecordsAffected {
507                         [MonoTODO]
508                         get { 
509                                 return recordsAffected;
510                         }
511                 }
512         
513                 public int FieldCount {
514                         [MonoTODO]
515                         get { 
516                                 return numFields;
517                         }
518                 }
519
520                 public object this[string name] {
521                         [MonoTODO]
522                         get { 
523                                 int i;
524                                 
525                                 for(i = 0; i < numFields; i++) {
526                                         if(fieldName[i].Equals(name))
527                                                 return dataValue[i];
528                                 }
529
530                                 for(i = 0; i < numFields; i++) {
531                                         string ta;
532                                         string n;
533                                         
534                                         ta = fieldName[i].ToUpper();
535                                         n = name.ToUpper();
536                                                 
537                                         if(ta.Equals(n)) {
538                                                 return dataValue[i];
539                                         }
540                                 }
541                         
542                                 throw new MissingFieldException("Missing field: " + name);
543                         }
544                 }
545
546                 public object this[int i] {
547                         [MonoTODO]
548                         get { 
549                                 return dataValue[i];
550                         }
551                 }
552
553                 #endregion // Properties
554         }
555 }