2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlDataReader.cs
index c40d02af2684fb2aae92fdd5f320881f3c777e14..1594d87f22f0030aabb8ec53ba389e529a1aecd3 100755 (executable)
-\r
-// Npgsql.NpgsqlDataReader.cs\r
-// \r
-// Author:\r
-//     Francisco Jr. (fxjrlists@yahoo.com.br)\r
-//\r
-//     Copyright (C) 2002 The Npgsql Development Team\r
-//\r
-\r
-// This library is free software; you can redistribute it and/or\r
-// modify it under the terms of the GNU Lesser General Public\r
-// License as published by the Free Software Foundation; either\r
-// version 2.1 of the License, or (at your option) any later version.\r
-// \r
-// This library is distributed in the hope that it will be useful,\r
-// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
-// Lesser General Public License for more details.\r
-// \r
-// You should have received a copy of the GNU Lesser General Public\r
-// License along with this library; if not, write to the Free Software\r
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
-\r
-using System;\r
-using System.Data;\r
-using System.Collections;\r
-\r
-\r
-namespace Npgsql\r
-{      \r
-       public class NpgsqlDataReader : IDataReader, IEnumerable\r
-       {\r
-         private NpgsqlConnection      _connection;\r
-       private ArrayList                               _resultsets;\r
-               private ArrayList                                       _responses;\r
-         private Int32                                                 _rowIndex;\r
-               private Int32                                                   _resultsetIndex;\r
-               private NpgsqlResultSet         _currentResultset;\r
-               private DataTable                                       _currentResultsetSchema;\r
-               \r
-               \r
-               // Logging related values\r
-    private static readonly String CLASSNAME = "NpgsqlDataReader";\r
-               \r
-         internal NpgsqlDataReader( ArrayList resultsets, ArrayList responses, NpgsqlConnection connection)\r
-         {\r
-           _resultsets                                 = resultsets;\r
-               _responses                                      = responses;\r
-               _connection                             = connection;\r
-               _rowIndex                                               = -1;\r
-               _resultsetIndex                 = 0;\r
-               \r
-               _currentResultset       = (NpgsqlResultSet)_resultsets[_resultsetIndex];\r
-               \r
-               \r
-               \r
-         }\r
-         \r
-         private Boolean CanRead()\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".CanRead() ", LogLevel.Debug);\r
-               /*if (_currentResultset == null)\r
-                       return false;*/\r
-               return (_currentResultset != null);\r
-               \r
-         }\r
-         \r
-         \r
-         public void Dispose()\r
-         {\r
-               \r
-         }\r
-\r
-         public Int32 Depth \r
-         {\r
-               get\r
-               {\r
-                       NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_Depth() ", LogLevel.Debug);\r
-                       return 0;\r
-               }\r
-         }\r
-         \r
-         public Boolean IsClosed\r
-         {\r
-               get\r
-               {\r
-                       NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_IsClosed()", LogLevel.Debug);\r
-                       return false; \r
-               }\r
-         }\r
-         \r
-         public Int32 RecordsAffected \r
-         {\r
-               get\r
-               {\r
-                       NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_RecordsAffected()", LogLevel.Debug);\r
-                       \r
-                       /*if (_currentResultset == null)\r
-                               return 0;       //[FIXME] Get the actual number of rows deleted, updated or inserted.\r
-                       return -1;\r
-                       */\r
-                       \r
-                       if (CanRead())\r
-                               return -1;\r
-                       \r
-                       String[] ret_string_tokens = ((String)_responses[_resultsetIndex]).Split(null); // whitespace separator.\r
-                               \r
-                               return Int32.Parse(ret_string_tokens[ret_string_tokens.Length - 1]);\r
-               }\r
-           \r
-         }\r
-         \r
-         public void Close()\r
-         {\r
-           \r
-         }\r
-         \r
-         public Boolean NextResult()\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".NextResult()", LogLevel.Debug);\r
-           //throw new NotImplementedException();\r
-               \r
-               //[FIXME] Should the currentResultset not be modified\r
-               // in case there aren't any more resultsets?\r
-               // SqlClient modify to a invalid resultset and throws exceptions\r
-               // when trying to access any data.\r
-               \r
-                               \r
-               if((_resultsetIndex + 1) < _resultsets.Count)\r
-               {\r
-                       _resultsetIndex++;\r
-                       _rowIndex = -1;\r
-                       _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];\r
-                       return true;\r
-               }\r
-               else\r
-                       return false;\r
-               \r
-         }\r
-         \r
-         public Boolean Read()\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".Read()", LogLevel.Debug);\r
-               \r
-               if (!CanRead())\r
-                       return false;\r
-               \r
-           _rowIndex++;\r
-               return (_rowIndex < _currentResultset.Count);\r
-         }\r
-         \r
-         public DataTable GetSchemaTable()\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetSchemaTable()", LogLevel.Debug);\r
-           //throw new NotImplementedException();\r
-               \r
-               if (!CanRead())\r
-                       return null; //[FIXME] Should we return null or throw an exception??\r
-               \r
-               if(_currentResultsetSchema == null)\r
-                       _currentResultsetSchema = GetResultsetSchema();\r
-               \r
-               return _currentResultsetSchema;\r
-               \r
-         }\r
-         \r
-         \r
-         public Int32 FieldCount\r
-         {\r
-               get\r
-               {\r
-                       \r
-                       NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_FieldCount()", LogLevel.Debug);\r
-                       //return ((_currentResultset == null) ? 0 : _currentResultset.RowDescription.NumFields);\r
-                       if (CanRead())\r
-                               return _currentResultset.RowDescription.NumFields;\r
-                       else\r
-                               return -1;\r
-                               \r
-               }\r
-           \r
-         }\r
-         \r
-         public String GetName(Int32 i)\r
-         {\r
-           //throw new NotImplementedException();\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetName(Int32)", LogLevel.Debug);\r
-               \r
-               if (CanRead())\r
-                       return _currentResultset.RowDescription[i].name;\r
-               else\r
-                       return String.Empty;\r
-         }\r
-         \r
-         public String GetDataTypeName(Int32 i)\r
-         {\r
-                 // FIXME: have a type name instead of the oid\r
-                return (_currentResultset.RowDescription[i].type_oid).ToString();\r
-         }\r
-         \r
-         public Type GetFieldType(Int32 i)\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetFieldType(Int32)", LogLevel.Debug);\r
-               //[FIXME] hack\r
-                       \r
-               return Type.GetType(PGUtil.GetSystemTypeFromDbType(_currentResultset.RowDescription[i].type_oid));\r
-         }\r
-         \r
-         public Object GetValue(Int32 i)\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetValue(Int32)", LogLevel.Debug);\r
-               if (i < 0 || _rowIndex < 0)\r
-                       throw new InvalidOperationException("Cannot read data.");\r
-               return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[i];\r
-         }\r
-         \r
-         public Int32 GetValues(Object[] values)\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetValues(Object[])", LogLevel.Debug);\r
-               \r
-               // Only the number of elements in the array are filled.\r
-               // It's also possible to pass an array with more that FieldCount elements.\r
-               Int32 maxColumnIndex = (values.Length < FieldCount) ? values.Length : FieldCount;\r
-               \r
-               for (Int32 i = 0; i < maxColumnIndex; i++)\r
-                       values[i] = GetValue(i);\r
-               \r
-               return maxColumnIndex;\r
-               \r
-         }\r
-         \r
-         public Int32 GetOrdinal(String name)\r
-         {\r
-               return _currentResultset.RowDescription.FieldIndex(name);\r
-         }\r
-         \r
-         public Object this [ Int32 i ]\r
-         {\r
-               get\r
-               {\r
-                       NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".this[Int32]", LogLevel.Debug);\r
-                       return GetValue(i);\r
-               }\r
-         }\r
-         \r
-         public Object this [ String name ]\r
-         {\r
-               get\r
-               {\r
-                       //throw new NotImplementedException();\r
-                       NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".this[String]", LogLevel.Debug);\r
-                       return GetValue(_currentResultset.RowDescription.FieldIndex(name));\r
-               }\r
-         }\r
-         \r
-         public Boolean GetBoolean(Int32 i)\r
-         {\r
-           // Should this be done using the GetValue directly and not by converting to String\r
-               // and parsing from there?\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetBoolean(Int32)", LogLevel.Debug);\r
-               \r
-               switch ((String) this[i])\r
-               {\r
-                       case "t":\r
-                               return true;\r
-                               \r
-                       case "f":\r
-                               return false;\r
-                               \r
-                       default:\r
-                               throw new System.InvalidCastException();\r
-                       \r
-               }\r
-               \r
-         }\r
-         \r
-         public Byte GetByte(Int32 i)\r
-         {\r
-           throw new NotImplementedException();\r
-         }\r
-         \r
-         public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)\r
-         {\r
-           throw new NotImplementedException();\r
-         }\r
-         \r
-         public Char GetChar(Int32 i)\r
-         {\r
-           throw new NotImplementedException();\r
-         }\r
-         \r
-         public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)\r
-         {\r
-                       String          str;\r
-\r
-                       str = GetString(i);\r
-               if (buffer == null)\r
-                       return str.Length;\r
-               \r
-                       str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);\r
-                       return buffer.GetLength(0);\r
-         }\r
-         \r
-         public Guid GetGuid(Int32 i)\r
-         {\r
-           throw new NotImplementedException();\r
-         }\r
-         \r
-         public Int16 GetInt16(Int32 i)\r
-         {\r
-           // Should this be done using the GetValue directly and not by converting to String\r
-               // and parsing from there?\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetInt16(Int32)", LogLevel.Debug);\r
-               try\r
-               {\r
-                   return Int16.Parse((String) this[i]);\r
-               } catch (System.FormatException)\r
-               {\r
-                       throw new System.InvalidCastException();\r
-               }\r
-               \r
-\r
-         }\r
-         \r
-         public Int32 GetInt32(Int32 i)\r
-         {\r
-           // Should this be done using the GetValue directly and not by converting to String\r
-               // and parsing from there?\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetInt32(Int32)", LogLevel.Debug);\r
-               try\r
-               {\r
-                   return Int32.Parse((String) this[i]);\r
-               } catch (System.FormatException)\r
-               {\r
-                       throw new System.InvalidCastException();\r
-               }\r
-               \r
-\r
-         }\r
-         \r
-         public Int64 GetInt64(Int32 i)\r
-         {\r
-           // Should this be done using the GetValue directly and not by converting to String\r
-               // and parsing from there?\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetInt64(Int32)", LogLevel.Debug);\r
-               try\r
-               {\r
-                   return Int64.Parse((String) this[i]);\r
-               } catch (System.FormatException)\r
-               {\r
-                       throw new System.InvalidCastException();\r
-               }\r
-         }\r
-         \r
-         public Single GetFloat(Int32 i)\r
-         {\r
-           // Should this be done using the GetValue directly and not by converting to String\r
-               // and parsing from there?\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetFloat(Int32)", LogLevel.Debug);\r
-               try\r
-               {\r
-                   return Single.Parse((String) this[i]);\r
-               } catch (System.FormatException)\r
-               {\r
-                       throw new System.InvalidCastException();\r
-               }\r
-         }\r
-         \r
-         public Double GetDouble(Int32 i)\r
-         {\r
-           // Should this be done using the GetValue directly and not by converting to String\r
-               // and parsing from there?\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetDouble(Int32)", LogLevel.Debug);\r
-               try\r
-               {\r
-                   return Double.Parse((String) this[i]);\r
-               } catch (System.FormatException)\r
-               {\r
-                       throw new System.InvalidCastException();\r
-               }\r
-         }\r
-         \r
-         public String GetString(Int32 i)\r
-         {\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetString(Int32)", LogLevel.Debug);\r
-           return (String) GetValue(i);\r
-         }\r
-         \r
-         public Decimal GetDecimal(Int32 i)\r
-         {\r
-           // Should this be done using the GetValue directly and not by converting to String\r
-               // and parsing from there?\r
-               NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetDecimal(Int32)", LogLevel.Debug);\r
-               try\r
-               {\r
-                   return Decimal.Parse((String) this[i]);\r
-               } catch (System.FormatException)\r
-               {\r
-                       throw new System.InvalidCastException();\r
-               }\r
-         }\r
-         \r
-         public DateTime GetDateTime(Int32 i)\r
-         {\r
-                 // Should this be done using the GetValue directly and not by converting to String\r
-                 // and parsing from there?\r
-                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetInt32(Int32)", LogLevel.Debug);\r
-                 try {\r
-                         return DateTime.ParseExact((string) this[i], "dd/MM/yyyy", null);
-                 } catch (System.FormatException) {\r
-                         throw new System.InvalidCastException();\r
-                 }\r
-         }\r
-         \r
-         public IDataReader GetData(Int32 i)\r
-         {\r
-           throw new NotImplementedException();\r
-         }\r
-         \r
-         public Boolean IsDBNull(Int32 i)\r
-         {\r
-               //throw new NotImplementedException();\r
-               \r
-               return ((NpgsqlAsciiRow)_currentResultset[_rowIndex]).IsNull(i);\r
-         }\r
-\r
-               private DataTable GetResultsetSchema()\r
-               {\r
-                       DataTable result = null;\r
-                       NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetResultsetSchema()", LogLevel.Debug);\r
-                       // [FIXME] For now, just support fields name.\r
-                       \r
-                       NpgsqlRowDescription rd = _currentResultset.RowDescription;\r
-                       Int16 numFields = rd.NumFields;\r
-                       if(numFields > 0) {\r
-                               result = new DataTable("SchemaTable");          \r
-\r
-                               result.Columns.Add ("ColumnName", typeof (string));
-                               result.Columns.Add ("ColumnOrdinal", typeof (int));
-                               result.Columns.Add ("ColumnSize", typeof (int));
-                               result.Columns.Add ("NumericPrecision", typeof (int));
-                               result.Columns.Add ("NumericScale", typeof (int));
-                               result.Columns.Add ("IsUnique", typeof (bool));
-                               result.Columns.Add ("IsKey", typeof (bool));
-                               DataColumn dc = result.Columns["IsKey"];
-                               dc.AllowDBNull = true; // IsKey can have a DBNull
-                               result.Columns.Add ("BaseCatalogName", typeof (string));
-                               result.Columns.Add ("BaseColumnName", typeof (string));
-                               result.Columns.Add ("BaseSchemaName", typeof (string));
-                               result.Columns.Add ("BaseTableName", typeof (string));
-                               result.Columns.Add ("DataType", typeof(Type));
-                               result.Columns.Add ("AllowDBNull", typeof (bool));
-                               result.Columns.Add ("ProviderType", typeof (int));
-                               result.Columns.Add ("IsAliased", typeof (bool));
-                               result.Columns.Add ("IsExpression", typeof (bool));
-                               result.Columns.Add ("IsIdentity", typeof (bool));
-                               result.Columns.Add ("IsAutoIncrement", typeof (bool));
-                               result.Columns.Add ("IsRowVersion", typeof (bool));
-                               result.Columns.Add ("IsHidden", typeof (bool));
-                               result.Columns.Add ("IsLong", typeof (bool));
-                               result.Columns.Add ("IsReadOnly", typeof (bool));
-\r
-                               DataRow row;\r
-                       \r
-                               for (Int16 i = 0; i < numFields; i++) {\r
-                                       row = result.NewRow();\r
-\r
-                                       row["ColumnName"] = GetName(i);
-                                       row["ColumnOrdinal"] = i + 1;                           
-                                       row["ColumnSize"] = (int) rd[i].type_size;
-                                       row["NumericPrecision"] = 0;
-                                       row["NumericScale"] = 0;
-                                       row["IsUnique"] = false;
-                                       row["IsKey"] = DBNull.Value;
-                                       row["BaseCatalogName"] = "";
-                                       row["BaseColumnName"] = GetName(i);
-                                       row["BaseSchemaName"] = "";
-                                       row["BaseTableName"] = "";\r
-                                       row["DataType"] = GetFieldType(i);
-                                       row["AllowDBNull"] = false;                                     
-                                       row["ProviderType"] = (int) rd[i].type_oid;
-                                       row["IsAliased"] = false;
-                                       row["IsExpression"] = false;
-                                       row["IsIdentity"] = false;
-                                       row["IsAutoIncrement"] = false;
-                                       row["IsRowVersion"] = false;
-                                       row["IsHidden"] = false;
-                                       row["IsLong"] = false;
-                                       row["IsReadOnly"] = false;\r
-\r
-                                       result.Rows.Add(row);\r
-                               }\r
-                       }\r
-                       \r
-                       return result;\r
-                       \r
-               }\r
-\r
-               IEnumerator IEnumerable.GetEnumerator () {
-                       return new System.Data.Common.DbEnumerator (this);
-               }\r
-       }\r
-}\r
+
+// Npgsql.NpgsqlDataReader.cs
+//
+// Author:
+//     Francisco Jr. (fxjrlists@yahoo.com.br)
+//
+//     Copyright (C) 2002 The Npgsql Development Team
+//     npgsql-general@gborg.postgresql.org
+//     http://gborg.postgresql.org/project/npgsql/projdisplay.php
+//
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+using System;
+using System.Data;
+using System.Collections;
+
+using NpgsqlTypes;
+
+namespace Npgsql
+{
+    /// <summary>
+    /// Provides a means of reading a forward-only stream of rows from a PostgreSQL backend.  This class cannot be inherited.
+    /// </summary>
+    public sealed class NpgsqlDataReader : IDataReader, IEnumerable
+    {
+        private NpgsqlConnection       _connection;
+        private ArrayList                      _resultsets;
+        private ArrayList                      _responses;
+        private Int32                          _rowIndex;
+        private Int32                          _resultsetIndex;
+        private        NpgsqlResultSet         _currentResultset;
+        private DataTable                      _currentResultsetSchema;
+        private CommandBehavior     _behavior;
+        private Boolean             _isClosed;
+
+
+        // Logging related values
+        private static readonly String CLASSNAME = "NpgsqlDataReader";
+
+        internal NpgsqlDataReader( ArrayList resultsets, ArrayList responses, NpgsqlConnection connection, CommandBehavior behavior)
+        {
+            _resultsets = resultsets;
+            _responses = responses;
+            _connection = connection;
+            _rowIndex = -1;
+            _resultsetIndex = 0;
+
+            if (_resultsets.Count > 0)
+                _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
+
+            _behavior = behavior;
+            _isClosed = false;
+        }
+
+        private Boolean HaveResultSet()
+        {
+            return (_currentResultset != null);
+        }
+
+        private Boolean HaveRow()
+        {
+            return (HaveResultSet() && _rowIndex >= 0 && _rowIndex < _currentResultset.Count);
+        }
+
+        private void CheckHaveResultSet()
+        {
+            if (! HaveResultSet())
+            {
+                throw new InvalidOperationException("Cannot read data. No result set.");
+            }
+        }
+
+        private void CheckHaveRow()
+        {
+            CheckHaveResultSet();
+
+            if (_rowIndex < 0)
+            {
+                throw new InvalidOperationException("DataReader positioned before beginning of result set. Did you call Read()?");
+            }
+            else if (_rowIndex >= _currentResultset.Count)
+            {
+                throw new InvalidOperationException("DataReader positioned beyond end of result set.");
+            }
+        }
+
+
+        /// <summary>
+        /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
+        /// </summary>
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        /// <summary>
+        /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
+        /// </summary>
+        protected void Dispose (bool disposing)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
+            if (disposing)
+            {
+                this.Close();
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating the depth of nesting for the current row.  Always returns zero.
+        /// </summary>
+        public Int32 Depth
+        {
+            get
+            {
+                NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Depth");
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether the data reader is closed.
+        /// </summary>
+        public Boolean IsClosed
+        {
+            get
+            {
+                NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsClosed");
+                return _isClosed;
+            }
+        }
+
+        /// <summary>
+        /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
+        /// </summary>
+        public Int32 RecordsAffected
+        {
+            get
+            {
+                NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected");
+
+                if (HaveResultSet())
+                {
+                    return -1;
+                }
+
+                String[] _returnStringTokens = ((String)_responses[_resultsetIndex]).Split(null);      // whitespace separator.
+
+                try
+                {
+                    return Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]);
+                }
+                catch (FormatException)
+                {
+                    return -1;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Indicates if NpgsqlDatareader has rows to be read.
+        /// </summary>
+
+        public Boolean HasRows
+        {
+            get
+            {
+                return _currentResultset.Count > 0;
+            }
+
+        }
+
+        /// <summary>
+        /// Closes the data reader object.
+        /// </summary>
+        public void Close()
+        {
+            if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
+            {
+                _connection.Close();
+
+            }
+
+            _isClosed = true;
+        }
+
+        /// <summary>
+        /// Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend.
+        /// </summary>
+        /// <returns>True if the reader was advanced, otherwise false.</returns>
+        public Boolean NextResult()
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "NextResult");
+
+            if((_resultsetIndex + 1) < _resultsets.Count)
+            {
+                _resultsetIndex++;
+                _rowIndex = -1;
+                _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
+                return true;
+            }
+            else
+                return false;
+
+        }
+
+        /// <summary>
+        /// Advances the data reader to the next row.
+        /// </summary>
+        /// <returns>True if the reader was advanced, otherwise false.</returns>
+        public Boolean Read()
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Read");
+
+            CheckHaveResultSet();
+
+            if (_rowIndex < _currentResultset.Count)
+            {
+                _rowIndex++;
+                return (_rowIndex < _currentResultset.Count);
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Returns a System.Data.DataTable that describes the column metadata of the DataReader.
+        /// </summary>
+        public DataTable GetSchemaTable()
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable");
+
+            if(_currentResultsetSchema == null)
+                _currentResultsetSchema = GetResultsetSchema();
+
+            return _currentResultsetSchema;
+        }
+
+        /// <summary>
+        /// Gets the number of columns in the current row.
+        /// </summary>
+        public Int32 FieldCount
+        {
+            get
+            {
+
+                NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "FieldCount");
+
+                if (! HaveResultSet()) //Executed a non return rows query.
+                    return -1;
+                else
+                    return _currentResultset.RowDescription.NumFields;
+
+
+            }
+
+        }
+
+        /// <summary>
+        /// Return the column name of the column at index <param name="Index"></param>.
+        /// </summary>
+        public String GetName(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName");
+
+            CheckHaveResultSet();
+
+            return _currentResultset.RowDescription[Index].name;
+        }
+
+        /// <summary>
+        /// Return the data type OID of the column at index <param name="Index"></param>.
+        /// </summary>
+        public String GetDataTypeOID(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            return _currentResultset.RowDescription[Index].type_oid.ToString();
+        }
+
+        /// <summary>
+        /// Return the data type name of the column at index <param name="Index"></param>.
+        /// </summary>
+        public String GetDataTypeName(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return _currentResultset.RowDescription[Index].type_oid.ToString();
+            }
+            else
+            {
+                return TI.Name;
+            }
+        }
+
+        /// <summary>
+        /// Return the data type of the column at index <param name="Index"></param>.
+        /// </summary>
+        public Type GetFieldType(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return typeof(String);  //Default type is string.
+            }
+            else
+            {
+                return TI.Type;
+            }
+        }
+
+        /// <summary>
+        /// Return the data DbType of the column at index <param name="Index"></param>.
+        /// </summary>
+        public DbType GetFieldDbType(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return DbType.String;
+            }
+            else
+            {
+                //return TI.DBType;
+                return DbType.String;
+            }
+        }
+
+        /// <summary>
+        /// Return the data NpgsqlDbType of the column at index <param name="Index"></param>.
+        /// </summary>
+        public NpgsqlDbType GetFieldNpgsqlDbType(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return NpgsqlDbType.Text;
+            }
+            else
+            {
+                return TI.NpgsqlDbType;
+
+            }
+        }
+
+
+        /// <summary>
+        /// Return the value of the column at index <param name="Index"></param>.
+        /// </summary>
+        public Object GetValue(Int32 Index)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue");
+
+            if (Index < 0 || Index >= _currentResultset.RowDescription.NumFields)
+            {
+                throw new IndexOutOfRangeException("Column index out of range");
+            }
+
+            CheckHaveRow();
+
+            return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[Index];
+        }
+
+        /// <summary>
+        /// Copy values from each column in the current row into <param name="Values"></param>.
+        /// </summary>
+        /// <returns>The number of column values copied.</returns>
+        public Int32 GetValues(Object[] Values)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValues");
+
+            CheckHaveRow();
+
+            // Only the number of elements in the array are filled.
+            // It's also possible to pass an array with more that FieldCount elements.
+            Int32 maxColumnIndex = (Values.Length < FieldCount) ? Values.Length : FieldCount;
+
+            for (Int32 i = 0; i < maxColumnIndex; i++)
+            {
+                Values[i] = GetValue(i);
+            }
+
+            return maxColumnIndex;
+
+        }
+
+        /// <summary>
+        /// Return the column name of the column named <param name="Name"></param>.
+        /// </summary>
+        public Int32 GetOrdinal(String Name)
+        {
+            CheckHaveResultSet();
+            return _currentResultset.RowDescription.FieldIndex(Name);
+        }
+
+        /// <summary>
+        /// Gets the value of a column in its native format.
+        /// </summary>
+        public Object this [ Int32 i ]
+        {
+            get
+            {
+                NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, i);
+                return GetValue(i);
+            }
+        }
+
+        /// <summary>
+        /// Gets the value of a column in its native format.
+        /// </summary>
+        public Object this [ String name ]
+        {
+            get
+            {
+                NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, name);
+                return GetValue(_currentResultset.RowDescription.FieldIndex(name));
+            }
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Boolean.
+        /// </summary>
+        public Boolean GetBoolean(Int32 i)
+        {
+            // Should this be done using the GetValue directly and not by converting to String
+            // and parsing from there?
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBoolean");
+
+            return (Boolean) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Byte.  Not implemented.
+        /// </summary>
+        public Byte GetByte(Int32 i)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// Gets raw data from a column.
+        /// </summary>
+        public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)
+        {
+
+            Byte[] result;
+
+            result = (Byte[]) GetValue(i);
+
+            if (buffer == null)
+                return result.Length;
+
+
+            // We just support read all the field for while. So, any fieldOffset value other than 0 will not read
+            // anything and return 0.
+
+            if (fieldOffset != 0)
+                return 0;
+
+            // [TODO] Implement blob support.
+
+            result.CopyTo(buffer, 0);
+
+
+            return result.Length;
+
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Char.  Not implemented.
+        /// </summary>
+        public Char GetChar(Int32 i)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// Gets raw data from a column.
+        /// </summary>
+        public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)
+        {
+            String             str;
+
+            str = GetString(i);
+            if (buffer == null)
+                return str.Length;
+
+            str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);
+            return buffer.GetLength(0);
+        }
+
+        /// <summary>
+        /// Gets the value of a column converted to a Guid.  Not implemented.
+        /// </summary>
+        public Guid GetGuid(Int32 i)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Int16.
+        /// </summary>
+        public Int16 GetInt16(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt16");
+
+            return (Int16) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Int32.
+        /// </summary>
+        public Int32 GetInt32(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt32");
+
+            return (Int32) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Int64.
+        /// </summary>
+        public Int64 GetInt64(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt64");
+
+            return (Int64) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Single.
+        /// </summary>
+        public Single GetFloat(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFloat");
+
+            return (Single) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Double.
+        /// </summary>
+        public Double GetDouble(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDouble");
+
+            return (Double) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as String.
+        /// </summary>
+        public String GetString(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetString");
+
+            return (String) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as Decimal.
+        /// </summary>
+        public Decimal GetDecimal(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDecimal");
+
+            return (Decimal) GetValue(i);
+        }
+
+        /// <summary>
+        /// Gets the value of a column as DateTime.
+        /// </summary>
+        public DateTime GetDateTime(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDateTime");
+
+            return (DateTime) GetValue(i);
+        }
+
+        /// <summary>
+        /// Not implemented.
+        /// </summary>
+        public IDataReader GetData(Int32 i)
+        {
+            throw new NotImplementedException();
+        }
+
+        /// <summary>
+        /// Report whether the value in a column is DBNull.
+        /// </summary>
+        public Boolean IsDBNull(Int32 i)
+        {
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsDBNull");
+
+            return (GetValue(i) == DBNull.Value);
+        }
+
+        internal NpgsqlBackendTypeInfo GetTypeInfo(Int32 FieldIndex)
+        {
+            return _currentResultset.RowDescription[FieldIndex].type_info;
+        }
+
+        private DataTable GetResultsetSchema()
+        {
+
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetResultsetSchema");
+            DataTable result = null;
+
+            NpgsqlRowDescription rd = _currentResultset.RowDescription;
+            Int16 numFields = rd.NumFields;
+            if(numFields > 0)
+            {
+                result = new DataTable("SchemaTable");
+
+                result.Columns.Add ("ColumnName", typeof (string));
+                result.Columns.Add ("ColumnOrdinal", typeof (int));
+                result.Columns.Add ("ColumnSize", typeof (int));
+                result.Columns.Add ("NumericPrecision", typeof (int));
+                result.Columns.Add ("NumericScale", typeof (int));
+                result.Columns.Add ("IsUnique", typeof (bool));
+                result.Columns.Add ("IsKey", typeof (bool));
+                DataColumn dc = result.Columns["IsKey"];
+                dc.AllowDBNull = true; // IsKey can have a DBNull
+                result.Columns.Add ("BaseCatalogName", typeof (string));
+                result.Columns.Add ("BaseColumnName", typeof (string));
+                result.Columns.Add ("BaseSchemaName", typeof (string));
+                result.Columns.Add ("BaseTableName", typeof (string));
+                result.Columns.Add ("DataType", typeof(Type));
+                result.Columns.Add ("AllowDBNull", typeof (bool));
+                result.Columns.Add ("ProviderType", typeof (int));
+                result.Columns.Add ("IsAliased", typeof (bool));
+                result.Columns.Add ("IsExpression", typeof (bool));
+                result.Columns.Add ("IsIdentity", typeof (bool));
+                result.Columns.Add ("IsAutoIncrement", typeof (bool));
+                result.Columns.Add ("IsRowVersion", typeof (bool));
+                result.Columns.Add ("IsHidden", typeof (bool));
+                result.Columns.Add ("IsLong", typeof (bool));
+                result.Columns.Add ("IsReadOnly", typeof (bool));
+
+                DataRow row;
+
+                for (Int16 i = 0; i < numFields; i++)
+                {
+                    row = result.NewRow();
+
+                    row["ColumnName"] = GetName(i);
+                    row["ColumnOrdinal"] = i + 1;
+                    row["ColumnSize"] = (int) rd[i].type_size;
+                    row["NumericPrecision"] = 0;
+                    row["NumericScale"] = 0;
+                    row["IsUnique"] = false;
+                    row["IsKey"] = DBNull.Value;
+                    row["BaseCatalogName"] = "";
+                    row["BaseColumnName"] = GetName(i);
+                    row["BaseSchemaName"] = "";
+                    row["BaseTableName"] = "";
+                    row["DataType"] = GetFieldType(i);
+                    row["AllowDBNull"] = false;
+                    row["ProviderType"] = (int) rd[i].type_oid;
+                    row["IsAliased"] = false;
+                    row["IsExpression"] = false;
+                    row["IsIdentity"] = false;
+                    row["IsAutoIncrement"] = false;
+                    row["IsRowVersion"] = false;
+                    row["IsHidden"] = false;
+                    row["IsLong"] = false;
+                    row["IsReadOnly"] = false;
+
+                    result.Rows.Add(row);
+                }
+            }
+
+            return result;
+
+        }
+
+        IEnumerator IEnumerable.GetEnumerator ()
+        {
+            return new System.Data.Common.DbEnumerator (this);
+        }
+    }
+}