Index: src/func.c
==================================================================
--- src/func.c
+++ src/func.c
@@ -884,21 +884,23 @@
sqlite3_result_text(context, sqlite3_sourceid(), -1, SQLITE_STATIC);
}
/*
** Implementation of the sqlite_log() function. This is a wrapper around
-** sqlite3_log(). The return value is NULL. The function exists purely for
-** its side-effects.
+** sqlite3_db_log(). The return value is NULL. The function exists purely
+** for its side-effects.
*/
static void errlogFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(context);
- sqlite3_log(sqlite3_value_int(argv[0]), "%s", sqlite3_value_text(argv[1]));
+ sqlite3_db_log(sqlite3_context_db_handle(context),
+ sqlite3_value_int(argv[0]),
+ "%s", sqlite3_value_text(argv[1]));
}
/*
** Implementation of the sqlite_compileoption_used() function.
** The result is an integer that identifies if the compiler option
Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -732,10 +732,18 @@
void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */
rc = setupLookaside(db, pBuf, sz, cnt);
break;
+ }
+ case SQLITE_DBCONFIG_LOG: {
+ /* MSVC is picky about pulling func ptrs from va lists.
+ ** http://support.microsoft.com/kb/47961 */
+ typedef void(*LOGFUNC_t)(void*,int,const char*);
+ db->xLog = va_arg(ap, LOGFUNC_t);
+ db->pLogArg = va_arg(ap, void*);
+ break;
}
default: {
static const struct {
int op; /* The opcode */
u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */
Index: src/printf.c
==================================================================
--- src/printf.c
+++ src/printf.c
@@ -1034,29 +1034,48 @@
**
** sqlite3_log() must render into a static buffer. It cannot dynamically
** allocate memory because it might be called while the memory allocator
** mutex is held.
*/
-static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
+static void renderLogMsg(
+ void (*xLog)(void*,int,const char*), /* Logging function */
+ void *pArg, /* First argument to xLog */
+ int iErrCode, /* Error code */
+ const char *zFormat, /* Format string for error message */
+ va_list ap /* Arguments to format string */
+){
StrAccum acc; /* String accumulator */
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
sqlite3StrAccumInit(&acc, zMsg, sizeof(zMsg), 0);
acc.useMalloc = 0;
sqlite3VXPrintf(&acc, 0, zFormat, ap);
- sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
- sqlite3StrAccumFinish(&acc));
+ xLog(pArg, iErrCode, sqlite3StrAccumFinish(&acc));
}
/*
** Format and write a message to the log if logging is enabled.
*/
void sqlite3_log(int iErrCode, const char *zFormat, ...){
va_list ap; /* Vararg list */
if( sqlite3GlobalConfig.xLog ){
va_start(ap, zFormat);
- renderLogMsg(iErrCode, zFormat, ap);
+ renderLogMsg(sqlite3GlobalConfig.xLog, sqlite3GlobalConfig.pLogArg,
+ iErrCode, zFormat, ap);
+ va_end(ap);
+ }
+}
+void sqlite3_db_log(sqlite3 *db, int iErrCode, const char *zFormat, ...){
+ va_list ap; /* Vararg list */
+ if( db && db->xLog ){
+ va_start(ap, zFormat);
+ renderLogMsg(db->xLog, db->pLogArg, iErrCode, zFormat, ap);
+ va_end(ap);
+ }else if( sqlite3GlobalConfig.xLog ){
+ va_start(ap, zFormat);
+ renderLogMsg(sqlite3GlobalConfig.xLog, sqlite3GlobalConfig.pLogArg,
+ iErrCode, zFormat, ap);
va_end(ap);
}
}
#if defined(SQLITE_DEBUG)
Index: src/sqlite.h.in
==================================================================
--- src/sqlite.h.in
+++ src/sqlite.h.in
@@ -1867,15 +1867,40 @@
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether triggers are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back.
**
+** [[SQLITE_DBCONFIG_LOG]]
SQLITE_DBCONFIG_LOG
+** The SQLITE_DBCONFIG_LOG option is used to configure the SQLite
+** [error log] for a specific database connection. See also the
+** [SQLITE_CONFIG_LOG] option to [sqlite3_config()] for the global log
+** callback.
+** (^The SQLITE_DBCONFIG_LOG option takes two arguments: a pointer to a
+** function with a call signature of void(*)(void*,int,const char*),
+** and a pointer to void. ^If the function pointer is not NULL, it is
+** invoked by [sqlite3_db_log()] to process each logging event. ^If the
+** function pointer is NULL, then calls to [sqlite3_db_log()] interface try
+** to use the [SQLITE_CONFIG_LOG|global error log]. If both the connection
+** and global error log callbacks are NULL then [sqlite3_db_log()] is a
+** harmless no-op.
+** ^The void pointer that is the second argument to SQLITE_DBCONFIG_LOG is
+** passed through as the first parameter to the application-defined logger
+** function whenever that function is invoked. ^The second parameter to
+** the logger function is a copy of the second parameter to the corresponding
+** [sqlite3_db_log()] call and is intended to be a [result code] or an
+** [extended result code]. ^The third parameter passed to the logger is
+** log message after formatting via [sqlite3_snprintf()].
+** The SQLite logging interface is not reentrant; the logger function
+** supplied by the application must not invoke any SQLite interface.
+**
+**
**
*/
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_LOG 1004 /* xFunc, void* */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
** METHOD: sqlite3
@@ -7283,14 +7308,23 @@
/*
** CAPI3REF: Error Logging Interface
**
** ^The [sqlite3_log()] interface writes a message into the [error log]
** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].
+** ^The [sqlite3_db_log()] interface writes a message into the [error log]
+** established by the [SQLITE_DBCONFIG_LOG] option to [sqlite3_db_config()].
** ^If logging is enabled, the zFormat string and subsequent arguments are
** used with [sqlite3_snprintf()] to generate the final output string.
**
-** The sqlite3_log() interface is intended for use by extensions such as
+** ^The [sqlite3_db_log()] interface writes error and warning information
+** to the callback identified by the [SQLITE_DBCONFIG_LOG] option if that
+** callback is not NULL, falling back to the [SQLITE_CONFIG_LOG] callback
+** if the first parameter to [sqlite3_db_log()] is NULL or if the
+** [SQLITE_DBCONFIG_LOG] callback is NULL.
+**
+** The sqlite3_log() and sqlite3_db_log() interfaces are intended for
+** use by extensions such as
** virtual tables, collating functions, and SQL functions. While there is
** nothing to prevent an application from calling sqlite3_log(), doing so
** is considered bad form.
**
** The zFormat string must not be NULL.
@@ -7300,10 +7334,11 @@
** a fixed-length buffer on the stack. If the log message is longer than
** a few hundred characters, it will be truncated to the length of the
** buffer.
*/
void sqlite3_log(int iErrCode, const char *zFormat, ...);
+void sqlite3_db_log(sqlite3*, int iErrCode, const char *zFormat, ...);
/*
** CAPI3REF: Write-Ahead Log Commit Hook
** METHOD: sqlite3
**
Index: src/sqliteInt.h
==================================================================
--- src/sqliteInt.h
+++ src/sqliteInt.h
@@ -1136,10 +1136,12 @@
union {
volatile int isInterrupted; /* True if sqlite3_interrupt has been called */
double notUsed1; /* Spacer */
} u1;
Lookaside lookaside; /* Lookaside malloc configuration */
+ void (*xLog)(void*,int,const char*); /* Function for logging */
+ void *pLogArg; /* First argument to xLog() */
#ifndef SQLITE_OMIT_AUTHORIZATION
sqlite3_xauth xAuth; /* Access authorization function */
void *pAuthArg; /* 1st argument to the access auth function */
#endif
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
Index: src/test1.c
==================================================================
--- src/test1.c
+++ src/test1.c
@@ -5795,17 +5795,43 @@
Tcl_DecrRefCount(logcallback.pObj);
logcallback.pObj = 0;
logcallback.pInterp = 0;
sqlite3_config(SQLITE_CONFIG_LOG, 0, 0);
}
- if( objc>1 ){
+ if( objc==2 ){
logcallback.pObj = objv[1];
Tcl_IncrRefCount(logcallback.pObj);
logcallback.pInterp = interp;
sqlite3_config(SQLITE_CONFIG_LOG, xLogcallback, 0);
}
return TCL_OK;
+}
+static int test_sqlite3_db_log(
+ ClientData clientData,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ if( objc<1 || objc>3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB SCRIPT");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ if( logcallback.pObj ){
+ Tcl_DecrRefCount(logcallback.pObj);
+ logcallback.pObj = 0;
+ logcallback.pInterp = 0;
+ sqlite3_db_config(db, SQLITE_DBCONFIG_LOG, 0, 0);
+ }
+ if( objc==3 ){
+ logcallback.pObj = objv[2];
+ Tcl_IncrRefCount(logcallback.pObj);
+ logcallback.pInterp = interp;
+ sqlite3_db_config(db, SQLITE_DBCONFIG_LOG, xLogcallback, 0);
+ }
+ return TCL_OK;
}
/*
** tcl_objproc COMMANDNAME ARGS...
**
@@ -6904,10 +6930,11 @@
#endif
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
{ "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 },
{ "sqlite3_wal_autocheckpoint",test_wal_autocheckpoint, 0 },
{ "test_sqlite3_log", test_sqlite3_log, 0 },
+ { "test_sqlite3_db_log", test_sqlite3_db_log, 0 },
#ifndef SQLITE_OMIT_EXPLAIN
{ "print_explain_query_plan", test_print_eqp, 0 },
#endif
{ "sqlite3_test_control", test_test_control },
#if SQLITE_OS_UNIX
Index: src/tokenize.c
==================================================================
--- src/tokenize.c
+++ src/tokenize.c
@@ -473,11 +473,11 @@
sqlite3SetString(&pParse->zErrMsg, db, "%s", sqlite3ErrStr(pParse->rc));
}
assert( pzErrMsg!=0 );
if( pParse->zErrMsg ){
*pzErrMsg = pParse->zErrMsg;
- sqlite3_log(pParse->rc, "%s", *pzErrMsg);
+ sqlite3_db_log(db, pParse->rc, "%s", *pzErrMsg);
pParse->zErrMsg = 0;
nErr++;
}
if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){
sqlite3VdbeDelete(pParse->pVdbe);
Index: src/vdbe.c
==================================================================
--- src/vdbe.c
+++ src/vdbe.c
@@ -972,11 +972,11 @@
}else if( pOp->p4.z ){
sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
}else{
sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType);
}
- sqlite3_log(pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg);
+ sqlite3_db_log(db, pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
if( rc==SQLITE_BUSY ){
p->rc = rc = SQLITE_BUSY;
@@ -6527,11 +6527,11 @@
*/
vdbe_error_halt:
assert( rc );
p->rc = rc;
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(rc, "statement aborts at %d: [%s] %s",
+ sqlite3_db_log(db, rc, "statement aborts at %d: [%s] %s",
(int)(pOp - aOp), p->zSql, p->zErrMsg);
sqlite3VdbeHalt(p);
if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
rc = SQLITE_ERROR;
if( resetSchemaOnFault>0 ){
Index: src/vdbeapi.c
==================================================================
--- src/vdbeapi.c
+++ src/vdbeapi.c
@@ -1143,13 +1143,13 @@
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(p->db->mutex);
if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
sqlite3Error(p->db, SQLITE_MISUSE);
- sqlite3_mutex_leave(p->db->mutex);
- sqlite3_log(SQLITE_MISUSE,
+ sqlite3_db_log(p->db, SQLITE_MISUSE,
"bind on a busy prepared statement: [%s]", p->zSql);
+ sqlite3_mutex_leave(p->db->mutex);
return SQLITE_MISUSE_BKPT;
}
if( i<1 || i>p->nVar ){
sqlite3Error(p->db, SQLITE_RANGE);
sqlite3_mutex_leave(p->db->mutex);
Index: src/vdbeaux.c
==================================================================
--- src/vdbeaux.c
+++ src/vdbeaux.c
@@ -2051,15 +2051,15 @@
if( zMaster==0 ) return SQLITE_NOMEM;
do {
u32 iRandom;
if( retryCount ){
if( retryCount>100 ){
- sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
+ sqlite3_db_log(db, SQLITE_FULL, "MJ delete: %s", zMaster);
sqlite3OsDelete(pVfs, zMaster, 0);
break;
}else if( retryCount==1 ){
- sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster);
+ sqlite3_db_log(db, SQLITE_FULL, "MJ collide: %s", zMaster);
}
}
retryCount++;
sqlite3_randomness(sizeof(iRandom), &iRandom);
sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X",
Index: src/where.c
==================================================================
--- src/where.c
+++ src/where.c
@@ -1748,11 +1748,11 @@
int iCol = pTerm->u.leftColumn;
Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
testcase( iCol==BMS );
testcase( iCol==BMS-1 );
if( !sentWarning ){
- sqlite3_log(SQLITE_WARNING_AUTOINDEX,
+ sqlite3_db_log(pParse->db, SQLITE_WARNING_AUTOINDEX,
"automatic index on %s(%s)", pTable->zName,
pTable->aCol[iCol].zName);
sentWarning = 1;
}
if( (idxCols & cMask)==0 ){
ADDED test/errlog01.test
Index: test/errlog01.test
==================================================================
--- /dev/null
+++ test/errlog01.test
@@ -0,0 +1,37 @@
+# 2015-04-22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+# Test cases for the sqlite3_log() and sqlite3_db_log() interfaces.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+unset -nocomplain log
+set log {}
+do_test errlog01-1.0 {
+ test_sqlite3_db_log db [list lappend ::log]
+ db eval {SELECT sqlite_log(7,'simulated error')}
+} {{}}
+do_test errlog01-1.1 {
+ set ::log
+} {SQLITE_NOMEM {simulated error}}
+do_test errlog01-1.2 {
+ sqlite3 db2 :memory:
+ set ::log {}
+ db2 eval {SELECT sqlite_log(7,'simulated error')}
+} {{}}
+do_test errlog01-1.3 {
+ set ::log
+} {}
+
+
+
+finish_test