2005-01-31 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / Mono.Data.SqliteClient / Mono.Data.SqliteClient / SqliteDataReader.cs
1 //
2 // Mono.Data.SqliteClient.SqliteDataReader.cs
3 //
4 // Provides a means of reading a forward-only stream of rows from a Sqlite 
5 // database file.
6 //
7 // Author(s): Vladimir Vukicevic  <vladimir@pobox.com>
8 //            Everaldo Canuto  <everaldo_canuto@yahoo.com.br>
9 //
10 // Copyright (C) 2002  Vladimir Vukicevic
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Runtime.InteropServices;
34 using System.Collections;
35 using System.Data;
36 using System.Data.Common;
37
38 namespace Mono.Data.SqliteClient
39 {
40         public class SqliteDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
41         {
42
43                 #region Fields
44                 
45                 private SqliteCommand command;
46                 private ArrayList rows;
47                 private ArrayList columns;
48                 private Hashtable column_names;
49                 private int current_row;
50                 private bool closed;
51                 private bool reading;
52                 private int records_affected;
53                 
54                 #endregion
55
56                 #region Constructors and destructors
57                 
58                 internal SqliteDataReader (SqliteCommand cmd, IntPtr pVm, int version)
59                 {
60                         command = cmd;
61                         rows = new ArrayList ();
62                         columns = new ArrayList ();
63                         column_names = new Hashtable ();
64                         closed = false;
65                         current_row = -1;
66                         reading = true;
67                         ReadpVm (pVm, version);
68                         ReadingDone ();
69                 }
70                 
71                 #endregion
72
73                 #region Properties
74                 
75                 public int Depth {
76                         get { return 0; }
77                 }
78                 
79                 public int FieldCount {
80                         get { return columns.Count; }
81                 }
82                 
83                 public object this[string name] {
84                         get { return ((ArrayList) rows[current_row])[(int) column_names[name]]; }
85                 }
86                 
87                 public object this[int i] {
88                         get { return ((ArrayList) rows[current_row])[i]; }
89                 }
90                 
91                 public bool IsClosed {
92                         get { return closed; }
93                 }
94                 
95                 public int RecordsAffected {
96                         get { return records_affected; }
97                 }
98                 
99                 #endregion
100
101                 #region Internal Methods
102                 
103                 internal void ReadpVm (IntPtr pVm, int version)
104                 {
105                         int pN = 0;
106                         IntPtr pazValue = IntPtr.Zero;
107                         IntPtr pazColName = IntPtr.Zero;
108                         SqliteError res;
109
110                         while (true) {
111                                 if (version == 3) {
112                                         res = Sqlite.sqlite3_step (pVm);
113                                         pN = Sqlite.sqlite3_column_count (pVm);
114                                 } else
115                                         res = Sqlite.sqlite_step (pVm, out pN, out pazValue, out pazColName);
116                                 if (res == SqliteError.ERROR) {         
117                                         throw new ApplicationException ("Sqlite error");
118                                 }
119                                 if (res == SqliteError.DONE) {
120                                         break;
121                                 }
122                                 // We have some data; lets read it
123                                 if (column_names.Count == 0) {
124                                         for (int i = 0; i < pN; i++) {
125                                                 string colName = "";
126                                                 if (version == 2) {
127                                                         IntPtr fieldPtr = (IntPtr)Marshal.ReadInt32 (pazColName, i*IntPtr.Size);
128                                                         colName = Marshal.PtrToStringAnsi (fieldPtr);
129                                                 } else {
130                                                         colName = Marshal.PtrToStringAnsi (Sqlite.sqlite3_column_name (pVm, i));
131                                                 }
132                                                 columns.Add (colName);
133                                                 column_names [colName] = i;
134                                         }
135                                 }
136                                 ArrayList data_row = new ArrayList (pN);
137                                 for (int i = 0; i < pN; i++) {
138                                         string colData = "";
139                                         if (version == 2) {
140                                                 IntPtr fieldPtr = (IntPtr)Marshal.ReadInt32 (pazValue, i*IntPtr.Size);
141                                                 colData = Marshal.PtrToStringAnsi (fieldPtr);
142                                                 data_row.Add (Marshal.PtrToStringAnsi (fieldPtr));
143                                         } else {
144                                                 switch (Sqlite.sqlite3_column_type (pVm, i)) {
145                                                         case 1:
146                                                                 Int64 sqliteint64 = Sqlite.sqlite3_column_int64 (pVm, i);
147                                                                 data_row.Add (sqliteint64.ToString ());
148                                                                 break;
149                                                         case 2:
150                                                                 double sqlitedouble = Sqlite.sqlite3_column_double (pVm, i);
151                                                                 data_row.Add (sqlitedouble.ToString ());
152                                                                 break;
153                                                         case 3:
154                                                                 colData = Marshal.PtrToStringAnsi (Sqlite.sqlite3_column_text (pVm, i));
155                                                                 data_row.Add (colData);
156                                                                 break;
157                                                         case 4:
158                                                                 int blobbytes = Sqlite.sqlite3_column_bytes (pVm, i);
159                                                                 IntPtr blobptr = Sqlite.sqlite3_column_blob (pVm, i);
160                                                                 byte[] blob = new byte[blobbytes];
161                                                                 Marshal.Copy (blobptr, blob, 0, blobbytes);
162                                                                 data_row.Add (blob);
163                                                                 break;
164                                                         case 5:
165                                                                 data_row.Add (null);
166                                                                 break;
167                                                         default:
168                                                                 throw new ApplicationException ("FATAL: Unknown sqlite3_column_type");
169                                                 }
170                                         }       
171                                 }
172                                 rows.Add (data_row);
173                         }
174                 }
175                 internal void ReadingDone ()
176                 {
177                         records_affected = command.NumChanges ();
178                         reading = false;
179                 }
180                 
181                 #endregion
182
183                 #region  Public Methods
184                 
185                 public void Close ()
186                 {
187                         closed = true;
188                 }
189                 
190                 public void Dispose ()
191                 {
192                         Close ();
193                 }
194                 
195                 IEnumerator IEnumerable.GetEnumerator () 
196                 {
197                         return new DbEnumerator (this);
198                 }
199                 
200                 public DataTable GetSchemaTable () 
201                 {
202                         DataTable dataTableSchema = new DataTable ();
203                         
204                         dataTableSchema.Columns.Add ("ColumnName", typeof (String));
205                         dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (Int32));
206                         dataTableSchema.Columns.Add ("ColumnSize", typeof (Int32));
207                         dataTableSchema.Columns.Add ("NumericPrecision", typeof (Int32));
208                         dataTableSchema.Columns.Add ("NumericScale", typeof (Int32));
209                         dataTableSchema.Columns.Add ("IsUnique", typeof (Boolean));
210                         dataTableSchema.Columns.Add ("IsKey", typeof (Boolean));
211                         dataTableSchema.Columns.Add ("BaseCatalogName", typeof (String));
212                         dataTableSchema.Columns.Add ("BaseColumnName", typeof (String));
213                         dataTableSchema.Columns.Add ("BaseSchemaName", typeof (String));
214                         dataTableSchema.Columns.Add ("BaseTableName", typeof (String));
215                         dataTableSchema.Columns.Add ("DataType", typeof(Type));
216                         dataTableSchema.Columns.Add ("AllowDBNull", typeof (Boolean));
217                         dataTableSchema.Columns.Add ("ProviderType", typeof (Int32));
218                         dataTableSchema.Columns.Add ("IsAliased", typeof (Boolean));
219                         dataTableSchema.Columns.Add ("IsExpression", typeof (Boolean));
220                         dataTableSchema.Columns.Add ("IsIdentity", typeof (Boolean));
221                         dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (Boolean));
222                         dataTableSchema.Columns.Add ("IsRowVersion", typeof (Boolean));
223                         dataTableSchema.Columns.Add ("IsHidden", typeof (Boolean));
224                         dataTableSchema.Columns.Add ("IsLong", typeof (Boolean));
225                         dataTableSchema.Columns.Add ("IsReadOnly", typeof (Boolean));
226                         
227                         dataTableSchema.BeginLoadData();
228                         for (int i = 0; i < this.FieldCount; i += 1 ) {
229                                 
230                                 DataRow schemaRow = dataTableSchema.NewRow ();
231                                 
232                                 schemaRow["ColumnName"] = columns[i];
233                                 schemaRow["ColumnOrdinal"] = i;
234                                 schemaRow["ColumnSize"] = 0;
235                                 schemaRow["NumericPrecision"] = 0;
236                                 schemaRow["NumericScale"] = 0;
237                                 schemaRow["IsUnique"] = false;
238                                 schemaRow["IsKey"] = false;
239                                 schemaRow["BaseCatalogName"] = "";
240                                 schemaRow["BaseColumnName"] = columns[i];
241                                 schemaRow["BaseSchemaName"] = "";
242                                 schemaRow["BaseTableName"] = "";
243                                 schemaRow["DataType"] = typeof(string);
244                                 schemaRow["AllowDBNull"] = true;
245                                 schemaRow["ProviderType"] = 0;
246                                 schemaRow["IsAliased"] = false;
247                                 schemaRow["IsExpression"] = false;
248                                 schemaRow["IsIdentity"] = false;
249                                 schemaRow["IsAutoIncrement"] = false;
250                                 schemaRow["IsRowVersion"] = false;
251                                 schemaRow["IsHidden"] = false;
252                                 schemaRow["IsLong"] = false;
253                                 schemaRow["IsReadOnly"] = false;
254                                 
255                                 dataTableSchema.Rows.Add (schemaRow);
256                                 schemaRow.AcceptChanges();
257                         }
258                         dataTableSchema.EndLoadData();
259                         
260                         return dataTableSchema;
261                 }
262                 
263                 public bool NextResult ()
264                 {
265                         current_row++;
266                         
267                         return (current_row < rows.Count);
268                 }
269                 
270                 public bool Read ()
271                 {
272                         return NextResult ();
273                 }
274
275                 #endregion
276                 
277                 #region IDataRecord getters
278                 
279                 public bool GetBoolean (int i)
280                 {
281                         return Convert.ToBoolean ((string) ((ArrayList) rows[current_row])[i]);
282                 }
283                 
284                 public byte GetByte (int i)
285                 {
286                         return Convert.ToByte ((string) ((ArrayList) rows[current_row])[i]);
287                 }
288                 
289                 public long GetBytes (int i, long fieldOffset, byte[] buffer, int bufferOffset, int length)
290                 {
291                         throw new NotImplementedException ();
292                 }
293                 
294                 public char GetChar (int i)
295                 {
296                         return Convert.ToChar ((string) ((ArrayList) rows[current_row])[i]);
297                 }
298                 
299                 public long GetChars (int i, long fieldOffset, char[] buffer, int bufferOffset, int length)
300                 {
301                         throw new NotImplementedException ();
302                 }
303                 
304                 public IDataReader GetData (int i)
305                 {
306                         throw new NotImplementedException ();
307                 }
308                 
309                 public string GetDataTypeName (int i)
310                 {
311                         return "text"; // SQL Lite data type
312                 }
313                 
314                 public DateTime GetDateTime (int i)
315                 {
316                         return Convert.ToDateTime ((string) ((ArrayList) rows[current_row])[i]);
317                 }
318                 
319                 public decimal GetDecimal (int i)
320                 {
321                         return Convert.ToDecimal ((string) ((ArrayList) rows[current_row])[i]);
322                 }
323                 
324                 public double GetDouble (int i)
325                 {
326                         return Convert.ToDouble ((string) ((ArrayList) rows[current_row])[i]);
327                 }
328                 
329                 public Type GetFieldType (int i)
330                 {
331                         return System.Type.GetType ("System.String"); // .NET data type
332                 }
333                 
334                 public float GetFloat (int i)
335                 {
336                         return Convert.ToSingle ((string) ((ArrayList) rows[current_row])[i]);
337                 }
338                 
339                 public Guid GetGuid (int i)
340                 {
341                         throw new NotImplementedException ();
342                 }
343                 
344                 public short GetInt16 (int i)
345                 {
346                         return Convert.ToInt16 ((string) ((ArrayList) rows[current_row])[i]);
347                 }
348                 
349                 public int GetInt32 (int i)
350                 {
351                         return Convert.ToInt32 ((string) ((ArrayList) rows[current_row])[i]);
352                 }
353                 
354                 public long GetInt64 (int i)
355                 {
356                         return Convert.ToInt64 ((string) ((ArrayList) rows[current_row])[i]);
357                 }
358                 
359                 public string GetName (int i)
360                 {
361                         return (string) columns[i];
362                 }
363                 
364                 public int GetOrdinal (string name)
365                 {
366                         return (int) column_names[name];
367                 }
368                 
369                 public string GetString (int i)
370                 {
371                         return ((string) ((ArrayList) rows[current_row])[i]);
372                 }
373                 
374                 public object GetValue (int i)
375                 {
376                         return ((ArrayList) rows[current_row])[i];
377                 }
378                 
379                 public int GetValues (object[] values)
380                 {
381                         int num_to_fill = System.Math.Min (values.Length, columns.Count);
382                         for (int i = 0; i < num_to_fill; i++) {
383                                 if (((ArrayList) rows[current_row])[i] != null) {
384                                         values[i] = ((ArrayList) rows[current_row])[i];
385                                 } else {
386                                         values[i] = DBNull.Value;
387                                 }
388                         }
389                         return num_to_fill;
390                 }
391                 
392                 public bool IsDBNull (int i)
393                 {
394                         return (((ArrayList) rows[current_row])[i] == null);
395                 }
396                         
397                 #endregion
398         }
399 }