Synchronized files from gborg repository
[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                 
60                 _currentResultset       = (NpgsqlResultSet)_resultsets[_resultsetIndex];
61                 
62                 
63                 
64           }
65           
66           private Boolean CanRead()
67           {
68                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".CanRead() ", LogLevel.Debug);
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.LogMsg("Entering " + CLASSNAME + ".get_Depth() ", LogLevel.Debug);
90                         return 0;
91                 }
92           }
93           
94           public Boolean IsClosed
95           {
96                 get
97                 {
98                         NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_IsClosed()", LogLevel.Debug);
99                         return false; 
100                 }
101           }
102           
103           public Int32 RecordsAffected 
104           {
105                 get
106                 {
107                         NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_RecordsAffected()", LogLevel.Debug);
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.LogMsg("Entering " + CLASSNAME + ".NextResult()", LogLevel.Debug);
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.LogMsg("Entering " + CLASSNAME + ".Read()", LogLevel.Debug);
155                 
156                 if (!CanRead())
157                         return false;
158                 
159             _rowIndex++;
160                 return (_rowIndex < _currentResultset.Count);
161           }
162           
163           public DataTable GetSchemaTable()
164           {
165                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetSchemaTable()", LogLevel.Debug);
166             //throw new NotImplementedException();
167                 
168                 if (!CanRead())
169                         return null; //[FIXME] Should we return null or throw an exception??
170                 
171                 if(_currentResultsetSchema == null)
172                         _currentResultsetSchema = GetResultsetSchema();
173                 
174                 return _currentResultsetSchema;
175                 
176           }
177           
178           
179           public Int32 FieldCount
180           {
181                 get
182                 {
183                         
184                         NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".get_FieldCount()", LogLevel.Debug);
185                         //return ((_currentResultset == null) ? 0 : _currentResultset.RowDescription.NumFields);
186                         if (CanRead())
187                                 return _currentResultset.RowDescription.NumFields;
188                         else
189                                 return -1;
190                                 
191                 }
192             
193           }
194           
195           public String GetName(Int32 i)
196           {
197             //throw new NotImplementedException();
198                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetName(Int32)", LogLevel.Debug);
199                 
200                 if (CanRead())
201                         return _currentResultset.RowDescription[i].name;
202                 else
203                         return String.Empty;
204           }
205           
206           public String GetDataTypeName(Int32 i)
207           {
208                 // FIXME: have a type name instead of the oid
209                         return (_currentResultset.RowDescription[i].type_oid).ToString();
210           }
211           
212           public Type GetFieldType(Int32 i)
213           {
214                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetFieldType(Int32)", LogLevel.Debug);
215                         
216                         
217                 //return Type.GetType(NpgsqlTypesHelper.GetSystemTypeNameFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[i].type_oid));
218                 
219                 return NpgsqlTypesHelper.GetSystemTypeFromTypeOid(_connection.OidToNameMapping, _currentResultset.RowDescription[i].type_oid);
220           }
221           
222           public Object GetValue(Int32 i)
223           {
224                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetValue(Int32)", LogLevel.Debug);
225             
226             CheckCanRead();
227             
228                 if (i < 0 || _rowIndex < 0)
229                         throw new InvalidOperationException("Cannot read data.");
230                 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex])[i];
231                 
232                 
233           }
234           
235           
236           public Int32 GetValues(Object[] values)
237           {
238                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetValues(Object[])", LogLevel.Debug);
239                 
240                 CheckCanRead();
241             
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;
245                 
246                 for (Int32 i = 0; i < maxColumnIndex; i++)
247                         values[i] = GetValue(i);
248                 
249                 return maxColumnIndex;
250                 
251           }
252           
253           public Int32 GetOrdinal(String name)
254           {
255             CheckCanRead();
256             return _currentResultset.RowDescription.FieldIndex(name);
257           }
258           
259           public Object this [ Int32 i ]
260           {
261                 get
262                 {
263                         NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".this[Int32]", LogLevel.Debug);
264                         return GetValue(i);
265                 }
266           }
267           
268           public Object this [ String name ]
269           {
270                 get
271                 {
272                         //throw new NotImplementedException();
273                         NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".this[String]", LogLevel.Debug);
274                         return GetValue(_currentResultset.RowDescription.FieldIndex(name));
275                 }
276           }
277           
278           public Boolean GetBoolean(Int32 i)
279           {
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);
283                 
284                                 
285                 return (Boolean) GetValue(i);
286                 
287           }
288           
289           public Byte GetByte(Int32 i)
290           {
291             throw new NotImplementedException();
292           }
293           
294           public Int64 GetBytes(Int32 i, Int64 fieldOffset, Byte[] buffer, Int32 bufferoffset, Int32 length)
295           {
296             throw new NotImplementedException();
297           }
298           
299           public Char GetChar(Int32 i)
300           {
301             throw new NotImplementedException();
302           }
303           
304           public Int64 GetChars(Int32 i, Int64 fieldoffset, Char[] buffer, Int32 bufferoffset, Int32 length)
305           {
306                         String          str;
307
308                         str = GetString(i);
309                 if (buffer == null)
310                         return str.Length;
311                 
312                         str.ToCharArray(bufferoffset, length).CopyTo(buffer, 0);
313                         return buffer.GetLength(0);
314           }
315           
316           public Guid GetGuid(Int32 i)
317           {
318             throw new NotImplementedException();
319           }
320           
321           public Int16 GetInt16(Int32 i)
322           {
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);
326                 /*try
327                 {
328                     return Int16.Parse((String) this[i]);
329                 } catch (System.FormatException)
330                 {
331                         throw new System.InvalidCastException();
332                 }*/
333                 
334                 return (Int16) GetValue(i);
335                 
336
337           }
338           
339           
340           public Int32 GetInt32(Int32 i)
341           {
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);
345                 /*try
346                 {
347                     return Int32.Parse((String) this[i]);
348                 } catch (System.FormatException)
349                 {
350                         throw new System.InvalidCastException();
351                 }*/
352                 
353                 
354                 return (Int32) GetValue(i);
355           
356           }
357           
358           
359           public Int64 GetInt64(Int32 i)
360           {
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);
364                 /*try
365                 {
366                     return Int64.Parse((String) this[i]);
367                 } catch (System.FormatException)
368                 {
369                         throw new System.InvalidCastException();
370                 }*/
371                 return (Int64) GetValue(i);
372           }
373           
374           public Single GetFloat(Int32 i)
375           {
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);
379                 try
380                 {
381                         return Single.Parse((String) this[i]);
382                 } catch (System.FormatException)
383                 {
384                         throw new System.InvalidCastException();
385                 }
386           }
387           
388           public Double GetDouble(Int32 i)
389           {
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);
393                 try
394                 {
395                     return Double.Parse((String) this[i]);
396                 } catch (System.FormatException)
397                 {
398                         throw new System.InvalidCastException();
399                 }
400           }
401           
402           public String GetString(Int32 i)
403           {
404                 NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetString(Int32)", LogLevel.Debug);
405             return (String) GetValue(i);
406           }
407           
408           public Decimal GetDecimal(Int32 i)
409           {
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);
413                 
414                 
415                 return (Decimal) GetValue(i);
416           }
417           
418           public DateTime GetDateTime(Int32 i)
419           {
420             //throw new NotImplementedException();
421                 return (DateTime) GetValue(i);
422           }
423           
424           public IDataReader GetData(Int32 i)
425           {
426             throw new NotImplementedException();
427           }
428           
429           public Boolean IsDBNull(Int32 i)
430           {
431                 //throw new NotImplementedException();
432                 
433                 if (!CanRead())
434                   throw new InvalidOperationException("Cannot read data");
435                 return ((NpgsqlAsciiRow)_currentResultset[_rowIndex]).IsNull(i);
436           }
437
438
439                 
440           
441           
442
443                 private DataTable GetResultsetSchema()
444                 {
445                         
446                         NpgsqlEventLog.LogMsg("Entering " + CLASSNAME + ".GetResultsetSchema()", LogLevel.Debug);
447                         DataTable result = null;
448
449                         NpgsqlRowDescription rd = _currentResultset.RowDescription;
450                         Int16 numFields = rd.NumFields;
451                         if(numFields > 0) {
452                                 result = new DataTable("SchemaTable");
453
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));
478
479                                 DataRow row;
480
481                                 for (Int16 i = 0; i < numFields; i++) {
482                                         row = result.NewRow();
483
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;
506
507                                         result.Rows.Add(row);
508                                 }
509                         }
510
511                         return result;
512
513                 }
514                 
515                 
516                 
517                 IEnumerator IEnumerable.GetEnumerator () 
518                 {
519                         return new System.Data.Common.DbEnumerator (this);
520                 }
521         }
522 }