/ Check-in [bebd967f]
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:Add code to create/update the btree 'pointer-map' for auto-vacuum mode. (CVS 2035)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: bebd967f3627220c3ce0352c8ca9c7c17b722ce6
User & Date: danielk1977 2004-10-31 16:25:43
Context
2004-11-01
16:03
Updates to the support.html page. (CVS 2036) check-in: 5515acce user: drh tags: trunk
2004-10-31
16:25
Add code to create/update the btree 'pointer-map' for auto-vacuum mode. (CVS 2035) check-in: bebd967f user: danielk1977 tags: trunk
02:22
Insert #ifdefs that can optionally remove features at compiletime resulting in a database engine with a smaller footprint. (CVS 2034) check-in: be661acf user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.194 2004/10/31 02:22:49 drh Exp $
           12  +** $Id: btree.c,v 1.195 2004/10/31 16:25:43 danielk1977 Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** For a detailed discussion of BTrees, refer to
    16     16   **
    17     17   **     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
    18     18   **     "Sorting And Searching", pages 473-480. Addison-Wesley
    19     19   **     Publishing Company, Reading, Massachusetts.
................................................................................
   307    307     u16 pageSize;         /* Total number of bytes on a page */
   308    308     u16 psAligned;        /* pageSize rounded up to a multiple of 8 */
   309    309     u16 usableSize;       /* Number of usable bytes on each page */
   310    310     int maxLocal;         /* Maximum local payload in non-LEAFDATA tables */
   311    311     int minLocal;         /* Minimum local payload in non-LEAFDATA tables */
   312    312     int maxLeaf;          /* Maximum local payload in a LEAFDATA table */
   313    313     int minLeaf;          /* Minimum local payload in a LEAFDATA table */
          314  +#ifndef SQLITE_OMIT_AUTOVACUUM
          315  +  u8 autoVacuum;        /* True if database supports auto-vacuum */
          316  +#endif
   314    317   };
   315    318   typedef Btree Bt;
   316    319   
   317    320   /*
   318    321   ** Btree.inTrans may take one of the following values.
   319    322   */
   320    323   #define TRANS_NONE  0
................................................................................
   387    390   ** be defined locally, but now we use the varint routines in the util.c
   388    391   ** file.
   389    392   */
   390    393   #define getVarint    sqlite3GetVarint
   391    394   #define getVarint32  sqlite3GetVarint32
   392    395   #define putVarint    sqlite3PutVarint
   393    396   
          397  +#ifndef SQLITE_OMIT_AUTOVACUUM
          398  +
          399  +/*
          400  +** These two macros define the location of the pointer-map entry for a 
          401  +** database page. The first argument to each is the page size used 
          402  +** by the database (often 1024). The second is the page number to look
          403  +** up in the pointer map.
          404  +**
          405  +** PTRMAP_PAGENO returns the database page number of the pointer-map
          406  +** page that stores the required pointer. PTRMAP_PTROFFSET returns
          407  +** the offset of the requested map entry.
          408  +**
          409  +** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page,
          410  +** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be
          411  +** used to test if pgno is a pointer-map page.
          412  +*/
          413  +#define PTRMAP_PAGENO(pgsz, pgno) (((pgno-2)/(pgsz/5+1))*(pgsz/5+1)+2)
          414  +#define PTRMAP_PTROFFSET(pgsz, pgno) (((pgno-2)%(pgsz/5+1)-1)*5)
          415  +
          416  +/*
          417  +** The first byte of each 5-byte pointer map entry identifies the type
          418  +** of page that the following 4-byte page number refers to (either a
          419  +** regular btree page or an overflow page).
          420  +**
          421  +** If the type is PTRMAP_OVERFLOW, then the page is an overflow page.
          422  +** In this case the pointer is always the first 4 bytes of the page.
          423  +**
          424  +** If the type is PTRMAP_BTREE, then the page is a btree page. In this
          425  +** case the pointer may be a 'left-pointer' (stored following a cell-header), 
          426  +** a pointer to an overflow page (stored after a cell's data payload), 
          427  +** or the 'right pointer' of a btree page.
          428  +*/
          429  +#define PTRMAP_BTREE 1
          430  +#define PTRMAP_OVERFLOW 2
          431  +
          432  +/*
          433  +** Write an entry into the pointer map.
          434  +*/
          435  +static int ptrmapPut(Btree *pBt, Pgno key, u8 eType, Pgno pgno){
          436  +  u8 *pPtrmap;    /* The pointer map page */
          437  +  Pgno iPtrmap;   /* The pointer map page number */
          438  +  int offset;     /* Offset in pointer map page */
          439  +  int rc;
          440  +
          441  +  iPtrmap = PTRMAP_PAGENO(pBt->pageSize, key);
          442  +  rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
          443  +  if( rc!=0 ){
          444  +    return rc;
          445  +  }
          446  +  offset = PTRMAP_PTROFFSET(pBt->pageSize, key);
          447  +
          448  +  if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=pgno ){
          449  +    rc = sqlite3pager_write(pPtrmap);
          450  +    if( rc!=0 ){
          451  +      return rc;
          452  +    }
          453  +    pPtrmap[offset] = eType;
          454  +    put4byte(&pPtrmap[offset+1], pgno);
          455  +  }
          456  +
          457  +  sqlite3pager_unref(pPtrmap);
          458  +  return SQLITE_OK;
          459  +}
          460  +
          461  +/*
          462  +** Read an entry from the pointer map.
          463  +*/
          464  +static int ptrmapGet(Btree *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
          465  +  int iPtrmap;       /* Pointer map page index */
          466  +  u8 *pPtrmap;       /* Pointer map page data */
          467  +  int offset;        /* Offset of entry in pointer map */
          468  +  int rc;
          469  +
          470  +  iPtrmap = PTRMAP_PAGENO(pBt->pageSize, key);
          471  +  rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
          472  +  if( rc!=0 ){
          473  +    return rc;
          474  +  }
          475  +
          476  +  offset = PTRMAP_PTROFFSET(pBt->pageSize, key);
          477  +  *pEType = pPtrmap[offset];
          478  +  *pPgno = get4byte(&pPtrmap[offset+1]);
          479  +
          480  +  sqlite3pager_unref(pPtrmap);
          481  +  return SQLITE_OK;
          482  +}
          483  +
          484  +#endif /* SQLITE_OMIT_AUTOVACUUM */
          485  +
   394    486   /*
   395    487   ** Given a btree page and a cell index (0 means the first cell on
   396    488   ** the page, 1 means the second cell, and so forth) return a pointer
   397    489   ** to the cell content.
   398    490   **
   399    491   ** This routine works only for pages that do not contain overflow cells.
   400    492   */
................................................................................
  1083   1175       pBt->minLeafFrac = zDbHeader[23];
  1084   1176       pBt->pageSizeFixed = 1;
  1085   1177     }
  1086   1178     pBt->usableSize = pBt->pageSize - nReserve;
  1087   1179     pBt->psAligned = FORCE_ALIGNMENT(pBt->pageSize);
  1088   1180     sqlite3pager_set_pagesize(pBt->pPager, pBt->pageSize);
  1089   1181     *ppBtree = pBt;
         1182  +#ifdef SQLITE_AUTOVACUUM
         1183  +  /* Note: This is temporary code for use during development of auto-vacuum. */
         1184  +  pBt->autoVacuum = 1;
         1185  +#endif
  1090   1186     return SQLITE_OK;
  1091   1187   }
  1092   1188   
  1093   1189   /*
  1094   1190   ** Close an open database and invalidate all cursors.
  1095   1191   */
  1096   1192   int sqlite3BtreeClose(Btree *pBt){
................................................................................
  2473   2569           rc = sqlite3pager_write((*ppPage)->aData);
  2474   2570         }
  2475   2571       }
  2476   2572     }else{
  2477   2573       /* There are no pages on the freelist, so create a new page at the
  2478   2574       ** end of the file */
  2479   2575       *pPgno = sqlite3pager_pagecount(pBt->pPager) + 1;
         2576  +
         2577  +#ifndef SQLITE_OMIT_AUTOVACUUM
         2578  +    if( pBt->autoVacuum && *pPgno==PTRMAP_PAGENO(pBt->pageSize, *pPgno) ){
         2579  +      /* If *pPgno refers to a pointer-map page, allocate two new pages
         2580  +      ** at the end of the file instead of one. The first allocated page
         2581  +      ** becomes a new pointer-map page, the second is used by the caller.
         2582  +      */
         2583  +      TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno));
         2584  +      (*pPgno)++;
         2585  +    }
         2586  +#endif
         2587  +
  2480   2588       rc = getPage(pBt, *pPgno, ppPage);
  2481   2589       if( rc ) return rc;
  2482   2590       rc = sqlite3pager_write((*ppPage)->aData);
  2483   2591       TRACE(("ALLOCATE: %d from end of file\n", *pPgno));
  2484   2592     }
  2485   2593     return rc;
  2486   2594   }
................................................................................
  2633   2741     *pnSize = info.nSize;
  2634   2742     spaceLeft = info.nLocal;
  2635   2743     pPayload = &pCell[nHeader];
  2636   2744     pPrior = &pCell[info.iOverflow];
  2637   2745   
  2638   2746     while( nPayload>0 ){
  2639   2747       if( spaceLeft==0 ){
  2640         -      rc =  allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl);
         2748  +#ifndef SQLITE_OMIT_AUTOVACUUM
         2749  +      Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */
         2750  +#endif
         2751  +      rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl);
         2752  +#ifndef SQLITE_OMIT_AUTOVACUUM
         2753  +      /* If the database supports auto-vacuum, add an entry to the 
         2754  +      ** pointer-map for the overflow page just allocated. If the page just 
         2755  +      ** allocated was the first in the overflow list, then the balance() 
         2756  +      ** routine may adjust the pointer-map entry later.
         2757  +      */
         2758  +      if( pBt->autoVacuum && rc==0 ){
         2759  +        if( pgnoPtrmap!=0 ){
         2760  +          rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW, pgnoPtrmap);
         2761  +        }else{
         2762  +          rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_BTREE, pPage->pgno);
         2763  +        }
         2764  +      }
         2765  +#endif
  2641   2766         if( rc ){
  2642   2767           releasePage(pToRelease);
  2643   2768           clearCell(pPage, pCell);
  2644   2769           return rc;
  2645   2770         }
  2646   2771         put4byte(pPrior, pgnoOvfl);
  2647   2772         releasePage(pToRelease);
................................................................................
  2670   2795   }
  2671   2796   
  2672   2797   /*
  2673   2798   ** Change the MemPage.pParent pointer on the page whose number is
  2674   2799   ** given in the second argument so that MemPage.pParent holds the
  2675   2800   ** pointer in the third argument.
  2676   2801   */
  2677         -static void reparentPage(Btree *pBt, Pgno pgno, MemPage *pNewParent, int idx){
         2802  +static int reparentPage(Btree *pBt, Pgno pgno, MemPage *pNewParent, int idx){
  2678   2803     MemPage *pThis;
  2679   2804     unsigned char *aData;
  2680   2805   
  2681         -  if( pgno==0 ) return;
         2806  +  if( pgno==0 ) return SQLITE_OK;
  2682   2807     assert( pBt->pPager!=0 );
  2683   2808     aData = sqlite3pager_lookup(pBt->pPager, pgno);
  2684   2809     if( aData ){
  2685   2810       pThis = (MemPage*)&aData[pBt->psAligned];
  2686   2811       assert( pThis->aData==aData );
  2687   2812       if( pThis->isInit ){
  2688   2813         if( pThis->pParent!=pNewParent ){
................................................................................
  2690   2815           pThis->pParent = pNewParent;
  2691   2816           if( pNewParent ) sqlite3pager_ref(pNewParent->aData);
  2692   2817         }
  2693   2818         pThis->idxParent = idx;
  2694   2819       }
  2695   2820       sqlite3pager_unref(aData);
  2696   2821     }
         2822  +
         2823  +#ifndef SQLITE_OMIT_AUTOVACUUM
         2824  +  if( pBt->autoVacuum ){
         2825  +    return ptrmapPut(pBt, pgno, PTRMAP_BTREE, pNewParent->pgno);
         2826  +  }
         2827  +#endif
         2828  +  return SQLITE_OK;
  2697   2829   }
  2698   2830   
  2699   2831   /*
  2700   2832   ** Change the pParent pointer of all children of pPage to point back
  2701   2833   ** to pPage.
  2702   2834   **
  2703   2835   ** In other words, for every child of pPage, invoke reparentPage()
  2704   2836   ** to make sure that each child knows that pPage is its parent.
  2705   2837   **
  2706   2838   ** This routine gets called after you memcpy() one page into
  2707   2839   ** another.
  2708   2840   */
  2709         -static void reparentChildPages(MemPage *pPage){
         2841  +static int reparentChildPages(MemPage *pPage){
  2710   2842     int i;
  2711         -  Btree *pBt;
         2843  +  Btree *pBt = pPage->pBt;
         2844  +  int rc = SQLITE_OK;
  2712   2845   
  2713         -  if( pPage->leaf ) return;
  2714         -  pBt = pPage->pBt;
         2846  +#ifdef SQLITE_OMIT_AUTOVACUUM
         2847  +  if( pPage->leaf ) return SQLITE_OK;
         2848  +#else
         2849  +  if( !pBt->autoVacuum && pPage->leaf ) return SQLITE_OK;
         2850  +#endif
         2851  +
  2715   2852     for(i=0; i<pPage->nCell; i++){
  2716         -    reparentPage(pBt, get4byte(findCell(pPage,i)), pPage, i);
         2853  +    u8 *pCell = findCell(pPage, i);
         2854  +    if( !pPage->leaf ){
         2855  +      rc = reparentPage(pBt, get4byte(pCell), pPage, i);
         2856  +      if( rc!=SQLITE_OK ) return rc;
         2857  +    }
         2858  +#ifndef SQLITE_OMIT_AUTOVACUUM
         2859  +    /* If the database supports auto-vacuum, then check each cell to see
         2860  +    ** if it contains a pointer to an overflow page. If so, then the 
         2861  +    ** pointer-map must be updated accordingly.
         2862  +    **
         2863  +    ** TODO: This looks like quite an expensive thing to do. Investigate.
         2864  +    */
         2865  +    if( pBt->autoVacuum ){
         2866  +      CellInfo info;
         2867  +      parseCellPtr(pPage, pCell, &info);
         2868  +      if( info.iOverflow ){
         2869  +        Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
         2870  +        rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_BTREE, pPage->pgno);
         2871  +        if( rc!=SQLITE_OK ) return rc;
         2872  +      }
         2873  +    }
         2874  +#endif
  2717   2875     }
  2718         -  reparentPage(pBt, get4byte(&pPage->aData[pPage->hdrOffset+8]), pPage, i);
  2719         -  pPage->idxShift = 0;
         2876  +  if( !pPage->leaf ){
         2877  +    rc = reparentPage(pBt, get4byte(&pPage->aData[pPage->hdrOffset+8]), 
         2878  +       pPage, i);
         2879  +    pPage->idxShift = 0;
         2880  +  }
         2881  +  return rc;
  2720   2882   }
  2721   2883   
  2722   2884   /*
  2723   2885   ** Remove the i-th cell from pPage.  This routine effects pPage only.
  2724   2886   ** The cell content is not freed or deallocated.  It is assumed that
  2725   2887   ** the cell content has been copied someplace else.  This routine just
  2726   2888   ** removes the reference to the cell from pPage.
................................................................................
  3308   3470       put4byte(findOverflowCell(pParent, nxDiv), pgnoNew[nNew-1]);
  3309   3471     }
  3310   3472   
  3311   3473     /*
  3312   3474     ** Reparent children of all cells.
  3313   3475     */
  3314   3476     for(i=0; i<nNew; i++){
  3315         -    reparentChildPages(apNew[i]);
         3477  +    rc = reparentChildPages(apNew[i]);
         3478  +    if( rc!=SQLITE_OK ) goto balance_cleanup;
  3316   3479     }
  3317         -  reparentChildPages(pParent);
         3480  +  rc = reparentChildPages(pParent);
         3481  +  if( rc!=SQLITE_OK ) goto balance_cleanup;
  3318   3482   
  3319   3483     /*
  3320   3484     ** Balance the parent page.  Note that the current page (pPage) might
  3321   3485     ** have been added to the freelist is it might no longer be initialized.
  3322   3486     ** But the parent page will always be initialized.
  3323   3487     */
  3324   3488     assert( pParent->isInit );
................................................................................
  3412   3576         pPage->pParent = 0;
  3413   3577         rc = initPage(pPage, 0);
  3414   3578         assert( rc==SQLITE_OK );
  3415   3579         freePage(pChild);
  3416   3580         TRACE(("BALANCE: transfer child %d into root %d\n",
  3417   3581                 pChild->pgno, pPage->pgno));
  3418   3582       }
  3419         -    reparentChildPages(pPage);
         3583  +    rc = reparentChildPages(pPage);
         3584  +    if( rc!=SQLITE_OK ) goto end_shallow_balance;
  3420   3585       releasePage(pChild);
  3421   3586     }
  3422   3587   end_shallow_balance:
  3423   3588     sqliteFree(apCell);
  3424   3589     return rc;
  3425   3590   }
  3426   3591   
................................................................................
  4112   4277     }
  4113   4278     if( pCheck->anRef[iPage]==1 ){
  4114   4279       checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
  4115   4280       return 1;
  4116   4281     }
  4117   4282     return  (pCheck->anRef[iPage]++)>1;
  4118   4283   }
  4119         -#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
  4120   4284   
  4121         -#ifndef SQLITE_OMIT_INTEGRITY_CHECK
         4285  +#ifndef SQLITE_OMIT_AUTOVACUUM
         4286  +/*
         4287  +** Check that the entry in the pointer-map for page iChild maps to 
         4288  +** page iParent, pointer type ptrType. If not, append an error message
         4289  +** to pCheck.
         4290  +*/
         4291  +static void checkPtrmap(
         4292  +  IntegrityCk *pCheck,   /* Integrity check context */
         4293  +  Pgno iChild,           /* Child page number */
         4294  +  u8 eType,              /* Expected pointer map type */
         4295  +  Pgno iParent,          /* Expected pointer map parent page number */
         4296  +  char *zContext         /* Context description (used for error msg) */
         4297  +){
         4298  +  int rc;
         4299  +  u8 ePtrmapType;
         4300  +  Pgno iPtrmapParent;
         4301  +
         4302  +  rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
         4303  +  if( rc!=SQLITE_OK ){
         4304  +    checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild);
         4305  +    return;
         4306  +  }
         4307  +
         4308  +  if( ePtrmapType!=eType || iPtrmapParent!=iParent ){
         4309  +    checkAppendMsg(pCheck, zContext, 
         4310  +      "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", 
         4311  +      iChild, eType, iParent, ePtrmapType, iPtrmapParent);
         4312  +  }
         4313  +}
         4314  +#endif
         4315  +
  4122   4316   /*
  4123   4317   ** Check the integrity of the freelist or of an overflow page list.
  4124   4318   ** Verify that the number of pages on the list is N.
  4125   4319   */
  4126   4320   static void checkList(
  4127   4321     IntegrityCk *pCheck,  /* Integrity checking context */
  4128   4322     int isFreeList,       /* True for a freelist.  False for overflow page list */
................................................................................
  4155   4349         }else{
  4156   4350           for(i=0; i<n; i++){
  4157   4351             checkRef(pCheck, get4byte(&pOvfl[8+i*4]), zContext);
  4158   4352           }
  4159   4353           N -= n;
  4160   4354         }
  4161   4355       }
         4356  +#ifndef SQLITE_OMIT_AUTOVACUUM
         4357  +    /* If this database supports auto-vacuum and iPage is not the last
         4358  +    ** page in this overflow list, check that the pointer-map entry for
         4359  +    ** the following page matches iPage.
         4360  +    */
         4361  +    if( pCheck->pBt->autoVacuum && !isFreeList && N>0 ){
         4362  +      i = get4byte(pOvfl);
         4363  +      checkPtrmap(pCheck, i, PTRMAP_OVERFLOW, iPage, zContext);
         4364  +    }
         4365  +#endif
  4162   4366       iPage = get4byte(pOvfl);
  4163   4367       sqlite3pager_unref(pOvfl);
  4164   4368     }
  4165   4369   }
  4166   4370   #endif /* SQLITE_OMIT_INTEGRITY_CHECK */
  4167   4371   
  4168   4372   #ifndef SQLITE_OMIT_INTEGRITY_CHECK
................................................................................
  4237   4441       sprintf(zContext, "On tree page %d cell %d: ", iPage, i);
  4238   4442       pCell = findCell(pPage,i);
  4239   4443       parseCellPtr(pPage, pCell, &info);
  4240   4444       sz = info.nData;
  4241   4445       if( !pPage->intKey ) sz += info.nKey;
  4242   4446       if( sz>info.nLocal ){
  4243   4447         int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
  4244         -      checkList(pCheck, 0, get4byte(&pCell[info.iOverflow]),nPage,zContext);
         4448  +      Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
         4449  +#ifndef SQLITE_OMIT_AUTOVACUUM
         4450  +      if( pBt->autoVacuum ){
         4451  +        checkPtrmap(pCheck, pgnoOvfl, PTRMAP_BTREE, iPage, zContext);
         4452  +      }
         4453  +#endif
         4454  +      checkList(pCheck, 0, pgnoOvfl, nPage, zContext);
  4245   4455       }
  4246   4456   
  4247   4457       /* Check sanity of left child page.
  4248   4458       */
  4249   4459       if( !pPage->leaf ){
  4250   4460         pgno = get4byte(pCell);
         4461  +#ifndef SQLITE_OMIT_AUTOVACUUM
         4462  +      if( pBt->autoVacuum ){
         4463  +        checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
         4464  +      }
         4465  +#endif
  4251   4466         d2 = checkTreePage(pCheck,pgno,pPage,zContext,0,0,0,0);
  4252   4467         if( i>0 && d2!=depth ){
  4253   4468           checkAppendMsg(pCheck, zContext, "Child page depth differs");
  4254   4469         }
  4255   4470         depth = d2;
  4256   4471       }
  4257   4472     }
  4258   4473     if( !pPage->leaf ){
  4259   4474       pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
  4260   4475       sprintf(zContext, "On page %d at right child: ", iPage);
         4476  +#ifndef SQLITE_OMIT_AUTOVACUUM
         4477  +    if( pBt->autoVacuum ){
         4478  +      checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
         4479  +    }
         4480  +#endif
  4261   4481       checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0);
  4262   4482     }
  4263   4483    
  4264   4484     /* Check for complete coverage of the page
  4265   4485     */
  4266   4486     data = pPage->aData;
  4267   4487     hdr = pPage->hdrOffset;
................................................................................
  4351   4571       if( aRoot[i]==0 ) continue;
  4352   4572       checkTreePage(&sCheck, aRoot[i], 0, "List of tree roots: ", 0,0,0,0);
  4353   4573     }
  4354   4574   
  4355   4575     /* Make sure every page in the file is referenced
  4356   4576     */
  4357   4577     for(i=1; i<=sCheck.nPage; i++){
         4578  +#ifdef SQLITE_OMIT_AUTOVACUUM
  4358   4579       if( sCheck.anRef[i]==0 ){
  4359   4580         checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
  4360   4581       }
         4582  +#else
         4583  +    /* If the database supports auto-vacuum, make sure no tables contain
         4584  +    ** references to pointer-map pages.
         4585  +    */
         4586  +    if( sCheck.anRef[i]==0 && 
         4587  +       (PTRMAP_PAGENO(pBt->pageSize, i)!=i || !pBt->autoVacuum) ){
         4588  +      checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
         4589  +    }
         4590  +    if( sCheck.anRef[i]!=0 && 
         4591  +       (PTRMAP_PAGENO(pBt->pageSize, i)==i && pBt->autoVacuum) ){
         4592  +      checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);
         4593  +    }
         4594  +#endif
  4361   4595     }
  4362   4596   
  4363   4597     /* Make sure this analysis did not leave any unref() pages
  4364   4598     */
  4365   4599     unlockBtreeIfUnused(pBt);
  4366   4600     if( nRef != *sqlite3pager_stats(pBt->pPager) ){
  4367   4601       checkAppendMsg(&sCheck, 0,