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