Have "PRAGMA quick_check" compare the number of entries in tables and indexes.
[sqlite.git] / ext / recover / test_recover.c
blobba8ef6da11eec94b2493af5945ad6008aac2c8d0
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.
11 *************************************************************************
15 #include "sqlite3recover.h"
16 #include "sqliteInt.h"
18 #include <tcl.h>
19 #include <assert.h>
21 #ifndef SQLITE_OMIT_VIRTUALTABLE
23 typedef struct TestRecover TestRecover;
24 struct TestRecover {
25 sqlite3_recover *p;
26 Tcl_Interp *interp;
27 Tcl_Obj *pScript;
30 static int xSqlCallback(void *pSqlArg, const char *zSql){
31 TestRecover *p = (TestRecover*)pSqlArg;
32 Tcl_Obj *pEval = 0;
33 int res = 0;
35 pEval = Tcl_DuplicateObj(p->pScript);
36 Tcl_IncrRefCount(pEval);
38 res = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zSql, -1));
39 if( res==TCL_OK ){
40 res = Tcl_EvalObjEx(p->interp, pEval, 0);
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;
56 return res;
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;
65 *pDb = *(sqlite3 **)info.objClientData;
66 return TCL_OK;
70 ** Implementation of the command created by [sqlite3_recover_init]:
72 ** $cmd config OP ARG
73 ** $cmd run
74 ** $cmd errmsg
75 ** $cmd errcode
76 ** $cmd finalize
78 static int testRecoverCmd(
79 void *clientData,
80 Tcl_Interp *interp,
81 int objc,
82 Tcl_Obj *CONST objv[]
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 }
97 int rc = TCL_OK;
98 int iSub = 0;
99 TestRecover *pTest = (TestRecover*)clientData;
101 if( objc<2 ){
102 Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
103 return TCL_ERROR;
105 rc = Tcl_GetIndexFromObjStruct(interp,
106 objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub
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;
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 */
125 int iOp = 0;
126 int res = 0;
127 if( Tcl_GetIndexFromObj(interp, objv[2], aOp, "option", 0, &iOp) ){
128 return TCL_ERROR;
130 switch( iOp ){
131 case 0:
132 res = sqlite3_recover_config(pTest->p,
133 789, (void*)Tcl_GetString(objv[3]) /* MAGIC NUMBER! */
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)
141 break;
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
149 break;
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
157 break;
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
165 break;
167 case 5: {
168 res = sqlite3_recover_config(pTest->p, 12345, 0);
169 break;
172 Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
173 break;
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;
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;
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;
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));
197 res2 = sqlite3_recover_finish(pTest->p);
198 assert( res2==res );
199 if( res ) return TCL_ERROR;
200 break;
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;
209 return TCL_OK;
213 ** sqlite3_recover_init DB DBNAME URI
215 static int test_sqlite3_recover_init(
216 void *clientData,
217 Tcl_Interp *interp,
218 int objc,
219 Tcl_Obj *CONST objv[]
221 static int iTestRecoverCmd = 1;
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;
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;
235 if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR;
236 zDb = Tcl_GetString(objv[2]);
237 if( zDb[0]=='\0' ) zDb = 0;
239 pNew = (TestRecover*)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);
250 sprintf(zCmd, "sqlite_recover%d", iTestRecoverCmd++);
251 Tcl_CreateObjCommand(interp, zCmd, testRecoverCmd, (void*)pNew, 0);
253 Tcl_SetObjResult(interp, Tcl_NewStringObj(zCmd, -1));
254 return TCL_OK;
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.
262 #ifdef _WIN32
263 __declspec(dllexport)
264 #endif
265 int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*);
268 ** sqlite3_recover_init DB DBNAME URI
270 static int test_sqlite3_dbdata_init(
271 void *clientData,
272 Tcl_Interp *interp,
273 int objc,
274 Tcl_Obj *CONST objv[]
276 sqlite3 *db = 0;
278 if( objc!=2 ){
279 Tcl_WrongNumArgs(interp, 1, objv, "DB");
280 return TCL_ERROR;
282 if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR;
283 sqlite3_dbdata_init(db, 0, 0);
285 Tcl_ResetResult(interp);
286 return TCL_OK;
289 #endif /* SQLITE_OMIT_VIRTUALTABLE */
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 },
302 int i;
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);
308 #endif
309 return TCL_OK;