Merge support for partial integrity checks.

FossilOrigin-Name: b5443b47af95f4f0ef527bee90f186ee25faa5df197dbcc3e14d48eee095e4aa
This commit is contained in:
drh 2020-07-23 09:14:25 +00:00
commit ed109c06e1
5 changed files with 109 additions and 49 deletions

View File

@ -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

View File

@ -1 +1 @@
e4a92688fca31335bf15933dec10ecba04cf340ee2f726fd36d46d4c76660eee
b5443b47af95f4f0ef527bee90f186ee25faa5df197dbcc3e14d48eee095e4aa

View File

@ -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
}

View File

@ -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);

View File

@ -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