Have "PRAGMA quick_check" compare the number of entries in tables and indexes.
FossilOrigin-Name: b736519d3d2e93c76fa36253143f2664b9dd12e26acba555f0bf9c4d44705499
This commit is contained in:
+16
-7
@@ -10800,6 +10800,9 @@ static int checkTreePage(
|
||||
** number of cells on the page. */
|
||||
nCell = get2byte(&data[hdr+3]);
|
||||
assert( pPage->nCell==nCell );
|
||||
if( pPage->leaf || pPage->intKey==0 ){
|
||||
pCheck->nRow += nCell;
|
||||
}
|
||||
|
||||
/* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
|
||||
** immediately follows the b-tree page header. */
|
||||
@@ -11010,6 +11013,7 @@ int sqlite3BtreeIntegrityCheck(
|
||||
sqlite3 *db, /* Database connection that is running the check */
|
||||
Btree *p, /* The btree to be checked */
|
||||
Pgno *aRoot, /* An array of root pages numbers for individual trees */
|
||||
Mem *aCnt, /* Memory cells to write counts for each tree to */
|
||||
int nRoot, /* Number of entries in aRoot[] */
|
||||
int mxErr, /* Stop reporting errors after this many */
|
||||
int *pnErr, /* OUT: Write number of errors seen to this variable */
|
||||
@@ -11096,15 +11100,20 @@ int sqlite3BtreeIntegrityCheck(
|
||||
testcase( pBt->db->flags & SQLITE_CellSizeCk );
|
||||
pBt->db->flags &= ~(u64)SQLITE_CellSizeCk;
|
||||
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
|
||||
i64 notUsed;
|
||||
if( aRoot[i]==0 ) continue;
|
||||
sCheck.nRow = 0;
|
||||
if( aRoot[i] && sCheck.mxErr ){
|
||||
i64 notUsed;
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
|
||||
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
|
||||
}
|
||||
if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
|
||||
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
|
||||
}
|
||||
#endif
|
||||
sCheck.v0 = aRoot[i];
|
||||
checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64);
|
||||
sCheck.v0 = aRoot[i];
|
||||
checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64);
|
||||
}
|
||||
if( aCnt ){
|
||||
sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow);
|
||||
}
|
||||
}
|
||||
pBt->db->flags = savedDbFlags;
|
||||
|
||||
|
||||
@@ -331,6 +331,7 @@ int sqlite3BtreeIntegrityCheck(
|
||||
sqlite3 *db, /* Database connection that is running the check */
|
||||
Btree *p, /* The btree to be checked */
|
||||
Pgno *aRoot, /* An array of root pages numbers for individual trees */
|
||||
sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */
|
||||
int nRoot, /* Number of entries in aRoot[] */
|
||||
int mxErr, /* Stop reporting errors after this many */
|
||||
int *pnErr, /* OUT: Write number of errors seen to this variable */
|
||||
|
||||
@@ -707,6 +707,7 @@ struct IntegrityCk {
|
||||
StrAccum errMsg; /* Accumulate the error message text here */
|
||||
u32 *heap; /* Min-heap used for analyzing cell coverage */
|
||||
sqlite3 *db; /* Database connection running the check */
|
||||
i64 nRow; /* Number of rows visited in current tree */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+35
-19
@@ -1720,7 +1720,6 @@ void sqlite3Pragma(
|
||||
Hash *pTbls; /* Set of all tables in the schema */
|
||||
int *aRoot; /* Array of root page numbers of all btrees */
|
||||
int cnt = 0; /* Number of entries in aRoot[] */
|
||||
int mxIdx = 0; /* Maximum number of indexes for any table */
|
||||
|
||||
if( OMIT_TEMPDB && i==1 ) continue;
|
||||
if( iDb>=0 && i!=iDb ) continue;
|
||||
@@ -1742,7 +1741,6 @@ void sqlite3Pragma(
|
||||
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++;
|
||||
@@ -1762,11 +1760,11 @@ void sqlite3Pragma(
|
||||
aRoot[0] = cnt;
|
||||
|
||||
/* Make sure sufficient number of registers have been allocated */
|
||||
sqlite3TouchRegister(pParse, 8+mxIdx);
|
||||
sqlite3TouchRegister(pParse, 8+cnt);
|
||||
sqlite3ClearTempRegCache(pParse);
|
||||
|
||||
/* Do the b-tree integrity checks */
|
||||
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY);
|
||||
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
|
||||
sqlite3VdbeChangeP5(v, (u8)i);
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
||||
@@ -1776,6 +1774,36 @@ void sqlite3Pragma(
|
||||
integrityCheckResultRow(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
||||
/* Check that the indexes all have the right number of rows */
|
||||
cnt = pObjTab ? 1 : 0;
|
||||
sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
|
||||
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
int iTab = 0;
|
||||
Table *pTab = sqliteHashData(x);
|
||||
Index *pIdx;
|
||||
if( pObjTab && pObjTab!=pTab ) continue;
|
||||
if( HasRowid(pTab) ){
|
||||
iTab = cnt++;
|
||||
}else{
|
||||
iTab = cnt;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( IsPrimaryKeyIndex(pIdx) ) break;
|
||||
iTab++;
|
||||
}
|
||||
}
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->pPartIdxWhere==0 ){
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
|
||||
integrityCheckResultRow(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure all the indices are constructed correctly.
|
||||
*/
|
||||
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
|
||||
@@ -2099,21 +2127,9 @@ void sqlite3Pragma(
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, loopTop-1);
|
||||
if( !isQuick ){
|
||||
sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
if( pPk==pIdx ) continue;
|
||||
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v);
|
||||
sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
|
||||
sqlite3VdbeLoadString(v, 4, pIdx->zName);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
|
||||
integrityCheckResultRow(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
if( pPk ){
|
||||
sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
|
||||
}
|
||||
if( pPk ){
|
||||
assert( !isQuick );
|
||||
sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -7118,13 +7118,13 @@ case OP_DropTrigger: {
|
||||
/* Opcode: IntegrityCk P1 P2 P3 P4 P5
|
||||
**
|
||||
** Do an analysis of the currently open database. Store in
|
||||
** register P1 the text of an error message describing any problems.
|
||||
** If no problems are found, store a NULL in register P1.
|
||||
** register (P1+1) the text of an error message describing any problems.
|
||||
** If no problems are found, store a NULL in register (P1+1).
|
||||
**
|
||||
** The register P3 contains one less than the maximum number of allowed errors.
|
||||
** At most reg(P3) errors will be reported.
|
||||
** In other words, the analysis stops as soon as reg(P3) errors are
|
||||
** seen. Reg(P3) is updated with the number of errors remaining.
|
||||
** The register (P1) contains one less than the maximum number of allowed
|
||||
** errors. At most reg(P1) errors will be reported.
|
||||
** In other words, the analysis stops as soon as reg(P1) errors are
|
||||
** seen. Reg(P1) is updated with the number of errors remaining.
|
||||
**
|
||||
** The root page numbers of all tables in the database are integers
|
||||
** stored in P4_INTARRAY argument.
|
||||
@@ -7146,15 +7146,15 @@ case OP_IntegrityCk: {
|
||||
aRoot = pOp->p4.ai;
|
||||
assert( nRoot>0 );
|
||||
assert( aRoot[0]==(Pgno)nRoot );
|
||||
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
|
||||
pnErr = &aMem[pOp->p3];
|
||||
assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) );
|
||||
pnErr = &aMem[pOp->p1];
|
||||
assert( (pnErr->flags & MEM_Int)!=0 );
|
||||
assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
|
||||
pIn1 = &aMem[pOp->p1];
|
||||
pIn1 = &aMem[pOp->p1+1];
|
||||
assert( pOp->p5<db->nDb );
|
||||
assert( DbMaskTest(p->btreeMask, pOp->p5) );
|
||||
rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
|
||||
(int)pnErr->u.i+1, &nErr, &z);
|
||||
rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1],
|
||||
&aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z);
|
||||
sqlite3VdbeMemSetNull(pIn1);
|
||||
if( nErr==0 ){
|
||||
assert( z==0 );
|
||||
|
||||
@@ -296,6 +296,8 @@ RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
|
||||
void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
|
||||
int sqlite3VdbeHasSubProgram(Vdbe*);
|
||||
|
||||
void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val);
|
||||
|
||||
int sqlite3NotPureFunc(sqlite3_context*);
|
||||
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
|
||||
int sqlite3VdbeBytecodeVtabInit(sqlite3*);
|
||||
|
||||
@@ -943,6 +943,11 @@ void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
|
||||
}
|
||||
}
|
||||
|
||||
void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){
|
||||
aMem[iIdx].u.i = val;
|
||||
aMem[iIdx].flags = MEM_Int;
|
||||
}
|
||||
|
||||
/* A no-op destructor */
|
||||
void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user