**** Merged r40732-r40872 from MCS ****
[mono.git] / mcs / class / System.Data / System.Data.Odbc / OdbcDataReader.cs
index 8a026edb665dbab7d9b44176004fb037faf354c3..984718353fa31a70ed3c84242991964755ff7245 100644 (file)
@@ -3,19 +3,52 @@
 //
 // Author:
 //   Brian Ritchie (brianlritchie@hotmail.com) 
+//   Daniel Morgan <danmorg@sc.rr.com>
+//   Sureshkumar T <tsureshkumar@novell.com> (2004)
 //
 // Copyright (C) Brian Ritchie, 2002
+// Copyright (C) Daniel Morgan, 2002
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
 using System.Collections;
 using System.ComponentModel;
 using System.Data;
 using System.Data.Common;
-using System.Runtime.InteropServices;
+#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
+#else
        public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
+#endif
        {
                #region Fields
                
@@ -24,15 +57,23 @@ namespace System.Data.Odbc
                private int currentRow;
                private OdbcColumn[] cols;
                private IntPtr hstmt;
+#if ONLY_1_1
+               private CommandBehavior behavior;
+#endif // ONLY_1_1
 
                #endregion
 
                #region Constructors
 
-               internal OdbcDataReader (OdbcCommand command) 
+               internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
+#if NET_2_0
+                        : base (behavior)
+#endif // NET_2_0
                {
                        this.command = command;
-                       this.command.Connection.DataReader = this;
+#if ONLY_1_1
+                       this.CommandBehavior=behavior;
+#endif // ONLY_1_1
                        open = true;
                        currentRow = -1;
                        hstmt=command.hStmt;
@@ -40,32 +81,70 @@ namespace System.Data.Odbc
                        short colcount=0;
                        libodbc.SQLNumResultCols(hstmt, ref colcount);
                        cols=new OdbcColumn[colcount];
+                       GetSchemaTable ();
                }
 
                #endregion
 
                #region Properties
 
-               public int Depth {
+#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
+#endif // NET_2_0
+                int Depth {
                        get {
                                return 0; // no nested selects supported
                        }
                }
 
-               public int FieldCount {
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                int FieldCount {
                        get {
-
-                       return cols.Length;
+                               return cols.Length;
                        }
                }
 
-               public bool IsClosed {
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                bool IsClosed {
                        get {
                                return !open;
                        }
                }
 
-               public object this[string name] {
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                object this[string name] {
                        get {
                                int pos;
 
@@ -81,18 +160,36 @@ namespace System.Data.Odbc
                        }
                }
 
-               public object this[int index] {
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                object this[int index] {
                        get {
                                return (object) GetValue (index);
                        }
                }
 
-               public int RecordsAffected {
+                [MonoTODO]
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                int RecordsAffected {
                        get {
                                return -1;
                        }
                }
 
+               [MonoTODO]
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                bool HasRows {
+                       get { throw new NotImplementedException(); }
+               }
+
                #endregion
 
                #region Methods
@@ -102,11 +199,11 @@ namespace System.Data.Odbc
                        int i=0;
                        foreach (OdbcColumn col in cols)
                        {
-                               if (col.ColumnName==colname)
+                               if (col != null && col.ColumnName==colname)
                                        return i;
                                i++;
                        }
-                       return 0;
+                       return -1;
                }
 
                // Dynamically load column descriptions as needed.
@@ -118,35 +215,45 @@ namespace System.Data.Odbc
                                byte[] colname_buffer=new byte[bufsize];
                                string colname;
                                short colname_size=0;
-                               OdbcType DataType=OdbcType.Int;
-                               short ColSize=0, DecDigits=0, Nullable=0, dt=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);
-                               libodbchelper.DisplayError("SQLDescribeCol",ret);
+                               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();\r
-                               OdbcColumn c=new OdbcColumn(colname, (OdbcType) dt);
+                               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=ColSize;
+                                       c.MaxLength=(int)ColSize;
                                cols[ordinal]=c;
                        }
                        return cols[ordinal];
                }
-       
-               public void Close ()
+
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                void Close ()
                {
-                       // libodbc.SQLFreeHandle((ushort) OdbcHandleType.Stmt, hstmt);
-               
-                       OdbcReturn ret=libodbc.SQLCloseCursor(hstmt);
-                       libodbchelper.DisplayError("SQLCancel",ret);
+                       // 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;
 
-                       this.command.Connection.DataReader = null;
+                       ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, hstmt);\r
+                       if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                               throw new OdbcException(new OdbcError("SQLFreeHandle",OdbcHandleType.Stmt,hstmt));
+
+                        if ((this.CommandBehavior & CommandBehavior.CloseConnection)==CommandBehavior.CloseConnection)
+                               this.command.Connection.Close();
                }
 
                ~OdbcDataReader ()
@@ -155,105 +262,223 @@ namespace System.Data.Odbc
                                Close ();
                }
 
-               public bool GetBoolean (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               bool GetBoolean (int ordinal)
                {
                        return (bool) GetValue(ordinal);
                }
 
-               [MonoTODO]
-               public byte GetByte (int ordinal)
-               {
-                       throw new NotImplementedException ();
-               }
-
-               [MonoTODO]
-               public long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
-               {
-                       throw new NotImplementedException ();
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               byte GetByte (int ordinal)
+               {
+                       return (byte) Convert.ToByte(GetValue(ordinal));
+               }
+
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               long GetBytes (int ordinal, long dataIndex, byte[] buffer, int bufferIndex, int length)
+               {
+                        OdbcReturn ret = OdbcReturn.Error;
+                        bool copyBuffer = false;
+                        int returnVal = 0, outsize = 0;
+                        byte [] tbuff = new byte [length+1];
+
+                        length = buffer == null ? 0 : length;
+                        ret=libodbc.SQLGetData (hstmt, (ushort) (ordinal+1), SQL_C_TYPE.BINARY, tbuff, length, 
+                                        ref outsize);
+
+                        if (ret == OdbcReturn.NoData)
+                                return 0;
+
+                        if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo)) 
+                                throw new OdbcException (new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt));
+
+                       OdbcError odbcErr = null;
+                       if ( (ret == OdbcReturn.SuccessWithInfo))
+                               odbcErr = new OdbcError ("SQLGetData", OdbcHandleType.Stmt, hstmt);
+
+                        if (buffer == null)
+                                return outsize; //if buffer is null,return length of the field
+                        
+                        if (ret == OdbcReturn.SuccessWithInfo) {
+                                if (outsize == (int) OdbcLengthIndicator.NoTotal)
+                                        copyBuffer = true;
+                                else if (outsize == (int) OdbcLengthIndicator.NullData) {
+                                        copyBuffer = false;
+                                        returnVal = -1;
+                                } else {
+                                        string sqlstate = odbcErr.SQLState;
+                                        //SQLState: String Data, Right truncated
+                                        if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC) 
+                                                throw new OdbcException ( odbcErr);
+                                        copyBuffer = true;
+                                }
+                        } else {
+                                copyBuffer = outsize == -1 ? false : true;
+                                returnVal = outsize;
+                        }
+
+                        if (copyBuffer) {
+                                int i = 0;
+                                while (tbuff [i] != libodbc.C_NULL) {
+                                        buffer [bufferIndex + i] = tbuff [i];
+                                        i++;
+                                }
+                                returnVal = i;
+                        }
+                        return returnVal;
                }
                
                [MonoTODO]
-               public char GetChar (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               char GetChar (int ordinal)
                {
                        throw new NotImplementedException ();
                }
 
                [MonoTODO]
-               public long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               long GetChars (int ordinal, long dataIndex, char[] buffer, int bufferIndex, int length)
                {
                        throw new NotImplementedException ();
                }
 
                [MonoTODO]
-               public OdbcDataReader GetData (int ordinal)
+               [EditorBrowsableAttribute (EditorBrowsableState.Never)]
+               public 
+#if NET_2_0
+               new
+#endif // NET_2_0
+               IDataReader GetData (int ordinal)
                {
                        throw new NotImplementedException ();
                }
 
-               public string GetDataTypeName (int index)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               string GetDataTypeName (int index)
                {
                        return GetColumn(index).OdbcType.ToString();
                }
 
-               public DateTime GetDateTime (int ordinal)
+               public DateTime GetDate(int ordinal) {
+                       return GetDateTime(ordinal);
+               }
+
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               DateTime GetDateTime (int ordinal)
                {
                        return (DateTime) GetValue(ordinal);
                }
 
                [MonoTODO]
-               public decimal GetDecimal (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               decimal GetDecimal (int ordinal)
                {
                        throw new NotImplementedException ();
                }
 
-               public double GetDouble (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               double GetDouble (int ordinal)
                {
                        return (double) GetValue(ordinal);
                }
 
-               public Type GetFieldType (int index)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               Type GetFieldType (int index)
                {
                        return GetColumn(index).DataType;
                }
 
-               public float GetFloat (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               float GetFloat (int ordinal)
                {
                        return (float) GetValue(ordinal);
                }
 
                [MonoTODO]
-               public Guid GetGuid (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               Guid GetGuid (int ordinal)
                {
                        throw new NotImplementedException ();
                }
 
-               public short GetInt16 (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               short GetInt16 (int ordinal)
                {
                        return (short) GetValue(ordinal);
                }
 
-               public int GetInt32 (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               int GetInt32 (int ordinal)
                {
                        return (int) GetValue(ordinal);
                }
 
-               public long GetInt64 (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               long GetInt64 (int ordinal)
                {
                        return (long) GetValue(ordinal);
                }
 
-               public string GetName (int index)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               string GetName (int index)
                {
-                       if (currentRow == -1)
-                               return null;
                        return GetColumn(index).ColumnName;
                }
 
-               public int GetOrdinal (string name)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               int GetOrdinal (string name)
                {
-                       if (currentRow == -1)
-                               throw new IndexOutOfRangeException ();
-
                        int i=ColIndex(name);
 
                        if (i==-1)
@@ -263,15 +488,20 @@ namespace System.Data.Odbc
                }
 
                [MonoTODO]
-               public DataTable GetSchemaTable() \r
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                DataTable GetSchemaTable() 
                {       
 
                        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) \r
+                       if(cols.Length > 0) 
                        {
+                                string [] keys = GetPrimaryKeys ();
                                
                                dataTableSchema = new DataTable ();
                                
@@ -299,13 +529,12 @@ namespace System.Data.Odbc
                                dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
                                dataTableSchema.Columns.Add ("IsLong", typeof (bool));
                                dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
-\r
+
                                DataRow schemaRow;
                                                                
-                               for (int i = 0; i < cols.Length; i += 1 ) \r
+                               for (int i = 0; i < cols.Length; i += 1 ) 
                                {
                                        OdbcColumn col=GetColumn(i);
-                                       //Console.WriteLine("{0}:{1}:{2}",col.ColumnName,col.DataType,col.OdbcType);
 
                                        schemaRow = dataTableSchema.NewRow ();
                                        dataTableSchema.Rows.Add (schemaRow);
@@ -317,14 +546,22 @@ namespace System.Data.Odbc
                                        schemaRow["NumericPrecision"] = 0;
                                        schemaRow["NumericScale"] = 0;
                                        // TODO: need to get KeyInfo
+
                                        schemaRow["IsUnique"] = false;
-                                       schemaRow["IsKey"] = DBNull.Value;                                      \r
+                                       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"] = col.ColumnName;
                                        schemaRow["BaseSchemaName"] = "";
                                        schemaRow["BaseTableName"] = "";
-                                       schemaRow["DataType"] = col.DataType;\r
+                                       schemaRow["DataType"] = col.DataType;
 
                                        schemaRow["AllowDBNull"] = col.AllowDBNull;
                                        
@@ -339,27 +576,38 @@ namespace System.Data.Odbc
                                        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();
                                        
                                }
-
+                                dataTableSchema.AcceptChanges();
                        }
-                       
-                       return dataTableSchema;
+                        return dataTableSchema;
                }
 
-               public string GetString (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               string GetString (int ordinal)
                {
                        return (string) GetValue(ordinal);
                }
 
                [MonoTODO]
-               public TimeSpan GetTimeSpan (int ordinal)
+               public TimeSpan GetTime (int ordinal)
                {
                        throw new NotImplementedException ();
                }
 
-               public object GetValue (int ordinal)
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               object GetValue (int ordinal)
                {
                        if (currentRow == -1)
                                throw new IndexOutOfRangeException ();
@@ -377,87 +625,137 @@ namespace System.Data.Odbc
                        // Check cached values
                        if (col.Value==null)
                        {
-
-                               // odbc help file
+                                // 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.Decimal:
                                                bufsize=50;
-                                               buffer=new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for decimal
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
+                                               buffer=new byte[bufsize];  // According to sqlext.h, use SQL_CHAR for decima. 
+                                                // 2005 03 10 : this now works with unixodbc with numeric c type.
+                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
+                                               byte[] temp = new byte[outsize];
+                                                for (int i=0;i<outsize;i++)
+                                                        temp[i]=buffer[i];
+
                                                if (outsize!=-1)
-                                                       DataValue=Decimal.Parse(System.Text.Encoding.Default.GetString(buffer));
+                                                       DataValue=Decimal.Parse(System.Text.Encoding.Default.GetString(temp));
                                                break;
                                        case OdbcType.TinyInt:
                                                short short_data=0;
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.TinyInt, ref short_data, 0, ref outsize);
-                                               DataValue=short_data;
+                                               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, OdbcType.Int, ref int_data, 0, ref outsize);
+                                               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, OdbcType.BigInt, ref long_data, 0, ref outsize);
+                                               ret=libodbc.SQLGetData(hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
                                                DataValue=long_data;
                                                break;
-                                       case OdbcType.NVarChar:\r
-                                               bufsize=col.MaxLength*2+1; // Unicode is double byte\r
+                                       case OdbcType.NVarChar:
+                                               bufsize=col.MaxLength*2+1; // Unicode is double byte
                                                buffer=new byte[bufsize];
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.NVarChar, buffer, bufsize, ref outsize);
+                                               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, OdbcType.Char, buffer, bufsize, ref outsize);
+                                               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, OdbcType.Real, ref float_data, 0, ref outsize);
+                                               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, OdbcType.DateTime, ref ts_data, 0, ref outsize);
-                                               if (outsize!=-1) // This means SQL_NULL_DATA 
+                                                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:
-                                               //Console.WriteLine("Fetching unsupported data type as string: "+col.OdbcType.ToString());
                                                bufsize=255;
                                                buffer=new byte[bufsize];
-                                               ret=libodbc.SQLGetData(hstmt, ColIndex, OdbcType.Char, buffer, bufsize, ref outsize);
+                                               ret=libodbc.SQLGetData(hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
                                                DataValue=System.Text.Encoding.Default.GetString(buffer);
                                                break;
                                }
 
+                               if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo)) 
+                                       throw new OdbcException(new OdbcError("SQLGetData",OdbcHandleType.Stmt,hstmt));
+
                                if (outsize==-1) // This means SQL_NULL_DATA 
                                        col.Value=DBNull.Value;
                                else
-                               {
-                                       libodbchelper.DisplayError("SQLGetData("+col.OdbcType.ToString()+")",ret);
-                                       col.Value=DataValue;    
-                               }
+                                       col.Value=DataValue;
                        }
                        return col.Value;
                }
+               
+               public 
+#if NET_2_0
+               override
+#endif // NET_2_0
+               int GetValues (object[] values)
+               {
+                       int numValues = 0;
+
+                       // copy values
+                       for (int i = 0; i < values.Length; i++) {
+                               if (i < FieldCount) {
+                                       values[i] = GetValue(i);
+                               }
+                               else {
+                                       values[i] = null;
+                               }
+                       }
 
-               [MonoTODO]
-               public int GetValues (object[] values)
-               {
-                       throw new NotImplementedException ();
+                       // get number of object instances in array
+                       if (values.Length < FieldCount)
+                               numValues = values.Length;
+                       else if (values.Length == FieldCount)
+                               numValues = FieldCount;
+                       else
+                               numValues = FieldCount;
+
+                       return numValues;
                }
 
-               [MonoTODO]
+#if ONLY_1_1
+
+                [MonoTODO]
                IDataReader IDataRecord.GetData (int ordinal)
                {
                        throw new NotImplementedException ();
@@ -468,37 +766,186 @@ namespace System.Data.Odbc
                {
                }
 
-               [MonoTODO]
-               IEnumerator IEnumerable.GetEnumerator ()
+                IEnumerator IEnumerable.GetEnumerator ()
                {
-                       throw new NotImplementedException ();
+                       return new DbEnumerator (this);
                }
+#endif // ONLY_1_1
 
-               public bool IsDBNull (int ordinal)
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                bool IsDBNull (int ordinal)
                {
                        return (GetValue(ordinal) is DBNull);
                }
 
-               public bool NextResult ()
+               /// <remarks>
+               ///     Move to the next result set.
+               /// </remarks>
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                bool NextResult ()
+               {
+                       OdbcReturn ret = OdbcReturn.Success;
+                       ret = libodbc.SQLMoreResults (hstmt);
+                       if (ret == OdbcReturn.Success) {
+                               short colcount = 0;
+                               libodbc.SQLNumResultCols (hstmt, ref colcount);
+                               cols = new OdbcColumn [colcount];
+                               GetSchemaTable ();
+                       }       
+                       return (ret==OdbcReturn.Success);
+               }
+
+               /// <remarks>
+               ///     Load the next row in the current result set.
+               /// </remarks>
+               public bool NextRow ()
                {
-                       OdbcReturn ret=libodbc.SQLFetch(hstmt);
-                       if (ret!=OdbcReturn.Success)
-                               currentRow=-1;
+                       OdbcReturn ret=libodbc.SQLFetch (hstmt);
+                       if (ret != OdbcReturn.Success)
+                               currentRow = -1;
                        else
                                currentRow++;
+
                        // Clear cached values from last record
                        foreach (OdbcColumn col in cols)
                        {
-                               if (col!=null)
-                                       col.Value=null;
+                               if (col != null)
+                                       col.Value = null;
                        }
-                       return (ret==OdbcReturn.Success);
-               }
+                       return (ret == OdbcReturn.Success);
+               }
+
+
+                private int GetColumnAttribute (int column, FieldIdentifier fieldId)
+                {
+                        OdbcReturn ret = OdbcReturn.Error;
+                        byte [] buffer = new byte [255];
+                        int outsize = 0;
+                        int val = 0;
+                        ret = libodbc.SQLColAttribute (hstmt, column, fieldId, 
+                                                       buffer, buffer.Length, 
+                                                       ref outsize, ref val);
+                        if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                throw new OdbcException (new OdbcError ("SQLColAttribute",
+                                                                        OdbcHandleType.Stmt,
+                                                                        hstmt)
+                                                         );
+                        return val;
+                        
+                }
+
+                private string GetColumnAttributeStr (int column, FieldIdentifier fieldId)
+                {
+                        OdbcReturn ret = OdbcReturn.Error;
+                        byte [] buffer = new byte [255];
+                        int outsize = 0;
+                        int val = 0;
+                        ret = libodbc.SQLColAttribute (hstmt, column, fieldId, 
+                                                       buffer, buffer.Length, 
+                                                       ref outsize, ref val);
+                        if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
+                                throw new OdbcException (new OdbcError ("SQLColAttribute",
+                                                                        OdbcHandleType.Stmt,
+                                                                        hstmt)
+                                                         );
+                        string value = Encoding.Default.GetString (buffer);
+                        return value;
+                }
+
+                private string [] GetPrimaryKeys ()
+                {
+                        if (cols.Length <= 0)
+                                return new string [0];
+
+                        string [] keys = new string [cols.Length];
+                        IntPtr handle = IntPtr.Zero;
+                        OdbcReturn ret = OdbcReturn.Error;                  
+                        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];
+                        
+                                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);
+                                        keys [i++] = pkey;
+                                }
+                        } catch (OdbcException){
+                                // FIXME: Try using SQLStatistics
+                        } 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));
+                                }                             
+                                        
+                        }
+                        
+                        return keys;
+                }
+
+               public
+#if NET_2_0
+                override
+#endif // NET_2_0
+                bool Read ()
+               {
+                       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
 
-               public bool Read ()
-               {
-                       return NextResult();
-               }
 
                #endregion
        }