/ Changes On Branch incr-vacuum-opt
Login

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

Changes In Branch incr-vacuum-opt Excluding Merge-Ins

This is equivalent to a diff from 9bd9bd9c to bf575341

2013-02-25
13:31
Merge the incr-vacuum-opt branch with the trunk. check-in: 26e235b7 user: dan tags: trunk
12:06
Add test file incrvacuum3.test. No code changes. Closed-Leaf check-in: bf575341 user: dan tags: incr-vacuum-opt
07:12
Catch a dropped error code in backup.c. check-in: ac8ca3ec user: dan tags: incr-vacuum-opt
2013-02-22
20:16
Avoid moving pages more than once in an incremental vacuum operation. check-in: c3939d24 user: dan tags: incr-vacuum-opt
19:34
Two new SQL functions: unicode() and char(). Closed-Leaf check-in: 209b2108 user: drh tags: unicode-function
2013-02-20
00:54
On Minix, disable the ".timer" command in the shell in order to avoid calling getrusage(). check-in: 9bd9bd9c user: drh tags: trunk
2013-02-19
22:26
Enhance the showdb tool with options to show PTRMAP usage and content. check-in: 06bd9130 user: drh tags: trunk

Changes to src/backup.c.

   458    458             if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
   459    459               nDestTruncate--;
   460    460             }
   461    461           }else{
   462    462             nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
   463    463           }
   464    464           assert( nDestTruncate>0 );
   465         -        sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
   466    465   
   467    466           if( pgszSrc<pgszDest ){
   468    467             /* If the source page-size is smaller than the destination page-size,
   469    468             ** two extra things may need to happen:
   470    469             **
   471    470             **   * The destination may need to be truncated, and
   472    471             **
   473    472             **   * Data stored on the pages immediately following the 
   474    473             **     pending-byte page in the source database may need to be
   475    474             **     copied into the destination database.
   476    475             */
   477    476             const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
   478    477             sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
          478  +          Pgno iPg;
          479  +          int nDstPage;
   479    480             i64 iOff;
   480    481             i64 iEnd;
   481    482   
   482    483             assert( pFile );
   483    484             assert( nDestTruncate==0 
   484    485                 || (i64)nDestTruncate*(i64)pgszDest >= iSize || (
   485    486                   nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
   486    487                && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
   487    488             ));
   488    489   
   489         -          /* This call ensures that all data required to recreate the original
          490  +          /* This block ensures that all data required to recreate the original
   490    491             ** database has been stored in the journal for pDestPager and the
   491    492             ** journal synced to disk. So at this point we may safely modify
   492    493             ** the database file in any way, knowing that if a power failure
   493    494             ** occurs, the original database will be reconstructed from the 
   494    495             ** journal file.  */
   495         -          rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
          496  +          sqlite3PagerPagecount(pDestPager, &nDstPage);
          497  +          for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
          498  +            if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){
          499  +              DbPage *pPg;
          500  +              rc = sqlite3PagerGet(pDestPager, iPg, &pPg);
          501  +              if( rc==SQLITE_OK ){
          502  +                rc = sqlite3PagerWrite(pPg);
          503  +                sqlite3PagerUnref(pPg);
          504  +              }
          505  +            }
          506  +          }
          507  +          if( rc==SQLITE_OK ){
          508  +            rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
          509  +          }
   496    510   
   497    511             /* Write the extra pages and truncate the database file as required */
   498    512             iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
   499    513             for(
   500    514               iOff=PENDING_BYTE+pgszSrc; 
   501    515               rc==SQLITE_OK && iOff<iEnd; 
   502    516               iOff+=pgszSrc
................................................................................
   515    529             }
   516    530   
   517    531             /* Sync the database file to disk. */
   518    532             if( rc==SQLITE_OK ){
   519    533               rc = sqlite3PagerSync(pDestPager);
   520    534             }
   521    535           }else{
          536  +          sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
   522    537             rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
   523    538           }
   524    539       
   525    540           /* Finish committing the transaction to the destination database. */
   526    541           if( SQLITE_OK==rc
   527    542            && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
   528    543           ){

Changes to src/btree.c.

  2591   2591     /* If the btree is already in a write-transaction, or it
  2592   2592     ** is already in a read-transaction and a read-transaction
  2593   2593     ** is requested, this is a no-op.
  2594   2594     */
  2595   2595     if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
  2596   2596       goto trans_begun;
  2597   2597     }
         2598  +  assert( pBt->bDoTruncate==0 );
  2598   2599   
  2599   2600     /* Write transactions are not possible on a read-only database */
  2600   2601     if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
  2601   2602       rc = SQLITE_READONLY;
  2602   2603       goto trans_begun;
  2603   2604     }
  2604   2605   
................................................................................
  2905   2906       }
  2906   2907     }
  2907   2908     return rc;
  2908   2909   }
  2909   2910   
  2910   2911   /* Forward declaration required by incrVacuumStep(). */
  2911   2912   static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
  2912         -
  2913         -/*
  2914         -** Perform a single step of an incremental-vacuum. If successful,
  2915         -** return SQLITE_OK. If there is no work to do (and therefore no
  2916         -** point in calling this function again), return SQLITE_DONE.
  2917         -**
  2918         -** More specificly, this function attempts to re-organize the 
  2919         -** database so that the last page of the file currently in use
  2920         -** is no longer in use.
  2921         -**
  2922         -** If the nFin parameter is non-zero, this function assumes
  2923         -** that the caller will keep calling incrVacuumStep() until
  2924         -** it returns SQLITE_DONE or an error, and that nFin is the
  2925         -** number of pages the database file will contain after this 
  2926         -** process is complete.  If nFin is zero, it is assumed that
  2927         -** incrVacuumStep() will be called a finite amount of times
  2928         -** which may or may not empty the freelist.  A full autovacuum
  2929         -** has nFin>0.  A "PRAGMA incremental_vacuum" has nFin==0.
  2930         -*/
  2931         -static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
         2913  +#define BTALLOC_ANY   0           /* Allocate any page */
         2914  +#define BTALLOC_EXACT 1           /* Allocate exact page if possible */
         2915  +#define BTALLOC_LE    2           /* Allocate any page <= the parameter */
         2916  +
         2917  +/*
         2918  +** Perform a single step of an incremental-vacuum. If successful, return
         2919  +** SQLITE_OK. If there is no work to do (and therefore no point in 
         2920  +** calling this function again), return SQLITE_DONE. Or, if an error 
         2921  +** occurs, return some other error code.
         2922  +**
         2923  +** More specificly, this function attempts to re-organize the database so 
         2924  +** that the last page of the file currently in use is no longer in use.
         2925  +**
         2926  +** Parameter nFin is the number of pages that this database would contain
         2927  +** were this function called until it returns SQLITE_DONE.
         2928  +**
         2929  +** If the bCommit parameter is non-zero, this function assumes that the 
         2930  +** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE 
         2931  +** or an error. bCommit is passed true for an auto-vacuum-on-commmit 
         2932  +** operation, or false for an incremental vacuum.
         2933  +*/
         2934  +static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
  2932   2935     Pgno nFreeList;           /* Number of pages still on the free-list */
  2933   2936     int rc;
  2934   2937   
  2935   2938     assert( sqlite3_mutex_held(pBt->mutex) );
  2936   2939     assert( iLastPg>nFin );
  2937   2940   
  2938   2941     if( !PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg!=PENDING_BYTE_PAGE(pBt) ){
................................................................................
  2949   2952         return rc;
  2950   2953       }
  2951   2954       if( eType==PTRMAP_ROOTPAGE ){
  2952   2955         return SQLITE_CORRUPT_BKPT;
  2953   2956       }
  2954   2957   
  2955   2958       if( eType==PTRMAP_FREEPAGE ){
  2956         -      if( nFin==0 ){
         2959  +      if( bCommit==0 ){
  2957   2960           /* Remove the page from the files free-list. This is not required
  2958         -        ** if nFin is non-zero. In that case, the free-list will be
         2961  +        ** if bCommit is non-zero. In that case, the free-list will be
  2959   2962           ** truncated to zero after this function returns, so it doesn't 
  2960   2963           ** matter if it still contains some garbage entries.
  2961   2964           */
  2962   2965           Pgno iFreePg;
  2963   2966           MemPage *pFreePg;
  2964         -        rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1);
         2967  +        rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT);
  2965   2968           if( rc!=SQLITE_OK ){
  2966   2969             return rc;
  2967   2970           }
  2968   2971           assert( iFreePg==iLastPg );
  2969   2972           releasePage(pFreePg);
  2970   2973         }
  2971   2974       } else {
  2972   2975         Pgno iFreePg;             /* Index of free page to move pLastPg to */
  2973   2976         MemPage *pLastPg;
         2977  +      u8 eMode = BTALLOC_ANY;   /* Mode parameter for allocateBtreePage() */
         2978  +      Pgno iNear = 0;           /* nearby parameter for allocateBtreePage() */
  2974   2979   
  2975   2980         rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0);
  2976   2981         if( rc!=SQLITE_OK ){
  2977   2982           return rc;
  2978   2983         }
  2979   2984   
  2980         -      /* If nFin is zero, this loop runs exactly once and page pLastPg
         2985  +      /* If bCommit is zero, this loop runs exactly once and page pLastPg
  2981   2986         ** is swapped with the first free page pulled off the free list.
  2982   2987         **
  2983         -      ** On the other hand, if nFin is greater than zero, then keep
         2988  +      ** On the other hand, if bCommit is greater than zero, then keep
  2984   2989         ** looping until a free-page located within the first nFin pages
  2985   2990         ** of the file is found.
  2986   2991         */
         2992  +      if( bCommit==0 ){
         2993  +        eMode = BTALLOC_LE;
         2994  +        iNear = nFin;
         2995  +      }
  2987   2996         do {
  2988   2997           MemPage *pFreePg;
  2989         -        rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0);
         2998  +        rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode);
  2990   2999           if( rc!=SQLITE_OK ){
  2991   3000             releasePage(pLastPg);
  2992   3001             return rc;
  2993   3002           }
  2994   3003           releasePage(pFreePg);
  2995         -      }while( nFin!=0 && iFreePg>nFin );
         3004  +      }while( bCommit && iFreePg>nFin );
  2996   3005         assert( iFreePg<iLastPg );
  2997   3006         
  2998         -      rc = sqlite3PagerWrite(pLastPg->pDbPage);
  2999         -      if( rc==SQLITE_OK ){
  3000         -        rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
  3001         -      }
         3007  +      rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
  3002   3008         releasePage(pLastPg);
  3003   3009         if( rc!=SQLITE_OK ){
  3004   3010           return rc;
  3005   3011         }
  3006   3012       }
  3007   3013     }
  3008   3014   
  3009         -  if( nFin==0 ){
  3010         -    iLastPg--;
  3011         -    while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){
  3012         -      if( PTRMAP_ISPAGE(pBt, iLastPg) ){
  3013         -        MemPage *pPg;
  3014         -        rc = btreeGetPage(pBt, iLastPg, &pPg, 0);
  3015         -        if( rc!=SQLITE_OK ){
  3016         -          return rc;
  3017         -        }
  3018         -        rc = sqlite3PagerWrite(pPg->pDbPage);
  3019         -        releasePage(pPg);
  3020         -        if( rc!=SQLITE_OK ){
  3021         -          return rc;
  3022         -        }
  3023         -      }
         3015  +  if( bCommit==0 ){
         3016  +    do {
  3024   3017         iLastPg--;
  3025         -    }
  3026         -    sqlite3PagerTruncateImage(pBt->pPager, iLastPg);
         3018  +    }while( iLastPg==PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg) );
         3019  +    pBt->bDoTruncate = 1;
  3027   3020       pBt->nPage = iLastPg;
  3028   3021     }
  3029   3022     return SQLITE_OK;
  3030   3023   }
         3024  +
         3025  +/*
         3026  +** The database opened by the first argument is an auto-vacuum database
         3027  +** nOrig pages in size containing nFree free pages. Return the expected 
         3028  +** size of the database in pages following an auto-vacuum operation.
         3029  +*/
         3030  +static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){
         3031  +  int nEntry;                     /* Number of entries on one ptrmap page */
         3032  +  Pgno nPtrmap;                   /* Number of PtrMap pages to be freed */
         3033  +  Pgno nFin;                      /* Return value */
         3034  +
         3035  +  nEntry = pBt->usableSize/5;
         3036  +  nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
         3037  +  nFin = nOrig - nFree - nPtrmap;
         3038  +  if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
         3039  +    nFin--;
         3040  +  }
         3041  +  while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
         3042  +    nFin--;
         3043  +  }
         3044  +
         3045  +  return nFin;
         3046  +}
  3031   3047   
  3032   3048   /*
  3033   3049   ** A write-transaction must be opened before calling this function.
  3034   3050   ** It performs a single unit of work towards an incremental vacuum.
  3035   3051   **
  3036   3052   ** If the incremental vacuum is finished after this function has run,
  3037   3053   ** SQLITE_DONE is returned. If it is not finished, but no error occurred,
................................................................................
  3042   3058     BtShared *pBt = p->pBt;
  3043   3059   
  3044   3060     sqlite3BtreeEnter(p);
  3045   3061     assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE );
  3046   3062     if( !pBt->autoVacuum ){
  3047   3063       rc = SQLITE_DONE;
  3048   3064     }else{
  3049         -    invalidateAllOverflowCache(pBt);
  3050         -    rc = incrVacuumStep(pBt, 0, btreePagecount(pBt));
  3051         -    if( rc==SQLITE_OK ){
  3052         -      rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
  3053         -      put4byte(&pBt->pPage1->aData[28], pBt->nPage);
         3065  +    Pgno nOrig = btreePagecount(pBt);
         3066  +    Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
         3067  +    Pgno nFin = finalDbSize(pBt, nOrig, nFree);
         3068  +
         3069  +    if( nOrig<nFin ){
         3070  +      rc = SQLITE_CORRUPT_BKPT;
         3071  +    }else if( nFree>0 ){
         3072  +      invalidateAllOverflowCache(pBt);
         3073  +      rc = incrVacuumStep(pBt, nFin, nOrig, 0);
         3074  +      if( rc==SQLITE_OK ){
         3075  +        rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
         3076  +        put4byte(&pBt->pPage1->aData[28], pBt->nPage);
         3077  +      }
         3078  +    }else{
         3079  +      rc = SQLITE_DONE;
  3054   3080       }
  3055   3081     }
  3056   3082     sqlite3BtreeLeave(p);
  3057   3083     return rc;
  3058   3084   }
  3059   3085   
  3060   3086   /*
................................................................................
  3073   3099   
  3074   3100     assert( sqlite3_mutex_held(pBt->mutex) );
  3075   3101     invalidateAllOverflowCache(pBt);
  3076   3102     assert(pBt->autoVacuum);
  3077   3103     if( !pBt->incrVacuum ){
  3078   3104       Pgno nFin;         /* Number of pages in database after autovacuuming */
  3079   3105       Pgno nFree;        /* Number of pages on the freelist initially */
  3080         -    Pgno nPtrmap;      /* Number of PtrMap pages to be freed */
  3081   3106       Pgno iFree;        /* The next page to be freed */
  3082         -    int nEntry;        /* Number of entries on one ptrmap page */
  3083   3107       Pgno nOrig;        /* Database size before freeing */
  3084   3108   
  3085   3109       nOrig = btreePagecount(pBt);
  3086   3110       if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){
  3087   3111         /* It is not possible to create a database for which the final page
  3088   3112         ** is either a pointer-map page or the pending-byte page. If one
  3089   3113         ** is encountered, this indicates corruption.
  3090   3114         */
  3091   3115         return SQLITE_CORRUPT_BKPT;
  3092   3116       }
  3093   3117   
  3094   3118       nFree = get4byte(&pBt->pPage1->aData[36]);
  3095         -    nEntry = pBt->usableSize/5;
  3096         -    nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
  3097         -    nFin = nOrig - nFree - nPtrmap;
  3098         -    if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
  3099         -      nFin--;
  3100         -    }
  3101         -    while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
  3102         -      nFin--;
  3103         -    }
         3119  +    nFin = finalDbSize(pBt, nOrig, nFree);
  3104   3120       if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT;
  3105   3121   
  3106   3122       for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){
  3107         -      rc = incrVacuumStep(pBt, nFin, iFree);
         3123  +      rc = incrVacuumStep(pBt, nFin, iFree, 1);
  3108   3124       }
  3109   3125       if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){
  3110   3126         rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
  3111   3127         put4byte(&pBt->pPage1->aData[32], 0);
  3112   3128         put4byte(&pBt->pPage1->aData[36], 0);
  3113   3129         put4byte(&pBt->pPage1->aData[28], nFin);
  3114         -      sqlite3PagerTruncateImage(pBt->pPager, nFin);
         3130  +      pBt->bDoTruncate = 1;
  3115   3131         pBt->nPage = nFin;
  3116   3132       }
  3117   3133       if( rc!=SQLITE_OK ){
  3118   3134         sqlite3PagerRollback(pPager);
  3119   3135       }
  3120   3136     }
  3121   3137   
................................................................................
  3162   3178       if( pBt->autoVacuum ){
  3163   3179         rc = autoVacuumCommit(pBt);
  3164   3180         if( rc!=SQLITE_OK ){
  3165   3181           sqlite3BtreeLeave(p);
  3166   3182           return rc;
  3167   3183         }
  3168   3184       }
         3185  +    if( pBt->bDoTruncate ){
         3186  +      sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
         3187  +    }
  3169   3188   #endif
  3170   3189       rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
  3171   3190       sqlite3BtreeLeave(p);
  3172   3191     }
  3173   3192     return rc;
  3174   3193   }
  3175   3194   
................................................................................
  3177   3196   ** This function is called from both BtreeCommitPhaseTwo() and BtreeRollback()
  3178   3197   ** at the conclusion of a transaction.
  3179   3198   */
  3180   3199   static void btreeEndTransaction(Btree *p){
  3181   3200     BtShared *pBt = p->pBt;
  3182   3201     assert( sqlite3BtreeHoldsMutex(p) );
  3183   3202   
         3203  +#ifndef SQLITE_OMIT_AUTOVACUUM
         3204  +  pBt->bDoTruncate = 0;
         3205  +#endif
  3184   3206     btreeClearHasContent(pBt);
  3185   3207     if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
  3186   3208       /* If there are other active statements that belong to this database
  3187   3209       ** handle, downgrade to a read-only transaction. The other statements
  3188   3210       ** may still be reading from the database.  */
  3189   3211       downgradeAllSharedCacheTableLocks(p);
  3190   3212       p->inTrans = TRANS_READ;
................................................................................
  4863   4885   ** is only used by auto-vacuum databases when allocating a new table.
  4864   4886   */
  4865   4887   static int allocateBtreePage(
  4866   4888     BtShared *pBt, 
  4867   4889     MemPage **ppPage, 
  4868   4890     Pgno *pPgno, 
  4869   4891     Pgno nearby,
  4870         -  u8 exact
         4892  +  u8 eMode
  4871   4893   ){
  4872   4894     MemPage *pPage1;
  4873   4895     int rc;
  4874   4896     u32 n;     /* Number of pages on the freelist */
  4875   4897     u32 k;     /* Number of leaves on the trunk of the freelist */
  4876   4898     MemPage *pTrunk = 0;
  4877   4899     MemPage *pPrevTrunk = 0;
................................................................................
  4891   4913       u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
  4892   4914       
  4893   4915       /* If the 'exact' parameter was true and a query of the pointer-map
  4894   4916       ** shows that the page 'nearby' is somewhere on the free-list, then
  4895   4917       ** the entire-list will be searched for that page.
  4896   4918       */
  4897   4919   #ifndef SQLITE_OMIT_AUTOVACUUM
  4898         -    if( exact && nearby<=mxPage ){
  4899         -      u8 eType;
  4900         -      assert( nearby>0 );
  4901         -      assert( pBt->autoVacuum );
  4902         -      rc = ptrmapGet(pBt, nearby, &eType, 0);
  4903         -      if( rc ) return rc;
  4904         -      if( eType==PTRMAP_FREEPAGE ){
  4905         -        searchList = 1;
         4920  +    if( eMode==BTALLOC_EXACT ){
         4921  +      if( nearby<=mxPage ){
         4922  +        u8 eType;
         4923  +        assert( nearby>0 );
         4924  +        assert( pBt->autoVacuum );
         4925  +        rc = ptrmapGet(pBt, nearby, &eType, 0);
         4926  +        if( rc ) return rc;
         4927  +        if( eType==PTRMAP_FREEPAGE ){
         4928  +          searchList = 1;
         4929  +        }
  4906   4930         }
  4907         -      *pPgno = nearby;
         4931  +    }else if( eMode==BTALLOC_LE ){
         4932  +      searchList = 1;
  4908   4933       }
  4909   4934   #endif
  4910   4935   
  4911   4936       /* Decrement the free-list count by 1. Set iTrunk to the index of the
  4912   4937       ** first free-list trunk page. iPrevTrunk is initially 1.
  4913   4938       */
  4914   4939       rc = sqlite3PagerWrite(pPage1->pDbPage);
................................................................................
  4955   4980           pTrunk = 0;
  4956   4981           TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
  4957   4982         }else if( k>(u32)(pBt->usableSize/4 - 2) ){
  4958   4983           /* Value of k is out of range.  Database corruption */
  4959   4984           rc = SQLITE_CORRUPT_BKPT;
  4960   4985           goto end_allocate_page;
  4961   4986   #ifndef SQLITE_OMIT_AUTOVACUUM
  4962         -      }else if( searchList && nearby==iTrunk ){
         4987  +      }else if( searchList 
         4988  +            && (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE)) 
         4989  +      ){
  4963   4990           /* The list is being searched and this trunk page is the page
  4964   4991           ** to allocate, regardless of whether it has leaves.
  4965   4992           */
  4966         -        assert( *pPgno==iTrunk );
         4993  +        *pPgno = iTrunk;
  4967   4994           *ppPage = pTrunk;
  4968   4995           searchList = 0;
  4969   4996           rc = sqlite3PagerWrite(pTrunk->pDbPage);
  4970   4997           if( rc ){
  4971   4998             goto end_allocate_page;
  4972   4999           }
  4973   5000           if( k==0 ){
................................................................................
  5022   5049         }else if( k>0 ){
  5023   5050           /* Extract a leaf from the trunk */
  5024   5051           u32 closest;
  5025   5052           Pgno iPage;
  5026   5053           unsigned char *aData = pTrunk->aData;
  5027   5054           if( nearby>0 ){
  5028   5055             u32 i;
  5029         -          int dist;
  5030   5056             closest = 0;
  5031         -          dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
  5032         -          for(i=1; i<k; i++){
  5033         -            int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
  5034         -            if( d2<dist ){
  5035         -              closest = i;
  5036         -              dist = d2;
         5057  +          if( eMode==BTALLOC_LE ){
         5058  +            for(i=0; i<k; i++){
         5059  +              iPage = get4byte(&aData[8+i*4]);
         5060  +              if( iPage<=nearby ){
         5061  +                closest = i;
         5062  +                break;
         5063  +              }
         5064  +            }
         5065  +          }else{
         5066  +            int dist;
         5067  +            dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
         5068  +            for(i=1; i<k; i++){
         5069  +              int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
         5070  +              if( d2<dist ){
         5071  +                closest = i;
         5072  +                dist = d2;
         5073  +              }
  5037   5074               }
  5038   5075             }
  5039   5076           }else{
  5040   5077             closest = 0;
  5041   5078           }
  5042   5079   
  5043   5080           iPage = get4byte(&aData[8+closest*4]);
  5044   5081           testcase( iPage==mxPage );
  5045   5082           if( iPage>mxPage ){
  5046   5083             rc = SQLITE_CORRUPT_BKPT;
  5047   5084             goto end_allocate_page;
  5048   5085           }
  5049   5086           testcase( iPage==mxPage );
  5050         -        if( !searchList || iPage==nearby ){
         5087  +        if( !searchList 
         5088  +         || (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE)) 
         5089  +        ){
  5051   5090             int noContent;
  5052   5091             *pPgno = iPage;
  5053   5092             TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
  5054   5093                    ": %d more free pages\n",
  5055   5094                    *pPgno, closest+1, k, pTrunk->pgno, n-1));
  5056   5095             rc = sqlite3PagerWrite(pTrunk->pDbPage);
  5057   5096             if( rc ) goto end_allocate_page;
................................................................................
  5070   5109             searchList = 0;
  5071   5110           }
  5072   5111         }
  5073   5112         releasePage(pPrevTrunk);
  5074   5113         pPrevTrunk = 0;
  5075   5114       }while( searchList );
  5076   5115     }else{
  5077         -    /* There are no pages on the freelist, so create a new page at the
  5078         -    ** end of the file */
         5116  +    /* There are no pages on the freelist, so append a new page to the
         5117  +    ** database image.
         5118  +    **
         5119  +    ** Normally, new pages allocated by this block can be requested from the
         5120  +    ** pager layer with the 'no-content' flag set. This prevents the pager
         5121  +    ** from trying to read the pages content from disk. However, if the
         5122  +    ** current transaction has already run one or more incremental-vacuum
         5123  +    ** steps, then the page we are about to allocate may contain content
         5124  +    ** that is required in the event of a rollback. In this case, do
         5125  +    ** not set the no-content flag. This causes the pager to load and journal
         5126  +    ** the current page content before overwriting it.
         5127  +    **
         5128  +    ** Note that the pager will not actually attempt to load or journal 
         5129  +    ** content for any page that really does lie past the end of the database
         5130  +    ** file on disk. So the effects of disabling the no-content optimization
         5131  +    ** here are confined to those pages that lie between the end of the
         5132  +    ** database image and the end of the database file.
         5133  +    */
         5134  +    int bNoContent = (0==pBt->bDoTruncate);
         5135  +
  5079   5136       rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
  5080   5137       if( rc ) return rc;
  5081   5138       pBt->nPage++;
  5082   5139       if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ) pBt->nPage++;
  5083   5140   
  5084   5141   #ifndef SQLITE_OMIT_AUTOVACUUM
  5085   5142       if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, pBt->nPage) ){
................................................................................
  5086   5143         /* If *pPgno refers to a pointer-map page, allocate two new pages
  5087   5144         ** at the end of the file instead of one. The first allocated page
  5088   5145         ** becomes a new pointer-map page, the second is used by the caller.
  5089   5146         */
  5090   5147         MemPage *pPg = 0;
  5091   5148         TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
  5092   5149         assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
  5093         -      rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1);
         5150  +      rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent);
  5094   5151         if( rc==SQLITE_OK ){
  5095   5152           rc = sqlite3PagerWrite(pPg->pDbPage);
  5096   5153           releasePage(pPg);
  5097   5154         }
  5098   5155         if( rc ) return rc;
  5099   5156         pBt->nPage++;
  5100   5157         if( pBt->nPage==PENDING_BYTE_PAGE(pBt) ){ pBt->nPage++; }
  5101   5158       }
  5102   5159   #endif
  5103   5160       put4byte(28 + (u8*)pBt->pPage1->aData, pBt->nPage);
  5104   5161       *pPgno = pBt->nPage;
  5105   5162   
  5106   5163       assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
  5107         -    rc = btreeGetPage(pBt, *pPgno, ppPage, 1);
         5164  +    rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
  5108   5165       if( rc ) return rc;
  5109   5166       rc = sqlite3PagerWrite((*ppPage)->pDbPage);
  5110   5167       if( rc!=SQLITE_OK ){
  5111   5168         releasePage(*ppPage);
  5112   5169       }
  5113   5170       TRACE(("ALLOCATE: %d from end of file\n", *pPgno));
  5114   5171     }
................................................................................
  7115   7172       }
  7116   7173       assert( pgnoRoot>=3 );
  7117   7174   
  7118   7175       /* Allocate a page. The page that currently resides at pgnoRoot will
  7119   7176       ** be moved to the allocated page (unless the allocated page happens
  7120   7177       ** to reside at pgnoRoot).
  7121   7178       */
  7122         -    rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1);
         7179  +    rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT);
  7123   7180       if( rc!=SQLITE_OK ){
  7124   7181         return rc;
  7125   7182       }
  7126   7183   
  7127   7184       if( pgnoMove!=pgnoRoot ){
  7128   7185         /* pgnoRoot is the page that will be used for the root-page of
  7129   7186         ** the new table (assuming an error did not occur). But we were

Changes to src/btreeInt.h.

   407    407     sqlite3 *db;          /* Database connection currently using this Btree */
   408    408     BtCursor *pCursor;    /* A list of all open cursors */
   409    409     MemPage *pPage1;      /* First page of the database */
   410    410     u8 openFlags;         /* Flags to sqlite3BtreeOpen() */
   411    411   #ifndef SQLITE_OMIT_AUTOVACUUM
   412    412     u8 autoVacuum;        /* True if auto-vacuum is enabled */
   413    413     u8 incrVacuum;        /* True if incr-vacuum is enabled */
          414  +  u8 bDoTruncate;       /* True to truncate db on commit */
   414    415   #endif
   415    416     u8 inTransaction;     /* Transaction state */
   416    417     u8 max1bytePayload;   /* Maximum first byte of cell for a 1-byte payload */
   417    418     u16 btsFlags;         /* Boolean parameters.  See BTS_* macros below */
   418    419     u16 maxLocal;         /* Maximum local payload in non-LEAFDATA tables */
   419    420     u16 minLocal;         /* Minimum local payload in non-LEAFDATA tables */
   420    421     u16 maxLeaf;          /* Maximum local payload in a LEAFDATA table */

Changes to src/pager.c.

  1834   1834     if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
  1835   1835       pPager->errCode = rc;
  1836   1836       pPager->eState = PAGER_ERROR;
  1837   1837     }
  1838   1838     return rc;
  1839   1839   }
  1840   1840   
         1841  +static int pager_truncate(Pager *pPager, Pgno nPage);
         1842  +
  1841   1843   /*
  1842   1844   ** This routine ends a transaction. A transaction is usually ended by 
  1843   1845   ** either a COMMIT or a ROLLBACK operation. This routine may be called 
  1844   1846   ** after rollback of a hot-journal, or if an error occurs while opening
  1845   1847   ** the journal file or writing the very first journal-header of a
  1846   1848   ** database transaction.
  1847   1849   ** 
................................................................................
  1887   1889   ** database then the IO error code is returned to the user. If the 
  1888   1890   ** operation to finalize the journal file fails, then the code still
  1889   1891   ** tries to unlock the database file if not in exclusive mode. If the
  1890   1892   ** unlock operation fails as well, then the first error code related
  1891   1893   ** to the first error encountered (the journal finalization one) is
  1892   1894   ** returned.
  1893   1895   */
  1894         -static int pager_end_transaction(Pager *pPager, int hasMaster){
         1896  +static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
  1895   1897     int rc = SQLITE_OK;      /* Error code from journal finalization operation */
  1896   1898     int rc2 = SQLITE_OK;     /* Error code from db file unlock operation */
  1897   1899   
  1898   1900     /* Do nothing if the pager does not have an open write transaction
  1899   1901     ** or at least a RESERVED lock. This function may be called when there
  1900   1902     ** is no write-transaction active but a RESERVED or greater lock is
  1901   1903     ** held under two circumstances:
................................................................................
  1973   1975     if( pagerUseWal(pPager) ){
  1974   1976       /* Drop the WAL write-lock, if any. Also, if the connection was in 
  1975   1977       ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE 
  1976   1978       ** lock held on the database file.
  1977   1979       */
  1978   1980       rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
  1979   1981       assert( rc2==SQLITE_OK );
         1982  +  }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){
         1983  +    /* This branch is taken when committing a transaction in rollback-journal
         1984  +    ** mode if the database file on disk is larger than the database image.
         1985  +    ** At this point the journal has been finalized and the transaction 
         1986  +    ** successfully committed, but the EXCLUSIVE lock is still held on the
         1987  +    ** file. So it is safe to truncate the database file to its minimum
         1988  +    ** required size.  */
         1989  +    assert( pPager->eLock==EXCLUSIVE_LOCK );
         1990  +    rc = pager_truncate(pPager, pPager->dbSize);
  1980   1991     }
         1992  +
  1981   1993     if( !pPager->exclusiveMode 
  1982   1994      && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
  1983   1995     ){
  1984   1996       rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
  1985   1997       pPager->changeCountDone = 0;
  1986   1998     }
  1987   1999     pPager->eState = PAGER_READER;
................................................................................
  2012   2024       assert( assert_pager_state(pPager) );
  2013   2025       if( pPager->eState>=PAGER_WRITER_LOCKED ){
  2014   2026         sqlite3BeginBenignMalloc();
  2015   2027         sqlite3PagerRollback(pPager);
  2016   2028         sqlite3EndBenignMalloc();
  2017   2029       }else if( !pPager->exclusiveMode ){
  2018   2030         assert( pPager->eState==PAGER_READER );
  2019         -      pager_end_transaction(pPager, 0);
         2031  +      pager_end_transaction(pPager, 0, 0);
  2020   2032       }
  2021   2033     }
  2022   2034     pager_unlock(pPager);
  2023   2035   }
  2024   2036   
  2025   2037   /*
  2026   2038   ** Parameter aData must point to a buffer of pPager->pageSize bytes
................................................................................
  2787   2799     }
  2788   2800     if( rc==SQLITE_OK
  2789   2801      && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
  2790   2802     ){
  2791   2803       rc = sqlite3PagerSync(pPager);
  2792   2804     }
  2793   2805     if( rc==SQLITE_OK ){
  2794         -    rc = pager_end_transaction(pPager, zMaster[0]!='\0');
         2806  +    rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0);
  2795   2807       testcase( rc!=SQLITE_OK );
  2796   2808     }
  2797   2809     if( rc==SQLITE_OK && zMaster[0] && res ){
  2798   2810       /* If there was a master journal and this routine will return success,
  2799   2811       ** see if it is possible to delete the master journal.
  2800   2812       */
  2801   2813       rc = pager_delmaster(pPager, zMaster);
................................................................................
  5881   5893           }
  5882   5894         }
  5883   5895     #else
  5884   5896         rc = pager_incr_changecounter(pPager, 0);
  5885   5897     #endif
  5886   5898         if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
  5887   5899     
  5888         -      /* If this transaction has made the database smaller, then all pages
  5889         -      ** being discarded by the truncation must be written to the journal
  5890         -      ** file.
  5891         -      **
  5892         -      ** Before reading the pages with page numbers larger than the 
  5893         -      ** current value of Pager.dbSize, set dbSize back to the value
  5894         -      ** that it took at the start of the transaction. Otherwise, the
  5895         -      ** calls to sqlite3PagerGet() return zeroed pages instead of 
  5896         -      ** reading data from the database file.
  5897         -      */
  5898         -      if( pPager->dbSize<pPager->dbOrigSize 
  5899         -       && pPager->journalMode!=PAGER_JOURNALMODE_OFF
  5900         -      ){
  5901         -        Pgno i;                                   /* Iterator variable */
  5902         -        const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
  5903         -        const Pgno dbSize = pPager->dbSize;       /* Database image size */ 
  5904         -        pPager->dbSize = pPager->dbOrigSize;
  5905         -        for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){
  5906         -          if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
  5907         -            PgHdr *pPage;             /* Page to journal */
  5908         -            rc = sqlite3PagerGet(pPager, i, &pPage);
  5909         -            if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
  5910         -            rc = sqlite3PagerWrite(pPage);
  5911         -            sqlite3PagerUnref(pPage);
  5912         -            if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
  5913         -          }
  5914         -        }
  5915         -        pPager->dbSize = dbSize;
  5916         -      } 
  5917         -  
  5918   5900         /* Write the master journal name into the journal file. If a master 
  5919   5901         ** journal file name has already been written to the journal file, 
  5920   5902         ** or if zMaster is NULL (no master journal), then this call is a no-op.
  5921   5903         */
  5922   5904         rc = writeMasterJournal(pPager, zMaster);
  5923   5905         if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
  5924   5906     
................................................................................
  5938   5920     
  5939   5921         rc = pager_write_pagelist(pPager,sqlite3PcacheDirtyList(pPager->pPCache));
  5940   5922         if( rc!=SQLITE_OK ){
  5941   5923           assert( rc!=SQLITE_IOERR_BLOCKED );
  5942   5924           goto commit_phase_one_exit;
  5943   5925         }
  5944   5926         sqlite3PcacheCleanAll(pPager->pPCache);
  5945         -  
  5946         -      /* If the file on disk is not the same size as the database image,
  5947         -      ** then use pager_truncate to grow or shrink the file here.
  5948         -      */
  5949         -      if( pPager->dbSize!=pPager->dbFileSize ){
         5927  +
         5928  +      /* If the file on disk is smaller than the database image, use 
         5929  +      ** pager_truncate to grow the file here. This can happen if the database
         5930  +      ** image was extended as part of the current transaction and then the
         5931  +      ** last page in the db image moved to the free-list. In this case the
         5932  +      ** last page is never written out to disk, leaving the database file
         5933  +      ** undersized. Fix this now if it is the case.  */
         5934  +      if( pPager->dbSize>pPager->dbFileSize ){
  5950   5935           Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
  5951   5936           assert( pPager->eState==PAGER_WRITER_DBMOD );
  5952   5937           rc = pager_truncate(pPager, nNew);
  5953   5938           if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
  5954   5939         }
  5955   5940     
  5956   5941         /* Finally, sync the database file. */
................................................................................
  6015   6000     ){
  6016   6001       assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) || !pPager->journalOff );
  6017   6002       pPager->eState = PAGER_READER;
  6018   6003       return SQLITE_OK;
  6019   6004     }
  6020   6005   
  6021   6006     PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
  6022         -  rc = pager_end_transaction(pPager, pPager->setMaster);
         6007  +  rc = pager_end_transaction(pPager, pPager->setMaster, 1);
  6023   6008     return pager_error(pPager, rc);
  6024   6009   }
  6025   6010   
  6026   6011   /*
  6027   6012   ** If a write transaction is open, then all changes made within the 
  6028   6013   ** transaction are reverted and the current write-transaction is closed.
  6029   6014   ** The pager falls back to PAGER_READER state if successful, or PAGER_ERROR
................................................................................
  6060   6045     assert( assert_pager_state(pPager) );
  6061   6046     if( pPager->eState==PAGER_ERROR ) return pPager->errCode;
  6062   6047     if( pPager->eState<=PAGER_READER ) return SQLITE_OK;
  6063   6048   
  6064   6049     if( pagerUseWal(pPager) ){
  6065   6050       int rc2;
  6066   6051       rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
  6067         -    rc2 = pager_end_transaction(pPager, pPager->setMaster);
         6052  +    rc2 = pager_end_transaction(pPager, pPager->setMaster, 0);
  6068   6053       if( rc==SQLITE_OK ) rc = rc2;
  6069   6054     }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
  6070   6055       int eState = pPager->eState;
  6071         -    rc = pager_end_transaction(pPager, 0);
         6056  +    rc = pager_end_transaction(pPager, 0, 0);
  6072   6057       if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
  6073   6058         /* This can happen using journal_mode=off. Move the pager to the error 
  6074   6059         ** state to indicate that the contents of the cache may not be trusted.
  6075   6060         ** Any active readers will get SQLITE_ABORT.
  6076   6061         */
  6077   6062         pPager->errCode = SQLITE_ABORT;
  6078   6063         pPager->eState = PAGER_ERROR;

Changes to src/test_vfs.c.

   261    261   
   262    262   
   263    263   static void tvfsExecTcl(
   264    264     Testvfs *p, 
   265    265     const char *zMethod,
   266    266     Tcl_Obj *arg1,
   267    267     Tcl_Obj *arg2,
   268         -  Tcl_Obj *arg3
          268  +  Tcl_Obj *arg3,
          269  +  Tcl_Obj *arg4
   269    270   ){
   270    271     int rc;                         /* Return code from Tcl_EvalObj() */
   271    272     Tcl_Obj *pEval;
   272    273     assert( p->pScript );
   273    274   
   274    275     assert( zMethod );
   275    276     assert( p );
................................................................................
   278    279   
   279    280     pEval = Tcl_DuplicateObj(p->pScript);
   280    281     Tcl_IncrRefCount(p->pScript);
   281    282     Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zMethod, -1));
   282    283     if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1);
   283    284     if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2);
   284    285     if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3);
          286  +  if( arg4 ) Tcl_ListObjAppendElement(p->interp, pEval, arg4);
   285    287   
   286    288     rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
   287    289     if( rc!=TCL_OK ){
   288    290       Tcl_BackgroundError(p->interp);
   289    291       Tcl_ResetResult(p->interp);
   290    292     }
   291    293   }
................................................................................
   298    300     int rc;
   299    301     TestvfsFile *pTestfile = (TestvfsFile *)pFile;
   300    302     TestvfsFd *pFd = pTestfile->pFd;
   301    303     Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
   302    304   
   303    305     if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
   304    306       tvfsExecTcl(p, "xClose", 
   305         -        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
          307  +        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
   306    308       );
   307    309     }
   308    310   
   309    311     if( pFd->pShmId ){
   310    312       Tcl_DecrRefCount(pFd->pShmId);
   311    313       pFd->pShmId = 0;
   312    314     }
................................................................................
   329    331     sqlite_int64 iOfst
   330    332   ){
   331    333     int rc = SQLITE_OK;
   332    334     TestvfsFd *pFd = tvfsGetFd(pFile);
   333    335     Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
   334    336     if( p->pScript && p->mask&TESTVFS_READ_MASK ){
   335    337       tvfsExecTcl(p, "xRead", 
   336         -        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
          338  +        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
   337    339       );
   338    340       tvfsResultCode(p, &rc);
   339    341     }
   340    342     if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){
   341    343       rc = SQLITE_IOERR;
   342    344     }
   343    345     if( rc==SQLITE_OK ){
................................................................................
   358    360     int rc = SQLITE_OK;
   359    361     TestvfsFd *pFd = tvfsGetFd(pFile);
   360    362     Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
   361    363   
   362    364     if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
   363    365       tvfsExecTcl(p, "xWrite", 
   364    366           Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 
   365         -        Tcl_NewWideIntObj(iOfst)
          367  +        Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt)
   366    368       );
   367    369       tvfsResultCode(p, &rc);
   368    370     }
   369    371   
   370    372     if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){
   371    373       rc = SQLITE_FULL;
   372    374     }
................................................................................
   386    388   static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
   387    389     int rc = SQLITE_OK;
   388    390     TestvfsFd *pFd = tvfsGetFd(pFile);
   389    391     Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
   390    392   
   391    393     if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
   392    394       tvfsExecTcl(p, "xTruncate", 
   393         -        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
          395  +        Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
   394    396       );
   395    397       tvfsResultCode(p, &rc);
   396    398     }
   397    399     
   398    400     if( rc==SQLITE_OK ){
   399    401       rc = sqlite3OsTruncate(pFd->pReal, size);
   400    402     }
................................................................................
   427    429           break;
   428    430         default:
   429    431           assert(0);
   430    432       }
   431    433   
   432    434       tvfsExecTcl(p, "xSync", 
   433    435           Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
   434         -        Tcl_NewStringObj(zFlags, -1)
          436  +        Tcl_NewStringObj(zFlags, -1), 0
   435    437       );
   436    438       tvfsResultCode(p, &rc);
   437    439     }
   438    440   
   439    441     if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
   440    442   
   441    443     if( rc==SQLITE_OK ){
................................................................................
   574    576         while( *z ){
   575    577           Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
   576    578           z += strlen(z) + 1;
   577    579           Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1));
   578    580           z += strlen(z) + 1;
   579    581         }
   580    582       }
   581         -    tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0);
          583  +    tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0, 0);
   582    584       Tcl_DecrRefCount(pArg);
   583    585       if( tvfsResultCode(p, &rc) ){
   584    586         if( rc!=SQLITE_OK ) return rc;
   585    587       }else{
   586    588         pId = Tcl_GetObjResult(p->interp);
   587    589       }
   588    590     }
................................................................................
   631    633   */
   632    634   static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
   633    635     int rc = SQLITE_OK;
   634    636     Testvfs *p = (Testvfs *)pVfs->pAppData;
   635    637   
   636    638     if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){
   637    639       tvfsExecTcl(p, "xDelete", 
   638         -        Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0
          640  +        Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0
   639    641       );
   640    642       tvfsResultCode(p, &rc);
   641    643     }
   642    644     if( rc==SQLITE_OK ){
   643    645       rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
   644    646     }
   645    647     return rc;
................................................................................
   659    661     if( p->pScript && p->mask&TESTVFS_ACCESS_MASK ){
   660    662       int rc;
   661    663       char *zArg = 0;
   662    664       if( flags==SQLITE_ACCESS_EXISTS ) zArg = "SQLITE_ACCESS_EXISTS";
   663    665       if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE";
   664    666       if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ";
   665    667       tvfsExecTcl(p, "xAccess", 
   666         -        Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0
          668  +        Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0, 0
   667    669       );
   668    670       if( tvfsResultCode(p, &rc) ){
   669    671         if( rc!=SQLITE_OK ) return rc;
   670    672       }else{
   671    673         Tcl_Interp *interp = p->interp;
   672    674         if( TCL_OK==Tcl_GetBooleanFromObj(0, Tcl_GetObjResult(interp), pResOut) ){
   673    675           return SQLITE_OK;
................................................................................
   687    689     const char *zPath, 
   688    690     int nOut, 
   689    691     char *zOut
   690    692   ){
   691    693     Testvfs *p = (Testvfs *)pVfs->pAppData;
   692    694     if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){
   693    695       int rc;
   694         -    tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0);
          696  +    tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0, 0);
   695    697       if( tvfsResultCode(p, &rc) ){
   696    698         if( rc!=SQLITE_OK ) return rc;
   697    699       }
   698    700     }
   699    701     return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
   700    702   }
   701    703   
................................................................................
   767    769   
   768    770     /* Evaluate the Tcl script: 
   769    771     **
   770    772     **   SCRIPT xShmOpen FILENAME
   771    773     */
   772    774     Tcl_ResetResult(p->interp);
   773    775     if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
   774         -    tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
          776  +    tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0);
   775    777       if( tvfsResultCode(p, &rc) ){
   776    778         if( rc!=SQLITE_OK ) return rc;
   777    779       }
   778    780     }
   779    781   
   780    782     assert( rc==SQLITE_OK );
   781    783     if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){
................................................................................
   837    839     if( p->pScript && p->mask&TESTVFS_SHMMAP_MASK ){
   838    840       Tcl_Obj *pArg = Tcl_NewObj();
   839    841       Tcl_IncrRefCount(pArg);
   840    842       Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage));
   841    843       Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
   842    844       Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
   843    845       tvfsExecTcl(p, "xShmMap", 
   844         -        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg
          846  +        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg, 0
   845    847       );
   846    848       tvfsResultCode(p, &rc);
   847    849       Tcl_DecrRefCount(pArg);
   848    850     }
   849    851     if( rc==SQLITE_OK && p->mask&TESTVFS_SHMMAP_MASK && tvfsInjectIoerr(p) ){
   850    852       rc = SQLITE_IOERR;
   851    853     }
................................................................................
   887    889       if( flags & SQLITE_SHM_SHARED ){
   888    890         strcpy(&zLock[nLock], " shared");
   889    891       }else{
   890    892         strcpy(&zLock[nLock], " exclusive");
   891    893       }
   892    894       tvfsExecTcl(p, "xShmLock", 
   893    895           Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
   894         -        Tcl_NewStringObj(zLock, -1)
          896  +        Tcl_NewStringObj(zLock, -1), 0
   895    897       );
   896    898       tvfsResultCode(p, &rc);
   897    899     }
   898    900   
   899    901     if( rc==SQLITE_OK && p->mask&TESTVFS_SHMLOCK_MASK && tvfsInjectIoerr(p) ){
   900    902       rc = SQLITE_IOERR;
   901    903     }
................................................................................
   933    935     if( p->isFullshm ){
   934    936       sqlite3OsShmBarrier(pFd->pReal);
   935    937       return;
   936    938     }
   937    939   
   938    940     if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
   939    941       tvfsExecTcl(p, "xShmBarrier", 
   940         -        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
          942  +        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0
   941    943       );
   942    944     }
   943    945   }
   944    946   
   945    947   static int tvfsShmUnmap(
   946    948     sqlite3_file *pFile,
   947    949     int deleteFlag
................................................................................
   957    959     }
   958    960   
   959    961     if( !pBuffer ) return SQLITE_OK;
   960    962     assert( pFd->pShmId && pFd->pShm );
   961    963   
   962    964     if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
   963    965       tvfsExecTcl(p, "xShmUnmap", 
   964         -        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
          966  +        Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0
   965    967       );
   966    968       tvfsResultCode(p, &rc);
   967    969     }
   968    970   
   969    971     for(ppFd=&pBuffer->pFile; *ppFd!=pFd; ppFd=&((*ppFd)->pNext));
   970    972     assert( (*ppFd)==pFd );
   971    973     *ppFd = pFd->pNext;

Added test/incrvacuum3.test.

            1  +# 2013 Feb 25
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for the SQLite library, focusing
           12  +# on the incremental vacuum feature.
           13  +#
           14  +# The tests in this file were added at the same time as optimizations 
           15  +# were made to:
           16  +#
           17  +#   * Truncate the database after a rollback mode commit, and
           18  +#
           19  +#   * Avoid moving pages to locations from which they may need to be moved
           20  +#     a second time if an incremental-vacuum proccess is allowed to vacuum
           21  +#     the entire database.
           22  +#
           23  +
           24  +set testdir [file dirname $argv0]
           25  +source $testdir/tester.tcl
           26  +set testprefix incrvacuum3
           27  +
           28  +# If this build of the library does not support auto-vacuum, omit this
           29  +# whole file.
           30  +ifcapable {!autovacuum || !pragma} {
           31  +  finish_test
           32  +  return
           33  +}
           34  +
           35  +proc check_on_disk {} {
           36  +
           37  +  # Copy the files for database "test.db" to "test2.db".
           38  +  forcedelete test2.db test2.db-journal test2.db-wal
           39  +  forcecopy test.db test2.db
           40  +  if {[file exists test.db-journal]} { 
           41  +    forcecopy test.db-journal test2.db-journal 
           42  +  }
           43  +  if {[file exists test.db-wal]} { 
           44  +    forcecopy test.db-wal test2.db-wal 
           45  +  }
           46  +
           47  +  # Open "test2.db" and check it is Ok.
           48  +  sqlite3 dbcheck test2.db
           49  +  set ret [dbcheck eval { PRAGMA integrity_check }]
           50  +  dbcheck close
           51  +  set ret
           52  +}
           53  +
           54  +# Run these tests once in rollback journal mode, and once in wal mode.
           55  +#
           56  +foreach {T jrnl_mode} {
           57  +  1 delete
           58  +  2 wal
           59  +} {
           60  +  catch { db close }
           61  +  forcedelete test.db test.db-journal test.db-wal
           62  +  sqlite3 db test.db
           63  +  db eval {
           64  +    PRAGMA cache_size = 5;
           65  +    PRAGMA page_size = 1024;
           66  +    PRAGMA auto_vacuum = 2;
           67  +  }
           68  +  db eval "PRAGMA journal_mode = $jrnl_mode"
           69  +  
           70  +  foreach {tn sql} {
           71  +    1 {
           72  +      CREATE TABLE t1(x UNIQUE);
           73  +      INSERT INTO t1 VALUES(randomblob(400));
           74  +      INSERT INTO t1 VALUES(randomblob(400));
           75  +      INSERT INTO t1 SELECT randomblob(400) FROM t1;    --   4
           76  +      INSERT INTO t1 SELECT randomblob(400) FROM t1;    --   8
           77  +      INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  16
           78  +      INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  32
           79  +      INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  64
           80  +      INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 128
           81  +      INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 256
           82  +    }
           83  +  
           84  +    2 {
           85  +      DELETE FROM t1 WHERE rowid%8;
           86  +    }
           87  +  
           88  +    3 { 
           89  +      BEGIN;
           90  +        PRAGMA incremental_vacuum = 100;
           91  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  64
           92  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 128
           93  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 256
           94  +      ROLLBACK;
           95  +    }
           96  +  
           97  +    4 { 
           98  +      BEGIN;
           99  +        SAVEPOINT one;
          100  +          PRAGMA incremental_vacuum = 100;
          101  +          SAVEPOINT two;
          102  +            INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  64
          103  +            INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 128
          104  +            INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 256
          105  +    }
          106  +  
          107  +    5 {   ROLLBACK to two }
          108  +  
          109  +    6 { ROLLBACK to one }
          110  +  
          111  +    7 { 
          112  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  64
          113  +        PRAGMA incremental_vacuum = 1000;
          114  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 128
          115  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    -- 256
          116  +      ROLLBACK;
          117  +    }
          118  +  
          119  +    8 { 
          120  +      BEGIN;
          121  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  64
          122  +        PRAGMA incremental_vacuum = 1000;
          123  +        INSERT INTO t1 SELECT randomblob(400) FROM t1;    --  128
          124  +      COMMIT;
          125  +    }
          126  +  } {
          127  +    do_execsql_test $T.1.$tn.1 $sql
          128  +    do_execsql_test $T.1.$tn.2 {PRAGMA integrity_check} ok
          129  +    do_test         $T.1.$tn.3 { check_on_disk }        ok
          130  +  }
          131  +
          132  +  do_execsql_test $T.1.x.1 { PRAGMA freelist_count   } 0
          133  +  do_execsql_test $T.1.x.2 { SELECT count(*) FROM t1 } 128
          134  +}
          135  +
          136  +finish_test
          137  +

Changes to test/incrvacuum_ioerr.test.

   135    135     
   136    136     set ::rc 1
   137    137     for {set iTest 1} {$::rc && $iTest<2000} {incr iTest} {
   138    138     
   139    139       # Figure out how big the database is and how many free pages it
   140    140       # has before running incremental-vacuum.
   141    141       #
   142         -    set nPage [expr {[file size test.db]/1024}]
   143    142       set nFree [execsql {pragma freelist_count} db1]
          143  +    set nPage [execsql {pragma page_count} db1]
   144    144     
   145    145       # Now run incremental-vacuum to vacuum 5 pages from the db file.
   146    146       # The iTest'th I/O call is set to fail.
   147    147       #
   148    148       set ::sqlite_io_error_pending $iTest
   149    149       set ::sqlite_io_error_persist 1
   150    150       do_test incrvacuum-ioerr-4.$iTest.1 {
................................................................................
   154    154     
   155    155       set ::sqlite_io_error_pending 0
   156    156       set ::sqlite_io_error_persist 0
   157    157       set ::sqlite_io_error_hit 0
   158    158       set ::sqlite_io_error_hardhit 0
   159    159     
   160    160       set nFree2 [execsql {pragma freelist_count} db1]
   161         -    set nPage2 [expr {[file size test.db]/1024}]
          161  +    set nPage2 [execsql {pragma page_count} db1]
   162    162     
   163    163       do_test incrvacuum-ioerr-4.$iTest.2 {
   164    164         set shrink [expr {$nPage-$nPage2}]
   165    165         expr {$shrink==0 || $shrink==5}
   166    166       } {1}
   167    167     
   168    168       do_test incrvacuum-ioerr-4.$iTest.3 {

Changes to test/tester.tcl.

  1113   1113       if {$msg=="child killed: unknown signal"} {
  1114   1114         set msg "child process exited abnormally"
  1115   1115       }
  1116   1116     }
  1117   1117     
  1118   1118     lappend r $msg
  1119   1119   }
         1120  +
         1121  +proc run_ioerr_prep {} {
         1122  +  set ::sqlite_io_error_pending 0
         1123  +  catch {db close}
         1124  +  catch {db2 close}
         1125  +  catch {forcedelete test.db}
         1126  +  catch {forcedelete test.db-journal}
         1127  +  catch {forcedelete test2.db}
         1128  +  catch {forcedelete test2.db-journal}
         1129  +  set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
         1130  +  sqlite3_extended_result_codes $::DB $::ioerropts(-erc)
         1131  +  if {[info exists ::ioerropts(-tclprep)]} {
         1132  +    eval $::ioerropts(-tclprep)
         1133  +  }
         1134  +  if {[info exists ::ioerropts(-sqlprep)]} {
         1135  +    execsql $::ioerropts(-sqlprep)
         1136  +  }
         1137  +  expr 0
         1138  +}
  1120   1139   
  1121   1140   # Usage: do_ioerr_test <test number> <options...>
  1122   1141   #
  1123   1142   # This proc is used to implement test cases that check that IO errors
  1124   1143   # are correctly handled. The first argument, <test number>, is an integer 
  1125   1144   # used to name the tests executed by this proc. Options are as follows:
  1126   1145   #
................................................................................
  1146   1165     set ::ioerropts(-ckrefcount) 0
  1147   1166     set ::ioerropts(-restoreprng) 1
  1148   1167     array set ::ioerropts $args
  1149   1168   
  1150   1169     # TEMPORARY: For 3.5.9, disable testing of extended result codes. There are
  1151   1170     # a couple of obscure IO errors that do not return them.
  1152   1171     set ::ioerropts(-erc) 0
         1172  +  
         1173  +  # Create a single TCL script from the TCL and SQL specified
         1174  +  # as the body of the test.
         1175  +  set ::ioerrorbody {}
         1176  +  if {[info exists ::ioerropts(-tclbody)]} {
         1177  +    append ::ioerrorbody "$::ioerropts(-tclbody)\n"
         1178  +  }
         1179  +  if {[info exists ::ioerropts(-sqlbody)]} {
         1180  +    append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
         1181  +  }
         1182  +
         1183  +  save_prng_state
         1184  +  if {$::ioerropts(-cksum)} {
         1185  +    run_ioerr_prep
         1186  +    eval $::ioerrorbody
         1187  +    set ::goodcksum [cksum]
         1188  +  }
  1153   1189   
  1154   1190     set ::go 1
  1155   1191     #reset_prng_state
  1156         -  save_prng_state
  1157   1192     for {set n $::ioerropts(-start)} {$::go} {incr n} {
  1158   1193       set ::TN $n
  1159   1194       incr ::ioerropts(-count) -1
  1160   1195       if {$::ioerropts(-count)<0} break
  1161   1196    
  1162   1197       # Skip this IO error if it was specified with the "-exclude" option.
  1163   1198       if {[info exists ::ioerropts(-exclude)]} {
................................................................................
  1166   1201       if {$::ioerropts(-restoreprng)} {
  1167   1202         restore_prng_state
  1168   1203       }
  1169   1204   
  1170   1205       # Delete the files test.db and test2.db, then execute the TCL and 
  1171   1206       # SQL (in that order) to prepare for the test case.
  1172   1207       do_test $testname.$n.1 {
  1173         -      set ::sqlite_io_error_pending 0
  1174         -      catch {db close}
  1175         -      catch {db2 close}
  1176         -      catch {forcedelete test.db}
  1177         -      catch {forcedelete test.db-journal}
  1178         -      catch {forcedelete test2.db}
  1179         -      catch {forcedelete test2.db-journal}
  1180         -      set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
  1181         -      sqlite3_extended_result_codes $::DB $::ioerropts(-erc)
  1182         -      if {[info exists ::ioerropts(-tclprep)]} {
  1183         -        eval $::ioerropts(-tclprep)
  1184         -      }
  1185         -      if {[info exists ::ioerropts(-sqlprep)]} {
  1186         -        execsql $::ioerropts(-sqlprep)
  1187         -      }
  1188         -      expr 0
         1208  +      run_ioerr_prep
  1189   1209       } {0}
  1190   1210   
  1191   1211       # Read the 'checksum' of the database.
  1192   1212       if {$::ioerropts(-cksum)} {
  1193         -      set checksum [cksum]
         1213  +      set ::checksum [cksum]
  1194   1214       }
  1195   1215   
  1196   1216       # Set the Nth IO error to fail.
  1197   1217       do_test $testname.$n.2 [subst {
  1198   1218         set ::sqlite_io_error_persist $::ioerropts(-persist)
  1199   1219         set ::sqlite_io_error_pending $n
  1200   1220       }] $n
  1201         -  
  1202         -    # Create a single TCL script from the TCL and SQL specified
  1203         -    # as the body of the test.
  1204         -    set ::ioerrorbody {}
  1205         -    if {[info exists ::ioerropts(-tclbody)]} {
  1206         -      append ::ioerrorbody "$::ioerropts(-tclbody)\n"
  1207         -    }
  1208         -    if {[info exists ::ioerropts(-sqlbody)]} {
  1209         -      append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
  1210         -    }
  1211   1221   
  1212         -    # Execute the TCL Script created in the above block. If
  1213         -    # there are at least N IO operations performed by SQLite as
  1214         -    # a result of the script, the Nth will fail.
         1222  +    # Execute the TCL script created for the body of this test. If
         1223  +    # at least N IO operations performed by SQLite as a result of 
         1224  +    # the script, the Nth will fail.
  1215   1225       do_test $testname.$n.3 {
  1216   1226         set ::sqlite_io_error_hit 0
  1217   1227         set ::sqlite_io_error_hardhit 0
  1218   1228         set r [catch $::ioerrorbody msg]
  1219   1229         set ::errseen $r
  1220   1230         set rc [sqlite3_errcode $::DB]
  1221   1231         if {$::ioerropts(-erc)} {
................................................................................
  1311   1321       # be the same as before the script that caused the IO error was run.
  1312   1322       #
  1313   1323       if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} {
  1314   1324         do_test $testname.$n.6 {
  1315   1325           catch {db close}
  1316   1326           catch {db2 close}
  1317   1327           set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
  1318         -        cksum
  1319         -      } $checksum
         1328  +        set nowcksum [cksum]
         1329  +        set res [expr {$nowcksum==$::checksum || $nowcksum==$::goodcksum}]
         1330  +        if {$res==0} {
         1331  +          puts "now=$nowcksum"
         1332  +          puts "the=$::checksum"
         1333  +          puts "fwd=$::goodcksum"
         1334  +        }
         1335  +        set res
         1336  +      } 1
  1320   1337       }
  1321   1338   
  1322   1339       set ::sqlite_io_error_hardhit 0
  1323   1340       set ::sqlite_io_error_pending 0
  1324   1341       if {[info exists ::ioerropts(-cleanup)]} {
  1325   1342         catch $::ioerropts(-cleanup)
  1326   1343       }

Changes to test/tkt3762.test.

     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12     12   # Ticket #3762:  Make sure that an incremental vacuum that reduces the
    13         -# size of the database file such that a pointer-map page is elemented
    14         -# can be correctly rolled back.
           13  +# size of the database file such that if a pointer-map page is eliminated
           14  +# it can be correctly rolled back.
    15     15   #
    16     16   # That ticket #3762 has been fixed has already been verified by the
    17     17   # savepoint6.test test script.  But this script is simplier and a
    18     18   # redundant test never hurts.
    19     19   #
    20     20   # $Id: tkt3762.test,v 1.1 2009/03/31 00:50:36 drh Exp $
    21     21