xref: /sqlite-3.40.0/ext/rbu/test_rbu.c (revision 40503750)
1 /*
2 ** 2015 February 16
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 *************************************************************************
12 */
13 
14 #include "sqlite3.h"
15 
16 #if defined(SQLITE_TEST)
17 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU)
18 
19 #include "sqlite3rbu.h"
20 #if defined(INCLUDE_SQLITE_TCL_H)
21 #  include "sqlite_tcl.h"
22 #else
23 #  include "tcl.h"
24 #  ifndef SQLITE_TCLAPI
25 #    define SQLITE_TCLAPI
26 #  endif
27 #endif
28 #include <assert.h>
29 #include <string.h>
30 
31 typedef struct TestRbu TestRbu;
32 struct TestRbu {
33   sqlite3rbu *pRbu;
34   Tcl_Interp *interp;
35   Tcl_Obj *xRename;
36 };
37 
38 /* From main.c */
39 extern const char *sqlite3ErrName(int);
40 extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
41 
test_rbu_delta(sqlite3_context * pCtx,int nArg,sqlite3_value ** apVal)42 void test_rbu_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){
43   Tcl_Interp *interp = (Tcl_Interp*)sqlite3_user_data(pCtx);
44   Tcl_Obj *pScript;
45   int i;
46 
47   pScript = Tcl_NewObj();
48   Tcl_IncrRefCount(pScript);
49   Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj("rbu_delta", -1));
50   for(i=0; i<nArg; i++){
51     sqlite3_value *pIn = apVal[i];
52     const char *z = (const char*)sqlite3_value_text(pIn);
53     Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(z, -1));
54   }
55 
56   if( TCL_OK==Tcl_EvalObjEx(interp, pScript, TCL_GLOBAL_ONLY) ){
57     const char *z = Tcl_GetStringResult(interp);
58     sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
59   }else{
60     Tcl_BackgroundError(interp);
61   }
62 
63   Tcl_DecrRefCount(pScript);
64 }
65 
xRenameCallback(void * pArg,const char * zOld,const char * zNew)66 static int xRenameCallback(void *pArg, const char *zOld, const char *zNew){
67   int rc = SQLITE_OK;
68   TestRbu *pTest = (TestRbu*)pArg;
69   Tcl_Obj *pEval = Tcl_DuplicateObj(pTest->xRename);
70 
71   Tcl_IncrRefCount(pEval);
72   Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zOld, -1));
73   Tcl_ListObjAppendElement(pTest->interp, pEval, Tcl_NewStringObj(zNew, -1));
74 
75   rc = Tcl_EvalObjEx(pTest->interp, pEval, TCL_GLOBAL_ONLY);
76   Tcl_DecrRefCount(pEval);
77 
78   return rc ? SQLITE_IOERR : SQLITE_OK;
79 }
80 
test_sqlite3rbu_cmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])81 static int SQLITE_TCLAPI test_sqlite3rbu_cmd(
82   ClientData clientData,
83   Tcl_Interp *interp,
84   int objc,
85   Tcl_Obj *CONST objv[]
86 ){
87   int ret = TCL_OK;
88   TestRbu *pTest = (TestRbu*)clientData;
89   sqlite3rbu *pRbu = pTest->pRbu;
90   struct RbuCmd {
91     const char *zName;
92     int nArg;
93     const char *zUsage;
94   } aCmd[] = {
95     {"step", 2, ""},                 /* 0 */
96     {"close", 2, ""},                /* 1 */
97     {"create_rbu_delta", 2, ""},     /* 2 */
98     {"savestate", 2, ""},            /* 3 */
99     {"dbMain_eval", 3, "SQL"},       /* 4 */
100     {"bp_progress", 2, ""},          /* 5 */
101     {"db", 3, "RBU"},                /* 6 */
102     {"state", 2, ""},                /* 7 */
103     {"progress", 2, ""},             /* 8 */
104     {"close_no_error", 2, ""},       /* 9 */
105     {"temp_size_limit", 3, "LIMIT"}, /* 10 */
106     {"temp_size", 2, ""},            /* 11 */
107     {"dbRbu_eval", 3, "SQL"},        /* 12 */
108     {"rename_handler", 3, "SCRIPT"},/* 13 */
109     {0,0,0}
110   };
111   int iCmd;
112 
113   if( objc<2 ){
114     Tcl_WrongNumArgs(interp, 1, objv, "METHOD");
115     return TCL_ERROR;
116   }
117   ret = Tcl_GetIndexFromObjStruct(
118       interp, objv[1], aCmd, sizeof(aCmd[0]), "method", 0, &iCmd
119   );
120   if( ret ) return TCL_ERROR;
121   if( objc!=aCmd[iCmd].nArg ){
122     Tcl_WrongNumArgs(interp, 1, objv, aCmd[iCmd].zUsage);
123     return TCL_ERROR;
124   }
125 
126   switch( iCmd ){
127     case 0: /* step */ {
128       int rc = sqlite3rbu_step(pRbu);
129       Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
130       break;
131     }
132 
133     case 9: /* close_no_error */
134     case 1: /* close */ {
135       char *zErrmsg = 0;
136       int rc;
137       Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
138       if( iCmd==1 ){
139         rc = sqlite3rbu_close(pRbu, &zErrmsg);
140       }else{
141         rc = sqlite3rbu_close(pRbu, 0);
142       }
143       if( rc==SQLITE_OK || rc==SQLITE_DONE ){
144         Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
145         assert( zErrmsg==0 );
146       }else{
147         Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
148         if( zErrmsg ){
149           Tcl_AppendResult(interp, " - ", zErrmsg, 0);
150           sqlite3_free(zErrmsg);
151         }
152         ret = TCL_ERROR;
153       }
154       if( pTest->xRename ) Tcl_DecrRefCount(pTest->xRename);
155       ckfree(pTest);
156       break;
157     }
158 
159     case 2: /* create_rbu_delta */ {
160       sqlite3 *db = sqlite3rbu_db(pRbu, 0);
161       int rc = sqlite3_create_function(
162           db, "rbu_delta", -1, SQLITE_UTF8, (void*)interp, test_rbu_delta, 0, 0
163       );
164       Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
165       ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
166       break;
167     }
168 
169     case 3: /* savestate */ {
170       int rc = sqlite3rbu_savestate(pRbu);
171       Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
172       ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
173       break;
174     }
175 
176     case 12: /* dbRbu_eval */
177     case 4:  /* dbMain_eval */ {
178       sqlite3 *db = sqlite3rbu_db(pRbu, (iCmd==12));
179       int rc = sqlite3_exec(db, Tcl_GetString(objv[2]), 0, 0, 0);
180       if( rc!=SQLITE_OK ){
181         Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3_errmsg(db), -1));
182         ret = TCL_ERROR;
183       }
184       break;
185     }
186 
187     case 5: /* bp_progress */ {
188       int one, two;
189       Tcl_Obj *pObj;
190       sqlite3rbu_bp_progress(pRbu, &one, &two);
191 
192       pObj = Tcl_NewObj();
193       Tcl_ListObjAppendElement(interp, pObj, Tcl_NewIntObj(one));
194       Tcl_ListObjAppendElement(interp, pObj, Tcl_NewIntObj(two));
195       Tcl_SetObjResult(interp, pObj);
196       break;
197     }
198 
199     case 6: /* db */ {
200       int bArg;
201       if( Tcl_GetBooleanFromObj(interp, objv[2], &bArg) ){
202         ret = TCL_ERROR;
203       }else{
204         char zBuf[50];
205         sqlite3 *db = sqlite3rbu_db(pRbu, bArg);
206         if( sqlite3TestMakePointerStr(interp, zBuf, (void*)db) ){
207           ret = TCL_ERROR;
208         }else{
209           Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
210         }
211       }
212       break;
213     }
214     case 7: /* state */ {
215       const char *aRes[] = { 0, "oal", "move", "checkpoint", "done", "error" };
216       int eState = sqlite3rbu_state(pRbu);
217       assert( eState>0 && eState<=5 );
218       Tcl_SetResult(interp, (char*)aRes[eState], TCL_STATIC);
219       break;
220     }
221     case 8: /* progress */ {
222       sqlite3_int64 nStep =  sqlite3rbu_progress(pRbu);
223       Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep));
224       break;
225     }
226 
227     case 10: /* temp_size_limit */ {
228       sqlite3_int64 nLimit;
229       if( Tcl_GetWideIntFromObj(interp, objv[2], &nLimit) ){
230         ret = TCL_ERROR;
231       }else{
232         nLimit = sqlite3rbu_temp_size_limit(pRbu, nLimit);
233         Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nLimit));
234       }
235       break;
236     }
237     case 11: /* temp_size */ {
238       sqlite3_int64 sz = sqlite3rbu_temp_size(pRbu);
239       Tcl_SetObjResult(interp, Tcl_NewWideIntObj(sz));
240       break;
241     }
242 
243     case 13: /* rename_handler */ {
244       Tcl_Obj *pScript = objv[2];
245       assert( !sqlite3_stricmp(aCmd[13].zName, "rename_handler") );
246       if( Tcl_GetCharLength(pScript)==0 ){
247         sqlite3rbu_rename_handler(pRbu, 0, 0);
248       }else{
249         pTest->xRename = Tcl_DuplicateObj(pScript);
250         Tcl_IncrRefCount(pTest->xRename);
251         sqlite3rbu_rename_handler(pRbu, pTest, xRenameCallback);
252       }
253       break;
254     }
255 
256     default: /* seems unlikely */
257       assert( !"cannot happen" );
258       break;
259   }
260 
261   return ret;
262 }
263 
createRbuWrapper(Tcl_Interp * interp,const char * zCmd,sqlite3rbu * pRbu)264 static void createRbuWrapper(
265   Tcl_Interp *interp,
266   const char *zCmd,
267   sqlite3rbu *pRbu
268 ){
269   TestRbu *pTest = (TestRbu*)ckalloc(sizeof(TestRbu));
270   memset(pTest, 0, sizeof(TestRbu));
271   pTest->pRbu = pRbu;
272   pTest->interp = interp;
273   Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pTest, 0);
274 }
275 
276 /*
277 ** Tclcmd: sqlite3rbu CMD <target-db> <rbu-db> ?<state-db>?
278 */
test_sqlite3rbu(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])279 static int SQLITE_TCLAPI test_sqlite3rbu(
280   ClientData clientData,
281   Tcl_Interp *interp,
282   int objc,
283   Tcl_Obj *CONST objv[]
284 ){
285   sqlite3rbu *pRbu = 0;
286   const char *zCmd;
287   const char *zTarget;
288   const char *zRbu;
289   const char *zStateDb = 0;
290 
291   if( objc!=4 && objc!=5 ){
292     Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB RBU-DB ?STATE-DB?");
293     return TCL_ERROR;
294   }
295   zCmd = Tcl_GetString(objv[1]);
296   zTarget = Tcl_GetString(objv[2]);
297   zRbu = Tcl_GetString(objv[3]);
298   if( objc==5 ) zStateDb = Tcl_GetString(objv[4]);
299 
300   pRbu = sqlite3rbu_open(zTarget, zRbu, zStateDb);
301   createRbuWrapper(interp, zCmd, pRbu);
302   Tcl_SetObjResult(interp, objv[1]);
303   return TCL_OK;
304 }
305 
306 /*
307 ** Tclcmd: sqlite3rbu_vacuum CMD <target-db> <state-db>
308 */
test_sqlite3rbu_vacuum(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])309 static int SQLITE_TCLAPI test_sqlite3rbu_vacuum(
310   ClientData clientData,
311   Tcl_Interp *interp,
312   int objc,
313   Tcl_Obj *CONST objv[]
314 ){
315   sqlite3rbu *pRbu = 0;
316   const char *zCmd;
317   const char *zTarget;
318   const char *zStateDb = 0;
319 
320   if( objc!=3 && objc!=4 ){
321     Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?");
322     return TCL_ERROR;
323   }
324   zCmd = Tcl_GetString(objv[1]);
325   zTarget = Tcl_GetString(objv[2]);
326   if( objc==4 ) zStateDb = Tcl_GetString(objv[3]);
327   if( zStateDb && zStateDb[0]=='\0' ) zStateDb = 0;
328 
329   pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
330   createRbuWrapper(interp, zCmd, pRbu);
331   Tcl_SetObjResult(interp, objv[1]);
332   return TCL_OK;
333 }
334 
335 /*
336 ** Tclcmd: sqlite3rbu_create_vfs ?-default? NAME PARENT
337 */
test_sqlite3rbu_create_vfs(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])338 static int SQLITE_TCLAPI test_sqlite3rbu_create_vfs(
339   ClientData clientData,
340   Tcl_Interp *interp,
341   int objc,
342   Tcl_Obj *CONST objv[]
343 ){
344   const char *zName;
345   const char *zParent;
346   int rc;
347 
348   if( objc!=3 && objc!=4 ){
349     Tcl_WrongNumArgs(interp, 1, objv, "?-default? NAME PARENT");
350     return TCL_ERROR;
351   }
352 
353   zName = Tcl_GetString(objv[objc-2]);
354   zParent = Tcl_GetString(objv[objc-1]);
355   if( zParent[0]=='\0' ) zParent = 0;
356 
357   rc = sqlite3rbu_create_vfs(zName, zParent);
358   if( rc!=SQLITE_OK ){
359     Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
360     return TCL_ERROR;
361   }else if( objc==4 ){
362     sqlite3_vfs *pVfs = sqlite3_vfs_find(zName);
363     sqlite3_vfs_register(pVfs, 1);
364   }
365 
366   Tcl_ResetResult(interp);
367   return TCL_OK;
368 }
369 
370 /*
371 ** Tclcmd: sqlite3rbu_destroy_vfs NAME
372 */
test_sqlite3rbu_destroy_vfs(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])373 static int SQLITE_TCLAPI test_sqlite3rbu_destroy_vfs(
374   ClientData clientData,
375   Tcl_Interp *interp,
376   int objc,
377   Tcl_Obj *CONST objv[]
378 ){
379   const char *zName;
380 
381   if( objc!=2 ){
382     Tcl_WrongNumArgs(interp, 1, objv, "NAME");
383     return TCL_ERROR;
384   }
385 
386   zName = Tcl_GetString(objv[1]);
387   sqlite3rbu_destroy_vfs(zName);
388   return TCL_OK;
389 }
390 
391 /*
392 ** Tclcmd: sqlite3rbu_internal_test
393 */
test_sqlite3rbu_internal_test(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])394 static int SQLITE_TCLAPI test_sqlite3rbu_internal_test(
395   ClientData clientData,
396   Tcl_Interp *interp,
397   int objc,
398   Tcl_Obj *CONST objv[]
399 ){
400   sqlite3 *db;
401 
402   if( objc!=1 ){
403     Tcl_WrongNumArgs(interp, 1, objv, "");
404     return TCL_ERROR;
405   }
406 
407   db = sqlite3rbu_db(0, 0);
408   if( db!=0 ){
409     Tcl_AppendResult(interp, "sqlite3rbu_db(0, 0)!=0", 0);
410     return TCL_ERROR;
411   }
412 
413   return TCL_OK;
414 }
415 
SqliteRbu_Init(Tcl_Interp * interp)416 int SqliteRbu_Init(Tcl_Interp *interp){
417   static struct {
418      char *zName;
419      Tcl_ObjCmdProc *xProc;
420   } aObjCmd[] = {
421     { "sqlite3rbu", test_sqlite3rbu },
422     { "sqlite3rbu_vacuum", test_sqlite3rbu_vacuum },
423     { "sqlite3rbu_create_vfs", test_sqlite3rbu_create_vfs },
424     { "sqlite3rbu_destroy_vfs", test_sqlite3rbu_destroy_vfs },
425     { "sqlite3rbu_internal_test", test_sqlite3rbu_internal_test },
426   };
427   int i;
428   for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
429     Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
430   }
431   return TCL_OK;
432 }
433 
434 #else
435 #if defined(INCLUDE_SQLITE_TCL_H)
436 #  include "sqlite_tcl.h"
437 #else
438 #  include "tcl.h"
439 #endif
SqliteRbu_Init(Tcl_Interp * interp)440 int SqliteRbu_Init(Tcl_Interp *interp){ return TCL_OK; }
441 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
442 #endif /* defined(SQLITE_TEST) */
443