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