2 // System.Data.Odbc.OdbcDataReader
5 // Brian Ritchie (brianlritchie@hotmail.com)
6 // Daniel Morgan <danmorg@sc.rr.com>
7 // Sureshkumar T <tsureshkumar@novell.com> (2004)
9 // Copyright (C) Brian Ritchie, 2002
10 // Copyright (C) Daniel Morgan, 2002
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
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.
36 using System.Collections;
37 using System.ComponentModel;
39 using System.Data.Common;
42 namespace System.Data.Odbc
44 public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
48 private OdbcCommand command;
50 private int currentRow;
51 private OdbcColumn[] cols;
53 private CommandBehavior behavior;
59 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
61 this.command = command;
62 this.behavior=behavior;
66 // Init columns array;
68 libodbc.SQLNumResultCols(hstmt, ref colcount);
69 cols=new OdbcColumn[colcount];
79 return 0; // no nested selects supported
83 public int FieldCount {
89 public bool IsClosed {
95 public object this[string name] {
100 throw new InvalidOperationException ();
102 pos = ColIndex(name);
105 throw new IndexOutOfRangeException ();
111 public object this[int index] {
113 return (object) GetValue (index);
117 public int RecordsAffected {
124 public bool HasRows {
125 get { throw new NotImplementedException(); }
132 private int ColIndex(string colname)
135 foreach (OdbcColumn col in cols)
137 if (col != null && col.ColumnName==colname)
144 // Dynamically load column descriptions as needed.
145 private OdbcColumn GetColumn(int ordinal)
147 if (cols[ordinal]==null)
150 byte[] colname_buffer=new byte[bufsize];
152 short colname_size=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);
166 c.MaxLength=(int)ColSize;
169 return cols[ordinal];
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));
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));
186 if ((behavior & CommandBehavior.CloseConnection)==CommandBehavior.CloseConnection)
187 this.command.Connection.Close();
196 public bool GetBoolean (int ordinal)
198 return (bool) GetValue(ordinal);
201 public byte GetByte (int ordinal)
203 return (byte) Convert.ToByte(GetValue(ordinal));
206 public long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
208 OdbcReturn ret = OdbcReturn.Error;
209 bool copyBuffer = false;
210 int returnVal = 0, outsize = 0;
211 byte [] tbuff = new byte [length+1];
213 length = buffer == null ? 0 : length;
214 ret=libodbc.SQLGetData (hstmt, (ushort) (ordinal+1), OdbcType.Binary, tbuff, length,
217 if (ret == OdbcReturn.NoData)
220 if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
221 throw new OdbcException (new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt));
223 OdbcError odbcErr = null;
224 if ( (ret == OdbcReturn.SuccessWithInfo))
225 odbcErr = new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt);
228 return outsize; //if buffer is null,return length of the field
230 if (ret == OdbcReturn.SuccessWithInfo) {
231 if (outsize == (int) OdbcLengthIndicator.NoTotal)
233 else if (outsize == (int) OdbcLengthIndicator.NullData) {
237 string sqlstate = odbcErr.SQLState;
238 //SQLState: String Data, Right truncated
239 if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC)
240 throw new OdbcException ( odbcErr);
244 copyBuffer = outsize == -1 ? false : true;
250 while (tbuff [i] != libodbc.C_NULL) {
251 buffer [bufferIndex + i] = tbuff [i];
260 public char GetChar (int ordinal)
262 throw new NotImplementedException ();
266 public long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
268 throw new NotImplementedException ();
272 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
273 public IDataReader GetData (int ordinal)
275 throw new NotImplementedException ();
278 public string GetDataTypeName (int index)
280 return GetColumn(index).OdbcType.ToString();
283 public DateTime GetDate(int ordinal) {
284 return GetDateTime(ordinal);
287 public DateTime GetDateTime (int ordinal)
289 return (DateTime) GetValue(ordinal);
293 public decimal GetDecimal (int ordinal)
295 throw new NotImplementedException ();
298 public double GetDouble (int ordinal)
300 return (double) GetValue(ordinal);
303 public Type GetFieldType (int index)
305 return GetColumn(index).DataType;
308 public float GetFloat (int ordinal)
310 return (float) GetValue(ordinal);
314 public Guid GetGuid (int ordinal)
316 throw new NotImplementedException ();
319 public short GetInt16 (int ordinal)
321 return (short) GetValue(ordinal);
324 public int GetInt32 (int ordinal)
326 return (int) GetValue(ordinal);
329 public long GetInt64 (int ordinal)
331 return (long) GetValue(ordinal);
334 public string GetName (int index)
336 return GetColumn(index).ColumnName;
339 public int GetOrdinal (string name)
341 if (currentRow == -1)
342 throw new IndexOutOfRangeException ();
344 int i=ColIndex(name);
347 throw new IndexOutOfRangeException ();
353 public DataTable GetSchemaTable()
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
363 dataTableSchema = new DataTable ();
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));
392 for (int i = 0; i < cols.Length; i += 1 )
394 OdbcColumn col=GetColumn(i);
396 schemaRow = dataTableSchema.NewRow ();
397 dataTableSchema.Rows.Add (schemaRow);
399 schemaRow["ColumnName"] = col.ColumnName;
400 schemaRow["ColumnOrdinal"] = i + 1;
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;
409 schemaRow["BaseCatalogName"] = "";
410 schemaRow["BaseColumnName"] = col.ColumnName;
411 schemaRow["BaseSchemaName"] = "";
412 schemaRow["BaseTableName"] = "";
413 schemaRow["DataType"] = col.DataType;
415 schemaRow["AllowDBNull"] = col.AllowDBNull;
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;
428 // FIXME: according to Brian,
429 // this does not work on MS .NET
430 // however, we need it for Mono
432 schemaRow.AcceptChanges();
437 dataTableSchema.AcceptChanges();
438 return dataTableSchema;
441 public string GetString (int ordinal)
443 return (string) GetValue(ordinal);
447 public TimeSpan GetTime (int ordinal)
449 throw new NotImplementedException ();
452 public object GetValue (int ordinal)
454 if (currentRow == -1)
455 throw new IndexOutOfRangeException ();
457 if (ordinal>cols.Length-1 || ordinal<0)
458 throw new IndexOutOfRangeException ();
461 int outsize=0, bufsize;
463 OdbcColumn col=GetColumn(ordinal);
464 object DataValue=null;
465 ushort ColIndex=Convert.ToUInt16(ordinal+1);
467 // Check cached values
472 // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
473 switch (col.OdbcType)
475 case OdbcType.Decimal:
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++)
484 DataValue=Decimal.Parse(System.Text.Encoding.Default.GetString(temp));
486 case OdbcType.TinyInt:
488 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.TinyInt, ref short_data, 0, ref outsize);
489 DataValue=System.Convert.ToByte(short_data);
493 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Int, ref int_data, 0, ref outsize);
497 case OdbcType.SmallInt:
499 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.SmallInt, ref sint_data, 0, ref outsize);
503 case OdbcType.BigInt:
505 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcCType.SignedBigInt, ref long_data, 0, ref outsize);
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);
513 DataValue=System.Text.Encoding.Unicode.GetString(buffer,0,outsize);
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);
520 DataValue=System.Text.Encoding.Default.GetString(buffer,0,outsize);
524 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Real, ref float_data, 0, ref outsize);
525 DataValue=float_data;
527 case OdbcType.Timestamp:
528 case OdbcType.DateTime:
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));
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;
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);
560 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
561 throw new OdbcException(new OdbcError("SQLGetData",OdbcHandleType.Stmt,hstmt));
563 if (outsize==-1) // This means SQL_NULL_DATA
564 col.Value=DBNull.Value;
571 public int GetValues (object[] values)
576 for (int i = 0; i < values.Length; i++) {
577 if (i < FieldCount) {
578 values[i] = GetValue(i);
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;
591 numValues = FieldCount;
597 IDataReader IDataRecord.GetData (int ordinal)
599 throw new NotImplementedException ();
603 void IDisposable.Dispose ()
608 IEnumerator IEnumerable.GetEnumerator ()
610 return new DbEnumerator (this);
613 public bool IsDBNull (int ordinal)
615 return (GetValue(ordinal) is DBNull);
619 /// Move to the next result set.
621 public bool NextResult ()
623 OdbcReturn ret = OdbcReturn.Success;
624 ret = libodbc.SQLMoreResults (hstmt);
625 if (ret == OdbcReturn.Success) {
627 libodbc.SQLNumResultCols (hstmt, ref colcount);
628 cols = new OdbcColumn [colcount];
631 return (ret==OdbcReturn.Success);
635 /// Load the next row in the current result set.
637 public bool NextRow ()
639 OdbcReturn ret=libodbc.SQLFetch (hstmt);
640 if (ret != OdbcReturn.Success)
645 // Clear cached values from last record
646 foreach (OdbcColumn col in cols)
651 return (ret == OdbcReturn.Success);