test_recover.c 8.2 KB

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