-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
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
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
}
#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
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);
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
" 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. */
" )"
" 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"
");"