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
46 public sealed class OdbcDataReader : DbDataReader
48 public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
53 private OdbcCommand command;
55 private int currentRow;
56 private OdbcColumn[] cols;
58 private int _recordsAffected = -1;
60 private DataTable _dataTableSchema;
61 private CommandBehavior behavior;
67 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
69 this.command = command;
70 this.CommandBehavior = behavior;
73 hstmt = command.hStmt;
74 // Init columns array;
76 libodbc.SQLNumResultCols (hstmt, ref colcount);
77 cols = new OdbcColumn [colcount];
81 internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior,
82 int recordAffected) : this (command, behavior)
84 _recordsAffected = recordAffected;
92 private CommandBehavior CommandBehavior {
93 get { return behavior; }
94 set { behavior = value; }
103 return 0; // no nested selects supported
114 throw new InvalidOperationException ("The reader is closed.");
133 object this [string value] {
135 int pos = GetOrdinal (value);
144 object this [int i] {
154 int RecordsAffected {
156 return _recordsAffected;
166 get { throw new NotImplementedException(); }
169 private OdbcConnection Connection {
172 return command.Connection;
181 private int ColIndex (string colname)
184 foreach (OdbcColumn col in cols) {
186 if (col.ColumnName == colname)
188 if (String.Compare (col.ColumnName, colname, true) == 0)
196 // Dynamically load column descriptions as needed.
197 private OdbcColumn GetColumn (int ordinal)
199 if (cols [ordinal] == null) {
201 byte [] colname_buffer = new byte [bufsize];
203 short colname_size = 0;
205 short DecDigits = 0, Nullable = 0, dt = 0;
206 OdbcReturn ret = libodbc.SQLDescribeCol (hstmt, Convert.ToUInt16 (ordinal + 1),
207 colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize,
208 ref DecDigits, ref Nullable);
209 if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
210 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
211 colname = RemoveTrailingNullChar (Encoding.Unicode.GetString (colname_buffer));
212 OdbcColumn c = new OdbcColumn (colname, (SQL_TYPE) dt);
213 c.AllowDBNull = (Nullable != 0);
214 c.Digits = DecDigits;
215 if (c.IsVariableSizeType)
216 c.MaxLength = (int) ColSize;
219 return cols [ordinal];
222 // Load all column descriptions
223 private void GetColumns ()
225 for(int i = 0; i < cols.Length; i++)
235 // FIXME : have to implement output parameter binding
239 this.command.FreeIfNotPrepared ();
241 if ((this.CommandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
242 this.command.Connection.Close ();
248 this.Dispose (false);
256 bool GetBoolean (int i)
258 return (bool) GetValue (i);
267 return Convert.ToByte (GetValue (i));
274 long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
277 throw new InvalidOperationException ("Reader is not open.");
278 if (currentRow == -1)
279 throw new InvalidOperationException ("No data available.");
281 OdbcReturn ret = OdbcReturn.Error;
282 bool copyBuffer = false;
283 int returnVal = 0, outsize = 0;
284 byte [] tbuff = new byte [length+1];
288 ret=libodbc.SQLGetData (hstmt, (ushort) (i + 1), SQL_C_TYPE.BINARY, tbuff, length,
291 if (ret == OdbcReturn.NoData)
294 if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
295 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
297 OdbcException odbcException = null;
298 if ( (ret == OdbcReturn.SuccessWithInfo))
299 odbcException = Connection.CreateOdbcException (
300 OdbcHandleType.Stmt, hstmt);
303 return outsize; //if buffer is null,return length of the field
305 if (ret == OdbcReturn.SuccessWithInfo) {
306 if (outsize == (int) OdbcLengthIndicator.NoTotal)
308 else if (outsize == (int) OdbcLengthIndicator.NullData) {
312 string sqlstate = odbcException.Errors [0].SQLState;
313 //SQLState: String Data, Right truncated
314 if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC)
319 copyBuffer = outsize == -1 ? false : true;
324 if (outsize == (int) OdbcLengthIndicator.NoTotal) {
326 while (tbuff [j] != libodbc.C_NULL) {
327 buffer [bufferIndex + j] = tbuff [j];
332 int read_bytes = Math.Min (outsize, length);
333 for (int j = 0; j < read_bytes; j++)
334 buffer [bufferIndex + j] = tbuff [j];
335 returnVal = read_bytes;
348 throw new NotImplementedException ();
356 long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
359 throw new InvalidOperationException ("The reader is closed.");
360 if (currentRow == -1)
361 throw new InvalidOperationException ("No data available.");
362 if (i < 0 || i >= FieldCount)
363 throw new IndexOutOfRangeException ();
364 throw new NotImplementedException ();
368 [EditorBrowsableAttribute (EditorBrowsableState.Never)]
375 IDataReader GetData (int i)
377 throw new NotImplementedException ();
384 string GetDataTypeName (int i)
387 throw new InvalidOperationException ("The reader is closed.");
388 if (i < 0 || i >= FieldCount)
389 throw new IndexOutOfRangeException ();
390 return GetColumnAttributeStr (i + 1, FieldIdentifier.TypeName);
393 public DateTime GetDate (int i)
395 return GetDateTime (i);
402 DateTime GetDateTime (int i)
404 return (DateTime) GetValue (i);
411 decimal GetDecimal (int i)
413 return (decimal) GetValue (i);
420 double GetDouble (int i)
422 return (double) GetValue (i);
429 Type GetFieldType (int i)
432 throw new InvalidOperationException ("The reader is closed.");
433 return GetColumn (i).DataType;
440 float GetFloat (int i)
442 return (float) GetValue (i);
452 throw new NotImplementedException ();
459 short GetInt16 (int i)
461 return (short) GetValue (i);
470 return (int) GetValue (i);
477 long GetInt64 (int i)
479 return (long) GetValue (i);
486 string GetName (int i)
489 throw new InvalidOperationException ("The reader is closed.");
490 return GetColumn (i).ColumnName;
497 int GetOrdinal (string value)
500 throw new InvalidOperationException ("The reader is closed.");
502 throw new ArgumentNullException ("fieldName");
504 int i = ColIndex (value);
506 throw new IndexOutOfRangeException ();
515 DataTable GetSchemaTable ()
518 throw new InvalidOperationException ("The reader is closed.");
521 // * Map OdbcType to System.Type and assign to DataType.
522 // This will eliminate the need for IsStringType in
525 if (_dataTableSchema != null)
526 return _dataTableSchema;
528 DataTable dataTableSchema = null;
529 // Only Results from SQL SELECT Queries
530 // get a DataTable for schema of the result
531 // otherwise, DataTable is null reference
532 if (cols.Length > 0) {
533 dataTableSchema = new DataTable ();
535 dataTableSchema.Columns.Add ("ColumnName", typeof (string));
536 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
537 dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
538 dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
539 dataTableSchema.Columns.Add ("NumericScale", typeof (int));
540 dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
541 dataTableSchema.Columns.Add ("IsKey", typeof (bool));
542 DataColumn dc = dataTableSchema.Columns["IsKey"];
543 dc.AllowDBNull = true; // IsKey can have a DBNull
544 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
545 dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
546 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
547 dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
548 dataTableSchema.Columns.Add ("DataType", typeof(Type));
549 dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
550 dataTableSchema.Columns.Add ("ProviderType", typeof (int));
551 dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
552 dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
553 dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
554 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
555 dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
556 dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
557 dataTableSchema.Columns.Add ("IsLong", typeof (bool));
558 dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
562 for (int i = 0; i < cols.Length; i += 1 ) {
563 OdbcColumn col=GetColumn(i);
565 schemaRow = dataTableSchema.NewRow ();
566 dataTableSchema.Rows.Add (schemaRow);
568 schemaRow ["ColumnName"] = col.ColumnName;
569 schemaRow ["ColumnOrdinal"] = i;
570 schemaRow ["ColumnSize"] = col.MaxLength;
571 schemaRow ["NumericPrecision"] = GetColumnAttribute (i+1, FieldIdentifier.Precision);
572 schemaRow ["NumericScale"] = GetColumnAttribute (i+1, FieldIdentifier.Scale);
573 schemaRow ["BaseTableName"] = GetColumnAttributeStr (i+1, FieldIdentifier.TableName);
574 schemaRow ["BaseSchemaName"] = GetColumnAttributeStr (i+1, FieldIdentifier.SchemaName);
575 schemaRow ["BaseCatalogName"] = GetColumnAttributeStr (i+1, FieldIdentifier.CatelogName);
576 schemaRow ["BaseColumnName"] = GetColumnAttributeStr (i+1, FieldIdentifier.BaseColumnName);
577 schemaRow ["DataType"] = col.DataType;
578 schemaRow ["IsUnique"] = false;
579 schemaRow ["IsKey"] = DBNull.Value;
580 schemaRow ["AllowDBNull"] = GetColumnAttribute (i+1, FieldIdentifier.Nullable) != libodbc.SQL_NO_NULLS;
581 schemaRow ["ProviderType"] = (int) col.OdbcType;
582 schemaRow ["IsAutoIncrement"] = GetColumnAttribute (i+1, FieldIdentifier.AutoUniqueValue) == libodbc.SQL_TRUE;
583 schemaRow ["IsExpression"] = schemaRow.IsNull ("BaseTableName") || (string) schemaRow ["BaseTableName"] == String.Empty;
584 schemaRow ["IsAliased"] = (string) schemaRow ["BaseColumnName"] != (string) schemaRow ["ColumnName"];
585 schemaRow ["IsReadOnly"] = ((bool) schemaRow ["IsExpression"]
586 || GetColumnAttribute (i+1, FieldIdentifier.Updatable) == libodbc.SQL_ATTR_READONLY);
588 // FIXME: all of these
589 schemaRow ["IsIdentity"] = false;
590 schemaRow ["IsRowVersion"] = false;
591 schemaRow ["IsHidden"] = false;
592 schemaRow ["IsLong"] = false;
594 // FIXME: according to Brian,
595 // this does not work on MS .NET
596 // however, we need it for Mono
598 // schemaRow.AcceptChanges();
602 DataRow [] rows = dataTableSchema.Select ("BaseTableName <> ''",
603 "BaseCatalogName, BaseSchemaName, BaseTableName ASC");
605 string lastTableName = String.Empty,
606 lastSchemaName = String.Empty,
607 lastCatalogName = String.Empty;
608 string [] keys = null; // assumed to be sorted.
609 foreach (DataRow row in rows) {
610 string tableName = (string) row ["BaseTableName"];
611 string schemaName = (string) row ["BaseSchemaName"];
612 string catalogName = (string) row ["BaseCatalogName"];
614 if (tableName != lastTableName || schemaName != lastSchemaName
615 || catalogName != lastCatalogName)
616 keys = GetPrimaryKeys (catalogName, schemaName, tableName);
619 Array.BinarySearch (keys, (string) row ["BaseColumnName"]) >= 0) {
620 row ["IsKey"] = true;
621 row ["IsUnique"] = true;
622 row ["AllowDBNull"] = false;
623 GetColumn ( ColIndex ( (string) row ["ColumnName"])).AllowDBNull = false;
625 lastTableName = tableName;
626 lastSchemaName = schemaName;
627 lastCatalogName = catalogName;
629 dataTableSchema.AcceptChanges ();
631 return (_dataTableSchema = dataTableSchema);
638 string GetString (int i)
640 object ret = GetValue (i);
642 if (ret != null && ret.GetType () != typeof (string))
643 return Convert.ToString (ret);
645 return (string) GetValue (i);
649 public TimeSpan GetTime (int i)
651 throw new NotImplementedException ();
658 object GetValue (int i)
661 throw new InvalidOperationException ("The reader is closed.");
662 if (currentRow == -1)
663 throw new InvalidOperationException ("No data available.");
664 if (i > cols.Length-1 || i < 0)
665 throw new IndexOutOfRangeException ();
668 int outsize = 0, bufsize;
670 OdbcColumn col = GetColumn (i);
671 object DataValue = null;
672 ushort ColIndex = Convert.ToUInt16 (i + 1);
674 // Check cached values
675 if (col.Value == null) {
677 // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
678 switch (col.OdbcType) {
681 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref bit_data, 0, ref outsize);
682 if (outsize != (int) OdbcLengthIndicator.NullData)
683 DataValue = bit_data == 0 ? "False" : "True";
685 case OdbcType.Numeric:
686 case OdbcType.Decimal:
688 buffer = new byte [bufsize]; // According to sqlext.h, use SQL_CHAR for decimal.
689 // FIXME : use Numeric.
690 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
692 byte [] temp = new byte [outsize];
693 for (int j = 0; j < outsize; j++)
694 temp [j] = buffer [j];
695 DataValue = Decimal.Parse (Encoding.Default.GetString (temp),
696 CultureInfo.InvariantCulture);
699 case OdbcType.TinyInt:
700 short short_data = 0;
701 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref short_data, 0, ref outsize);
702 DataValue = Convert.ToByte (short_data);
706 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref int_data, 0, ref outsize);
707 DataValue = int_data;
710 case OdbcType.SmallInt:
712 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref sint_data, 0, ref outsize);
713 DataValue = sint_data;
716 case OdbcType.BigInt:
718 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
719 DataValue = long_data;
723 buffer = new byte [bufsize];
724 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.WCHAR, buffer, bufsize, ref outsize);
725 if (outsize != (int) OdbcLengthIndicator.NullData)
726 if (!(ret == OdbcReturn.SuccessWithInfo
727 && outsize == (int) OdbcLengthIndicator.NoTotal))
728 DataValue = Encoding.Unicode.GetString (buffer, 0, outsize);
731 case OdbcType.NVarChar:
732 bufsize = (col.MaxLength < 127 ? (col.MaxLength*2+1) : 255);
733 buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
734 StringBuilder sb = new StringBuilder ();
735 char[] charBuffer = new char[bufsize];
736 Decoder unicodeDecoder = Encoding.Unicode.GetDecoder ();
738 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
739 if (ret == OdbcReturn.Error)
741 // Fix for strance ODBC drivers (like psqlODBC)
742 if (ret == OdbcReturn.Success && outsize==-1)
743 ret = OdbcReturn.NoData;
745 if (ret == OdbcReturn.Success || ret == OdbcReturn.SuccessWithInfo) {
746 if (outsize >= bufsize || outsize == (int)OdbcLengthIndicator.NoTotal)
748 int charCount = unicodeDecoder.GetChars (buffer, 0, outsize, charBuffer, 0);
749 string strValue = new String (charBuffer, 0, charCount);
750 sb.Append (RemoveTrailingNullChar (strValue));
752 } while (ret != OdbcReturn.NoData);
753 DataValue = sb.ToString ();
757 case OdbcType.VarChar:
758 bufsize = (col.MaxLength < 255 ? (col.MaxLength+1) : 255);
759 buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
760 StringBuilder sb1 = new StringBuilder ();
761 charBuffer = new char[bufsize];
762 Decoder defaultDecoder = Encoding.Default.GetDecoder();
764 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
765 if (ret == OdbcReturn.Error)
767 // Fix for strance ODBC drivers (like psqlODBC)
768 if (ret == OdbcReturn.Success && outsize==-1)
769 ret = OdbcReturn.NoData;
770 if (ret == OdbcReturn.Success || ret == OdbcReturn.SuccessWithInfo) {
771 if (outsize >= bufsize || outsize == (int)OdbcLengthIndicator.NoTotal)
772 outsize = bufsize - 1;
773 int charCount = defaultDecoder.GetChars(buffer, 0, outsize, charBuffer, 0);
774 sb1.Append(charBuffer, 0, charCount);
776 } while (ret != OdbcReturn.NoData);
777 DataValue = sb1.ToString ();
780 float float_data = 0;
781 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref float_data, 0, ref outsize);
782 DataValue = float_data;
784 case OdbcType.Double:
785 double double_data = 0;
786 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref double_data, 0, ref outsize);
787 DataValue = double_data;
789 case OdbcType.Timestamp:
790 case OdbcType.DateTime:
793 OdbcTimestamp ts_data = new OdbcTimestamp();
794 ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref ts_data, 0, ref outsize);
795 if (outsize != -1) {// This means SQL_NULL_DATA
796 if (col.OdbcType == OdbcType.Time) {
797 // libodbc returns value in first three fields for OdbcType.Time
798 DataValue = new System.TimeSpan (ts_data.year, ts_data.month, ts_data.day);
800 DataValue = new DateTime(ts_data.year, ts_data.month,
801 ts_data.day, ts_data.hour, ts_data.minute,
803 if (ts_data.fraction != 0)
804 DataValue = ((DateTime) DataValue).AddTicks ((long)ts_data.fraction / 100);
808 case OdbcType.VarBinary :
809 case OdbcType.Image :
810 bufsize = (col.MaxLength < 255 && col.MaxLength > 0 ? col.MaxLength : 255);
811 buffer= new byte [bufsize];
812 ArrayList al = new ArrayList ();
813 //get the size of data to be returned.
814 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, 0, ref outsize);
815 if (outsize != (int) OdbcLengthIndicator.NullData) {
817 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, bufsize, ref outsize);
818 if (ret == OdbcReturn.Error)
820 if (ret != OdbcReturn.NoData && outsize != -1) {
821 if (outsize < bufsize) {
822 byte [] tmparr = new byte [outsize];
823 Array.Copy (buffer, 0, tmparr, 0, outsize);
824 al.AddRange (tmparr);
826 al.AddRange (buffer);
830 } while (ret != OdbcReturn.NoData);
832 DataValue = al.ToArray (typeof (byte));
834 case OdbcType.Binary :
835 bufsize = col.MaxLength;
836 buffer = new byte [bufsize];
837 GetBytes (i, 0, buffer, 0, bufsize);
838 ret = OdbcReturn.Success;
843 buffer = new byte[bufsize];
844 ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
845 if (outsize != (int) OdbcLengthIndicator.NullData)
846 if (! (ret == OdbcReturn.SuccessWithInfo
847 && outsize == (int) OdbcLengthIndicator.NoTotal))
848 DataValue = Encoding.Default.GetString (buffer, 0, outsize);
852 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo) && (ret!=OdbcReturn.NoData))
853 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
855 if (outsize == -1) // This means SQL_NULL_DATA
856 col.Value = DBNull.Value;
858 col.Value = DataValue;
867 int GetValues (object [] values)
872 throw new InvalidOperationException ("The reader is closed.");
873 if (currentRow == -1)
874 throw new InvalidOperationException ("No data available.");
877 for (int i = 0; i < values.Length; i++) {
878 if (i < FieldCount) {
879 values [i] = GetValue (i);
885 // get number of object instances in array
886 if (values.Length < FieldCount)
887 numValues = values.Length;
888 else if (values.Length == FieldCount)
889 numValues = FieldCount;
891 numValues = FieldCount;
897 void IDisposable.Dispose ()
900 GC.SuppressFinalize (this);
903 IEnumerator IEnumerable.GetEnumerator ()
905 return new DbEnumerator (this);
910 public override IEnumerator GetEnumerator ()
912 return new DbEnumerator (this);
919 void Dispose (bool disposing)
925 // dispose managed resources
931 _dataTableSchema = null;
939 bool IsDBNull (int i)
941 return (GetValue (i) is DBNull);
945 /// Move to the next result set.
953 OdbcReturn ret = OdbcReturn.Success;
954 ret = libodbc.SQLMoreResults (hstmt);
955 if (ret == OdbcReturn.Success) {
957 libodbc.SQLNumResultCols (hstmt, ref colcount);
958 cols = new OdbcColumn [colcount];
959 _dataTableSchema = null; // force fresh creation
962 return (ret == OdbcReturn.Success);
966 /// Load the next row in the current result set.
968 private bool NextRow ()
970 OdbcReturn ret = libodbc.SQLFetch (hstmt);
971 if (ret != OdbcReturn.Success)
976 // Clear cached values from last record
977 foreach (OdbcColumn col in cols) {
981 return (ret == OdbcReturn.Success);
984 private int GetColumnAttribute (int column, FieldIdentifier fieldId)
986 OdbcReturn ret = OdbcReturn.Error;
987 byte [] buffer = new byte [255];
990 ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
991 buffer, (short)buffer.Length,
992 ref outsize, ref val);
993 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
994 throw Connection.CreateOdbcException (
995 OdbcHandleType.Stmt, hstmt);
999 private string GetColumnAttributeStr (int column, FieldIdentifier fieldId)
1001 OdbcReturn ret = OdbcReturn.Error;
1002 byte [] buffer = new byte [255];
1005 ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
1006 buffer, (short)buffer.Length,
1007 ref outsize, ref val);
1008 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1009 throw Connection.CreateOdbcException (
1010 OdbcHandleType.Stmt, hstmt);
1011 string value = string.Empty;
1013 value = Encoding.Unicode.GetString (buffer, 0, outsize);
1017 private string [] GetPrimaryKeys (string catalog, string schema, string table)
1019 if (cols.Length <= 0)
1020 return new string [0];
1022 ArrayList keys = null;
1024 keys = GetPrimaryKeysBySQLPrimaryKey (catalog, schema, table);
1025 } catch (OdbcException) {
1027 keys = GetPrimaryKeysBySQLStatistics (catalog, schema, table);
1028 } catch (OdbcException) {
1034 return (string []) keys.ToArray (typeof (string));
1037 private ArrayList GetPrimaryKeysBySQLPrimaryKey (string catalog, string schema, string table)
1039 ArrayList keys = new ArrayList ();
1040 IntPtr handle = IntPtr.Zero;
1043 ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
1044 command.Connection.hDbc, ref handle);
1045 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1046 throw Connection.CreateOdbcException (
1047 OdbcHandleType.Dbc, Connection.hDbc);
1049 ret = libodbc.SQLPrimaryKeys (handle, catalog, -3,
1050 schema, -3, table, -3);
1051 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1052 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1055 byte [] primaryKey = new byte [255];
1057 ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.CHAR, primaryKey, primaryKey.Length, ref length);
1058 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1059 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1062 ret = libodbc.SQLFetch (handle);
1063 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1065 string pkey = Encoding.Default.GetString (primaryKey, 0, length);
1069 if (handle != IntPtr.Zero) {
1070 ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
1071 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1072 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1074 ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
1075 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1076 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1082 private unsafe ArrayList GetPrimaryKeysBySQLStatistics (string catalog, string schema, string table)
1084 ArrayList keys = new ArrayList ();
1085 IntPtr handle = IntPtr.Zero;
1088 ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
1089 command.Connection.hDbc, ref handle);
1090 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1091 throw Connection.CreateOdbcException (
1092 OdbcHandleType.Dbc, Connection.hDbc);
1094 ret = libodbc.SQLStatistics (handle, catalog, -3,
1097 libodbc.SQL_INDEX_UNIQUE,
1099 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1100 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1103 int nonUniqueLength = 0;
1104 short nonUnique = libodbc.SQL_FALSE;
1105 ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.SHORT, ref nonUnique, sizeof (short), ref nonUniqueLength);
1106 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1107 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1111 byte [] colName = new byte [255];
1112 ret = libodbc.SQLBindCol (handle, 9, SQL_C_TYPE.CHAR, colName, colName.Length, ref length);
1113 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1114 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1117 ret = libodbc.SQLFetch (handle);
1118 if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
1120 if (nonUnique == libodbc.SQL_TRUE) {
1121 string pkey = Encoding.Default.GetString (colName, 0, length);
1127 if (handle != IntPtr.Zero) {
1128 ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
1129 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1130 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1132 ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Stmt, handle);
1133 if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
1134 throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
1149 static string RemoveTrailingNullChar (string value)
1151 return value.TrimEnd ('\0');