2 // System.Data.Odbc.OdbcDataReader
5 // Brian Ritchie (brianlritchie@hotmail.com)
7 // Copyright (C) Brian Ritchie, 2002
10 using System.Collections;
11 using System.ComponentModel;
13 using System.Data.Common;
15 namespace System.Data.Odbc
17 public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
21 private OdbcCommand command;
23 private int currentRow;
24 private OdbcColumn[] cols;
26 private CommandBehavior behavior;
32 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
34 this.command = command;
35 this.behavior=behavior;
36 this.command.Connection.DataReader = this;
40 // Init columns array;
42 libodbc.SQLNumResultCols(hstmt, ref colcount);
43 cols=new OdbcColumn[colcount];
52 return 0; // no nested selects supported
56 public int FieldCount {
63 public bool IsClosed {
69 public object this[string name] {
74 throw new InvalidOperationException ();
79 throw new IndexOutOfRangeException ();
85 public object this[int index] {
87 return (object) GetValue (index);
91 public int RecordsAffected {
101 private int ColIndex(string colname)
104 foreach (OdbcColumn col in cols)
106 if (col.ColumnName==colname)
113 // Dynamically load column descriptions as needed.
114 private OdbcColumn GetColumn(int ordinal)
116 if (cols[ordinal]==null)
119 byte[] colname_buffer=new byte[bufsize];
121 short colname_size=0;
122 short ColSize=0, DecDigits=0, Nullable=0, dt=0;
123 OdbcReturn ret=libodbc.SQLDescribeCol(hstmt, Convert.ToUInt16(ordinal+1),
124 colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize,
125 ref DecDigits, ref Nullable);
126 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
127 throw new OdbcException(new OdbcError("SQLDescribeCol",OdbcHandleType.Stmt,hstmt));
128 colname=System.Text.Encoding.Default.GetString(colname_buffer);
129 colname=colname.Replace((char) 0,' ').Trim();
130 OdbcColumn c=new OdbcColumn(colname, (OdbcType) dt);
131 c.AllowDBNull=(Nullable!=0);
137 return cols[ordinal];
142 // libodbc.SQLFreeHandle((ushort) OdbcHandleType.Stmt, hstmt);
144 OdbcReturn ret=libodbc.SQLCloseCursor(hstmt);
145 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
146 throw new OdbcException(new OdbcError("SQLCloseCursor",OdbcHandleType.Stmt,hstmt));
151 this.command.Connection.DataReader = null;
153 if ((behavior & CommandBehavior.CloseConnection)==CommandBehavior.CloseConnection)
154 this.command.Connection.Close();
163 public bool GetBoolean (int ordinal)
165 return (bool) GetValue(ordinal);
169 public byte GetByte (int ordinal)
171 throw new NotImplementedException ();
175 public long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
177 throw new NotImplementedException ();
181 public char GetChar (int ordinal)
183 throw new NotImplementedException ();
187 public long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
189 throw new NotImplementedException ();
193 public OdbcDataReader GetData (int ordinal)
195 throw new NotImplementedException ();
198 public string GetDataTypeName (int index)
200 return GetColumn(index).OdbcType.ToString();
203 public DateTime GetDateTime (int ordinal)
205 return (DateTime) GetValue(ordinal);
209 public decimal GetDecimal (int ordinal)
211 throw new NotImplementedException ();
214 public double GetDouble (int ordinal)
216 return (double) GetValue(ordinal);
219 public Type GetFieldType (int index)
221 return GetColumn(index).DataType;
224 public float GetFloat (int ordinal)
226 return (float) GetValue(ordinal);
230 public Guid GetGuid (int ordinal)
232 throw new NotImplementedException ();
235 public short GetInt16 (int ordinal)
237 return (short) GetValue(ordinal);
240 public int GetInt32 (int ordinal)
242 return (int) GetValue(ordinal);
245 public long GetInt64 (int ordinal)
247 return (long) GetValue(ordinal);
250 public string GetName (int index)
252 if (currentRow == -1)
254 return GetColumn(index).ColumnName;
257 public int GetOrdinal (string name)
259 if (currentRow == -1)
260 throw new IndexOutOfRangeException ();
262 int i=ColIndex(name);
265 throw new IndexOutOfRangeException ();
271 public DataTable GetSchemaTable()
274 DataTable dataTableSchema = null;
275 // Only Results from SQL SELECT Queries
276 // get a DataTable for schema of the result
277 // otherwise, DataTable is null reference
281 dataTableSchema = new DataTable ();
283 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
284 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
285 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
286 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
287 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
288 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
289 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
290 DataColumn dc = dataTableSchema.Columns["IsKey"];
291 dc.AllowDBNull = true; // IsKey can have a DBNull
292 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
293 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
294 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
295 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
296 dataTableSchema.Columns.Add ("DataType", typeof(Type));
297 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
298 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
299 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
300 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
301 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
302 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
303 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
304 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
305 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
306 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
310 for (int i = 0; i < cols.Length; i += 1 )
312 OdbcColumn col=GetColumn(i);
313 //Console.WriteLine("{0}:{1}:{2}",col.ColumnName,col.DataType,col.OdbcType);
315 schemaRow = dataTableSchema.NewRow ();
316 dataTableSchema.Rows.Add (schemaRow);
318 schemaRow["ColumnName"] = col.ColumnName;
319 schemaRow["ColumnOrdinal"] = i + 1;
321 schemaRow["ColumnSize"] = col.MaxLength;
322 schemaRow["NumericPrecision"] = 0;
323 schemaRow["NumericScale"] = 0;
324 // TODO: need to get KeyInfo
325 schemaRow["IsUnique"] = false;
326 schemaRow["IsKey"] = DBNull.Value;
328 schemaRow["BaseCatalogName"] = "";
329 schemaRow["BaseColumnName"] = col.ColumnName;
330 schemaRow["BaseSchemaName"] = "";
331 schemaRow["BaseTableName"] = "";
332 schemaRow["DataType"] = col.DataType;
334 schemaRow["AllowDBNull"] = col.AllowDBNull;
336 schemaRow["ProviderType"] = (int) col.OdbcType;
337 // TODO: all of these
338 schemaRow["IsAliased"] = false;
339 schemaRow["IsExpression"] = false;
340 schemaRow["IsIdentity"] = false;
341 schemaRow["IsAutoIncrement"] = false;
342 schemaRow["IsRowVersion"] = false;
343 schemaRow["IsHidden"] = false;
344 schemaRow["IsLong"] = false;
345 schemaRow["IsReadOnly"] = false;
347 // schemaRow.AcceptChanges();
352 dataTableSchema.AcceptChanges();
353 return dataTableSchema;
356 public string GetString (int ordinal)
358 return (string) GetValue(ordinal);
362 public TimeSpan GetTimeSpan (int ordinal)
364 throw new NotImplementedException ();
367 public object GetValue (int ordinal)
369 if (currentRow == -1)
370 throw new IndexOutOfRangeException ();
372 if (ordinal>cols.Length-1 || ordinal<0)
373 throw new IndexOutOfRangeException ();
376 int outsize=0, bufsize;
378 OdbcColumn col=GetColumn(ordinal);
379 object DataValue=null;
380 ushort ColIndex=Convert.ToUInt16(ordinal+1);
382 // Check cached values
387 // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
388 switch (col.OdbcType)
390 case OdbcType.Decimal:
392 buffer=new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for decimal
393 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
395 DataValue=Decimal.Parse(System.Text.Encoding.Default.GetString(buffer));
397 case OdbcType.TinyInt:
399 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.TinyInt, ref short_data, 0, ref outsize);
400 DataValue=short_data;
404 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Int, ref int_data, 0, ref outsize);
407 case OdbcType.BigInt:
409 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.BigInt, ref long_data, 0, ref outsize);
412 case OdbcType.NVarChar:
413 bufsize=col.MaxLength*2+1; // Unicode is double byte
414 buffer=new byte[bufsize];
415 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.NVarChar, buffer, bufsize, ref outsize);
417 DataValue=System.Text.Encoding.Unicode.GetString(buffer,0,outsize);
419 case OdbcType.VarChar:
420 bufsize=col.MaxLength+1;
421 buffer=new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
422 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
424 DataValue=System.Text.Encoding.Default.GetString(buffer,0,outsize);
428 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Real, ref float_data, 0, ref outsize);
429 DataValue=float_data;
431 case OdbcType.Timestamp:
432 case OdbcType.DateTime:
433 OdbcTimestamp ts_data=new OdbcTimestamp();
434 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.DateTime, ref ts_data, 0, ref outsize);
435 if (outsize!=-1) // This means SQL_NULL_DATA
436 DataValue=new DateTime(ts_data.year,ts_data.month,ts_data.day,ts_data.hour,
437 ts_data.minute,ts_data.second,Convert.ToInt32(ts_data.fraction));
440 //Console.WriteLine("Fetching unsupported data type as string: "+col.OdbcType.ToString());
442 buffer=new byte[bufsize];
443 ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
444 DataValue=System.Text.Encoding.Default.GetString(buffer);
448 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
449 throw new OdbcException(new OdbcError("SQLGetData",OdbcHandleType.Stmt,hstmt));
451 if (outsize==-1) // This means SQL_NULL_DATA
452 col.Value=DBNull.Value;
460 public int GetValues (object[] values)
462 throw new NotImplementedException ();
466 IDataReader IDataRecord.GetData (int ordinal)
468 throw new NotImplementedException ();
472 void IDisposable.Dispose ()
477 IEnumerator IEnumerable.GetEnumerator ()
479 throw new NotImplementedException ();
482 public bool IsDBNull (int ordinal)
484 return (GetValue(ordinal) is DBNull);
487 public bool NextResult ()
489 OdbcReturn ret=libodbc.SQLFetch(hstmt);
490 if (ret!=OdbcReturn.Success)
494 // Clear cached values from last record
495 foreach (OdbcColumn col in cols)
500 return (ret==OdbcReturn.Success);