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