-C Remove\s"#ifdef\sSQLITE_ENABLE_FTS5"\sfrom\sindividual\sfts5\ssource\sfiles.\sAdd\sa\ssingle\s"#if\s!defined(SQLITE_CORE)\s||\sdefined(SQLITE_ENABLE_FTS5)"\sto\sfts5.c.
-D 2015-07-02T15:52:21.801
+C Smaller\sand\sfaster\sPRAGMA\sintegrity_check\sthat\salso\sdoes\sa\sbetter\sjob\sof\ndetecting\serrors.\s\sSome\soutput\stext\sdescribing\sdiscovered\sfile\scorruption\nhas\schanged\sfor\sclarity.
+D 2015-07-02T16:17:30.545
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 1f525f24e2d3a4defd0ce819c10980caeec967fe
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F src/backup.c 4d9134dc988a87838c06056c89c0e8c4700a0452
F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d
F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
-F src/btree.c e283de2f9da7ec5e47bedcd442a66a671aa2e118
+F src/btree.c 98ef3db8f90e2258eae6428cbe9cf47802b81958
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
F src/btreeInt.h 2ad754dd4528baa8d0946a593cc373b890bf859e
F src/build.c b3f15255d5b16e42dafeaa638fd4f8a47c94ed70
F test/conflict3.test dec0634c0f31dec9a4b01c63063e939f0cd21b6b
F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4
F test/corrupt.test 141c39ea650c1365e85a49e402fa05cb9617fb97
-F test/corrupt2.test f2064e0bf934124cc38868fd8badb8f0dd67b552
+F test/corrupt2.test 08cec1e5ffa68a3610306d6068f112d08bc9f090
F test/corrupt3.test 4b548d0bbe2933bc81d3f54099a05fc4d28aff18
F test/corrupt4.test b99652079d542b21f4965f6248703b983e40fe80
F test/corrupt5.test 8ead52af76006f3286e9396cb41898018ccea107
F test/corrupt6.test 269548d19427ac554c830763b1c5ea54a0252f80
-F test/corrupt7.test 22cc644c2e76c9804bc844121267aa6f8f7c0ded
+F test/corrupt7.test e4fa6d6584276679cc1d20c4e58beb9559a4eb85
F test/corrupt8.test 2399dfe40d2c0c63af86706e30f3e6302a8d0516
F test/corrupt9.test 730a3db08d4ab9aa43392ea30d9c2b4879cbff85
F test/corruptA.test 53e56dafd180addcdadb402244b8cb9771d2ba26
F test/corruptB.test 73a8d6c0b9833697ecf16b63e3c5c05c945b5dec
F test/corruptC.test 3fcc0f73d2cf2d69befe2d96332b942426a6aae2
F test/corruptD.test b3c205fac7952b1de645ce44bb02335cd9e3e040
-F test/corruptE.test 193b4ca4e927e77c1d5f4f56203ddc998432a7ee
+F test/corruptE.test be8e5088c369fc7979c662cd644efdaafc0f7f6d
F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
F test/corruptG.test 1ab3bf97ee7bdba70e0ff3ba2320657df55d1804
F test/corruptH.test 5dd4fa98c6c1ed33b178f9e8a48c4fdd3cfc9067
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P c9ddbd88998d9523e72ad910ea67eb55024b3a88
-R 6e7391ae89eb1933cb67f3ce933af577
-U dan
-Z 75f643c411d6a670d86998374f15370f
+P 7819002ed85497bbd0f9cf4d39df641573324436
+R f272e35570685d031c8340df24a1680f
+U drh
+Z 39a394fff4020992662b6a21d701df1d
static int checkTreePage(
IntegrityCk *pCheck, /* Context for the sanity check */
int iPage, /* Page number of the page to check */
- i64 *pnParentMinKey,
- i64 *pnParentMaxKey
+ i64 *piMinKey, /* Write minimum integer primary key here */
+ i64 maxKey /* Error if integer primary key greater than this */
){
- MemPage *pPage = 0;
- int i, rc, depth, d2, pgno, cnt;
- int hdr, cellStart;
- int nCell;
- u8 *data;
- BtShared *pBt;
- int usableSize;
- u32 *heap = 0;
+ MemPage *pPage = 0; /* The page being analyzed */
+ int i; /* Loop counter */
+ int rc; /* Result code from subroutine call */
+ int depth = -1, d2; /* Depth of a subtree */
+ int pgno; /* Page number */
+ int nFrag; /* Number of fragmented bytes on the page */
+ int hdr; /* Offset to the page header */
+ int cellStart; /* Offset to the start of the cell pointer array */
+ int nCell; /* Number of cells */
+ int doCoverageCheck = 1; /* True if cell coverage checking should be done */
+ int keyCanBeEqual = 1; /* True if IPK can be equal to maxKey
+ ** False if IPK must be strictly less than maxKey */
+ u8 *data; /* Page content */
+ u8 *pCell; /* Cell content */
+ u8 *pCellIdx; /* Next element of the cell pointer array */
+ BtShared *pBt; /* The BtShared object that owns pPage */
+ u32 pc; /* Address of a cell */
+ u32 usableSize; /* Usable size of the page */
+ u32 contentOffset; /* Offset to the start of the cell content area */
+ u32 *heap = 0; /* Min-heap used for checking cell coverage */
u32 x, prev = 0;
- i64 nMinKey = 0;
- i64 nMaxKey = 0;
const char *saved_zPfx = pCheck->zPfx;
int saved_v1 = pCheck->v1;
int saved_v2 = pCheck->v2;
if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
- depth = -1;
goto end_of_check;
}
assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */
checkAppendMsg(pCheck,
"btreeInitPage() returns error code %d", rc);
- depth = -1;
goto end_of_check;
}
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
- /* Check out all the cells.
- */
- depth = 0;
+ /* Set up for cell analysis */
pCheck->zPfx = "On tree page %d cell %d: ";
- for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
- u8 *pCell;
- u32 sz;
+ contentOffset = get2byteNotZero(&data[hdr+5]);
+ assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
+
+ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
+ ** number of cells on the page. */
+ nCell = get2byte(&data[hdr+3]);
+ assert( pPage->nCell==nCell );
+
+ /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
+ ** immediately follows the b-tree page header. */
+ cellStart = hdr + 12 - 4*pPage->leaf;
+ assert( pPage->aCellIdx==&data[cellStart] );
+ pCellIdx = &data[cellStart + 2*(nCell-1)];
+
+ if( !pPage->leaf ){
+ /* Analyze the right-child page of internal pages */
+ pgno = get4byte(&data[hdr+8]);
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ if( pBt->autoVacuum ){
+ pCheck->zPfx = "On page %d at right child: ";
+ checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
+ }
+#endif
+ depth = checkTreePage(pCheck, pgno, &maxKey, maxKey);
+ keyCanBeEqual = 0;
+ }else{
+ /* For leaf pages, the coverage check will occur in the same loop
+ ** as the other cell checks, so initialize the heap. */
+ heap = pCheck->heap;
+ heap[0] = 0;
+ btreeHeapInsert(heap, contentOffset-1);
+ }
+
+ /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
+ ** integer offsets to the cell contents. */
+ for(i=nCell-1; i>=0 && pCheck->mxErr; i--){
CellInfo info;
- /* Check payload overflow pages
- */
+ /* Check cell size */
pCheck->v2 = i;
- pCell = findCell(pPage,i);
+ assert( pCellIdx==&data[cellStart + i*2] );
+ pc = get2byteAligned(pCellIdx);
+ pCellIdx -= 2;
+ if( pc<contentOffset || pc>usableSize-4 ){
+ checkAppendMsg(pCheck, "Offset %d out of range %d..%d",
+ pc, contentOffset, usableSize-4);
+ doCoverageCheck = 0;
+ continue;
+ }
+ pCell = &data[pc];
pPage->xParseCell(pPage, pCell, &info);
- sz = info.nPayload;
- /* For intKey pages, check that the keys are in order.
- */
+ if( pc+info.nSize>usableSize ){
+ checkAppendMsg(pCheck, "Extends off end of page");
+ doCoverageCheck = 0;
+ continue;
+ }
+
+ /* Check for integer primary key out of range */
if( pPage->intKey ){
- if( i==0 ){
- nMinKey = nMaxKey = info.nKey;
- }else if( info.nKey <= nMaxKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
+ if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){
+ checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey);
}
- nMaxKey = info.nKey;
+ maxKey = info.nKey;
}
- if( (sz>info.nLocal)
- && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
- ){
- int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
- Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
+
+ /* Check the content overflow list */
+ if( info.nPayload>info.nLocal ){
+ int nPage; /* Number of pages on the overflow chain */
+ Pgno pgnoOvfl; /* First page of the overflow chain */
+ assert( pc + info.iOverflow <= usableSize );
+ nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
+ pgnoOvfl = get4byte(&pCell[info.iOverflow]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
checkList(pCheck, 0, pgnoOvfl, nPage);
}
- /* Check sanity of left child page.
- */
if( !pPage->leaf ){
+ /* Check sanity of left child page for internal pages */
pgno = get4byte(pCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
- d2 = checkTreePage(pCheck, pgno, &nMinKey, i==0?NULL:&nMaxKey);
- if( i>0 && d2!=depth ){
+ d2 = checkTreePage(pCheck, pgno, &maxKey, maxKey);
+ keyCanBeEqual = 0;
+ if( d2!=depth ){
checkAppendMsg(pCheck, "Child page depth differs");
+ depth = d2;
}
- depth = d2;
- }
- }
-
- if( !pPage->leaf ){
- pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- pCheck->zPfx = "On page %d at right child: ";
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
- }
-#endif
- d2 = checkTreePage(pCheck, pgno, NULL, !pPage->nCell?NULL:&nMaxKey);
- if( d2!=depth && iPage!=1 ){
- checkAppendMsg(pCheck, "Child page depth differs");
- }
- }
-
- /* For intKey leaf pages, check that the min/max keys are in order
- ** with any left/parent/right pages.
- */
- pCheck->zPfx = "Page %d: ";
- if( pPage->leaf && pPage->intKey ){
- /* if we are a left child page */
- if( pnParentMinKey ){
- /* if we are the left most child page */
- if( !pnParentMaxKey ){
- if( nMaxKey > *pnParentMinKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (max larger than parent min of %lld)",
- nMaxKey, *pnParentMinKey);
- }
- }else{
- if( nMinKey <= *pnParentMinKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (min less than parent min of %lld)",
- nMinKey, *pnParentMinKey);
- }
- if( nMaxKey > *pnParentMaxKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (max larger than parent max of %lld)",
- nMaxKey, *pnParentMaxKey);
- }
- *pnParentMinKey = nMaxKey;
- }
- /* else if we're a right child page */
- } else if( pnParentMaxKey ){
- if( nMinKey <= *pnParentMaxKey ){
- checkAppendMsg(pCheck,
- "Rowid %lld out of order (min less than parent max of %lld)",
- nMinKey, *pnParentMaxKey);
- }
+ }else{
+ /* Populate the coverage-checking heap for leaf pages */
+ btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1));
}
}
+ *piMinKey = maxKey;
/* Check for complete coverage of the page
*/
- data = pPage->aData;
- hdr = pPage->hdrOffset;
- heap = pCheck->heap;
- heap[0] = 0;
pCheck->zPfx = 0;
- {
- int contentOffset = get2byteNotZero(&data[hdr+5]);
- assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
- btreeHeapInsert(heap, contentOffset-1);
- /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
- ** number of cells on the page. */
- nCell = get2byte(&data[hdr+3]);
- /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
- ** immediately follows the b-tree page header. */
- cellStart = hdr + 12 - 4*pPage->leaf;
- /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
- ** integer offsets to the cell contents. */
- for(i=nCell-1; i>=0; i--){
- u32 pc = get2byteAligned(&data[cellStart+i*2]);
- u32 size = pPage->xCellSize(pPage, &data[pc]);
- if( (int)(pc+size-1)>=usableSize ){
- pCheck->zPfx = 0;
- checkAppendMsg(pCheck,
- "Corruption detected in cell %d on page %d",i,iPage);
- }else{
+ if( doCoverageCheck && pCheck->mxErr>0 ){
+ /* For leaf pages, the min-heap has already been initialized and the
+ ** cells have already been inserted. But for internal pages, that has
+ ** not yet been done, so do it now */
+ if( !pPage->leaf ){
+ heap = pCheck->heap;
+ heap[0] = 0;
+ btreeHeapInsert(heap, contentOffset-1);
+ for(i=nCell-1; i>=0; i--){
+ u32 pc = get2byteAligned(&data[cellStart+i*2]);
+ u32 size = pPage->xCellSize(pPage, &data[pc]);
btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
}
}
- /* EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
+ /* Add the freeblocks to the min-heap
+ **
+ ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
** is the offset of the first freeblock, or zero if there are no
- ** freeblocks on the page. */
+ ** freeblocks on the page.
+ */
i = get2byte(&data[hdr+1]);
while( i>0 ){
int size, j;
assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */
i = j;
}
- cnt = 0;
+ /* Analyze the min-heap looking for overlap between cells and/or
+ ** freeblocks, and counting the number of untracked bytes in nFrag.
+ */
+ nFrag = 0;
assert( heap[0]>0 );
assert( (heap[1]>>16)==0 );
btreeHeapPull(heap,&prev);
"Multiple uses for byte %u of page %d", x>>16, iPage);
break;
}else{
- cnt += (x>>16) - (prev&0xffff) - 1;
+ nFrag += (x>>16) - (prev&0xffff) - 1;
prev = x;
}
}
- cnt += usableSize - (prev&0xffff) - 1;
+ nFrag += usableSize - (prev&0xffff) - 1;
/* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
** is stored in the fifth field of the b-tree page header.
** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
** number of fragmented free bytes within the cell content area.
*/
- if( heap[0]==0 && cnt!=data[hdr+7] ){
+ if( heap[0]==0 && nFrag!=data[hdr+7] ){
checkAppendMsg(pCheck,
"Fragmentation of %d bytes reported as %d on page %d",
- cnt, data[hdr+7], iPage);
+ nFrag, data[hdr+7], iPage);
}
}
int *pnErr /* Write number of errors seen to this variable */
){
Pgno i;
- VVA_ONLY( int nRef );
IntegrityCk sCheck;
BtShared *pBt = p->pBt;
+ int savedDbFlags = pBt->db->flags;
char zErr[100];
+ VVA_ONLY( int nRef );
sqlite3BtreeEnter(p);
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
/* Check all the tables.
*/
+ testcase( pBt->db->flags & SQLITE_CellSizeCk );
+ pBt->db->flags &= ~SQLITE_CellSizeCk;
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
+ i64 notUsed;
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && aRoot[i]>1 ){
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
}
#endif
- checkTreePage(&sCheck, aRoot[i], NULL, NULL);
+ checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64);
}
+ pBt->db->flags = savedDbFlags;
/* Make sure every page in the file is referenced
*/