/ Check-in [35f04235]
Login

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

Overview
Comment:Improved detection of database corruption while balancing pages from an auto_vacuum database with overflow pages. Test cases in TH3.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 35f04235c477501390acea126d07a730d81d03cdf7abcd82d861e397b3f75b0f
User & Date: drh 2019-01-13 20:17:21
Context
2019-01-13
20:17
Relax the minimum size database file constraint on the dbtotxt utility program. check-in: 97e723d7 user: drh tags: trunk
20:17
Improved detection of database corruption while balancing pages from an auto_vacuum database with overflow pages. Test cases in TH3. check-in: 35f04235 user: drh tags: trunk
00:58
Move a local variable declaration into the outermost scope in which it is used. This fixes an ASAN warning. check-in: ac3b6021 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

  1062   1062     if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_PGNO(iPtrmap);
  1063   1063     return SQLITE_OK;
  1064   1064   }
  1065   1065   
  1066   1066   #else /* if defined SQLITE_OMIT_AUTOVACUUM */
  1067   1067     #define ptrmapPut(w,x,y,z,rc)
  1068   1068     #define ptrmapGet(w,x,y,z) SQLITE_OK
  1069         -  #define ptrmapPutOvflPtr(x, y, rc)
         1069  +  #define ptrmapPutOvflPtr(x, y, z, rc)
  1070   1070   #endif
  1071   1071   
  1072   1072   /*
  1073   1073   ** Given a btree page and a cell index (0 means the first cell on
  1074   1074   ** the page, 1 means the second cell, and so forth) return a pointer
  1075   1075   ** to the cell content.
  1076   1076   **
................................................................................
  1355   1355   static u16 cellSize(MemPage *pPage, int iCell){
  1356   1356     return pPage->xCellSize(pPage, findCell(pPage, iCell));
  1357   1357   }
  1358   1358   #endif
  1359   1359   
  1360   1360   #ifndef SQLITE_OMIT_AUTOVACUUM
  1361   1361   /*
  1362         -** If the cell pCell, part of page pPage contains a pointer
  1363         -** to an overflow page, insert an entry into the pointer-map
  1364         -** for the overflow page.
         1362  +** The cell pCell is currently part of page pSrc but will ultimately be part
         1363  +** of pPage.  (pSrc and pPager are often the same.)  If pCell contains a
         1364  +** pointer to an overflow page, insert an entry into the pointer-map for
         1365  +** the overflow page that will be valid after pCell has been moved to pPage.
  1365   1366   */
  1366         -static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
         1367  +static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){
  1367   1368     CellInfo info;
  1368   1369     if( *pRC ) return;
  1369   1370     assert( pCell!=0 );
  1370   1371     pPage->xParseCell(pPage, pCell, &info);
  1371   1372     if( info.nLocal<info.nPayload ){
  1372   1373       Pgno ovfl;
  1373         -    if( SQLITE_WITHIN(pPage->aDataEnd, pCell, pCell+info.nLocal) ){
         1374  +    if( SQLITE_WITHIN(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){
         1375  +      testcase( pSrc!=pPage );
  1374   1376         *pRC = SQLITE_CORRUPT_BKPT;
  1375   1377         return;
  1376   1378       }
  1377   1379       ovfl = get4byte(&pCell[info.nSize-4]);
  1378   1380       ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC);
  1379   1381     }
  1380   1382   }
................................................................................
  3487   3489     rc = pPage->isInit ? SQLITE_OK : btreeInitPage(pPage);
  3488   3490     if( rc!=SQLITE_OK ) return rc;
  3489   3491     nCell = pPage->nCell;
  3490   3492   
  3491   3493     for(i=0; i<nCell; i++){
  3492   3494       u8 *pCell = findCell(pPage, i);
  3493   3495   
  3494         -    ptrmapPutOvflPtr(pPage, pCell, &rc);
         3496  +    ptrmapPutOvflPtr(pPage, pPage, pCell, &rc);
  3495   3497   
  3496   3498       if( !pPage->leaf ){
  3497   3499         Pgno childPgno = get4byte(pCell);
  3498   3500         ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc);
  3499   3501       }
  3500   3502     }
  3501   3503   
................................................................................
  6673   6675       if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
  6674   6676       assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell );
  6675   6677   #ifndef SQLITE_OMIT_AUTOVACUUM
  6676   6678       if( pPage->pBt->autoVacuum ){
  6677   6679         /* The cell may contain a pointer to an overflow page. If so, write
  6678   6680         ** the entry for the overflow page into the pointer map.
  6679   6681         */
  6680         -      ptrmapPutOvflPtr(pPage, pCell, pRC);
         6682  +      ptrmapPutOvflPtr(pPage, pPage, pCell, pRC);
  6681   6683       }
  6682   6684   #endif
  6683   6685     }
  6684   6686   }
  6685   6687   
  6686   6688   /*
  6687   6689   ** A CellArray object contains a cache of pointers and sizes for a
................................................................................
  7089   7091       ** That is Ok, at this point the parent page is guaranteed to
  7090   7092       ** be marked as dirty. Returning an error code will cause a
  7091   7093       ** rollback, undoing any changes made to the parent page.
  7092   7094       */
  7093   7095       if( ISAUTOVACUUM ){
  7094   7096         ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc);
  7095   7097         if( szCell>pNew->minLocal ){
  7096         -        ptrmapPutOvflPtr(pNew, pCell, &rc);
         7098  +        ptrmapPutOvflPtr(pNew, pNew, pCell, &rc);
  7097   7099         }
  7098   7100       }
  7099   7101     
  7100   7102       /* Create a divider cell to insert into pParent. The divider cell
  7101   7103       ** consists of a 4-byte page number (the page number of pPage) and
  7102   7104       ** a variable length key value (which must be the same value as the
  7103   7105       ** largest key on pPage).
................................................................................
  7312   7314     memset(abDone, 0, sizeof(abDone));
  7313   7315     b.nCell = 0;
  7314   7316     b.apCell = 0;
  7315   7317     pBt = pParent->pBt;
  7316   7318     assert( sqlite3_mutex_held(pBt->mutex) );
  7317   7319     assert( sqlite3PagerIswriteable(pParent->pDbPage) );
  7318   7320   
  7319         -#if 0
  7320         -  TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));
  7321         -#endif
  7322         -
  7323   7321     /* At this point pParent may have at most one overflow cell. And if
  7324   7322     ** this overflow cell is present, it must be the cell with 
  7325   7323     ** index iParentIdx. This scenario comes about when this function
  7326   7324     ** is called (indirectly) from sqlite3BtreeDelete().
  7327   7325     */
  7328   7326     assert( pParent->nOverflow==0 || pParent->nOverflow==1 );
  7329   7327     assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx );
................................................................................
  7781   7779     **
  7782   7780     ** If the sibling pages are not leaves, then the pointer map entry 
  7783   7781     ** associated with the right-child of each sibling may also need to be 
  7784   7782     ** updated. This happens below, after the sibling pages have been 
  7785   7783     ** populated, not here.
  7786   7784     */
  7787   7785     if( ISAUTOVACUUM ){
  7788         -    MemPage *pNew = apNew[0];
         7786  +    MemPage *pOld;
         7787  +    MemPage *pNew = pOld = apNew[0];
  7789   7788       u8 *aOld = pNew->aData;
  7790   7789       int cntOldNext = pNew->nCell + pNew->nOverflow;
  7791   7790       int usableSize = pBt->usableSize;
  7792   7791       int iNew = 0;
  7793   7792       int iOld = 0;
  7794   7793   
  7795   7794       for(i=0; i<b.nCell; i++){
  7796   7795         u8 *pCell = b.apCell[i];
  7797   7796         if( i==cntOldNext ){
  7798         -        MemPage *pOld = (++iOld)<nNew ? apNew[iOld] : apOld[iOld];
         7797  +        pOld = (++iOld)<nNew ? apNew[iOld] : apOld[iOld];
  7799   7798           cntOldNext += pOld->nCell + pOld->nOverflow + !leafData;
  7800   7799           aOld = pOld->aData;
  7801   7800         }
  7802   7801         if( i==cntNew[iNew] ){
  7803   7802           pNew = apNew[++iNew];
  7804   7803           if( !leafData ) continue;
  7805   7804         }
................................................................................
  7814   7813          || pNew->pgno!=aPgno[iOld]
  7815   7814          || !SQLITE_WITHIN(pCell,aOld,&aOld[usableSize])
  7816   7815         ){
  7817   7816           if( !leafCorrection ){
  7818   7817             ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc);
  7819   7818           }
  7820   7819           if( cachedCellSize(&b,i)>pNew->minLocal ){
  7821         -          ptrmapPutOvflPtr(pNew, pCell, &rc);
         7820  +          ptrmapPutOvflPtr(pNew, pOld, pCell, &rc);
  7822   7821           }
  7823   7822           if( rc ) goto balance_cleanup;
  7824   7823         }
  7825   7824       }
  7826   7825     }
  7827   7826   
  7828   7827     /* Insert new divider cells into pParent. */