#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;
};
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, ""},
{"close", 2, ""},
{"create_rbu_delta", 2, ""},
{"savestate", 2, ""},
{"dbMain_eval", 3, "SQL"},
{"bp_progress", 2, ""},
{"db", 3, "RBU"},
{"state", 2, ""},
{"progress", 2, ""},
{"close_no_error", 2, ""},
{"temp_size_limit", 3, "LIMIT"},
{"temp_size", 2, ""},
{"dbRbu_eval", 3, "SQL"},
{"rename_handler", 3, "SCRIPT"},
{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: {
int rc = sqlite3rbu_step(pRbu);
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
break;
}
case 9:
case 1: {
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: {
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: {
int rc = sqlite3rbu_savestate(pRbu);
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
break;
}
case 12:
case 4: {
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: {
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: {
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: {
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: {
sqlite3_int64 nStep = sqlite3rbu_progress(pRbu);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep));
break;
}
case 10: {
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: {
sqlite3_int64 sz = sqlite3rbu_temp_size(pRbu);
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sz));
break;
}
case 13: {
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:
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);
}
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;
}
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;
}
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;
}
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;
}
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
#endif