2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlDataReader.cs
index daa0a5d687376f5310000e5d439c18f1410464a5..1594d87f22f0030aabb8ec53ba389e529a1aecd3 100755 (executable)
 using System;
 using System.Data;
 using System.Collections;
-using NpgsqlTypes;
 
+using NpgsqlTypes;
 
 namespace Npgsql
 {
-
-    public class NpgsqlDataReader : IDataReader, IEnumerable
+    /// <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;
@@ -46,7 +45,6 @@ namespace Npgsql
         private DataTable                      _currentResultsetSchema;
         private CommandBehavior     _behavior;
         private Boolean             _isClosed;
-        
 
 
         // Logging related values
@@ -59,37 +57,55 @@ namespace Npgsql
             _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 CanRead()
+        private Boolean HaveRow()
         {
-            //NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CanRead");
-            /*if (_currentResultset == null)
-               return false;*/
-            return ((_currentResultset != null) && 
-                    (_currentResultset.Count > 0) && 
-                    (_rowIndex < _currentResultset.Count));
+            return (HaveResultSet() && _rowIndex >= 0 && _rowIndex < _currentResultset.Count);
+        }
 
+        private void CheckHaveResultSet()
+        {
+            if (! HaveResultSet())
+            {
+                throw new InvalidOperationException("Cannot read data. No result set.");
+            }
         }
 
-        private void CheckCanRead()
+        private void CheckHaveRow()
         {
-            if (!CanRead())
-                throw new InvalidOperationException("Cannot read data");
+            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>
@@ -101,6 +117,10 @@ namespace Npgsql
                 this.Close();
             }
         }
+
+        /// <summary>
+        /// Gets a value indicating the depth of nesting for the current row.  Always returns zero.
+        /// </summary>
         public Int32 Depth
         {
             get
@@ -110,6 +130,9 @@ namespace Npgsql
             }
         }
 
+        /// <summary>
+        /// Gets a value indicating whether the data reader is closed.
+        /// </summary>
         public Boolean IsClosed
         {
             get
@@ -119,37 +142,68 @@ namespace Npgsql
             }
         }
 
+        /// <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 (CanRead())
+                if (HaveResultSet())
+                {
                     return -1;
+                }
 
                 String[] _returnStringTokens = ((String)_responses[_resultsetIndex]).Split(null);      // whitespace separator.
 
-                return Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]);
+                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)
+            if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
             {
                 _connection.Close();
-                _isClosed = true;
+
             }
 
+            _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++;
@@ -162,19 +216,30 @@ namespace Npgsql
 
         }
 
+        /// <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");
 
-            _rowIndex++;
-            
-            if (!CanRead())
-                return false;
-            else
-                return true;
+            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");
@@ -183,18 +248,19 @@ namespace Npgsql
                 _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 (_currentResultset == null) //Executed a non return rows query.
+
+                if (! HaveResultSet()) //Executed a non return rows query.
                     return -1;
                 else
                     return _currentResultset.RowDescription.NumFields;
@@ -204,75 +270,171 @@ namespace Npgsql
 
         }
 
-        public String GetName(Int32 i)
+        /// <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");
 
-            if (_currentResultset == null)
-                return String.Empty;
+            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 _currentResultset.RowDescription[i].name;
+            {
+                return TI.Name;
+            }
         }
 
-        public String GetDataTypeName(Int32 i)
+        /// <summary>
+        /// Return the data type of the column at index <param name="Index"></param>.
+        /// </summary>
+        public Type GetFieldType(Int32 Index)
         {
-            // FIXME: have a type name instead of the oid
-            if (_currentResultset == null)
-                return String.Empty;
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
+
+            CheckHaveResultSet();
+
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
+
+            if (TI == null)
+            {
+                return typeof(String);  //Default type is string.
+            }
             else
-                return (_currentResultset.RowDescription[i].type_oid).ToString();
+            {
+                return TI.Type;
+            }
         }
 
-        public Type GetFieldType(Int32 i)
+        /// <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 (_currentResultset == null)
-                return null;
+            if (TI == null)
+            {
+                return DbType.String;
+            }
             else
-                return NpgsqlTypesHelper.GetSystemTypeFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[i].type_oid);
+            {
+                //return TI.DBType;
+                return DbType.String;
+            }
         }
 
-        public Object GetValue(Int32 i)
+        /// <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, "GetValue");
+            NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
 
-            CheckCanRead();
+            CheckHaveResultSet();
 
-            if (i < 0)
-                throw new InvalidOperationException("Cannot read data. Column less than 0 specified.");
-            if (_rowIndex < 0)
-                throw new InvalidOperationException("Cannot read data. DataReader not initialized. Maybe you forgot to call Read()?");
-            return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[i];
+            NpgsqlBackendTypeInfo  TI = GetTypeInfo(Index);
 
+            if (TI == null)
+            {
+                return NpgsqlDbType.Text;
+            }
+            else
+            {
+                return TI.NpgsqlDbType;
 
+            }
         }
 
 
-        public Int32 GetValues(Object[] values)
+        /// <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");
 
-            CheckCanRead();
+            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;
+            Int32 maxColumnIndex = (Values.Length < FieldCount) ? Values.Length : FieldCount;
 
             for (Int32 i = 0; i < maxColumnIndex; i++)
-                values[i] = GetValue(i);
+            {
+                Values[i] = GetValue(i);
+            }
 
             return maxColumnIndex;
 
         }
 
-        public Int32 GetOrdinal(String name)
+        /// <summary>
+        /// Return the column name of the column named <param name="Name"></param>.
+        /// </summary>
+        public Int32 GetOrdinal(String Name)
         {
-            CheckCanRead();
-            return _currentResultset.RowDescription.FieldIndex(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
@@ -282,32 +444,41 @@ namespace Npgsql
             }
         }
 
+        /// <summary>
+        /// Gets the value of a column in its native format.
+        /// </summary>
         public Object this [ String name ]
         {
             get
             {
-                //throw new NotImplementedException();
                 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)
         {
 
@@ -315,21 +486,36 @@ namespace Npgsql
 
             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.
-            if (buffer != null)
-            {
-                result.CopyTo(buffer, 0);
-            }
+
+            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;
@@ -342,29 +528,37 @@ namespace Npgsql
             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");
@@ -372,20 +566,29 @@ namespace Npgsql
             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");
@@ -393,6 +596,9 @@ namespace Npgsql
             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");
@@ -400,6 +606,9 @@ namespace Npgsql
             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");
@@ -407,18 +616,27 @@ namespace Npgsql
             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");
 
-            CheckCanRead();
+            return (GetValue(i) == DBNull.Value);
+        }
 
-            return ((NpgsqlAsciiRow)_currentResultset[_rowIndex]).IsNull(i);
+        internal NpgsqlBackendTypeInfo GetTypeInfo(Int32 FieldIndex)
+        {
+            return _currentResultset.RowDescription[FieldIndex].type_info;
         }
 
         private DataTable GetResultsetSchema()
@@ -495,8 +713,6 @@ namespace Npgsql
 
         }
 
-
-
         IEnumerator IEnumerable.GetEnumerator ()
         {
             return new System.Data.Common.DbEnumerator (this);