Merging from head
[beagle.git] / beagled / Mono.Data.SqliteClient / SqliteDataReader.cs
blobc9278a6f9ddecec86f7eb38b5c3c968234ca1ddc
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 // Joshua Tauberer <tauberer@for.net>
11 // Copyright (C) 2002 Vladimir Vukicevic
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System;
34 using System.Runtime.InteropServices;
35 using System.Collections;
36 using System.Data;
37 using System.Data.Common;
39 namespace Mono.Data.SqliteClient
41 public class SqliteDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
44 #region Fields
46 private SqliteCommand command;
47 private IntPtr pVm;
48 private int version;
49 private ArrayList current_row;
50 private string[] columns;
51 private Hashtable column_names_sens, column_names_insens;
52 private bool closed;
53 private string[] decltypes;
54 private int[] declmode;
57 #endregion
59 #region Constructors and destructors
61 internal SqliteDataReader (SqliteCommand cmd, IntPtr _pVm, int _version)
63 command = cmd;
64 pVm = _pVm;
65 version = _version;
67 current_row = new ArrayList();
69 column_names_sens = new Hashtable ();
70 column_names_insens = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant, CaseInsensitiveComparer.DefaultInvariant);
71 closed = false;
74 #endregion
76 #region Properties
78 public int Depth {
79 get { return 0; }
82 public int FieldCount {
83 get { return columns.Length; }
86 public object this[string name] {
87 get {
88 return GetValue (GetOrdinal (name));
92 public object this[int i] {
93 get { return GetValue (i); }
96 public bool IsClosed {
97 get { return closed; }
100 public int RecordsAffected {
101 get { return command.NumChanges (); }
104 #endregion
106 #region Internal Methods
108 internal bool ReadNextColumn ()
110 int pN;
111 IntPtr pazValue;
112 IntPtr pazColName;
113 bool first = true;
115 bool hasdata = command.ExecuteStatement(pVm, out pN, out pazValue, out pazColName);
117 // For the first row, get the column information (names and types)
118 if (columns == null) {
119 if (version == 3) {
120 // A decltype might be null if the type is unknown to sqlite.
121 decltypes = new string[pN];
122 declmode = new int[pN]; // 1 == integer, 2 == datetime
123 for (int i = 0; i < pN; i++) {
124 IntPtr decl = Sqlite.sqlite3_column_decltype16 (pVm, i);
125 if (decl != IntPtr.Zero) {
126 decltypes[i] = Marshal.PtrToStringUni (decl).ToLower(System.Globalization.CultureInfo.InvariantCulture);
127 if (decltypes[i] == "int" || decltypes[i] == "integer")
128 declmode[i] = 1;
129 else if (decltypes[i] == "date" || decltypes[i] == "datetime")
130 declmode[i] = 2;
135 columns = new string[pN];
136 for (int i = 0; i < pN; i++) {
137 string colName;
138 if (version == 2) {
139 IntPtr fieldPtr = Marshal.ReadIntPtr (pazColName, i*IntPtr.Size);
140 colName = Sqlite.HeapToString (fieldPtr, command.Connection.Encoding);
141 } else {
142 colName = Marshal.PtrToStringUni (Sqlite.sqlite3_column_name16 (pVm, i));
144 columns[i] = colName;
145 column_names_sens [colName] = i;
146 column_names_insens [colName] = i;
150 if (!hasdata)
151 return false;
153 current_row.Clear();
154 for (int i = 0; i < pN; i++) {
155 if (version == 2) {
156 IntPtr fieldPtr = Marshal.ReadIntPtr (pazValue, i*IntPtr.Size);
157 current_row.Add (Sqlite.HeapToString (fieldPtr, command.Connection.Encoding));
158 } else {
159 switch (Sqlite.sqlite3_column_type (pVm, i)) {
160 case 1:
161 long val = Sqlite.sqlite3_column_int64 (pVm, i);
163 // If the column was declared as an 'int' or 'integer', let's play
164 // nice and return an int (version 3 only).
165 if (declmode[i] == 1 && val >= int.MinValue && val <= int.MaxValue)
166 current_row.Add ((int)val);
168 // Or if it was declared a date or datetime, do the reverse of what we
169 // do for DateTime parameters.
170 else if (declmode[i] == 2)
171 current_row.Add (DateTime.FromFileTime(val));
173 else
174 current_row.Add (val);
176 break;
177 case 2:
178 current_row.Add (Sqlite.sqlite3_column_double (pVm, i));
179 break;
180 case 3:
181 string strval = Marshal.PtrToStringUni (Sqlite.sqlite3_column_text16 (pVm, i));
183 // If the column was declared as a 'date' or 'datetime', let's play
184 // nice and return a DateTime (version 3 only).
185 if (declmode[i] == 2)
186 current_row.Add (DateTime.Parse (strval));
187 else
188 current_row.Add (strval);
190 break;
191 case 4:
192 int blobbytes = Sqlite.sqlite3_column_bytes16 (pVm, i);
193 IntPtr blobptr = Sqlite.sqlite3_column_blob (pVm, i);
194 byte[] blob = new byte[blobbytes];
195 Marshal.Copy (blobptr, blob, 0, blobbytes);
196 current_row.Add (blob);
197 break;
198 case 5:
199 current_row.Add (null);
200 break;
201 default:
202 throw new ApplicationException ("FATAL: Unknown sqlite3_column_type");
206 return true;
209 #endregion
211 #region Public Methods
213 public void Close ()
215 closed = true;
217 if (pVm != IntPtr.Zero) {
218 IntPtr errMsg;
219 if (version == 3)
220 Sqlite.sqlite3_finalize (pVm);
221 else
222 Sqlite.sqlite_finalize (pVm, out errMsg);
223 pVm = IntPtr.Zero;
227 public void Dispose ()
229 Close ();
232 IEnumerator IEnumerable.GetEnumerator ()
234 return new DbEnumerator (this);
237 public DataTable GetSchemaTable ()
239 DataTable dataTableSchema = new DataTable ();
241 dataTableSchema.Columns.Add ("ColumnName", typeof (String));
242 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (Int32));
243 dataTableSchema.Columns.Add ("ColumnSize", typeof (Int32));
244 dataTableSchema.Columns.Add ("NumericPrecision", typeof (Int32));
245 dataTableSchema.Columns.Add ("NumericScale", typeof (Int32));
246 dataTableSchema.Columns.Add ("IsUnique", typeof (Boolean));
247 dataTableSchema.Columns.Add ("IsKey", typeof (Boolean));
248 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (String));
249 dataTableSchema.Columns.Add ("BaseColumnName", typeof (String));
250 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (String));
251 dataTableSchema.Columns.Add ("BaseTableName", typeof (String));
252 dataTableSchema.Columns.Add ("DataType", typeof(Type));
253 dataTableSchema.Columns.Add ("AllowDBNull", typeof (Boolean));
254 dataTableSchema.Columns.Add ("ProviderType", typeof (Int32));
255 dataTableSchema.Columns.Add ("IsAliased", typeof (Boolean));
256 dataTableSchema.Columns.Add ("IsExpression", typeof (Boolean));
257 dataTableSchema.Columns.Add ("IsIdentity", typeof (Boolean));
258 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (Boolean));
259 dataTableSchema.Columns.Add ("IsRowVersion", typeof (Boolean));
260 dataTableSchema.Columns.Add ("IsHidden", typeof (Boolean));
261 dataTableSchema.Columns.Add ("IsLong", typeof (Boolean));
262 dataTableSchema.Columns.Add ("IsReadOnly", typeof (Boolean));
264 dataTableSchema.BeginLoadData();
265 for (int i = 0; i < this.FieldCount; i += 1 ) {
267 DataRow schemaRow = dataTableSchema.NewRow ();
269 schemaRow["ColumnName"] = columns[i];
270 schemaRow["ColumnOrdinal"] = i;
271 schemaRow["ColumnSize"] = 0;
272 schemaRow["NumericPrecision"] = 0;
273 schemaRow["NumericScale"] = 0;
274 schemaRow["IsUnique"] = false;
275 schemaRow["IsKey"] = false;
276 schemaRow["BaseCatalogName"] = "";
277 schemaRow["BaseColumnName"] = columns[i];
278 schemaRow["BaseSchemaName"] = "";
279 schemaRow["BaseTableName"] = "";
280 schemaRow["DataType"] = typeof(string);
281 schemaRow["AllowDBNull"] = true;
282 schemaRow["ProviderType"] = 0;
283 schemaRow["IsAliased"] = false;
284 schemaRow["IsExpression"] = false;
285 schemaRow["IsIdentity"] = false;
286 schemaRow["IsAutoIncrement"] = false;
287 schemaRow["IsRowVersion"] = false;
288 schemaRow["IsHidden"] = false;
289 schemaRow["IsLong"] = false;
290 schemaRow["IsReadOnly"] = false;
292 dataTableSchema.Rows.Add (schemaRow);
293 schemaRow.AcceptChanges();
295 dataTableSchema.EndLoadData();
297 return dataTableSchema;
300 public bool NextResult ()
302 return ReadNextColumn ();
305 public bool Read ()
307 return NextResult ();
310 #endregion
312 #region IDataRecord getters
314 public bool GetBoolean (int i)
316 return Convert.ToBoolean (current_row[i]);
319 public byte GetByte (int i)
321 return Convert.ToByte (current_row[i]);
324 public long GetBytes (int i, long fieldOffset, byte[] buffer, int bufferOffset, int length)
326 throw new NotImplementedException ();
329 public char GetChar (int i)
331 return Convert.ToChar (current_row[i]);
334 public long GetChars (int i, long fieldOffset, char[] buffer, int bufferOffset, int length)
336 throw new NotImplementedException ();
339 public IDataReader GetData (int i)
341 throw new NotImplementedException ();
344 public string GetDataTypeName (int i)
346 if (decltypes != null && decltypes[i] != null)
347 return decltypes[i];
348 return "text"; // SQL Lite data type
351 public DateTime GetDateTime (int i)
353 return Convert.ToDateTime (current_row[i]);
356 public decimal GetDecimal (int i)
358 return Convert.ToDecimal (current_row[i]);
361 public double GetDouble (int i)
363 return Convert.ToDouble (current_row[i]);
366 public Type GetFieldType (int i)
368 if (current_row == null)
369 return null;
371 return current_row [i].GetType ();
374 public float GetFloat (int i)
376 return Convert.ToSingle (current_row[i]);
379 public Guid GetGuid (int i)
381 throw new NotImplementedException ();
384 public short GetInt16 (int i)
386 return Convert.ToInt16 (current_row[i]);
389 public int GetInt32 (int i)
391 return Convert.ToInt32 (current_row[i]);
394 public long GetInt64 (int i)
396 return Convert.ToInt64 (current_row[i]);
399 public string GetName (int i)
401 return columns[i];
404 public int GetOrdinal (string name)
406 object v = column_names_sens[name];
407 if (v == null)
408 v = column_names_insens[name];
409 if (v == null)
410 throw new ArgumentException("Column does not exist.");
411 return (int) v;
414 public string GetString (int i)
416 return current_row[i].ToString();
419 public object GetValue (int i)
421 return current_row[i];
424 public int GetValues (object[] values)
426 int num_to_fill = System.Math.Min (values.Length, columns.Length);
427 for (int i = 0; i < num_to_fill; i++) {
428 if (current_row[i] != null) {
429 values[i] = current_row[i];
430 } else {
431 values[i] = DBNull.Value;
434 return num_to_fill;
437 public bool IsDBNull (int i)
439 return (current_row[i] == null);
442 #endregion