2003-11-11 Pedro Mart�nez Juli� <yoros@wanadoo.es>
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlDataReader.cs
1
2 // Npgsql.NpgsqlDataReader.cs
3 // 
4 // Author:
5 //      Francisco Jr. (fxjrlists@yahoo.com.br)
6 //
7 //      Copyright (C) 2002 The Npgsql Development Team
8 //      npgsql-general@gborg.postgresql.org
9 //      http://gborg.postgresql.org/project/npgsql/projdisplay.php
10 //
11 //
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.
16 // 
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.
21 // 
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
25
26 using System;
27 using System.Data;
28 using System.Collections;
29 using NpgsqlTypes;
30
31
32 namespace Npgsql
33 {
34                 
35         public class NpgsqlDataReader : IDataReader, IEnumerable
36         {
37                 
38                 
39                 
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;
47                 
48                 
49                 // Logging related values
50     private static readonly String CLASSNAME = "NpgsqlDataReader";
51                 
52           internal NpgsqlDataReader( ArrayList resultsets, ArrayList responses, NpgsqlConnection connection)
53           {
54             _resultsets                                 = resultsets;
55                 _responses                                      = responses;
56                 _connection                             = connection;
57                 _rowIndex                                               = -1;
58                 _resultsetIndex                 = 0;
59                 if (_resultsets.Count > 0)
60                         _currentResultset       = (NpgsqlResultSet)_resultsets[_resultsetIndex];
61                 
62                 
63                 
64           }
65           
66           private Boolean CanRead()
67           {
68                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CanRead");
69                 /*if (_currentResultset == null)
70                         return false;*/
71                 return ((_currentResultset != null) && (_currentResultset.Count > 0));
72                 
73           }
74           
75           private void CheckCanRead()
76           {
77             if (!CanRead())
78               throw new InvalidOperationException("Cannot read data");
79           }
80           
81           public void Dispose()
82           {
83                 
84           }
85           public Int32 Depth 
86           {
87                 get
88                 {
89                         NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Depth");
90                         return 0;
91                 }
92           }
93           
94           public Boolean IsClosed
95           {
96                 get
97                 {
98                         NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsClosed");
99                         return false; 
100                 }
101           }
102           
103           public Int32 RecordsAffected 
104           {
105                 get
106                 {
107                         NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "RecordsAffected");
108                         
109                         /*if (_currentResultset == null)
110                                 return 0;       //[FIXME] Get the actual number of rows deleted, updated or inserted.
111                         return -1;
112                         */
113                         
114                         if (CanRead())
115                                 return -1;
116                         
117                         String[] ret_string_tokens = ((String)_responses[_resultsetIndex]).Split(null); // whitespace separator.
118                                 
119                                 return Int32.Parse(ret_string_tokens[ret_string_tokens.Length - 1]);
120                 }
121             
122           }
123           
124           public void Close()
125           {
126             
127           }
128           
129           public Boolean NextResult()
130           {
131                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "NextResult");
132             //throw new NotImplementedException();
133                 
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.
138                 
139                                 
140                 if((_resultsetIndex + 1) < _resultsets.Count)
141                 {
142                         _resultsetIndex++;
143                         _rowIndex = -1;
144                         _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
145                         return true;
146                 }
147                 else
148                         return false;
149                 
150           }
151           
152           public Boolean Read()
153           {
154                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Read");
155                 
156                 if (!CanRead())
157                         return false;
158                 
159             _rowIndex++;
160                 return (_rowIndex < _currentResultset.Count);
161           }
162           
163           public DataTable GetSchemaTable()
164           {
165                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable");
166                 
167                 if(_currentResultsetSchema == null)
168                         _currentResultsetSchema = GetResultsetSchema();
169                 
170                 return _currentResultsetSchema;
171                 
172           }
173           
174           
175           public Int32 FieldCount
176           {
177                 get
178                 {
179                         
180                         NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "FieldCount");
181                         //return ((_currentResultset == null) ? 0 : _currentResultset.RowDescription.NumFields);
182                         
183                         /*if (CanRead())
184                                 return _currentResultset.RowDescription.NumFields;
185                         else
186                                 return -1;*/
187                                 
188                         if (_currentResultset == null) //Executed a non return rows query.
189                           return -1;
190                   else
191                     return _currentResultset.RowDescription.NumFields;
192                   
193                                 
194                 }
195             
196           }
197           
198           public String GetName(Int32 i)
199           {
200             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName");
201                 
202                 if (_currentResultset == null)
203                         return String.Empty;
204                 else
205                         return _currentResultset.RowDescription[i].name;
206           }
207           
208           public String GetDataTypeName(Int32 i)
209           {
210                 // FIXME: have a type name instead of the oid
211                 if (_currentResultset == null)
212                         return String.Empty;
213                 else
214                           return (_currentResultset.RowDescription[i].type_oid).ToString();
215           }
216           
217           public Type GetFieldType(Int32 i)
218           {
219                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
220                         
221                         
222                 if (_currentResultset == null)
223                         return null;
224                 else
225                 return NpgsqlTypesHelper.GetSystemTypeFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[i].type_oid);
226           }
227           
228           public Object GetValue(Int32 i)
229           {
230                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue");
231             
232             CheckCanRead();
233             
234                 if (i < 0 || _rowIndex < 0)
235                         throw new InvalidOperationException("Cannot read data.");
236                 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[i];
237                 
238                 
239           }
240           
241           
242           public Int32 GetValues(Object[] values)
243           {
244                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValues");
245                 
246                 CheckCanRead();
247             
248                 // Only the number of elements in the array are filled.
249                 // It's also possible to pass an array with more that FieldCount elements.
250                 Int32 maxColumnIndex = (values.Length < FieldCount) ? values.Length : FieldCount;
251                 
252                 for (Int32 i = 0; i < maxColumnIndex; i++)
253                         values[i] = GetValue(i);
254                 
255                 return maxColumnIndex;
256                 
257           }
258           
259           public Int32 GetOrdinal(String name)
260           {
261             CheckCanRead();
262             return _currentResultset.RowDescription.FieldIndex(name);
263           }
264           
265           public Object this [ Int32 i ]
266           {
267                 get
268                 {
269                         NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, i);
270                         return GetValue(i);
271                 }
272           }
273           
274           public Object this [ String name ]
275           {
276                 get
277                 {
278                         //throw new NotImplementedException();
279                         NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, name);
280                         return GetValue(_currentResultset.RowDescription.FieldIndex(name));
281                 }
282           }
283           
284           public Boolean GetBoolean(Int32 i)
285           {
286             // Should this be done using the GetValue directly and not by converting to String
287                 // and parsing from there?
288                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBoolean");
289                 
290                                 
291                 return (Boolean) GetValue(i);
292                 
293           }
294           
295           public Byte GetByte(Int32 i)
296           {
297             throw new NotImplementedException();
298           }
299           
300           public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)
301           {
302             
303             Byte[] result;
304             
305             result = (Byte[]) GetValue(i);
306             
307             // [TODO] Implement blob support.
308             if (buffer != null)
309             {
310               result.CopyTo(buffer, 0);
311             }
312             
313             return result.Length;
314             
315           }
316           
317           public Char GetChar(Int32 i)
318           {
319             throw new NotImplementedException();
320           }
321           
322           public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)
323           {
324                         String          str;
325
326                         str = GetString(i);
327                 if (buffer == null)
328                         return str.Length;
329                 
330                         str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);
331                         return buffer.GetLength(0);
332           }
333           
334           public Guid GetGuid(Int32 i)
335           {
336             throw new NotImplementedException();
337           }
338           
339           public Int16 GetInt16(Int32 i)
340           {
341                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt16");
342                 
343                 return (Int16) GetValue(i);
344                 
345           }
346           
347           
348           public Int32 GetInt32(Int32 i)
349           {
350             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt32");
351                 
352                 return (Int32) GetValue(i);
353           
354           }
355           
356           
357           public Int64 GetInt64(Int32 i)
358           {
359             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt64");
360                 
361                 return (Int64) GetValue(i);
362           }
363           
364           public Single GetFloat(Int32 i)
365           {
366             // Should this be done using the GetValue directly and not by converting to String
367                 // and parsing from there?
368                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFloat");
369                 /*try
370                 {
371                         return Single.Parse((String) this[i]);
372                 } catch (System.FormatException)
373                 {
374                         throw new System.InvalidCastException();
375                 }*/
376                 return (Single) GetValue(i);
377           }
378           
379           public Double GetDouble(Int32 i)
380           {
381             // Should this be done using the GetValue directly and not by converting to String
382                 // and parsing from there?
383                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDouble");
384                 /*try
385                 {
386                     return Double.Parse((String) this[i]);
387                 } catch (System.FormatException)
388                 {
389                         throw new System.InvalidCastException();
390                 }*/
391                 return (Double) GetValue(i);
392           }
393           
394           public String GetString(Int32 i)
395           {
396                 NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetString");
397             
398             return (String) GetValue(i);
399           }
400           
401           public Decimal GetDecimal(Int32 i)
402           {
403             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDecimal");
404                 
405                 return (Decimal) GetValue(i);
406           }
407           
408           public DateTime GetDateTime(Int32 i)
409           {
410             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDateTime");
411             
412             return (DateTime) GetValue(i);
413           }
414           
415           public IDataReader GetData(Int32 i)
416           {
417             throw new NotImplementedException();
418           }
419           
420           public Boolean IsDBNull(Int32 i)
421           {
422             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsDBNull");
423                 
424                 CheckCanRead();
425             
426                 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex]).IsNull(i);
427           }
428
429                 private DataTable GetResultsetSchema()
430                 {
431                         
432                         NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetResultsetSchema");
433                         DataTable result = null;
434
435                         NpgsqlRowDescription rd = _currentResultset.RowDescription;
436                         Int16 numFields = rd.NumFields;
437                         if(numFields > 0) {
438                                 result = new DataTable("SchemaTable");
439
440                                 result.Columns.Add ("ColumnName", typeof (string));
441                                 result.Columns.Add ("ColumnOrdinal", typeof (int));
442                                 result.Columns.Add ("ColumnSize", typeof (int));
443                                 result.Columns.Add ("NumericPrecision", typeof (int));
444                                 result.Columns.Add ("NumericScale", typeof (int));
445                                 result.Columns.Add ("IsUnique", typeof (bool));
446                                 result.Columns.Add ("IsKey", typeof (bool));
447                                 DataColumn dc = result.Columns["IsKey"];
448                                 dc.AllowDBNull = true; // IsKey can have a DBNull
449                                 result.Columns.Add ("BaseCatalogName", typeof (string));
450                                 result.Columns.Add ("BaseColumnName", typeof (string));
451                                 result.Columns.Add ("BaseSchemaName", typeof (string));
452                                 result.Columns.Add ("BaseTableName", typeof (string));
453                                 result.Columns.Add ("DataType", typeof(Type));
454                                 result.Columns.Add ("AllowDBNull", typeof (bool));
455                                 result.Columns.Add ("ProviderType", typeof (int));
456                                 result.Columns.Add ("IsAliased", typeof (bool));
457                                 result.Columns.Add ("IsExpression", typeof (bool));
458                                 result.Columns.Add ("IsIdentity", typeof (bool));
459                                 result.Columns.Add ("IsAutoIncrement", typeof (bool));
460                                 result.Columns.Add ("IsRowVersion", typeof (bool));
461                                 result.Columns.Add ("IsHidden", typeof (bool));
462                                 result.Columns.Add ("IsLong", typeof (bool));
463                                 result.Columns.Add ("IsReadOnly", typeof (bool));
464
465                                 DataRow row;
466
467                                 for (Int16 i = 0; i < numFields; i++) {
468                                         row = result.NewRow();
469
470                                         row["ColumnName"] = GetName(i);
471                                         row["ColumnOrdinal"] = i + 1;
472                                         row["ColumnSize"] = (int) rd[i].type_size;
473                                         row["NumericPrecision"] = 0;
474                                         row["NumericScale"] = 0;
475                                         row["IsUnique"] = false;
476                                         row["IsKey"] = DBNull.Value;
477                                         row["BaseCatalogName"] = "";
478                                         row["BaseColumnName"] = GetName(i);
479                                         row["BaseSchemaName"] = "";
480                                         row["BaseTableName"] = "";
481                                         row["DataType"] = GetFieldType(i);
482                                         row["AllowDBNull"] = false;
483                                         row["ProviderType"] = (int) rd[i].type_oid;
484                                         row["IsAliased"] = false;
485                                         row["IsExpression"] = false;
486                                         row["IsIdentity"] = false;
487                                         row["IsAutoIncrement"] = false;
488                                         row["IsRowVersion"] = false;
489                                         row["IsHidden"] = false;
490                                         row["IsLong"] = false;
491                                         row["IsReadOnly"] = false;
492
493                                         result.Rows.Add(row);
494                                 }
495                         }
496
497                         return result;
498
499                 }
500                 
501                 
502                 
503                 IEnumerator IEnumerable.GetEnumerator () 
504                 {
505                         return new System.Data.Common.DbEnumerator (this);
506                 }
507         }
508 }