]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a mode to output SQL statements instead of populating a database to the recover...
authordan <Dan Kennedy>
Mon, 5 Sep 2022 21:00:22 +0000 (21:00 +0000)
committerdan <Dan Kennedy>
Mon, 5 Sep 2022 21:00:22 +0000 (21:00 +0000)
FossilOrigin-Name: 73058416e7da6581000898b7988a7010e2ce6632246f4c12b4398700c7744b83

ext/recover/recover1.test
ext/recover/recoverold.test
ext/recover/sqlite3recover.c
ext/recover/sqlite3recover.h
ext/recover/test_recover.c
manifest
manifest.uuid

index 73832c8a1244571369ff1ab3cf052ccc09be86da..167b2796bd8af2c791c2667340804768a583816d 100644 (file)
@@ -52,8 +52,27 @@ proc do_recover_test {tn} {
   sqlite3 db2 test.db2
   uplevel [list do_test $tn.2 [list compare_dbs db db2] {}]
   db2 close
+
+  forcedelete test.db2
+  forcedelete rstate.db
+
+  uplevel [list do_test $tn.3 {
+    set ::sqlhook [list]
+    set R [sqlite3_recover_init_sql db main my_sql_hook]
+    $R config testdb rstate.db
+    $R step
+    $R finish
+  } {}]
+
+  sqlite3 db2 test.db2
+  execsql [join $::sqlhook ";"] db2
+  uplevel [list do_test $tn.4 [list compare_dbs db db2] {}]
+  db2 close
 }
 
+proc my_sql_hook {sql} {
+  lappend ::sqlhook $sql
+}
 
 do_execsql_test 1.0 {
   CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
@@ -112,6 +131,7 @@ do_execsql_test 7.1 {
   SELECT * FROM t2
 } {10 11 ten}
 
+breakpoint
 do_recover_test 7.2
 
 finish_test
index 8f70a7473d0e46f3f11e226617da46a0f158d982..83f210aebcca7f9026da75c86dff8772a2f9a6ef 100644 (file)
@@ -53,13 +53,37 @@ proc do_recover_test {tn {tsql {}} {res {}}} {
   sqlite3 db2 test.db2
 
   if {$tsql==""} {
-    uplevel [list do_test $tn [list compare_dbs db db2] {}]
+    uplevel [list do_test $tn.1 [list compare_dbs db db2] {}]
   } else {
-    uplevel [list do_execsql_test -db db2 $tn $tsql $res]
+    uplevel [list do_execsql_test -db db2 $tn.1 $tsql $res]
   }
   db2 close
+
+  forcedelete test.db2
+  forcedelete rstate.db
+
+  set ::sqlhook [list]
+  set R [sqlite3_recover_init_sql db main my_sql_hook]
+  $R config lostandfound lost_and_found
+  $R step
+  $R finish
+  
+  sqlite3 db2 test.db2
+  db2 eval [join $::sqlhook ";"]
+
+  if {$tsql==""} {
+    uplevel [list do_test $tn.sql [list compare_dbs db db2] {}]
+  } else {
+    uplevel [list do_execsql_test -db db2 $tn.sql $tsql $res]
+  }
+  db2 close
+}
+
+proc my_sql_hook {sql} {
+  lappend ::sqlhook $sql
 }
 
+
 set doc {
   hello
   world
index e0eda09d8246ce8163068f58da122a88ef97718d..1014e576a4fc5efdd500a0bf1be43c8b4f502abb 100644 (file)
@@ -64,12 +64,6 @@ struct RecoverBitmap {
 ** 
 */
 #define RECOVERY_SCHEMA \
-"  CREATE TABLE recovery.freelist("                            \
-"      pgno INTEGER PRIMARY KEY"                               \
-"  );"                                                         \
-"  CREATE TABLE recovery.dbptr("                               \
-"      pgno, child, PRIMARY KEY(child, pgno)"                  \
-"  ) WITHOUT ROWID;"                                           \
 "  CREATE TABLE recovery.map("                                 \
 "      pgno INTEGER PRIMARY KEY, parent INT"                   \
 "  );"                                                         \
@@ -96,6 +90,9 @@ struct sqlite3_recover {
   char *zLostAndFound;            /* Name of lost-and-found table (or NULL) */
   int bFreelistCorrupt;
   int bRecoverRowid;
+
+  void *pSqlCtx;
+  int (*xSql)(void*,const char*);
 };
 
 /*
@@ -445,13 +442,6 @@ static int recoverOpenOutput(sqlite3_recover *p){
   return rc;
 }
 
-static int recoverCacheDbptr(sqlite3_recover *p){
-  return recoverExec(p, p->dbOut,
-    "INSERT INTO recovery.dbptr "
-    "SELECT pgno, child FROM sqlite_dbptr('getpage()')"
-  );
-}
-
 static int recoverCacheSchema(sqlite3_recover *p){
   return recoverExec(p, p->dbOut,
     "WITH RECURSIVE pages(p) AS ("
@@ -555,6 +545,15 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){
   }
 }
 
+static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){
+  if( p->errCode==SQLITE_OK && p->xSql ){
+    int res = p->xSql(p->pSqlCtx, zSql);
+    if( res ){
+      recoverError(p, SQLITE_ERROR, "callback returned an error - %d", res);
+    }
+  }
+}
+
 /*
 **
 */
@@ -581,6 +580,7 @@ static int recoverWriteSchema1(sqlite3_recover *p){
 
       int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
       if( rc==SQLITE_OK ){
+        recoverSqlCallback(p, zSql);
         if( bTable ){
           if( SQLITE_ROW==sqlite3_step(pTblname) ){
             const char *zName = sqlite3_column_text(pTblname, 0);
@@ -614,6 +614,8 @@ static int recoverWriteSchema2(sqlite3_recover *p){
       int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
       if( rc!=SQLITE_OK && rc!=SQLITE_ERROR ){
         recoverDbError(p, p->dbOut);
+      }else if( rc==SQLITE_OK ){
+        recoverSqlCallback(p, zSql);
       }
     }
   }
@@ -644,9 +646,12 @@ static sqlite3_stmt *recoverInsertStmt(
   int nField
 ){
   const char *zSep = "";
+  const char *zSqlSep = "";
   char *zSql = 0;
+  char *zFinal = 0;
   char *zBind = 0;
   int ii;
+  int bSql = p->xSql ? 1 : 0;
   sqlite3_stmt *pRet = 0;
 
   assert( nField<=pTab->nCol );
@@ -656,9 +661,16 @@ static sqlite3_stmt *recoverInsertStmt(
   if( pTab->iRowidBind ){
     assert( pTab->bIntkey );
     zSql = recoverMPrintf(p, "%z_rowid_", zSql);
-    zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind);
+    if( bSql ){
+      zBind = recoverMPrintf(p, "%zquote(?%d)", zBind, pTab->iRowidBind);
+    }else{
+      zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind);
+    }
+    zSqlSep = "||', '||";
     zSep = ", ";
   }
+
+
   for(ii=0; ii<nField; ii++){
     int eHidden = pTab->aCol[ii].eHidden;
     if( eHidden!=RECOVER_EHIDDEN_VIRTUAL
@@ -666,14 +678,31 @@ static sqlite3_stmt *recoverInsertStmt(
     ){
       assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 );
       zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol);
-      zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind);
+
+      if( bSql ){
+        zBind = recoverMPrintf(
+            p, "%z%squote(?%d)", zBind, zSqlSep, pTab->aCol[ii].iBind
+        );
+        zSqlSep = "||', '||";
+      }else{
+        zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind);
+      }
       zSep = ", ";
     }
   }
-  zSql = recoverMPrintf(p, "%z) VALUES (%z)", zSql, zBind);
 
-  pRet = recoverPrepare(p, p->dbOut, zSql);
+  if( bSql ){
+    zFinal = recoverMPrintf(p, "SELECT %Q || ') VALUES (' || %s || ')'", 
+        zSql, zBind
+    );
+  }else{
+    zFinal = recoverMPrintf(p, "%s) VALUES (%s)", zSql, zBind);
+  }
+
+  pRet = recoverPrepare(p, p->dbOut, zFinal);
   sqlite3_free(zSql);
+  sqlite3_free(zBind);
+  sqlite3_free(zFinal);
   
   return pRet;
 }
@@ -744,6 +773,7 @@ static char *recoverLostAndFoundCreate(
     sqlite3_free(zField);
 
     recoverExec(p, p->dbOut, zSql);
+    recoverSqlCallback(p, zSql);
     sqlite3_free(zSql);
   }else if( p->errCode==SQLITE_OK ){
     recoverError(
@@ -838,6 +868,7 @@ static void recoverLostAndFoundPopulate(
         sqlite3_value_free(apVal[ii]);
         apVal[ii] = 0;
       }
+      sqlite3_clear_bindings(pInsert);
       bHaveRowid = 0;
       nVal = -1;
     }
@@ -1052,7 +1083,10 @@ static int recoverWriteData(sqlite3_recover *p){
               sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid);
             }
 
-            sqlite3_step(pInsert);
+            if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){
+              const char *zSql = (const char*)sqlite3_column_text(pInsert, 0);
+              recoverSqlCallback(p, zSql);
+            }
             recoverReset(p, pInsert);
             assert( p->errCode || pInsert );
             if( pInsert ) sqlite3_clear_bindings(pInsert);
@@ -1098,10 +1132,12 @@ static int recoverWriteData(sqlite3_recover *p){
   return p->errCode;
 }
 
-sqlite3_recover *sqlite3_recover_init(
+sqlite3_recover *recoverInit(
   sqlite3* db, 
   const char *zDb, 
-  const char *zUri
+  const char *zUri,
+  int (*xSql)(void*, const char*),
+  void *pSqlCtx
 ){
   sqlite3_recover *pRet = 0;
   int nDb = 0;
@@ -1123,11 +1159,30 @@ sqlite3_recover *sqlite3_recover_init(
     pRet->zUri = &pRet->zDb[nDb+1];
     memcpy(pRet->zDb, zDb, nDb);
     memcpy(pRet->zUri, zUri, nUri);
+    pRet->xSql = xSql;
+    pRet->pSqlCtx = pSqlCtx;
   }
 
   return pRet;
 }
 
+sqlite3_recover *sqlite3_recover_init(
+  sqlite3* db, 
+  const char *zDb, 
+  const char *zUri
+){
+  return recoverInit(db, zDb, zUri, 0, 0);
+}
+
+sqlite3_recover *sqlite3_recover_init_sql(
+  sqlite3* db, 
+  const char *zDb, 
+  int (*xSql)(void*, const char*),
+  void *pSqlCtx
+){
+  return recoverInit(db, zDb, "", xSql, pSqlCtx);
+}
+
 const char *sqlite3_recover_errmsg(sqlite3_recover *p){
   return p ? p->zErrMsg : "not an error";
 }
@@ -1176,7 +1231,6 @@ static void recoverStep(sqlite3_recover *p){
 
   if( p->dbOut==0 ){
     if( recoverOpenOutput(p) ) return;
-    if( recoverCacheDbptr(p) ) return;
     if( recoverCacheSchema(p) ) return;
     if( recoverWriteSchema1(p) ) return;
     if( recoverWriteData(p) ) return;
index 0294baddaa2e33f50b71441e7d10fd5a59bd7723..5dc2a56d300e6085918c02209ccbdc2a38dbbe61 100644 (file)
@@ -24,7 +24,8 @@ extern "C" {
 
 typedef struct sqlite3_recover sqlite3_recover;
 
-/* Create an object to recover data from database zDb (e.g. "main")
+/* 
+** Create an object to recover data from database zDb (e.g. "main")
 ** opened by handle db. Data will be recovered into the database
 ** identified by parameter zUri. Database zUri is clobbered if it
 ** already exists.
@@ -35,6 +36,13 @@ sqlite3_recover *sqlite3_recover_init(
   const char *zUri
 );
 
+sqlite3_recover *sqlite3_recover_init_sql(
+  sqlite3* db, 
+  const char *zDb, 
+  int (*xSql)(void*, const char*),
+  void *pCtx
+);
+
 /* Details TBD. */
 int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg);
 
@@ -60,6 +68,8 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg);
 **   database, but often ends up "recovering" deleted records.
 **
 ** SQLITE_RECOVER_ROWIDS:
+**
+** SQLITE_RECOVER_SQLHOOK:
 */
 #define SQLITE_RECOVER_TESTDB           789
 #define SQLITE_RECOVER_LOST_AND_FOUND   790
index 0646ff78b876dd79bc33845252ac3c7c118db13b..acfcf8c7a38201a6ff580973fd6239f567dece74 100644 (file)
 typedef struct TestRecover TestRecover;
 struct TestRecover {
   sqlite3_recover *p;
+  Tcl_Interp *interp;
+  Tcl_Obj *pScript;
 };
 
+static int xSqlCallback(void *pSqlArg, const char *zSql){
+  TestRecover *p = (TestRecover*)pSqlArg;
+  Tcl_Obj *pEval = 0;
+  int res = 0;
+
+  pEval = Tcl_DuplicateObj(p->pScript);
+  Tcl_IncrRefCount(pEval);
+
+  res = Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zSql, -1));
+  if( res==TCL_OK ){
+    res = Tcl_EvalObjEx(p->interp, pEval, 0);
+  }
+
+  Tcl_DecrRefCount(pEval);
+  if( res ){
+    Tcl_BackgroundError(p->interp);
+    return TCL_ERROR;
+  }
+  return SQLITE_OK;
+}
+
 static int getDbPointer(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3 **pDb){
   Tcl_CmdInfo info;
   if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(pObj), &info) ){
@@ -171,17 +194,26 @@ static int test_sqlite3_recover_init(
   const char *zDb = 0;
   const char *zUri = 0;
   char zCmd[128];
+  int bSql = clientData ? 1 : 0;
 
   if( objc!=4 ){
-    Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME URI");
+    const char *zErr = (bSql ? "DB DBNAME SCRIPT" : "DB DBNAME URI");
+    Tcl_WrongNumArgs(interp, 1, objv, zErr);
     return TCL_ERROR;
   }
   if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR;
   zDb = Tcl_GetString(objv[2]);
-  zUri = Tcl_GetString(objv[3]);
 
   pNew = ckalloc(sizeof(TestRecover));
-  pNew->p = sqlite3_recover_init(db, zDb, zUri);
+  if( bSql==0 ){
+    zUri = Tcl_GetString(objv[3]);
+    pNew->p = sqlite3_recover_init(db, zDb, zUri);
+  }else{
+    pNew->interp = interp;
+    pNew->pScript = objv[3];
+    Tcl_IncrRefCount(pNew->pScript);
+    pNew->p = sqlite3_recover_init_sql(db, zDb, xSqlCallback, (void*)pNew);
+  }
 
   sprintf(zCmd, "sqlite_recover%d", iTestRecoverCmd++);
   Tcl_CreateObjCommand(interp, zCmd, testRecoverCmd, (void*)pNew, 0);
@@ -194,14 +226,16 @@ int TestRecover_Init(Tcl_Interp *interp){
   struct Cmd {
     const char *zCmd;
     Tcl_ObjCmdProc *xProc;
+    void *pArg;
   } aCmd[] = {
-    { "sqlite3_recover_init", test_sqlite3_recover_init },
+    { "sqlite3_recover_init", test_sqlite3_recover_init, 0 },
+    { "sqlite3_recover_init_sql", test_sqlite3_recover_init, (void*)1 },
   };
   int i;
 
   for(i=0; i<sizeof(aCmd)/sizeof(struct Cmd); i++){
     struct Cmd *p = &aCmd[i];
-    Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, 0, 0);
+    Tcl_CreateObjCommand(interp, p->zCmd, p->xProc, p->pArg, 0);
   }
 
   return TCL_OK;
index 9f01e7c919652737c821641bda18c9ed28a9546b..9244609811d292bb41cbbffbcbae8fff3a7226ba 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sthe\sSQLITE_RECOVER_ROWIDS\soption.\sTo\sspecify\sthat\srowid\svalues\sthat\sare\snot\salso\sexplicit\sINTEGER\sPRIMARY\sKEY\svalues\sshould\sbe\spreserved.
-D 2022-09-05T15:56:09.391
+C Add\sa\smode\sto\soutput\sSQL\sstatements\sinstead\sof\spopulating\sa\sdatabase\sto\sthe\srecover\sextension.
+D 2022-09-05T21:00:22.268
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -387,12 +387,12 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2
 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291
 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
-F ext/recover/recover1.test a848af8c82fe0731af835ff99475724f8654d2f24f772cc4e6f7ec4eb2ab71ea
+F ext/recover/recover1.test ae8ce9828210aa6c466bf88e23b0933849d5bb43091abe48cf2e56d636e51e93
 F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c
-F ext/recover/recoverold.test e7e00c78ec35b60488369ddf99e36a3b30e686566571969b05781e5063bdffe8
-F ext/recover/sqlite3recover.c 13e78c8a9d5521e06ebe5ac992a90169155e685f5c4b3cebc632c50b41e061c9
-F ext/recover/sqlite3recover.h ed34bc96befdf581a7de039d99772caabf0b338eb2a841d869acd9f7893ffce7
-F ext/recover/test_recover.c 9b8144ac94e6a2f3aabfcd24db6416179afdd32a3d654d1ef603c570e0384b2f
+F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074
+F ext/recover/sqlite3recover.c 47767b52f09fb1bba47009236285f09bcf68b6b5d1ec0de96b1a30b508e536a4
+F ext/recover/sqlite3recover.h 32f89b66f0235c0d94dfee0f1c3e9ed1ad726b3c4f3716ef0262b31cc33e8301
+F ext/recover/test_recover.c be0d74f0eba44fe7964e22d287dba0f3fa2baf197f630d51f0f9066af9b5eb2a
 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996
 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890
@@ -2005,8 +2005,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 253e498f5200b8b3e2bc309587af108dd1cec8a884b3d2a49d5406525c9a4b4c
-R e5ada01eb624b55d2f64c4994830b06d
+P 69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809
+R fe94b03379801a454a2e4362e6c6eea7
 U dan
-Z a7e3c99975f0369d94d22f82581262dd
+Z bbf845b5205dd27415c5eec6b9356a28
 # Remove this line to create a well-formed Fossil manifest.
index f34597368df9ebdadf9fb461902754df7c4b22f7..89f3b16ef495293cf3a224eb7a253125f51e7506 100644 (file)
@@ -1 +1 @@
-69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809
\ No newline at end of file
+73058416e7da6581000898b7988a7010e2ce6632246f4c12b4398700c7744b83
\ No newline at end of file