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 public class NpgsqlDataReader : IDataReader, IEnumerable
40 private NpgsqlConnection _connection;
41 private ArrayList _resultsets;
42 private ArrayList _responses;
43 private Int32 _rowIndex;
44 private Int32 _resultsetIndex;
45 private NpgsqlResultSet _currentResultset;
46 private DataTable _currentResultsetSchema;
49 // Logging related values
50 private static readonly String CLASSNAME = "NpgsqlDataReader";
52 internal NpgsqlDataReader( ArrayList resultsets, ArrayList responses, NpgsqlConnection connection)
54 _resultsets = resultsets;
55 _responses = responses;
56 _connection = connection;
60 _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
66 private Boolean CanRead()
68 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".CanRead() ", LogLevel.Debug);
69 /*if (_currentResultset == null)
71 return ((_currentResultset != null) && (_currentResultset.Count > 0));
75 private void CheckCanRead()
78 throw new InvalidOperationException("Cannot read data");
89 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_Depth() ", LogLevel.Debug);
94 public Boolean IsClosed
98 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_IsClosed()", LogLevel.Debug);
103 public Int32 RecordsAffected
107 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_RecordsAffected()", LogLevel.Debug);
109 /*if (_currentResultset == null)
110 return 0; //[FIXME] Get the actual number of rows deleted, updated or inserted.
117 String[] ret_string_tokens = ((String)_responses[_resultsetIndex]).Split(null); // whitespace separator.
119 return Int32.Parse(ret_string_tokens[ret_string_tokens.Length - 1]);
129 public Boolean NextResult()
131 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".NextResult()", LogLevel.Debug);
132 //throw new NotImplementedException();
134 //[FIXME] Should the currentResultset not be modified
135 // in case there aren't any more resultsets?
136 // SqlClient modify to a invalid resultset and throws exceptions
137 // when trying to access any data.
140 if((_resultsetIndex + 1) < _resultsets.Count)
144 _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
152 public Boolean Read()
154 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".Read()", LogLevel.Debug);
160 return (_rowIndex < _currentResultset.Count);
163 public DataTable GetSchemaTable()
165 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetSchemaTable()", LogLevel.Debug);
166 //throw new NotImplementedException();
169 return null; //[FIXME] Should we return null or throw an exception??
171 if(_currentResultsetSchema == null)
172 _currentResultsetSchema = GetResultsetSchema();
174 return _currentResultsetSchema;
179 public Int32 FieldCount
184 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_FieldCount()", LogLevel.Debug);
185 //return ((_currentResultset == null) ? 0 : _currentResultset.RowDescription.NumFields);
187 return _currentResultset.RowDescription.NumFields;
195 public String GetName(Int32 i)
197 //throw new NotImplementedException();
198 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetName(Int32)", LogLevel.Debug);
201 return _currentResultset.RowDescription[i].name;
206 public String GetDataTypeName(Int32 i)
208 // FIXME: have a type name instead of the oid
209 return (_currentResultset.RowDescription[i].type_oid).ToString();
212 public Type GetFieldType(Int32 i)
214 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetFieldType(Int32)", LogLevel.Debug);
217 //return Type.GetType(NpgsqlTypesHelper.GetSystemTypeNameFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[i].type_oid));
219 return NpgsqlTypesHelper.GetSystemTypeFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[i].type_oid);
222 public Object GetValue(Int32 i)
224 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetValue(Int32)", LogLevel.Debug);
228 if (i < 0 || _rowIndex < 0)
229 throw new InvalidOperationException("Cannot read data.");
230 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[i];
236 public Int32 GetValues(Object[] values)
238 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetValues(Object[])", LogLevel.Debug);
242 // Only the number of elements in the array are filled.
243 // It's also possible to pass an array with more that FieldCount elements.
244 Int32 maxColumnIndex = (values.Length < FieldCount) ? values.Length : FieldCount;
246 for (Int32 i = 0; i < maxColumnIndex; i++)
247 values[i] = GetValue(i);
249 return maxColumnIndex;
253 public Int32 GetOrdinal(String name)
256 return _currentResultset.RowDescription.FieldIndex(name);
259 public Object this [ Int32 i ]
263 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".this[Int32]", LogLevel.Debug);
268 public Object this [ String name ]
272 //throw new NotImplementedException();
273 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".this[String]", LogLevel.Debug);
274 return GetValue(_currentResultset.RowDescription.FieldIndex(name));
278 public Boolean GetBoolean(Int32 i)
280 // Should this be done using the GetValue directly and not by converting to String
281 // and parsing from there?
282 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetBoolean(Int32)", LogLevel.Debug);
285 return (Boolean) GetValue(i);
289 public Byte GetByte(Int32 i)
291 throw new NotImplementedException();
294 public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)
296 throw new NotImplementedException();
299 public Char GetChar(Int32 i)
301 throw new NotImplementedException();
304 public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)
312 str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);
313 return buffer.GetLength(0);
316 public Guid GetGuid(Int32 i)
318 throw new NotImplementedException();
321 public Int16 GetInt16(Int32 i)
323 // Should this be done using the GetValue directly and not by converting to String
324 // and parsing from there?
325 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetInt16(Int32)", LogLevel.Debug);
328 return Int16.Parse((String) this[i]);
329 } catch (System.FormatException)
331 throw new System.InvalidCastException();
334 return (Int16) GetValue(i);
340 public Int32 GetInt32(Int32 i)
342 // Should this be done using the GetValue directly and not by converting to String
343 // and parsing from there?
344 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetInt32(Int32)", LogLevel.Debug);
347 return Int32.Parse((String) this[i]);
348 } catch (System.FormatException)
350 throw new System.InvalidCastException();
354 return (Int32) GetValue(i);
359 public Int64 GetInt64(Int32 i)
361 // Should this be done using the GetValue directly and not by converting to String
362 // and parsing from there?
363 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetInt64(Int32)", LogLevel.Debug);
366 return Int64.Parse((String) this[i]);
367 } catch (System.FormatException)
369 throw new System.InvalidCastException();
371 return (Int64) GetValue(i);
374 public Single GetFloat(Int32 i)
376 // Should this be done using the GetValue directly and not by converting to String
377 // and parsing from there?
378 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetFloat(Int32)", LogLevel.Debug);
381 return Single.Parse((String) this[i]);
382 } catch (System.FormatException)
384 throw new System.InvalidCastException();
388 public Double GetDouble(Int32 i)
390 // Should this be done using the GetValue directly and not by converting to String
391 // and parsing from there?
392 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetDouble(Int32)", LogLevel.Debug);
395 return Double.Parse((String) this[i]);
396 } catch (System.FormatException)
398 throw new System.InvalidCastException();
402 public String GetString(Int32 i)
404 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetString(Int32)", LogLevel.Debug);
405 return (String) GetValue(i);
408 public Decimal GetDecimal(Int32 i)
410 // Should this be done using the GetValue directly and not by converting to String
411 // and parsing from there?
412 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetDecimal(Int32)", LogLevel.Debug);
415 return (Decimal) GetValue(i);
418 public DateTime GetDateTime(Int32 i)
420 //throw new NotImplementedException();
421 return (DateTime) GetValue(i);
424 public IDataReader GetData(Int32 i)
426 throw new NotImplementedException();
429 public Boolean IsDBNull(Int32 i)
431 //throw new NotImplementedException();
434 throw new InvalidOperationException("Cannot read data");
435 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex]).IsNull(i);
443 private DataTable GetResultsetSchema()
446 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetResultsetSchema()", LogLevel.Debug);
447 DataTable result = null;
449 NpgsqlRowDescription rd = _currentResultset.RowDescription;
450 Int16 numFields = rd.NumFields;
452 result = new DataTable("SchemaTable");
454 result.Columns.Add ("ColumnName", typeof (string));
455 result.Columns.Add ("ColumnOrdinal", typeof (int));
456 result.Columns.Add ("ColumnSize", typeof (int));
457 result.Columns.Add ("NumericPrecision", typeof (int));
458 result.Columns.Add ("NumericScale", typeof (int));
459 result.Columns.Add ("IsUnique", typeof (bool));
460 result.Columns.Add ("IsKey", typeof (bool));
461 DataColumn dc = result.Columns["IsKey"];
462 dc.AllowDBNull = true; // IsKey can have a DBNull
463 result.Columns.Add ("BaseCatalogName", typeof (string));
464 result.Columns.Add ("BaseColumnName", typeof (string));
465 result.Columns.Add ("BaseSchemaName", typeof (string));
466 result.Columns.Add ("BaseTableName", typeof (string));
467 result.Columns.Add ("DataType", typeof(Type));
468 result.Columns.Add ("AllowDBNull", typeof (bool));
469 result.Columns.Add ("ProviderType", typeof (int));
470 result.Columns.Add ("IsAliased", typeof (bool));
471 result.Columns.Add ("IsExpression", typeof (bool));
472 result.Columns.Add ("IsIdentity", typeof (bool));
473 result.Columns.Add ("IsAutoIncrement", typeof (bool));
474 result.Columns.Add ("IsRowVersion", typeof (bool));
475 result.Columns.Add ("IsHidden", typeof (bool));
476 result.Columns.Add ("IsLong", typeof (bool));
477 result.Columns.Add ("IsReadOnly", typeof (bool));
481 for (Int16 i = 0; i < numFields; i++) {
482 row = result.NewRow();
484 row["ColumnName"] = GetName(i);
485 row["ColumnOrdinal"] = i + 1;
486 row["ColumnSize"] = (int) rd[i].type_size;
487 row["NumericPrecision"] = 0;
488 row["NumericScale"] = 0;
489 row["IsUnique"] = false;
490 row["IsKey"] = DBNull.Value;
491 row["BaseCatalogName"] = "";
492 row["BaseColumnName"] = GetName(i);
493 row["BaseSchemaName"] = "";
494 row["BaseTableName"] = "";
495 row["DataType"] = GetFieldType(i);
496 row["AllowDBNull"] = false;
497 row["ProviderType"] = (int) rd[i].type_oid;
498 row["IsAliased"] = false;
499 row["IsExpression"] = false;
500 row["IsIdentity"] = false;
501 row["IsAutoIncrement"] = false;
502 row["IsRowVersion"] = false;
503 row["IsHidden"] = false;
504 row["IsLong"] = false;
505 row["IsReadOnly"] = false;
507 result.Rows.Add(row);
517 IEnumerator IEnumerable.GetEnumerator ()
519 return new System.Data.Common.DbEnumerator (this);