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