Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Mono.Data.Sqlite / Mono.Data.Sqlite_2.0 / SQLite3.cs
1 /********************************************************\r
2  * ADO.NET 2.0 Data Provider for SQLite Version 3.X\r
3  * Written by Robert Simpson (robert@blackcastlesoft.com)\r
4  * \r
5  * Released to the public domain, use at your own risk!\r
6  ********************************************************/\r
7 \r
8 namespace Mono.Data.Sqlite\r
9 {\r
10   using System;\r
11   using System.Data;\r
12   using System.Runtime.InteropServices;\r
13   using System.Collections.Generic;\r
14   using System.Globalization;\r
15 \r
16   /// <summary>\r
17   /// This class implements SQLiteBase completely, and is the guts of the code that interop's SQLite with .NET\r
18   /// </summary>\r
19   internal class SQLite3 : SQLiteBase\r
20   {\r
21     /// <summary>\r
22     /// The opaque pointer returned to us by the sqlite provider\r
23     /// </summary>\r
24     protected SqliteConnectionHandle _sql;\r
25     protected string _fileName;\r
26     protected bool _usePool;\r
27     protected int _poolVersion = 0;\r
28 \r
29 #if !PLATFORM_COMPACTFRAMEWORK\r
30     private bool _buildingSchema = false;\r
31 #endif\r
32 #if MONOTOUCH\r
33     GCHandle gch;\r
34 #endif\r
35     /// <summary>\r
36     /// The user-defined functions registered on this connection\r
37     /// </summary>\r
38     protected SqliteFunction[] _functionsArray;\r
39 \r
40     internal SQLite3(SQLiteDateFormats fmt)\r
41       : base(fmt)\r
42     {\r
43 #if MONOTOUCH\r
44       gch = GCHandle.Alloc (this);\r
45 #endif\r
46     }\r
47 \r
48     protected override void Dispose(bool bDisposing)\r
49     {\r
50       if (bDisposing)\r
51         Close();\r
52     }\r
53 \r
54     // It isn't necessary to cleanup any functions we've registered.  If the connection\r
55     // goes to the pool and is resurrected later, re-registered functions will overwrite the\r
56     // previous functions.  The SqliteFunctionCookieHandle will take care of freeing unmanaged\r
57     // resources belonging to the previously-registered functions.\r
58     internal override void Close()\r
59     {\r
60       if (_sql != null)\r
61       {\r
62         if (_usePool)\r
63         {\r
64           SQLiteBase.ResetConnection(_sql);\r
65           SqliteConnectionPool.Add(_fileName, _sql, _poolVersion);\r
66         }\r
67         else\r
68           _sql.Dispose();\r
69       }\r
70 \r
71       _sql = null;\r
72 #if MONOTOUCH\r
73       if (gch.IsAllocated)\r
74         gch.Free ();\r
75 #endif\r
76     }\r
77 \r
78     internal override void Cancel()\r
79     {\r
80       UnsafeNativeMethods.sqlite3_interrupt(_sql);\r
81     }\r
82 \r
83     internal override string Version\r
84     {\r
85       get\r
86       {\r
87         return SQLite3.SQLiteVersion;\r
88       }\r
89     }\r
90 \r
91     internal static string SQLiteVersion\r
92     {\r
93       get\r
94       {\r
95         return UTF8ToString(UnsafeNativeMethods.sqlite3_libversion(), -1);\r
96       }\r
97     }\r
98 \r
99     internal override int Changes\r
100     {\r
101       get\r
102       {\r
103         return UnsafeNativeMethods.sqlite3_changes(_sql);\r
104       }\r
105     }\r
106 \r
107     internal override void Open(string strFilename, SQLiteOpenFlagsEnum flags, int maxPoolSize, bool usePool)\r
108     {\r
109       if (_sql != null) return;\r
110 \r
111       _usePool = usePool;\r
112       if (usePool)\r
113       {\r
114         _fileName = strFilename;\r
115         _sql = SqliteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);\r
116       }\r
117 \r
118       if (_sql == null)\r
119       {\r
120         IntPtr db;\r
121 \r
122 #if !SQLITE_STANDARD\r
123         int n = UnsafeNativeMethods.sqlite3_open_interop(ToUTF8(strFilename), (int)flags, out db);\r
124 #else\r
125         // Compatibility with versions < 3.5.0\r
126         int n;\r
127 \r
128         try {\r
129                 n = UnsafeNativeMethods.sqlite3_open_v2(ToUTF8(strFilename), out db, (int)flags, IntPtr.Zero);\r
130         } catch (EntryPointNotFoundException) {\r
131                 Console.WriteLine ("Your sqlite3 version is old - please upgrade to at least v3.5.0!");\r
132                 n = UnsafeNativeMethods.sqlite3_open (ToUTF8 (strFilename), out db);\r
133         }\r
134         \r
135 #endif\r
136         if (n > 0) throw new SqliteException(n, null);\r
137 \r
138         _sql = db;\r
139       }\r
140       // Bind functions to this connection.  If any previous functions of the same name\r
141       // were already bound, then the new bindings replace the old.\r
142       _functionsArray = SqliteFunction.BindFunctions(this);\r
143       SetTimeout(0);\r
144     }\r
145 \r
146     internal override void ClearPool()\r
147     {\r
148       SqliteConnectionPool.ClearPool(_fileName);\r
149     }\r
150 \r
151     internal override void SetTimeout(int nTimeoutMS)\r
152     {\r
153       int n = UnsafeNativeMethods.sqlite3_busy_timeout(_sql, nTimeoutMS);\r
154       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
155     }\r
156 \r
157     internal override bool Step(SqliteStatement stmt)\r
158     {\r
159       int n;\r
160       Random rnd = null;\r
161       uint starttick = (uint)Environment.TickCount;\r
162       uint timeout = (uint)(stmt._command._commandTimeout * 1000);\r
163 \r
164       while (true)\r
165       {\r
166         n = UnsafeNativeMethods.sqlite3_step(stmt._sqlite_stmt);\r
167 \r
168         if (n == 100) return true;\r
169         if (n == 101) return false;\r
170 \r
171         if (n > 0)\r
172         {\r
173           int r;\r
174 \r
175           // An error occurred, attempt to reset the statement.  If the reset worked because the\r
176           // schema has changed, re-try the step again.  If it errored our because the database\r
177           // is locked, then keep retrying until the command timeout occurs.\r
178           r = Reset(stmt);\r
179 \r
180           if (r == 0)\r
181             throw new SqliteException(n, SQLiteLastError());\r
182 \r
183           else if ((r == 6 || r == 5) && stmt._command != null) // SQLITE_LOCKED || SQLITE_BUSY\r
184           {\r
185             // Keep trying\r
186             if (rnd == null) // First time we've encountered the lock\r
187               rnd = new Random();\r
188 \r
189             // If we've exceeded the command's timeout, give up and throw an error\r
190             if ((uint)Environment.TickCount - starttick > timeout)\r
191             {\r
192               throw new SqliteException(r, SQLiteLastError());\r
193             }\r
194             else\r
195             {\r
196               // Otherwise sleep for a random amount of time up to 150ms\r
197               System.Threading.Thread.CurrentThread.Join(rnd.Next(1, 150));\r
198             }\r
199           }\r
200         }\r
201       }\r
202     }\r
203 \r
204     internal override int Reset(SqliteStatement stmt)\r
205     {\r
206       int n;\r
207 \r
208 #if !SQLITE_STANDARD\r
209       n = UnsafeNativeMethods.sqlite3_reset_interop(stmt._sqlite_stmt);\r
210 #else\r
211       n = UnsafeNativeMethods.sqlite3_reset(stmt._sqlite_stmt);\r
212 #endif\r
213 \r
214       // If the schema changed, try and re-prepare it\r
215       if (n == 17) // SQLITE_SCHEMA\r
216       {\r
217         // Recreate a dummy statement\r
218         string str;\r
219         using (SqliteStatement tmp = Prepare(null, stmt._sqlStatement, null, (uint)(stmt._command._commandTimeout * 1000), out str))\r
220         {\r
221           // Finalize the existing statement\r
222           stmt._sqlite_stmt.Dispose();\r
223           // Reassign a new statement pointer to the old statement and clear the temporary one\r
224           stmt._sqlite_stmt = tmp._sqlite_stmt;\r
225           tmp._sqlite_stmt = null;\r
226 \r
227           // Reapply parameters\r
228           stmt.BindParameters();\r
229         }\r
230         return -1; // Reset was OK, with schema change\r
231       }\r
232       else if (n == 6 || n == 5) // SQLITE_LOCKED || SQLITE_BUSY\r
233         return n;\r
234 \r
235       if (n > 0)\r
236         throw new SqliteException(n, SQLiteLastError());\r
237 \r
238       return 0; // We reset OK, no schema changes\r
239     }\r
240 \r
241     internal override string SQLiteLastError()\r
242     {\r
243       return SQLiteBase.SQLiteLastError(_sql);\r
244     }\r
245 \r
246     internal override SqliteStatement Prepare(SqliteConnection cnn, string strSql, SqliteStatement previous, uint timeoutMS, out string strRemain)\r
247     {\r
248       IntPtr stmt = IntPtr.Zero;\r
249       IntPtr ptr = IntPtr.Zero;\r
250       int len = 0;\r
251       int n = 17;\r
252       int retries = 0;\r
253       byte[] b = ToUTF8(strSql);\r
254       string typedefs = null;\r
255       SqliteStatement cmd = null;\r
256       Random rnd = null;\r
257       uint starttick = (uint)Environment.TickCount;\r
258 \r
259       GCHandle handle = GCHandle.Alloc(b, GCHandleType.Pinned);\r
260       IntPtr psql = handle.AddrOfPinnedObject();\r
261       try\r
262       {\r
263         while ((n == 17 || n == 6 || n == 5) && retries < 3)\r
264         {\r
265 #if !SQLITE_STANDARD\r
266           n = UnsafeNativeMethods.sqlite3_prepare_interop(_sql, psql, b.Length - 1, out stmt, out ptr, out len);\r
267 #else\r
268           n = UnsafeNativeMethods.sqlite3_prepare(_sql, psql, b.Length - 1, out stmt, out ptr);\r
269           len = -1;\r
270 #endif\r
271 \r
272           if (n == 17)\r
273             retries++;\r
274           else if (n == 1)\r
275           {\r
276             if (String.Compare(SQLiteLastError(), "near \"TYPES\": syntax error", StringComparison.OrdinalIgnoreCase) == 0)\r
277             {\r
278               int pos = strSql.IndexOf(';');\r
279               if (pos == -1) pos = strSql.Length - 1;\r
280 \r
281               typedefs = strSql.Substring(0, pos + 1);\r
282               strSql = strSql.Substring(pos + 1);\r
283 \r
284               strRemain = "";\r
285 \r
286               while (cmd == null && strSql.Length > 0)\r
287               {\r
288                 cmd = Prepare(cnn, strSql, previous, timeoutMS, out strRemain);\r
289                 strSql = strRemain;\r
290               }\r
291 \r
292               if (cmd != null)\r
293                 cmd.SetTypes(typedefs);\r
294 \r
295               return cmd;\r
296             }\r
297 #if !PLATFORM_COMPACTFRAMEWORK\r
298             else if (_buildingSchema == false && String.Compare(SQLiteLastError(), 0, "no such table: TEMP.SCHEMA", 0, 26, StringComparison.OrdinalIgnoreCase) == 0)\r
299             {\r
300               strRemain = "";\r
301               _buildingSchema = true;\r
302               try\r
303               {\r
304                 ISQLiteSchemaExtensions ext = ((IServiceProvider)SqliteFactory.Instance).GetService(typeof(ISQLiteSchemaExtensions)) as ISQLiteSchemaExtensions;\r
305 \r
306                 if (ext != null)\r
307                   ext.BuildTempSchema(cnn);\r
308 \r
309                 while (cmd == null && strSql.Length > 0)\r
310                 {\r
311                   cmd = Prepare(cnn, strSql, previous, timeoutMS, out strRemain);\r
312                   strSql = strRemain;\r
313                 }\r
314 \r
315                 return cmd;\r
316               }\r
317               finally\r
318               {\r
319                 _buildingSchema = false;\r
320               }\r
321             }\r
322 #endif\r
323           }\r
324           else if (n == 6 || n == 5) // Locked -- delay a small amount before retrying\r
325           {\r
326             // Keep trying\r
327             if (rnd == null) // First time we've encountered the lock\r
328               rnd = new Random();\r
329 \r
330             // If we've exceeded the command's timeout, give up and throw an error\r
331             if ((uint)Environment.TickCount - starttick > timeoutMS)\r
332             {\r
333               throw new SqliteException(n, SQLiteLastError());\r
334             }\r
335             else\r
336             {\r
337               // Otherwise sleep for a random amount of time up to 150ms\r
338               System.Threading.Thread.CurrentThread.Join(rnd.Next(1, 150));\r
339             }\r
340           }\r
341         }\r
342 \r
343         if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
344 \r
345         strRemain = UTF8ToString(ptr, len);\r
346 \r
347         if (stmt != IntPtr.Zero) cmd = new SqliteStatement(this, stmt, strSql.Substring(0, strSql.Length - strRemain.Length), previous);\r
348 \r
349         return cmd;\r
350       }\r
351       finally\r
352       {\r
353         handle.Free();\r
354       }\r
355     }\r
356 \r
357     internal override void Bind_Double(SqliteStatement stmt, int index, double value)\r
358     {\r
359 #if !PLATFORM_COMPACTFRAMEWORK\r
360       int n = UnsafeNativeMethods.sqlite3_bind_double(stmt._sqlite_stmt, index, value);\r
361 #else\r
362       int n = UnsafeNativeMethods.sqlite3_bind_double_interop(stmt._sqlite_stmt, index, ref value);\r
363 #endif\r
364       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
365     }\r
366 \r
367     internal override void Bind_Int32(SqliteStatement stmt, int index, int value)\r
368     {\r
369       int n = UnsafeNativeMethods.sqlite3_bind_int(stmt._sqlite_stmt, index, value);\r
370       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
371     }\r
372 \r
373     internal override void Bind_Int64(SqliteStatement stmt, int index, long value)\r
374     {\r
375 #if !PLATFORM_COMPACTFRAMEWORK\r
376       int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, value);\r
377 #else\r
378       int n = UnsafeNativeMethods.sqlite3_bind_int64_interop(stmt._sqlite_stmt, index, ref value);\r
379 #endif\r
380       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
381     }\r
382 \r
383     internal override void Bind_Text(SqliteStatement stmt, int index, string value)\r
384     {\r
385       byte[] b = ToUTF8(value);\r
386       int n = UnsafeNativeMethods.sqlite3_bind_text(stmt._sqlite_stmt, index, b, b.Length - 1, (IntPtr)(-1));\r
387       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
388     }\r
389 \r
390     internal override void Bind_DateTime(SqliteStatement stmt, int index, DateTime dt)\r
391     {\r
392       byte[] b = ToUTF8(dt);\r
393       int n = UnsafeNativeMethods.sqlite3_bind_text(stmt._sqlite_stmt, index, b, b.Length - 1, (IntPtr)(-1));\r
394       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
395     }\r
396 \r
397     internal override void Bind_Blob(SqliteStatement stmt, int index, byte[] blobData)\r
398     {\r
399       int n = UnsafeNativeMethods.sqlite3_bind_blob(stmt._sqlite_stmt, index, blobData, blobData.Length, (IntPtr)(-1));\r
400       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
401     }\r
402 \r
403     internal override void Bind_Null(SqliteStatement stmt, int index)\r
404     {\r
405       int n = UnsafeNativeMethods.sqlite3_bind_null(stmt._sqlite_stmt, index);\r
406       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
407     }\r
408 \r
409     internal override int Bind_ParamCount(SqliteStatement stmt)\r
410     {\r
411       return UnsafeNativeMethods.sqlite3_bind_parameter_count(stmt._sqlite_stmt);\r
412     }\r
413 \r
414     internal override string Bind_ParamName(SqliteStatement stmt, int index)\r
415     {\r
416 #if !SQLITE_STANDARD\r
417       int len;\r
418       return UTF8ToString(UnsafeNativeMethods.sqlite3_bind_parameter_name_interop(stmt._sqlite_stmt, index, out len), len);\r
419 #else\r
420       return UTF8ToString(UnsafeNativeMethods.sqlite3_bind_parameter_name(stmt._sqlite_stmt, index), -1);\r
421 #endif\r
422     }\r
423 \r
424     internal override int Bind_ParamIndex(SqliteStatement stmt, string paramName)\r
425     {\r
426       return UnsafeNativeMethods.sqlite3_bind_parameter_index(stmt._sqlite_stmt, ToUTF8(paramName));\r
427     }\r
428 \r
429     internal override int ColumnCount(SqliteStatement stmt)\r
430     {\r
431       return UnsafeNativeMethods.sqlite3_column_count(stmt._sqlite_stmt);\r
432     }\r
433 \r
434     internal override string ColumnName(SqliteStatement stmt, int index)\r
435     {\r
436 #if !SQLITE_STANDARD\r
437       int len;\r
438       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_name_interop(stmt._sqlite_stmt, index, out len), len);\r
439 #else\r
440       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_name(stmt._sqlite_stmt, index), -1);\r
441 #endif\r
442     }\r
443 \r
444     internal override TypeAffinity ColumnAffinity(SqliteStatement stmt, int index)\r
445     {\r
446       return UnsafeNativeMethods.sqlite3_column_type(stmt._sqlite_stmt, index);\r
447     }\r
448 \r
449     internal override string ColumnType(SqliteStatement stmt, int index, out TypeAffinity nAffinity)\r
450     {\r
451       int len;\r
452 #if !SQLITE_STANDARD\r
453       IntPtr p = UnsafeNativeMethods.sqlite3_column_decltype_interop(stmt._sqlite_stmt, index, out len);\r
454 #else\r
455       len = -1;\r
456       IntPtr p = UnsafeNativeMethods.sqlite3_column_decltype(stmt._sqlite_stmt, index);\r
457 #endif\r
458       nAffinity = ColumnAffinity(stmt, index);\r
459 \r
460       if (p != IntPtr.Zero) return UTF8ToString(p, len);\r
461       else\r
462       {\r
463         string[] ar = stmt.TypeDefinitions;\r
464         if (ar != null)\r
465         {\r
466           if (index < ar.Length && ar[index] != null)\r
467             return ar[index];\r
468         }\r
469         return String.Empty;\r
470 \r
471         //switch (nAffinity)\r
472         //{\r
473         //  case TypeAffinity.Int64:\r
474         //    return "BIGINT";\r
475         //  case TypeAffinity.Double:\r
476         //    return "DOUBLE";\r
477         //  case TypeAffinity.Blob:\r
478         //    return "BLOB";\r
479         //  default:\r
480         //    return "TEXT";\r
481         //}\r
482       }\r
483     }\r
484 \r
485     internal override int ColumnIndex(SqliteStatement stmt, string columnName)\r
486     {\r
487       int x = ColumnCount(stmt);\r
488 \r
489       for (int n = 0; n < x; n++)\r
490       {\r
491         if (String.Compare(columnName, ColumnName(stmt, n), true, CultureInfo.InvariantCulture) == 0)\r
492           return n;\r
493       }\r
494       return -1;\r
495     }\r
496 \r
497     internal override string ColumnOriginalName(SqliteStatement stmt, int index)\r
498     {\r
499 #if !SQLITE_STANDARD\r
500       int len;\r
501       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_origin_name_interop(stmt._sqlite_stmt, index, out len), len);\r
502 #else\r
503       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_origin_name(stmt._sqlite_stmt, index), -1);\r
504 #endif\r
505     }\r
506 \r
507     internal override string ColumnDatabaseName(SqliteStatement stmt, int index)\r
508     {\r
509 #if !SQLITE_STANDARD\r
510       int len;\r
511       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_database_name_interop(stmt._sqlite_stmt, index, out len), len);\r
512 #else\r
513       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_database_name(stmt._sqlite_stmt, index), -1);\r
514 #endif\r
515     }\r
516 \r
517     internal override string ColumnTableName(SqliteStatement stmt, int index)\r
518     {\r
519 #if !SQLITE_STANDARD\r
520       int len;\r
521       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_table_name_interop(stmt._sqlite_stmt, index, out len), len);\r
522 #else\r
523       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_table_name(stmt._sqlite_stmt, index), -1);\r
524 #endif\r
525     }\r
526 \r
527     internal override void ColumnMetaData(string dataBase, string table, string column, out string dataType, out string collateSequence, out bool notNull, out bool primaryKey, out bool autoIncrement)\r
528     {\r
529       IntPtr dataTypePtr;\r
530       IntPtr collSeqPtr;\r
531       int nnotNull;\r
532       int nprimaryKey;\r
533       int nautoInc;\r
534       int n;\r
535       int dtLen;\r
536       int csLen;\r
537 \r
538 #if !SQLITE_STANDARD\r
539       n = UnsafeNativeMethods.sqlite3_table_column_metadata_interop(_sql, ToUTF8(dataBase), ToUTF8(table), ToUTF8(column), out dataTypePtr, out collSeqPtr, out nnotNull, out nprimaryKey, out nautoInc, out dtLen, out csLen);\r
540 #else\r
541       dtLen = -1;\r
542       csLen = -1;\r
543       n = UnsafeNativeMethods.sqlite3_table_column_metadata(_sql, ToUTF8(dataBase), ToUTF8(table), ToUTF8(column), out dataTypePtr, out collSeqPtr, out nnotNull, out nprimaryKey, out nautoInc);\r
544 #endif\r
545       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
546 \r
547       dataType = UTF8ToString(dataTypePtr, dtLen);\r
548       collateSequence = UTF8ToString(collSeqPtr, csLen);\r
549 \r
550       notNull = (nnotNull == 1);\r
551       primaryKey = (nprimaryKey == 1);\r
552       autoIncrement = (nautoInc == 1);\r
553     }\r
554 \r
555     internal override double GetDouble(SqliteStatement stmt, int index)\r
556     {\r
557       double value;\r
558 #if !PLATFORM_COMPACTFRAMEWORK\r
559       value = UnsafeNativeMethods.sqlite3_column_double(stmt._sqlite_stmt, index);\r
560 #else\r
561       UnsafeNativeMethods.sqlite3_column_double_interop(stmt._sqlite_stmt, index, out value);\r
562 #endif\r
563       return value;\r
564     }\r
565 \r
566     internal override int GetInt32(SqliteStatement stmt, int index)\r
567     {\r
568       return UnsafeNativeMethods.sqlite3_column_int(stmt._sqlite_stmt, index);\r
569     }\r
570 \r
571     internal override long GetInt64(SqliteStatement stmt, int index)\r
572     {\r
573       long value;\r
574 #if !PLATFORM_COMPACTFRAMEWORK\r
575       value = UnsafeNativeMethods.sqlite3_column_int64(stmt._sqlite_stmt, index);\r
576 #else\r
577       UnsafeNativeMethods.sqlite3_column_int64_interop(stmt._sqlite_stmt, index, out value);\r
578 #endif\r
579       return value;\r
580     }\r
581 \r
582     internal override string GetText(SqliteStatement stmt, int index)\r
583     {\r
584 #if !SQLITE_STANDARD\r
585       int len;\r
586       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text_interop(stmt._sqlite_stmt, index, out len), len);\r
587 #else\r
588       return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index), -1);\r
589 #endif\r
590     }\r
591 \r
592     internal override DateTime GetDateTime(SqliteStatement stmt, int index)\r
593     {\r
594 #if !SQLITE_STANDARD\r
595       int len;\r
596       return ToDateTime(UnsafeNativeMethods.sqlite3_column_text_interop(stmt._sqlite_stmt, index, out len), len);\r
597 #else\r
598       return ToDateTime(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index), -1);\r
599 #endif\r
600     }\r
601 \r
602     internal override long GetBytes(SqliteStatement stmt, int index, int nDataOffset, byte[] bDest, int nStart, int nLength)\r
603     {\r
604       IntPtr ptr;\r
605       int nlen;\r
606       int nCopied = nLength;\r
607 \r
608       nlen = UnsafeNativeMethods.sqlite3_column_bytes(stmt._sqlite_stmt, index);\r
609       ptr = UnsafeNativeMethods.sqlite3_column_blob(stmt._sqlite_stmt, index);\r
610 \r
611       if (bDest == null) return nlen;\r
612 \r
613       if (nCopied + nStart > bDest.Length) nCopied = bDest.Length - nStart;\r
614       if (nCopied + nDataOffset > nlen) nCopied = nlen - nDataOffset;\r
615 \r
616       unsafe {\r
617               if (nCopied > 0)\r
618                       Marshal.Copy((IntPtr)((byte*)ptr + nDataOffset), bDest, nStart, nCopied);\r
619               else nCopied = 0;\r
620       }\r
621 \r
622       return nCopied;\r
623     }\r
624 \r
625     internal override long GetChars(SqliteStatement stmt, int index, int nDataOffset, char[] bDest, int nStart, int nLength)\r
626     {\r
627       int nlen;\r
628       int nCopied = nLength;\r
629 \r
630       string str = GetText(stmt, index);\r
631       nlen = str.Length;\r
632 \r
633       if (bDest == null) return nlen;\r
634 \r
635       if (nCopied + nStart > bDest.Length) nCopied = bDest.Length - nStart;\r
636       if (nCopied + nDataOffset > nlen) nCopied = nlen - nDataOffset;\r
637 \r
638       if (nCopied > 0)\r
639         str.CopyTo(nDataOffset, bDest, nStart, nCopied);\r
640       else nCopied = 0;\r
641 \r
642       return nCopied;\r
643     }\r
644 \r
645     internal override bool IsNull(SqliteStatement stmt, int index)\r
646     {\r
647       return (ColumnAffinity(stmt, index) == TypeAffinity.Null);\r
648     }\r
649 \r
650     internal override int AggregateCount(IntPtr context)\r
651     {\r
652       return UnsafeNativeMethods.sqlite3_aggregate_count(context);\r
653     }\r
654 \r
655     internal override void CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal)\r
656     {\r
657       int n;\r
658 \r
659 #if !SQLITE_STANDARD\r
660       n = UnsafeNativeMethods.sqlite3_create_function_interop(_sql, ToUTF8(strFunction), nArgs, 4, IntPtr.Zero, func, funcstep, funcfinal, (needCollSeq == true) ? 1 : 0);\r
661       if (n == 0) n = UnsafeNativeMethods.sqlite3_create_function_interop(_sql, ToUTF8(strFunction), nArgs, 1, IntPtr.Zero, func, funcstep, funcfinal, (needCollSeq == true) ? 1 : 0);\r
662 #else\r
663       n = UnsafeNativeMethods.sqlite3_create_function(_sql, ToUTF8(strFunction), nArgs, 4, IntPtr.Zero, func, funcstep, funcfinal);\r
664       if (n == 0) n = UnsafeNativeMethods.sqlite3_create_function(_sql, ToUTF8(strFunction), nArgs, 1, IntPtr.Zero, func, funcstep, funcfinal);\r
665 #endif\r
666       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
667     }\r
668 \r
669     internal override void CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16, IntPtr user_data)\r
670     {\r
671       int n = UnsafeNativeMethods.sqlite3_create_collation(_sql, ToUTF8(strCollation), 2, user_data, func16);\r
672       if (n == 0) UnsafeNativeMethods.sqlite3_create_collation(_sql, ToUTF8(strCollation), 1, user_data, func);\r
673       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
674     }\r
675 \r
676     internal override int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, string s1, string s2)\r
677     {\r
678 #if !SQLITE_STANDARD\r
679       byte[] b1;\r
680       byte[] b2;\r
681       System.Text.Encoding converter = null;\r
682 \r
683       switch (enc)\r
684       {\r
685         case CollationEncodingEnum.UTF8:\r
686           converter = System.Text.Encoding.UTF8;\r
687           break;\r
688         case CollationEncodingEnum.UTF16LE:\r
689           converter = System.Text.Encoding.Unicode;\r
690           break;\r
691         case CollationEncodingEnum.UTF16BE:\r
692           converter = System.Text.Encoding.BigEndianUnicode;\r
693           break;\r
694       }\r
695 \r
696       b1 = converter.GetBytes(s1);\r
697       b2 = converter.GetBytes(s2);\r
698 \r
699       return UnsafeNativeMethods.sqlite3_context_collcompare(context, b1, b1.Length, b2, b2.Length);\r
700 #else\r
701       throw new NotImplementedException();\r
702 #endif\r
703     }\r
704 \r
705     internal override int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, char[] c1, char[] c2)\r
706     {\r
707 #if !SQLITE_STANDARD\r
708       byte[] b1;\r
709       byte[] b2;\r
710       System.Text.Encoding converter = null;\r
711 \r
712       switch (enc)\r
713       {\r
714         case CollationEncodingEnum.UTF8:\r
715           converter = System.Text.Encoding.UTF8;\r
716           break;\r
717         case CollationEncodingEnum.UTF16LE:\r
718           converter = System.Text.Encoding.Unicode;\r
719           break;\r
720         case CollationEncodingEnum.UTF16BE:\r
721           converter = System.Text.Encoding.BigEndianUnicode;\r
722           break;\r
723       }\r
724 \r
725       b1 = converter.GetBytes(c1);\r
726       b2 = converter.GetBytes(c2);\r
727 \r
728       return UnsafeNativeMethods.sqlite3_context_collcompare(context, b1, b1.Length, b2, b2.Length);\r
729 #else\r
730       throw new NotImplementedException();\r
731 #endif\r
732     }\r
733 \r
734     internal override CollationSequence GetCollationSequence(SqliteFunction func, IntPtr context)\r
735     {\r
736 #if !SQLITE_STANDARD\r
737       CollationSequence seq = new CollationSequence();\r
738       int len;\r
739       int type;\r
740       int enc;\r
741       IntPtr p = UnsafeNativeMethods.sqlite3_context_collseq(context, out type, out enc, out len);\r
742 \r
743       if (p != null) seq.Name = UTF8ToString(p, len);\r
744       seq.Type = (CollationTypeEnum)type;\r
745       seq._func = func;\r
746       seq.Encoding = (CollationEncodingEnum)enc;\r
747 \r
748       return seq;\r
749 #else\r
750       throw new NotImplementedException();\r
751 #endif\r
752     }\r
753 \r
754     internal override long GetParamValueBytes(IntPtr p, int nDataOffset, byte[] bDest, int nStart, int nLength)\r
755     {\r
756       IntPtr ptr;\r
757       int nlen;\r
758       int nCopied = nLength;\r
759 \r
760       nlen = UnsafeNativeMethods.sqlite3_value_bytes(p);\r
761       ptr = UnsafeNativeMethods.sqlite3_value_blob(p);\r
762 \r
763       if (bDest == null) return nlen;\r
764 \r
765       if (nCopied + nStart > bDest.Length) nCopied = bDest.Length - nStart;\r
766       if (nCopied + nDataOffset > nlen) nCopied = nlen - nDataOffset;\r
767 \r
768       unsafe {\r
769               if (nCopied > 0)\r
770                       Marshal.Copy((IntPtr)((byte*)ptr + nDataOffset), bDest, nStart, nCopied);\r
771               else nCopied = 0;\r
772       }\r
773 \r
774       return nCopied;\r
775     }\r
776 \r
777     internal override double GetParamValueDouble(IntPtr ptr)\r
778     {\r
779       double value;\r
780 #if !PLATFORM_COMPACTFRAMEWORK\r
781       value = UnsafeNativeMethods.sqlite3_value_double(ptr);\r
782 #else\r
783       UnsafeNativeMethods.sqlite3_value_double_interop(ptr, out value);\r
784 #endif\r
785       return value;\r
786     }\r
787 \r
788     internal override int GetParamValueInt32(IntPtr ptr)\r
789     {\r
790       return UnsafeNativeMethods.sqlite3_value_int(ptr);\r
791     }\r
792 \r
793     internal override long GetParamValueInt64(IntPtr ptr)\r
794     {\r
795       Int64 value;\r
796 #if !PLATFORM_COMPACTFRAMEWORK\r
797       value = UnsafeNativeMethods.sqlite3_value_int64(ptr);\r
798 #else\r
799       UnsafeNativeMethods.sqlite3_value_int64_interop(ptr, out value);\r
800 #endif\r
801       return value;\r
802     }\r
803 \r
804     internal override string GetParamValueText(IntPtr ptr)\r
805     {\r
806 #if !SQLITE_STANDARD\r
807       int len;\r
808       return UTF8ToString(UnsafeNativeMethods.sqlite3_value_text_interop(ptr, out len), len);\r
809 #else\r
810       return UTF8ToString(UnsafeNativeMethods.sqlite3_value_text(ptr), -1);\r
811 #endif\r
812     }\r
813 \r
814     internal override TypeAffinity GetParamValueType(IntPtr ptr)\r
815     {\r
816       return UnsafeNativeMethods.sqlite3_value_type(ptr);\r
817     }\r
818 \r
819     internal override void ReturnBlob(IntPtr context, byte[] value)\r
820     {\r
821       UnsafeNativeMethods.sqlite3_result_blob(context, value, value.Length, (IntPtr)(-1));\r
822     }\r
823 \r
824     internal override void ReturnDouble(IntPtr context, double value)\r
825     {\r
826 #if !PLATFORM_COMPACTFRAMEWORK\r
827       UnsafeNativeMethods.sqlite3_result_double(context, value);\r
828 #else\r
829       UnsafeNativeMethods.sqlite3_result_double_interop(context, ref value);\r
830 #endif\r
831     }\r
832 \r
833     internal override void ReturnError(IntPtr context, string value)\r
834     {\r
835       UnsafeNativeMethods.sqlite3_result_error(context, ToUTF8(value), value.Length);\r
836     }\r
837 \r
838     internal override void ReturnInt32(IntPtr context, int value)\r
839     {\r
840       UnsafeNativeMethods.sqlite3_result_int(context, value);\r
841     }\r
842 \r
843     internal override void ReturnInt64(IntPtr context, long value)\r
844     {\r
845 #if !PLATFORM_COMPACTFRAMEWORK\r
846       UnsafeNativeMethods.sqlite3_result_int64(context, value);\r
847 #else\r
848       UnsafeNativeMethods.sqlite3_result_int64_interop(context, ref value);\r
849 #endif\r
850     }\r
851 \r
852     internal override void ReturnNull(IntPtr context)\r
853     {\r
854       UnsafeNativeMethods.sqlite3_result_null(context);\r
855     }\r
856 \r
857     internal override void ReturnText(IntPtr context, string value)\r
858     {\r
859       byte[] b = ToUTF8(value);\r
860       UnsafeNativeMethods.sqlite3_result_text(context, ToUTF8(value), b.Length - 1, (IntPtr)(-1));\r
861     }\r
862 \r
863     internal override IntPtr AggregateContext(IntPtr context)\r
864     {\r
865       return UnsafeNativeMethods.sqlite3_aggregate_context(context, 1);\r
866     }\r
867 \r
868     internal override void SetPassword(byte[] passwordBytes)\r
869     {\r
870       int n = UnsafeNativeMethods.sqlite3_key(_sql, passwordBytes, passwordBytes.Length);\r
871       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
872     }\r
873 \r
874     internal override void ChangePassword(byte[] newPasswordBytes)\r
875     {\r
876       int n = UnsafeNativeMethods.sqlite3_rekey(_sql, newPasswordBytes, (newPasswordBytes == null) ? 0 : newPasswordBytes.Length);\r
877       if (n > 0) throw new SqliteException(n, SQLiteLastError());\r
878     }\r
879                 \r
880 #if MONOTOUCH\r
881     SQLiteUpdateCallback update_callback;\r
882     SQLiteCommitCallback commit_callback;\r
883     SQLiteRollbackCallback rollback_callback;\r
884                 \r
885     [MonoTouch.MonoPInvokeCallback (typeof (SQLiteUpdateCallback))]\r
886     static void update (IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid)\r
887     {\r
888       SQLite3 instance = GCHandle.FromIntPtr (puser).Target as SQLite3;\r
889       instance.update_callback (puser, type, database, table, rowid);\r
890     }\r
891                         \r
892     internal override void SetUpdateHook (SQLiteUpdateCallback func)\r
893     {\r
894       update_callback = func;\r
895       if (func == null)\r
896         UnsafeNativeMethods.sqlite3_update_hook (_sql, null, IntPtr.Zero);\r
897       else\r
898         UnsafeNativeMethods.sqlite3_update_hook (_sql, update, GCHandle.ToIntPtr (gch));\r
899     }\r
900 \r
901     [MonoTouch.MonoPInvokeCallback (typeof (SQLiteCommitCallback))]\r
902     static int commit (IntPtr puser)\r
903     {\r
904       SQLite3 instance = GCHandle.FromIntPtr (puser).Target as SQLite3;\r
905       return instance.commit_callback (puser);\r
906     }\r
907                 \r
908     internal override void SetCommitHook (SQLiteCommitCallback func)\r
909     {\r
910       commit_callback = func;\r
911       if (func == null)\r
912         UnsafeNativeMethods.sqlite3_commit_hook (_sql, null, IntPtr.Zero);\r
913       else\r
914         UnsafeNativeMethods.sqlite3_commit_hook (_sql, commit, GCHandle.ToIntPtr (gch));\r
915     }\r
916 \r
917     [MonoTouch.MonoPInvokeCallback (typeof (SQLiteRollbackCallback))]\r
918     static void rollback (IntPtr puser)\r
919     {\r
920       SQLite3 instance = GCHandle.FromIntPtr (puser).Target as SQLite3;\r
921       instance.rollback_callback (puser);\r
922     }\r
923 \r
924     internal override void SetRollbackHook (SQLiteRollbackCallback func)\r
925     {\r
926       rollback_callback = func;\r
927       if (func == null)\r
928         UnsafeNativeMethods.sqlite3_rollback_hook (_sql, null, IntPtr.Zero);\r
929       else\r
930         UnsafeNativeMethods.sqlite3_rollback_hook (_sql, rollback, GCHandle.ToIntPtr (gch));\r
931     }\r
932 #else\r
933     internal override void SetUpdateHook(SQLiteUpdateCallback func)\r
934     {\r
935       UnsafeNativeMethods.sqlite3_update_hook(_sql, func, IntPtr.Zero);\r
936     }\r
937 \r
938     internal override void SetCommitHook(SQLiteCommitCallback func)\r
939     {\r
940       UnsafeNativeMethods.sqlite3_commit_hook(_sql, func, IntPtr.Zero);\r
941     }\r
942 \r
943     internal override void SetRollbackHook(SQLiteRollbackCallback func)\r
944     {\r
945       UnsafeNativeMethods.sqlite3_rollback_hook(_sql, func, IntPtr.Zero);\r
946     }\r
947 #endif\r
948     /// <summary>\r
949     /// Helper function to retrieve a column of data from an active statement.\r
950     /// </summary>\r
951     /// <param name="stmt">The statement being step()'d through</param>\r
952     /// <param name="index">The column index to retrieve</param>\r
953     /// <param name="typ">The type of data contained in the column.  If Uninitialized, this function will retrieve the datatype information.</param>\r
954     /// <returns>Returns the data in the column</returns>\r
955     internal override object GetValue(SqliteStatement stmt, int index, SQLiteType typ)\r
956     {\r
957       if (IsNull(stmt, index)) return DBNull.Value;\r
958       TypeAffinity aff = typ.Affinity;\r
959       Type t = null;\r
960 \r
961       if (typ.Type != DbType.Object)\r
962       {\r
963         t = SqliteConvert.SQLiteTypeToType(typ);\r
964         aff = TypeToAffinity(t);\r
965       }\r
966 \r
967       switch (aff)\r
968       {\r
969         case TypeAffinity.Blob:\r
970           if (typ.Type == DbType.Guid && typ.Affinity == TypeAffinity.Text)\r
971             return new Guid(GetText(stmt, index));\r
972 \r
973           int n = (int)GetBytes(stmt, index, 0, null, 0, 0);\r
974           byte[] b = new byte[n];\r
975           GetBytes(stmt, index, 0, b, 0, n);\r
976 \r
977           if (typ.Type == DbType.Guid && n == 16)\r
978             return new Guid(b);\r
979 \r
980           return b;\r
981         case TypeAffinity.DateTime:\r
982           return GetDateTime(stmt, index);\r
983         case TypeAffinity.Double:\r
984           if (t == null) return GetDouble(stmt, index);\r
985           else\r
986             return Convert.ChangeType(GetDouble(stmt, index), t, null);\r
987         case TypeAffinity.Int64:\r
988           if (t == null) return GetInt64(stmt, index);\r
989           else\r
990             return Convert.ChangeType(GetInt64(stmt, index), t, null);\r
991         default:\r
992           return GetText(stmt, index);\r
993       }\r
994     }\r
995 \r
996     internal override int GetCursorForTable(SqliteStatement stmt, int db, int rootPage)\r
997     {\r
998 #if !SQLITE_STANDARD\r
999       return UnsafeNativeMethods.sqlite3_table_cursor(stmt._sqlite_stmt, db, rootPage);\r
1000 #else\r
1001       return -1;\r
1002 #endif\r
1003     }\r
1004 \r
1005     internal override long GetRowIdForCursor(SqliteStatement stmt, int cursor)\r
1006     {\r
1007 #if !SQLITE_STANDARD\r
1008       long rowid;\r
1009       int rc = UnsafeNativeMethods.sqlite3_cursor_rowid(stmt._sqlite_stmt, cursor, out rowid);\r
1010       if (rc == 0) return rowid;\r
1011 \r
1012       return 0;\r
1013 #else\r
1014       return 0;\r
1015 #endif\r
1016     }\r
1017 \r
1018     internal override void GetIndexColumnExtendedInfo(string database, string index, string column, out int sortMode, out int onError, out string collationSequence)\r
1019     {\r
1020 #if !SQLITE_STANDARD\r
1021       IntPtr coll;\r
1022       int colllen;\r
1023       int rc;\r
1024 \r
1025       rc = UnsafeNativeMethods.sqlite3_index_column_info_interop(_sql, ToUTF8(database), ToUTF8(index), ToUTF8(column), out sortMode, out onError, out coll, out colllen);\r
1026       if (rc != 0) throw new SqliteException(rc, "");\r
1027 \r
1028       collationSequence = UTF8ToString(coll, colllen);\r
1029 #else\r
1030       sortMode = 0;\r
1031       onError = 2;\r
1032       collationSequence = "BINARY";\r
1033 #endif\r
1034     }\r
1035   }\r
1036 }\r