xref: /sqlite-3.40.0/ext/recover/test_recover.c (revision ffc9b1b0)
1 /*
2 ** 2022-08-27
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 
15 #include "sqlite3recover.h"
16 #include "sqliteInt.h"
17 
18 #include <tcl.h>
19 #include <assert.h>
20 
21 #ifndef SQLITE_OMIT_VIRTUALTABLE
22 
23 typedef struct TestRecover TestRecover;
24 struct TestRecover {
25   sqlite3_recover *p;
26   Tcl_Interp *interp;
27   Tcl_Obj *pScript;
28 };
29 
xSqlCallback(void * pSqlArg,const char * zSql)30 static int xSqlCallback(void *pSqlArg, const char *zSql){
31   TestRecover *p = (TestRecover*)pSqlArg;
32   Tcl_Obj *pEval = 0;
33   int res = 0;
34 
35   pEval = Tcl_DuplicateObj(p->pScript);
36   Tcl_IncrRefCount(pEval);
37 
38   res = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zSql, -1));
39   if( res==TCL_OK ){
40     res = Tcl_EvalObjEx(p->interp, pEval, 0);
41   }
42 
43   Tcl_DecrRefCount(pEval);
44   if( res ){
45     Tcl_BackgroundError(p->interp);
46     return TCL_ERROR;
47   }else{
48     Tcl_Obj *pObj = Tcl_GetObjResult(p->interp);
49     if( Tcl_GetCharLength(pObj)==0 ){
50       res = 0;
51     }else if( Tcl_GetIntFromObj(p->interp, pObj, &res) ){
52       Tcl_BackgroundError(p->interp);
53       return TCL_ERROR;
54     }
55   }
56   return res;
57 }
58 
getDbPointer(Tcl_Interp * interp,Tcl_Obj * pObj,sqlite3 ** pDb)59 static int getDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
60   Tcl_CmdInfo info;
61   if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
62     Tcl_AppendResult(interp, "no such handle: ", Tcl_GetString(pObj), 0);
63     return TCL_ERROR;
64   }
65   *pDb = *(sqlite3 **)info.objClientData;
66   return TCL_OK;
67 }
68 
69 /*
70 ** Implementation of the command created by [sqlite3_recover_init]:
71 **
72 **     $cmd config OP ARG
73 **     $cmd run
74 **     $cmd errmsg
75 **     $cmd errcode
76 **     $cmd finalize
77 */
testRecoverCmd(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])78 static int testRecoverCmd(
79   void *clientData,
80   Tcl_Interp *interp,
81   int objc,
82   Tcl_Obj *CONST objv[]
83 ){
84   static struct RecoverSub {
85     const char *zSub;
86     int nArg;
87     const char *zMsg;
88   } aSub[] = {
89     { "config",    2, "ARG"         }, /* 0 */
90     { "run",      0, ""             }, /* 1 */
91     { "errmsg",    0, ""            }, /* 2 */
92     { "errcode",   0, ""            }, /* 3 */
93     { "finish",  0, ""              }, /* 4 */
94     { "step",  0, ""                }, /* 5 */
95     { 0 }
96   };
97   int rc = TCL_OK;
98   int iSub = 0;
99   TestRecover *pTest = (TestRecover*)clientData;
100 
101   if( objc<2 ){
102     Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
103     return TCL_ERROR;
104   }
105   rc = Tcl_GetIndexFromObjStruct(interp,
106       objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
107   );
108   if( rc!=TCL_OK ) return rc;
109   if( (objc-2)!=aSub[iSub].nArg ){
110     Tcl_WrongNumArgs(interp, 2, objv, aSub[iSub].zMsg);
111     return TCL_ERROR;
112   }
113 
114   switch( iSub ){
115     case 0:  assert( sqlite3_stricmp("config", aSub[iSub].zSub)==0 ); {
116       const char *aOp[] = {
117         "testdb",          /* 0 */
118         "lostandfound",    /* 1 */
119         "freelistcorrupt", /* 2 */
120         "rowids",          /* 3 */
121         "slowindexes",     /* 4 */
122         "invalid",         /* 5 */
123         0
124       };
125       int iOp = 0;
126       int res = 0;
127       if( Tcl_GetIndexFromObj(interp, objv[2], aOp, "option", 0, &iOp) ){
128         return TCL_ERROR;
129       }
130       switch( iOp ){
131         case 0:
132           res = sqlite3_recover_config(pTest->p,
133               789, (void*)Tcl_GetString(objv[3]) /* MAGIC NUMBER! */
134           );
135           break;
136         case 1: {
137           const char *zStr = Tcl_GetString(objv[3]);
138           res = sqlite3_recover_config(pTest->p,
139               SQLITE_RECOVER_LOST_AND_FOUND, (void*)(zStr[0] ? zStr : 0)
140           );
141           break;
142         }
143         case 2: {
144           int iVal = 0;
145           if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR;
146           res = sqlite3_recover_config(pTest->p,
147               SQLITE_RECOVER_FREELIST_CORRUPT, (void*)&iVal
148           );
149           break;
150         }
151         case 3: {
152           int iVal = 0;
153           if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR;
154           res = sqlite3_recover_config(pTest->p,
155               SQLITE_RECOVER_ROWIDS, (void*)&iVal
156           );
157           break;
158         }
159         case 4: {
160           int iVal = 0;
161           if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR;
162           res = sqlite3_recover_config(pTest->p,
163               SQLITE_RECOVER_SLOWINDEXES, (void*)&iVal
164           );
165           break;
166         }
167         case 5: {
168           res = sqlite3_recover_config(pTest->p, 12345, 0);
169           break;
170         }
171       }
172       Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
173       break;
174     }
175     case 1:  assert( sqlite3_stricmp("run", aSub[iSub].zSub)==0 ); {
176       int res = sqlite3_recover_run(pTest->p);
177       Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
178       break;
179     }
180     case 2:  assert( sqlite3_stricmp("errmsg", aSub[iSub].zSub)==0 ); {
181       const char *zErr = sqlite3_recover_errmsg(pTest->p);
182       Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
183       break;
184     }
185     case 3:  assert( sqlite3_stricmp("errcode", aSub[iSub].zSub)==0 ); {
186       int errCode = sqlite3_recover_errcode(pTest->p);
187       Tcl_SetObjResult(interp, Tcl_NewIntObj(errCode));
188       break;
189     }
190     case 4:  assert( sqlite3_stricmp("finish", aSub[iSub].zSub)==0 ); {
191       int res = sqlite3_recover_errcode(pTest->p);
192       int res2;
193       if( res!=SQLITE_OK ){
194         const char *zErr = sqlite3_recover_errmsg(pTest->p);
195         Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
196       }
197       res2 = sqlite3_recover_finish(pTest->p);
198       assert( res2==res );
199       if( res ) return TCL_ERROR;
200       break;
201     }
202     case 5:  assert( sqlite3_stricmp("step", aSub[iSub].zSub)==0 ); {
203       int res = sqlite3_recover_step(pTest->p);
204       Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
205       break;
206     }
207   }
208 
209   return TCL_OK;
210 }
211 
212 /*
213 ** sqlite3_recover_init DB DBNAME URI
214 */
test_sqlite3_recover_init(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])215 static int test_sqlite3_recover_init(
216   void *clientData,
217   Tcl_Interp *interp,
218   int objc,
219   Tcl_Obj *CONST objv[]
220 ){
221   static int iTestRecoverCmd = 1;
222 
223   TestRecover *pNew = 0;
224   sqlite3 *db = 0;
225   const char *zDb = 0;
226   const char *zUri = 0;
227   char zCmd[128];
228   int bSql = clientData ? 1 : 0;
229 
230   if( objc!=4 ){
231     const char *zErr = (bSql ? "DB DBNAME SCRIPT" : "DB DBNAME URI");
232     Tcl_WrongNumArgs(interp, 1, objv, zErr);
233     return TCL_ERROR;
234   }
235   if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR;
236   zDb = Tcl_GetString(objv[2]);
237   if( zDb[0]=='\0' ) zDb = 0;
238 
239   pNew = ckalloc(sizeof(TestRecover));
240   if( bSql==0 ){
241     zUri = Tcl_GetString(objv[3]);
242     pNew->p = sqlite3_recover_init(db, zDb, zUri);
243   }else{
244     pNew->interp = interp;
245     pNew->pScript = objv[3];
246     Tcl_IncrRefCount(pNew->pScript);
247     pNew->p = sqlite3_recover_init_sql(db, zDb, xSqlCallback, (void*)pNew);
248   }
249 
250   sprintf(zCmd, "sqlite_recover%d", iTestRecoverCmd++);
251   Tcl_CreateObjCommand(interp, zCmd, testRecoverCmd, (void*)pNew, 0);
252 
253   Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
254   return TCL_OK;
255 }
256 
257 /*
258 ** Declaration for public API function in file dbdata.c. This may be called
259 ** with NULL as the final two arguments to register the sqlite_dbptr and
260 ** sqlite_dbdata virtual tables with a database handle.
261 */
262 #ifdef _WIN32
263 __declspec(dllexport)
264 #endif
265 int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*);
266 
267 /*
268 ** sqlite3_recover_init DB DBNAME URI
269 */
test_sqlite3_dbdata_init(void * clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])270 static int test_sqlite3_dbdata_init(
271   void *clientData,
272   Tcl_Interp *interp,
273   int objc,
274   Tcl_Obj *CONST objv[]
275 ){
276   sqlite3 *db = 0;
277 
278   if( objc!=2 ){
279     Tcl_WrongNumArgs(interp, 1, objv, "DB");
280     return TCL_ERROR;
281   }
282   if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR;
283   sqlite3_dbdata_init(db, 0, 0);
284 
285   Tcl_ResetResult(interp);
286   return TCL_OK;
287 }
288 
289 #endif /* SQLITE_OMIT_VIRTUALTABLE */
290 
TestRecover_Init(Tcl_Interp * interp)291 int TestRecover_Init(Tcl_Interp *interp){
292 #ifndef SQLITE_OMIT_VIRTUALTABLE
293   struct Cmd {
294     const char *zCmd;
295     Tcl_ObjCmdProc *xProc;
296     void *pArg;
297   } aCmd[] = {
298     { "sqlite3_recover_init", test_sqlite3_recover_init, 0 },
299     { "sqlite3_recover_init_sql", test_sqlite3_recover_init, (void*)1 },
300     { "sqlite3_dbdata_init", test_sqlite3_dbdata_init, (void*)1 },
301   };
302   int i;
303 
304   for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
305     struct Cmd *p = &aCmd[i];
306     Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, p->pArg, 0);
307   }
308 #endif
309   return TCL_OK;
310 }
311 
312