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 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()) {
81 throw new InvalidOperationException("Cannot read data. No result set.");
85 private void CheckHaveRow()
90 throw new InvalidOperationException("DataReader positioned before beginning of result set. Did you call Read()?");
91 } else if (_rowIndex >= _currentResultset.Count) {
92 throw new InvalidOperationException("DataReader positioned beyond end of result set.");
98 /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
100 public void Dispose()
106 /// Releases the resources used by the <see cref="Npgsql.NpgsqlCommand">NpgsqlCommand</see>.
108 protected void Dispose (bool disposing)
110 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Dispose");
118 /// Gets a value indicating the depth of nesting for the current row. Always returns zero.
124 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Depth");
130 /// Gets a value indicating whether the data reader is closed.
132 public Boolean IsClosed
136 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsClosed");
142 /// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement.
144 public Int32 RecordsAffected
148 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected");
150 if (HaveResultSet()) {
154 String[] _returnStringTokens = ((String)_responses[_resultsetIndex]).Split(null); // whitespace separator.
157 return Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]);
159 catch (FormatException) {
166 /// Closes the data reader object.
170 if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
178 /// Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend.
180 /// <returns>True if the reader was advanced, otherwise false.</returns>
181 public Boolean NextResult()
183 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "NextResult");
185 if((_resultsetIndex + 1) < _resultsets.Count)
189 _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
198 /// Advances the data reader to the next row.
200 /// <returns>True if the reader was advanced, otherwise false.</returns>
201 public Boolean Read()
203 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Read");
205 CheckHaveResultSet();
207 if (_rowIndex < _currentResultset.Count) {
209 return (_rowIndex < _currentResultset.Count);
216 /// Returns a System.Data.DataTable that describes the column metadata of the DataReader.
218 public DataTable GetSchemaTable()
220 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable");
222 if(_currentResultsetSchema == null)
223 _currentResultsetSchema = GetResultsetSchema();
225 return _currentResultsetSchema;
229 /// Gets the number of columns in the current row.
231 public Int32 FieldCount
236 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "FieldCount");
238 if (! HaveResultSet()) //Executed a non return rows query.
241 return _currentResultset.RowDescription.NumFields;
249 /// Return the column name of the column at index <param name="Index"></param>.
251 public String GetName(Int32 Index)
253 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName");
255 if (! HaveResultSet())
258 return _currentResultset.RowDescription[Index].name;
262 /// Return the data type name of the column at index <param name="Index"></param>.
264 public String GetDataTypeName(Int32 Index)
266 // FIXME: have a type name instead of the oid
267 if (! HaveResultSet())
270 return (_currentResultset.RowDescription[Index].type_oid).ToString();
274 /// Return the data type of the column at index <param name="Index"></param>.
276 public Type GetFieldType(Int32 Index)
278 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
281 if (! HaveResultSet())
284 return NpgsqlTypesHelper.GetSystemTypeFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[Index].type_oid);
288 /// Return the value of the column at index <param name="Index"></param>.
290 public Object GetValue(Int32 Index)
292 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue");
294 if (Index < 0 || Index >= _currentResultset.RowDescription.NumFields) {
295 throw new IndexOutOfRangeException("Column index out of range");
300 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[Index];
304 /// Copy values from each column in the current row into <param name="Values"></param>.
306 /// <returns>The number of column values copied.</returns>
307 public Int32 GetValues(Object[] Values)
309 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValues");
313 // Only the number of elements in the array are filled.
314 // It's also possible to pass an array with more that FieldCount elements.
315 Int32 maxColumnIndex = (Values.Length < FieldCount) ? Values.Length : FieldCount;
317 for (Int32 i = 0; i < maxColumnIndex; i++) {
318 Values[i] = GetValue(i);
321 return maxColumnIndex;
326 /// Return the column name of the column named <param name="Name"></param>.
328 public Int32 GetOrdinal(String Name)
330 CheckHaveResultSet();
331 return _currentResultset.RowDescription.FieldIndex(Name);
335 /// Gets the value of a column in its native format.
337 public Object this [ Int32 i ]
341 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, i);
347 /// Gets the value of a column in its native format.
349 public Object this [ String name ]
353 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, name);
354 return GetValue(_currentResultset.RowDescription.FieldIndex(name));
359 /// Gets the value of a column converted to a Boolean.
361 public Boolean GetBoolean(Int32 i)
363 // Should this be done using the GetValue directly and not by converting to String
364 // and parsing from there?
365 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBoolean");
367 return Convert.ToBoolean(GetValue(i));
371 /// Gets the value of a column converted to a Byte. Not implemented.
373 public Byte GetByte(Int32 i)
375 throw new NotImplementedException();
379 /// Gets raw data from a column.
381 public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)
386 result = (Byte[]) GetValue(i);
388 // [TODO] Implement blob support.
391 result.CopyTo(buffer, 0);
394 return result.Length;
399 /// Gets the value of a column converted to a Char. Not implemented.
401 public Char GetChar(Int32 i)
403 throw new NotImplementedException();
407 /// Gets raw data from a column.
409 public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)
417 str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);
418 return buffer.GetLength(0);
422 /// Gets the value of a column converted to a Guid. Not implemented.
424 public Guid GetGuid(Int32 i)
426 throw new NotImplementedException();
430 /// Gets the value of a column converted to Int16.
432 public Int16 GetInt16(Int32 i)
434 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt16");
436 return Convert.ToInt16(GetValue(i));
440 /// Gets the value of a column converted to Int32.
442 public Int32 GetInt32(Int32 i)
444 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt32");
446 return Convert.ToInt32(GetValue(i));
450 /// Gets the value of a column converted to Int64.
452 public Int64 GetInt64(Int32 i)
454 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt64");
456 return Convert.ToInt64(GetValue(i));
460 /// Gets the value of a column converted to Single.
462 public Single GetFloat(Int32 i)
464 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFloat");
466 return Convert.ToSingle(GetValue(i));
470 /// Gets the value of a column converted to Double.
472 public Double GetDouble(Int32 i)
474 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDouble");
476 return Convert.ToDouble(GetValue(i));
480 /// Gets the value of a column converted to a String.
482 public String GetString(Int32 i)
484 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetString");
486 return Convert.ToString(GetValue(i));
490 /// Gets the value of a column converted to Decimal.
492 public Decimal GetDecimal(Int32 i)
494 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDecimal");
496 return Convert.ToDecimal(GetValue(i));
500 /// Gets the value of a column converted to a DateTime.
502 public DateTime GetDateTime(Int32 i)
504 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDateTime");
506 return Convert.ToDateTime(GetValue(i));
512 public IDataReader GetData(Int32 i)
514 throw new NotImplementedException();
518 /// Report whether the value in a column is DBNull.
520 public Boolean IsDBNull(Int32 i)
522 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsDBNull");
524 return (GetValue(i) == DBNull.Value);
527 private DataTable GetResultsetSchema()
530 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetResultsetSchema");
531 DataTable result = null;
533 NpgsqlRowDescription rd = _currentResultset.RowDescription;
534 Int16 numFields = rd.NumFields;
537 result = new DataTable("SchemaTable");
539 result.Columns.Add ("ColumnName", typeof (string));
540 result.Columns.Add ("ColumnOrdinal", typeof (int));
541 result.Columns.Add ("ColumnSize", typeof (int));
542 result.Columns.Add ("NumericPrecision", typeof (int));
543 result.Columns.Add ("NumericScale", typeof (int));
544 result.Columns.Add ("IsUnique", typeof (bool));
545 result.Columns.Add ("IsKey", typeof (bool));
546 DataColumn dc = result.Columns["IsKey"];
547 dc.AllowDBNull = true; // IsKey can have a DBNull
548 result.Columns.Add ("BaseCatalogName", typeof (string));
549 result.Columns.Add ("BaseColumnName", typeof (string));
550 result.Columns.Add ("BaseSchemaName", typeof (string));
551 result.Columns.Add ("BaseTableName", typeof (string));
552 result.Columns.Add ("DataType", typeof(Type));
553 result.Columns.Add ("AllowDBNull", typeof (bool));
554 result.Columns.Add ("ProviderType", typeof (int));
555 result.Columns.Add ("IsAliased", typeof (bool));
556 result.Columns.Add ("IsExpression", typeof (bool));
557 result.Columns.Add ("IsIdentity", typeof (bool));
558 result.Columns.Add ("IsAutoIncrement", typeof (bool));
559 result.Columns.Add ("IsRowVersion", typeof (bool));
560 result.Columns.Add ("IsHidden", typeof (bool));
561 result.Columns.Add ("IsLong", typeof (bool));
562 result.Columns.Add ("IsReadOnly", typeof (bool));
566 for (Int16 i = 0; i < numFields; i++)
568 row = result.NewRow();
570 row["ColumnName"] = GetName(i);
571 row["ColumnOrdinal"] = i + 1;
572 row["ColumnSize"] = (int) rd[i].type_size;
573 row["NumericPrecision"] = 0;
574 row["NumericScale"] = 0;
575 row["IsUnique"] = false;
576 row["IsKey"] = DBNull.Value;
577 row["BaseCatalogName"] = "";
578 row["BaseColumnName"] = GetName(i);
579 row["BaseSchemaName"] = "";
580 row["BaseTableName"] = "";
581 row["DataType"] = GetFieldType(i);
582 row["AllowDBNull"] = false;
583 row["ProviderType"] = (int) rd[i].type_oid;
584 row["IsAliased"] = false;
585 row["IsExpression"] = false;
586 row["IsIdentity"] = false;
587 row["IsAutoIncrement"] = false;
588 row["IsRowVersion"] = false;
589 row["IsHidden"] = false;
590 row["IsLong"] = false;
591 row["IsReadOnly"] = false;
593 result.Rows.Add(row);
601 IEnumerator IEnumerable.GetEnumerator ()
603 return new System.Data.Common.DbEnumerator (this);