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

More tests for the recover module.

FossilOrigin-Name: 37fb093b95c6b7d7ad07a275697df73b69f9fb5c5549aea8544b26e38f24833f
This commit is contained in:
dan
2022-09-12 19:23:50 +00:00
parent 65660916dc
commit aede9b5bd3
6 changed files with 211 additions and 51 deletions

View File

@ -0,0 +1,68 @@
# 2022 August 28
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recovercorrupt
do_execsql_test 1.0 {
PRAGMA page_size = 512;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(2, hex(randomblob(100)), randomblob(200));
CREATE INDEX i1 ON t1(b, c);
CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID;
INSERT INTO t2 VALUES(1, 2, 3);
INSERT INTO t2 VALUES(2, hex(randomblob(100)), randomblob(200));
ANALYZE;
PRAGMA writable_schema = 1;
DELETE FROM sqlite_schema WHERE name='t2';
}
do_test 1.1 {
file size test.db
} {5120}
proc toggle_bit {blob bit} {
set byte [expr {$bit / 8}]
set bit [expr {$bit & 0x0F}]
binary scan $blob a${byte}ca* A x B
set x [expr {$x ^ (1 << $bit)}]
binary format a*ca* $A $x $B
}
db_save_and_close
for {set ii 200} {$ii < 10000} {incr ii} {
db_restore_and_reopen
db func toggle_bit toggle_bit
set pg [expr {($ii / 512)+1}]
set byte [expr {$ii % 512}]
db eval {
UPDATE sqlite_dbpage SET data = toggle_bit(data, $byte) WHERE pgno=$pg
}
do_test 1.2.$ii {
set R [sqlite3_recover_init db main test.db2]
$R config lostandfound lost_and_found
$R step
$R finish
} {}
}
finish_test

View File

@ -0,0 +1,72 @@
# 2022 August 28
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source [file join [file dirname [info script]] recover_common.tcl]
source $testdir/tester.tcl
set testprefix recoverfault
#--------------------------------------------------------------------------
proc compare_result {db1 db2 sql} {
set r1 [$db1 eval $sql]
set r2 [$db2 eval $sql]
if {$r1 != $r2} {
puts "r1: $r1"
puts "r2: $r2"
error "mismatch for $sql"
}
return ""
}
proc compare_dbs {db1 db2} {
compare_result $db1 $db2 "SELECT sql FROM sqlite_master ORDER BY 1"
foreach tbl [$db1 eval {SELECT name FROM sqlite_master WHERE type='table'}] {
compare_result $db1 $db2 "SELECT * FROM $tbl"
}
}
#--------------------------------------------------------------------------
do_execsql_test 1.0 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(1, 2, 3);
INSERT INTO t1 VALUES(2, hex(randomblob(1000)), randomblob(2000));
CREATE INDEX i1 ON t1(b, c);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t2 VALUES(1, 2, 3);
INSERT INTO t2 VALUES(2, hex(randomblob(1000)), randomblob(2000));
ANALYZE;
PRAGMA writable_schema = 1;
DELETE FROM sqlite_schema WHERE name='t2';
}
do_faultsim_test 1 -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
set R [sqlite3_recover_init db main test.db2]
$R config lostandfound lost_and_found
$R step
$R finish
} -test {
faultsim_test_result {0 {}} {1 {}}
if {$testrc==0} {
sqlite3 db2 test.db2
compare_dbs db db2
db2 close
}
}
finish_test

View File

@ -226,12 +226,13 @@ static int recoverError(
int errCode,
const char *zFmt, ...
){
char *z = 0;
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
if( zFmt ){
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
sqlite3_free(p->zErrMsg);
p->zErrMsg = z;
p->errCode = errCode;
@ -448,7 +449,8 @@ static i64 recoverPageCount(sqlite3_recover *p){
if( p->errCode==SQLITE_OK ){
sqlite3_stmt *pStmt = 0;
pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb);
if( pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
if( pStmt ){
sqlite3_step(pStmt);
nPg = sqlite3_column_int64(pStmt, 0);
}
recoverFinalize(p, pStmt);
@ -866,11 +868,11 @@ static void recoverAddTable(
pNew->pNext = p->pTblList;
p->pTblList = pNew;
pNew->bIntkey = 1;
}
recoverFinalize(p, pStmt);
pNew->bIntkey = 1;
pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName);
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
int iField = sqlite3_column_int(pStmt, 0);
@ -884,10 +886,12 @@ static void recoverAddTable(
}
recoverFinalize(p, pStmt);
if( iPk>=0 ){
pNew->aCol[iPk].bIPK = 1;
}else if( pNew->bIntkey ){
pNew->iRowidBind = iBind++;
if( p->errCode==SQLITE_OK ){
if( iPk>=0 ){
pNew->aCol[iPk].bIPK = 1;
}else if( pNew->bIntkey ){
pNew->iRowidBind = iBind++;
}
}
}
}
@ -1068,6 +1072,7 @@ static sqlite3_stmt *recoverInsertStmt(
RecoverTable *pTab,
int nField
){
sqlite3_stmt *pRet = 0;
const char *zSep = "";
const char *zSqlSep = "";
char *zSql = 0;
@ -1075,7 +1080,8 @@ static sqlite3_stmt *recoverInsertStmt(
char *zBind = 0;
int ii;
int bSql = p->xSql ? 1 : 0;
sqlite3_stmt *pRet = 0;
if( nField<=0 ) return 0;
assert( nField<=pTab->nCol );
@ -1525,7 +1531,7 @@ static int recoverWriteData(sqlite3_recover *p){
int bNewCell = (iPrevPage!=iPage || iPrevCell!=iCell);
assert( bNewCell==0 || (iField==-1 || iField==0) );
assert( bNewCell || iField==nVal );
assert( bNewCell || iField==nVal || nVal==pTab->nCol );
if( bNewCell ){
if( nVal>=0 ){
@ -1538,29 +1544,31 @@ static int recoverWriteData(sqlite3_recover *p){
pInsert = recoverInsertStmt(p, pTab, nVal);
nInsert = nVal;
}
if( nVal>0 ){
for(ii=0; ii<pTab->nCol; ii++){
RecoverColumn *pCol = &pTab->aCol[ii];
for(ii=0; ii<pTab->nCol; ii++){
RecoverColumn *pCol = &pTab->aCol[ii];
if( pCol->iBind>0 ){
if( pCol->bIPK ){
sqlite3_bind_int64(pInsert, pCol->iBind, iRowid);
}else if( pCol->iField<nVal ){
sqlite3_bind_value(pInsert,pCol->iBind,apVal[pCol->iField]);
if( pCol->iBind>0 ){
int iBind = pCol->iBind;
if( pCol->bIPK ){
sqlite3_bind_int64(pInsert, iBind, iRowid);
}else if( pCol->iField<nVal ){
sqlite3_bind_value(pInsert, iBind, apVal[pCol->iField]);
}
}
}
}
if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){
sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid);
}
if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){
sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid);
}
if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){
const char *zSql = (const char*)sqlite3_column_text(pInsert, 0);
recoverSqlCallback(p, zSql);
if( SQLITE_ROW==sqlite3_step(pInsert) && p->xSql ){
const char *z = (const char*)sqlite3_column_text(pInsert, 0);
recoverSqlCallback(p, z);
}
recoverReset(p, pInsert);
assert( p->errCode || pInsert );
if( pInsert ) sqlite3_clear_bindings(pInsert);
}
recoverReset(p, pInsert);
assert( p->errCode || pInsert );
if( pInsert ) sqlite3_clear_bindings(pInsert);
}
for(ii=0; ii<nVal; ii++){
@ -1577,9 +1585,12 @@ static int recoverWriteData(sqlite3_recover *p){
assert( nVal==-1 );
nVal = 0;
bHaveRowid = 1;
}else if( iField<nMax ){
}else if( iField<pTab->nCol ){
assert( apVal[iField]==0 );
apVal[iField] = sqlite3_value_dup( pVal );
if( apVal[iField]==0 ){
recoverError(p, SQLITE_NOMEM, 0);
}
nVal = iField+1;
}
iPrevCell = iCell;
@ -1686,7 +1697,6 @@ sqlite3_recover *recoverInit(
int nByte = 0;
if( zDb==0 ){ zDb = "main"; }
if( zUri==0 ){ zUri = ""; }
nDb = recoverStrlen(zDb);
nUri = recoverStrlen(zUri);
@ -1699,7 +1709,7 @@ sqlite3_recover *recoverInit(
pRet->zDb = (char*)&pRet[1];
pRet->zUri = &pRet->zDb[nDb+1];
memcpy(pRet->zDb, zDb, nDb);
memcpy(pRet->zUri, zUri, nUri);
if( nUri>0 ) memcpy(pRet->zUri, zUri, nUri);
pRet->xSql = xSql;
pRet->pSqlCtx = pSqlCtx;
pRet->bRecoverRowid = RECOVER_ROWID_DEFAULT;
@ -1730,14 +1740,14 @@ sqlite3_recover *sqlite3_recover_init_sql(
int (*xSql)(void*, const char*),
void *pSqlCtx
){
return recoverInit(db, zDb, "", xSql, pSqlCtx);
return recoverInit(db, zDb, 0, xSql, pSqlCtx);
}
/*
** Return the handle error message, if any.
*/
const char *sqlite3_recover_errmsg(sqlite3_recover *p){
return p ? p->zErrMsg : "not an error";
return (p && p->errCode!=SQLITE_NOMEM) ? p->zErrMsg : "out of memory";
}
/*
@ -1753,6 +1763,7 @@ int sqlite3_recover_errcode(sqlite3_recover *p){
int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){
int rc = SQLITE_OK;
if( p==0 ) return SQLITE_NOMEM;
switch( op ){
case 789:
sqlite3_free(p->zStateDb);
@ -1791,8 +1802,12 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){
*/
int sqlite3_recover_run(sqlite3_recover *p){
if( p ){
recoverExec(p, p->dbIn, "PRAGMA writable_schema=1");
if( p->bRun ) return SQLITE_MISUSE; /* Has already run */
if( p->errCode==SQLITE_OK ) recoverRun(p);
if( sqlite3_exec(p->dbIn, "PRAGMA writable_schema=0", 0, 0, 0) ){
recoverDbError(p, p->dbIn);
}
}
return p ? p->errCode : SQLITE_NOMEM;
}
@ -1807,11 +1822,16 @@ int sqlite3_recover_run(sqlite3_recover *p){
** not been called on this handle.
*/
int sqlite3_recover_finish(sqlite3_recover *p){
int rc = p->errCode;
sqlite3_free(p->zErrMsg);
sqlite3_free(p->zStateDb);
sqlite3_free(p->zLostAndFound);
sqlite3_free(p);
int rc;
if( p==0 ){
rc = SQLITE_NOMEM;
}else{
rc = p->errCode;
sqlite3_free(p->zErrMsg);
sqlite3_free(p->zStateDb);
sqlite3_free(p->zLostAndFound);
sqlite3_free(p);
}
return rc;
}

View File

@ -165,9 +165,7 @@ static int testRecoverCmd(
int res2;
if( res!=SQLITE_OK ){
const char *zErr = sqlite3_recover_errmsg(pTest->p);
char *zRes = sqlite3_mprintf("(%d) - %s", res, zErr);
Tcl_SetObjResult(interp, Tcl_NewStringObj(zRes, -1));
sqlite3_free(zRes);
Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1));
}
res2 = sqlite3_recover_finish(pTest->p);
assert( res2==res );