2 // Mono.Data.Sqlite.SqliteDataReader.cs
4 // Provides a means of reading a forward-only stream of rows from a Sqlite
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:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
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.
34 using System.Runtime.InteropServices;
35 using System.Collections;
37 using System.Data.Common;
39 namespace Mono.Data.Sqlite
41 public class SqliteDataReader : MarshalByRefObject, IEnumerable, IDataReader, IDisposable, IDataRecord
46 private SqliteCommand command;
47 private ArrayList rows;
48 private string[] columns;
49 private Hashtable column_names_sens, column_names_insens;
50 private int current_row;
53 private int records_affected;
54 private string[] decltypes;
58 #region Constructors and destructors
60 internal SqliteDataReader (SqliteCommand cmd, IntPtr pVm, int version)
63 rows = new ArrayList ();
64 column_names_sens = new Hashtable ();
65 column_names_insens = new Hashtable (CaseInsensitiveHashCodeProvider.DefaultInvariant,
66 CaseInsensitiveComparer.DefaultInvariant);
70 ReadpVm (pVm, version, cmd);
82 public int FieldCount {
83 get { return columns.Length; }
86 public object this[string name] {
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 records_affected; }
106 #region Internal Methods
108 internal void ReadpVm (IntPtr pVm, int version, SqliteCommand cmd)
115 int[] declmode = null;
118 bool hasdata = cmd.ExecuteStatement(pVm, out pN, out pazValue, out pazColName);
120 // For the first row, get the column information
125 // A decltype might be null if the type is unknown to sqlite.
126 decltypes = new string[pN];
127 declmode = new int[pN]; // 1 == integer, 2 == datetime
128 for (int i = 0; i < pN; i++) {
129 IntPtr decl = Sqlite.sqlite3_column_decltype16 (pVm, i);
130 if (decl != IntPtr.Zero) {
131 decltypes[i] = Marshal.PtrToStringUni (decl).ToLower(System.Globalization.CultureInfo.InvariantCulture);
132 if (decltypes[i] == "int" || decltypes[i] == "integer")
134 else if (decltypes[i] == "date" || decltypes[i] == "datetime")
140 columns = new string[pN];
141 for (int i = 0; i < pN; i++) {
144 IntPtr fieldPtr = Marshal.ReadIntPtr (pazColName, i*IntPtr.Size);
145 colName = Sqlite.HeapToString (fieldPtr, ((SqliteConnection)cmd.Connection).Encoding);
147 colName = Marshal.PtrToStringUni (Sqlite.sqlite3_column_name16 (pVm, i));
149 columns[i] = colName;
150 column_names_sens [colName] = i;
151 column_names_insens [colName] = i;
157 object[] data_row = new object [pN];
158 for (int i = 0; i < pN; i++) {
160 IntPtr fieldPtr = Marshal.ReadIntPtr (pazValue, i*IntPtr.Size);
161 data_row[i] = Sqlite.HeapToString (fieldPtr, ((SqliteConnection)cmd.Connection).Encoding);
163 switch (Sqlite.sqlite3_column_type (pVm, i)) {
165 long val = Sqlite.sqlite3_column_int64 (pVm, i);
167 // If the column was declared as an 'int' or 'integer', let's play
168 // nice and return an int (version 3 only).
169 if (declmode[i] == 1 && val >= int.MinValue && val <= int.MaxValue)
170 data_row[i] = (int)val;
172 // Or if it was declared a date or datetime, do the reverse of what we
173 // do for DateTime parameters.
174 else if (declmode[i] == 2)
175 data_row[i] = DateTime.FromFileTime(val);
182 data_row[i] = Sqlite.sqlite3_column_double (pVm, i);
185 data_row[i] = Marshal.PtrToStringUni (Sqlite.sqlite3_column_text16 (pVm, i));
187 // If the column was declared as a 'date' or 'datetime', let's play
188 // nice and return a DateTime (version 3 only).
189 if (declmode[i] == 2)
190 data_row[i] = DateTime.Parse((string)data_row[i]);
193 int blobbytes = Sqlite.sqlite3_column_bytes16 (pVm, i);
194 IntPtr blobptr = Sqlite.sqlite3_column_blob (pVm, i);
195 byte[] blob = new byte[blobbytes];
196 Marshal.Copy (blobptr, blob, 0, blobbytes);
203 throw new ApplicationException ("FATAL: Unknown sqlite3_column_type");
211 internal void ReadingDone ()
213 records_affected = command.NumChanges ();
219 #region Public Methods
226 public void Dispose ()
231 IEnumerator IEnumerable.GetEnumerator ()
233 return new DbEnumerator (this);
236 public DataTable GetSchemaTable ()
238 DataTable dataTableSchema = new DataTable ();
240 dataTableSchema.Columns.Add ("ColumnName", typeof (String));
241 dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (Int32));
242 dataTableSchema.Columns.Add ("ColumnSize", typeof (Int32));
243 dataTableSchema.Columns.Add ("NumericPrecision", typeof (Int32));
244 dataTableSchema.Columns.Add ("NumericScale", typeof (Int32));
245 dataTableSchema.Columns.Add ("IsUnique", typeof (Boolean));
246 dataTableSchema.Columns.Add ("IsKey", typeof (Boolean));
247 dataTableSchema.Columns.Add ("BaseCatalogName", typeof (String));
248 dataTableSchema.Columns.Add ("BaseColumnName", typeof (String));
249 dataTableSchema.Columns.Add ("BaseSchemaName", typeof (String));
250 dataTableSchema.Columns.Add ("BaseTableName", typeof (String));
251 dataTableSchema.Columns.Add ("DataType", typeof(Type));
252 dataTableSchema.Columns.Add ("AllowDBNull", typeof (Boolean));
253 dataTableSchema.Columns.Add ("ProviderType", typeof (Int32));
254 dataTableSchema.Columns.Add ("IsAliased", typeof (Boolean));
255 dataTableSchema.Columns.Add ("IsExpression", typeof (Boolean));
256 dataTableSchema.Columns.Add ("IsIdentity", typeof (Boolean));
257 dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (Boolean));
258 dataTableSchema.Columns.Add ("IsRowVersion", typeof (Boolean));
259 dataTableSchema.Columns.Add ("IsHidden", typeof (Boolean));
260 dataTableSchema.Columns.Add ("IsLong", typeof (Boolean));
261 dataTableSchema.Columns.Add ("IsReadOnly", typeof (Boolean));
263 dataTableSchema.BeginLoadData();
264 for (int i = 0; i < this.FieldCount; i += 1 ) {
266 DataRow schemaRow = dataTableSchema.NewRow ();
268 schemaRow["ColumnName"] = columns[i];
269 schemaRow["ColumnOrdinal"] = i;
270 schemaRow["ColumnSize"] = 0;
271 schemaRow["NumericPrecision"] = 0;
272 schemaRow["NumericScale"] = 0;
273 schemaRow["IsUnique"] = false;
274 schemaRow["IsKey"] = false;
275 schemaRow["BaseCatalogName"] = "";
276 schemaRow["BaseColumnName"] = columns[i];
277 schemaRow["BaseSchemaName"] = "";
278 schemaRow["BaseTableName"] = "";
279 schemaRow["DataType"] = typeof(string);
280 schemaRow["AllowDBNull"] = true;
281 schemaRow["ProviderType"] = 0;
282 schemaRow["IsAliased"] = false;
283 schemaRow["IsExpression"] = false;
284 schemaRow["IsIdentity"] = false;
285 schemaRow["IsAutoIncrement"] = false;
286 schemaRow["IsRowVersion"] = false;
287 schemaRow["IsHidden"] = false;
288 schemaRow["IsLong"] = false;
289 schemaRow["IsReadOnly"] = false;
291 dataTableSchema.Rows.Add (schemaRow);
292 schemaRow.AcceptChanges();
294 dataTableSchema.EndLoadData();
296 return dataTableSchema;
299 public bool NextResult ()
303 return (current_row < rows.Count);
308 return NextResult ();
313 #region IDataRecord getters
315 public bool GetBoolean (int i)
317 return Convert.ToBoolean (((object[]) rows[current_row])[i]);
320 public byte GetByte (int i)
322 return Convert.ToByte (((object[]) rows[current_row])[i]);
325 public long GetBytes (int i, long fieldOffset, byte[] buffer, int bufferOffset, int length)
327 byte[] data = (byte[])(((object[]) rows[current_row])[i]);
329 Array.Copy (data, fieldOffset, buffer, bufferOffset, length);
330 return data.LongLength - fieldOffset;
333 public char GetChar (int i)
335 return Convert.ToChar (((object[]) rows[current_row])[i]);
338 public long GetChars (int i, long fieldOffset, char[] buffer, int bufferOffset, int length)
340 char[] data = (char[])(((object[]) rows[current_row])[i]);
342 Array.Copy (data, fieldOffset, buffer, bufferOffset, length);
343 return data.LongLength - fieldOffset;
346 public IDataReader GetData (int i)
348 return ((IDataReader) this [i]);
351 public string GetDataTypeName (int i)
353 if (decltypes != null && decltypes[i] != null)
355 return "text"; // SQL Lite data type
358 public DateTime GetDateTime (int i)
360 return Convert.ToDateTime (((object[]) rows[current_row])[i]);
363 public decimal GetDecimal (int i)
365 return Convert.ToDecimal (((object[]) rows[current_row])[i]);
368 public double GetDouble (int i)
370 return Convert.ToDouble (((object[]) rows[current_row])[i]);
373 public Type GetFieldType (int i)
375 int row = current_row;
376 if (row == -1 && rows.Count == 0) return typeof(string);
377 if (row == -1) row = 0;
378 object element = ((object[]) rows[row])[i];
380 return element.GetType();
382 return typeof (string);
384 // Note that the return value isn't guaranteed to
385 // be the same as the rows are read if different
386 // types of information are stored in the column.
389 public float GetFloat (int i)
391 return Convert.ToSingle (((object[]) rows[current_row])[i]);
394 public Guid GetGuid (int i)
396 object value = GetValue (i);
397 if (!(value is Guid)) {
399 throw new SqliteExecutionException ("Column value must not be null");
400 throw new InvalidCastException ("Type is " + value.GetType ().ToString ());
402 return ((Guid) value);
405 public short GetInt16 (int i)
407 return Convert.ToInt16 (((object[]) rows[current_row])[i]);
410 public int GetInt32 (int i)
412 return Convert.ToInt32 (((object[]) rows[current_row])[i]);
415 public long GetInt64 (int i)
417 return Convert.ToInt64 (((object[]) rows[current_row])[i]);
420 public string GetName (int i)
425 public int GetOrdinal (string name)
427 object v = column_names_sens[name];
429 v = column_names_insens[name];
431 throw new ArgumentException("Column does not exist.");
435 public string GetString (int i)
437 return (((object[]) rows[current_row])[i]).ToString();
440 public object GetValue (int i)
442 return ((object[]) rows[current_row])[i];
445 public int GetValues (object[] values)
447 int num_to_fill = System.Math.Min (values.Length, columns.Length);
448 for (int i = 0; i < num_to_fill; i++) {
449 if (((object[]) rows[current_row])[i] != null) {
450 values[i] = ((object[]) rows[current_row])[i];
452 values[i] = DBNull.Value;
458 public bool IsDBNull (int i)
460 return (((object[]) rows[current_row])[i] == null);