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)