/ Check-in [18b2c23a]
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:Change the way wal2 locks work to ensure a reader only ever has to lock a single slot.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wal2
Files: files | file ages | folders
SHA3-256: 18b2c23ac53d985ccc5798ea2d92fb75644b857c373fb490e0d04d5d0194a3d5
User & Date: dan 2018-12-11 17:56:23
Wiki:wal2
Context
2018-12-12
19:04
Add tests to ensure that each of the 4 wal read-locks does what it is supposed to. check-in: 4d5779f3 user: dan tags: wal2
2018-12-11
17:56
Change the way wal2 locks work to ensure a reader only ever has to lock a single slot. check-in: 18b2c23a user: dan tags: wal2
13:44
Merge latest trunk changes into this branch. check-in: d8dd98a3 user: dan tags: wal2
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/wal.c.

   358    358   ** it is held, but does not prevent a checkpointer from checkpointing 
   359    359   ** it.
   360    360   **
   361    361   ** There is still a single WRITER and a single CHECKPOINTER lock. The
   362    362   ** recovery procedure still takes the same exclusive lock on the entire
   363    363   ** range of SQLITE_SHM_NLOCK shm-locks. This works because the read-locks
   364    364   ** above use four of the six read-locking slots used by legacy wal mode.
   365         -** See the header comment for function walLockReader() for details.
   366    365   **
   367    366   ** STARTUP/RECOVERY
   368    367   **
   369    368   ** The read and write version fields of the database header in a wal2
   370    369   ** database are set to 0x03, instead of 0x02 as in legacy wal mode.
   371    370   **
   372    371   ** The wal file format used in wal2 mode is the same as the format used
................................................................................
   469    468   /*
   470    469   ** Values that may be stored in Wal.readLock in wal2 mode.
   471    470   **
   472    471   ** In wal mode, the Wal.readLock member is set to -1 when no read-lock
   473    472   ** is held, or else is the index of the read-mark on which a lock is
   474    473   ** held.
   475    474   **
   476         -** In wal2 mode, Wal.readLock must be set to one of the following values.
   477         -** A value of -1 still indicates that no read-lock is held, but the other
   478         -** values are symbolic. See the implementation of walLockReader() for
   479         -** details of how the symbols map to OS level locks.
          475  +** In wal2 mode, a value of -1 still indicates that no read-lock is held.
          476  +** And a non-zero value still represents the index of the read-mark on
          477  +** which a lock is held. There are two differences:
          478  +**
          479  +**   1. wal2 mode never uses read-mark 0.
          480  +**
          481  +**   2. locks on each read-mark have a different interpretation, as 
          482  +**      indicated by the symbolic names below.
   480    483   */
   481    484   #define WAL_LOCK_NONE        -1
   482    485   #define WAL_LOCK_PART1        1
   483    486   #define WAL_LOCK_PART1_FULL2  2
   484         -#define WAL_LOCK_PART2        3
   485         -#define WAL_LOCK_PART2_FULL1  4
          487  +#define WAL_LOCK_PART2_FULL1  3
          488  +#define WAL_LOCK_PART2        4
   486    489   
   487    490   /* 
   488    491   ** This constant is used in wal2 mode only.
   489    492   **
   490    493   ** In wal2 mode, when committing a transaction, if the current wal file 
   491    494   ** is sufficiently large and there are no conflicting locks held, the
   492    495   ** writer writes the new transaction into the start of the other wal
................................................................................
  1107   1110     if( pWal->exclusiveMode ) return;
  1108   1111     (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
  1109   1112                            SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE);
  1110   1113     WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal,
  1111   1114                walLockName(lockIdx), n));
  1112   1115   }
  1113   1116   
  1114         -/*
  1115         -** This function is used to take and release read-locks in wal2 mode.
  1116         -**
  1117         -** Use of WAL_READ_LOCK(x) slots for (1<=x<=4).
  1118         -**
  1119         -** 1) Partial read of *-wal-1   (blocks checkpointer from checkpointing)
  1120         -** 2) Full read of *-wal-2      (blocks writer from writing)
  1121         -** 3) Partial read of *-wal-2   (blocks checkpointer from checkpointing)
  1122         -** 4) Full read of *-wal-1      (blocks writer from writing)
  1123         -*/
  1124         -static int walLockReader(Wal *pWal, int eLock, int bLock){
  1125         -  int i;                          /* Index of first readmark to lock */
  1126         -  int n;                          /* Number of readmarks to lock */
  1127         -
  1128         -  assert( pWal->hdr.iVersion==WAL_VERSION2 );
  1129         -  if( pWal->exclusiveMode ) return SQLITE_OK;
  1130         -
  1131         -  switch( eLock ){
  1132         -    case WAL_LOCK_PART1      : i = 1; n = 1; break; 
  1133         -    case WAL_LOCK_PART1_FULL2: i = 1; n = 2; break; 
  1134         -    case WAL_LOCK_PART2      : i = 3; n = 1; break; 
  1135         -    case WAL_LOCK_PART2_FULL1: i = 3; n = 2; break; 
  1136         -    default: assert( !"cannot happen" );
  1137         -  }
  1138         -
  1139         -  return sqlite3OsShmLock(pWal->pDbFd, WAL_READ_LOCK(i), n,
  1140         -      SQLITE_SHM_SHARED | (bLock ? SQLITE_SHM_LOCK : SQLITE_SHM_UNLOCK) 
  1141         -  );
  1142         -}
  1143         -
  1144   1117   /*
  1145   1118   ** Compute a hash on a page number.  The resulting hash value must land
  1146   1119   ** between 0 and (HASHTABLE_NSLOT-1).  The walHashNext() function advances
  1147   1120   ** the hash to the next value in the event of a collision.
  1148   1121   */
  1149   1122   static int walHash(u32 iPage){
  1150   1123     assert( iPage>0 );
................................................................................
  2246   2219     walIndexWriteHdr(pWal);
  2247   2220     pInfo->nBackfill = 0;
  2248   2221     pInfo->nBackfillAttempted = 0;
  2249   2222     pInfo->aReadMark[1] = 0;
  2250   2223     for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
  2251   2224     assert( pInfo->aReadMark[0]==0 );
  2252   2225   }
         2226  +
         2227  +/*
         2228  +** This function is used in wal2 mode.
         2229  +**
         2230  +** This function is called when writer pWal is just about to start 
         2231  +** writing out frames. Parameter iApp is the current wal file. The "other" wal
         2232  +** file (wal file !iApp) has been fully checkpointed. This function returns
         2233  +** SQLITE_OK if there are no readers preventing the writer from switching to
         2234  +** the other wal file. Or SQLITE_BUSY if there are.
         2235  +*/
         2236  +static int wal2RestartOk(Wal *pWal, int iApp){
         2237  +  /* The other wal file (wal file !iApp) can be overwritten if there
         2238  +  ** are no readers reading from it - no "full" or "partial" locks.
         2239  +  ** Technically speaking it is not possible for any reader to hold
         2240  +  ** a "part" lock, as this would have prevented the file from being
         2241  +  ** checkpointed. But checking anyway doesn't hurt. The following
         2242  +  ** is equivalent to:
         2243  +  **
         2244  +  **   if( iApp==0 ) eLock = WAL_LOCK_PART1_FULL2;
         2245  +  **   if( iApp==1 ) eLock = WAL_LOCK_PART1;
         2246  +  */
         2247  +  int eLock = 1 + (iApp==0);
         2248  +
         2249  +  assert( WAL_LOCK_PART1==1 );
         2250  +  assert( WAL_LOCK_PART1_FULL2==2 );
         2251  +  assert( WAL_LOCK_PART2_FULL1==3 );
         2252  +  assert( WAL_LOCK_PART2==4 );
         2253  +
         2254  +  assert( iApp!=0 || eLock==WAL_LOCK_PART1_FULL2 );
         2255  +  assert( iApp!=1 || eLock==WAL_LOCK_PART1 );
         2256  +
         2257  +  return walLockExclusive(pWal, WAL_READ_LOCK(eLock), 3);
         2258  +}
         2259  +static void wal2RestartFinished(Wal *pWal, int iApp){
         2260  +  walUnlockExclusive(pWal, WAL_READ_LOCK(1 + (iApp==0)), 3);
         2261  +}
         2262  +
         2263  +/*
         2264  +** This function is used in wal2 mode.
         2265  +**
         2266  +** This function is called when a checkpointer wishes to checkpoint wal
         2267  +** file iCkpt. It takes the required lock and, if successful, returns
         2268  +** SQLITE_OK. Otherwise, an SQLite error code (e.g. SQLITE_BUSY). If this
         2269  +** function returns SQLITE_OK, it is the responsibility of the caller
         2270  +** to invoke wal2CheckpointFinished() to release the lock.
         2271  +*/
         2272  +static int wal2CheckpointOk(Wal *pWal, int iCkpt){
         2273  +  int eLock = 1 + (iCkpt*2);
         2274  +
         2275  +  assert( WAL_LOCK_PART1==1 );
         2276  +  assert( WAL_LOCK_PART1_FULL2==2 );
         2277  +  assert( WAL_LOCK_PART2_FULL1==3 );
         2278  +  assert( WAL_LOCK_PART2==4 );
         2279  +
         2280  +  assert( iCkpt!=0 || eLock==WAL_LOCK_PART1 );
         2281  +  assert( iCkpt!=1 || eLock==WAL_LOCK_PART2_FULL1 );
         2282  +
         2283  +  return walLockExclusive(pWal, WAL_READ_LOCK(eLock), 2);
         2284  +}
         2285  +static void wal2CheckpointFinished(Wal *pWal, int iCkpt){
         2286  +  walUnlockExclusive(pWal, WAL_READ_LOCK(1 + (iCkpt*2)), 2);
         2287  +}
  2253   2288   
  2254   2289   /*
  2255   2290   ** Copy as much content as we can from the WAL back into the database file
  2256   2291   ** in response to an sqlite3_wal_checkpoint() request or the equivalent.
  2257   2292   **
  2258   2293   ** The amount of information copies from WAL to database might be limited
  2259   2294   ** by active readers.  This routine will never overwrite a database page
................................................................................
  2314   2349       sqlite3_file *pWalFd = pWal->apWalFd[iCkpt];
  2315   2350       mxPage = pWal->hdr.nPage;
  2316   2351   
  2317   2352       /* If this is a wal2 system, check for a reader holding a lock 
  2318   2353       ** preventing this checkpoint operation. If one is found, return
  2319   2354       ** early.  */
  2320   2355       if( bWal2 ){
  2321         -      rc = walLockExclusive(pWal, WAL_READ_LOCK(1 + iCkpt*2), 1);
         2356  +      rc = wal2CheckpointOk(pWal, iCkpt);
  2322   2357         if( rc!=SQLITE_OK ) return rc;
  2323   2358       }
  2324   2359   
  2325   2360       /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
  2326   2361       ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
  2327   2362       assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
  2328   2363   
................................................................................
  2363   2398       /* Allocate the iterator */
  2364   2399       if( bWal2 || pInfo->nBackfill<mxSafeFrame ){
  2365   2400         assert( bWal2==0 || pInfo->nBackfill==0 );
  2366   2401         rc = walIteratorInit(pWal, iCkpt, pInfo->nBackfill, &pIter);
  2367   2402         assert( rc==SQLITE_OK || pIter==0 );
  2368   2403       }
  2369   2404   
  2370         -    if( pIter
  2371         -     && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK
  2372         -    ){
         2405  +    if( pIter && (bWal2 
         2406  +     || (rc = walBusyLock(pWal, xBusy, pBusyArg,WAL_READ_LOCK(0),1))==SQLITE_OK
         2407  +    )){
  2373   2408         u32 nBackfill = pInfo->nBackfill;
  2374   2409   
  2375   2410         assert( bWal2==0 || nBackfill==0 );
  2376   2411         pInfo->nBackfillAttempted = mxSafeFrame;
  2377   2412   
  2378   2413         /* Sync the wal file being checkpointed to disk */
  2379   2414         rc = sqlite3OsSync(pWalFd, CKPT_SYNC_FLAGS(sync_flags));
................................................................................
  2430   2465           }
  2431   2466         }
  2432   2467         if( rc==SQLITE_OK ){
  2433   2468           pInfo->nBackfill = bWal2 ? 1 : mxSafeFrame;
  2434   2469         }
  2435   2470   
  2436   2471         /* Release the reader lock held while backfilling */
  2437         -      walUnlockExclusive(pWal, WAL_READ_LOCK(bWal2 ? 1 + iCkpt*2 : 0), 1);
         2472  +      if( bWal2==0 ){
         2473  +        walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
         2474  +      }
  2438   2475       }
  2439   2476   
  2440   2477       if( rc==SQLITE_BUSY ){
  2441   2478         /* Reset the return code so as not to report a checkpoint failure
  2442   2479         ** just because there are active readers.  */
  2443   2480         rc = SQLITE_OK;
  2444   2481       }
         2482  +    if( bWal2 ) wal2CheckpointFinished(pWal, iCkpt);
  2445   2483     }
  2446   2484   
  2447   2485     /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
  2448   2486     ** entire wal file has been copied into the database file, then block 
  2449   2487     ** until all readers have finished using the wal file. This ensures that 
  2450   2488     ** the next process to write to the database restarts the wal file.
  2451   2489     */
................................................................................
  3056   3094       }
  3057   3095     }
  3058   3096   
  3059   3097     assert( pWal->nWiData>0 );
  3060   3098     assert( pWal->apWiData[0]!=0 );
  3061   3099     pInfo = walCkptInfo(pWal);
  3062   3100     if( isWalMode2(pWal) ){
  3063         -    int eLock = 1 + (walidxGetFile(&pWal->hdr)*2);
  3064         -    if( pInfo->nBackfill==0 ){
  3065         -      eLock += walidxGetMxFrame(&pWal->hdr, !walidxGetFile(&pWal->hdr))>0;
  3066         -    }
  3067         -    rc = walLockReader(pWal, eLock, 1);
         3101  +    /* This connection needs a "part" lock on the current wal file and, 
         3102  +    ** unless pInfo->nBackfill is set to indicate that it has already been
         3103  +    ** checkpointed, a "full" lock on the other wal file.  */
         3104  +    int iWal = walidxGetFile(&pWal->hdr);
         3105  +    int nBackfill = pInfo->nBackfill || walidxGetMxFrame(&pWal->hdr, !iWal)==0;
         3106  +    int eLock = 1 + (iWal*2) + (nBackfill==iWal);
         3107  +
         3108  +    assert( nBackfill==0 || nBackfill==1 );
         3109  +    assert( iWal==0 || iWal==1 );
         3110  +    assert( iWal!=0 || nBackfill!=1 || eLock==WAL_LOCK_PART1 );
         3111  +    assert( iWal!=0 || nBackfill!=0 || eLock==WAL_LOCK_PART1_FULL2 );
         3112  +    assert( iWal!=1 || nBackfill!=1 || eLock==WAL_LOCK_PART2 );
         3113  +    assert( iWal!=1 || nBackfill!=0 || eLock==WAL_LOCK_PART2_FULL1 );
         3114  +
         3115  +    rc = walLockShared(pWal, WAL_READ_LOCK(eLock));
  3068   3116       if( rc!=SQLITE_OK ){
  3069   3117         return rc;
  3070   3118       }
  3071         -
  3072   3119       walShmBarrier(pWal);
  3073   3120       if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
  3074         -      walLockReader(pWal, eLock, 0);
         3121  +      walUnlockShared(pWal, WAL_READ_LOCK(eLock));
  3075   3122         return WAL_RETRY;
  3076   3123       }else{
  3077   3124         pWal->readLock = eLock;
  3078   3125       }
  3079   3126       assert( pWal->minFrame==0 && walFramePage(pWal->minFrame)==0 );
  3080   3127     }else{
  3081   3128       u32 mxReadMark;               /* Largest aReadMark[] value */
................................................................................
  3399   3446   /*
  3400   3447   ** Finish with a read transaction.  All this does is release the
  3401   3448   ** read-lock.
  3402   3449   */
  3403   3450   void sqlite3WalEndReadTransaction(Wal *pWal){
  3404   3451     sqlite3WalEndWriteTransaction(pWal);
  3405   3452     if( pWal->readLock!=WAL_LOCK_NONE ){
  3406         -    if( isWalMode2(pWal) ){
  3407         -      (void)walLockReader(pWal, pWal->readLock, 0);
  3408         -    }else{
  3409         -      walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  3410         -    }
         3453  +    walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  3411   3454       pWal->readLock = WAL_LOCK_NONE;
  3412   3455     }
  3413   3456   }
  3414   3457   
  3415   3458   /* Search hash table iHash for an entry matching page number
  3416   3459   ** pgno. Each call to this function searches a single hash table
  3417   3460   ** (each hash table indexes up to HASHTABLE_NPAGE frames).
................................................................................
  3790   3833       pWal->hdr.aFrameCksum[1] = aWalData[2];
  3791   3834       walCleanupHash(pWal);
  3792   3835     }
  3793   3836   
  3794   3837     return rc;
  3795   3838   }
  3796   3839   
  3797         -/*
  3798         -** This function is used in wal2 mode.
  3799         -**
  3800         -** This function is called when writer pWal is just about to start 
  3801         -** writing out frames. The "other" wal file (wal file !pWal->hdr.iAppend)
  3802         -** has been fully checkpointed. This function returns SQLITE_OK if there
  3803         -** are no readers preventing the writer from switching to the other wal
  3804         -** file. Or SQLITE_BUSY if there are.
  3805         -*/
  3806         -static int walRestartOk(Wal *pWal){
  3807         -  int rc;                                        /* Return code */
  3808         -  int iApp = walidxGetFile(&pWal->hdr);          /* Current WAL file */
  3809         -
  3810         -  /* No reader can be doing a "partial" read of wal file !iApp - in that
  3811         -  ** case it would not have been possible to checkpoint the file. So
  3812         -  ** it is only necessary to test for "full" readers. See the comment
  3813         -  ** above walLockReader() function for exactly what this means in terms
  3814         -  ** of locks.  */
  3815         -  int i = (iApp==0) ? 2 : 4;
  3816         -
  3817         -  rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
  3818         -  if( rc==SQLITE_OK ){
  3819         -    walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
  3820         -  }
  3821         -  return rc;
  3822         -}
  3823         -
  3824   3840   /*
  3825   3841   ** This function is called just before writing a set of frames to the log
  3826   3842   ** file (see sqlite3WalFrames()). It checks to see if, instead of appending
  3827   3843   ** to the current log file, it is possible and desirable to switch to the
  3828   3844   ** other log file and write the new transaction to the start of it.
  3829   3845   ** If so, the wal-index header is updated accordingly - both in heap memory
  3830   3846   ** and in the *-shm file.
................................................................................
  3844   3860           / (pWal->szPage+WAL_FRAME_HDRSIZE);
  3845   3861         nWalSize = MAX(nWalSize, 1);
  3846   3862       }
  3847   3863   
  3848   3864       if( walidxGetMxFrame(&pWal->hdr, iApp)>=nWalSize ){
  3849   3865         volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
  3850   3866         if( walidxGetMxFrame(&pWal->hdr, !iApp)==0 || pInfo->nBackfill ){
  3851         -        rc = walRestartOk(pWal);
         3867  +        rc = wal2RestartOk(pWal, iApp);
  3852   3868           if( rc==SQLITE_OK ){
  3853         -          iApp = !iApp;
         3869  +          int iNew = !iApp;
  3854   3870             pWal->nCkpt++;
  3855         -          walidxSetFile(&pWal->hdr, iApp);
  3856         -          walidxSetMxFrame(&pWal->hdr, iApp, 0);
         3871  +          walidxSetFile(&pWal->hdr, iNew);
         3872  +          walidxSetMxFrame(&pWal->hdr, iNew, 0);
  3857   3873             sqlite3Put4byte((u8*)&pWal->hdr.aSalt[0], pWal->hdr.aFrameCksum[0]);
  3858   3874             sqlite3Put4byte((u8*)&pWal->hdr.aSalt[1], pWal->hdr.aFrameCksum[1]);
  3859   3875             walIndexWriteHdr(pWal);
  3860   3876             pInfo->nBackfill = 0;
  3861         -          walLockReader(pWal, pWal->readLock, 0);
  3862         -          pWal->readLock = iApp ? WAL_LOCK_PART2_FULL1 : WAL_LOCK_PART1_FULL2;
  3863         -          rc = walLockReader(pWal, pWal->readLock, 1);
         3877  +          wal2RestartFinished(pWal, iApp);
         3878  +          walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
         3879  +          pWal->readLock = iNew ? WAL_LOCK_PART2_FULL1 : WAL_LOCK_PART1_FULL2;
         3880  +          rc = walLockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  3864   3881           }else if( rc==SQLITE_BUSY ){
  3865   3882             rc = SQLITE_OK;
  3866   3883           }
  3867   3884         }
  3868   3885       }
  3869   3886     }else if( pWal->readLock==0 ){
  3870   3887       int cnt;
................................................................................
  4486   4503     */
  4487   4504     assert( pWal->readLock!=WAL_LOCK_NONE || pWal->lockError );
  4488   4505     assert( pWal->readLock!=WAL_LOCK_NONE || (op<=0 && pWal->exclusiveMode==0) );
  4489   4506   
  4490   4507     if( op==0 ){
  4491   4508       if( pWal->exclusiveMode ){
  4492   4509         pWal->exclusiveMode = WAL_NORMAL_MODE;
  4493         -      if( isWalMode2(pWal) ){
  4494         -        rc = walLockReader(pWal, pWal->readLock, 1);
  4495         -      }else{
  4496         -        rc = walLockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  4497         -      }
         4510  +      rc = walLockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  4498   4511         if( rc!=SQLITE_OK ){
  4499   4512           pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
  4500   4513         }
  4501   4514         rc = pWal->exclusiveMode==WAL_NORMAL_MODE;
  4502   4515       }else{
  4503   4516         /* Already in locking_mode=NORMAL */
  4504   4517         rc = 0;
  4505   4518       }
  4506   4519     }else if( op>0 ){
  4507   4520       assert( pWal->exclusiveMode==WAL_NORMAL_MODE );
  4508   4521       assert( pWal->readLock>=0 );
  4509         -    if( isWalMode2(pWal) ){
  4510         -      walLockReader(pWal, pWal->readLock, 0);
  4511         -    }else{
  4512         -      walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  4513         -    }
         4522  +    walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
  4514   4523       pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
  4515   4524       rc = 1;
  4516   4525     }else{
  4517   4526       rc = pWal->exclusiveMode==WAL_NORMAL_MODE;
  4518   4527     }
  4519   4528     return rc;
  4520   4529   }

Changes to test/wal2simple.test.

   239    239   
   240    240   do_test 6.1 {
   241    241     for {set i 0} {$i < 10} {incr i} {
   242    242       execsql "CREATE TABLE t$i (x);"
   243    243     }
   244    244   } {}
   245    245   
   246         -puts "[file size test.db-wal] [file size test.db-wal2]"
   247         -
   248    246   do_test 6.2.1 {
   249    247     foreach f [glob -nocomplain test.db2*] { forcedelete $f }
   250    248     forcecopy test.db-wal2 test.db2-wal2
   251    249     sqlite3 db2 test.db2
   252    250     db2 eval { SELECT * FROM sqlite_master }
   253    251   } {}
   254    252   do_test 6.2.2 {