/ Check-in [251a7590]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Smaller and faster PRAGMA integrity_check that also does a better job of detecting errors. Some output text describing discovered file corruption has changed for clarity.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 251a7590ff4f65f59a1c871892533e4e2c544515
User & Date: drh 2015-07-02 16:17:30
Context
2015-07-02
16:29
Fix a (harmless) shadowed local variable definition in the integrity_check logic. check-in: 3a26a919 user: drh tags: trunk
16:17
Smaller and faster PRAGMA integrity_check that also does a better job of detecting errors. Some output text describing discovered file corruption has changed for clarity. check-in: 251a7590 user: drh tags: trunk
15:52
Remove "#ifdef SQLITE_ENABLE_FTS5" from individual fts5 source files. Add a single "#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5)" to fts5.c. check-in: 7819002e user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

  8928   8928   **      3.  Check the integrity of overflow pages.
  8929   8929   **      4.  Recursively call checkTreePage on all children.
  8930   8930   **      5.  Verify that the depth of all children is the same.
  8931   8931   */
  8932   8932   static int checkTreePage(
  8933   8933     IntegrityCk *pCheck,  /* Context for the sanity check */
  8934   8934     int iPage,            /* Page number of the page to check */
  8935         -  i64 *pnParentMinKey, 
  8936         -  i64 *pnParentMaxKey
         8935  +  i64 *piMinKey,        /* Write minimum integer primary key here */
         8936  +  i64 maxKey            /* Error if integer primary key greater than this */
  8937   8937   ){
  8938         -  MemPage *pPage = 0;
  8939         -  int i, rc, depth, d2, pgno, cnt;
  8940         -  int hdr, cellStart;
  8941         -  int nCell;
  8942         -  u8 *data;
  8943         -  BtShared *pBt;
  8944         -  int usableSize;
  8945         -  u32 *heap = 0;
         8938  +  MemPage *pPage = 0;      /* The page being analyzed */
         8939  +  int i;                   /* Loop counter */
         8940  +  int rc;                  /* Result code from subroutine call */
         8941  +  int depth = -1, d2;      /* Depth of a subtree */
         8942  +  int pgno;                /* Page number */
         8943  +  int nFrag;               /* Number of fragmented bytes on the page */
         8944  +  int hdr;                 /* Offset to the page header */
         8945  +  int cellStart;           /* Offset to the start of the cell pointer array */
         8946  +  int nCell;               /* Number of cells */
         8947  +  int doCoverageCheck = 1; /* True if cell coverage checking should be done */
         8948  +  int keyCanBeEqual = 1;   /* True if IPK can be equal to maxKey
         8949  +                           ** False if IPK must be strictly less than maxKey */
         8950  +  u8 *data;                /* Page content */
         8951  +  u8 *pCell;               /* Cell content */
         8952  +  u8 *pCellIdx;            /* Next element of the cell pointer array */
         8953  +  BtShared *pBt;           /* The BtShared object that owns pPage */
         8954  +  u32 pc;                  /* Address of a cell */
         8955  +  u32 usableSize;          /* Usable size of the page */
         8956  +  u32 contentOffset;       /* Offset to the start of the cell content area */
         8957  +  u32 *heap = 0;           /* Min-heap used for checking cell coverage */
  8946   8958     u32 x, prev = 0;
  8947         -  i64 nMinKey = 0;
  8948         -  i64 nMaxKey = 0;
  8949   8959     const char *saved_zPfx = pCheck->zPfx;
  8950   8960     int saved_v1 = pCheck->v1;
  8951   8961     int saved_v2 = pCheck->v2;
  8952   8962   
  8953   8963     /* Check that the page exists
  8954   8964     */
  8955   8965     pBt = pCheck->pBt;
................................................................................
  8957   8967     if( iPage==0 ) return 0;
  8958   8968     if( checkRef(pCheck, iPage) ) return 0;
  8959   8969     pCheck->zPfx = "Page %d: ";
  8960   8970     pCheck->v1 = iPage;
  8961   8971     if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
  8962   8972       checkAppendMsg(pCheck,
  8963   8973          "unable to get the page. error code=%d", rc);
  8964         -    depth = -1;
  8965   8974       goto end_of_check;
  8966   8975     }
  8967   8976   
  8968   8977     /* Clear MemPage.isInit to make sure the corruption detection code in
  8969   8978     ** btreeInitPage() is executed.  */
  8970   8979     pPage->isInit = 0;
  8971   8980     if( (rc = btreeInitPage(pPage))!=0 ){
  8972   8981       assert( rc==SQLITE_CORRUPT );  /* The only possible error from InitPage */
  8973   8982       checkAppendMsg(pCheck,
  8974   8983                      "btreeInitPage() returns error code %d", rc);
  8975         -    depth = -1;
  8976   8984       goto end_of_check;
  8977   8985     }
         8986  +  data = pPage->aData;
         8987  +  hdr = pPage->hdrOffset;
  8978   8988   
  8979         -  /* Check out all the cells.
  8980         -  */
  8981         -  depth = 0;
         8989  +  /* Set up for cell analysis */
  8982   8990     pCheck->zPfx = "On tree page %d cell %d: ";
  8983         -  for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
  8984         -    u8 *pCell;
  8985         -    u32 sz;
         8991  +  contentOffset = get2byteNotZero(&data[hdr+5]);
         8992  +  assert( contentOffset<=usableSize );  /* Enforced by btreeInitPage() */
         8993  +
         8994  +  /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
         8995  +  ** number of cells on the page. */
         8996  +  nCell = get2byte(&data[hdr+3]);
         8997  +  assert( pPage->nCell==nCell );
         8998  +
         8999  +  /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
         9000  +  ** immediately follows the b-tree page header. */
         9001  +  cellStart = hdr + 12 - 4*pPage->leaf;
         9002  +  assert( pPage->aCellIdx==&data[cellStart] );
         9003  +  pCellIdx = &data[cellStart + 2*(nCell-1)];
         9004  +
         9005  +  if( !pPage->leaf ){
         9006  +    /* Analyze the right-child page of internal pages */
         9007  +    pgno = get4byte(&data[hdr+8]);
         9008  +#ifndef SQLITE_OMIT_AUTOVACUUM
         9009  +    if( pBt->autoVacuum ){
         9010  +      pCheck->zPfx = "On page %d at right child: ";
         9011  +      checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
         9012  +    }
         9013  +#endif
         9014  +    depth = checkTreePage(pCheck, pgno, &maxKey, maxKey);
         9015  +    keyCanBeEqual = 0;
         9016  +  }else{
         9017  +    /* For leaf pages, the coverage check will occur in the same loop
         9018  +    ** as the other cell checks, so initialize the heap.  */
         9019  +    heap = pCheck->heap;
         9020  +    heap[0] = 0;
         9021  +    btreeHeapInsert(heap, contentOffset-1);
         9022  +  }
         9023  +
         9024  +  /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
         9025  +  ** integer offsets to the cell contents. */
         9026  +  for(i=nCell-1; i>=0 && pCheck->mxErr; i--){
  8986   9027       CellInfo info;
  8987   9028   
  8988         -    /* Check payload overflow pages
  8989         -    */
         9029  +    /* Check cell size */
  8990   9030       pCheck->v2 = i;
  8991         -    pCell = findCell(pPage,i);
         9031  +    assert( pCellIdx==&data[cellStart + i*2] );
         9032  +    pc = get2byteAligned(pCellIdx);
         9033  +    pCellIdx -= 2;
         9034  +    if( pc<contentOffset || pc>usableSize-4 ){
         9035  +      checkAppendMsg(pCheck, "Offset %d out of range %d..%d",
         9036  +                             pc, contentOffset, usableSize-4);
         9037  +      doCoverageCheck = 0;
         9038  +      continue;
         9039  +    }
         9040  +    pCell = &data[pc];
  8992   9041       pPage->xParseCell(pPage, pCell, &info);
  8993         -    sz = info.nPayload;
  8994         -    /* For intKey pages, check that the keys are in order.
  8995         -    */
         9042  +    if( pc+info.nSize>usableSize ){
         9043  +      checkAppendMsg(pCheck, "Extends off end of page");
         9044  +      doCoverageCheck = 0;
         9045  +      continue;
         9046  +    }
         9047  +
         9048  +    /* Check for integer primary key out of range */
  8996   9049       if( pPage->intKey ){
  8997         -      if( i==0 ){
  8998         -        nMinKey = nMaxKey = info.nKey;
  8999         -      }else if( info.nKey <= nMaxKey ){
  9000         -        checkAppendMsg(pCheck,
  9001         -           "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
         9050  +      if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){
         9051  +        checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey);
  9002   9052         }
  9003         -      nMaxKey = info.nKey;
         9053  +      maxKey = info.nKey;
  9004   9054       }
  9005         -    if( (sz>info.nLocal) 
  9006         -     && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
  9007         -    ){
  9008         -      int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
  9009         -      Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
         9055  +
         9056  +    /* Check the content overflow list */
         9057  +    if( info.nPayload>info.nLocal ){
         9058  +      int nPage;       /* Number of pages on the overflow chain */
         9059  +      Pgno pgnoOvfl;   /* First page of the overflow chain */
         9060  +      assert( pc + info.iOverflow <= usableSize );
         9061  +      nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
         9062  +      pgnoOvfl = get4byte(&pCell[info.iOverflow]);
  9010   9063   #ifndef SQLITE_OMIT_AUTOVACUUM
  9011   9064         if( pBt->autoVacuum ){
  9012   9065           checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
  9013   9066         }
  9014   9067   #endif
  9015   9068         checkList(pCheck, 0, pgnoOvfl, nPage);
  9016   9069       }
  9017   9070   
  9018         -    /* Check sanity of left child page.
  9019         -    */
  9020   9071       if( !pPage->leaf ){
         9072  +      /* Check sanity of left child page for internal pages */
  9021   9073         pgno = get4byte(pCell);
  9022   9074   #ifndef SQLITE_OMIT_AUTOVACUUM
  9023   9075         if( pBt->autoVacuum ){
  9024   9076           checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
  9025   9077         }
  9026   9078   #endif
  9027         -      d2 = checkTreePage(pCheck, pgno, &nMinKey, i==0?NULL:&nMaxKey);
  9028         -      if( i>0 && d2!=depth ){
         9079  +      d2 = checkTreePage(pCheck, pgno, &maxKey, maxKey);
         9080  +      keyCanBeEqual = 0;
         9081  +      if( d2!=depth ){
  9029   9082           checkAppendMsg(pCheck, "Child page depth differs");
         9083  +        depth = d2;
  9030   9084         }
  9031         -      depth = d2;
  9032         -    }
  9033         -  }
  9034         -
  9035         -  if( !pPage->leaf ){
  9036         -    pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
  9037         -    pCheck->zPfx = "On page %d at right child: ";
  9038         -#ifndef SQLITE_OMIT_AUTOVACUUM
  9039         -    if( pBt->autoVacuum ){
  9040         -      checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
  9041         -    }
  9042         -#endif
  9043         -    d2 = checkTreePage(pCheck, pgno, NULL, !pPage->nCell?NULL:&nMaxKey);
  9044         -    if( d2!=depth && iPage!=1 ){
  9045         -      checkAppendMsg(pCheck, "Child page depth differs");
         9085  +    }else{
         9086  +      /* Populate the coverage-checking heap for leaf pages */
         9087  +      btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1));
  9046   9088       }
  9047   9089     }
  9048         - 
  9049         -  /* For intKey leaf pages, check that the min/max keys are in order
  9050         -  ** with any left/parent/right pages.
  9051         -  */
  9052         -  pCheck->zPfx = "Page %d: ";
  9053         -  if( pPage->leaf && pPage->intKey ){
  9054         -    /* if we are a left child page */
  9055         -    if( pnParentMinKey ){
  9056         -      /* if we are the left most child page */
  9057         -      if( !pnParentMaxKey ){
  9058         -        if( nMaxKey > *pnParentMinKey ){
  9059         -          checkAppendMsg(pCheck,
  9060         -              "Rowid %lld out of order (max larger than parent min of %lld)",
  9061         -              nMaxKey, *pnParentMinKey);
  9062         -        }
  9063         -      }else{
  9064         -        if( nMinKey <= *pnParentMinKey ){
  9065         -          checkAppendMsg(pCheck,
  9066         -              "Rowid %lld out of order (min less than parent min of %lld)",
  9067         -              nMinKey, *pnParentMinKey);
  9068         -        }
  9069         -        if( nMaxKey > *pnParentMaxKey ){
  9070         -          checkAppendMsg(pCheck,
  9071         -              "Rowid %lld out of order (max larger than parent max of %lld)",
  9072         -              nMaxKey, *pnParentMaxKey);
  9073         -        }
  9074         -        *pnParentMinKey = nMaxKey;
  9075         -      }
  9076         -    /* else if we're a right child page */
  9077         -    } else if( pnParentMaxKey ){
  9078         -      if( nMinKey <= *pnParentMaxKey ){
  9079         -        checkAppendMsg(pCheck,
  9080         -            "Rowid %lld out of order (min less than parent max of %lld)",
  9081         -            nMinKey, *pnParentMaxKey);
  9082         -      }
  9083         -    }
  9084         -  }
         9090  +  *piMinKey = maxKey;
  9085   9091   
  9086   9092     /* Check for complete coverage of the page
  9087   9093     */
  9088         -  data = pPage->aData;
  9089         -  hdr = pPage->hdrOffset;
  9090         -  heap = pCheck->heap;
  9091         -  heap[0] = 0;
  9092   9094     pCheck->zPfx = 0;
  9093         -  {
  9094         -    int contentOffset = get2byteNotZero(&data[hdr+5]);
  9095         -    assert( contentOffset<=usableSize );  /* Enforced by btreeInitPage() */
  9096         -    btreeHeapInsert(heap, contentOffset-1);
  9097         -    /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
  9098         -    ** number of cells on the page. */
  9099         -    nCell = get2byte(&data[hdr+3]);
  9100         -    /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
  9101         -    ** immediately follows the b-tree page header. */
  9102         -    cellStart = hdr + 12 - 4*pPage->leaf;
  9103         -    /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
  9104         -    ** integer offsets to the cell contents. */
  9105         -    for(i=nCell-1; i>=0; i--){
  9106         -      u32 pc = get2byteAligned(&data[cellStart+i*2]);
  9107         -      u32 size = pPage->xCellSize(pPage, &data[pc]);
  9108         -      if( (int)(pc+size-1)>=usableSize ){
  9109         -        pCheck->zPfx = 0;
  9110         -        checkAppendMsg(pCheck,
  9111         -            "Corruption detected in cell %d on page %d",i,iPage);
  9112         -      }else{
         9095  +  if( doCoverageCheck && pCheck->mxErr>0 ){
         9096  +    /* For leaf pages, the min-heap has already been initialized and the
         9097  +    ** cells have already been inserted.  But for internal pages, that has
         9098  +    ** not yet been done, so do it now */
         9099  +    if( !pPage->leaf ){
         9100  +      heap = pCheck->heap;
         9101  +      heap[0] = 0;
         9102  +      btreeHeapInsert(heap, contentOffset-1);
         9103  +      for(i=nCell-1; i>=0; i--){
         9104  +        u32 pc = get2byteAligned(&data[cellStart+i*2]);
         9105  +        u32 size = pPage->xCellSize(pPage, &data[pc]);
  9113   9106           btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
  9114   9107         }
  9115   9108       }
  9116         -    /* EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
         9109  +    /* Add the freeblocks to the min-heap
         9110  +    **
         9111  +    ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
  9117   9112       ** is the offset of the first freeblock, or zero if there are no
  9118         -    ** freeblocks on the page. */
         9113  +    ** freeblocks on the page. 
         9114  +    */
  9119   9115       i = get2byte(&data[hdr+1]);
  9120   9116       while( i>0 ){
  9121   9117         int size, j;
  9122   9118         assert( i<=usableSize-4 );     /* Enforced by btreeInitPage() */
  9123   9119         size = get2byte(&data[i+2]);
  9124   9120         assert( i+size<=usableSize );  /* Enforced by btreeInitPage() */
  9125   9121         btreeHeapInsert(heap, (i<<16)|(i+size-1));
................................................................................
  9130   9126         j = get2byte(&data[i]);
  9131   9127         /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
  9132   9128         ** increasing offset. */
  9133   9129         assert( j==0 || j>i+size );  /* Enforced by btreeInitPage() */
  9134   9130         assert( j<=usableSize-4 );   /* Enforced by btreeInitPage() */
  9135   9131         i = j;
  9136   9132       }
  9137         -    cnt = 0;
         9133  +    /* Analyze the min-heap looking for overlap between cells and/or 
         9134  +    ** freeblocks, and counting the number of untracked bytes in nFrag.
         9135  +    */
         9136  +    nFrag = 0;
  9138   9137       assert( heap[0]>0 );
  9139   9138       assert( (heap[1]>>16)==0 );
  9140   9139       btreeHeapPull(heap,&prev);
  9141   9140       while( btreeHeapPull(heap,&x) ){
  9142   9141         if( (prev&0xffff)+1>(x>>16) ){
  9143   9142           checkAppendMsg(pCheck,
  9144   9143             "Multiple uses for byte %u of page %d", x>>16, iPage);
  9145   9144           break;
  9146   9145         }else{
  9147         -        cnt += (x>>16) - (prev&0xffff) - 1;
         9146  +        nFrag += (x>>16) - (prev&0xffff) - 1;
  9148   9147           prev = x;
  9149   9148         }
  9150   9149       }
  9151         -    cnt += usableSize - (prev&0xffff) - 1;
         9150  +    nFrag += usableSize - (prev&0xffff) - 1;
  9152   9151       /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
  9153   9152       ** is stored in the fifth field of the b-tree page header.
  9154   9153       ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
  9155   9154       ** number of fragmented free bytes within the cell content area.
  9156   9155       */
  9157         -    if( heap[0]==0 && cnt!=data[hdr+7] ){
         9156  +    if( heap[0]==0 && nFrag!=data[hdr+7] ){
  9158   9157         checkAppendMsg(pCheck,
  9159   9158             "Fragmentation of %d bytes reported as %d on page %d",
  9160         -          cnt, data[hdr+7], iPage);
         9159  +          nFrag, data[hdr+7], iPage);
  9161   9160       }
  9162   9161     }
  9163   9162   
  9164   9163   end_of_check:
  9165   9164     releasePage(pPage);
  9166   9165     pCheck->zPfx = saved_zPfx;
  9167   9166     pCheck->v1 = saved_v1;
................................................................................
  9188   9187     Btree *p,     /* The btree to be checked */
  9189   9188     int *aRoot,   /* An array of root pages numbers for individual trees */
  9190   9189     int nRoot,    /* Number of entries in aRoot[] */
  9191   9190     int mxErr,    /* Stop reporting errors after this many */
  9192   9191     int *pnErr    /* Write number of errors seen to this variable */
  9193   9192   ){
  9194   9193     Pgno i;
  9195         -  VVA_ONLY( int nRef );
  9196   9194     IntegrityCk sCheck;
  9197   9195     BtShared *pBt = p->pBt;
         9196  +  int savedDbFlags = pBt->db->flags;
  9198   9197     char zErr[100];
         9198  +  VVA_ONLY( int nRef );
  9199   9199   
  9200   9200     sqlite3BtreeEnter(p);
  9201   9201     assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
  9202   9202     assert( (nRef = sqlite3PagerRefcount(pBt->pPager))>=0 );
  9203   9203     sCheck.pBt = pBt;
  9204   9204     sCheck.pPager = pBt->pPager;
  9205   9205     sCheck.nPage = btreePagecount(sCheck.pBt);
................................................................................
  9235   9235     sCheck.zPfx = "Main freelist: ";
  9236   9236     checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
  9237   9237               get4byte(&pBt->pPage1->aData[36]));
  9238   9238     sCheck.zPfx = 0;
  9239   9239   
  9240   9240     /* Check all the tables.
  9241   9241     */
         9242  +  testcase( pBt->db->flags & SQLITE_CellSizeCk );
         9243  +  pBt->db->flags &= ~SQLITE_CellSizeCk;
  9242   9244     for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
         9245  +    i64 notUsed;
  9243   9246       if( aRoot[i]==0 ) continue;
  9244   9247   #ifndef SQLITE_OMIT_AUTOVACUUM
  9245   9248       if( pBt->autoVacuum && aRoot[i]>1 ){
  9246   9249         checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
  9247   9250       }
  9248   9251   #endif
  9249         -    checkTreePage(&sCheck, aRoot[i], NULL, NULL);
         9252  +    checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
  9250   9253     }
         9254  +  pBt->db->flags = savedDbFlags;
  9251   9255   
  9252   9256     /* Make sure every page in the file is referenced
  9253   9257     */
  9254   9258     for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
  9255   9259   #ifdef SQLITE_OMIT_AUTOVACUUM
  9256   9260       if( getPageReferenced(&sCheck, i)==0 ){
  9257   9261         checkAppendMsg(&sCheck, "Page %d is never used", i);

Changes to test/corrupt2.test.

   245    245     db2 eval {SELECT rowid FROM t1} {
   246    246       set result [db2 eval {pragma integrity_check}]
   247    247       break
   248    248     }
   249    249     set result
   250    250   } {{*** in database main ***
   251    251   On tree page 2 cell 0: 2nd reference to page 10
   252         -On tree page 2 cell 1: Child page depth differs
   253    252   Page 4 is never used}}
   254    253   
   255    254   db2 close
   256    255   
   257    256   proc corruption_test {args} {
   258    257     set A(-corrupt) {}
   259    258     set A(-sqlprep) {}

Changes to test/corrupt7.test.

    61     61     hexio_get_int [hexio_read test.db 20 1]
    62     62   } 0      ;# Unused bytes per page is 0
    63     63   
    64     64   integrity_check corrupt7-1.4
    65     65   
    66     66   # Deliberately corrupt some of the cell offsets in the btree page
    67     67   # on page 2 of the database.
    68         -#
    69         -# The error message is different depending on whether or not the
    70         -# SQLITE_ENABLE_OVERSIZE_CELL_CHECK compile-time option is engaged.
    71         -#
    72         -ifcapable oversize_cell_check {
    73         -  do_test corrupt7-2.1 {
    74         -    db close
    75         -    hexio_write test.db 1062 FF
    76         -    sqlite3 db test.db
    77         -    db eval {PRAGMA integrity_check(1)}
    78         -  } {{*** in database main ***
    79         -Page 2: btreeInitPage() returns error code 11}}
    80         -  do_test corrupt7-2.2 {
    81         -    db close
    82         -    hexio_write test.db 1062 04
    83         -    sqlite3 db test.db
    84         -    db eval {PRAGMA integrity_check(1)}
    85         -  } {{*** in database main ***
    86         -Page 2: btreeInitPage() returns error code 11}}
    87         -} else {
    88         -  do_test corrupt7-2.1 {
    89         -    db close
    90         -    hexio_write test.db 1062 FF
    91         -    sqlite3 db test.db
    92         -    db eval {PRAGMA integrity_check(1)}
    93         -  } {{*** in database main ***
    94         -Corruption detected in cell 15 on page 2}}
    95         -  do_test corrupt7-2.2 {
    96         -    db close
    97         -    hexio_write test.db 1062 04
    98         -    sqlite3 db test.db
    99         -    db eval {PRAGMA integrity_check(1)}
   100         -  } {{*** in database main ***
   101         -On tree page 2 cell 15: Rowid 0 out of order (previous was 15)}}
   102         -}
           68  +do_test corrupt7-2.1 {
           69  +  db close
           70  +  hexio_write test.db 1062 FF
           71  +  sqlite3 db test.db
           72  +  db eval {PRAGMA integrity_check(1)}
           73  +} {{*** in database main ***
           74  +On tree page 2 cell 15: Offset 65457 out of range 945..1020}}
           75  +do_test corrupt7-2.2 {
           76  +  db close
           77  +  hexio_write test.db 1062 04
           78  +  sqlite3 db test.db
           79  +  db eval {PRAGMA integrity_check(1)}
           80  +} {{*** in database main ***
           81  +On tree page 2 cell 15: Offset 1201 out of range 945..1020}}
   103     82     
   104     83   # The code path that was causing the buffer overrun that this test
   105     84   # case was checking for was removed.
   106     85   #
   107     86   #do_test corrupt7-3.1 {
   108     87   #  execsql {
   109     88   #    DROP TABLE t1;

Changes to test/corruptE.test.

    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.
    12     12   #
    13     13   # This file implements tests to make sure SQLite does not crash or
    14     14   # segfault if it sees a corrupt database file.  It specifcally
    15     15   # focuses on rowid order corruption.
    16     16   #
    17         -# $Id: corruptE.test,v 1.14 2009/07/11 06:55:34 danielk1977 Exp $
    18     17   
    19     18   set testdir [file dirname $argv0]
    20     19   source $testdir/tester.tcl
    21     20   
    22     21   # Do not use a codec for tests in this file, as the database file is
    23     22   # manipulated directly using tcl scripts (using the [hexio_write] command).
    24     23   #
................................................................................
    75     74     forcecopy test.bu test.db
    76     75   
    77     76     # insert corrupt byte(s)
    78     77     hexio_write test.db 2041 [format %02x 0x2e]
    79     78   
    80     79     sqlite3 db test.db
    81     80   
    82         -  set res [ catchsql {PRAGMA integrity_check} ]
    83         -  set ans [lindex $res 1]
    84         -
    85         -  list [regexp {out of order.*previous was} $ans] \
    86         -       [regexp {out of order.*max larger than parent max} $ans]
    87         -} {1 1}
           81  +  catchsql {PRAGMA integrity_check}
           82  +} {/ out of order/}
    88     83   
    89     84   do_test corruptE-2.2 {
    90     85     db close
    91     86     forcecopy test.bu test.db
    92     87   
    93     88     # insert corrupt byte(s)
    94     89     hexio_write test.db 2047 [format %02x 0x84]
    95     90   
    96     91     sqlite3 db test.db
    97     92   
    98         -  set res [ catchsql {PRAGMA integrity_check} ]
    99         -  set ans [lindex $res 1]
   100         -
   101         -  list [regexp {out of order.*previous was} $ans] \
   102         -       [regexp {out of order.*min less than parent min} $ans]
   103         -} {1 1}
           93  +  catchsql {PRAGMA integrity_check}
           94  +} {/ Extends off end of page/}
   104     95   
   105     96   do_test corruptE-2.3 {
   106     97     db close
   107     98     forcecopy test.bu test.db
   108     99   
   109    100     # insert corrupt byte(s)
   110    101     hexio_write test.db 7420 [format %02x 0xa8]
   111    102     hexio_write test.db 10459 [format %02x 0x8d]
   112    103   
   113    104     sqlite3 db test.db
   114    105   
   115         -  set res [ catchsql {PRAGMA integrity_check} ]
   116         -  set ans [lindex $res 1]
   117         -
   118         -  list [regexp {out of order.*max larger than parent min} $ans]
   119         -} {1}
          106  +  catchsql {PRAGMA integrity_check}
          107  +} {/out of order/}
   120    108   
   121    109   do_test corruptE-2.4 {
   122    110     db close
   123    111     forcecopy test.bu test.db
   124    112   
   125    113     # insert corrupt byte(s)
   126    114     hexio_write test.db 10233 [format %02x 0xd0]
   127    115   
   128    116     sqlite3 db test.db
   129    117   
   130         -  set res [ catchsql {PRAGMA integrity_check} ]
   131         -  set ans [lindex $res 1]
   132         -
   133         -  list [regexp {out of order.*min less than parent max} $ans]
   134         -} {1}
          118  +  catchsql {PRAGMA integrity_check}
          119  +} {/out of order/}
   135    120   
   136    121   
   137    122   set tests [list {10233 0xd0} \
   138    123                   {941 0x42} \
   139         -                {1028 0x53} \
   140    124                   {2041 0xd0} \
   141    125                   {2042 0x1f} \
   142         -                {2047 0xaa} \
   143         -                {2263 0x29} \
   144    126                   {2274 0x75} \
   145    127                   {3267 0xf2} \
   146         -                {4104 0x2c} \
   147    128                   {5113 0x36} \
   148    129                   {10233 0x84} \
   149    130                   {10234 0x74} \
   150    131                   {10239 0x41} \
   151         -                {10453 0x11} \
   152    132                   {11273 0x28} \
   153         -                {11455 0x11} \
   154    133                   {11461 0xe6} \
   155         -                {12281 0x99} \
   156         -                {12296 0x9e} \
   157    134                   {12297 0xd7} \
   158    135                   {13303 0x53} ]
   159    136   
   160    137   set tc 1
   161    138   foreach test $tests {
   162    139     do_test corruptE-3.$tc {
   163    140       db close
................................................................................
   164    141       forcecopy test.bu test.db
   165    142   
   166    143       # insert corrupt byte(s)
   167    144       hexio_write test.db [lindex $test 0] [format %02x [lindex $test 1]]
   168    145   
   169    146       sqlite3 db test.db
   170    147   
   171         -    set res [ catchsql {PRAGMA integrity_check} ]
   172         -    set ans [lindex $res 1]
   173         -
   174         -    list [regexp {out of order} $ans]
   175         -  } {1}
          148  +    catchsql {PRAGMA integrity_check}
          149  +  } {/out of order/}
   176    150     incr tc 1
   177    151   }
   178    152   
   179    153   finish_test