diff options
Diffstat (limited to '')
-rw-r--r-- | ext/rbu/test_rbu.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/ext/rbu/test_rbu.c b/ext/rbu/test_rbu.c new file mode 100644 index 0000000..af794d8 --- /dev/null +++ b/ext/rbu/test_rbu.c @@ -0,0 +1,442 @@ +/* +** 2015 February 16 +** +** 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. +** +************************************************************************* +*/ + +#include "sqlite3.h" + +#if defined(SQLITE_TEST) +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) + +#include "sqlite3rbu.h" +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +# ifndef SQLITE_TCLAPI +# define SQLITE_TCLAPI +# endif +#endif +#include <assert.h> +#include <string.h> + +typedef struct TestRbu TestRbu; +struct TestRbu { + sqlite3rbu *pRbu; + Tcl_Interp *interp; + Tcl_Obj *xRename; +}; + +/* From main.c */ +extern const char *sqlite3ErrName(int); +extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*); + +void test_rbu_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){ + Tcl_Interp *interp = (Tcl_Interp*)sqlite3_user_data(pCtx); + Tcl_Obj *pScript; + int i; + + pScript = Tcl_NewObj(); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj("rbu_delta", -1)); + for(i=0; i<nArg; i++){ + sqlite3_value *pIn = apVal[i]; + const char *z = (const char*)sqlite3_value_text(pIn); + Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(z, -1)); + } + + if( TCL_OK==Tcl_EvalObjEx(interp, pScript, TCL_GLOBAL_ONLY) ){ + const char *z = Tcl_GetStringResult(interp); + sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); + }else{ + Tcl_BackgroundError(interp); + } + + Tcl_DecrRefCount(pScript); +} + +static int xRenameCallback(void *pArg, const char *zOld, const char *zNew){ + int rc = SQLITE_OK; + TestRbu *pTest = (TestRbu*)pArg; + Tcl_Obj *pEval = Tcl_DuplicateObj(pTest->xRename); + + Tcl_IncrRefCount(pEval); + Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zOld, -1)); + Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zNew, -1)); + + rc = Tcl_EvalObjEx(pTest->interp, pEval, TCL_GLOBAL_ONLY); + Tcl_DecrRefCount(pEval); + + return rc ? SQLITE_IOERR : SQLITE_OK; +} + +static int SQLITE_TCLAPI test_sqlite3rbu_cmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int ret = TCL_OK; + TestRbu *pTest = (TestRbu*)clientData; + sqlite3rbu *pRbu = pTest->pRbu; + struct RbuCmd { + const char *zName; + int nArg; + const char *zUsage; + } aCmd[] = { + {"step", 2, ""}, /* 0 */ + {"close", 2, ""}, /* 1 */ + {"create_rbu_delta", 2, ""}, /* 2 */ + {"savestate", 2, ""}, /* 3 */ + {"dbMain_eval", 3, "SQL"}, /* 4 */ + {"bp_progress", 2, ""}, /* 5 */ + {"db", 3, "RBU"}, /* 6 */ + {"state", 2, ""}, /* 7 */ + {"progress", 2, ""}, /* 8 */ + {"close_no_error", 2, ""}, /* 9 */ + {"temp_size_limit", 3, "LIMIT"}, /* 10 */ + {"temp_size", 2, ""}, /* 11 */ + {"dbRbu_eval", 3, "SQL"}, /* 12 */ + {"rename_handler", 3, "SCRIPT"},/* 13 */ + {0,0,0} + }; + int iCmd; + + if( objc<2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "METHOD"); + return TCL_ERROR; + } + ret = Tcl_GetIndexFromObjStruct( + interp, objv[1], aCmd, sizeof(aCmd[0]), "method", 0, &iCmd + ); + if( ret ) return TCL_ERROR; + if( objc!=aCmd[iCmd].nArg ){ + Tcl_WrongNumArgs(interp, 1, objv, aCmd[iCmd].zUsage); + return TCL_ERROR; + } + + switch( iCmd ){ + case 0: /* step */ { + int rc = sqlite3rbu_step(pRbu); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + break; + } + + case 9: /* close_no_error */ + case 1: /* close */ { + char *zErrmsg = 0; + int rc; + Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); + if( iCmd==1 ){ + rc = sqlite3rbu_close(pRbu, &zErrmsg); + }else{ + rc = sqlite3rbu_close(pRbu, 0); + } + if( rc==SQLITE_OK || rc==SQLITE_DONE ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + assert( zErrmsg==0 ); + }else{ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + if( zErrmsg ){ + Tcl_AppendResult(interp, " - ", zErrmsg, 0); + sqlite3_free(zErrmsg); + } + ret = TCL_ERROR; + } + if( pTest->xRename ) Tcl_DecrRefCount(pTest->xRename); + ckfree(pTest); + break; + } + + case 2: /* create_rbu_delta */ { + sqlite3 *db = sqlite3rbu_db(pRbu, 0); + int rc = sqlite3_create_function( + db, "rbu_delta", -1, SQLITE_UTF8, (void*)interp, test_rbu_delta, 0, 0 + ); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); + break; + } + + case 3: /* savestate */ { + int rc = sqlite3rbu_savestate(pRbu); + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); + break; + } + + case 12: /* dbRbu_eval */ + case 4: /* dbMain_eval */ { + sqlite3 *db = sqlite3rbu_db(pRbu, (iCmd==12)); + int rc = sqlite3_exec(db, Tcl_GetString(objv[2]), 0, 0, 0); + if( rc!=SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(db), -1)); + ret = TCL_ERROR; + } + break; + } + + case 5: /* bp_progress */ { + int one, two; + Tcl_Obj *pObj; + sqlite3rbu_bp_progress(pRbu, &one, &two); + + pObj = Tcl_NewObj(); + Tcl_ListObjAppendElement(interp, pObj, Tcl_NewIntObj(one)); + Tcl_ListObjAppendElement(interp, pObj, Tcl_NewIntObj(two)); + Tcl_SetObjResult(interp, pObj); + break; + } + + case 6: /* db */ { + int bArg; + if( Tcl_GetBooleanFromObj(interp, objv[2], &bArg) ){ + ret = TCL_ERROR; + }else{ + char zBuf[50]; + sqlite3 *db = sqlite3rbu_db(pRbu, bArg); + if( sqlite3TestMakePointerStr(interp, zBuf, (void*)db) ){ + ret = TCL_ERROR; + }else{ + Tcl_SetResult(interp, zBuf, TCL_VOLATILE); + } + } + break; + } + case 7: /* state */ { + const char *aRes[] = { 0, "oal", "move", "checkpoint", "done", "error" }; + int eState = sqlite3rbu_state(pRbu); + assert( eState>0 && eState<=5 ); + Tcl_SetResult(interp, (char*)aRes[eState], TCL_STATIC); + break; + } + case 8: /* progress */ { + sqlite3_int64 nStep = sqlite3rbu_progress(pRbu); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep)); + break; + } + + case 10: /* temp_size_limit */ { + sqlite3_int64 nLimit; + if( Tcl_GetWideIntFromObj(interp, objv[2], &nLimit) ){ + ret = TCL_ERROR; + }else{ + nLimit = sqlite3rbu_temp_size_limit(pRbu, nLimit); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nLimit)); + } + break; + } + case 11: /* temp_size */ { + sqlite3_int64 sz = sqlite3rbu_temp_size(pRbu); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sz)); + break; + } + + case 13: /* rename_handler */ { + Tcl_Obj *pScript = objv[2]; + assert( !sqlite3_stricmp(aCmd[13].zName, "rename_handler") ); + if( Tcl_GetCharLength(pScript)==0 ){ + sqlite3rbu_rename_handler(pRbu, 0, 0); + }else{ + pTest->xRename = Tcl_DuplicateObj(pScript); + Tcl_IncrRefCount(pTest->xRename); + sqlite3rbu_rename_handler(pRbu, pTest, xRenameCallback); + } + break; + } + + default: /* seems unlikely */ + assert( !"cannot happen" ); + break; + } + + return ret; +} + +static void createRbuWrapper( + Tcl_Interp *interp, + const char *zCmd, + sqlite3rbu *pRbu +){ + TestRbu *pTest = (TestRbu*)ckalloc(sizeof(TestRbu)); + memset(pTest, 0, sizeof(TestRbu)); + pTest->pRbu = pRbu; + pTest->interp = interp; + Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pTest, 0); +} + +/* +** Tclcmd: sqlite3rbu CMD <target-db> <rbu-db> ?<state-db>? +*/ +static int SQLITE_TCLAPI test_sqlite3rbu( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3rbu *pRbu = 0; + const char *zCmd; + const char *zTarget; + const char *zRbu; + const char *zStateDb = 0; + + if( objc!=4 && objc!=5 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB RBU-DB ?STATE-DB?"); + return TCL_ERROR; + } + zCmd = Tcl_GetString(objv[1]); + zTarget = Tcl_GetString(objv[2]); + zRbu = Tcl_GetString(objv[3]); + if( objc==5 ) zStateDb = Tcl_GetString(objv[4]); + + pRbu = sqlite3rbu_open(zTarget, zRbu, zStateDb); + createRbuWrapper(interp, zCmd, pRbu); + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; +} + +/* +** Tclcmd: sqlite3rbu_vacuum CMD <target-db> <state-db> +*/ +static int SQLITE_TCLAPI test_sqlite3rbu_vacuum( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3rbu *pRbu = 0; + const char *zCmd; + const char *zTarget; + const char *zStateDb = 0; + + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?"); + return TCL_ERROR; + } + zCmd = Tcl_GetString(objv[1]); + zTarget = Tcl_GetString(objv[2]); + if( objc==4 ) zStateDb = Tcl_GetString(objv[3]); + if( zStateDb && zStateDb[0]=='\0' ) zStateDb = 0; + + pRbu = sqlite3rbu_vacuum(zTarget, zStateDb); + createRbuWrapper(interp, zCmd, pRbu); + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; +} + +/* +** Tclcmd: sqlite3rbu_create_vfs ?-default? NAME PARENT +*/ +static int SQLITE_TCLAPI test_sqlite3rbu_create_vfs( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + const char *zName; + const char *zParent; + int rc; + + if( objc!=3 && objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "?-default? NAME PARENT"); + return TCL_ERROR; + } + + zName = Tcl_GetString(objv[objc-2]); + zParent = Tcl_GetString(objv[objc-1]); + if( zParent[0]=='\0' ) zParent = 0; + + rc = sqlite3rbu_create_vfs(zName, zParent); + if( rc!=SQLITE_OK ){ + Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); + return TCL_ERROR; + }else if( objc==4 ){ + sqlite3_vfs *pVfs = sqlite3_vfs_find(zName); + sqlite3_vfs_register(pVfs, 1); + } + + Tcl_ResetResult(interp); + return TCL_OK; +} + +/* +** Tclcmd: sqlite3rbu_destroy_vfs NAME +*/ +static int SQLITE_TCLAPI test_sqlite3rbu_destroy_vfs( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + const char *zName; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME"); + return TCL_ERROR; + } + + zName = Tcl_GetString(objv[1]); + sqlite3rbu_destroy_vfs(zName); + return TCL_OK; +} + +/* +** Tclcmd: sqlite3rbu_internal_test +*/ +static int SQLITE_TCLAPI test_sqlite3rbu_internal_test( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3 *db; + + if( objc!=1 ){ + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + + db = sqlite3rbu_db(0, 0); + if( db!=0 ){ + Tcl_AppendResult(interp, "sqlite3rbu_db(0, 0)!=0", 0); + return TCL_ERROR; + } + + return TCL_OK; +} + +int SqliteRbu_Init(Tcl_Interp *interp){ + static struct { + char *zName; + Tcl_ObjCmdProc *xProc; + } aObjCmd[] = { + { "sqlite3rbu", test_sqlite3rbu }, + { "sqlite3rbu_vacuum", test_sqlite3rbu_vacuum }, + { "sqlite3rbu_create_vfs", test_sqlite3rbu_create_vfs }, + { "sqlite3rbu_destroy_vfs", test_sqlite3rbu_destroy_vfs }, + { "sqlite3rbu_internal_test", test_sqlite3rbu_internal_test }, + }; + int i; + for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ + Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); + } + return TCL_OK; +} + +#else +#if defined(INCLUDE_SQLITE_TCL_H) +# include "sqlite_tcl.h" +#else +# include "tcl.h" +#endif +int SqliteRbu_Init(Tcl_Interp *interp){ return TCL_OK; } +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */ +#endif /* defined(SQLITE_TEST) */ |