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
;
40 using System
.Globalization
;
43 namespace System
.Data
.Odbc
45 public sealed class OdbcDataReader
: DbDataReader
49 private OdbcCommand command
;
51 private int currentRow
;
52 private OdbcColumn
[] cols
;
54 private int _recordsAffected
= -1;
56 private DataTable _dataTableSchema
;
57 private CommandBehavior behavior
;
63 internal OdbcDataReader (OdbcCommand command
, CommandBehavior behavior
)
65 this.command
= command
;
66 this.CommandBehavior
= behavior
;
69 hstmt
= command
.hStmt
;
70 // Init columns array;
72 libodbc
.SQLNumResultCols (hstmt
, ref colcount
);
73 cols
= new OdbcColumn
[colcount
];
77 internal OdbcDataReader (OdbcCommand command
, CommandBehavior behavior
,
78 int recordAffected
) : this (command
, behavior
)
80 _recordsAffected
= recordAffected
;
88 private CommandBehavior CommandBehavior
{
89 get { return behavior; }
90 set { behavior = value; }
97 return 0; // no nested selects supported
106 throw new InvalidOperationException ("The reader is closed.");
121 object this [string value] {
123 int pos
= GetOrdinal (value);
130 object this [int i
] {
138 int RecordsAffected
{
140 return _recordsAffected
;
148 get { throw new NotImplementedException(); }
151 private OdbcConnection Connection
{
154 return command
.Connection
;
163 private int ColIndex (string colname
)
166 foreach (OdbcColumn col
in cols
) {
168 if (col
.ColumnName
== colname
)
170 if (String
.Compare (col
.ColumnName
, colname
, true) == 0)
178 // Dynamically load column descriptions as needed.
179 private OdbcColumn
GetColumn (int ordinal
)
181 if (cols
[ordinal
] == null) {
183 byte [] colname_buffer
= new byte [bufsize
];
185 short colname_size
= 0;
187 short DecDigits
= 0, Nullable
= 0, dt
= 0;
188 OdbcReturn ret
= libodbc
.SQLDescribeCol (hstmt
, Convert
.ToUInt16 (ordinal
+ 1),
189 colname_buffer
, bufsize
, ref colname_size
, ref dt
, ref ColSize
,
190 ref DecDigits
, ref Nullable
);
191 if ((ret
!= OdbcReturn
.Success
) && (ret
!= OdbcReturn
.SuccessWithInfo
))
192 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, hstmt
);
193 colname
= RemoveTrailingNullChar (Encoding
.Unicode
.GetString (colname_buffer
));
194 OdbcColumn c
= new OdbcColumn (colname
, (SQL_TYPE
) dt
);
195 c
.AllowDBNull
= (Nullable
!= 0);
196 c
.Digits
= DecDigits
;
197 if (c
.IsVariableSizeType
)
198 c
.MaxLength
= (int) ColSize
;
201 return cols
[ordinal
];
204 // Load all column descriptions
205 private void GetColumns ()
207 for(int i
= 0; i
< cols
.Length
; i
++)
215 // FIXME : have to implement output parameter binding
219 this.command
.FreeIfNotPrepared ();
221 if ((this.CommandBehavior
& CommandBehavior
.CloseConnection
) == CommandBehavior
.CloseConnection
)
222 this.command
.Connection
.Close ();
228 bool GetBoolean (int i
)
230 return (bool) GetValue (i
);
237 return Convert
.ToByte (GetValue (i
));
242 long GetBytes (int i
, long dataIndex
, byte[] buffer
, int bufferIndex
, int length
)
245 throw new InvalidOperationException ("Reader is not open.");
246 if (currentRow
== -1)
247 throw new InvalidOperationException ("No data available.");
249 OdbcReturn ret
= OdbcReturn
.Error
;
250 bool copyBuffer
= false;
251 int returnVal
= 0, outsize
= 0;
252 byte [] tbuff
= new byte [length
+1];
256 ret
=libodbc
.SQLGetData (hstmt
, (ushort) (i
+ 1), SQL_C_TYPE
.BINARY
, tbuff
, length
,
259 if (ret
== OdbcReturn
.NoData
)
262 if ( (ret
!= OdbcReturn
.Success
) && (ret
!= OdbcReturn
.SuccessWithInfo
))
263 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, hstmt
);
265 OdbcException odbcException
= null;
266 if ( (ret
== OdbcReturn
.SuccessWithInfo
))
267 odbcException
= Connection
.CreateOdbcException (
268 OdbcHandleType
.Stmt
, hstmt
);
271 return outsize
; //if buffer is null,return length of the field
273 if (ret
== OdbcReturn
.SuccessWithInfo
) {
274 if (outsize
== (int) OdbcLengthIndicator
.NoTotal
)
276 else if (outsize
== (int) OdbcLengthIndicator
.NullData
) {
280 string sqlstate
= odbcException
.Errors
[0].SQLState
;
281 //SQLState: String Data, Right truncated
282 if (sqlstate
!= libodbc
.SQLSTATE_RIGHT_TRUNC
)
287 copyBuffer
= outsize
== -1 ? false : true;
292 if (outsize
== (int) OdbcLengthIndicator
.NoTotal
) {
294 while (tbuff
[j
] != libodbc
.C_NULL
) {
295 buffer
[bufferIndex
+ j
] = tbuff
[j
];
300 int read_bytes
= Math
.Min (outsize
, length
);
301 for (int j
= 0; j
< read_bytes
; j
++)
302 buffer
[bufferIndex
+ j
] = tbuff
[j
];
303 returnVal
= read_bytes
;
314 throw new NotImplementedException ();
320 long GetChars (int i
, long dataIndex
, char[] buffer
, int bufferIndex
, int length
)
323 throw new InvalidOperationException ("The reader is closed.");
324 if (currentRow
== -1)
325 throw new InvalidOperationException ("No data available.");
326 if (i
< 0 || i
>= FieldCount
)
327 throw new IndexOutOfRangeException ();
328 throw new NotImplementedException ();
332 [EditorBrowsableAttribute (EditorBrowsableState
.Never
)]
334 IDataReader
GetData (int i
)
336 throw new NotImplementedException ();
341 string GetDataTypeName (int i
)
344 throw new InvalidOperationException ("The reader is closed.");
345 if (i
< 0 || i
>= FieldCount
)
346 throw new IndexOutOfRangeException ();
347 return GetColumnAttributeStr (i
+ 1, FieldIdentifier
.TypeName
);
350 public DateTime
GetDate (int i
)
352 return GetDateTime (i
);
357 DateTime
GetDateTime (int i
)
359 return (DateTime
) GetValue (i
);
364 decimal GetDecimal (int i
)
366 return (decimal) GetValue (i
);
371 double GetDouble (int i
)
373 return (double) GetValue (i
);
378 Type
GetFieldType (int i
)
381 throw new InvalidOperationException ("The reader is closed.");
382 return GetColumn (i
).DataType
;
387 float GetFloat (int i
)
389 return (float) GetValue (i
);
397 throw new NotImplementedException ();
402 short GetInt16 (int i
)
404 return (short) GetValue (i
);
411 return (int) GetValue (i
);
416 long GetInt64 (int i
)
418 return (long) GetValue (i
);
423 string GetName (int i
)
426 throw new InvalidOperationException ("The reader is closed.");
427 return GetColumn (i
).ColumnName
;
432 int GetOrdinal (string value)
435 throw new InvalidOperationException ("The reader is closed.");
437 throw new ArgumentNullException ("fieldName");
439 int i
= ColIndex (value);
441 throw new IndexOutOfRangeException ();
448 DataTable
GetSchemaTable ()
451 throw new InvalidOperationException ("The reader is closed.");
454 // * Map OdbcType to System.Type and assign to DataType.
455 // This will eliminate the need for IsStringType in
458 if (_dataTableSchema
!= null)
459 return _dataTableSchema
;
461 DataTable dataTableSchema
= null;
462 // Only Results from SQL SELECT Queries
463 // get a DataTable for schema of the result
464 // otherwise, DataTable is null reference
465 if (cols
.Length
> 0) {
466 dataTableSchema
= new DataTable ();
468 dataTableSchema
.Columns
.Add ("ColumnName", typeof (string));
469 dataTableSchema
.Columns
.Add ("ColumnOrdinal", typeof (int));
470 dataTableSchema
.Columns
.Add ("ColumnSize", typeof (int));
471 dataTableSchema
.Columns
.Add ("NumericPrecision", typeof (int));
472 dataTableSchema
.Columns
.Add ("NumericScale", typeof (int));
473 dataTableSchema
.Columns
.Add ("IsUnique", typeof (bool));
474 dataTableSchema
.Columns
.Add ("IsKey", typeof (bool));
475 DataColumn dc
= dataTableSchema
.Columns
["IsKey"];
476 dc
.AllowDBNull
= true; // IsKey can have a DBNull
477 dataTableSchema
.Columns
.Add ("BaseCatalogName", typeof (string));
478 dataTableSchema
.Columns
.Add ("BaseColumnName", typeof (string));
479 dataTableSchema
.Columns
.Add ("BaseSchemaName", typeof (string));
480 dataTableSchema
.Columns
.Add ("BaseTableName", typeof (string));
481 dataTableSchema
.Columns
.Add ("DataType", typeof(Type
));
482 dataTableSchema
.Columns
.Add ("AllowDBNull", typeof (bool));
483 dataTableSchema
.Columns
.Add ("ProviderType", typeof (int));
484 dataTableSchema
.Columns
.Add ("IsAliased", typeof (bool));
485 dataTableSchema
.Columns
.Add ("IsExpression", typeof (bool));
486 dataTableSchema
.Columns
.Add ("IsIdentity", typeof (bool));
487 dataTableSchema
.Columns
.Add ("IsAutoIncrement", typeof (bool));
488 dataTableSchema
.Columns
.Add ("IsRowVersion", typeof (bool));
489 dataTableSchema
.Columns
.Add ("IsHidden", typeof (bool));
490 dataTableSchema
.Columns
.Add ("IsLong", typeof (bool));
491 dataTableSchema
.Columns
.Add ("IsReadOnly", typeof (bool));
495 for (int i
= 0; i
< cols
.Length
; i
+= 1 ) {
496 OdbcColumn col
=GetColumn(i
);
498 schemaRow
= dataTableSchema
.NewRow ();
499 dataTableSchema
.Rows
.Add (schemaRow
);
501 schemaRow
["ColumnName"] = col
.ColumnName
;
502 schemaRow
["ColumnOrdinal"] = i
;
503 schemaRow
["ColumnSize"] = col
.MaxLength
;
504 schemaRow
["NumericPrecision"] = GetColumnAttribute (i
+1, FieldIdentifier
.Precision
);
505 schemaRow
["NumericScale"] = GetColumnAttribute (i
+1, FieldIdentifier
.Scale
);
506 schemaRow
["BaseTableName"] = GetColumnAttributeStr (i
+1, FieldIdentifier
.TableName
);
507 schemaRow
["BaseSchemaName"] = GetColumnAttributeStr (i
+1, FieldIdentifier
.SchemaName
);
508 schemaRow
["BaseCatalogName"] = GetColumnAttributeStr (i
+1, FieldIdentifier
.CatelogName
);
509 schemaRow
["BaseColumnName"] = GetColumnAttributeStr (i
+1, FieldIdentifier
.BaseColumnName
);
510 schemaRow
["DataType"] = col
.DataType
;
511 schemaRow
["IsUnique"] = false;
512 schemaRow
["IsKey"] = DBNull
.Value
;
513 schemaRow
["AllowDBNull"] = GetColumnAttribute (i
+1, FieldIdentifier
.Nullable
) != libodbc
.SQL_NO_NULLS
;
514 schemaRow
["ProviderType"] = (int) col
.OdbcType
;
515 schemaRow
["IsAutoIncrement"] = GetColumnAttribute (i
+1, FieldIdentifier
.AutoUniqueValue
) == libodbc
.SQL_TRUE
;
516 schemaRow
["IsExpression"] = schemaRow
.IsNull ("BaseTableName") || (string) schemaRow
["BaseTableName"] == String
.Empty
;
517 schemaRow
["IsAliased"] = (string) schemaRow
["BaseColumnName"] != (string) schemaRow
["ColumnName"];
518 schemaRow
["IsReadOnly"] = ((bool) schemaRow
["IsExpression"]
519 || GetColumnAttribute (i
+1, FieldIdentifier
.Updatable
) == libodbc
.SQL_ATTR_READONLY
);
521 // FIXME: all of these
522 schemaRow
["IsIdentity"] = false;
523 schemaRow
["IsRowVersion"] = false;
524 schemaRow
["IsHidden"] = false;
525 schemaRow
["IsLong"] = false;
527 // FIXME: according to Brian,
528 // this does not work on MS .NET
529 // however, we need it for Mono
531 // schemaRow.AcceptChanges();
535 DataRow
[] rows
= dataTableSchema
.Select ("BaseTableName <> ''",
536 "BaseCatalogName, BaseSchemaName, BaseTableName ASC");
538 string lastTableName
= String
.Empty
,
539 lastSchemaName
= String
.Empty
,
540 lastCatalogName
= String
.Empty
;
541 string [] keys
= null; // assumed to be sorted.
542 foreach (DataRow row
in rows
) {
543 string tableName
= (string) row
["BaseTableName"];
544 string schemaName
= (string) row
["BaseSchemaName"];
545 string catalogName
= (string) row
["BaseCatalogName"];
547 if (tableName
!= lastTableName
|| schemaName
!= lastSchemaName
548 || catalogName
!= lastCatalogName
)
549 keys
= GetPrimaryKeys (catalogName
, schemaName
, tableName
);
552 Array
.BinarySearch (keys
, (string) row
["BaseColumnName"]) >= 0) {
553 row
["IsKey"] = true;
554 row
["IsUnique"] = true;
555 row
["AllowDBNull"] = false;
556 GetColumn ( ColIndex ( (string) row
["ColumnName"])).AllowDBNull
= false;
558 lastTableName
= tableName
;
559 lastSchemaName
= schemaName
;
560 lastCatalogName
= catalogName
;
562 dataTableSchema
.AcceptChanges ();
564 return (_dataTableSchema
= dataTableSchema
);
569 string GetString (int i
)
571 object ret
= GetValue (i
);
573 if (ret
!= null && ret
.GetType () != typeof (string))
574 return Convert
.ToString (ret
);
576 return (string) GetValue (i
);
580 public TimeSpan
GetTime (int i
)
582 throw new NotImplementedException ();
587 object GetValue (int i
)
590 throw new InvalidOperationException ("The reader is closed.");
591 if (currentRow
== -1)
592 throw new InvalidOperationException ("No data available.");
593 if (i
> cols
.Length
-1 || i
< 0)
594 throw new IndexOutOfRangeException ();
597 int outsize
= 0, bufsize
;
599 OdbcColumn col
= GetColumn (i
);
600 object DataValue
= null;
601 ushort ColIndex
= Convert
.ToUInt16 (i
+ 1);
603 // Check cached values
604 if (col
.Value
== null) {
606 // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
607 switch (col
.OdbcType
) {
610 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref bit_data
, 0, ref outsize
);
611 if (outsize
!= (int) OdbcLengthIndicator
.NullData
)
612 DataValue
= bit_data
== 0 ? "False" : "True";
614 case OdbcType
.Numeric
:
615 case OdbcType
.Decimal
:
617 buffer
= new byte [bufsize
]; // According to sqlext.h, use SQL_CHAR for decimal.
618 // FIXME : use Numeric.
619 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, SQL_C_TYPE
.CHAR
, buffer
, bufsize
, ref outsize
);
621 byte [] temp
= new byte [outsize
];
622 for (int j
= 0; j
< outsize
; j
++)
623 temp
[j
] = buffer
[j
];
624 DataValue
= Decimal
.Parse (Encoding
.Default
.GetString (temp
),
625 CultureInfo
.InvariantCulture
);
628 case OdbcType
.TinyInt
:
629 short short_data
= 0;
630 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref short_data
, 0, ref outsize
);
631 DataValue
= Convert
.ToByte (short_data
);
635 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref int_data
, 0, ref outsize
);
636 DataValue
= int_data
;
639 case OdbcType
.SmallInt
:
641 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref sint_data
, 0, ref outsize
);
642 DataValue
= sint_data
;
645 case OdbcType
.BigInt
:
647 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref long_data
, 0, ref outsize
);
648 DataValue
= long_data
;
652 buffer
= new byte [bufsize
];
653 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, SQL_C_TYPE
.WCHAR
, buffer
, bufsize
, ref outsize
);
654 if (outsize
!= (int) OdbcLengthIndicator
.NullData
)
655 if (!(ret
== OdbcReturn
.SuccessWithInfo
656 && outsize
== (int) OdbcLengthIndicator
.NoTotal
))
657 DataValue
= Encoding
.Unicode
.GetString (buffer
, 0, outsize
);
660 case OdbcType
.NVarChar
:
661 bufsize
= (col
.MaxLength
< 127 ? (col
.MaxLength
*2+1) : 255);
662 buffer
= new byte[bufsize
]; // According to sqlext.h, use SQL_CHAR for both char and varchar
663 StringBuilder sb
= new StringBuilder ();
664 char[] charBuffer
= new char[bufsize
];
665 Decoder unicodeDecoder
= Encoding
.Unicode
.GetDecoder ();
667 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, buffer
, bufsize
, ref outsize
);
668 if (ret
== OdbcReturn
.Error
)
670 // Fix for strance ODBC drivers (like psqlODBC)
671 if (ret
== OdbcReturn
.Success
&& outsize
==-1)
672 ret
= OdbcReturn
.NoData
;
674 if (ret
== OdbcReturn
.Success
|| ret
== OdbcReturn
.SuccessWithInfo
) {
675 if (outsize
>= bufsize
|| outsize
== (int)OdbcLengthIndicator
.NoTotal
)
677 int charCount
= unicodeDecoder
.GetChars (buffer
, 0, outsize
, charBuffer
, 0);
678 string strValue
= new String (charBuffer
, 0, charCount
);
679 sb
.Append (RemoveTrailingNullChar (strValue
));
681 } while (ret
!= OdbcReturn
.NoData
);
682 DataValue
= sb
.ToString ();
686 case OdbcType
.VarChar
:
687 bufsize
= (col
.MaxLength
< 255 ? (col
.MaxLength
+1) : 255);
688 buffer
= new byte[bufsize
]; // According to sqlext.h, use SQL_CHAR for both char and varchar
689 StringBuilder sb1
= new StringBuilder ();
690 charBuffer
= new char[bufsize
];
691 Decoder defaultDecoder
= Encoding
.Default
.GetDecoder();
693 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, buffer
, bufsize
, ref outsize
);
694 if (ret
== OdbcReturn
.Error
)
696 // Fix for strance ODBC drivers (like psqlODBC)
697 if (ret
== OdbcReturn
.Success
&& outsize
==-1)
698 ret
= OdbcReturn
.NoData
;
699 if (ret
== OdbcReturn
.Success
|| ret
== OdbcReturn
.SuccessWithInfo
) {
700 if (outsize
>= bufsize
|| outsize
== (int)OdbcLengthIndicator
.NoTotal
)
701 outsize
= bufsize
- 1;
702 int charCount
= defaultDecoder
.GetChars(buffer
, 0, outsize
, charBuffer
, 0);
703 sb1
.Append(charBuffer
, 0, charCount
);
705 } while (ret
!= OdbcReturn
.NoData
);
706 DataValue
= sb1
.ToString ();
709 float float_data
= 0;
710 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref float_data
, 0, ref outsize
);
711 DataValue
= float_data
;
713 case OdbcType
.Double
:
714 double double_data
= 0;
715 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref double_data
, 0, ref outsize
);
716 DataValue
= double_data
;
718 case OdbcType
.Timestamp
:
719 case OdbcType
.DateTime
:
722 OdbcTimestamp ts_data
= new OdbcTimestamp();
723 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, col
.SqlCType
, ref ts_data
, 0, ref outsize
);
724 if (outsize
!= -1) {// This means SQL_NULL_DATA
725 if (col
.OdbcType
== OdbcType
.Time
) {
726 // libodbc returns value in first three fields for OdbcType.Time
727 DataValue
= new System
.TimeSpan (ts_data
.year
, ts_data
.month
, ts_data
.day
);
729 DataValue
= new DateTime(ts_data
.year
, ts_data
.month
,
730 ts_data
.day
, ts_data
.hour
, ts_data
.minute
,
732 if (ts_data
.fraction
!= 0)
733 DataValue
= ((DateTime
) DataValue
).AddTicks ((long)ts_data
.fraction
/ 100);
737 case OdbcType
.VarBinary
:
738 case OdbcType
.Image
:
739 bufsize
= (col
.MaxLength
< 255 && col
.MaxLength
> 0 ? col
.MaxLength
: 255);
740 buffer
= new byte [bufsize
];
741 ArrayList al
= new ArrayList ();
742 //get the size of data to be returned.
743 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, SQL_C_TYPE
.BINARY
, buffer
, 0, ref outsize
);
744 if (outsize
!= (int) OdbcLengthIndicator
.NullData
) {
746 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, SQL_C_TYPE
.BINARY
, buffer
, bufsize
, ref outsize
);
747 if (ret
== OdbcReturn
.Error
)
749 if (ret
!= OdbcReturn
.NoData
&& outsize
!= -1) {
750 if (outsize
< bufsize
) {
751 byte [] tmparr
= new byte [outsize
];
752 Array
.Copy (buffer
, 0, tmparr
, 0, outsize
);
753 al
.AddRange (tmparr
);
755 al
.AddRange (buffer
);
759 } while (ret
!= OdbcReturn
.NoData
);
761 DataValue
= al
.ToArray (typeof (byte));
763 case OdbcType
.Binary
:
764 bufsize
= col
.MaxLength
;
765 buffer
= new byte [bufsize
];
766 GetBytes (i
, 0, buffer
, 0, bufsize
);
767 ret
= OdbcReturn
.Success
;
772 buffer
= new byte[bufsize
];
773 ret
= libodbc
.SQLGetData (hstmt
, ColIndex
, SQL_C_TYPE
.CHAR
, buffer
, bufsize
, ref outsize
);
774 if (outsize
!= (int) OdbcLengthIndicator
.NullData
)
775 if (! (ret
== OdbcReturn
.SuccessWithInfo
776 && outsize
== (int) OdbcLengthIndicator
.NoTotal
))
777 DataValue
= Encoding
.Default
.GetString (buffer
, 0, outsize
);
781 if ((ret
!=OdbcReturn
.Success
) && (ret
!=OdbcReturn
.SuccessWithInfo
) && (ret
!=OdbcReturn
.NoData
))
782 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, hstmt
);
784 if (outsize
== -1) // This means SQL_NULL_DATA
785 col
.Value
= DBNull
.Value
;
787 col
.Value
= DataValue
;
794 int GetValues (object [] values
)
799 throw new InvalidOperationException ("The reader is closed.");
800 if (currentRow
== -1)
801 throw new InvalidOperationException ("No data available.");
804 for (int i
= 0; i
< values
.Length
; i
++) {
805 if (i
< FieldCount
) {
806 values
[i
] = GetValue (i
);
812 // get number of object instances in array
813 if (values
.Length
< FieldCount
)
814 numValues
= values
.Length
;
815 else if (values
.Length
== FieldCount
)
816 numValues
= FieldCount
;
818 numValues
= FieldCount
;
824 public override IEnumerator
GetEnumerator ()
826 return new DbEnumerator (this);
830 void Dispose (bool disposing
)
836 // dispose managed resources
842 _dataTableSchema
= null;
848 bool IsDBNull (int i
)
850 return (GetValue (i
) is DBNull
);
854 /// Move to the next result set.
860 OdbcReturn ret
= OdbcReturn
.Success
;
861 ret
= libodbc
.SQLMoreResults (hstmt
);
862 if (ret
== OdbcReturn
.Success
) {
864 libodbc
.SQLNumResultCols (hstmt
, ref colcount
);
865 cols
= new OdbcColumn
[colcount
];
866 _dataTableSchema
= null; // force fresh creation
869 return (ret
== OdbcReturn
.Success
);
873 /// Load the next row in the current result set.
875 private bool NextRow ()
877 OdbcReturn ret
= libodbc
.SQLFetch (hstmt
);
878 if (ret
!= OdbcReturn
.Success
)
883 // Clear cached values from last record
884 foreach (OdbcColumn col
in cols
) {
888 return (ret
== OdbcReturn
.Success
);
891 private int GetColumnAttribute (int column
, FieldIdentifier fieldId
)
893 OdbcReturn ret
= OdbcReturn
.Error
;
894 byte [] buffer
= new byte [255];
897 ret
= libodbc
.SQLColAttribute (hstmt
, (short)column
, fieldId
,
898 buffer
, (short)buffer
.Length
,
899 ref outsize
, ref val
);
900 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
901 throw Connection
.CreateOdbcException (
902 OdbcHandleType
.Stmt
, hstmt
);
906 private string GetColumnAttributeStr (int column
, FieldIdentifier fieldId
)
908 OdbcReturn ret
= OdbcReturn
.Error
;
909 byte [] buffer
= new byte [255];
912 ret
= libodbc
.SQLColAttribute (hstmt
, (short)column
, fieldId
,
913 buffer
, (short)buffer
.Length
,
914 ref outsize
, ref val
);
915 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
916 throw Connection
.CreateOdbcException (
917 OdbcHandleType
.Stmt
, hstmt
);
918 string value = string.Empty
;
920 value = Encoding
.Unicode
.GetString (buffer
, 0, outsize
);
924 private string [] GetPrimaryKeys (string catalog
, string schema
, string table
)
926 if (cols
.Length
<= 0)
927 return new string [0];
929 ArrayList keys
= null;
931 keys
= GetPrimaryKeysBySQLPrimaryKey (catalog
, schema
, table
);
932 } catch (OdbcException
) {
934 keys
= GetPrimaryKeysBySQLStatistics (catalog
, schema
, table
);
935 } catch (OdbcException
) {
941 return (string []) keys
.ToArray (typeof (string));
944 private ArrayList
GetPrimaryKeysBySQLPrimaryKey (string catalog
, string schema
, string table
)
946 ArrayList keys
= new ArrayList ();
947 IntPtr handle
= IntPtr
.Zero
;
950 ret
=libodbc
.SQLAllocHandle(OdbcHandleType
.Stmt
,
951 command
.Connection
.hDbc
, ref handle
);
952 if ((ret
!=OdbcReturn
.Success
) && (ret
!=OdbcReturn
.SuccessWithInfo
))
953 throw Connection
.CreateOdbcException (
954 OdbcHandleType
.Dbc
, Connection
.hDbc
);
956 ret
= libodbc
.SQLPrimaryKeys (handle
, catalog
, -3,
957 schema
, -3, table
, -3);
958 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
959 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
962 byte [] primaryKey
= new byte [255];
964 ret
= libodbc
.SQLBindCol (handle
, 4, SQL_C_TYPE
.CHAR
, primaryKey
, primaryKey
.Length
, ref length
);
965 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
966 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
969 ret
= libodbc
.SQLFetch (handle
);
970 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
972 string pkey
= Encoding
.Default
.GetString (primaryKey
, 0, length
);
976 if (handle
!= IntPtr
.Zero
) {
977 ret
= libodbc
.SQLFreeStmt (handle
, libodbc
.SQLFreeStmtOptions
.Close
);
978 if ((ret
!=OdbcReturn
.Success
) && (ret
!=OdbcReturn
.SuccessWithInfo
))
979 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
981 ret
= libodbc
.SQLFreeHandle( (ushort) OdbcHandleType
.Stmt
, handle
);
982 if ((ret
!=OdbcReturn
.Success
) && (ret
!=OdbcReturn
.SuccessWithInfo
))
983 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
989 private unsafe ArrayList
GetPrimaryKeysBySQLStatistics (string catalog
, string schema
, string table
)
991 ArrayList keys
= new ArrayList ();
992 IntPtr handle
= IntPtr
.Zero
;
995 ret
=libodbc
.SQLAllocHandle(OdbcHandleType
.Stmt
,
996 command
.Connection
.hDbc
, ref handle
);
997 if ((ret
!=OdbcReturn
.Success
) && (ret
!=OdbcReturn
.SuccessWithInfo
))
998 throw Connection
.CreateOdbcException (
999 OdbcHandleType
.Dbc
, Connection
.hDbc
);
1001 ret
= libodbc
.SQLStatistics (handle
, catalog
, -3,
1004 libodbc
.SQL_INDEX_UNIQUE
,
1006 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
1007 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
1010 int nonUniqueLength
= 0;
1011 short nonUnique
= libodbc
.SQL_FALSE
;
1012 ret
= libodbc
.SQLBindCol (handle
, 4, SQL_C_TYPE
.SHORT
, ref nonUnique
, sizeof (short), ref nonUniqueLength
);
1013 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
1014 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
1018 byte [] colName
= new byte [255];
1019 ret
= libodbc
.SQLBindCol (handle
, 9, SQL_C_TYPE
.CHAR
, colName
, colName
.Length
, ref length
);
1020 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
1021 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
1024 ret
= libodbc
.SQLFetch (handle
);
1025 if (ret
!= OdbcReturn
.Success
&& ret
!= OdbcReturn
.SuccessWithInfo
)
1027 if (nonUnique
== libodbc
.SQL_TRUE
) {
1028 string pkey
= Encoding
.Default
.GetString (colName
, 0, length
);
1034 if (handle
!= IntPtr
.Zero
) {
1035 ret
= libodbc
.SQLFreeStmt (handle
, libodbc
.SQLFreeStmtOptions
.Close
);
1036 if ((ret
!=OdbcReturn
.Success
) && (ret
!=OdbcReturn
.SuccessWithInfo
))
1037 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
1039 ret
= libodbc
.SQLFreeHandle ((ushort) OdbcHandleType
.Stmt
, handle
);
1040 if ((ret
!=OdbcReturn
.Success
) && (ret
!=OdbcReturn
.SuccessWithInfo
))
1041 throw Connection
.CreateOdbcException (OdbcHandleType
.Stmt
, handle
);
1054 static string RemoveTrailingNullChar (string value)
1056 return value.TrimEnd ('\0');