Repurpose the SQLITE_TESTCTRL_FAULT_INSTALL test-control to register a
callback to be invoked by sqlite3FaultSim(). That test-control has been
unused since 2008-06-20 and was never used in any official release.
FossilOrigin-Name: 0d43a7ad9abe821e33e0bf83a997aa4461b1e3f2
diff --git a/src/global.c b/src/global.c
index 1ee3f64..2c14b58 100644
--- a/src/global.c
+++ b/src/global.c
@@ -173,15 +173,22 @@
0, /* isMutexInit */
0, /* isMallocInit */
0, /* isPCacheInit */
- 0, /* pInitMutex */
0, /* nRefInitMutex */
+ 0, /* pInitMutex */
0, /* xLog */
0, /* pLogArg */
- 0, /* bLocaltimeFault */
#ifdef SQLITE_ENABLE_SQLLOG
0, /* xSqllog */
- 0 /* pSqllogArg */
+ 0, /* pSqllogArg */
#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ 0, /* xVdbeBranch */
+ 0, /* pVbeBranchArg */
+#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ 0, /* xTestCallback */
+#endif
+ 0 /* bLocaltimeFault */
};
/*
diff --git a/src/main.c b/src/main.c
index 65521f4..b3bc66e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3115,6 +3115,23 @@
}
/*
+ ** sqlite3_test_control(FAULT_INSTALL, xCallback)
+ **
+ ** Arrange to invoke xCallback() whenever sqlite3FaultSim() is called,
+ ** if xCallback is not NULL.
+ **
+ ** As a test of the fault simulator mechanism itself, sqlite3FaultSim(0)
+ ** is called immediately after installing the new callback and the return
+ ** value from sqlite3FaultSim(0) becomes the return from
+ ** sqlite3_test_control().
+ */
+ case SQLITE_TESTCTRL_FAULT_INSTALL: {
+ sqlite3Config.xTestCallback = va_arg(ap, int(*)(int));
+ rc = sqlite3FaultSim(0);
+ break;
+ }
+
+ /*
** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd)
**
** Register hooks to call to indicate which malloc() failures
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 4bd65a6..bb8763c 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2709,11 +2709,10 @@
int isMutexInit; /* True after mutexes are initialized */
int isMallocInit; /* True after malloc is initialized */
int isPCacheInit; /* True after malloc is initialized */
- sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
int nRefInitMutex; /* Number of users of pInitMutex */
+ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
void (*xLog)(void*,int,const char*); /* Function for logging */
void *pLogArg; /* First argument to xLog() */
- int bLocaltimeFault; /* True to fail localtime() calls */
#ifdef SQLITE_ENABLE_SQLLOG
void(*xSqllog)(void*,sqlite3*,const char*, int);
void *pSqllogArg;
@@ -2725,6 +2724,10 @@
void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */
void *pVdbeBranchArg; /* 1st argument */
#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
+#endif
+ int bLocaltimeFault; /* True to fail localtime() calls */
};
/*
@@ -3026,6 +3029,12 @@
Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
int sqlite3CodeOnce(Parse *);
+#ifdef SQLITE_OMIT_BUILTIN_TEST
+# define sqlite3FaultSim(X) SQLITE_OK
+#else
+ int sqlite3FaultSim(int);
+#endif
+
Bitvec *sqlite3BitvecCreate(u32);
int sqlite3BitvecTest(Bitvec*, u32);
int sqlite3BitvecSet(Bitvec*, u32);
diff --git a/src/test2.c b/src/test2.c
index d130e9d..58f271f 100644
--- a/src/test2.c
+++ b/src/test2.c
@@ -568,7 +568,90 @@
rc = sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
-}
+}
+
+/*
+** The sqlite3FaultSim() callback:
+*/
+static Tcl_Interp *faultSimInterp = 0;
+static int faultSimScriptSize = 0;
+static char *faultSimScript;
+static int faultSimCallback(int x){
+ char zInt[30];
+ int i;
+ int isNeg;
+ int rc;
+ if( x==0 ){
+ memcpy(faultSimScript+faultSimScriptSize, "0", 2);
+ }else{
+ /* Convert x to text without using any sqlite3 routines */
+ if( x<0 ){
+ isNeg = 1;
+ x = -x;
+ }else{
+ isNeg = 0;
+ }
+ zInt[sizeof(zInt)-1] = 0;
+ for(i=sizeof(zInt)-2; i>0 && x>0; i--, x /= 10){
+ zInt[i] = (x%10) + '0';
+ }
+ if( isNeg ) zInt[i--] = '-';
+ memcpy(faultSimScript+faultSimScriptSize, zInt+i+1, sizeof(zInt)-i);
+ }
+ rc = Tcl_Eval(faultSimInterp, faultSimScript);
+ if( rc ){
+ fprintf(stderr, "fault simulator script failed: [%s]", faultSimScript);
+ rc = SQLITE_ERROR;
+ }else{
+ rc = atoi(Tcl_GetStringResult(faultSimInterp));
+ }
+ Tcl_ResetResult(faultSimInterp);
+ return rc;
+}
+
+/*
+** sqlite3_test_control_fault_install SCRIPT
+**
+** Arrange to invoke SCRIPT with the integer argument to sqlite3FaultSim()
+** appended, whenever sqlite3FaultSim() is called. Or, if SCRIPT is the
+** empty string, cancel the sqlite3FaultSim() callback.
+*/
+static int faultInstallCmd(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ const char *zScript;
+ int nScript;
+ int rc;
+ if( argc!=1 && argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " SCRIPT\"", (void*)0);
+ }
+ zScript = argc==2 ? argv[1] : "";
+ nScript = (int)strlen(zScript);
+ if( faultSimScript ){
+ free(faultSimScript);
+ faultSimScript = 0;
+ }
+ if( nScript==0 ){
+ rc = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL, 0);
+ }else{
+ faultSimScript = malloc( nScript+100 );
+ if( faultSimScript==0 ){
+ Tcl_AppendResult(interp, "out of memory", (void*)0);
+ return SQLITE_ERROR;
+ }
+ memcpy(faultSimScript, zScript, nScript);
+ faultSimScript[nScript] = ' ';
+ faultSimScriptSize = nScript+1;
+ faultSimInterp = interp;
+ rc = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL, faultSimCallback);
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return SQLITE_OK;
+}
/*
** sqlite3BitvecBuiltinTest SIZE PROGRAM
@@ -638,7 +721,8 @@
{ "fake_big_file", (Tcl_CmdProc*)fake_big_file },
#endif
{ "sqlite3BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest },
- { "sqlite3_test_control_pending_byte", (Tcl_CmdProc*)testPendingByte },
+ { "sqlite3_test_control_pending_byte", (Tcl_CmdProc*)testPendingByte },
+ { "sqlite3_test_control_fault_install", (Tcl_CmdProc*)faultInstallCmd },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
diff --git a/src/util.c b/src/util.c
index 577d552..4fe07a8 100644
--- a/src/util.c
+++ b/src/util.c
@@ -31,6 +31,24 @@
}
#endif
+/*
+** Give a callback to the test harness that can be used to simulate faults
+** in places where it is difficult or expensive to do so purely by means
+** of inputs.
+**
+** The intent of the integer argument is to let the fault simulator know
+** which of multiple sqlite3FaultSim() calls has been hit.
+**
+** Return whatever integer value the test callback returns, or return
+** SQLITE_OK if no test callback is installed.
+*/
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+int sqlite3FaultSim(int iTest){
+ int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback;
+ return xCallback ? xCallback(iTest) : SQLITE_OK;
+}
+#endif
+
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Return true if the floating point value is Not a Number (NaN).