2 // Npgsql.NpgsqlDataReader.cs
5 // Francisco Jr. (fxjrlists@yahoo.com.br)
7 // Copyright (C) 2002 The Npgsql Development Team
8 // npgsql-general@gborg.postgresql.org
9 // http://gborg.postgresql.org/project/npgsql/projdisplay.php
12 // This library is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU Lesser General Public
14 // License as published by the Free Software Foundation; either
15 // version 2.1 of the License, or (at your option) any later version.
17 // This library is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 // Lesser General Public License for more details.
22 // You should have received a copy of the GNU Lesser General Public
23 // License along with this library; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 using System.Collections;
35 /// Provides a means of reading a forward-only stream of rows from a PostgreSQL backend. This class cannot be inherited.
37 public sealed class NpgsqlDataReader : IDataReader, IEnumerable
39 private NpgsqlConnection _connection;
40 private ArrayList _resultsets;
41 private ArrayList _responses;
42 private Int32 _rowIndex;
43 private Int32 _resultsetIndex;
44 private NpgsqlResultSet _currentResultset;
45 private DataTable _currentResultsetSchema;
46 private CommandBehavior _behavior;
47 private Boolean _isClosed;
50 // Logging related values
51 private static readonly String CLASSNAME = "NpgsqlDataReader";
53 internal NpgsqlDataReader( ArrayList resultsets, ArrayList responses, NpgsqlConnection connection, CommandBehavior behavior)
55 _resultsets = resultsets;
56 _responses = responses;
57 _connection = connection;
61 if (_resultsets.Count > 0)
62 _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
68 private Boolean HaveResultSet()
70 return (_currentResultset != null);
73 private Boolean HaveRow()
75 return (HaveResultSet() && _rowIndex >= 0 && _rowIndex < _currentResultset.Count);
78 private void CheckHaveResultSet()
80 if (! HaveResultSet())
82 throw new InvalidOperationException("Cannot read data. No result set.");
86 private void CheckHaveRow()
92 throw new InvalidOperationException("DataReader positioned before beginning of result set. Did you call Read()?");
94 else if (_rowIndex >= _currentResultset.Count)
96 throw new InvalidOperationException("DataReader positioned beyond end of result set.");
102 /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
104 public void Dispose()
110 /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
112 protected void Dispose (bool disposing)
114 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
122 /// Gets a value indicating the depth of nesting for the current row. Always returns zero.
128 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Depth");
134 /// Gets a value indicating whether the data reader is closed.
136 public Boolean IsClosed
140 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsClosed");
146 /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
148 public Int32 RecordsAffected
152 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected");
159 String[] _returnStringTokens = ((String)_responses[_resultsetIndex]).Split(null); // whitespace separator.
163 return Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]);
165 catch (FormatException)
173 /// Indicates if NpgsqlDatareader has rows to be read.
176 public Boolean HasRows
180 return _currentResultset.Count > 0;
186 /// Closes the data reader object.
190 if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
200 /// Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend.
202 /// <returns>True if the reader was advanced, otherwise false.</returns>
203 public Boolean NextResult()
205 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "NextResult");
207 if((_resultsetIndex + 1) < _resultsets.Count)
211 _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
220 /// Advances the data reader to the next row.
222 /// <returns>True if the reader was advanced, otherwise false.</returns>
223 public Boolean Read()
225 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Read");
227 CheckHaveResultSet();
229 if (_rowIndex < _currentResultset.Count)
232 return (_rowIndex < _currentResultset.Count);
241 /// Returns a System.Data.DataTable that describes the column metadata of the DataReader.
243 public DataTable GetSchemaTable()
245 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable");
247 if(_currentResultsetSchema == null)
248 _currentResultsetSchema = GetResultsetSchema();
250 return _currentResultsetSchema;
254 /// Gets the number of columns in the current row.
256 public Int32 FieldCount
261 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "FieldCount");
263 if (! HaveResultSet()) //Executed a non return rows query.
266 return _currentResultset.RowDescription.NumFields;
274 /// Return the column name of the column at index <param name="Index"></param>.
276 public String GetName(Int32 Index)
278 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName");
280 CheckHaveResultSet();
282 return _currentResultset.RowDescription[Index].name;
286 /// Return the data type OID of the column at index <param name="Index"></param>.
288 public String GetDataTypeOID(Int32 Index)
290 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName");
292 CheckHaveResultSet();
294 NpgsqlBackendTypeInfo TI = GetTypeInfo(Index);
296 return _currentResultset.RowDescription[Index].type_oid.ToString();
300 /// Return the data type name of the column at index <param name="Index"></param>.
302 public String GetDataTypeName(Int32 Index)
304 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDataTypeName");
306 CheckHaveResultSet();
308 NpgsqlBackendTypeInfo TI = GetTypeInfo(Index);
312 return _currentResultset.RowDescription[Index].type_oid.ToString();
321 /// Return the data type of the column at index <param name="Index"></param>.
323 public Type GetFieldType(Int32 Index)
325 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
327 CheckHaveResultSet();
329 NpgsqlBackendTypeInfo TI = GetTypeInfo(Index);
333 return typeof(String); //Default type is string.
342 /// Return the data DbType of the column at index <param name="Index"></param>.
344 public DbType GetFieldDbType(Int32 Index)
346 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
348 CheckHaveResultSet();
350 NpgsqlBackendTypeInfo TI = GetTypeInfo(Index);
354 return DbType.String;
359 return DbType.String;
364 /// Return the data NpgsqlDbType of the column at index <param name="Index"></param>.
366 public NpgsqlDbType GetFieldNpgsqlDbType(Int32 Index)
368 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
370 CheckHaveResultSet();
372 NpgsqlBackendTypeInfo TI = GetTypeInfo(Index);
376 return NpgsqlDbType.Text;
380 return TI.NpgsqlDbType;
387 /// Return the value of the column at index <param name="Index"></param>.
389 public Object GetValue(Int32 Index)
391 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue");
393 if (Index < 0 || Index >= _currentResultset.RowDescription.NumFields)
395 throw new IndexOutOfRangeException("Column index out of range");
400 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[Index];
404 /// Copy values from each column in the current row into <param name="Values"></param>.
406 /// <returns>The number of column values copied.</returns>
407 public Int32 GetValues(Object[] Values)
409 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValues");
413 // Only the number of elements in the array are filled.
414 // It's also possible to pass an array with more that FieldCount elements.
415 Int32 maxColumnIndex = (Values.Length < FieldCount) ? Values.Length : FieldCount;
417 for (Int32 i = 0; i < maxColumnIndex; i++)
419 Values[i] = GetValue(i);
422 return maxColumnIndex;
427 /// Return the column name of the column named <param name="Name"></param>.
429 public Int32 GetOrdinal(String Name)
431 CheckHaveResultSet();
432 return _currentResultset.RowDescription.FieldIndex(Name);
436 /// Gets the value of a column in its native format.
438 public Object this [ Int32 i ]
442 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, i);
448 /// Gets the value of a column in its native format.
450 public Object this [ String name ]
454 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, name);
455 return GetValue(_currentResultset.RowDescription.FieldIndex(name));
460 /// Gets the value of a column converted to a Boolean.
462 public Boolean GetBoolean(Int32 i)
464 // Should this be done using the GetValue directly and not by converting to String
465 // and parsing from there?
466 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBoolean");
468 return Convert.ToBoolean(GetValue(i));
472 /// Gets the value of a column converted to a Byte. Not implemented.
474 public Byte GetByte(Int32 i)
476 throw new NotImplementedException();
480 /// Gets raw data from a column.
482 public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)
487 result = (Byte[]) GetValue(i);
490 return result.Length;
493 // We just support read all the field for while. So, any fieldOffset value other than 0 will not read
494 // anything and return 0.
496 if (fieldOffset != 0)
499 // [TODO] Implement blob support.
501 result.CopyTo(buffer, 0);
504 return result.Length;
509 /// Gets the value of a column converted to a Char. Not implemented.
511 public Char GetChar(Int32 i)
513 throw new NotImplementedException();
517 /// Gets raw data from a column.
519 public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)
527 str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);
528 return buffer.GetLength(0);
532 /// Gets the value of a column converted to a Guid. Not implemented.
534 public Guid GetGuid(Int32 i)
536 throw new NotImplementedException();
540 /// Gets the value of a column converted to Int16.
542 public Int16 GetInt16(Int32 i)
544 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt16");
546 return Convert.ToInt16(GetValue(i));
550 /// Gets the value of a column converted to Int32.
552 public Int32 GetInt32(Int32 i)
554 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt32");
556 return Convert.ToInt32(GetValue(i));
560 /// Gets the value of a column converted to Int64.
562 public Int64 GetInt64(Int32 i)
564 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt64");
566 return Convert.ToInt64(GetValue(i));
570 /// Gets the value of a column converted to Single.
572 public Single GetFloat(Int32 i)
574 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFloat");
576 return Convert.ToSingle(GetValue(i));
580 /// Gets the value of a column converted to Double.
582 public Double GetDouble(Int32 i)
584 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDouble");
586 return Convert.ToDouble(GetValue(i));
590 /// Gets the value of a column converted to a String.
592 public String GetString(Int32 i)
594 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetString");
596 return Convert.ToString(GetValue(i));
600 /// Gets the value of a column converted to Decimal.
602 public Decimal GetDecimal(Int32 i)
604 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDecimal");
606 return Convert.ToDecimal(GetValue(i));
610 /// Gets the value of a column converted to a DateTime.
612 public DateTime GetDateTime(Int32 i)
614 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDateTime");
616 return Convert.ToDateTime(GetValue(i));
622 public IDataReader GetData(Int32 i)
624 throw new NotImplementedException();
628 /// Report whether the value in a column is DBNull.
630 public Boolean IsDBNull(Int32 i)
632 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsDBNull");
634 return (GetValue(i) == DBNull.Value);
637 internal NpgsqlBackendTypeInfo GetTypeInfo(Int32 FieldIndex)
639 return _currentResultset.RowDescription[FieldIndex].type_info;
642 private DataTable GetResultsetSchema()
645 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetResultsetSchema");
646 DataTable result = null;
648 NpgsqlRowDescription rd = _currentResultset.RowDescription;
649 Int16 numFields = rd.NumFields;
652 result = new DataTable("SchemaTable");
654 result.Columns.Add ("ColumnName", typeof (string));
655 result.Columns.Add ("ColumnOrdinal", typeof (int));
656 result.Columns.Add ("ColumnSize", typeof (int));
657 result.Columns.Add ("NumericPrecision", typeof (int));
658 result.Columns.Add ("NumericScale", typeof (int));
659 result.Columns.Add ("IsUnique", typeof (bool));
660 result.Columns.Add ("IsKey", typeof (bool));
661 DataColumn dc = result.Columns["IsKey"];
662 dc.AllowDBNull = true; // IsKey can have a DBNull
663 result.Columns.Add ("BaseCatalogName", typeof (string));
664 result.Columns.Add ("BaseColumnName", typeof (string));
665 result.Columns.Add ("BaseSchemaName", typeof (string));
666 result.Columns.Add ("BaseTableName", typeof (string));
667 result.Columns.Add ("DataType", typeof(Type));
668 result.Columns.Add ("AllowDBNull", typeof (bool));
669 result.Columns.Add ("ProviderType", typeof (int));
670 result.Columns.Add ("IsAliased", typeof (bool));
671 result.Columns.Add ("IsExpression", typeof (bool));
672 result.Columns.Add ("IsIdentity", typeof (bool));
673 result.Columns.Add ("IsAutoIncrement", typeof (bool));
674 result.Columns.Add ("IsRowVersion", typeof (bool));
675 result.Columns.Add ("IsHidden", typeof (bool));
676 result.Columns.Add ("IsLong", typeof (bool));
677 result.Columns.Add ("IsReadOnly", typeof (bool));
681 for (Int16 i = 0; i < numFields; i++)
683 row = result.NewRow();
685 row["ColumnName"] = GetName(i);
686 row["ColumnOrdinal"] = i + 1;
687 row["ColumnSize"] = (int) rd[i].type_size;
688 row["NumericPrecision"] = 0;
689 row["NumericScale"] = 0;
690 row["IsUnique"] = false;
691 row["IsKey"] = DBNull.Value;
692 row["BaseCatalogName"] = "";
693 row["BaseColumnName"] = GetName(i);
694 row["BaseSchemaName"] = "";
695 row["BaseTableName"] = "";
696 row["DataType"] = GetFieldType(i);
697 row["AllowDBNull"] = false;
698 row["ProviderType"] = (int) rd[i].type_oid;
699 row["IsAliased"] = false;
700 row["IsExpression"] = false;
701 row["IsIdentity"] = false;
702 row["IsAutoIncrement"] = false;
703 row["IsRowVersion"] = false;
704 row["IsHidden"] = false;
705 row["IsLong"] = false;
706 row["IsReadOnly"] = false;
708 result.Rows.Add(row);
716 IEnumerator IEnumerable.GetEnumerator ()
718 return new System.Data.Common.DbEnumerator (this);