2007-09-11 AMC <amc1999@gmail.com>
[mono.git] / mcs / class / System.Data / System.Data.Odbc / OdbcDataReader.cs
index 991b7124ece9962e920407a2645332351f498edb..1359cfe681db013b8a32248ea137588f88d2efec 100644 (file)
@@ -37,15 +37,12 @@ using System.Collections;
 using System.ComponentModel;
 using System.Data;
 using System.Data.Common;
-#if NET_2_0
-using System.Data.ProviderBase;
-#endif // NET_2_0
 using System.Text;
 
 namespace System.Data.Odbc
 {
 #if NET_2_0
-        public sealed class OdbcDataReader : DbDataReaderBase
+        public sealed class OdbcDataReader : DbDataReader
 #else
        public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
 #endif
@@ -57,59 +54,46 @@ namespace System.Data.Odbc
                private int currentRow;
                private OdbcColumn[] cols;
                private IntPtr hstmt;
-#if ONLY_1_1
+               private int _recordsAffected = -1;
+               bool disposed = false;
+               private DataTable _dataTableSchema;
                private CommandBehavior behavior;
-#endif // ONLY_1_1
 
                #endregion
 
                #region Constructors
 
                internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
-#if NET_2_0
-                        : base (behavior)
-#endif // NET_2_0
                {
                        this.command = command;
-#if ONLY_1_1
-                       this.CommandBehavior=behavior;
-#endif // ONLY_1_1
+                       this.CommandBehavior = behavior;
                        open = true;
                        currentRow = -1;
-                       hstmt=command.hStmt;
+                       hstmt = command.hStmt;
                        // Init columns array;
-                       short colcount=0;
-                       libodbc.SQLNumResultCols(hstmt, ref colcount);
-                       cols=new OdbcColumn[colcount];
+                       short colcount = 0;
+                       libodbc.SQLNumResultCols (hstmt, ref colcount);
+                       cols = new OdbcColumn [colcount];
                        GetSchemaTable ();
                }
 
+               internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior,
+                                        int recordAffected) : this (command, behavior)
+               {
+                       _recordsAffected = recordAffected;
+               }
+               
+
                #endregion
 
                #region Properties
 
-#if ONLY_1_1
                 private CommandBehavior CommandBehavior 
                 {
                         get { return behavior; }
                         set { value = behavior; }
                 }
-#endif // ONLY_1_1
                 
-#if NET_2_0
-                [MonoTODO]
-                public override int VisibleFieldCount
-                {
-                        get { throw new NotImplementedException (); }
-                }
-
-                [MonoTODO]
-                protected override bool IsValidRow 
-                {
-                        get { throw new NotImplementedException (); }
-                }
-
-#endif // NET_2_0
                public
 #if NET_2_0
                 override
@@ -164,20 +148,19 @@ namespace System.Data.Odbc
 #if NET_2_0
                 override
 #endif // NET_2_0
-                object this[int index] {
+                object this [int index] {
                        get {
                                return (object) GetValue (index);
                        }
                }
 
-                [MonoTODO]
                public
 #if NET_2_0
                 override
 #endif // NET_2_0
                 int RecordsAffected {
                        get {
-                               return -1;
+                               return _recordsAffected;
                        }
                }
 
@@ -194,9 +177,9 @@ namespace System.Data.Odbc
 
                #region Methods
                
-               private int ColIndex(string colname)
+               private int ColIndex (string colname)
                {
-                       int i=0;
+                       int i = 0;
                        foreach (OdbcColumn col in cols)
                        {
                                if (col != null) {
@@ -212,31 +195,30 @@ namespace System.Data.Odbc
                }
 
                // Dynamically load column descriptions as needed.
-               private OdbcColumn GetColumn(int ordinal)
+               private OdbcColumn GetColumn (int ordinal)
                {
-                       if (cols[ordinal]==null)
-                       {
-                               short bufsize=255;
-                               byte[] colname_buffer=new byte[bufsize];
+                       if (cols [ordinal] == null) {
+                               short bufsize = 255;
+                               byte [] colname_buffer = new byte [bufsize];
                                string colname;
-                               short colname_size=0;
-                               uint ColSize=0;
-                               short DecDigits=0, Nullable=0, dt=0;
-                               OdbcReturn ret=libodbc.SQLDescribeCol(hstmt, Convert.ToUInt16(ordinal+1), 
-                                       colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize, 
-                                       ref DecDigits, ref Nullable);
-                               if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
-                                       throw new OdbcException(new OdbcError("SQLDescribeCol",OdbcHandleType.Stmt,hstmt));
-                               colname=System.Text.Encoding.Default.GetString(colname_buffer);
-                               colname=colname.Replace((char) 0,' ').Trim();
-                               OdbcColumn c=new OdbcColumn(colname, (SQL_TYPE) dt);
-                               c.AllowDBNull=(Nullable!=0);
-                               c.Digits=DecDigits;
-                               if (c.IsStringType)
-                                       c.MaxLength=(int)ColSize;
-                               cols[ordinal]=c;
+                               short colname_size = 0;
+                               uint ColSize = 0;
+                               short DecDigits = 0, Nullable = 0, dt = 0;
+                               OdbcReturn ret = libodbc.SQLDescribeCol (hstmt, Convert.ToUInt16 (ordinal + 1), 
+                                                                        colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize, 
+                                                                        ref DecDigits, ref Nullable);
+                               if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
+                                       throw new OdbcException (new OdbcError ("SQLDescribeCol", OdbcHandleType.Stmt, hstmt));
+                               colname = System.Text.Encoding.Default.GetString (colname_buffer);
+                               colname = colname.Replace ((char) 0, ' ').Trim ();
+                               OdbcColumn c = new OdbcColumn (colname, (SQL_TYPE) dt);
+                               c.AllowDBNull = (Nullable != 0);
+                               c.Digits = DecDigits;
+                               if (c.IsVariableSizeType)
+                                       c.MaxLength = (int) ColSize;
+                               cols [ordinal] = c;
                        }
-                       return cols[ordinal];
+                       return cols [ordinal];
                }
 
                public
@@ -246,25 +228,19 @@ namespace System.Data.Odbc
                 void Close ()
                {
                        // FIXME : have to implement output parameter binding
-                       OdbcReturn ret = libodbc.SQLFreeStmt (hstmt, libodbc.SQLFreeStmtOptions.Close);
-                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
-                               throw new OdbcException(new OdbcError("SQLCloseCursor",OdbcHandleType.Stmt,hstmt));
-       
                        open = false;
                        currentRow = -1;
 
-                       ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, hstmt);\r
-                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
-                               throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,hstmt));
+                       this.command.FreeIfNotPrepared ();
 
-                        if ((this.CommandBehavior & CommandBehavior.CloseConnection)==CommandBehavior.CloseConnection)
-                               this.command.Connection.Close();
+                       if ((this.CommandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection) {
+                               this.command.Connection.Close ();
+                       }
                }
 
                ~OdbcDataReader ()
                {
-                       if (open)
-                               Close ();
+                       this.Dispose (false);
                }
 
                public 
@@ -298,7 +274,7 @@ namespace System.Data.Odbc
 
                         length = buffer == null ? 0 : length;
                         ret=libodbc.SQLGetData (hstmt, (ushort) (ordinal+1), SQL_C_TYPE.BINARY, tbuff, length, 
-                                        ref outsize);
+                                               ref outsize);
 
                         if (ret == OdbcReturn.NoData)
                                 return 0;
@@ -364,10 +340,12 @@ namespace System.Data.Odbc
 
                [MonoTODO]
                [EditorBrowsableAttribute (EditorBrowsableState.Never)]
-               public 
+#if ONLY_1_1
+               public
+#endif
 #if NET_2_0
                new
-#endif // NET_2_0
+#endif
                IDataReader GetData (int ordinal)
                {
                        throw new NotImplementedException ();
@@ -379,11 +357,11 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                string GetDataTypeName (int index)
                {
-                       return GetColumn(index).OdbcType.ToString();
+                       return GetColumn (index).OdbcType.ToString ();
                }
 
-               public DateTime GetDate(int ordinal) {
-                       return GetDateTime(ordinal);
+               public DateTime GetDate (int ordinal) {
+                       return GetDateTime (ordinal);
                }
 
                public 
@@ -392,17 +370,16 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                DateTime GetDateTime (int ordinal)
                {
-                       return (DateTime) GetValue(ordinal);
+                       return (DateTime) GetValue (ordinal);
                }
 
-               [MonoTODO]
                public 
 #if NET_2_0
                override
 #endif // NET_2_0
                decimal GetDecimal (int ordinal)
                {
-                       throw new NotImplementedException ();
+                       return (decimal) GetValue (ordinal);
                }
 
                public 
@@ -411,7 +388,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                double GetDouble (int ordinal)
                {
-                       return (double) GetValue(ordinal);
+                       return (double) GetValue (ordinal);
                }
 
                public 
@@ -429,7 +406,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                float GetFloat (int ordinal)
                {
-                       return (float) GetValue(ordinal);
+                       return (float) GetValue (ordinal);
                }
 
                [MonoTODO]
@@ -448,7 +425,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                short GetInt16 (int ordinal)
                {
-                       return (short) GetValue(ordinal);
+                       return (short) GetValue (ordinal);
                }
 
                public 
@@ -457,7 +434,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                int GetInt32 (int ordinal)
                {
-                       return (int) GetValue(ordinal);
+                       return (int) GetValue (ordinal);
                }
 
                public 
@@ -466,7 +443,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                long GetInt64 (int ordinal)
                {
-                       return (long) GetValue(ordinal);
+                       return (long) GetValue (ordinal);
                }
 
                public 
@@ -503,17 +480,16 @@ namespace System.Data.Odbc
                         // * Map OdbcType to System.Type and assign to DataType.
                         //   This will eliminate the need for IsStringType in
                         //   OdbcColumn.
-                        // * Cache this DataTable so that it is not contacting 
-                        //   datasource everytime for the same result set.
 
+                       if (_dataTableSchema != null)
+                               return _dataTableSchema;
+                       
                        DataTable dataTableSchema = null;
                        // Only Results from SQL SELECT Queries 
                        // get a DataTable for schema of the result
                        // otherwise, DataTable is null reference
                        if(cols.Length > 0) 
                        {
-                                string [] keys = GetPrimaryKeys ();
-                               
                                dataTableSchema = new DataTable ();
                                
                                dataTableSchema.Columns.Add ("ColumnName", typeof (string));
@@ -542,70 +518,82 @@ namespace System.Data.Odbc
                                dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
 
                                DataRow schemaRow;
-                                                               
+
                                for (int i = 0; i < cols.Length; i += 1 ) 
                                {
+                                       string baseTableName = String.Empty;
+                                       bool isKey = false;
                                        OdbcColumn col=GetColumn(i);
 
                                        schemaRow = dataTableSchema.NewRow ();
                                        dataTableSchema.Rows.Add (schemaRow);
                                                                                
-                                       schemaRow["ColumnName"] = col.ColumnName;
-                                       schemaRow["ColumnOrdinal"] = i;
-                                       
-                                       schemaRow["ColumnSize"] = col.MaxLength;
-                                       schemaRow["NumericPrecision"] = GetColumnAttribute (i+1, FieldIdentifier.Precision);
-                                       schemaRow["NumericScale"] = GetColumnAttribute (i+1, FieldIdentifier.Scale);
-
-                                       schemaRow["IsUnique"] = false;
-                                       schemaRow["IsKey"] = DBNull.Value;
-                                       
-                                        for (int j=0; j < keys.Length; j++) {
-                                                if (keys [j] == col.ColumnName) {
-                                                        schemaRow ["IsUnique"] = true;
-                                                        schemaRow ["IsKey"] = true;
-                                                }
-                                        }
-
-                                        schemaRow["BaseCatalogName"] = "";
-                                        schemaRow["BaseColumnName"] = "";
-                                        schemaRow["BaseSchemaName"] = "";
-                                        schemaRow["BaseTableName"] = "";
-
-                                        try {
-                                                schemaRow["BaseColumnName"] = GetColumnAttributeStr (i+1, FieldIdentifier.BaseColumnName);
-                                                schemaRow["BaseTableName"] = GetColumnAttributeStr (i+1, FieldIdentifier.BaseTableName);
-                                        } catch (Exception e) {
-                                                // ignore these properties as they are optional.
-                                        }
-                                        
-
+                                       schemaRow ["ColumnName"]        = col.ColumnName;
+                                       schemaRow ["ColumnOrdinal"]     = i;
+                                       schemaRow ["ColumnSize"]        = col.MaxLength;
+                                       schemaRow ["NumericPrecision"]  = GetColumnAttribute (i+1, FieldIdentifier.Precision);
+                                       schemaRow ["NumericScale"]      = GetColumnAttribute (i+1, FieldIdentifier.Scale);
+                                       schemaRow ["BaseTableName"]     = GetColumnAttributeStr (i+1, FieldIdentifier.TableName);
+                                       schemaRow ["BaseSchemaName"]    = GetColumnAttributeStr (i+1, FieldIdentifier.SchemaName);
+                                       schemaRow ["BaseCatalogName"]   = GetColumnAttributeStr (i+1, FieldIdentifier.CatelogName);
+                                       schemaRow ["BaseColumnName"]    = GetColumnAttributeStr (i+1, FieldIdentifier.BaseColumnName);
+                                       schemaRow ["DataType"]          = col.DataType;
+                                       schemaRow ["IsUnique"]          = false;
+                                       schemaRow ["IsKey"]             = DBNull.Value;
+                                       schemaRow ["AllowDBNull"]       = GetColumnAttribute (i+1, FieldIdentifier.Nullable) != libodbc.SQL_NO_NULLS;
+                                       schemaRow ["ProviderType"]      = (int) col.OdbcType;
+                                       schemaRow ["IsAutoIncrement"]   = GetColumnAttribute (i+1, FieldIdentifier.AutoUniqueValue) == libodbc.SQL_TRUE;
+                                       schemaRow ["IsExpression"]      = schemaRow.IsNull ("BaseTableName") || (string) schemaRow ["BaseTableName"] == String.Empty;
+                                       schemaRow ["IsAliased"]         = (string) schemaRow ["BaseColumnName"] != (string) schemaRow ["ColumnName"];
+                                       schemaRow ["IsReadOnly"]        = ((bool) schemaRow ["IsExpression"]
+                                                                          || GetColumnAttribute (i+1, FieldIdentifier.Updatable) == libodbc.SQL_ATTR_READONLY);
+
+                                       // FIXME: all of these
+                                       schemaRow ["IsIdentity"]        = false;
+                                       schemaRow ["IsRowVersion"]      = false;
+                                       schemaRow ["IsHidden"]          = false;
+                                       schemaRow ["IsLong"]            = false;
 
-                                       schemaRow["DataType"] = col.DataType;
-
-                                       schemaRow["AllowDBNull"] = col.AllowDBNull;
-                                       
-                                       schemaRow["ProviderType"] = (int) col.OdbcType;
-                                       // TODO: all of these
-                                       schemaRow["IsAliased"] = false;
-                                       schemaRow["IsExpression"] = false;
-                                       schemaRow["IsIdentity"] = false;
-                                       schemaRow["IsAutoIncrement"] = GetColumnAttribute (i+1, FieldIdentifier.AutoUniqueValue) == 1;
-                                       schemaRow["IsRowVersion"] = false;
-                                       schemaRow["IsHidden"] = false;
-                                       schemaRow["IsLong"] = false;
-                                       schemaRow["IsReadOnly"] = false;
                                        
                                        // FIXME: according to Brian, 
                                        // this does not work on MS .NET
                                        // however, we need it for Mono 
                                        // for now
-                                       schemaRow.AcceptChanges();
+                                       // schemaRow.AcceptChanges();
                                        
                                }
-                                dataTableSchema.AcceptChanges();
+
+                               // set primary keys
+                               DataRow [] rows = dataTableSchema.Select ("BaseTableName <> ''",
+                                                                         "BaseCatalogName, BaseSchemaName, BaseTableName ASC");
+
+                               string lastTableName = String.Empty,
+                                       lastSchemaName = String.Empty,
+                                       lastCatalogName = String.Empty;
+                               string [] keys = null; // assumed to be sorted.
+                               foreach (DataRow row in rows) {
+                                       string tableName = (string) row ["BaseTableName"];
+                                       string schemaName = (string) row ["BaseSchemaName"];
+                                       string catalogName = (string) row ["BaseCatalogName"];
+
+                                       if (tableName != lastTableName || schemaName != lastSchemaName
+                                           || catalogName != lastCatalogName)
+                                               keys = GetPrimaryKeys (catalogName, schemaName, tableName);
+                               
+                                       if (keys != null &&
+                                           Array.BinarySearch (keys, (string) row ["BaseColumnName"]) >= 0) {
+                                               row ["IsKey"] = true;
+                                               row ["IsUnique"] = true;
+                                               row ["AllowDBNull"] = false;
+                                               GetColumn ( ColIndex ( (string) row ["ColumnName"])).AllowDBNull = false;
+                                       }
+                                       lastTableName = tableName;
+                                       lastSchemaName = schemaName;
+                                       lastCatalogName = catalogName;
+                               }
+                               dataTableSchema.AcceptChanges();
                        }
-                        return dataTableSchema;
+                        return (_dataTableSchema = dataTableSchema);
                }
 
                public 
@@ -614,7 +602,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                string GetString (int ordinal)
                {
-                       return (string) GetValue(ordinal);
+                       return (string) GetValue (ordinal);
                }
 
                [MonoTODO]
@@ -632,117 +620,166 @@ namespace System.Data.Odbc
                        if (currentRow == -1)
                                throw new IndexOutOfRangeException ();
 
-                       if (ordinal>cols.Length-1 || ordinal<0)
+                       if (ordinal > cols.Length-1 || ordinal < 0)
                                throw new IndexOutOfRangeException ();
 
                        OdbcReturn ret;
-                       int outsize=0, bufsize;
+                       int outsize = 0, bufsize;
                        byte[] buffer;
-                       OdbcColumn col=GetColumn(ordinal);
-                       object DataValue=null;
-                       ushort ColIndex=Convert.ToUInt16(ordinal+1);
+                       OdbcColumn col = GetColumn (ordinal);
+                       object DataValue = null;
+                       ushort ColIndex = Convert.ToUInt16 (ordinal+1);
 
                        // Check cached values
-                       if (col.Value==null)
-                       {
+                       if (col.Value == null) {
                                 // odbc help file
                                // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
-                               switch (col.OdbcType)
-                               {
-                                       case OdbcType.Numeric:
-                                       case OdbcType.Decimal:
-                                               bufsize=50;
-                                               buffer=new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for decimal. 
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
-                                               if (outsize!=-1) {
-                                                       byte[] temp = new byte[outsize];
-                                                       for (int i=0;i<outsize;i++)
-                                                               temp[i]=buffer[i];
-                                                       DataValue=Decimal.Parse(System.Text.Encoding.Default.GetString(temp));
+                               switch (col.OdbcType) {
+                               case OdbcType.Bit:
+                                       short bit_data = 0;
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref bit_data, 0, ref outsize);
+                                       if (outsize != (int) OdbcLengthIndicator.NullData)
+                                               DataValue = bit_data == 0 ? "False" : "True";
+                                       break;
+                               case OdbcType.Numeric:
+                               case OdbcType.Decimal:
+                                       bufsize = 50;
+                                       buffer = new byte [bufsize];  // According to sqlext.h, use SQL_CHAR for decimal.
+                                       // FIXME : use Numeric.
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
+                                       if (outsize!=-1) {
+                                               byte [] temp = new byte [outsize];
+                                               for (int i = 0;i<outsize;i++)
+                                                       temp[i] = buffer[i];
+                                               DataValue = Decimal.Parse(System.Text.Encoding.Default.GetString(temp));
+                                       }
+                                       break;
+                               case OdbcType.TinyInt:
+                                       short short_data = 0;
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref short_data, 0, ref outsize);
+                                       DataValue = System.Convert.ToByte(short_data);
+                                       break;
+                               case OdbcType.Int:
+                                       int int_data = 0;
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref int_data, 0, ref outsize);
+                                       DataValue = int_data;
+                                       break;
+
+                               case OdbcType.SmallInt:
+                                       short sint_data = 0;
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref sint_data, 0, ref outsize);
+                                       DataValue = sint_data;
+                                       break;
+
+                               case OdbcType.BigInt:
+                                       long long_data = 0;
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
+                                       DataValue = long_data;
+                                       break;
+                               case OdbcType.NText:
+                               case OdbcType.NVarChar:
+                                       bufsize = (col.MaxLength < 127 ? (col.MaxLength*2+1) : 255);
+                                       buffer = new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for both char and varchar
+                                       StringBuilder sb = new StringBuilder ();
+                                       do { 
+                                               ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
+                                               if (ret == OdbcReturn.Error)
+                                                       break;
+                                               if (ret != OdbcReturn.NoData && outsize!=-1) {
+                                                       if (outsize < bufsize)
+                                                               sb.Append (System.Text.Encoding.Unicode.GetString(buffer,0,outsize));
+                                                       else
+                                                               sb.Append (System.Text.Encoding.Unicode.GetString(buffer,0,bufsize));
                                                }
-                                               break;
-                                       case OdbcType.TinyInt:
-                                               short short_data=0;
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref short_data, 0, ref outsize);
-                                               DataValue=System.Convert.ToByte(short_data);
-                                               break;
-                                       case OdbcType.Int:
-                                               int int_data=0;
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref int_data, 0, ref outsize);
-                                               DataValue=int_data;
-                                               break;
-
-                                       case OdbcType.SmallInt:
-                                                short sint_data=0;
-                                                ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref sint_data, 0, ref outsize);
-                                                DataValue=sint_data;
-                                                break;
-
-                                       case OdbcType.BigInt:
-                                               long long_data=0;
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
-                                               DataValue=long_data;
-                                               break;
-                                       case OdbcType.NVarChar:
-                                               bufsize=col.MaxLength*2+1; // Unicode is double byte
-                                               buffer=new byte[bufsize];
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
-                                               if (outsize!=-1)
-                                                       DataValue=System.Text.Encoding.Unicode.GetString(buffer,0,outsize);
-                                               break;
-                                       case OdbcType.VarChar:
-                                               bufsize=col.MaxLength+1;
-                                               buffer=new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for both char and varchar
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
-                                               if (outsize!=-1)
-                                                       DataValue=System.Text.Encoding.Default.GetString(buffer,0,outsize);
-                                               break;
-                                       case OdbcType.Real:
-                                               float float_data=0;
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref float_data, 0, ref outsize);
-                                               DataValue=float_data;
-                                               break;
-                                        case OdbcType.Double:
-                                               double double_data=0;
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref double_data, 0, ref outsize);
-                                               DataValue=double_data;
-                                               break;
-                                       case OdbcType.Timestamp:
-                                       case OdbcType.DateTime:
-                                       case OdbcType.Date:
-                                       case OdbcType.Time:
-                                               OdbcTimestamp ts_data=new OdbcTimestamp();
-                                                ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref ts_data, 0, ref outsize);
-                                                if (outsize!=-1) // This means SQL_NULL_DATA 
-                                                       DataValue=new DateTime(ts_data.year,ts_data.month,ts_data.day,ts_data.hour,
-                                                               ts_data.minute,ts_data.second,Convert.ToInt32(ts_data.fraction));
-                                               break;
-                                        case OdbcType.Binary :
-                                        case OdbcType.Image :
-                                                bufsize = col.MaxLength + 1;
-                                                buffer = new byte [bufsize];
-                                                long read = GetBytes (ordinal, 0, buffer, 0, bufsize);
-                                                ret = OdbcReturn.Success;
-                                                DataValue = buffer;
-                                                break;
-                                       default:
-                                               bufsize=255;
-                                               buffer=new byte[bufsize];
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
-                                                if (outsize != (int) OdbcLengthIndicator.NullData)
-                                                    if (! (ret == OdbcReturn.SuccessWithInfo
-                                                        && outsize == (int) OdbcLengthIndicator.NoTotal))
-                                                            DataValue=System.Text.Encoding.Default.GetString(buffer, 0, outsize);
-                                               break;
+                                       } while (ret != OdbcReturn.NoData);
+                                       DataValue = sb.ToString ();
+                                       break;
+                               case OdbcType.Text:
+                               case OdbcType.VarChar:
+                                       bufsize = (col.MaxLength < 255 ? (col.MaxLength+1) : 255);
+                                       buffer = new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for both char and varchar
+                                       StringBuilder sb1 = new StringBuilder ();
+                                       do { 
+                                               ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
+                                               if (ret == OdbcReturn.Error)
+                                                       break;
+                                               if (ret != OdbcReturn.NoData && outsize!=-1) {
+                                                       if (outsize < bufsize)
+                                                               sb1.Append (System.Text.Encoding.Default.GetString(buffer,0,outsize));
+                                                       else
+                                                               sb1.Append (System.Text.Encoding.Default.GetString (buffer, 0, bufsize - 1));
+                                               }
+                                       } while (ret != OdbcReturn.NoData);
+                                       DataValue = sb1.ToString ();
+                                       break;
+                               case OdbcType.Real:
+                                       float float_data = 0;
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref float_data, 0, ref outsize);
+                                       DataValue = float_data;
+                                       break;
+                               case OdbcType.Double:
+                                       double double_data = 0;
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref double_data, 0, ref outsize);
+                                       DataValue = double_data;
+                                       break;
+                               case OdbcType.Timestamp:
+                               case OdbcType.DateTime:
+                               case OdbcType.Date:
+                               case OdbcType.Time:
+                                       OdbcTimestamp ts_data = new OdbcTimestamp();
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref ts_data, 0, ref outsize);
+                                       if (outsize!=-1) {// This means SQL_NULL_DATA
+                                               DataValue = new DateTime(ts_data.year,ts_data.month,ts_data.day,ts_data.hour,
+                                                                      ts_data.minute,ts_data.second);
+                                               if (ts_data.fraction != 0)
+                                                       DataValue = ((DateTime) DataValue).AddTicks ((long)ts_data.fraction / 100);
+                                       }
+                                       break;
+                               case OdbcType.VarBinary :
+                               case OdbcType.Image :
+                                       bufsize =  (col.MaxLength < 255 ? col.MaxLength : 255);
+                                       buffer= new byte [bufsize];
+                                       ArrayList al = new ArrayList ();
+                                       do { 
+                                               ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, bufsize, ref outsize);
+                                               if (ret == OdbcReturn.Error)
+                                                       break;
+                                               if (ret != OdbcReturn.NoData && outsize!=-1) {
+                                                       if (outsize < bufsize) {
+                                                               byte[] tmparr = new byte [outsize];
+                                                               Array.Copy (buffer, 0, tmparr, 0, outsize);
+                                                               al.AddRange (tmparr);
+                                                       } else
+                                                               al.AddRange (buffer);
+                                               }
+                                       } while (ret != OdbcReturn.NoData);
+                                       DataValue = al.ToArray (typeof (byte));
+                                       break;
+                               case OdbcType.Binary :
+                                       bufsize = col.MaxLength;
+                                       buffer = new byte [bufsize];
+                                       long read = GetBytes (ordinal, 0, buffer, 0, bufsize);
+                                       ret = OdbcReturn.Success;
+                                       DataValue = buffer;
+                                       break;
+                               default:
+                                       bufsize = 255;
+                                       buffer = new byte[bufsize];
+                                       ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
+                                       if (outsize != (int) OdbcLengthIndicator.NullData)
+                                               if (! (ret == OdbcReturn.SuccessWithInfo
+                                                      && outsize == (int) OdbcLengthIndicator.NoTotal))
+                                                       DataValue = System.Text.Encoding.Default.GetString(buffer, 0, outsize);
+                                       break;
                                }
 
-                               if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                               if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo) && (ret!=OdbcReturn.NoData)
                                        throw new OdbcException(new OdbcError("SQLGetData",OdbcHandleType.Stmt,hstmt));
 
                                if (outsize==-1) // This means SQL_NULL_DATA 
-                                       col.Value=DBNull.Value;
+                                       col.Value = DBNull.Value;
                                else
-                                       col.Value=DataValue;
+                                       col.Value = DataValue;
                        }
                        return col.Value;
                }
@@ -758,7 +795,7 @@ namespace System.Data.Odbc
                        // copy values
                        for (int i = 0; i < values.Length; i++) {
                                if (i < FieldCount) {
-                                       values[i] = GetValue(i);
+                                       values[i] = GetValue (i);
                                }
                                else {
                                        values[i] = null;
@@ -778,22 +815,44 @@ namespace System.Data.Odbc
 
 #if ONLY_1_1
 
-                [MonoTODO]
-               IDataReader IDataRecord.GetData (int ordinal)
+               void IDisposable.Dispose ()
                {
-                       throw new NotImplementedException ();
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
                }
 
-               [MonoTODO]
-               void IDisposable.Dispose ()
+               IEnumerator IEnumerable.GetEnumerator ()
                {
+                       return new DbEnumerator (this);
                }
+#endif // ONLY_1_1
 
-                IEnumerator IEnumerable.GetEnumerator ()
+               
+#if NET_2_0
+                public override IEnumerator GetEnumerator ()
                {
                        return new DbEnumerator (this);
                }
-#endif // ONLY_1_1
+#endif
+
+#if NET_2_0
+               protected override
+#endif
+               void Dispose (bool disposing)
+               {
+                       if (disposed)
+                               return;
+
+                       if (disposing) {
+                               // dispose managed resources
+                               Close ();
+                       }
+
+                       command = null;
+                       cols = null;
+                       _dataTableSchema = null;
+                       disposed = true;
+               }
 
                public
 #if NET_2_0
@@ -801,7 +860,7 @@ namespace System.Data.Odbc
 #endif // NET_2_0
                 bool IsDBNull (int ordinal)
                {
-                       return (GetValue(ordinal) is DBNull);
+                       return (GetValue (ordinal) is DBNull);
                }
 
                /// <remarks>
@@ -819,6 +878,7 @@ namespace System.Data.Odbc
                                short colcount = 0;
                                libodbc.SQLNumResultCols (hstmt, ref colcount);
                                cols = new OdbcColumn [colcount];
+                               _dataTableSchema = null; // force fresh creation
                                GetSchemaTable ();
                        }       
                        return (ret==OdbcReturn.Success);
@@ -827,7 +887,7 @@ namespace System.Data.Odbc
                /// <remarks>
                ///     Load the next row in the current result set.
                /// </remarks>
-               public bool NextRow ()
+               private bool NextRow ()
                {
                        OdbcReturn ret=libodbc.SQLFetch (hstmt);
                        if (ret != OdbcReturn.Success)
@@ -849,10 +909,10 @@ namespace System.Data.Odbc
                 {
                         OdbcReturn ret = OdbcReturn.Error;
                         byte [] buffer = new byte [255];
-                        int outsize = 0;
+                        short outsize = 0;
                         int val = 0;
-                        ret = libodbc.SQLColAttribute (hstmt, column, fieldId, 
-                                                       buffer, buffer.Length, 
+                        ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId, 
+                                                       buffer, (short)buffer.Length, 
                                                        ref outsize, ref val);
                         if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
                                 throw new OdbcException (new OdbcError ("SQLColAttribute",
@@ -867,10 +927,10 @@ namespace System.Data.Odbc
                 {
                         OdbcReturn ret = OdbcReturn.Error;
                         byte [] buffer = new byte [255];
-                        int outsize = 0;
+                        short outsize = 0;
                         int val = 0;
-                        ret = libodbc.SQLColAttribute (hstmt, column, fieldId, 
-                                                       buffer, buffer.Length, 
+                        ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId, 
+                                                       buffer, (short)buffer.Length, 
                                                        ref outsize, ref val);
                         if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
                                 throw new OdbcException (new OdbcError ("SQLColAttribute",
@@ -883,48 +943,120 @@ namespace System.Data.Odbc
                         return value;
                 }
 
-                private string [] GetPrimaryKeys ()
+                private string [] GetPrimaryKeys (string catalog, string schema, string table)
                 {
                         if (cols.Length <= 0)
                                 return new string [0];
 
-                        string [] keys = new string [cols.Length];
-                        IntPtr handle = IntPtr.Zero;
-                        OdbcReturn ret = OdbcReturn.Error;                  
+                       ArrayList keys = null;
                         try {
-                                ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, 
-                                                           command.Connection.hDbc, ref handle);
+                               keys = GetPrimaryKeysBySQLPrimaryKey (catalog, schema, table);
+                        } catch (OdbcException){
+                               try {
+                                       keys = GetPrimaryKeysBySQLStatistics (catalog, schema, table);
+                               } catch (OdbcException) {
+                               }
+                        }
+                       if (keys == null)
+                               return null;
+                       keys.Sort ();
+                       return (string []) keys.ToArray (typeof (string));
+                }
+
+               private ArrayList GetPrimaryKeysBySQLPrimaryKey (string catalog, string schema, string table)
+               {
+                       ArrayList keys = new ArrayList ();
+                       IntPtr handle = IntPtr.Zero;
+                       OdbcReturn ret;
+                       try {
+                               ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, 
+                                                          command.Connection.hDbc, ref handle);
                                if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
                                        throw new OdbcException(new OdbcError("SQLAllocHandle",
-                                                                              OdbcHandleType.Dbc,
-                                                                              command.Connection.hDbc));
-
-                                string tableName   = GetColumnAttributeStr (1, FieldIdentifier.TableName);
-                                string schemaName  = GetColumnAttributeStr (1, FieldIdentifier.SchemaName);
-                                string catalogName = GetColumnAttributeStr (1, FieldIdentifier.CatelogName);
-                                ret = libodbc.SQLPrimaryKeys (handle, catalogName, -3,  
-                                                              schemaName, -3, 
-                                                              tableName, -3);
-                                if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
-                                        throw new OdbcException (new OdbcError ("SQLPrimaryKeys", OdbcHandleType.Stmt, handle));
-                        
-                                int length = 0;
-                                byte [] primaryKey = new byte [255];
+                                                                             OdbcHandleType.Dbc,
+                                                                             command.Connection.hDbc));
+
+                               ret = libodbc.SQLPrimaryKeys (handle, catalog, -3,  
+                                                             schema, -3, 
+                                                             table, -3);
+                               if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                       throw new OdbcException (new OdbcError ("SQLPrimaryKeys", OdbcHandleType.Stmt, handle));
                         
-                                ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.CHAR, primaryKey, primaryKey.Length, ref length);
-                                if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
-                                        throw new OdbcException (new OdbcError ("SQLBindCol", OdbcHandleType.Stmt, handle));
+                               int length = 0;
+                               byte [] primaryKey = new byte [255];
                         
-                                int i = 0;                              
-                                while (true) {
-                                        ret = libodbc.SQLFetch (handle);
-                                        if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
-                                                break;
-                                        string pkey = Encoding.Default.GetString (primaryKey);
-                                        keys [i++] = pkey;
+                               ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.CHAR, primaryKey, primaryKey.Length, ref length);
+                               if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                       throw new OdbcException (new OdbcError ("SQLBindCol", OdbcHandleType.Stmt, handle));
+
+                               int i = 0;                              
+                               while (true) {
+                                       ret = libodbc.SQLFetch (handle);
+                                       if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                               break;
+                                       string pkey = Encoding.Default.GetString (primaryKey, 0, length);
+                                       keys.Add (pkey);
+                               }
+                       } finally {
+                                if (handle != IntPtr.Zero) {
+                                        ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
+                                        if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                                                throw new OdbcException(new OdbcError("SQLFreeStmt",OdbcHandleType.Stmt,handle));
+                                        
+                                        ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
+                                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                                               throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,handle));
                                 }
-                        } catch (OdbcException){
-                                // FIXME: Try using SQLStatistics
+                       }
+                       return keys;
+               }
+               
+               private unsafe ArrayList GetPrimaryKeysBySQLStatistics (string catalog, string schema, string table)
+               {
+                       ArrayList keys = new ArrayList ();
+                       IntPtr handle = IntPtr.Zero;
+                       OdbcReturn ret;
+                       try {
+                               ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt, 
+                                                          command.Connection.hDbc, ref handle);
+                               if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                                       throw new OdbcException(new OdbcError("SQLAllocHandle",
+                                                                             OdbcHandleType.Dbc,
+                                                                             command.Connection.hDbc));
+
+                               ret = libodbc.SQLStatistics (handle, catalog, -3,  
+                                                            schema, -3, 
+                                                            table, -3,
+                                                            libodbc.SQL_INDEX_UNIQUE,
+                                                            libodbc.SQL_QUICK);
+                               if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                       throw new OdbcException (new OdbcError ("SQLStatistics", OdbcHandleType.Stmt, handle));
+                        
+                               // NON_UNIQUE
+                               int  nonUniqueLength = 0;
+                               short nonUnique = libodbc.SQL_FALSE;
+                               ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.SHORT, ref (short) nonUnique, sizeof (short), ref nonUniqueLength);
+                               if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                       throw new OdbcException (new OdbcError ("SQLBindCol", OdbcHandleType.Stmt, handle));
+                        
+                               // COLUMN_NAME
+                               int length = 0;
+                               byte [] colName = new byte [255];
+                               ret = libodbc.SQLBindCol (handle, 9, SQL_C_TYPE.CHAR, colName, colName.Length, ref length);
+                               if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                       throw new OdbcException (new OdbcError ("SQLBindCol", OdbcHandleType.Stmt, handle));
+                       
+                               int i = 0;    
+                               while (true) {
+                                       ret = libodbc.SQLFetch (handle);
+                                       if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                               break;
+                                       if (nonUnique == libodbc.SQL_TRUE) {
+                                               string pkey = Encoding.Default.GetString (colName, 0, length);
+                                               keys.Add (pkey);
+                                               break;
+                                       }
+                               }
                         } finally {
                                 if (handle != IntPtr.Zero) {
                                         ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
@@ -932,15 +1064,13 @@ namespace System.Data.Odbc
                                                 throw new OdbcException(new OdbcError("SQLFreeStmt",OdbcHandleType.Stmt,handle));
                                         
                                         ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
-                                                if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
-                                                        throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,handle));
+                                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                                               throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,handle));
                                 }                             
-                                        
-                        }
-                        
-                        return keys;
-                }
-
+                       }
+                       return keys;
+               }
+               
                public
 #if NET_2_0
                 override
@@ -950,28 +1080,6 @@ namespace System.Data.Odbc
                        return NextRow ();
                }
 
-#if NET_2_0
-                [MonoTODO]
-               public override object GetProviderSpecificValue (int i)
-                {
-                       throw new NotImplementedException ();
-                }
-                
-                [MonoTODO]
-               public override int GetProviderSpecificValues (object[] values)
-                {
-                       throw new NotImplementedException ();
-                }
-
-                [MonoTODO]
-               public override Type GetFieldProviderSpecificType (int i)
-                {
-                       throw new NotImplementedException ();
-                }
-                
-#endif // NET_2_0
-
-
                #endregion
        }
 }