e8f9c88b595cd535cc1754eb5acfe6191109f69f
[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         public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
45         {
46                 #region Fields
47                 
48                 private OdbcCommand command;
49                 private bool open;
50                 private int currentRow;
51                 private OdbcColumn[] cols;
52                 private IntPtr hstmt;
53                 private CommandBehavior behavior;
54
55                 #endregion
56
57                 #region Constructors
58
59                 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior) 
60                 {
61                         this.command = command;
62                         this.behavior=behavior;
63                         open = true;
64                         currentRow = -1;
65                         hstmt=command.hStmt;
66                         // Init columns array;
67                         short colcount=0;
68                         libodbc.SQLNumResultCols(hstmt, ref colcount);
69                         cols=new OdbcColumn[colcount];
70                         GetSchemaTable ();
71                 }
72
73                 #endregion
74
75                 #region Properties
76
77                 public int Depth {
78                         get {
79                                 return 0; // no nested selects supported
80                         }
81                 }
82
83                 public int FieldCount {
84                         get {
85                                 return cols.Length;
86                         }
87                 }
88
89                 public bool IsClosed {
90                         get {
91                                 return !open;
92                         }
93                 }
94
95                 public object this[string name] {
96                         get {
97                                 int pos;
98
99                                 if (currentRow == -1)
100                                         throw new InvalidOperationException ();
101
102                                 pos = ColIndex(name);
103                                 
104                                 if (pos == -1)
105                                         throw new IndexOutOfRangeException ();
106
107                                 return this[pos];
108                         }
109                 }
110
111                 public object this[int index] {
112                         get {
113                                 return (object) GetValue (index);
114                         }
115                 }
116
117                 public int RecordsAffected {
118                         get {
119                                 return -1;
120                         }
121                 }
122
123                 [MonoTODO]
124                 public bool HasRows {
125                         get { throw new NotImplementedException(); }
126                 }
127
128                 #endregion
129
130                 #region Methods
131                 
132                 private int ColIndex(string colname)
133                 {
134                         int i=0;
135                         foreach (OdbcColumn col in cols)
136                         {
137                                 if (col != null && col.ColumnName==colname)
138                                         return i;
139                                 i++;
140                         }
141                         return 0;
142                 }
143
144                 // Dynamically load column descriptions as needed.
145                 private OdbcColumn GetColumn(int ordinal)
146                 {
147                         if (cols[ordinal]==null)
148                         {
149                                 short bufsize=255;
150                                 byte[] colname_buffer=new byte[bufsize];
151                                 string colname;
152                                 short colname_size=0;
153                                 uint ColSize=0;
154                                 short DecDigits=0, Nullable=0, dt=0;
155                                 OdbcReturn ret=libodbc.SQLDescribeCol(hstmt, Convert.ToUInt16(ordinal+1), 
156                                         colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize, 
157                                         ref DecDigits, ref Nullable);
158                                 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
159                                         throw new OdbcException(new OdbcError("SQLDescribeCol",OdbcHandleType.Stmt,hstmt));
160                                 colname=System.Text.Encoding.Default.GetString(colname_buffer);
161                                 colname=colname.Replace((char) 0,' ').Trim();
162                                 OdbcColumn c=new OdbcColumn(colname, (OdbcType) dt);
163                                 c.AllowDBNull=(Nullable!=0);
164                                 c.Digits=DecDigits;
165                                 if (c.IsStringType)
166                                         c.MaxLength=(int)ColSize;
167                                 cols[ordinal]=c;
168                         }
169                         return cols[ordinal];
170                 }
171
172                 public void Close ()
173                 {
174                         // FIXME : have to implement output parameter binding
175                         OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
176                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
177                                 throw new OdbcException(new OdbcError("SQLCloseCursor",OdbcHandleType.Stmt,hstmt));
178         
179                         open = false;
180                         currentRow = -1;
181
182                         ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, hstmt);\r
183                         if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
184                                 throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,hstmt));
185
186                         if ((behavior & CommandBehavior.CloseConnection)==CommandBehavior.CloseConnection)
187                                 this.command.Connection.Close();
188                 }
189
190                 ~OdbcDataReader ()
191                 {
192                         if (open)
193                                 Close ();
194                 }
195
196                 public bool GetBoolean (int ordinal)
197                 {
198                         return (bool) GetValue(ordinal);
199                 }
200
201                 public byte GetByte (int ordinal)
202                 {
203                         return (byte) Convert.ToByte(GetValue(ordinal));
204                 }
205
206                 public long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
207                 {
208                         OdbcReturn ret = OdbcReturn.Error;
209                         bool copyBuffer = false;
210                         int returnVal = 0, outsize = 0;
211                         byte [] tbuff = new byte [length+1];
212
213                         length = buffer == null ? 0 : length;
214                         ret=libodbc.SQLGetData (hstmt, (ushort) (ordinal+1), OdbcType.Binary, tbuff, length, 
215                                         ref outsize);
216
217                         if (ret == OdbcReturn.NoData)
218                                 return 0;
219
220                         if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo)) 
221                                 throw new OdbcException (new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt));
222
223                         OdbcError odbcErr = null;
224                         if ( (ret == OdbcReturn.SuccessWithInfo))
225                                 odbcErr = new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt);
226
227                         if (buffer == null)
228                                 return outsize; //if buffer is null,return length of the field
229                         
230                         if (ret == OdbcReturn.SuccessWithInfo) {
231                                 if (outsize == (int) OdbcLengthIndicator.NoTotal)
232                                         copyBuffer = true;
233                                 else if (outsize == (int) OdbcLengthIndicator.NullData) {
234                                         copyBuffer = false;
235                                         returnVal = -1;
236                                 } else {
237                                         string sqlstate = odbcErr.SQLState;
238                                         //SQLState: String Data, Right truncated
239                                         if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC) 
240                                                 throw new OdbcException ( odbcErr);
241                                         copyBuffer = true;
242                                 }
243                         } else {
244                                 copyBuffer = outsize == -1 ? false : true;
245                                 returnVal = outsize;
246                         }
247
248                         if (copyBuffer) {
249                                 int i = 0;
250                                 while (tbuff [i] != libodbc.C_NULL) {
251                                         buffer [bufferIndex + i] = tbuff [i];
252                                         i++;
253                                 }
254                                 returnVal = i;
255                         }
256                         return returnVal;
257                 }
258                 
259                 [MonoTODO]
260                 public char GetChar (int ordinal)
261                 {
262                         throw new NotImplementedException ();
263                 }
264
265                 [MonoTODO]
266                 public long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
267                 {
268                         throw new NotImplementedException ();
269                 }
270
271                 [MonoTODO]
272                 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
273                 public IDataReader GetData (int ordinal)
274                 {
275                         throw new NotImplementedException ();
276                 }
277
278                 public string GetDataTypeName (int index)
279                 {
280                         return GetColumn(index).OdbcType.ToString();
281                 }
282
283                 public DateTime GetDate(int ordinal) {
284                         return GetDateTime(ordinal);
285                 }
286
287                 public DateTime GetDateTime (int ordinal)
288                 {
289                         return (DateTime) GetValue(ordinal);
290                 }
291
292                 [MonoTODO]
293                 public decimal GetDecimal (int ordinal)
294                 {
295                         throw new NotImplementedException ();
296                 }
297
298                 public double GetDouble (int ordinal)
299                 {
300                         return (double) GetValue(ordinal);
301                 }
302
303                 public Type GetFieldType (int index)
304                 {
305                         return GetColumn(index).DataType;
306                 }
307
308                 public float GetFloat (int ordinal)
309                 {
310                         return (float) GetValue(ordinal);
311                 }
312
313                 [MonoTODO]
314                 public Guid GetGuid (int ordinal)
315                 {
316                         throw new NotImplementedException ();
317                 }
318
319                 public short GetInt16 (int ordinal)
320                 {
321                         return (short) GetValue(ordinal);
322                 }
323
324                 public int GetInt32 (int ordinal)
325                 {
326                         return (int) GetValue(ordinal);
327                 }
328
329                 public long GetInt64 (int ordinal)
330                 {
331                         return (long) GetValue(ordinal);
332                 }
333
334                 public string GetName (int index)
335                 {
336                         return GetColumn(index).ColumnName;
337                 }
338
339                 public int GetOrdinal (string name)
340                 {
341                         if (currentRow == -1)
342                                 throw new IndexOutOfRangeException ();
343
344                         int i=ColIndex(name);
345
346                         if (i==-1)
347                                 throw new IndexOutOfRangeException ();
348                         else
349                                 return i;
350                 }
351
352                 [MonoTODO]
353                 public DataTable GetSchemaTable() 
354                 {       
355
356                         DataTable dataTableSchema = null;
357                         // Only Results from SQL SELECT Queries 
358                         // get a DataTable for schema of the result
359                         // otherwise, DataTable is null reference
360                         if(cols.Length > 0) 
361                         {
362                                 
363                                 dataTableSchema = new DataTable ();
364                                 
365                                 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
366                                 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
367                                 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
368                                 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
369                                 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
370                                 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
371                                 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
372                                 DataColumn dc = dataTableSchema.Columns["IsKey"];
373                                 dc.AllowDBNull = true; // IsKey can have a DBNull
374                                 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
375                                 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
376                                 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
377                                 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
378                                 dataTableSchema.Columns.Add ("DataType", typeof(Type));
379                                 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
380                                 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
381                                 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
382                                 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
383                                 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
384                                 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
385                                 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
386                                 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
387                                 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
388                                 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
389
390                                 DataRow schemaRow;
391                                                                 
392                                 for (int i = 0; i < cols.Length; i += 1 ) 
393                                 {
394                                         OdbcColumn col=GetColumn(i);
395
396                                         schemaRow = dataTableSchema.NewRow ();
397                                         dataTableSchema.Rows.Add (schemaRow);
398                                                                                 
399                                         schemaRow["ColumnName"] = col.ColumnName;
400                                         schemaRow["ColumnOrdinal"] = i + 1;
401                                         
402                                         schemaRow["ColumnSize"] = col.MaxLength;
403                                         schemaRow["NumericPrecision"] = 0;
404                                         schemaRow["NumericScale"] = 0;
405                                         // TODO: need to get KeyInfo
406                                         schemaRow["IsUnique"] = false;
407                                         schemaRow["IsKey"] = DBNull.Value;                                      
408
409                                         schemaRow["BaseCatalogName"] = "";                              
410                                         schemaRow["BaseColumnName"] = col.ColumnName;
411                                         schemaRow["BaseSchemaName"] = "";
412                                         schemaRow["BaseTableName"] = "";
413                                         schemaRow["DataType"] = col.DataType;
414
415                                         schemaRow["AllowDBNull"] = col.AllowDBNull;
416                                         
417                                         schemaRow["ProviderType"] = (int) col.OdbcType;
418                                         // TODO: all of these
419                                         schemaRow["IsAliased"] = false;
420                                         schemaRow["IsExpression"] = false;
421                                         schemaRow["IsIdentity"] = false;
422                                         schemaRow["IsAutoIncrement"] = false;
423                                         schemaRow["IsRowVersion"] = false;
424                                         schemaRow["IsHidden"] = false;
425                                         schemaRow["IsLong"] = false;
426                                         schemaRow["IsReadOnly"] = false;
427                                         
428                                         // FIXME: according to Brian, 
429                                         // this does not work on MS .NET
430                                         // however, we need it for Mono 
431                                         // for now
432                                         schemaRow.AcceptChanges();
433                                         
434                                 }
435
436                         }
437                         dataTableSchema.AcceptChanges();
438                         return dataTableSchema;
439                 }
440
441                 public string GetString (int ordinal)
442                 {
443                         return (string) GetValue(ordinal);
444                 }
445
446                 [MonoTODO]
447                 public TimeSpan GetTime (int ordinal)
448                 {
449                         throw new NotImplementedException ();
450                 }
451
452                 public object GetValue (int ordinal)
453                 {
454                         if (currentRow == -1)
455                                 throw new IndexOutOfRangeException ();
456
457                         if (ordinal>cols.Length-1 || ordinal<0)
458                                 throw new IndexOutOfRangeException ();
459
460                         OdbcReturn ret;
461                         int outsize=0, bufsize;
462                         byte[] buffer;
463                         OdbcColumn col=GetColumn(ordinal);
464                         object DataValue=null;
465                         ushort ColIndex=Convert.ToUInt16(ordinal+1);
466
467                         // Check cached values
468                         if (col.Value==null)
469                         {
470
471                                 // odbc help file
472                                 // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
473                                 switch (col.OdbcType)
474                                 {
475                                         case OdbcType.Decimal:
476                                                 bufsize=50;
477                                                 buffer=new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for decimal
478                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
479                                                 byte[] temp = new byte[outsize];
480                                                 for (int i=0;i<outsize;i++)
481                                                         temp[i]=buffer[i];
482
483                                                 if (outsize!=-1)
484                                                         DataValue=Decimal.Parse(System.Text.Encoding.Default.GetString(temp));
485                                                 break;
486                                         case OdbcType.TinyInt:
487                                                 short short_data=0;
488                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.TinyInt, ref short_data, 0, ref outsize);
489                                                 DataValue=System.Convert.ToByte(short_data);
490                                                 break;
491                                         case OdbcType.Int:
492                                                 int int_data=0;
493                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Int, ref int_data, 0, ref outsize);
494                                                 DataValue=int_data;
495                                                 break;
496
497                                         case OdbcType.SmallInt:
498                                                 short sint_data=0;
499                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.SmallInt, ref sint_data, 0, ref outsize);
500                                                 DataValue=sint_data;
501                                                 break;
502
503                                         case OdbcType.BigInt:
504                                                 long long_data=0;
505                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcCType.SignedBigInt, ref long_data, 0, ref outsize);
506                                                 DataValue=long_data;
507                                                 break;
508                                         case OdbcType.NVarChar:
509                                                 bufsize=col.MaxLength*2+1; // Unicode is double byte
510                                                 buffer=new byte[bufsize];
511                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.NVarChar, buffer, bufsize, ref outsize);
512                                                 if (outsize!=-1)
513                                                         DataValue=System.Text.Encoding.Unicode.GetString(buffer,0,outsize);
514                                                 break;
515                                         case OdbcType.VarChar:
516                                                 bufsize=col.MaxLength+1;
517                                                 buffer=new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for both char and varchar
518                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
519                                                 if (outsize!=-1)
520                                                         DataValue=System.Text.Encoding.Default.GetString(buffer,0,outsize);
521                                                 break;
522                                         case OdbcType.Real:
523                                                 float float_data=0;
524                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Real, ref float_data, 0, ref outsize);
525                                                 DataValue=float_data;
526                                                 break;
527                                         case OdbcType.Timestamp:
528                                         case OdbcType.DateTime:
529                                         case OdbcType.Date:
530                                         case OdbcType.Time:
531                                                 OdbcTimestamp ts_data=new OdbcTimestamp();
532                                                 if (col.OdbcType == OdbcType.Timestamp) 
533                                                         ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Timestamp, ref ts_data, 0, ref outsize);
534                                                 else if (col.OdbcType == OdbcType.DateTime)
535                                                         ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.DateTime, ref ts_data, 0, ref outsize);
536                                                 else if (col.OdbcType == OdbcType.Date)
537                                                         ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Date, ref ts_data, 0, ref outsize);
538                                                 else  // FIXME: how to get TIME datatype ??
539                                                         ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.DateTime, ref ts_data, 0, ref outsize);
540                                                 if (outsize!=-1) // This means SQL_NULL_DATA 
541                                                         DataValue=new DateTime(ts_data.year,ts_data.month,ts_data.day,ts_data.hour,
542                                                                 ts_data.minute,ts_data.second,Convert.ToInt32(ts_data.fraction));
543                                                 break;
544                                         case OdbcType.Binary :
545                                         case OdbcType.Image :
546                                                 bufsize = col.MaxLength + 1;
547                                                 buffer = new byte [bufsize];
548                                                 long read = GetBytes (ordinal, 0, buffer, 0, bufsize);
549                                                 ret = OdbcReturn.Success;
550                                                 DataValue = buffer;
551                                                 break;
552                                         default:
553                                                 bufsize=255;
554                                                 buffer=new byte[bufsize];
555                                                 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
556                                                 DataValue=System.Text.Encoding.Default.GetString(buffer);
557                                                 break;
558                                 }
559
560                                 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
561                                         throw new OdbcException(new OdbcError("SQLGetData",OdbcHandleType.Stmt,hstmt));
562
563                                 if (outsize==-1) // This means SQL_NULL_DATA 
564                                         col.Value=DBNull.Value;
565                                 else
566                                         col.Value=DataValue;
567                         }
568                         return col.Value;
569                 }
570                 
571                 public int GetValues (object[] values)
572                 {
573                         int numValues = 0;
574
575                         // copy values
576                         for (int i = 0; i < values.Length; i++) {
577                                 if (i < FieldCount) {
578                                         values[i] = GetValue(i);
579                                 }
580                                 else {
581                                         values[i] = null;
582                                 }
583                         }
584
585                         // get number of object instances in array
586                         if (values.Length < FieldCount)
587                                 numValues = values.Length;
588                         else if (values.Length == FieldCount)
589                                 numValues = FieldCount;
590                         else
591                                 numValues = FieldCount;
592
593                         return numValues;
594                 }
595
596                 [MonoTODO]
597                 IDataReader IDataRecord.GetData (int ordinal)
598                 {
599                         throw new NotImplementedException ();
600                 }
601
602                 [MonoTODO]
603                 void IDisposable.Dispose ()
604                 {
605                 }
606
607                 [MonoTODO]
608                 IEnumerator IEnumerable.GetEnumerator ()
609                 {
610                         return new DbEnumerator (this);
611                 }
612
613                 public bool IsDBNull (int ordinal)
614                 {
615                         return (GetValue(ordinal) is DBNull);
616                 }
617
618                 /// <remarks>
619                 ///     Move to the next result set.
620                 /// </remarks>
621                 public bool NextResult ()
622                 {
623                         OdbcReturn ret = OdbcReturn.Success;
624                         ret = libodbc.SQLMoreResults (hstmt);
625                         if (ret == OdbcReturn.Success) {
626                                 short colcount = 0;
627                                 libodbc.SQLNumResultCols (hstmt, ref colcount);
628                                 cols = new OdbcColumn [colcount];
629                                 GetSchemaTable ();
630                         }       
631                         return (ret==OdbcReturn.Success);
632                 }
633
634                 /// <remarks>
635                 ///     Load the next row in the current result set.
636                 /// </remarks>
637                 public bool NextRow ()
638                 {
639                         OdbcReturn ret=libodbc.SQLFetch (hstmt);
640                         if (ret != OdbcReturn.Success)
641                                 currentRow = -1;
642                         else
643                                 currentRow++;
644
645                         // Clear cached values from last record
646                         foreach (OdbcColumn col in cols)
647                         {
648                                 if (col != null)
649                                         col.Value = null;
650                         }
651                         return (ret == OdbcReturn.Success);
652                 }
653
654                 public bool Read ()
655                 {
656                         return NextRow ();
657                 }
658
659                 #endregion
660         }
661 }