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