1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Add the ability to do a PRAGMA integrity_check (or quick_check) on a single

table by specifying the table name as the argument.

FossilOrigin-Name: 65dd321432e8f80bc1cb11be8ca06656b41ac997a74a5eb271c797cf0fbb764e
This commit is contained in:
drh
2020-07-23 00:45:06 +00:00
parent 2f04583f04
commit 17d2d592fb
5 changed files with 109 additions and 49 deletions

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 ){
int 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 ){
int 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 ){
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
}