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:
@ -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
|
||||
|
@ -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. */
|
||||
|
Reference in New Issue
Block a user