]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Unless the "--freelist-corrupt" option is specified, do not have the .recover command...
authordan <dan@noemail.net>
Thu, 25 Apr 2019 19:23:15 +0000 (19:23 +0000)
committerdan <dan@noemail.net>
Thu, 25 Apr 2019 19:23:15 +0000 (19:23 +0000)
FossilOrigin-Name: 8d2f52bb640d6d0f84b18d746043e56f45a73ace93239be1d036701f7f4018fd

manifest
manifest.uuid
src/shell.c.in

index 270106866336a5adaad6a367997675cfc9b8cf41..f7a1f6f81c8ad8ddcb2b124bb21ce6627ff900a2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sbug\spreventing\s.recover\sfrom\sworking\son\sdatabases\swhere\sthe\sfinal\spage\sof\sthe\sdb\sis\scorrupt.
-D 2019-04-25T16:20:40.499
+C Unless\sthe\s"--freelist-corrupt"\soption\sis\sspecified,\sdo\snot\shave\sthe\s.recover\scommand\sattempt\sto\srecover\sdata\sfrom\spages\sthat\sare\son\sthe\sdatabase\sfree-list.
+D 2019-04-25T19:23:15.197
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -520,7 +520,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8
 F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
 F src/select.c 9263f5c30dd44c7ac2eb29f40a7ec64322a96885b71c00de6bc30b756c2e1c49
-F src/shell.c.in 2e9b6b05fd202499bbe233632f9e5a913d2afcd645b59a114bfeb440c3e5cd38
+F src/shell.c.in a3b1bb23c3c3dde9aab1823dc37cb0f3e81debfd3937e40ee8fa69fe1f0e4c16
 F src/sqlite.h.in 38390767acc1914d58930e03149595ee4710afa4e3c43ab6c3a8aea3f1a6b8cd
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5
@@ -1821,7 +1821,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a50768314d10d743a0cc013b434b516f0763e0a6c5b79655d8fefde7de53e869
-R 9da7af39576825c9e597192b24527e0d
+P 959bbd11e92cc789973daf20dfcb8a6d8dc724dd603b286cbdd59e5d1fdb2909
+R d86f5b916c733bd261b32efe2b58444b
 U dan
-Z 0de6a10b348dda1bb213a1119e4cc094
+Z 3b2dfcc08d011cfeb383f50fbff24d3e
index e18c3e1cac95e94c180f1a98b78fc7faee802c54..37c6cf3c7d7895bd13460ab1c468ec36e8aaeece 100644 (file)
@@ -1 +1 @@
-959bbd11e92cc789973daf20dfcb8a6d8dc724dd603b286cbdd59e5d1fdb2909
\ No newline at end of file
+8d2f52bb640d6d0f84b18d746043e56f45a73ace93239be1d036701f7f4018fd
\ No newline at end of file
index 73bf9dff979e0ce8115ac882cc4d05be4fa56e90..95c18ae4d1be36fd9b0a064cf55ab07f22ab4efe 100644 (file)
@@ -3934,6 +3934,35 @@ readHexDb_error:
 }
 #endif /* SQLITE_ENABLE_DESERIALIZE */
 
+/*
+** Scalar function "shell_int32". The first argument to this function
+** must be a blob. The second a non-negative integer. This function
+** reads and returns a 32-bit big-endian integer from byte
+** offset (4*<arg2>) of the blob.
+*/
+static void shellInt32(
+  sqlite3_context *context, 
+  int argc, 
+  sqlite3_value **argv
+){
+  const unsigned char *pBlob;
+  int nBlob;
+  int iInt;
+  
+  nBlob = sqlite3_value_bytes(argv[0]);
+  pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]);
+  iInt = sqlite3_value_int(argv[1]);
+
+  if( iInt>=0 && (iInt+1)*4<=nBlob ){
+    const unsigned char *a = &pBlob[iInt*4];
+    sqlite3_int64 iVal = ((sqlite3_int64)a[0]<<24)
+                       + ((sqlite3_int64)a[1]<<16)
+                       + ((sqlite3_int64)a[2]<< 8)
+                       + ((sqlite3_int64)a[3]<< 0);
+    sqlite3_result_int64(context, iVal);
+  }
+}
+
 /*
 ** Scalar function "shell_escape_crnl" used by the .recover command.
 ** The argument passed to this function is the output of built-in
@@ -4105,6 +4134,8 @@ static void open_db(ShellState *p, int openFlags){
                             shellPutsFunc, 0, 0);
     sqlite3_create_function(p->db, "shell_escape_crnl", 1, SQLITE_UTF8, 0,
                             shellEscapeCrnl, 0, 0);
+    sqlite3_create_function(p->db, "shell_int32", 2, SQLITE_UTF8, 0,
+                            shellInt32, 0, 0);
 #ifndef SQLITE_NOHAVE_SYSTEM
     sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0,
                             editFunc, 0, 0);
@@ -6374,6 +6405,25 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
   sqlite3_stmt *pLoop = 0;        /* Loop through all root pages */
   sqlite3_stmt *pPages = 0;       /* Loop through all pages in a group */
   sqlite3_stmt *pCells = 0;       /* Loop through all cells in a page */
+  int i;
+
+  int bFreelist = 1;              /* 0 if --freelist-corrupt is specified */
+  for(i=1; i<nArg; i++){
+    char *z = azArg[i];
+    int n;
+    if( z[0]=='-' && z[1]=='-' ) z++;
+    n = strlen(z);
+    if( n<=17 && memcmp("-freelist-corrupt", z, n)==0 ){
+      bFreelist = 0;
+    }
+    else{
+      raw_printf(stderr, 
+        "unexpected option: %s - expected \"--freelist-corrupt\"\n", 
+        azArg[i]
+      );
+      return 1;
+    }
+  }
 
   shellExec(pState->db, &rc, 
     /* Attach an in-memory database named 'recovery'. Create an indexed 
@@ -6395,6 +6445,32 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
     "  SELECT child FROM recovery.dbptr GROUP BY child HAVING count(*)>1"
     ");"
 
+    "CREATE TABLE recovery.freelist(pgno INTEGER PRIMARY KEY);"
+  );
+
+  if( bFreelist ){
+    shellExec(pState->db, &rc,
+      "WITH trunk(pgno) AS ("
+      "  SELECT shell_int32("
+      "      (SELECT data FROM sqlite_dbpage WHERE pgno=1), 8) AS x "
+      "      WHERE x>0"
+      "    UNION"
+      "  SELECT shell_int32("
+      "      (SELECT data FROM sqlite_dbpage WHERE pgno=trunk.pgno), 0) AS x "
+      "      FROM trunk WHERE x>0"
+      "),"
+      "freelist(data, n, freepgno) AS ("
+      "  SELECT data, shell_int32(data, 1)-1, t.pgno "
+      "      FROM trunk t, sqlite_dbpage s WHERE s.pgno=t.pgno"
+      "    UNION ALL"
+      "  SELECT data, n-1, shell_int32(data, 2+n) "
+      "      FROM freelist WHERE n>=0"
+      ")"
+      "REPLACE INTO recovery.freelist SELECT freepgno FROM freelist;"
+    );
+  }
+
+  shellExec(pState->db, &rc, 
     /* Create the "map" table that will (eventually) contain instructions
     ** for dealing with each page in the db that contains one or more 
     ** records. */
@@ -6424,7 +6500,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
     "    )"
     "    SELECT pgno FROM p WHERE (parent IS NULL OR pgno = orig)"
     ") "
-    "FROM pages WHERE maxlen > 0;"
+    "FROM pages WHERE maxlen > 0 AND i NOT IN freelist;"
     "UPDATE recovery.map AS o SET intkey = ("
     "  SELECT substr(data, 1, 1)==X'0D' FROM sqlite_dbpage WHERE pgno=o.pgno"
     ");"