1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Take the freelist into account when recovering data that is not linked in to any tree associated with a schema entry.

FossilOrigin-Name: dbd1f1efb349a9c8886e42b3f07d3f4c576924136f111558c7294d0a272e415a
This commit is contained in:
dan
2022-09-03 20:31:36 +00:00
parent f2f8a3a348
commit be2e212cf9
4 changed files with 86 additions and 11 deletions

View File

@ -131,6 +131,23 @@ do_recover_test 2.4.1 {
2 2 3 {} 8 9 7
}
do_execsql_test 2.5 {
CREATE TABLE x1(a, b, c);
WITH s(i) AS (
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
)
INSERT INTO x1 SELECT i, i, hex(randomblob(500)) FROM s;
DROP TABLE x1;
}
do_recover_test 2.5.1 {
SELECT name FROM sqlite_master;
SELECT * FROM lost_and_found_1;
} {lost_and_found lost_and_found_0 lost_and_found_1
2 2 3 {} 2 3 1
2 2 3 {} 5 6 4
2 2 3 {} 8 9 7
}
#-------------------------------------------------------------------------
breakpoint
reset_db

View File

@ -107,7 +107,7 @@ static int recoverStrlen(const char *zStr){
return nRet;
}
static void *recoverMalloc(sqlite3_recover *p, sqlite3_int64 nByte){
static void *recoverMalloc(sqlite3_recover *p, i64 nByte){
void *pRet = 0;
assert( nByte>0 );
if( p->errCode==SQLITE_OK ){
@ -276,6 +276,36 @@ static i64 recoverPageCount(sqlite3_recover *p){
return nPg;
}
/*
** Scalar function "read_i32". 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 recoverReadI32(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const unsigned char *pBlob;
int nBlob;
int iInt;
assert( argc==2 );
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];
i64 iVal = ((i64)a[0]<<24)
+ ((i64)a[1]<<16)
+ ((i64)a[2]<< 8)
+ ((i64)a[3]<< 0);
sqlite3_result_int64(context, iVal);
}
}
/*
** SELECT page_is_used(pgno);
*/
@ -285,7 +315,7 @@ static void recoverPageIsUsed(
sqlite3_value **apArg
){
sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx);
sqlite3_int64 pgno = sqlite3_value_int64(apArg[0]);
i64 pgno = sqlite3_value_int64(apArg[0]);
sqlite3_stmt *pStmt = 0;
int bRet;
@ -314,7 +344,7 @@ static void recoverGetPage(
sqlite3_value **apArg
){
sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx);
sqlite3_int64 pgno = sqlite3_value_int64(apArg[0]);
i64 pgno = sqlite3_value_int64(apArg[0]);
sqlite3_stmt *pStmt = 0;
assert( nArg==1 );
@ -397,6 +427,11 @@ static int recoverOpenOutput(sqlite3_recover *p){
db, "page_is_used", 1, SQLITE_UTF8, (void*)p, recoverPageIsUsed, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "read_i32", 2, SQLITE_UTF8, (void*)p, recoverReadI32, 0, 0
);
}
if( rc!=SQLITE_OK ){
if( p->errCode==SQLITE_OK ) rc = recoverDbError(p, db);
@ -852,6 +887,29 @@ static int recoverLostAndFound(sqlite3_recover *p){
}
recoverFinalize(p, pStmt);
/* Add all pages that appear to be part of the freelist to the bitmap. */
pStmt = recoverPrepare(p, p->dbOut,
"WITH trunk(pgno) AS ("
" SELECT read_i32(getpage(1), 8) AS x WHERE x>0"
" UNION"
" SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0"
"),"
"trunkdata(pgno, data) AS ("
" SELECT pgno, getpage(pgno) FROM trunk"
"),"
"freelist(data, n, freepgno) AS ("
" SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata"
" UNION ALL"
" SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0"
")"
"SELECT freepgno FROM freelist"
);
while( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
i64 iPg = sqlite3_column_int64(pStmt, 0);
recoverBitmapSet(pMap, iPg);
}
recoverFinalize(p, pStmt);
/* Add an entry for each page not already added to the bitmap to
** the recovery.map table. This loop leaves the "parent" column
** of each recovery.map row set to NULL - to be filled in below. */