* FileSystemInfo.cs: corrected COM visibility of UTC properties
[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                 return Int32.Parse(_returnStringTokens[_returnStringTokens.Length - 1]);
135             }
136
137         }
138
139         public void Close()
140         {
141            if ((_behavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
142             {
143                 _connection.Close();
144                 _isClosed = true;
145             }
146
147         }
148
149         public Boolean NextResult()
150         {
151             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "NextResult");
152             
153             if((_resultsetIndex + 1) < _resultsets.Count)
154             {
155                 _resultsetIndex++;
156                 _rowIndex = -1;
157                 _currentResultset = (NpgsqlResultSet)_resultsets[_resultsetIndex];
158                 return true;
159             }
160             else
161                 return false;
162
163         }
164
165         public Boolean Read()
166         {
167             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Read");
168
169             _rowIndex++;
170             
171             if (!CanRead())
172                 return false;
173             else
174                 return true;
175
176         }
177
178         public DataTable GetSchemaTable()
179         {
180             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetSchemaTable");
181
182             if(_currentResultsetSchema == null)
183                 _currentResultsetSchema = GetResultsetSchema();
184
185             return _currentResultsetSchema;
186
187         }
188
189
190         public Int32 FieldCount
191         {
192             get
193             {
194
195                 NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "FieldCount");
196                 
197                 if (_currentResultset == null) //Executed a non return rows query.
198                     return -1;
199                 else
200                     return _currentResultset.RowDescription.NumFields;
201
202
203             }
204
205         }
206
207         public String GetName(Int32 i)
208         {
209             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetName");
210
211             if (_currentResultset == null)
212                 return String.Empty;
213             else
214                 return _currentResultset.RowDescription[i].name;
215         }
216
217         public String GetDataTypeName(Int32 i)
218         {
219             // FIXME: have a type name instead of the oid
220             if (_currentResultset == null)
221                 return String.Empty;
222             else
223                 return (_currentResultset.RowDescription[i].type_oid).ToString();
224         }
225
226         public Type GetFieldType(Int32 i)
227         {
228             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFieldType");
229
230
231             if (_currentResultset == null)
232                 return null;
233             else
234                 return NpgsqlTypesHelper.GetSystemTypeFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[i].type_oid);
235         }
236
237         public Object GetValue(Int32 i)
238         {
239             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValue");
240
241             CheckCanRead();
242
243             if (i < 0)
244                 throw new InvalidOperationException("Cannot read data. Column less than 0 specified.");
245             if (_rowIndex < 0)
246                 throw new InvalidOperationException("Cannot read data. DataReader not initialized. Maybe you forgot to call Read()?");
247             return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[i];
248
249
250         }
251
252
253         public Int32 GetValues(Object[] values)
254         {
255             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetValues");
256
257             CheckCanRead();
258
259             // Only the number of elements in the array are filled.
260             // It's also possible to pass an array with more that FieldCount elements.
261             Int32 maxColumnIndex = (values.Length < FieldCount) ? values.Length : FieldCount;
262
263             for (Int32 i = 0; i < maxColumnIndex; i++)
264                 values[i] = GetValue(i);
265
266             return maxColumnIndex;
267
268         }
269
270         public Int32 GetOrdinal(String name)
271         {
272             CheckCanRead();
273             return _currentResultset.RowDescription.FieldIndex(name);
274         }
275
276         public Object this [ Int32 i ]
277         {
278             get
279             {
280                 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, i);
281                 return GetValue(i);
282             }
283         }
284
285         public Object this [ String name ]
286         {
287             get
288             {
289                 //throw new NotImplementedException();
290                 NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, name);
291                 return GetValue(_currentResultset.RowDescription.FieldIndex(name));
292             }
293         }
294
295         public Boolean GetBoolean(Int32 i)
296         {
297             // Should this be done using the GetValue directly and not by converting to String
298             // and parsing from there?
299             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBoolean");
300
301
302             return (Boolean) GetValue(i);
303
304         }
305
306         public Byte GetByte(Int32 i)
307         {
308             throw new NotImplementedException();
309         }
310
311         public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)
312         {
313
314             Byte[] result;
315
316             result = (Byte[]) GetValue(i);
317
318             // [TODO] Implement blob support.
319             if (buffer != null)
320             {
321                 result.CopyTo(buffer, 0);
322             }
323
324             return result.Length;
325
326         }
327
328         public Char GetChar(Int32 i)
329         {
330             throw new NotImplementedException();
331         }
332
333         public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)
334         {
335             String              str;
336
337             str = GetString(i);
338             if (buffer == null)
339                 return str.Length;
340
341             str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);
342             return buffer.GetLength(0);
343         }
344
345         public Guid GetGuid(Int32 i)
346         {
347             throw new NotImplementedException();
348         }
349
350         public Int16 GetInt16(Int32 i)
351         {
352             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt16");
353
354             return (Int16) GetValue(i);
355
356         }
357
358
359         public Int32 GetInt32(Int32 i)
360         {
361             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt32");
362
363             return (Int32) GetValue(i);
364
365         }
366
367
368         public Int64 GetInt64(Int32 i)
369         {
370             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetInt64");
371
372             return (Int64) GetValue(i);
373         }
374
375         public Single GetFloat(Int32 i)
376         {
377             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetFloat");
378             
379             return (Single) GetValue(i);
380         }
381
382         public Double GetDouble(Int32 i)
383         {
384             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDouble");
385             
386             return (Double) GetValue(i);
387         }
388
389         public String GetString(Int32 i)
390         {
391             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetString");
392
393             return (String) GetValue(i);
394         }
395
396         public Decimal GetDecimal(Int32 i)
397         {
398             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDecimal");
399
400             return (Decimal) GetValue(i);
401         }
402
403         public DateTime GetDateTime(Int32 i)
404         {
405             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetDateTime");
406
407             return (DateTime) GetValue(i);
408         }
409
410         public IDataReader GetData(Int32 i)
411         {
412             throw new NotImplementedException();
413         }
414
415         public Boolean IsDBNull(Int32 i)
416         {
417             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IsDBNull");
418
419             CheckCanRead();
420
421             return ((NpgsqlAsciiRow)_currentResultset[_rowIndex]).IsDBNull(i);
422         }
423
424         private DataTable GetResultsetSchema()
425         {
426
427             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetResultsetSchema");
428             DataTable result = null;
429
430             NpgsqlRowDescription rd = _currentResultset.RowDescription;
431             Int16 numFields = rd.NumFields;
432             if(numFields > 0)
433             {
434                 result = new DataTable("SchemaTable");
435
436                 result.Columns.Add ("ColumnName", typeof (string));
437                 result.Columns.Add ("ColumnOrdinal", typeof (int));
438                 result.Columns.Add ("ColumnSize", typeof (int));
439                 result.Columns.Add ("NumericPrecision", typeof (int));
440                 result.Columns.Add ("NumericScale", typeof (int));
441                 result.Columns.Add ("IsUnique", typeof (bool));
442                 result.Columns.Add ("IsKey", typeof (bool));
443                 DataColumn dc = result.Columns["IsKey"];
444                 dc.AllowDBNull = true; // IsKey can have a DBNull
445                 result.Columns.Add ("BaseCatalogName", typeof (string));
446                 result.Columns.Add ("BaseColumnName", typeof (string));
447                 result.Columns.Add ("BaseSchemaName", typeof (string));
448                 result.Columns.Add ("BaseTableName", typeof (string));
449                 result.Columns.Add ("DataType", typeof(Type));
450                 result.Columns.Add ("AllowDBNull", typeof (bool));
451                 result.Columns.Add ("ProviderType", typeof (int));
452                 result.Columns.Add ("IsAliased", typeof (bool));
453                 result.Columns.Add ("IsExpression", typeof (bool));
454                 result.Columns.Add ("IsIdentity", typeof (bool));
455                 result.Columns.Add ("IsAutoIncrement", typeof (bool));
456                 result.Columns.Add ("IsRowVersion", typeof (bool));
457                 result.Columns.Add ("IsHidden", typeof (bool));
458                 result.Columns.Add ("IsLong", typeof (bool));
459                 result.Columns.Add ("IsReadOnly", typeof (bool));
460
461                 DataRow row;
462
463                 for (Int16 i = 0; i < numFields; i++)
464                 {
465                     row = result.NewRow();
466
467                     row["ColumnName"] = GetName(i);
468                     row["ColumnOrdinal"] = i + 1;
469                     row["ColumnSize"] = (int) rd[i].type_size;
470                     row["NumericPrecision"] = 0;
471                     row["NumericScale"] = 0;
472                     row["IsUnique"] = false;
473                     row["IsKey"] = DBNull.Value;
474                     row["BaseCatalogName"] = "";
475                     row["BaseColumnName"] = GetName(i);
476                     row["BaseSchemaName"] = "";
477                     row["BaseTableName"] = "";
478                     row["DataType"] = GetFieldType(i);
479                     row["AllowDBNull"] = false;
480                     row["ProviderType"] = (int) rd[i].type_oid;
481                     row["IsAliased"] = false;
482                     row["IsExpression"] = false;
483                     row["IsIdentity"] = false;
484                     row["IsAutoIncrement"] = false;
485                     row["IsRowVersion"] = false;
486                     row["IsHidden"] = false;
487                     row["IsLong"] = false;
488                     row["IsReadOnly"] = false;
489
490                     result.Rows.Add(row);
491                 }
492             }
493
494             return result;
495
496         }
497
498
499
500         IEnumerator IEnumerable.GetEnumerator ()
501         {
502             return new System.Data.Common.DbEnumerator (this);
503         }
504     }
505 }