2007-05-04 Nagappan A <anagappan@novell.com>
[mono.git] / mcs / class / System.Data / System.Data.Odbc / OdbcDataReader.cs
1 //
2 // System.Data.Odbc.OdbcDataReader
3 //
4 // Author:
5 //   Brian Ritchie (brianlritchie@hotmail.com) 
6 //   Daniel Morgan <danmorg@sc.rr.com>
7 //   Sureshkumar T <tsureshkumar@novell.com> (2004)
8 //
9 // Copyright (C) Brian Ritchie, 2002
10 // Copyright (C) Daniel Morgan, 2002
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System.Collections;
37 using System.ComponentModel;
38 using System.Data;
39 using System.Data.Common;
40 using System.Text;
41
42 namespace System.Data.Odbc
43 {
44 #if NET_2_0
45         public sealed class OdbcDataReader : DbDataReader
46 #else
47         public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
48 #endif
49         {
50                 #region Fields
51                 
52                 private OdbcCommand command;
53                 private bool open;
54                 private int currentRow;
55                 private OdbcColumn[] cols;
56                 private IntPtr hstmt;
57                 private int _recordsAffected = -1;
58                 bool disposed = false;
59                 private DataTable _dataTableSchema;
60                 private CommandBehavior behavior;
61
62                 #endregion
63
64                 #region Constructors
65
66                 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
67                 {
68                         this.command = command;
69                         this.CommandBehavior = behavior;
70                         open = true;
71                         currentRow = -1;
72                         hstmt = command.hStmt;
73                         // Init columns array;
74                         short colcount = 0;
75                         libodbc.SQLNumResultCols (hstmt, ref colcount);
76                         cols = new OdbcColumn [colcount];
77                         GetSchemaTable ();
78                 }
79
80                 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior,
81                                          int recordAffected) : this (command, behavior)
82                 {
83                         _recordsAffected = recordAffected;
84                 }
85                 
86
87                 #endregion
88
89                 #region Properties
90
91                 private CommandBehavior CommandBehavior 
92                 {
93                         get { return behavior; }
94                         set { value = behavior; }
95                 }
96                 
97                 public
98 #if NET_2_0
99                 override
100 #endif // NET_2_0
101                 int Depth {
102                         get {
103                                 return 0; // no nested selects supported
104                         }
105                 }
106
107                 public
108 #if NET_2_0
109                 override
110 #endif // NET_2_0
111                 int FieldCount {
112                         get {
113                                 return cols.Length;
114                         }
115                 }
116
117                 public
118 #if NET_2_0
119                 override
120 #endif // NET_2_0
121                 bool IsClosed {
122                         get {
123                                 return !open;
124                         }
125                 }
126
127                 public
128 #if NET_2_0
129                 override
130 #endif // NET_2_0
131                 object this[string name] {
132                         get {
133                                 int pos;
134
135                                 if (currentRow == -1)
136                                         throw new InvalidOperationException ();
137
138                                 pos = ColIndex(name);
139                                 
140                                 if (pos == -1)
141                                         throw new IndexOutOfRangeException ();
142
143                                 return this[pos];
144                         }
145                 }
146
147                 public
148 #if NET_2_0
149                 override
150 #endif // NET_2_0
151                 object this [int index] {
152                         get {
153                                 return (object) GetValue (index);
154                         }
155                 }
156
157                 public
158 #if NET_2_0
159                 override
160 #endif // NET_2_0
161                 int RecordsAffected {
162                         get {
163                                 return _recordsAffected;
164                         }
165                 }
166
167                 [MonoTODO]
168                 public
169 #if NET_2_0
170                 override
171 #endif // NET_2_0
172                 bool HasRows {
173                         get { throw new NotImplementedException(); }
174                 }
175
176                 #endregion
177
178                 #region Methods
179                 
180                 private int ColIndex (string colname)
181                 {
182                         int i = 0;
183                         foreach (OdbcColumn col in cols)
184                         {
185                                 if (col != null) {
186                                         if (col.ColumnName == colname)
187                                                 return i;
188                                         if (String.Compare (col.ColumnName, colname, true) == 0)
189                                                 return i;
190                                 }
191                                                 
192                                 i++;
193                         }
194                         return -1;
195                 }
196
197                 // Dynamically load column descriptions as needed.
198                 private OdbcColumn GetColumn (int ordinal)
199                 {
200                         if (cols [ordinal] == null) {
201                                 short bufsize = 255;
202                                 byte [] colname_buffer = new byte [bufsize];
203                                 string colname;
204                                 short colname_size = 0;
205                                 uint ColSize = 0;
206                                 short DecDigits = 0, Nullable = 0, dt = 0;
207                                 OdbcReturn ret = libodbc.SQLDescribeCol (hstmt, Convert.ToUInt16 (ordinal + 1), 
208                                                                          colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize, 
209                                                                          ref DecDigits, ref Nullable);
210                                 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
211                                         throw new OdbcException (new OdbcError ("SQLDescribeCol", OdbcHandleType.Stmt, hstmt));
212                                 colname = System.Text.Encoding.Default.GetString (colname_buffer);
213                                 colname = colname.Replace ((char) 0, ' ').Trim ();
214                                 OdbcColumn c = new OdbcColumn (colname, (SQL_TYPE) dt);
215                                 c.AllowDBNull = (Nullable != 0);
216                                 c.Digits = DecDigits;
217                                 if (c.IsVariableSizeType)
218                                         c.MaxLength = (int) ColSize;
219                                 cols [ordinal] = c;
220                         }
221                         return cols [ordinal];
222                 }
223
224                 public
225 #if NET_2_0
226                 override
227 #endif // NET_2_0
228                 void Close ()
229                 {
230                         // FIXME : have to implement output parameter binding
231                         open = false;
232                         currentRow = -1;
233
234                         this.command.FreeIfNotPrepared ();
235
236                         if ((this.CommandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) {
237                                 this.command.Connection.Close ();
238                         }
239                 }
240
241                 ~OdbcDataReader ()
242                 {
243                         this.Dispose (false);
244                 }
245
246                 public 
247 #if NET_2_0
248                 override
249 #endif // NET_2_0
250                 bool GetBoolean (int ordinal)
251                 {
252                         return (bool) GetValue(ordinal);
253                 }
254
255                 public 
256 #if NET_2_0
257                 override
258 #endif // NET_2_0
259                 byte GetByte (int ordinal)
260                 {
261                         return (byte) Convert.ToByte(GetValue(ordinal));
262                 }
263
264                 public 
265 #if NET_2_0
266                 override
267 #endif // NET_2_0
268                 long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
269                 {
270                         OdbcReturn ret = OdbcReturn.Error;
271                         bool copyBuffer = false;
272                         int returnVal = 0, outsize = 0;
273                         byte [] tbuff = new byte [length+1];
274
275                         length = buffer == null ? 0 : length;
276                         ret=libodbc.SQLGetData (hstmt, (ushort) (ordinal+1), SQL_C_TYPE.BINARY, tbuff, length, 
277                                                 ref outsize);
278
279                         if (ret == OdbcReturn.NoData)
280                                 return 0;
281
282                         if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo)) 
283                                 throw new OdbcException (new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt));
284
285                         OdbcError odbcErr = null;
286                         if ( (ret == OdbcReturn.SuccessWithInfo))
287                                 odbcErr = new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt);
288
289                         if (buffer == null)
290                                 return outsize; //if buffer is null,return length of the field
291                         
292                         if (ret == OdbcReturn.SuccessWithInfo) {
293                                 if (outsize == (int) OdbcLengthIndicator.NoTotal)
294                                         copyBuffer = true;
295                                 else if (outsize == (int) OdbcLengthIndicator.NullData) {
296                                         copyBuffer = false;
297                                         returnVal = -1;
298                                 } else {
299                                         string sqlstate = odbcErr.SQLState;
300                                         //SQLState: String Data, Right truncated
301                                         if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC) 
302                                                 throw new OdbcException ( odbcErr);
303                                         copyBuffer = true;
304                                 }
305                         } else {
306                                 copyBuffer = outsize == -1 ? false : true;
307                                 returnVal = outsize;
308                         }
309
310                         if (copyBuffer) {
311                                 int i = 0;
312                                 while (tbuff [i] != libodbc.C_NULL) {
313                                         buffer [bufferIndex + i] = tbuff [i];
314                                         i++;
315                                 }
316                                 returnVal = i;
317                         }
318                         return returnVal;
319                 }
320                 
321                 [MonoTODO]
322                 public 
323 #if NET_2_0
324                 override
325 #endif // NET_2_0
326                 char GetChar (int ordinal)
327                 {
328                         throw new NotImplementedException ();
329                 }
330
331                 [MonoTODO]
332                 public 
333 #if NET_2_0
334                 override
335 #endif // NET_2_0
336                 long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
337                 {
338                         throw new NotImplementedException ();
339                 }
340
341                 [MonoTODO]
342                 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
343                 public 
344 #if NET_2_0
345                 new
346 #endif // NET_2_0
347                 IDataReader GetData (int ordinal)
348                 {
349                         throw new NotImplementedException ();
350                 }
351
352                 public 
353 #if NET_2_0
354                 override
355 #endif // NET_2_0
356                 string GetDataTypeName (int index)
357                 {
358                         return GetColumn (index).OdbcType.ToString ();
359                 }
360
361                 public DateTime GetDate (int ordinal) {
362                         return GetDateTime (ordinal);
363                 }
364
365                 public 
366 #if NET_2_0
367                 override
368 #endif // NET_2_0
369                 DateTime GetDateTime (int ordinal)
370                 {
371                         return (DateTime) GetValue (ordinal);
372                 }
373
374                 public 
375 #if NET_2_0
376                 override
377 #endif // NET_2_0
378                 decimal GetDecimal (int ordinal)
379                 {
380                         return (decimal) GetValue (ordinal);
381                 }
382
383                 public 
384 #if NET_2_0
385                 override
386 #endif // NET_2_0
387                 double GetDouble (int ordinal)
388                 {
389                         return (double) GetValue (ordinal);
390                 }
391
392                 public 
393 #if NET_2_0
394                 override
395 #endif // NET_2_0
396                 Type GetFieldType (int index)
397                 {
398                         return GetColumn(index).DataType;
399                 }
400
401                 public 
402 #if NET_2_0
403                 override
404 #endif // NET_2_0
405                 float GetFloat (int ordinal)
406                 {
407                         return (float) GetValue (ordinal);
408                 }
409
410                 [MonoTODO]
411                 public 
412 #if NET_2_0
413                 override
414 #endif // NET_2_0
415                 Guid GetGuid (int ordinal)
416                 {
417                         throw new NotImplementedException ();
418                 }
419
420                 public 
421 #if NET_2_0
422                 override
423 #endif // NET_2_0
424                 short GetInt16 (int ordinal)
425                 {
426                         return (short) GetValue (ordinal);
427                 }
428
429                 public 
430 #if NET_2_0
431                 override
432 #endif // NET_2_0
433                 int GetInt32 (int ordinal)
434                 {
435                         return (int) GetValue (ordinal);
436                 }
437
438                 public 
439 #if NET_2_0
440                 override
441 #endif // NET_2_0
442                 long GetInt64 (int ordinal)
443                 {
444                         return (long) GetValue (ordinal);
445                 }
446
447                 public 
448 #if NET_2_0
449                 override
450 #endif // NET_2_0
451                 string GetName (int index)
452                 {
453                         return GetColumn(index).ColumnName;
454                 }
455
456                 public 
457 #if NET_2_0
458                 override
459 #endif // NET_2_0
460                 int GetOrdinal (string name)
461                 {
462                         int i=ColIndex(name);
463
464                         if (i==-1)
465                                 throw new IndexOutOfRangeException ();
466                         else
467                                 return i;
468                 }
469
470                 [MonoTODO]
471                 public
472 #if NET_2_0
473                 override
474 #endif // NET_2_0
475                 DataTable GetSchemaTable() 
476                 {
477                         // FIXME : 
478                         // * Map OdbcType to System.Type and assign to DataType.
479                         //   This will eliminate the need for IsStringType in
480                         //   OdbcColumn.
481
482                         if (_dataTableSchema != null)
483                                 return _dataTableSchema;
484                         
485                         DataTable dataTableSchema = null;
486                         // Only Results from SQL SELECT Queries 
487                         // get a DataTable for schema of the result
488                         // otherwise, DataTable is null reference
489                         if(cols.Length > 0) 
490                         {
491                                 dataTableSchema = new DataTable ();
492                                 
493                                 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
494                                 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
495                                 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
496                                 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
497                                 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
498                                 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
499                                 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
500                                 DataColumn dc = dataTableSchema.Columns["IsKey"];
501                                 dc.AllowDBNull = true; // IsKey can have a DBNull
502                                 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
503                                 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
504                                 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
505                                 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
506                                 dataTableSchema.Columns.Add ("DataType", typeof(Type));
507                                 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
508                                 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
509                                 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
510                                 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
511                                 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
512                                 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
513                                 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
514                                 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
515                                 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
516                                 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
517
518                                 DataRow schemaRow;
519
520                                 for (int i = 0; i < cols.Length; i += 1 ) 
521                                 {
522                                         string baseTableName = String.Empty;
523                                         bool isKey = false;
524                                         OdbcColumn col=GetColumn(i);
525
526                                         schemaRow = dataTableSchema.NewRow ();
527                                         dataTableSchema.Rows.Add (schemaRow);
528                                                                                 
529                                         schemaRow ["ColumnName"]        = col.ColumnName;
530                                         schemaRow ["ColumnOrdinal"]     = i;
531                                         schemaRow ["ColumnSize"]        = col.MaxLength;
532                                         schemaRow ["NumericPrecision"]  = GetColumnAttribute (i+1, FieldIdentifier.Precision);
533                                         schemaRow ["NumericScale"]      = GetColumnAttribute (i+1, FieldIdentifier.Scale);
534                                         schemaRow ["BaseTableName"]     = GetColumnAttributeStr (i+1, FieldIdentifier.TableName);
535                                         schemaRow ["BaseSchemaName"]    = GetColumnAttributeStr (i+1, FieldIdentifier.SchemaName);
536                                         schemaRow ["BaseCatalogName"]   = GetColumnAttributeStr (i+1, FieldIdentifier.CatelogName);
537                                         schemaRow ["BaseColumnName"]    = GetColumnAttributeStr (i+1, FieldIdentifier.BaseColumnName);
538                                         schemaRow ["DataType"]          = col.DataType;
539                                         schemaRow ["IsUnique"]          = false;
540                                         schemaRow ["IsKey"]             = DBNull.Value;
541                                         schemaRow ["AllowDBNull"]       = GetColumnAttribute (i+1, FieldIdentifier.Nullable) != libodbc.SQL_NO_NULLS;
542                                         schemaRow ["ProviderType"]      = (int) col.OdbcType;
543                                         schemaRow ["IsAutoIncrement"]   = GetColumnAttribute (i+1, FieldIdentifier.AutoUniqueValue) == libodbc.SQL_TRUE;
544                                         schemaRow ["IsExpression"]      = schemaRow.IsNull ("BaseTableName") || (string) schemaRow ["BaseTableName"] == String.Empty;
545                                         schemaRow ["IsAliased"]         = (string) schemaRow ["BaseColumnName"] != (string) schemaRow ["ColumnName"];
546                                         schemaRow ["IsReadOnly"]        = ((bool) schemaRow ["IsExpression"]
547                                                                            || GetColumnAttribute (i+1, FieldIdentifier.Updatable) == libodbc.SQL_ATTR_READONLY);
548
549                                         // FIXME: all of these
550                                         schemaRow ["IsIdentity"]        = false;
551                                         schemaRow ["IsRowVersion"]      = false;
552                                         schemaRow ["IsHidden"]          = false;
553                                         schemaRow ["IsLong"]            = false;
554
555                                         
556                                         // FIXME: according to Brian, 
557                                         // this does not work on MS .NET
558                                         // however, we need it for Mono 
559                                         // for now
560                                         // schemaRow.AcceptChanges();
561                                         
562                                 }
563
564                                 // set primary keys
565                                 DataRow [] rows = dataTableSchema.Select ("BaseTableName <> ''",
566                                                                           "BaseCatalogName, BaseSchemaName, BaseTableName ASC");
567
568                                 string lastTableName = String.Empty,
569                                         lastSchemaName = String.Empty,
570                                         lastCatalogName = String.Empty;
571                                 string [] keys = null; // assumed to be sorted.
572                                 foreach (DataRow row in rows) {
573                                         string tableName = (string) row ["BaseTableName"];
574                                         string schemaName = (string) row ["BaseSchemaName"];
575                                         string catalogName = (string) row ["BaseCatalogName"];
576
577                                         if (tableName != lastTableName || schemaName != lastSchemaName
578                                             || catalogName != lastCatalogName)
579                                                 keys = GetPrimaryKeys (catalogName, schemaName, tableName);
580                                 
581                                         if (keys != null &&
582                                             Array.BinarySearch (keys, (string) row ["BaseColumnName"]) >= 0) {
583                                                 row ["IsKey"] = true;
584                                                 row ["IsUnique"] = true;
585                                                 row ["AllowDBNull"] = false;
586                                                 GetColumn ( ColIndex ( (string) row ["ColumnName"])).AllowDBNull = false;
587                                         }
588                                         lastTableName = tableName;
589                                         lastSchemaName = schemaName;
590                                         lastCatalogName = catalogName;
591                                 }
592                                 dataTableSchema.AcceptChanges();
593                         }
594                         return (_dataTableSchema = dataTableSchema);
595                 }
596
597                 public 
598 #if NET_2_0
599                 override
600 #endif // NET_2_0
601                 string GetString (int ordinal)
602                 {
603                         return (string) GetValue (ordinal);
604                 }
605
606                 [MonoTODO]
607                 public TimeSpan GetTime (int ordinal)
608                 {
609                         throw new NotImplementedException ();
610                 }
611
612                 public 
613 #if NET_2_0
614                 override
615 #endif // NET_2_0
616                 object GetValue (int ordinal)
617                 {
618                         if (currentRow == -1)
619                                 throw new IndexOutOfRangeException ();
620
621                         if (ordinal > cols.Length-1 || ordinal < 0)
622                                 throw new IndexOutOfRangeException ();
623
624                         OdbcReturn ret;
625                         int outsize = 0, bufsize;
626                         byte[] buffer;
627                         OdbcColumn col = GetColumn (ordinal);
628                         object DataValue = null;
629                         ushort ColIndex = Convert.ToUInt16 (ordinal+1);
630
631                         // Check cached values
632                         if (col.Value == null) {
633                                 // odbc help file
634                                 // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
635                                 switch (col.OdbcType) {
636                                 case OdbcType.Bit:
637                                         short bit_data = 0;
638                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref bit_data, 0, ref outsize);
639                                         if (outsize != (int) OdbcLengthIndicator.NullData)
640                                                 DataValue = bit_data == 0 ? "False" : "True";
641                                         break;
642                                 case OdbcType.Numeric:
643                                 case OdbcType.Decimal:
644                                         bufsize = 50;
645                                         buffer = new byte [bufsize];  // According to sqlext.h, use SQL_CHAR for decimal.
646                                         // FIXME : use Numeric.
647                                         ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
648                                         if (outsize!=-1) {
649                                                 byte [] temp = new byte [outsize];
650                                                 for (int i = 0;i<outsize;i++)
651                                                         temp[i] = buffer[i];
652                                                 DataValue = Decimal.Parse(System.Text.Encoding.Default.GetString(temp));
653                                         }
654                                         break;
655                                 case OdbcType.TinyInt:
656                                         short short_data = 0;
657                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref short_data, 0, ref outsize);
658                                         DataValue = System.Convert.ToByte(short_data);
659                                         break;
660                                 case OdbcType.Int:
661                                         int int_data = 0;
662                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref int_data, 0, ref outsize);
663                                         DataValue = int_data;
664                                         break;
665
666                                 case OdbcType.SmallInt:
667                                         short sint_data = 0;
668                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref sint_data, 0, ref outsize);
669                                         DataValue = sint_data;
670                                         break;
671
672                                 case OdbcType.BigInt:
673                                         long long_data = 0;
674                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
675                                         DataValue = long_data;
676                                         break;
677                                 case OdbcType.NText:
678                                 case OdbcType.NVarChar:
679                                         bufsize = (col.MaxLength < 127 ? (col.MaxLength*2+1) : 255);
680                                         buffer = new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for both char and varchar
681                                         StringBuilder sb = new StringBuilder ();
682                                         do { 
683                                                 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
684                                                 if (ret == OdbcReturn.Error)
685                                                         break;
686                                                 if (ret != OdbcReturn.NoData && outsize!=-1) {
687                                                         if (outsize < bufsize)
688                                                                 sb.Append (System.Text.Encoding.Unicode.GetString(buffer,0,outsize));
689                                                         else
690                                                                 sb.Append (System.Text.Encoding.Unicode.GetString(buffer,0,bufsize));
691                                                 }
692                                         } while (ret != OdbcReturn.NoData);
693                                         DataValue = sb.ToString ();
694                                         break;
695                                 case OdbcType.Text:
696                                 case OdbcType.VarChar:
697                                         bufsize = (col.MaxLength < 255 ? (col.MaxLength+1) : 255);
698                                         buffer = new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for both char and varchar
699                                         StringBuilder sb1 = new StringBuilder ();
700                                         do { 
701                                                 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
702                                                 if (ret == OdbcReturn.Error)
703                                                         break;
704                                                 if (ret != OdbcReturn.NoData && outsize!=-1) {
705                                                         if (outsize < bufsize)
706                                                                 sb1.Append (System.Text.Encoding.Default.GetString(buffer,0,outsize));
707                                                         else
708                                                                 sb1.Append (System.Text.Encoding.Default.GetString(buffer,0,bufsize));
709                                                 }
710                                         } while (ret != OdbcReturn.NoData);
711                                         DataValue = sb1.ToString ();
712                                         break;
713                                 case OdbcType.Real:
714                                         float float_data = 0;
715                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref float_data, 0, ref outsize);
716                                         DataValue = float_data;
717                                         break;
718                                 case OdbcType.Double:
719                                         double double_data = 0;
720                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref double_data, 0, ref outsize);
721                                         DataValue = double_data;
722                                         break;
723                                 case OdbcType.Timestamp:
724                                 case OdbcType.DateTime:
725                                 case OdbcType.Date:
726                                 case OdbcType.Time:
727                                         OdbcTimestamp ts_data = new OdbcTimestamp();
728                                         ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref ts_data, 0, ref outsize);
729                                         if (outsize!=-1) // This means SQL_NULL_DATA 
730                                                 DataValue = new DateTime(ts_data.year,ts_data.month,ts_data.day,ts_data.hour,
731                                                                        ts_data.minute,ts_data.second,Convert.ToInt32(ts_data.fraction));
732                                         break;
733                                 case OdbcType.VarBinary :
734                                 case OdbcType.Image :
735                                         bufsize =  (col.MaxLength < 255 ? col.MaxLength : 255);
736                                         buffer= new byte [bufsize];
737                                         ArrayList al = new ArrayList ();
738                                         do { 
739                                                 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, bufsize, ref outsize);
740                                                 if (ret == OdbcReturn.Error)
741                                                         break;
742                                                 if (ret != OdbcReturn.NoData && outsize!=-1) {
743                                                         if (outsize < bufsize) {
744                                                                 byte[] tmparr = new byte [outsize];
745                                                                 Array.Copy (buffer, 0, tmparr, 0, outsize);
746                                                                 al.AddRange (tmparr);
747                                                         } else
748                                                                 al.AddRange (buffer);
749                                                 }
750                                         } while (ret != OdbcReturn.NoData);
751                                         DataValue = al.ToArray (typeof (byte));
752                                         break;
753                                 case OdbcType.Binary :
754                                         bufsize = col.MaxLength;
755                                         buffer = new byte [bufsize];
756                                         long read = GetBytes (ordinal, 0, buffer, 0, bufsize);
757                                         ret = OdbcReturn.Success;
758                                         DataValue = buffer;
759                                         break;
760                                 default:
761                                         bufsize = 255;
762                                         buffer = new byte[bufsize];
763                                         ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
764                                         if (outsize != (int) OdbcLengthIndicator.NullData)
765                                                 if (! (ret == OdbcReturn.SuccessWithInfo
766                                                        && outsize == (int) OdbcLengthIndicator.NoTotal))
767                                                         DataValue = System.Text.Encoding.Default.GetString(buffer, 0, outsize);
768                                         break;
769                                 }
770
771                                 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo) && (ret!=OdbcReturn.NoData)) 
772                                         throw new OdbcException(new OdbcError("SQLGetData",OdbcHandleType.Stmt,hstmt));
773
774                                 if (outsize==-1) // This means SQL_NULL_DATA 
775                                         col.Value = DBNull.Value;
776                                 else
777                                         col.Value = DataValue;
778                         }
779                         return col.Value;
780                 }
781                 
782                 public 
783 #if NET_2_0
784                 override
785 #endif // NET_2_0
786                 int GetValues (object[] values)
787                 {
788                         int numValues = 0;
789
790                         // copy values
791                         for (int i = 0; i < values.Length; i++) {
792                                 if (i < FieldCount) {
793                                         values[i] = GetValue (i);
794                                 }
795                                 else {
796                                         values[i] = null;
797                                 }
798                         }
799
800                         // get number of object instances in array
801                         if (values.Length < FieldCount)
802                                 numValues = values.Length;
803                         else if (values.Length == FieldCount)
804                                 numValues = FieldCount;
805                         else
806                                 numValues = FieldCount;
807
808                         return numValues;
809                 }
810
811 #if ONLY_1_1
812
813                 void IDisposable.Dispose ()
814                 {
815                         Dispose (true);
816                         GC.SuppressFinalize (this);
817                 }
818
819                 IEnumerator IEnumerable.GetEnumerator ()
820                 {
821                         return new DbEnumerator (this);
822                 }
823 #endif // ONLY_1_1
824
825                 
826 #if NET_2_0
827                 public override IEnumerator GetEnumerator ()
828                 {
829                         return new DbEnumerator (this);
830                 }
831 #endif
832
833 #if NET_2_0
834                 protected override
835 #endif
836                 void Dispose (bool disposing)
837                 {
838                         if (disposed)
839                                 return;
840
841                         if (disposing) {
842                                 // dispose managed resources
843                                 Close ();
844                         }
845
846                         command = null;
847                         cols = null;
848                         _dataTableSchema = null;
849                         disposed = true;
850                 }
851
852                 public
853 #if NET_2_0
854                 override
855 #endif // NET_2_0
856                 bool IsDBNull (int ordinal)
857                 {
858                         return (GetValue (ordinal) is DBNull);
859                 }
860
861                 /// <remarks>
862                 ///     Move to the next result set.
863                 /// </remarks>
864                 public
865 #if NET_2_0
866                 override
867 #endif // NET_2_0
868                 bool NextResult ()
869                 {
870                         OdbcReturn ret = OdbcReturn.Success;
871                         ret = libodbc.SQLMoreResults (hstmt);
872                         if (ret == OdbcReturn.Success) {
873                                 short colcount = 0;
874                                 libodbc.SQLNumResultCols (hstmt, ref colcount);
875                                 cols = new OdbcColumn [colcount];
876                                 _dataTableSchema = null; // force fresh creation
877                                 GetSchemaTable ();
878                         }       
879                         return (ret==OdbcReturn.Success);
880                 }
881
882                 /// <remarks>
883                 ///     Load the next row in the current result set.
884                 /// </remarks>
885                 private bool NextRow ()
886                 {
887                         OdbcReturn ret=libodbc.SQLFetch (hstmt);
888                         if (ret != OdbcReturn.Success)
889                                 currentRow = -1;
890                         else
891                                 currentRow++;
892
893                         // Clear cached values from last record
894                         foreach (OdbcColumn col in cols)
895                         {
896                                 if (col != null)
897                                         col.Value = null;
898                         }
899                         return (ret == OdbcReturn.Success);
900                 }
901
902
903                 private int GetColumnAttribute (int column, FieldIdentifier fieldId)
904                 {
905                         OdbcReturn ret = OdbcReturn.Error;
906                         byte [] buffer = new byte [255];
907                         short outsize = 0;
908                         int val = 0;
909                         ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId, 
910                                                        buffer, (short)buffer.Length, 
911                                                        ref outsize, ref val);
912                         if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
913                                 throw new OdbcException (new OdbcError ("SQLColAttribute",
914                                                                         OdbcHandleType.Stmt,
915                                                                         hstmt)
916                                                          );
917                         return val;
918                         
919                 }
920
921                 private string GetColumnAttributeStr (int column, FieldIdentifier fieldId)
922                 {
923                         OdbcReturn ret = OdbcReturn.Error;
924                         byte [] buffer = new byte [255];
925                         short outsize = 0;
926                         int val = 0;
927                         ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId, 
928                                                        buffer, (short)buffer.Length, 
929                                                        ref outsize, ref val);
930                         if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
931                                 throw new OdbcException (new OdbcError ("SQLColAttribute",
932                                                                         OdbcHandleType.Stmt,
933                                                                         hstmt)
934                                                          );
935                         string value = "";
936                         if (outsize > 0)
937                                 value = Encoding.Default.GetString (buffer, 0, outsize);
938                         return value;
939                 }
940
941                 private string [] GetPrimaryKeys (string catalog, string schema, string table)
942                 {
943                         if (cols.Length <= 0)
944                                 return new string [0];
945
946                         ArrayList keys = null;
947                         try {
948                                 keys = GetPrimaryKeysBySQLPrimaryKey (catalog, schema, table);
949                         } catch (OdbcException){
950                                 try {
951                                         keys = GetPrimaryKeysBySQLStatistics (catalog, schema, table);
952                                 } catch (OdbcException) {
953                                 }
954                         }
955                         keys.Sort ();
956                         return (string []) keys.ToArray (typeof (string));
957                 }
958
959                 private ArrayList GetPrimaryKeysBySQLPrimaryKey (string catalog, string schema, string table)
960                 {
961                         ArrayList keys = new ArrayList ();
962                         IntPtr handle = IntPtr.Zero;
963                         OdbcReturn ret;
964                         try {
965                                 ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, 
966                                                            command.Connection.hDbc, ref handle);
967                                 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
968                                         throw new OdbcException(new OdbcError("SQLAllocHandle",
969                                                                               OdbcHandleType.Dbc,
970                                                                               command.Connection.hDbc));
971
972                                 ret = libodbc.SQLPrimaryKeys (handle, catalog, -3,  
973                                                               schema, -3, 
974                                                               table, -3);
975                                 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
976                                         throw new OdbcException (new OdbcError ("SQLPrimaryKeys", OdbcHandleType.Stmt, handle));
977                         
978                                 int length = 0;
979                                 byte [] primaryKey = new byte [255];
980                         
981                                 ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.CHAR, primaryKey, primaryKey.Length, ref length);
982                                 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
983                                         throw new OdbcException (new OdbcError ("SQLBindCol", OdbcHandleType.Stmt, handle));
984
985                                 int i = 0;                              
986                                 while (true) {
987                                         ret = libodbc.SQLFetch (handle);
988                                         if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
989                                                 break;
990                                         string pkey = Encoding.Default.GetString (primaryKey, 0, length);
991                                         keys.Add (pkey);
992                                 }
993                         } finally {
994                                 if (handle != IntPtr.Zero) {
995                                         ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
996                                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
997                                                 throw new OdbcException(new OdbcError("SQLFreeStmt",OdbcHandleType.Stmt,handle));
998                                         
999                                         ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
1000                                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
1001                                                 throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,handle));
1002                                 }
1003                         }
1004                         return keys;
1005                 }
1006                 
1007                 private unsafe ArrayList GetPrimaryKeysBySQLStatistics (string catalog, string schema, string table)
1008                 {
1009                         ArrayList keys = new ArrayList ();
1010                         IntPtr handle = IntPtr.Zero;
1011                         OdbcReturn ret;
1012                         try {
1013                                 ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, 
1014                                                            command.Connection.hDbc, ref handle);
1015                                 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
1016                                         throw new OdbcException(new OdbcError("SQLAllocHandle",
1017                                                                               OdbcHandleType.Dbc,
1018                                                                               command.Connection.hDbc));
1019
1020                                 ret = libodbc.SQLStatistics (handle, catalog, -3,  
1021                                                              schema, -3, 
1022                                                              table, -3,
1023                                                              libodbc.SQL_INDEX_UNIQUE,
1024                                                              libodbc.SQL_QUICK);
1025                                 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1026                                         throw new OdbcException (new OdbcError ("SQLStatistics", OdbcHandleType.Stmt, handle));
1027                         
1028                                 // NON_UNIQUE
1029                                 int  nonUniqueLength = 0;
1030                                 short nonUnique = libodbc.SQL_FALSE;
1031                                 ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.SHORT, ref (short) nonUnique, sizeof (short), ref nonUniqueLength);
1032                                 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1033                                         throw new OdbcException (new OdbcError ("SQLBindCol", OdbcHandleType.Stmt, handle));
1034                         
1035                                 // COLUMN_NAME
1036                                 int length = 0;
1037                                 byte [] colName = new byte [255];
1038                                 ret = libodbc.SQLBindCol (handle, 9, SQL_C_TYPE.CHAR, colName, colName.Length, ref length);
1039                                 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1040                                         throw new OdbcException (new OdbcError ("SQLBindCol", OdbcHandleType.Stmt, handle));
1041                         
1042                                 int i = 0;    
1043                                 while (true) {
1044                                         ret = libodbc.SQLFetch (handle);
1045                                         if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1046                                                 break;
1047                                         if (nonUnique == libodbc.SQL_TRUE) {
1048                                                 string pkey = Encoding.Default.GetString (colName, 0, length);
1049                                                 keys.Add (pkey);
1050                                                 break;
1051                                         }
1052                                 }
1053                         } finally {
1054                                 if (handle != IntPtr.Zero) {
1055                                         ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
1056                                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
1057                                                 throw new OdbcException(new OdbcError("SQLFreeStmt",OdbcHandleType.Stmt,handle));
1058                                         
1059                                         ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
1060                                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
1061                                                 throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,handle));
1062                                 }                             
1063                         }
1064                         return keys;
1065                 }
1066                 
1067                 public
1068 #if NET_2_0
1069                 override
1070 #endif // NET_2_0
1071                 bool Read ()
1072                 {
1073                         return NextRow ();
1074                 }
1075
1076 #if NET_2_0
1077                 [MonoTODO]
1078                 public override object GetProviderSpecificValue (int i)
1079                 {
1080                         throw new NotImplementedException ();
1081                 }
1082                 
1083                 [MonoTODO]
1084                 public override int GetProviderSpecificValues (object[] values)
1085                 {
1086                         throw new NotImplementedException ();
1087                 }
1088
1089                 [MonoTODO]
1090                 public override Type GetProviderSpecificFieldType (int i)
1091                 {
1092                         throw new NotImplementedException ();
1093                 }
1094                 
1095 #endif // NET_2_0
1096
1097
1098                 #endregion
1099         }
1100 }