2 // Mono.Data.SqliteClient.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
.SqliteClient
41 public class SqliteDataReader
: MarshalByRefObject
, IEnumerable
, IDataReader
, IDisposable
, IDataRecord
46 private SqliteCommand command
;
49 private ArrayList current_row
;
50 private string[] columns
;
51 private Hashtable column_names_sens
, column_names_insens
;
53 private string[] decltypes
;
54 private int[] declmode
;
59 #region Constructors and destructors
61 internal SqliteDataReader (SqliteCommand cmd
, IntPtr _pVm
, int _version
)
67 current_row
= new ArrayList();
69 column_names_sens
= new Hashtable ();
70 column_names_insens
= new Hashtable (CaseInsensitiveHashCodeProvider
.DefaultInvariant
, CaseInsensitiveComparer
.DefaultInvariant
);
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 command.NumChanges (); }
106 #region Internal Methods
108 internal bool ReadNextColumn ()
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) {
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")
129 else if (decltypes
[i
] == "date" || decltypes
[i
] == "datetime")
135 columns
= new string[pN
];
136 for (int i
= 0; i
< pN
; i
++) {
139 IntPtr fieldPtr
= Marshal
.ReadIntPtr (pazColName
, i
*IntPtr
.Size
);
140 colName
= Sqlite
.HeapToString (fieldPtr
, command
.Connection
.Encoding
);
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
;
154 for (int i
= 0; i
< pN
; i
++) {
156 IntPtr fieldPtr
= Marshal
.ReadIntPtr (pazValue
, i
*IntPtr
.Size
);
157 current_row
.Add (Sqlite
.HeapToString (fieldPtr
, command
.Connection
.Encoding
));
159 switch (Sqlite
.sqlite3_column_type (pVm
, i
)) {
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
));
174 current_row
.Add (val
);
178 current_row
.Add (Sqlite
.sqlite3_column_double (pVm
, i
));
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
));
188 current_row
.Add (strval
);
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
);
199 current_row
.Add (null);
202 throw new ApplicationException ("FATAL: Unknown sqlite3_column_type");
211 #region Public Methods
217 if (pVm
!= IntPtr
.Zero
) {
220 Sqlite
.sqlite3_finalize (pVm
);
222 Sqlite
.sqlite_finalize (pVm
, out errMsg
);
227 public void Dispose ()
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 ();
307 return NextResult ();
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)
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)
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
)
404 public int GetOrdinal (string name
)
406 object v
= column_names_sens
[name
];
408 v
= column_names_insens
[name
];
410 throw new ArgumentException("Column does not exist.");
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
];
431 values
[i
] = DBNull
.Value
;
437 public bool IsDBNull (int i
)
439 return (current_row
[i
] == null);