064b681e9b
FossilOrigin-Name: 6e5bb48a74d63fb8c30528f0005d1763cd2dbb882abf86baf1565721e6bfcf84
432 lines
11 KiB
C
432 lines
11 KiB
C
/*
|
|
** 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"
|
|
#include "tclsqlite.h"
|
|
#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
|
|
#include "tclsqlite.h"
|
|
int SqliteRbu_Init(Tcl_Interp *interp){ return TCL_OK; }
|
|
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
|
|
#endif /* defined(SQLITE_TEST) */
|