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