Merge support for partial integrity checks.
FossilOrigin-Name: b5443b47af95f4f0ef527bee90f186ee25faa5df197dbcc3e14d48eee095e4aa
This commit is contained in:
commit
ed109c06e1
15
manifest
15
manifest
@ -1,13 +1,14 @@
|
||||
B d2aac001204621062e6cb3230ce2ac1b4545cb83b3ebb6bfebccee4d51162e97
|
||||
C Detect\sout-of-bounds\srootpage\svalues\sin\sthe\sschema.
|
||||
D 2020-07-22T21:11:10.066
|
||||
C Merge\ssupport\sfor\spartial\sintegrity\schecks.
|
||||
D 2020-07-23T09:14:25.312
|
||||
F src/analyze.c 5cffff3d355858cd22bfc6e20ac7203510d2e1cc935086eb06f4abb2f579f628
|
||||
F src/btree.c a4720f51945a86379ecd962a715d6fe9de08651a67d1e6f7b4884612da83ceb5
|
||||
F src/btree.c 312780d344ab1c205b6571ef38757c7d5ea1cec539802cdd5a508381dd71be88
|
||||
F src/btree.h 7af72bbb4863c331c8f6753277ab40ee67d2a2125a63256d5c25489722ec162b
|
||||
F src/btreeInt.h 83166f6daeb91062b6ae9ee6247b3ad07e40eba58f3c05ba9e8dedad4ab1ea38
|
||||
F src/build.c 1b8436ed3ac339a0507e61b14e4bd823eb02b76a9499b2241fddc61a5ff38c1a
|
||||
F src/main.c eb8169cb49d36ef3481ed8f39459a4d1d61f07bd71ec26e6ee0b5da4ab73d49c
|
||||
F src/pager.c a5f65ff2cd73b8d381cc7b338cac382ca6978d578fa0b84fdaa11d3cdc3c3e18
|
||||
F src/pragma.c bdb600be936f66b9fe69d26dfbba4528beaaf4f95c479c85b328a92484e0bf71
|
||||
F src/prepare.c 28193f0b7fc377e14682c56b10b9dd75cf7e41eb25b8ff1ce5a4536e680e1193
|
||||
F src/select.c 0e75d64091200a2a8fdc02abafe176a0c2e9b2654c4cc34564f25f0b408e91de
|
||||
F src/sqliteInt.h ec260b2441d94ef0b5be424c323cf255ae30d23e2fb2bd1c42a3a59c2fbafedb
|
||||
@ -23,10 +24,10 @@ F test/corruptL.test ddd255069ec87976587956c7afc1932005d7ee5eaf4fe426a8994d945b8
|
||||
F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b321e30
|
||||
F test/fts3corrupt4.test b352268a7092e5581b1c3fb29b7f19b424fefbc1edfd3bb9ee4eeb0d4beef970
|
||||
F test/pager1.test 4fba160bf450cea19f6bf1d6483ef467545bac6405570e176c83c2c4b5d6d0d5
|
||||
F test/pragma.test 50b91bedea9324d3ab48e793f908ee7d2c7dcf84bfa2281e792838be59641ec8
|
||||
F test/tester.tcl 6417cbb60c4169804e2e1b36ce1a840c9f33d0b0d97956e058f3cc49ed3904f0
|
||||
F tool/showdb.c 49e810f5c414c792b5bf38cd5557ca9639713ebfef32aaff32faf7cb7ccce513
|
||||
P 4c5f3c6cacf84a36d0347790d98d82d1f584cd1537a13a2736348405c4d20367 d7dd4fc464c791915f646b1ad228697d1fa16f530fc7d0e9aa702c8df3068c65
|
||||
R 9f768fbf5ac24d5cfc1f04bde3af2863
|
||||
T +closed d7dd4fc464c791915f646b1ad228697d1fa16f530fc7d0e9aa702c8df3068c65
|
||||
P e4a92688fca31335bf15933dec10ecba04cf340ee2f726fd36d46d4c76660eee 65dd321432e8f80bc1cb11be8ca06656b41ac997a74a5eb271c797cf0fbb764e
|
||||
R e4d9af4b2c8b3592cb28dca1e5ada87e
|
||||
U drh
|
||||
Z 1b7e8560967702c3a371d0209708674f
|
||||
Z 84df909394c97f14dff1300a1886c42c
|
||||
|
@ -1 +1 @@
|
||||
e4a92688fca31335bf15933dec10ecba04cf340ee2f726fd36d46d4c76660eee
|
||||
b5443b47af95f4f0ef527bee90f186ee25faa5df197dbcc3e14d48eee095e4aa
|
87
src/btree.c
87
src/btree.c
@ -10120,6 +10120,15 @@ end_of_check:
|
||||
** allocation errors, an error message held in memory obtained from
|
||||
** malloc is returned if *pnErr is non-zero. If *pnErr==0 then NULL is
|
||||
** returned. If a memory allocation error occurs, NULL is returned.
|
||||
**
|
||||
** If the first entry in aRoot[] is 0, that indicates that the list of
|
||||
** root pages is incomplete. This is a "partial integrity-check". This
|
||||
** happens when performing an integrity check on a single table. The
|
||||
** zero is skipped, of course. But in addition, the freelist checks
|
||||
** and the checks to make sure every page is referenced are also skipped,
|
||||
** since obviously it is not possible to know which pages are covered by
|
||||
** the unverified btrees. Except, if aRoot[1] is 1, then the freelist
|
||||
** checks are still performed.
|
||||
*/
|
||||
char *sqlite3BtreeIntegrityCheck(
|
||||
sqlite3 *db, /* Database connection that is running the check */
|
||||
@ -10135,6 +10144,16 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
u64 savedDbFlags = pBt->db->flags;
|
||||
char zErr[100];
|
||||
VVA_ONLY( int nRef );
|
||||
int bPartial = 0; /* True if not checking all btrees */
|
||||
int bCkFreelist = 1; /* True to scan the freelist */
|
||||
assert( nRoot>0 );
|
||||
|
||||
/* aRoot[0]==0 means this is a partial check */
|
||||
if( aRoot[0]==0 ){
|
||||
assert( nRoot>1 );
|
||||
bPartial = 1;
|
||||
if( aRoot[1]!=1 ) bCkFreelist = 0;
|
||||
}
|
||||
|
||||
sqlite3BtreeEnter(p);
|
||||
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
|
||||
@ -10174,29 +10193,33 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
|
||||
/* Check the integrity of the freelist
|
||||
*/
|
||||
sCheck.zPfx = "Main freelist: ";
|
||||
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
|
||||
get4byte(&pBt->pPage1->aData[36]));
|
||||
sCheck.zPfx = 0;
|
||||
if( bCkFreelist ){
|
||||
sCheck.zPfx = "Main freelist: ";
|
||||
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
|
||||
get4byte(&pBt->pPage1->aData[36]));
|
||||
sCheck.zPfx = 0;
|
||||
}
|
||||
|
||||
/* Check all the tables.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum ){
|
||||
Pgno mx = 0;
|
||||
int mxInHdr;
|
||||
for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
|
||||
mxInHdr = get4byte(&pBt->pPage1->aData[52]);
|
||||
if( mx!=mxInHdr ){
|
||||
if( !bPartial ){
|
||||
if( pBt->autoVacuum ){
|
||||
Pgno mx = 0;
|
||||
Pgno mxInHdr;
|
||||
for(i=0; (int)i<nRoot; i++) if( mx<aRoot[i] ) mx = aRoot[i];
|
||||
mxInHdr = get4byte(&pBt->pPage1->aData[52]);
|
||||
if( mx!=mxInHdr ){
|
||||
checkAppendMsg(&sCheck,
|
||||
"max rootpage (%d) disagrees with header (%d)",
|
||||
mx, mxInHdr
|
||||
);
|
||||
}
|
||||
}else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
|
||||
checkAppendMsg(&sCheck,
|
||||
"max rootpage (%d) disagrees with header (%d)",
|
||||
mx, mxInHdr
|
||||
"incremental_vacuum enabled with a max rootpage of zero"
|
||||
);
|
||||
}
|
||||
}else if( get4byte(&pBt->pPage1->aData[64])!=0 ){
|
||||
checkAppendMsg(&sCheck,
|
||||
"incremental_vacuum enabled with a max rootpage of zero"
|
||||
);
|
||||
}
|
||||
#endif
|
||||
testcase( pBt->db->flags & SQLITE_CellSizeCk );
|
||||
@ -10205,7 +10228,7 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
i64 notUsed;
|
||||
if( aRoot[i]==0 ) continue;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum && aRoot[i]>1 ){
|
||||
if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
|
||||
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
|
||||
}
|
||||
#endif
|
||||
@ -10215,22 +10238,24 @@ char *sqlite3BtreeIntegrityCheck(
|
||||
|
||||
/* Make sure every page in the file is referenced
|
||||
*/
|
||||
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
|
||||
if( !bPartial ){
|
||||
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
|
||||
#ifdef SQLITE_OMIT_AUTOVACUUM
|
||||
if( getPageReferenced(&sCheck, i)==0 ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
if( getPageReferenced(&sCheck, i)==0 ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
#else
|
||||
/* If the database supports auto-vacuum, make sure no tables contain
|
||||
** references to pointer-map pages.
|
||||
*/
|
||||
if( getPageReferenced(&sCheck, i)==0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
if( getPageReferenced(&sCheck, i)!=0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
|
||||
/* If the database supports auto-vacuum, make sure no tables contain
|
||||
** references to pointer-map pages.
|
||||
*/
|
||||
if( getPageReferenced(&sCheck, i)==0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Page %d is never used", i);
|
||||
}
|
||||
if( getPageReferenced(&sCheck, i)!=0 &&
|
||||
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
|
||||
checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
32
src/pragma.c
32
src/pragma.c
@ -1526,9 +1526,22 @@ void sqlite3Pragma(
|
||||
** integrity_check designed to detect most database corruption
|
||||
** without the overhead of cross-checking indexes. Quick_check
|
||||
** is linear time wherease integrity_check is O(NlogN).
|
||||
**
|
||||
** The maximum nubmer of errors is 100 by default. A different default
|
||||
** can be specified using a numeric parameter N.
|
||||
**
|
||||
** Or, the parameter N can be the name of a table. In that case, only
|
||||
** the one table named is verified. The freelist is only verified if
|
||||
** the named table is "sqlite_schema" (or one of its aliases).
|
||||
**
|
||||
** All schemas are checked by default. To check just a single
|
||||
** schema, use the form:
|
||||
**
|
||||
** PRAGMA schema.integrity_check;
|
||||
*/
|
||||
case PragTyp_INTEGRITY_CHECK: {
|
||||
int i, j, addr, mxErr;
|
||||
Table *pObjTab = 0; /* Check only this one table, if not NULL */
|
||||
|
||||
int isQuick = (sqlite3Tolower(zLeft[0])=='q');
|
||||
|
||||
@ -1551,9 +1564,13 @@ void sqlite3Pragma(
|
||||
/* Set the maximum error count */
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
if( zRight ){
|
||||
sqlite3GetInt32(zRight, &mxErr);
|
||||
if( mxErr<=0 ){
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
if( sqlite3GetInt32(zRight, &mxErr) ){
|
||||
if( mxErr<=0 ){
|
||||
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
|
||||
}
|
||||
}else{
|
||||
pObjTab = sqlite3LocateTable(pParse, 0, zRight,
|
||||
iDb>=0 ? db->aDb[iDb].zDbSName : 0);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, mxErr-1, 1); /* reg[1] holds errors left */
|
||||
@ -1582,15 +1599,21 @@ void sqlite3Pragma(
|
||||
Table *pTab = sqliteHashData(x); /* Current table */
|
||||
Index *pIdx; /* An index on pTab */
|
||||
int nIdx; /* Number of indexes on pTab */
|
||||
if( pObjTab && pObjTab!=pTab ) continue;
|
||||
if( HasRowid(pTab) ) cnt++;
|
||||
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
|
||||
if( nIdx>mxIdx ) mxIdx = nIdx;
|
||||
}
|
||||
if( cnt==0 ) continue;
|
||||
if( pObjTab ) cnt++;
|
||||
aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1));
|
||||
if( aRoot==0 ) break;
|
||||
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
cnt = 0;
|
||||
if( pObjTab ) aRoot[++cnt] = 0;
|
||||
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
if( pObjTab && pObjTab!=pTab ) continue;
|
||||
if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
aRoot[++cnt] = pIdx->tnum;
|
||||
@ -1624,6 +1647,7 @@ void sqlite3Pragma(
|
||||
int r1 = -1;
|
||||
|
||||
if( pTab->tnum<1 ) continue; /* Skip VIEWs or VIRTUAL TABLEs */
|
||||
if( pObjTab && pObjTab!=pTab ) continue;
|
||||
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
|
||||
1, 0, &iDataCur, &iIdxCur);
|
||||
|
@ -387,11 +387,15 @@ ifcapable attach {
|
||||
PRAGMA integrity_check=4
|
||||
}
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2}}
|
||||
do_test pragma-3.6 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=xyz
|
||||
}
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_catchsql_test pragma-3.6 {
|
||||
PRAGMA integrity_check=xyz
|
||||
} {1 {no such table: xyz}}
|
||||
do_catchsql_test pragma-3.6b {
|
||||
PRAGMA integrity_check=t2
|
||||
} {0 {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}}
|
||||
do_catchsql_test pragma-3.6c {
|
||||
PRAGMA integrity_check=sqlite_schema
|
||||
} {0 ok}
|
||||
do_test pragma-3.7 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=0
|
||||
@ -423,7 +427,7 @@ ifcapable attach {
|
||||
do_test pragma-3.8.2 {
|
||||
execsql {PRAGMA QUICK_CHECK}
|
||||
} {ok}
|
||||
do_test pragma-3.9 {
|
||||
do_test pragma-3.9a {
|
||||
execsql {
|
||||
ATTACH 'testerr.db' AS t2;
|
||||
PRAGMA integrity_check
|
||||
@ -432,6 +436,12 @@ ifcapable attach {
|
||||
Page 4 is never used
|
||||
Page 5 is never used
|
||||
Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_execsql_test pragma-3.9b {
|
||||
PRAGMA t2.integrity_check=t2;
|
||||
} {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
|
||||
do_execsql_test pragma-3.9c {
|
||||
PRAGMA t2.integrity_check=sqlite_schema;
|
||||
} {ok}
|
||||
do_test pragma-3.10 {
|
||||
execsql {
|
||||
PRAGMA integrity_check=1
|
||||
|
Loading…
Reference in New Issue
Block a user