Index: SQLite.Interop/src/win/interop.c
==================================================================
--- SQLite.Interop/src/win/interop.c
+++ SQLite.Interop/src/win/interop.c
@@ -1,487 +1,469 @@
-#include "../core/sqlite3.c"
-#include "../contrib/extension-functions.c"
-#include "crypt.c"
-
-extern int RegisterExtensionFunctions(sqlite3 *db);
-
-#ifdef SQLITE_OS_WIN
-
-// Additional open flags, we use this one privately
-//#define SQLITE_OPEN_SHAREDCACHE 0x01000000
-
-typedef void (*SQLITEUSERFUNC)(sqlite3_context *, int, sqlite3_value **);
-typedef void (*SQLITEFUNCFINAL)(sqlite3_context *);
-
-/*
- The goal of this version of close is different than that of sqlite3_close(), and is designed to lend itself better to .NET's non-deterministic finalizers and
- the GC thread. SQLite will not close a database if statements are open on it -- but for our purposes, we'd rather finalize all active statements
- and forcibly close the database. The reason is simple -- a lot of people don't Dispose() of their objects correctly and let the garbage collector
- do it. This leads to unexpected behavior when a user thinks they've closed a database, but it's still open because not all the statements have
- hit the GC yet.
-
- So, here we have a problem ... .NET has a pointer to any number of sqlite3_stmt objects. We can't call sqlite3_finalize() on these because
- their memory is freed and can be used for something else. The GC thread could potentially try and call finalize again on the statement after
- that memory was deallocated. BAD. So, what we need to do is make a copy of each statement, and call finalize() on the copy -- so that the original
- statement's memory is preserved, and marked as BAD, but we can still manage to finalize everything and forcibly close the database. Later when the
- GC gets around to calling finalize_interop() on the "bad" statement, we detect that and finish deallocating the pointer.
-*/
-__declspec(dllexport) int WINAPI sqlite3_close_interop(sqlite3 *db)
-{
- int ret;
-
- ret = sqlite3_close(db);
-
- if (ret == SQLITE_BUSY && db->pVdbe)
- {
- while (db->pVdbe)
- {
- // Make a copy of the first prepared statement
- Vdbe *p = (Vdbe *)sqlite3_malloc(sizeof(Vdbe));
- Vdbe *po = db->pVdbe;
-
- if (!p)
- {
- ret = SQLITE_NOMEM;
- break;
- }
-
- CopyMemory(p, po, sizeof(Vdbe));
-
- // Put it on the chain so we can free it
- db->pVdbe = p;
- ret = sqlite3_finalize((sqlite3_stmt *)p); // This will also free the copy's memory
- if (ret)
- {
- // finalize failed -- so we must put back anything we munged
- CopyMemory(po, p, sizeof(Vdbe));
- db->pVdbe = po;
- break;
- }
- else
- {
- ZeroMemory(po, sizeof(Vdbe));
- po->magic = VDBE_MAGIC_DEAD;
- }
- }
- ret = sqlite3_close(db);
- }
-
- return ret;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_open_interop(const char*filename, int flags, sqlite3 **ppdb)
-{
- int ret;
- //int sharedcache = ((flags & SQLITE_OPEN_SHAREDCACHE) != 0);
- //flags &= ~SQLITE_OPEN_SHAREDCACHE;
-
- //sqlite3_enable_shared_cache(sharedcache);
- ret = sqlite3_open_v2(filename, ppdb, flags, NULL);
- //sqlite3_enable_shared_cache(0);
-
- if (ret == 0)
- RegisterExtensionFunctions(*ppdb);
-
- return ret;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_open16_interop(const char *filename, int flags, sqlite3 **ppdb)
-{
- int ret = sqlite3_open_interop(filename, flags, ppdb);
- if (!ret)
- {
- if(!DbHasProperty(*ppdb, 0, DB_SchemaLoaded))
- ENC(*ppdb) = SQLITE_UTF16NATIVE;
- }
- return ret;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_extended_result_codes_interop(sqlite3 *db, int onoff)
-{
- int rc = sqlite3_extended_result_codes(db, onoff);
- return rc;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_errcode_interop(sqlite3 *db)
-{
- int rc = sqlite3_errcode(db);
- return rc;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_extended_errcode_interop(sqlite3 *db)
-{
- int rc = sqlite3_extended_errcode(db);
- return rc;
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_errmsg_interop(sqlite3 *db, int *plen)
-{
- const char *pval = sqlite3_errmsg(db);
- *plen = (pval != 0) ? strlen(pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_prepare_interop(sqlite3 *db, const char *sql, int nbytes, sqlite3_stmt **ppstmt, const char **pztail, int *plen)
-{
- int n;
-
- n = sqlite3_prepare(db, sql, nbytes, ppstmt, pztail);
- *plen = (*pztail != 0) ? strlen(*pztail) : 0;
-
- return n;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_prepare16_interop(sqlite3 *db, const void *sql, int nchars, sqlite3_stmt **ppstmt, const void **pztail, int *plen)
-{
- int n;
-
- n = sqlite3_prepare16(db, sql, nchars * sizeof(wchar_t), ppstmt, pztail);
- *plen = (*pztail != 0) ? wcslen((wchar_t *)*pztail) * sizeof(wchar_t) : 0;
-
- return n;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_bind_double_interop(sqlite3_stmt *stmt, int iCol, double *val)
-{
- return sqlite3_bind_double(stmt,iCol,*val);
-}
-
-__declspec(dllexport) int WINAPI sqlite3_bind_int64_interop(sqlite3_stmt *stmt, int iCol, sqlite_int64 *val)
-{
- return sqlite3_bind_int64(stmt,iCol,*val);
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_bind_parameter_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const char *pval = sqlite3_bind_parameter_name(stmt, iCol);
- *plen = (pval != 0) ? strlen(pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_column_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const char *pval = sqlite3_column_name(stmt, iCol);
- *plen = (pval != 0) ? strlen(pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const void * WINAPI sqlite3_column_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const void *pval = sqlite3_column_name16(stmt, iCol);
- *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
- return pval;
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_column_decltype_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const char *pval = sqlite3_column_decltype(stmt, iCol);
- *plen = (pval != 0) ? strlen(pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const void * WINAPI sqlite3_column_decltype16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const void *pval = sqlite3_column_decltype16(stmt, iCol);
- *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
- return pval;
-}
-
-__declspec(dllexport) void WINAPI sqlite3_column_double_interop(sqlite3_stmt *stmt, int iCol, double *val)
-{
- *val = sqlite3_column_double(stmt,iCol);
-}
-
-__declspec(dllexport) void WINAPI sqlite3_column_int64_interop(sqlite3_stmt *stmt, int iCol, sqlite_int64 *val)
-{
- *val = sqlite3_column_int64(stmt,iCol);
-}
-
-__declspec(dllexport) const unsigned char * WINAPI sqlite3_column_text_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const unsigned char *pval = sqlite3_column_text(stmt, iCol);
- *plen = (pval != 0) ? strlen((char *)pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const void * WINAPI sqlite3_column_text16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const void *pval = sqlite3_column_text16(stmt, iCol);
- *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t): 0;
- return pval;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_finalize_interop(sqlite3_stmt *stmt)
-{
- Vdbe *p;
- sqlite3 *db;
- int ret;
-
- p = (Vdbe *)stmt;
- if (p && p->magic == VDBE_MAGIC_DEAD)
- {
- db = p->db;
- if (db == NULL)
- {
- sqlite3_free(p);
- ret = SQLITE_OK;
- }
- }
- else
- ret = sqlite3_finalize(stmt);
-
- return ret;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_reset_interop(sqlite3_stmt *stmt)
-{
- int ret;
-
- if (((Vdbe *)stmt)->magic == VDBE_MAGIC_DEAD) return SQLITE_SCHEMA;
- ret = sqlite3_reset(stmt);
- return ret;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_create_function_interop(sqlite3 *psql, const char *zFunctionName, int nArg, int eTextRep, void *pvUser, SQLITEUSERFUNC func, SQLITEUSERFUNC funcstep, SQLITEFUNCFINAL funcfinal, int needCollSeq)
-{
- int n;
-
- if (eTextRep == SQLITE_UTF16)
- eTextRep = SQLITE_UTF16NATIVE;
-
- n = sqlite3_create_function(psql, zFunctionName, nArg, eTextRep, 0, func, funcstep, funcfinal);
- if (n == 0)
- {
- if (needCollSeq)
- {
- FuncDef *pFunc = sqlite3FindFunction(psql, zFunctionName, strlen(zFunctionName), nArg, eTextRep, 0);
- if( pFunc )
- {
- pFunc->flags |= SQLITE_FUNC_NEEDCOLL;
- }
- }
- }
-
- return n;
-}
-
-__declspec(dllexport) void WINAPI sqlite3_value_double_interop(sqlite3_value *pval, double *val)
-{
- *val = sqlite3_value_double(pval);
-}
-
-__declspec(dllexport) void WINAPI sqlite3_value_int64_interop(sqlite3_value *pval, sqlite_int64 *val)
-{
- *val = sqlite3_value_int64(pval);
-}
-
-__declspec(dllexport) const unsigned char * WINAPI sqlite3_value_text_interop(sqlite3_value *val, int *plen)
-{
- const unsigned char *pval = sqlite3_value_text(val);
- *plen = (pval != 0) ? strlen((char *)pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const void * WINAPI sqlite3_value_text16_interop(sqlite3_value *val, int *plen)
-{
- const void *pval = sqlite3_value_text16(val);
- *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
- return pval;
-}
-
-__declspec(dllexport) void WINAPI sqlite3_result_double_interop(sqlite3_context *pctx, double *val)
-{
- sqlite3_result_double(pctx, *val);
-}
-
-__declspec(dllexport) void WINAPI sqlite3_result_int64_interop(sqlite3_context *pctx, sqlite_int64 *val)
-{
- sqlite3_result_int64(pctx, *val);
-}
-
-__declspec(dllexport) int WINAPI sqlite3_context_collcompare(sqlite3_context *ctx, const void *p1, int p1len, const void *p2, int p2len)
-{
- if ((ctx->pFunc->flags & SQLITE_FUNC_NEEDCOLL) == 0) return 2;
- return ctx->pColl->xCmp(ctx->pColl->pUser, p1len, p1, p2len, p2);
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_context_collseq(sqlite3_context *ctx, int *ptype, int *enc, int *plen)
-{
- CollSeq *pColl = ctx->pColl;
- *ptype = 0;
- *plen = 0;
- *enc = 0;
-
- if ((ctx->pFunc->flags & SQLITE_FUNC_NEEDCOLL) == 0) return NULL;
-
- if (pColl)
- {
- *enc = pColl->enc;
- *ptype = pColl->type;
- *plen = (pColl->zName != 0) ? strlen(pColl->zName) : 0;
-
- return pColl->zName;
- }
- return NULL;
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_column_database_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const char *pval = sqlite3_column_database_name(stmt, iCol);
- *plen = (pval != 0) ? strlen(pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const void * WINAPI sqlite3_column_database_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const void *pval = sqlite3_column_database_name16(stmt, iCol);
- *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
- return pval;
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_column_table_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const char *pval = sqlite3_column_table_name(stmt, iCol);
- *plen = (pval != 0) ? strlen(pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const void * WINAPI sqlite3_column_table_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const void *pval = sqlite3_column_table_name16(stmt, iCol);
- *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
- return pval;
-}
-
-__declspec(dllexport) const char * WINAPI sqlite3_column_origin_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const char *pval = sqlite3_column_origin_name(stmt, iCol);
- *plen = (pval != 0) ? strlen(pval) : 0;
- return pval;
-}
-
-__declspec(dllexport) const void * WINAPI sqlite3_column_origin_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
-{
- const void *pval = sqlite3_column_origin_name16(stmt, iCol);
- *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
- return pval;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_table_column_metadata_interop(sqlite3 *db, const char *zDbName, const char *zTableName, const char *zColumnName, char **pzDataType, char **pzCollSeq, int *pNotNull, int *pPrimaryKey, int *pAutoinc, int *pdtLen, int *pcsLen)
-{
- int n;
-
- n = sqlite3_table_column_metadata(db, zDbName, zTableName, zColumnName, pzDataType, pzCollSeq, pNotNull, pPrimaryKey, pAutoinc);
- *pdtLen = (*pzDataType != 0) ? strlen(*pzDataType) : 0;
- *pcsLen = (*pzCollSeq != 0) ? strlen(*pzCollSeq) : 0;
-
- return n;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_index_column_info_interop(sqlite3 *db, const char *zDb, const char *zIndexName, const char *zColumnName, int *sortOrder, int *onError, char **pzColl, int *plen)
-{
- Index *pIdx;
- Table *pTab;
- int n;
- pIdx = sqlite3FindIndex(db, zIndexName, zDb);
- if (!pIdx) return SQLITE_ERROR;
-
- pTab = pIdx->pTable;
- for (n = 0; n < pIdx->nColumn; n++)
- {
- int cnum = pIdx->aiColumn[n];
- if (sqlite3StrICmp(pTab->aCol[cnum].zName, zColumnName) == 0)
- {
- *sortOrder = pIdx->aSortOrder[n];
- *pzColl = pIdx->azColl[n];
- *plen = strlen(*pzColl);
- *onError = pIdx->onError;
-
- return SQLITE_OK;
- }
- }
- return SQLITE_ERROR;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_table_cursor(sqlite3_stmt *pstmt, int iDb, Pgno tableRootPage)
-{
- Vdbe *p = (Vdbe *)pstmt;
- sqlite3 *db = (p == NULL) ? NULL : p->db;
- int n;
- int ret = -1;
-
- sqlite3_mutex_enter(db->mutex);
- for (n = 0; n < p->nCursor && p->apCsr[n] != NULL; n++)
- {
- if (p->apCsr[n]->isTable == FALSE) continue;
- if (p->apCsr[n]->iDb != iDb) continue;
- if (p->apCsr[n]->pCursor->pgnoRoot == tableRootPage)
- {
- ret = n;
- break;
- }
- }
- sqlite3_mutex_leave(db->mutex);
-
- return ret;
-}
-
-__declspec(dllexport) int WINAPI sqlite3_cursor_rowid(sqlite3_stmt *pstmt, int cursor, sqlite_int64 *prowid)
-{
- Vdbe *p = (Vdbe *)pstmt;
- sqlite3 *db = (p == NULL) ? NULL : p->db;
- VdbeCursor *pC;
- int ret = 0;
-
- sqlite3_mutex_enter(db->mutex);
- while (1)
- {
- if (cursor < 0 || cursor >= p->nCursor)
- {
- ret = SQLITE_ERROR;
- break;
- }
- if (p->apCsr[cursor] == NULL)
- {
- ret = SQLITE_ERROR;
- break;
- }
-
- pC = p->apCsr[cursor];
-
- ret = sqlite3VdbeCursorMoveto(pC);
- if(ret)
- break;
-
- if(pC->rowidIsValid)
- {
- *prowid = pC->lastRowid;
- }
- else if(pC->pseudoTableReg > 0)
- {
- ret = SQLITE_ERROR;
- break;
- }
- else if(pC->nullRow || pC->pCursor==0)
- {
- ret = SQLITE_ERROR;
- break;
- }
- else
- {
- if (pC->pCursor == NULL)
- {
- ret = SQLITE_ERROR;
- break;
- }
- sqlite3BtreeKeySize(pC->pCursor, prowid);
- *prowid = *prowid;
- }
- break;
- }
- sqlite3_mutex_leave(db->mutex);
-
- return ret;
-}
-
-#endif // SQLITE_OS_WIN
-
+#include "../core/sqlite3.c"
+#include "../contrib/extension-functions.c"
+#include "crypt.c"
+
+extern int RegisterExtensionFunctions(sqlite3 *db);
+
+#ifdef SQLITE_OS_WIN
+
+// Additional open flags, we use this one privately
+//#define SQLITE_OPEN_SHAREDCACHE 0x01000000
+
+typedef void (*SQLITEUSERFUNC)(sqlite3_context *, int, sqlite3_value **);
+typedef void (*SQLITEFUNCFINAL)(sqlite3_context *);
+
+/*
+ The goal of this version of close is different than that of sqlite3_close(), and is designed to lend itself better to .NET's non-deterministic finalizers and
+ the GC thread. SQLite will not close a database if statements are open on it -- but for our purposes, we'd rather finalize all active statements
+ and forcibly close the database. The reason is simple -- a lot of people don't Dispose() of their objects correctly and let the garbage collector
+ do it. This leads to unexpected behavior when a user thinks they've closed a database, but it's still open because not all the statements have
+ hit the GC yet.
+
+ So, here we have a problem ... .NET has a pointer to any number of sqlite3_stmt objects. We can't call sqlite3_finalize() on these because
+ their memory is freed and can be used for something else. The GC thread could potentially try and call finalize again on the statement after
+ that memory was deallocated. BAD. So, what we need to do is make a copy of each statement, and call finalize() on the copy -- so that the original
+ statement's memory is preserved, and marked as BAD, but we can still manage to finalize everything and forcibly close the database. Later when the
+ GC gets around to calling finalize_interop() on the "bad" statement, we detect that and finish deallocating the pointer.
+*/
+__declspec(dllexport) int WINAPI sqlite3_close_interop(sqlite3 *db)
+{
+ int ret;
+
+ ret = sqlite3_close(db);
+
+ if (ret == SQLITE_BUSY && db->pVdbe)
+ {
+ while (db->pVdbe)
+ {
+ // Make a copy of the first prepared statement
+ Vdbe *p = (Vdbe *)sqlite3_malloc(sizeof(Vdbe));
+ Vdbe *po = db->pVdbe;
+
+ if (!p)
+ {
+ ret = SQLITE_NOMEM;
+ break;
+ }
+
+ CopyMemory(p, po, sizeof(Vdbe));
+
+ // Put it on the chain so we can free it
+ db->pVdbe = p;
+ ret = sqlite3_finalize((sqlite3_stmt *)p); // This will also free the copy's memory
+ if (ret)
+ {
+ // finalize failed -- so we must put back anything we munged
+ CopyMemory(po, p, sizeof(Vdbe));
+ db->pVdbe = po;
+ break;
+ }
+ else
+ {
+ ZeroMemory(po, sizeof(Vdbe));
+ po->magic = VDBE_MAGIC_DEAD;
+ }
+ }
+ ret = sqlite3_close(db);
+ }
+
+ return ret;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_open_interop(const char*filename, int flags, sqlite3 **ppdb)
+{
+ int ret;
+ //int sharedcache = ((flags & SQLITE_OPEN_SHAREDCACHE) != 0);
+ //flags &= ~SQLITE_OPEN_SHAREDCACHE;
+
+ //sqlite3_enable_shared_cache(sharedcache);
+ ret = sqlite3_open_v2(filename, ppdb, flags, NULL);
+ //sqlite3_enable_shared_cache(0);
+
+ if (ret == 0)
+ RegisterExtensionFunctions(*ppdb);
+
+ return ret;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_open16_interop(const char *filename, int flags, sqlite3 **ppdb)
+{
+ int ret = sqlite3_open_interop(filename, flags, ppdb);
+ if (!ret)
+ {
+ if(!DbHasProperty(*ppdb, 0, DB_SchemaLoaded))
+ ENC(*ppdb) = SQLITE_UTF16NATIVE;
+ }
+ return ret;
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_errmsg_interop(sqlite3 *db, int *plen)
+{
+ const char *pval = sqlite3_errmsg(db);
+ *plen = (pval != 0) ? strlen(pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_prepare_interop(sqlite3 *db, const char *sql, int nbytes, sqlite3_stmt **ppstmt, const char **pztail, int *plen)
+{
+ int n;
+
+ n = sqlite3_prepare(db, sql, nbytes, ppstmt, pztail);
+ *plen = (*pztail != 0) ? strlen(*pztail) : 0;
+
+ return n;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_prepare16_interop(sqlite3 *db, const void *sql, int nchars, sqlite3_stmt **ppstmt, const void **pztail, int *plen)
+{
+ int n;
+
+ n = sqlite3_prepare16(db, sql, nchars * sizeof(wchar_t), ppstmt, pztail);
+ *plen = (*pztail != 0) ? wcslen((wchar_t *)*pztail) * sizeof(wchar_t) : 0;
+
+ return n;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_bind_double_interop(sqlite3_stmt *stmt, int iCol, double *val)
+{
+ return sqlite3_bind_double(stmt,iCol,*val);
+}
+
+__declspec(dllexport) int WINAPI sqlite3_bind_int64_interop(sqlite3_stmt *stmt, int iCol, sqlite_int64 *val)
+{
+ return sqlite3_bind_int64(stmt,iCol,*val);
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_bind_parameter_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const char *pval = sqlite3_bind_parameter_name(stmt, iCol);
+ *plen = (pval != 0) ? strlen(pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_column_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const char *pval = sqlite3_column_name(stmt, iCol);
+ *plen = (pval != 0) ? strlen(pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const void * WINAPI sqlite3_column_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const void *pval = sqlite3_column_name16(stmt, iCol);
+ *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_column_decltype_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const char *pval = sqlite3_column_decltype(stmt, iCol);
+ *plen = (pval != 0) ? strlen(pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const void * WINAPI sqlite3_column_decltype16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const void *pval = sqlite3_column_decltype16(stmt, iCol);
+ *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
+ return pval;
+}
+
+__declspec(dllexport) void WINAPI sqlite3_column_double_interop(sqlite3_stmt *stmt, int iCol, double *val)
+{
+ *val = sqlite3_column_double(stmt,iCol);
+}
+
+__declspec(dllexport) void WINAPI sqlite3_column_int64_interop(sqlite3_stmt *stmt, int iCol, sqlite_int64 *val)
+{
+ *val = sqlite3_column_int64(stmt,iCol);
+}
+
+__declspec(dllexport) const unsigned char * WINAPI sqlite3_column_text_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const unsigned char *pval = sqlite3_column_text(stmt, iCol);
+ *plen = (pval != 0) ? strlen((char *)pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const void * WINAPI sqlite3_column_text16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const void *pval = sqlite3_column_text16(stmt, iCol);
+ *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t): 0;
+ return pval;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_finalize_interop(sqlite3_stmt *stmt)
+{
+ Vdbe *p;
+ sqlite3 *db;
+ int ret;
+
+ p = (Vdbe *)stmt;
+ if (p && p->magic == VDBE_MAGIC_DEAD)
+ {
+ db = p->db;
+ if (db == NULL)
+ {
+ sqlite3_free(p);
+ ret = SQLITE_OK;
+ }
+ }
+ else
+ ret = sqlite3_finalize(stmt);
+
+ return ret;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_reset_interop(sqlite3_stmt *stmt)
+{
+ int ret;
+
+ if (((Vdbe *)stmt)->magic == VDBE_MAGIC_DEAD) return SQLITE_SCHEMA;
+ ret = sqlite3_reset(stmt);
+ return ret;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_create_function_interop(sqlite3 *psql, const char *zFunctionName, int nArg, int eTextRep, void *pvUser, SQLITEUSERFUNC func, SQLITEUSERFUNC funcstep, SQLITEFUNCFINAL funcfinal, int needCollSeq)
+{
+ int n;
+
+ if (eTextRep == SQLITE_UTF16)
+ eTextRep = SQLITE_UTF16NATIVE;
+
+ n = sqlite3_create_function(psql, zFunctionName, nArg, eTextRep, 0, func, funcstep, funcfinal);
+ if (n == 0)
+ {
+ if (needCollSeq)
+ {
+ FuncDef *pFunc = sqlite3FindFunction(psql, zFunctionName, strlen(zFunctionName), nArg, eTextRep, 0);
+ if( pFunc )
+ {
+ pFunc->flags |= SQLITE_FUNC_NEEDCOLL;
+ }
+ }
+ }
+
+ return n;
+}
+
+__declspec(dllexport) void WINAPI sqlite3_value_double_interop(sqlite3_value *pval, double *val)
+{
+ *val = sqlite3_value_double(pval);
+}
+
+__declspec(dllexport) void WINAPI sqlite3_value_int64_interop(sqlite3_value *pval, sqlite_int64 *val)
+{
+ *val = sqlite3_value_int64(pval);
+}
+
+__declspec(dllexport) const unsigned char * WINAPI sqlite3_value_text_interop(sqlite3_value *val, int *plen)
+{
+ const unsigned char *pval = sqlite3_value_text(val);
+ *plen = (pval != 0) ? strlen((char *)pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const void * WINAPI sqlite3_value_text16_interop(sqlite3_value *val, int *plen)
+{
+ const void *pval = sqlite3_value_text16(val);
+ *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
+ return pval;
+}
+
+__declspec(dllexport) void WINAPI sqlite3_result_double_interop(sqlite3_context *pctx, double *val)
+{
+ sqlite3_result_double(pctx, *val);
+}
+
+__declspec(dllexport) void WINAPI sqlite3_result_int64_interop(sqlite3_context *pctx, sqlite_int64 *val)
+{
+ sqlite3_result_int64(pctx, *val);
+}
+
+__declspec(dllexport) int WINAPI sqlite3_context_collcompare(sqlite3_context *ctx, const void *p1, int p1len, const void *p2, int p2len)
+{
+ if ((ctx->pFunc->flags & SQLITE_FUNC_NEEDCOLL) == 0) return 2;
+ return ctx->pColl->xCmp(ctx->pColl->pUser, p1len, p1, p2len, p2);
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_context_collseq(sqlite3_context *ctx, int *ptype, int *enc, int *plen)
+{
+ CollSeq *pColl = ctx->pColl;
+ *ptype = 0;
+ *plen = 0;
+ *enc = 0;
+
+ if ((ctx->pFunc->flags & SQLITE_FUNC_NEEDCOLL) == 0) return NULL;
+
+ if (pColl)
+ {
+ *enc = pColl->enc;
+ *ptype = pColl->type;
+ *plen = (pColl->zName != 0) ? strlen(pColl->zName) : 0;
+
+ return pColl->zName;
+ }
+ return NULL;
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_column_database_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const char *pval = sqlite3_column_database_name(stmt, iCol);
+ *plen = (pval != 0) ? strlen(pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const void * WINAPI sqlite3_column_database_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const void *pval = sqlite3_column_database_name16(stmt, iCol);
+ *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_column_table_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const char *pval = sqlite3_column_table_name(stmt, iCol);
+ *plen = (pval != 0) ? strlen(pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const void * WINAPI sqlite3_column_table_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const void *pval = sqlite3_column_table_name16(stmt, iCol);
+ *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const char * WINAPI sqlite3_column_origin_name_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const char *pval = sqlite3_column_origin_name(stmt, iCol);
+ *plen = (pval != 0) ? strlen(pval) : 0;
+ return pval;
+}
+
+__declspec(dllexport) const void * WINAPI sqlite3_column_origin_name16_interop(sqlite3_stmt *stmt, int iCol, int *plen)
+{
+ const void *pval = sqlite3_column_origin_name16(stmt, iCol);
+ *plen = (pval != 0) ? wcslen((wchar_t *)pval) * sizeof(wchar_t) : 0;
+ return pval;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_table_column_metadata_interop(sqlite3 *db, const char *zDbName, const char *zTableName, const char *zColumnName, char **pzDataType, char **pzCollSeq, int *pNotNull, int *pPrimaryKey, int *pAutoinc, int *pdtLen, int *pcsLen)
+{
+ int n;
+
+ n = sqlite3_table_column_metadata(db, zDbName, zTableName, zColumnName, pzDataType, pzCollSeq, pNotNull, pPrimaryKey, pAutoinc);
+ *pdtLen = (*pzDataType != 0) ? strlen(*pzDataType) : 0;
+ *pcsLen = (*pzCollSeq != 0) ? strlen(*pzCollSeq) : 0;
+
+ return n;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_index_column_info_interop(sqlite3 *db, const char *zDb, const char *zIndexName, const char *zColumnName, int *sortOrder, int *onError, char **pzColl, int *plen)
+{
+ Index *pIdx;
+ Table *pTab;
+ int n;
+ pIdx = sqlite3FindIndex(db, zIndexName, zDb);
+ if (!pIdx) return SQLITE_ERROR;
+
+ pTab = pIdx->pTable;
+ for (n = 0; n < pIdx->nColumn; n++)
+ {
+ int cnum = pIdx->aiColumn[n];
+ if (sqlite3StrICmp(pTab->aCol[cnum].zName, zColumnName) == 0)
+ {
+ *sortOrder = pIdx->aSortOrder[n];
+ *pzColl = pIdx->azColl[n];
+ *plen = strlen(*pzColl);
+ *onError = pIdx->onError;
+
+ return SQLITE_OK;
+ }
+ }
+ return SQLITE_ERROR;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_table_cursor(sqlite3_stmt *pstmt, int iDb, Pgno tableRootPage)
+{
+ Vdbe *p = (Vdbe *)pstmt;
+ sqlite3 *db = (p == NULL) ? NULL : p->db;
+ int n;
+ int ret = -1;
+
+ sqlite3_mutex_enter(db->mutex);
+ for (n = 0; n < p->nCursor && p->apCsr[n] != NULL; n++)
+ {
+ if (p->apCsr[n]->isTable == FALSE) continue;
+ if (p->apCsr[n]->iDb != iDb) continue;
+ if (p->apCsr[n]->pCursor->pgnoRoot == tableRootPage)
+ {
+ ret = n;
+ break;
+ }
+ }
+ sqlite3_mutex_leave(db->mutex);
+
+ return ret;
+}
+
+__declspec(dllexport) int WINAPI sqlite3_cursor_rowid(sqlite3_stmt *pstmt, int cursor, sqlite_int64 *prowid)
+{
+ Vdbe *p = (Vdbe *)pstmt;
+ sqlite3 *db = (p == NULL) ? NULL : p->db;
+ VdbeCursor *pC;
+ int ret = 0;
+
+ sqlite3_mutex_enter(db->mutex);
+ while (1)
+ {
+ if (cursor < 0 || cursor >= p->nCursor)
+ {
+ ret = SQLITE_ERROR;
+ break;
+ }
+ if (p->apCsr[cursor] == NULL)
+ {
+ ret = SQLITE_ERROR;
+ break;
+ }
+
+ pC = p->apCsr[cursor];
+
+ ret = sqlite3VdbeCursorMoveto(pC);
+ if(ret)
+ break;
+
+ if(pC->rowidIsValid)
+ {
+ *prowid = pC->lastRowid;
+ }
+ else if(pC->pseudoTableReg > 0)
+ {
+ ret = SQLITE_ERROR;
+ break;
+ }
+ else if(pC->nullRow || pC->pCursor==0)
+ {
+ ret = SQLITE_ERROR;
+ break;
+ }
+ else
+ {
+ if (pC->pCursor == NULL)
+ {
+ ret = SQLITE_ERROR;
+ break;
+ }
+ sqlite3BtreeKeySize(pC->pCursor, prowid);
+ *prowid = *prowid;
+ }
+ break;
+ }
+ sqlite3_mutex_leave(db->mutex);
+
+ return ret;
+}
+
+#endif // SQLITE_OS_WIN
+
Index: System.Data.SQLite/SQLite3.cs
==================================================================
--- System.Data.SQLite/SQLite3.cs
+++ System.Data.SQLite/SQLite3.cs
@@ -98,10 +98,21 @@
get
{
return UnsafeNativeMethods.sqlite3_changes(_sql);
}
}
+
+ ///
+ /// Shutdown the SQLite engine so that it can be restarted with different config options.
+ /// We depend on auto initialization to recover.
+ ///
+ /// Returns a result code
+ internal override int Shutdown()
+ {
+ int rc = UnsafeNativeMethods.sqlite3_shutdown();
+ return rc;
+ }
internal override void Open(string strFilename, SQLiteOpenFlagsEnum flags, int maxPoolSize, bool usePool)
{
if (_sql != null) return;
@@ -851,33 +862,21 @@
}
/// Enables or disabled extended result codes returned by SQLite
internal override void SetExtendedResultCodes(bool bOnOff)
{
-#if !SQLITE_STANDARD
- UnsafeNativeMethods.sqlite3_extended_result_codes_interop(_sql, (bOnOff ? -1 : 0));
-#else
UnsafeNativeMethods.sqlite3_extended_result_codes(_sql, (bOnOff ? -1 : 0));
-#endif
}
/// Gets the last SQLite error code
internal override int ResultCode()
{
-#if !SQLITE_STANDARD
- return UnsafeNativeMethods.sqlite3_errcode_interop(_sql);
-#else
return UnsafeNativeMethods.sqlite3_errcode(_sql);
-#endif
}
/// Gets the last SQLite extended error code
internal override int ExtendedResultCode()
{
-#if !SQLITE_STANDARD
- return UnsafeNativeMethods.sqlite3_extended_errcode_interop(_sql);
-#else
return UnsafeNativeMethods.sqlite3_extended_errcode(_sql);
-#endif
}
internal override void SetPassword(byte[] passwordBytes)
{
int n = UnsafeNativeMethods.sqlite3_key(_sql, passwordBytes, passwordBytes.Length);
@@ -907,10 +906,23 @@
internal override void SetRollbackHook(SQLiteRollbackCallback func)
{
UnsafeNativeMethods.sqlite3_rollback_hook(_sql, func, IntPtr.Zero);
}
+
+ ///
+ /// Allows the setting of a logging callback invoked by SQLite when a
+ /// log event occurs. Only one callback may be set. If NULL is passed,
+ /// the logging callback is unregistered.
+ ///
+ /// The callback function to invoke.
+ /// Returns a result code
+ internal override int SetLogCallback(SQLiteLogCallback func)
+ {
+ int rc = UnsafeNativeMethods.sqlite3_config((int)SQLiteConfigOpsEnum.SQLITE_CONFIG_LOG, func, (IntPtr)0);
+ return rc;
+ }
///
/// Helper function to retrieve a column of data from an active statement.
///
/// The statement being step()'d through
Index: System.Data.SQLite/SQLiteBase.cs
==================================================================
--- System.Data.SQLite/SQLiteBase.cs
+++ System.Data.SQLite/SQLiteBase.cs
@@ -30,10 +30,15 @@
///
/// Returns the number of changes the last executing insert/update caused.
///
internal abstract int Changes { get; }
///
+ /// Shutdown the SQLite engine so that it can be restarted with different config options.
+ /// We depend on auto initialization to recover.
+ ///
+ internal abstract int Shutdown();
+ ///
/// Opens a database.
///
///
/// Implementers should call SQLiteFunction.BindFunctions() and save the array after opening a connection
/// to bind all attributed user-defined functions and collating sequences to the new connection.
@@ -174,10 +179,11 @@
internal abstract void SetUpdateHook(SQLiteUpdateCallback func);
internal abstract void SetCommitHook(SQLiteCommitCallback func);
internal abstract void SetTraceCallback(SQLiteTraceCallback func);
internal abstract void SetRollbackHook(SQLiteRollbackCallback func);
+ internal abstract int SetLogCallback(SQLiteLogCallback func);
internal abstract int GetCursorForTable(SQLiteStatement stmt, int database, int rootPage);
internal abstract long GetRowIdForCursor(SQLiteStatement stmt, int cursor);
internal abstract object GetValue(SQLiteStatement stmt, int index, SQLiteType typ);
@@ -286,6 +292,28 @@
ReadWrite = 0x02,
Create = 0x04,
SharedCache = 0x01000000,
Default = 0x06,
}
+
+ // These are the options to the internal sqlite3_config call.
+ internal enum SQLiteConfigOpsEnum
+ {
+ SQLITE_CONFIG_SINGLETHREAD = 1, // nil
+ SQLITE_CONFIG_MULTITHREAD = 2, // nil
+ SQLITE_CONFIG_SERIALIZED = 3, // nil
+ SQLITE_CONFIG_MALLOC = 4, // sqlite3_mem_methods*
+ SQLITE_CONFIG_GETMALLOC = 5, // sqlite3_mem_methods*
+ SQLITE_CONFIG_SCRATCH = 6, // void*, int sz, int N
+ SQLITE_CONFIG_PAGECACHE = 7, // void*, int sz, int N
+ SQLITE_CONFIG_HEAP = 8, // void*, int nByte, int min
+ SQLITE_CONFIG_MEMSTATUS = 9, // boolean
+ SQLITE_CONFIG_MUTEX = 10, // sqlite3_mutex_methods*
+ SQLITE_CONFIG_GETMUTEX = 11, // sqlite3_mutex_methods*
+ // previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused
+ SQLITE_CONFIG_LOOKASIDE = 13, // int int
+ SQLITE_CONFIG_PCACHE = 14, // sqlite3_pcache_methods*
+ SQLITE_CONFIG_GETPCACHE = 15, // sqlite3_pcache_methods*
+ SQLITE_CONFIG_LOG = 16, // xFunc, void*
+ }
+
}
Index: System.Data.SQLite/SQLiteConnection.cs
==================================================================
--- System.Data.SQLite/SQLiteConnection.cs
+++ System.Data.SQLite/SQLiteConnection.cs
@@ -205,15 +205,17 @@
private event SQLiteUpdateEventHandler _updateHandler;
private event SQLiteCommitHandler _commitHandler;
private event SQLiteTraceEventHandler _traceHandler;
private event EventHandler _rollbackHandler;
+ private event SQLiteLogEventHandler _logHandler;
private SQLiteUpdateCallback _updateCallback;
private SQLiteCommitCallback _commitCallback;
private SQLiteTraceCallback _traceCallback;
private SQLiteRollbackCallback _rollbackCallback;
+ private SQLiteLogCallback _logCallback;
///
/// This event is raised whenever the database is opened or closed.
///
public override event StateChangeEventHandler StateChange;
@@ -442,10 +444,11 @@
cnn._connectionState = _connectionState;
cnn._version = _version;
cnn._enlistment._transaction._cnn = cnn;
cnn._enlistment._disposeConnection = true;
+
_sql = null;
_enlistment = null;
}
#endif
if (_sql != null)
@@ -787,28 +790,40 @@
fileName = ExpandFileName(fileName);
}
try
{
bool usePooling = (SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", Boolean.FalseString)) == true);
- bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true);
int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", "100"), CultureInfo.InvariantCulture);
_defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", "30"), CultureInfo.CurrentCulture);
_defaultIsolation = (IsolationLevel)Enum.Parse(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", "Serializable"), true);
if (_defaultIsolation != IsolationLevel.Serializable && _defaultIsolation != IsolationLevel.ReadCommitted)
throw new NotSupportedException("Invalid Default IsolationLevel specified");
- SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat", "ISO8601"), true);
//string temp = FindKey(opts, "DateTimeFormat", "ISO8601");
//if (String.Compare(temp, "ticks", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.Ticks;
//else if (String.Compare(temp, "julianday", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.JulianDay;
- if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16()
- _sql = new SQLite3_UTF16(dateFormat);
- else
- _sql = new SQLite3(dateFormat);
+ if (_sql == null)
+ {
+ bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true);
+ SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats),
+ FindKey(opts, "DateTimeFormat", "ISO8601"),
+ true);
+
+ if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16()
+ _sql = new SQLite3_UTF16(dateFormat);
+ else
+ _sql = new SQLite3(dateFormat);
+
+ if (_sql != null && _logHandler != null)
+ {
+ if (_logCallback == null) _logCallback = new SQLiteLogCallback(LogCallback);
+ if (_logCallback != null) _sql.SetLogCallback(_logCallback);
+ }
+ }
SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None;
if (SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", Boolean.FalseString)) == false)
flags |= SQLiteOpenFlagsEnum.Create;
@@ -973,10 +988,32 @@
get
{
return _connectionState;
}
}
+
+ /// Passes a shutdown request off to SQLite.
+ public int Shutdown()
+ {
+ // make sure we have an instance of the base class
+ if (_sql == null)
+ {
+ SortedList opts = ParseConnectionString(_connectionString);
+
+ bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true);
+ SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats),
+ FindKey(opts, "DateTimeFormat", "ISO8601"),
+ true);
+
+ if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16()
+ _sql = new SQLite3_UTF16(dateFormat);
+ else
+ _sql = new SQLite3(dateFormat);
+ }
+ if (_sql != null) return _sql.Shutdown();
+ throw new InvalidOperationException("Database connection not active.");
+ }
/// Enables or disabled extended result codes returned by SQLite
public void SetExtendedResultCodes(bool bOnOff)
{
if (_sql != null) _sql.SetExtendedResultCodes(bOnOff);
@@ -2305,10 +2342,42 @@
private void RollbackCallback(IntPtr parg)
{
_rollbackHandler(this, EventArgs.Empty);
}
+
+ ///
+ /// This event is raised whenever SQLite raises a logging event.
+ ///
+ public event SQLiteLogEventHandler Log
+ {
+ add
+ {
+ _logHandler += value;
+ // callback handler will be set/removed at open/close
+ }
+ remove
+ {
+ _logHandler -= value;
+ if (_logHandler==null)
+ {
+ _sql.SetLogCallback(null);
+ _logCallback = null;
+ }
+
+ }
+ }
+
+ private void LogCallback(IntPtr puser, int err_code, IntPtr message)
+ {
+ if (_logHandler != null)
+ _logHandler(this,
+ new LogEventArgs(puser,
+ err_code,
+ SQLiteBase.UTF8ToString(message, -1)));
+ }
+
}
///
/// The I/O file cache flushing behavior for the connection
///
@@ -2330,23 +2399,31 @@
#if !PLATFORM_COMPACTFRAMEWORK
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
internal delegate void SQLiteUpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid);
+
#if !PLATFORM_COMPACTFRAMEWORK
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
internal delegate int SQLiteCommitCallback(IntPtr puser);
+
#if !PLATFORM_COMPACTFRAMEWORK
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
internal delegate void SQLiteTraceCallback(IntPtr puser, IntPtr statement);
+
#if !PLATFORM_COMPACTFRAMEWORK
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
#endif
internal delegate void SQLiteRollbackCallback(IntPtr puser);
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate void SQLiteLogCallback(IntPtr puser, int err_code, IntPtr message);
+
///
/// Raised when a transaction is about to be committed. To roll back a transaction, set the
/// rollbackTrans boolean value to true.
///
/// The connection committing the transaction
@@ -2365,10 +2442,17 @@
///
/// The connection executing the statement
/// Event arguments on the trace
public delegate void SQLiteTraceEventHandler(object sender, TraceEventArgs e);
+ ///
+ /// Raised when a log event occurs.
+ ///
+ /// The current connection
+ /// Event arguments on the trace
+ public delegate void SQLiteLogEventHandler(object sender, LogEventArgs e);
+
///
/// Whenever an update event is triggered on a connection, this enum will indicate
/// exactly what type of operation is being performed.
///
public enum UpdateEventType
@@ -2450,7 +2534,30 @@
internal TraceEventArgs(string statement)
{
Statement = statement;
}
}
+
+ ///
+ /// Passed during an Log callback
+ ///
+ public class LogEventArgs : EventArgs
+ {
+ ///
+ /// The error code.
+ ///
+ public readonly int ErrorCode;
+
+ ///
+ /// SQL statement text as the statement first begins executing
+ ///
+ public readonly string Message;
+
+ internal LogEventArgs(IntPtr puser, int err_code, string message)
+ {
+ // puser should be NULL
+ ErrorCode = err_code;
+ Message = message;
+ }
+ }
}
Index: System.Data.SQLite/UnsafeNativeMethods.cs
==================================================================
--- System.Data.SQLite/UnsafeNativeMethods.cs
+++ System.Data.SQLite/UnsafeNativeMethods.cs
@@ -33,14 +33,16 @@
#else
private const string SQLITE_DLL = "sqlite3";
#endif
// This section uses interop calls that also fetch text length to optimize conversion.
- // When using the standard dll, we can replace these calls with normal sqlite calls and do unoptimized conversions instead afterwards
+ // When using the standard dll, we can replace these calls with normal sqlite calls and
+ // do unoptimized conversions instead afterwards
#region interop added textlength calls
#if !SQLITE_STANDARD
+
[DllImport(SQLITE_DLL)]
internal static extern IntPtr sqlite3_bind_parameter_name_interop(IntPtr stmt, int index, out int len);
[DllImport(SQLITE_DLL)]
internal static extern IntPtr sqlite3_column_database_name_interop(IntPtr stmt, int index, out int len);
@@ -76,19 +78,10 @@
internal static extern IntPtr sqlite3_column_text_interop(IntPtr stmt, int index, out int len);
[DllImport(SQLITE_DLL)]
internal static extern IntPtr sqlite3_column_text16_interop(IntPtr stmt, int index, out int len);
- [DllImport(SQLITE_DLL)]
- internal static extern int sqlite3_extended_result_codes_interop(IntPtr db, int onoff);
-
- [DllImport(SQLITE_DLL)]
- internal static extern int sqlite3_errcode_interop(IntPtr db);
-
- [DllImport(SQLITE_DLL)]
- internal static extern int sqlite3_extended_errcode_interop(IntPtr db);
-
[DllImport(SQLITE_DLL)]
internal static extern IntPtr sqlite3_errmsg_interop(IntPtr db, out int len);
[DllImport(SQLITE_DLL)]
internal static extern int sqlite3_prepare_interop(IntPtr db, IntPtr pSql, int nBytes, out IntPtr stmt, out IntPtr ptrRemain, out int nRemain);
@@ -99,19 +92,22 @@
[DllImport(SQLITE_DLL)]
internal static extern IntPtr sqlite3_value_text_interop(IntPtr p, out int len);
[DllImport(SQLITE_DLL)]
internal static extern IntPtr sqlite3_value_text16_interop(IntPtr p, out int len);
+
#endif
+// !SQLITE_STANDARD
#endregion
// These functions add existing functionality on top of SQLite and require a little effort to
// get working when using the standard SQLite library.
#region interop added functionality
#if !SQLITE_STANDARD
+
[DllImport(SQLITE_DLL)]
internal static extern int sqlite3_close_interop(IntPtr db);
[DllImport(SQLITE_DLL)]
internal static extern int sqlite3_create_function_interop(IntPtr db, byte[] strName, int nArgs, int nType, IntPtr pvUser, SQLiteCallback func, SQLiteCallback fstep, SQLiteFinalCallback ffinal, int needCollSeq);
@@ -127,17 +123,19 @@
[DllImport(SQLITE_DLL)]
internal static extern int sqlite3_reset_interop(IntPtr stmt);
#endif
+// !SQLITE_STANDARD
#endregion
// The standard api call equivalents of the above interop calls
#region standard versions of interop functions
#if SQLITE_STANDARD
+
#if !PLATFORM_COMPACTFRAMEWORK
[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
[DllImport(SQLITE_DLL)]
#endif
@@ -270,31 +268,10 @@
internal static extern IntPtr sqlite3_column_text16(IntPtr stmt, int index);
#if !PLATFORM_COMPACTFRAMEWORK
[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
- [DllImport(SQLITE_DLL)]
-#endif
- internal static extern int sqlite3_extended_result_codes(IntPtr db, int onoff);
-
-#if !PLATFORM_COMPACTFRAMEWORK
- [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
-#else
- [DllImport(SQLITE_DLL)]
-#endif
- internal static extern int sqlite3_errcode(IntPtr db);
-
-#if !PLATFORM_COMPACTFRAMEWORK
- [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
-#else
- [DllImport(SQLITE_DLL)]
-#endif
- internal static extern int sqlite3_extended_errcode(IntPtr db);
-
-#if !PLATFORM_COMPACTFRAMEWORK
- [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
-#else
[DllImport(SQLITE_DLL)]
#endif
internal static extern IntPtr sqlite3_errmsg(IntPtr db);
#if !PLATFORM_COMPACTFRAMEWORK
@@ -322,19 +299,22 @@
[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
[DllImport(SQLITE_DLL)]
#endif
internal static extern IntPtr sqlite3_value_text16(IntPtr p);
+
#endif
+// SQLITE_STANDARD
#endregion
// These functions are custom and have no equivalent standard library method.
// All of them are "nice to haves" and not necessarily "need to haves".
#region no equivalent standard method
#if !SQLITE_STANDARD
+
[DllImport(SQLITE_DLL)]
internal static extern IntPtr sqlite3_context_collseq(IntPtr context, out int type, out int enc, out int len);
[DllImport(SQLITE_DLL)]
internal static extern int sqlite3_context_collcompare(IntPtr context, byte[] p1, int p1len, byte[] p2, int p2len);
@@ -348,11 +328,13 @@
[DllImport(SQLITE_DLL)]
internal static extern void sqlite3_resetall_interop(IntPtr db);
[DllImport(SQLITE_DLL)]
internal static extern int sqlite3_table_cursor(IntPtr stmt, int db, int tableRootPage);
+
#endif
+// !SQLITE_STANDARD
#endregion
// Standard API calls global across versions. There are a few instances of interop calls
// scattered in here, but they are only active when PLATFORM_COMPACTFRAMEWORK is declared.
@@ -377,10 +359,17 @@
#else
[DllImport(SQLITE_DLL)]
#endif
internal static extern int sqlite3_changes(IntPtr db);
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_shutdown();
+
#if !PLATFORM_COMPACTFRAMEWORK
[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
[DllImport(SQLITE_DLL)]
#endif
@@ -672,10 +661,19 @@
#else
[DllImport(SQLITE_DLL)]
#endif
internal static extern IntPtr sqlite3_trace(IntPtr db, SQLiteTraceCallback func, IntPtr pvUser);
+ // Since sqlite3_config() takes a variable argument list, we have to overload declarations
+ // for all possible calls. For now, we are only exposing the SQLITE_CONFIG_LOG call.
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_config(int op, SQLiteLogCallback func, IntPtr pvUser);
+
#if !PLATFORM_COMPACTFRAMEWORK
[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
[DllImport(SQLITE_DLL)]
#endif
@@ -706,10 +704,32 @@
[DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
#else
[DllImport(SQLITE_DLL)]
#endif
internal static extern int sqlite3_get_autocommit(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_extended_result_codes(IntPtr db, int onoff);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_errcode(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_extended_errcode(IntPtr db);
+
#endregion
}
#if PLATFORM_COMPACTFRAMEWORK
Index: test/TestCases.cs
==================================================================
--- test/TestCases.cs
+++ test/TestCases.cs
@@ -1525,10 +1525,52 @@
int xrc = cnn.ExtendedResultCode();
cnn.Close();
}
}
+
+ //Applying EventHandler
+ public void OnLogEvent(object sender, LogEventArgs logEvent)
+ {
+ int err_code = logEvent.ErrorCode;
+ string err_msg = logEvent.Message;
+ }
+
+ ///
+ /// Tests SQLITE_CONFIG_LOG support.
+ ///
+ [Test]
+ internal void SetLogCallbackTest()
+ {
+ if (_factstring.ToLower().Contains("sqlite"))
+ {
+ SQLiteConnection cnn = new SQLiteConnection(_cnnstring.ConnectionString);
+
+ cnn.Shutdown(); // we need to shutdown so that we can change config options
+
+ SQLiteLogEventHandler logHandler = new SQLiteLogEventHandler(OnLogEvent);
+ cnn.Log += logHandler;
+
+ cnn.Open();
+
+ maydroptable.Add("LogCallbackTest");
+ if (cnn.State != ConnectionState.Open) cnn.Open();
+ using (DbCommand cmd = cnn.CreateCommand())
+ {
+ cmd.CommandText = "CREATE TABLE LogCallbackTest(ID int primary key)";
+ cmd.ExecuteNonQuery();
+ }
+
+ cnn.Close();
+
+ cnn.Shutdown(); // we need to shutdown so that we can change config options
+
+ // remove the log handler before the connection is closed.
+ cnn.Log -= logHandler;
+
+ }
+ }
///
/// Open a reader and then attempt to write to test the writer's command timeout property
/// SQLite doesn't allow a write when a reader is active.
/// *** NOTE AS OF 3.3.8 this test no longer blocks because SQLite now allows you to update table(s)