/ Check-in [96bef18c]
Login

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

Overview
Comment:Use the read and write version fields of the database header to mark a database as operating in wal-mode.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wal
Files: files | file ages | folders
SHA1: 96bef18c1411c3e0348295886f105e1646c46320
User & Date: dan 2010-04-20 18:53:15
Context
2010-04-21
06:19
Minor changes to test cases to account for the fact that databases with read/write versions of 2 are now understood. check-in: 278ed41e user: dan tags: wal
2010-04-20
18:53
Use the read and write version fields of the database header to mark a database as operating in wal-mode. check-in: 96bef18c user: dan tags: wal
2010-04-19
18:03
Add the sqlite3_log_hook() interface for scheduling checkpoints. check-in: 9bda6014 user: dan tags: wal
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269












2270
2271
2272
2273
2274
2275
2276
....
7959
7960
7961
7962
7963
7964
7965























    int pageSize;
    int usableSize;
    u8 *page1 = pPage1->aData;
    rc = SQLITE_NOTADB;
    if( memcmp(page1, zMagicHeader, 16)!=0 ){
      goto page1_init_failed;
    }
    if( page1[18]>1 ){
      pBt->readOnly = 1;
    }
    if( page1[19]>1 ){
      goto page1_init_failed;
    }













    /* The maximum embedded fraction must be exactly 25%.  And the minimum
    ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
    ** The original design allowed these amounts to vary, but as of
    ** version 3.6.0, we require them to be fixed.
    */
    if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
................................................................................
  assert( cursorHoldsMutex(pCur) );
  assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
  assert(!pCur->isIncrblobHandle);
  assert(!pCur->aOverflow);
  pCur->isIncrblobHandle = 1;
}
#endif






























|


|


>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
....
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
    int pageSize;
    int usableSize;
    u8 *page1 = pPage1->aData;
    rc = SQLITE_NOTADB;
    if( memcmp(page1, zMagicHeader, 16)!=0 ){
      goto page1_init_failed;
    }
    if( page1[18]>2 ){
      pBt->readOnly = 1;
    }
    if( page1[19]>2 ){
      goto page1_init_failed;
    }

    /* If the write version is set to 2, turn on write-ahead logging mode. */
    if( page1[19]==2 ){
      int isOpen = 0;
      rc = sqlite3PagerOpenLog(pBt->pPager, &isOpen);
      if( rc!=SQLITE_OK ){
        goto page1_init_failed;
      }else if( isOpen==0 ){
        releasePage(pPage1);
        return SQLITE_OK;
      }
    }

    /* The maximum embedded fraction must be exactly 25%.  And the minimum
    ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
    ** The original design allowed these amounts to vary, but as of
    ** version 3.6.0, we require them to be fixed.
    */
    if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
................................................................................
  assert( cursorHoldsMutex(pCur) );
  assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
  assert(!pCur->isIncrblobHandle);
  assert(!pCur->aOverflow);
  pCur->isIncrblobHandle = 1;
}
#endif

/*
** Set both the "read version" (single byte at byte offset 18) and 
** "write version" (single byte at byte offset 19) fields in the database
** header to iVersion.
*/
int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
  BtShared *pBt = pBtree->pBt;
  int rc;                         /* Return code */
 
  assert( pBtree->inTrans==TRANS_WRITE );
  assert( pBt->pPage1 );
  assert( iVersion==1 || iVersion==2 );

  rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
  if( rc==SQLITE_OK ){
    u8 *aData = pBt->pPage1->aData;
    aData[18] = (u8)iVersion;
    aData[19] = (u8)iVersion;
  }

  return rc;
}

Changes to src/btree.h.

181
182
183
184
185
186
187


188
189
190
191
192
193
194

char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
struct Pager *sqlite3BtreePager(Btree*);

int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
void sqlite3BtreeCacheOverflow(BtCursor *);
void sqlite3BtreeClearCursor(BtCursor *);



#ifndef NDEBUG
int sqlite3BtreeCursorIsValid(BtCursor*);
#endif

#ifndef SQLITE_OMIT_BTREECOUNT
int sqlite3BtreeCount(BtCursor *, i64 *);







>
>







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
struct Pager *sqlite3BtreePager(Btree*);

int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
void sqlite3BtreeCacheOverflow(BtCursor *);
void sqlite3BtreeClearCursor(BtCursor *);

int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);

#ifndef NDEBUG
int sqlite3BtreeCursorIsValid(BtCursor*);
#endif

#ifndef SQLITE_OMIT_BTREECOUNT
int sqlite3BtreeCount(BtCursor *, i64 *);

Changes to src/pager.c.

1401
1402
1403
1404
1405
1406
1407
1408

1409
1410

1411
1412
1413
1414
1415
1416
1417
....
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
....
3822
3823
3824
3825
3826
3827
3828
3829
3830

3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848


3849
3850
3851
3852
3853
3854
3855
....
3998
3999
4000
4001
4002
4003
4004




4005
4006
4007
4008
4009
4010
4011
....
4380
4381
4382
4383
4384
4385
4386

4387
4388
4389
4390
4391
4392
4393
....
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
....
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
....
5636
5637
5638
5639
5640
5641
5642





5643
5644
5645
5646
5647
5648
5649
5650
....
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
....
5708
5709
5710
5711
5712
5713
5714
5715









































































5716
      pager_error(pPager, rc);
      pPager->journalOff = 0;
      pPager->journalStarted = 0;
    }else{
      /* This branch may be executed with Pager.journalMode==MEMORY if
      ** a hot-journal was just rolled back. In this case the journal
      ** file should be closed and deleted. If this connection writes to
      ** the database file, it will do so using an in-memory journal.  */

      assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE 
           || pPager->journalMode==PAGER_JOURNALMODE_MEMORY 

      );
      sqlite3OsClose(pPager->jfd);
      if( !pPager->tempFile ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }
    }

................................................................................
      }
    }
  }

  return rc;
}

/*
** Open a connection to the write-ahead log file for pager pPager. If
** the log connection is already open, this function is a no-op.
*/
static int pagerOpenLog(Pager *pPager){
  if( !pPager->pLog ){
    int rc;                       /* Return code */

    /* Before opening the log file, obtain a SHARED lock on the database
    ** file. This lock will not be released until after the log file
    ** connection has been closed. The purpose of this lock is to stop
    ** any other process from unlinking the log or log-summary files while
    ** this connection still has them open. An EXCLUSIVE lock on the
    ** database file is required to unlink either of those two files.
    */
    assert( pPager->state==PAGER_UNLOCK );
    rc = pager_wait_on_lock(pPager, SHARED_LOCK);
    if( rc!=SQLITE_OK ){
      assert( pPager->state==PAGER_UNLOCK );
      return pager_error(pPager, rc);
    }
    assert( pPager->state>=SHARED_LOCK );

    /* Open the connection to the log file. If this operation fails, 
    ** (e.g. due to malloc() failure), unlock the database file and 
    ** return an error code.
    */
    rc = sqlite3LogOpen(pPager->pVfs, pPager->zFilename, &pPager->pLog);
    if( rc!=SQLITE_OK ){
      osUnlock(pPager->fd, SQLITE_LOCK_NONE);
      pPager->state = PAGER_UNLOCK;
      return rc;
    }
  }else{
    /* If the log file was already open, check that the pager is still holding
    ** the required SHARED lock on the database file. 
    */
#ifdef SQLITE_DEBUG
    int locktype;
    sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype);
    assert( locktype==SQLITE_LOCK_SHARED );
#endif
    pPager->state = PAGER_SHARED;
  }

  return SQLITE_OK;
}


/*
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerAcquire() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
................................................................................
    if( isOpen(pPager->jfd) || pPager->zJournal ){
      isErrorReset = 1;
    }
    pPager->errCode = SQLITE_OK;
    pager_reset(pPager);
  }


  if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){

    int changed = 0;              /* True if the cache must be flushed */

    /* Open the log file, if it is not already open. */
    rc = pagerOpenLog(pPager);
    if( rc!=SQLITE_OK ){
      return rc;
    }

    /* Open a log snapshot to read from. */
    rc = sqlite3LogOpenSnapshot(pPager->pLog, &changed);
    if( rc==SQLITE_OK ){
      int dummy;
      if( changed ){
        pager_reset(pPager);
        assert( pPager->errCode || pPager->dbSizeValid==0 );
      }
      rc = sqlite3PagerPagecount(pPager, &dummy);
    }


  }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
    sqlite3_vfs * const pVfs = pPager->pVfs;
    int isHotJournal = 0;
    assert( !MEMDB );
    assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
    if( pPager->noReadlock ){
      assert( pPager->readOnly );
................................................................................
      }

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);
      }
    }
    assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED );




  }

 failed:
  if( rc!=SQLITE_OK ){
    /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
    pager_unlock(pPager);
  }
................................................................................
      ** The busy-handler is not invoked if another connection already
      ** holds the write-lock. If possible, the upper layer will call it.
      */
      rc = sqlite3LogWriteLock(pPager->pLog, 1);
      if( rc==SQLITE_OK ){
        pPager->dbOrigSize = pPager->dbSize;
        pPager->state = PAGER_RESERVED;

      }
    }else{
      /* Obtain a RESERVED lock on the database file. If the exFlag parameter
      ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
      ** busy-handler callback can be used when upgrading to the EXCLUSIVE
      ** lock, but not when obtaining the RESERVED lock.
      */
................................................................................
    */
    rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory);
    if( rc!=SQLITE_OK ){
      return rc;
    }
    if( !isOpen(pPager->jfd) 
     && pPager->journalMode!=PAGER_JOURNALMODE_OFF 
     && pPager->journalMode!=PAGER_JOURNALMODE_WAL 
    ){
      assert( pPager->useJournal );
      rc = pager_open_journal(pPager);
      if( rc!=SQLITE_OK ) return rc;
    }
    pPager->dbModified = 1;
  
................................................................................
**    PAGER_JOURNALMODE_TRUNCATE
**    PAGER_JOURNALMODE_PERSIST
**    PAGER_JOURNALMODE_OFF
**    PAGER_JOURNALMODE_MEMORY
**    PAGER_JOURNALMODE_WAL
**
** If the parameter is not _QUERY, then the journal_mode is set to the
** value specified if the change is allowed.  The change is disallowed
** for the following reasons:
**
**   *  An in-memory database can only have its journal_mode set to _OFF
**      or _MEMORY.
**
**   *  The journal mode may not be changed while a transaction is active.
**
................................................................................
            || eMode==PAGER_JOURNALMODE_DELETE
            || eMode==PAGER_JOURNALMODE_TRUNCATE
            || eMode==PAGER_JOURNALMODE_PERSIST
            || eMode==PAGER_JOURNALMODE_OFF 
            || eMode==PAGER_JOURNALMODE_WAL 
            || eMode==PAGER_JOURNALMODE_MEMORY );
  assert( PAGER_JOURNALMODE_QUERY<0 );





  if( eMode>=0
   && (pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL)
   && (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY||eMode==PAGER_JOURNALMODE_OFF)
   && !pPager->dbModified
   && (!isOpen(pPager->jfd) || 0==pPager->journalOff)
  ){
    if( isOpen(pPager->jfd) ){
      sqlite3OsClose(pPager->jfd);
................................................................................
    assert( (PAGER_JOURNALMODE_MEMORY & 1)==0 );
    assert( (PAGER_JOURNALMODE_OFF & 1)==0 );
    if( (pPager->journalMode & 1)==1 && (eMode & 1)==0
         && !pPager->exclusiveMode ){
      sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
    }

    /* Switching into WAL mode can only take place when no 
    ** locks are held on the database file. 
    */
    if( eMode==PAGER_JOURNALMODE_WAL && pPager->state!=PAGER_UNLOCK ){
      return (int)pPager->journalMode;
    }

    pPager->journalMode = (u8)eMode;
  }
  return (int)pPager->journalMode;
}

/*
** Get/set the size-limit used for persistent journal files.
................................................................................
  }
  return rc;
}

int sqlite3PagerLogCallback(Pager *pPager){
  return sqlite3LogCallback(pPager->pLog);
}










































































#endif /* SQLITE_OMIT_DISKIO */







|
>


>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
<
>


<
<
<
<
<
<










>
>







 







>
>
>
>







 







>







 







|







 







|







 







>
>
>
>
>
|







 







<
<
<
<
<
<
<







 








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
....
3722
3723
3724
3725
3726
3727
3728
















































3729
3730
3731
3732
3733
3734
3735
....
3776
3777
3778
3779
3780
3781
3782


3783
3784
3785






3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
....
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
....
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
....
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
....
5571
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
....
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
....
5614
5615
5616
5617
5618
5619
5620







5621
5622
5623
5624
5625
5626
5627
....
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
      pager_error(pPager, rc);
      pPager->journalOff = 0;
      pPager->journalStarted = 0;
    }else{
      /* This branch may be executed with Pager.journalMode==MEMORY if
      ** a hot-journal was just rolled back. In this case the journal
      ** file should be closed and deleted. If this connection writes to
      ** the database file, it will do so using an in-memory journal. 
      */
      assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE 
           || pPager->journalMode==PAGER_JOURNALMODE_MEMORY 
           || pPager->journalMode==PAGER_JOURNALMODE_WAL 
      );
      sqlite3OsClose(pPager->jfd);
      if( !pPager->tempFile ){
        rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }
    }

................................................................................
      }
    }
  }

  return rc;
}


















































/*
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerAcquire() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
................................................................................
    if( isOpen(pPager->jfd) || pPager->zJournal ){
      isErrorReset = 1;
    }
    pPager->errCode = SQLITE_OK;
    pager_reset(pPager);
  }



  if( pagerUseLog(pPager) ){
    int changed = 0;              /* True if the cache must be flushed */







    /* Open a log snapshot to read from. */
    rc = sqlite3LogOpenSnapshot(pPager->pLog, &changed);
    if( rc==SQLITE_OK ){
      int dummy;
      if( changed ){
        pager_reset(pPager);
        assert( pPager->errCode || pPager->dbSizeValid==0 );
      }
      rc = sqlite3PagerPagecount(pPager, &dummy);
    }
    pPager->state = PAGER_SHARED;

  }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
    sqlite3_vfs * const pVfs = pPager->pVfs;
    int isHotJournal = 0;
    assert( !MEMDB );
    assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
    if( pPager->noReadlock ){
      assert( pPager->readOnly );
................................................................................
      }

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);
      }
    }
    assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED );

    if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
      pPager->journalMode = PAGER_JOURNALMODE_DELETE;
    }
  }

 failed:
  if( rc!=SQLITE_OK ){
    /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
    pager_unlock(pPager);
  }
................................................................................
      ** The busy-handler is not invoked if another connection already
      ** holds the write-lock. If possible, the upper layer will call it.
      */
      rc = sqlite3LogWriteLock(pPager->pLog, 1);
      if( rc==SQLITE_OK ){
        pPager->dbOrigSize = pPager->dbSize;
        pPager->state = PAGER_RESERVED;
        pPager->journalOff = 0;
      }
    }else{
      /* Obtain a RESERVED lock on the database file. If the exFlag parameter
      ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
      ** busy-handler callback can be used when upgrading to the EXCLUSIVE
      ** lock, but not when obtaining the RESERVED lock.
      */
................................................................................
    */
    rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory);
    if( rc!=SQLITE_OK ){
      return rc;
    }
    if( !isOpen(pPager->jfd) 
     && pPager->journalMode!=PAGER_JOURNALMODE_OFF 
     && !pagerUseLog(pPager)
    ){
      assert( pPager->useJournal );
      rc = pager_open_journal(pPager);
      if( rc!=SQLITE_OK ) return rc;
    }
    pPager->dbModified = 1;
  
................................................................................
**    PAGER_JOURNALMODE_TRUNCATE
**    PAGER_JOURNALMODE_PERSIST
**    PAGER_JOURNALMODE_OFF
**    PAGER_JOURNALMODE_MEMORY
**    PAGER_JOURNALMODE_WAL
**
** If the parameter is not _QUERY, then the journal_mode is set to the
** value specified if the change is allowed. The change may be disallowed
** for the following reasons:
**
**   *  An in-memory database can only have its journal_mode set to _OFF
**      or _MEMORY.
**
**   *  The journal mode may not be changed while a transaction is active.
**
................................................................................
            || eMode==PAGER_JOURNALMODE_DELETE
            || eMode==PAGER_JOURNALMODE_TRUNCATE
            || eMode==PAGER_JOURNALMODE_PERSIST
            || eMode==PAGER_JOURNALMODE_OFF 
            || eMode==PAGER_JOURNALMODE_WAL 
            || eMode==PAGER_JOURNALMODE_MEMORY );
  assert( PAGER_JOURNALMODE_QUERY<0 );

  if( eMode==PAGER_JOURNALMODE_WAL 
   && pPager->journalMode==PAGER_JOURNALMODE_DELETE
  ){
    pPager->journalMode = PAGER_JOURNALMODE_WAL;
  }else if( eMode>=0
   && (pPager->tempFile==0 || eMode!=PAGER_JOURNALMODE_WAL)
   && (!MEMDB || eMode==PAGER_JOURNALMODE_MEMORY||eMode==PAGER_JOURNALMODE_OFF)
   && !pPager->dbModified
   && (!isOpen(pPager->jfd) || 0==pPager->journalOff)
  ){
    if( isOpen(pPager->jfd) ){
      sqlite3OsClose(pPager->jfd);
................................................................................
    assert( (PAGER_JOURNALMODE_MEMORY & 1)==0 );
    assert( (PAGER_JOURNALMODE_OFF & 1)==0 );
    if( (pPager->journalMode & 1)==1 && (eMode & 1)==0
         && !pPager->exclusiveMode ){
      sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
    }








    pPager->journalMode = (u8)eMode;
  }
  return (int)pPager->journalMode;
}

/*
** Get/set the size-limit used for persistent journal files.
................................................................................
  }
  return rc;
}

int sqlite3PagerLogCallback(Pager *pPager){
  return sqlite3LogCallback(pPager->pLog);
}

/*
** Open a connection to the write-ahead log file for pager pPager. If
** the log connection is already open, this function is a no-op.
**
** The caller must be holding a SHARED lock on the database file to call
** this function.
*/
int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen){
  int rc = SQLITE_OK;             /* Return code */

#ifdef SQLITE_DEBUG
  int locktype;
  sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype);
  assert( locktype==SQLITE_LOCK_SHARED );
  assert( pPager->state>=PAGER_SHARED );
#endif

  if( !pPager->pLog ){

    /* Open the connection to the log file. If this operation fails, 
    ** (e.g. due to malloc() failure), unlock the database file and 
    ** return an error code.
    */
    rc = sqlite3LogOpen(pPager->pVfs, pPager->zFilename, &pPager->pLog);
    if( rc==SQLITE_OK ){
      pPager->journalMode = PAGER_JOURNALMODE_WAL;
    }
  }else{
    *pisOpen = 1;
  }

  return rc;
}


/*
** This function is called to close the connection to the log file prior
** to switching from WAL to rollback mode.
**
** Before closing the log file, this function attempts to take an 
** EXCLUSIVE lock on the database file. If this cannot be obtained, an
** error (SQLITE_BUSY) is returned and the log connection is not closed.
** If successful, the EXCLUSIVE lock is not released before returning.
*/
int sqlite3PagerCloseLog(Pager *pPager){
  int rc = SQLITE_OK;
  if( pPager->pLog ){

    /* Try to obtain an EXCLUSIVE lock on the database file. */
    rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);

    /* If the EXCLUSIVE lock was obtained, checkpoint and close the log. */
    if( rc==SQLITE_OK ){
      rc = sqlite3LogClose(pPager->pLog, pPager->fd,
        (pPager->noSync ? 0 : pPager->sync_flags), 
        (u8*)pPager->pTmpSpace
      );
      pPager->pLog = 0;
    }

    /* Make sure the EXCLUSIVE lock has not been lost somehow */
#ifdef SQLITE_DEBUG
    {
      int locktype;
      sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype);
      assert( locktype==SQLITE_LOCK_EXCLUSIVE );
    }
#endif

  }
  return rc;
}

#endif /* SQLITE_OMIT_DISKIO */

Changes to src/pager.h.

132
133
134
135
136
137
138


139
140
141
142
143
144
145
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);

int sqlite3PagerCheckpoint(Pager *pPager);
int sqlite3PagerLogCallback(Pager *pPager);



/* Functions used to query pager state and configuration. */
u8 sqlite3PagerIsreadonly(Pager*);
int sqlite3PagerRefcount(Pager*);
int sqlite3PagerMemUsed(Pager*);
const char *sqlite3PagerFilename(Pager*);
const sqlite3_vfs *sqlite3PagerVfs(Pager*);







>
>







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);

int sqlite3PagerCheckpoint(Pager *pPager);
int sqlite3PagerLogCallback(Pager *pPager);
int sqlite3PagerOpenLog(Pager *pPager, int *pisOpen);
int sqlite3PagerCloseLog(Pager *pPager);

/* Functions used to query pager state and configuration. */
u8 sqlite3PagerIsreadonly(Pager*);
int sqlite3PagerRefcount(Pager*);
int sqlite3PagerMemUsed(Pager*);
const char *sqlite3PagerFilename(Pager*);
const sqlite3_vfs *sqlite3PagerVfs(Pager*);

Changes to src/pragma.c.

253
254
255
256
257
258
259






















260
261
262
263
264
265
266
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523

524
525

526

527

528
529
530
531
532
533
534
535


536
537


538
539
540
541
542
543
544
545

546
547
548
549



550
551
552


553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
    default:          zName = "NO ACTION";  
                      assert( action==OE_None ); break;
  }
  return zName;
}
#endif























/*
** Process a pragma statement.  
**
** Pragmas are of this form:
**
**      PRAGMA [database.]id [= value]
**
................................................................................
  }else

  /*
  **  PRAGMA [database.]journal_mode
  **  PRAGMA [database.]journal_mode = (delete|persist|off|truncate|memory)
  */
  if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
    int eMode;
    static char * const azModeName[] = {
      "delete", "persist", "off", "truncate", "memory", "wal"
    };

    if( zRight==0 ){
      eMode = PAGER_JOURNALMODE_QUERY;
    }else{

      int n = sqlite3Strlen30(zRight);
      eMode = sizeof(azModeName)/sizeof(azModeName[0]) - 1;

      while( eMode>=0 && sqlite3StrNICmp(zRight, azModeName[eMode], n)!=0 ){

        eMode--;

      }
    }
    if( pId2->n==0 && eMode==PAGER_JOURNALMODE_QUERY ){
      /* Simple "PRAGMA journal_mode;" statement. This is a query for
      ** the current default journal mode (which may be different to
      ** the journal-mode of the main database).
      */
      eMode = db->dfltJournalMode;


    }else{
      Pager *pPager;


      if( pId2->n==0 ){
        /* This indicates that no database name was specified as part
        ** of the PRAGMA command. In this case the journal-mode must be
        ** set on all attached databases, as well as the main db file.
        **
        ** Also, the sqlite3.dfltJournalMode variable is set so that
        ** any subsequently attached databases also use the specified
        ** journal mode.

        */
        int ii;
        assert(pDb==&db->aDb[0]);
        for(ii=1; ii<db->nDb; ii++){



          if( db->aDb[ii].pBt ){
            pPager = sqlite3BtreePager(db->aDb[ii].pBt);
            sqlite3PagerJournalMode(pPager, eMode);


          }
        }
        db->dfltJournalMode = (u8)eMode;
      }
      pPager = sqlite3BtreePager(pDb->pBt);
      eMode = sqlite3PagerJournalMode(pPager, eMode);
    }
    assert( eMode==PAGER_JOURNALMODE_DELETE
              || eMode==PAGER_JOURNALMODE_TRUNCATE
              || eMode==PAGER_JOURNALMODE_PERSIST
              || eMode==PAGER_JOURNALMODE_OFF
              || eMode==PAGER_JOURNALMODE_WAL
              || eMode==PAGER_JOURNALMODE_MEMORY );
    sqlite3VdbeSetNumCols(v, 1);
    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
    sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, 
           azModeName[eMode], P4_STATIC);
    sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
  }else

  /*
  **  PRAGMA [database.]journal_size_limit
  **  PRAGMA [database.]journal_size_limit=N
  **







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
|
|




>

<
>
|
>
|
>








>
>

<
>
>
|






|
>

<
|
<
>
>
>
|
<
<
>
>
|
|
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<







253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
...
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547

548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563

564
565
566
567
568
569
570
571
572
573
574
575

576

577
578
579
580


581
582
583
584

585


586










587
588
589
590
591
592
593
    default:          zName = "NO ACTION";  
                      assert( action==OE_None ); break;
  }
  return zName;
}
#endif


/*
** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants
** defined in pager.h. This function returns the associated lowercase
** journal-mode name.
*/
const char *sqlite3JournalModename(int eMode){
  static char * const azModeName[] = {
    "delete", "persist", "off", "truncate", "memory", "wal"
  };
  assert( PAGER_JOURNALMODE_DELETE==0 );
  assert( PAGER_JOURNALMODE_PERSIST==1 );
  assert( PAGER_JOURNALMODE_OFF==2 );
  assert( PAGER_JOURNALMODE_TRUNCATE==3 );
  assert( PAGER_JOURNALMODE_MEMORY==4 );
  assert( PAGER_JOURNALMODE_WAL==5 );
  assert( eMode>=0 && eMode<=ArraySize(azModeName) );

  if( eMode==ArraySize(azModeName) ) return 0;
  return azModeName[eMode];
}

/*
** Process a pragma statement.  
**
** Pragmas are of this form:
**
**      PRAGMA [database.]id [= value]
**
................................................................................
  }else

  /*
  **  PRAGMA [database.]journal_mode
  **  PRAGMA [database.]journal_mode = (delete|persist|off|truncate|memory)
  */
  if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
    int eMode;                    /* One of the PAGER_JOURNALMODE_XXX symbols */

    sqlite3VdbeSetNumCols(v, 1);
    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);

    if( zRight==0 ){
      eMode = PAGER_JOURNALMODE_QUERY;
    }else{
      const char *zMode;
      int n = sqlite3Strlen30(zRight);

      for(eMode=0; (zMode = sqlite3JournalModename(eMode)); eMode++){
        if( sqlite3StrNICmp(zRight, zMode, n)==0 ) break;
      }
      if( !zMode ){
        eMode = PAGER_JOURNALMODE_QUERY;
      }
    }
    if( pId2->n==0 && eMode==PAGER_JOURNALMODE_QUERY ){
      /* Simple "PRAGMA journal_mode;" statement. This is a query for
      ** the current default journal mode (which may be different to
      ** the journal-mode of the main database).
      */
      eMode = db->dfltJournalMode;
      sqlite3VdbeAddOp2(v, OP_String8, 0, 1);
      sqlite3VdbeChangeP4(v, -1, sqlite3JournalModename(eMode), P4_STATIC);
    }else{

      int ii;

      if( pId2->n==0 && eMode!=PAGER_JOURNALMODE_WAL ){
        /* This indicates that no database name was specified as part
        ** of the PRAGMA command. In this case the journal-mode must be
        ** set on all attached databases, as well as the main db file.
        **
        ** Also, the sqlite3.dfltJournalMode variable is set so that
        ** any subsequently attached databases also use the specified
        ** journal mode. Except, the default journal mode is never set
        ** to WAL.
        */

        db->dfltJournalMode = (u8)eMode;

      }

      for(ii=db->nDb-1; ii>=0; ii--){
        if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){


          sqlite3VdbeUsesBtree(v, ii);
          sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode);
        }
      }

    }













    sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
  }else

  /*
  **  PRAGMA [database.]journal_size_limit
  **  PRAGMA [database.]journal_size_limit=N
  **

Changes to src/vdbe.c.

5183
5184
5185
5186
5187
5188
5189



5190
5191
5192
5193
5194
5195
5196
5197
5198
5199














































































5200
5201
5202
5203
5204
5205
5206
  if( sqlite3VdbeMemTooBig(pMem) ){
    goto too_big;
  }
  break;
}

/* Opcode: Checkpoint P1 * * * *



*/
case OP_Checkpoint: {
  Btree *pBt;                     /* Btree to checkpoint */

  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );
  pBt = db->aDb[pOp->p1].pBt;
  rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
  break;
};  















































































#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/* Opcode: Vacuum * * * * *
**
** Vacuum the entire database.  This opcode will cause other virtual
** machines to be created and run.  It may not be called from within
** a transaction.







>
>
>










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
  if( sqlite3VdbeMemTooBig(pMem) ){
    goto too_big;
  }
  break;
}

/* Opcode: Checkpoint P1 * * * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
** WAL mode.
*/
case OP_Checkpoint: {
  Btree *pBt;                     /* Btree to checkpoint */

  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );
  pBt = db->aDb[pOp->p1].pBt;
  rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
  break;
};  

/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the
** PAGER_JOURNALMODE_XXX values. If changing between the various rollback
** modes (delete, truncate, persist, off and memory), this is a simple
** operation. No IO is required.
**
** If changing into or out of WAL mode the procedure is more complicated.
**
** Write a string containing the final journal-mode to register P2.
*/
case OP_JournalMode: {
  Btree *pBt;                     /* Btree to change journal mode of */
  Pager *pPager;                  /* Pager associated with pBt */
  int eNew = pOp->p3;             /* New journal mode */

  assert( eNew==PAGER_JOURNALMODE_DELETE 
       || eNew==PAGER_JOURNALMODE_TRUNCATE 
       || eNew==PAGER_JOURNALMODE_PERSIST 
       || eNew==PAGER_JOURNALMODE_OFF
       || eNew==PAGER_JOURNALMODE_MEMORY
       || eNew==PAGER_JOURNALMODE_WAL
       || eNew==PAGER_JOURNALMODE_QUERY
  );
  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );

  pBt = db->aDb[pOp->p1].pBt;
  pPager = sqlite3BtreePager(pBt);

  if( eNew!=PAGER_JOURNALMODE_QUERY ){
    int eOld = sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_QUERY);
    if( (eNew!=eOld)
     && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL)
    ){
      if( !db->autoCommit || db->activeVdbeCnt>1 ){
        rc = SQLITE_ERROR;
        sqlite3SetString(&p->zErrMsg, db, 
            "cannot change %s wal mode from within a transaction",
            (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
        );
      }else{
  
        /* If leaving WAL mode, close the log file. If successful, the call to
        ** PagerCloseLog() checkpoints and deletes the write-ahead-log file.
        ** An EXCLUSIVE lock is still held on the database file after returning.
        */
        if( eOld==PAGER_JOURNALMODE_WAL ){
          rc = sqlite3PagerCloseLog(pPager);
          if( rc!=SQLITE_OK ) goto abort_due_to_error;
          sqlite3PagerJournalMode(pPager, eNew);
        }else{
          sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_DELETE);
        }
  
        /* Open a transaction on the database file. Regardless of the journal
        ** mode, this transaction always uses a rollback journal.
        */
        assert( sqlite3BtreeIsInTrans(pBt)==0 );
        rc = sqlite3BtreeBeginTrans(pBt, 2);
        assert( rc==SQLITE_OK || eOld!=PAGER_JOURNALMODE_WAL );
        if( rc!=SQLITE_OK ) goto abort_due_to_error;
        rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
        if( rc!=SQLITE_OK ) goto abort_due_to_error;
      }
    }
  }

  eNew = sqlite3PagerJournalMode(pPager, eNew);
  pOut = &aMem[pOp->p2];
  pOut->flags = MEM_Str|MEM_Static|MEM_Term;
  pOut->z = sqlite3JournalModename(eNew);
  pOut->n = sqlite3Strlen30(pOut->z);
  pOut->enc = SQLITE_UTF8;
  sqlite3VdbeChangeEncoding(pOut, encoding);
  break;
};  

#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/* Opcode: Vacuum * * * * *
**
** Vacuum the entire database.  This opcode will cause other virtual
** machines to be created and run.  It may not be called from within
** a transaction.

Changes to test/wal.test.

27
28
29
30
31
32
33

34
35
36
37
38
39
40
..
56
57
58
59
60
61
62



63
64
65
66
67
68
69


70
71
72
73
74
75
76
77
...
193
194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237





238
239

240
241
242
243
244
245
246
247
248
249
...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
...
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
...
646
647
648
649
650
651
652
653
654
655
656
657

658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
...
690
691
692
693
694
695
696
697
698
699
700
proc blob {nByte} {
  incr ::blobcnt
  return [string range [string repeat "${::blobcnt}x" $nByte] 1 $nByte]
}

proc sqlite3_wal {args} {
  eval sqlite3 $args

  [lindex $args 0] eval { PRAGMA journal_mode = wal }
  [lindex $args 0] eval { PRAGMA synchronous = normal }
  [lindex $args 0] function blob blob
}

proc log_file_size {nFrame pgsz} {
  expr {12 + ($pgsz+16)*$nFrame}
................................................................................
# wal-6.*: Test creating databases with different page sizes.
#

do_test wal-0.1 {
  execsql { PRAGMA synchronous = normal }
  execsql { PRAGMA journal_mode = wal }
} {wal}




do_test wal-1.0 {
  execsql { 
    BEGIN;
    CREATE TABLE t1(a, b); 
  }
  list [file exists test.db-journal] [file exists test.db-wal]


} {0 1}
do_test wal-1.1 {
  execsql COMMIT
  list [file exists test.db-journal] [file exists test.db-wal]
} {0 1}
do_test wal-1.2 {
  # There are now two pages in the log.
  file size test.db-wal
................................................................................
db close

foreach sector {512 4096} {
  sqlite3_simulate_device -sectorsize $sector
  foreach pgsz {512 1024 2048 4096} {
    file delete -force test.db test.db-wal
    do_test wal-6.$sector.$pgsz.1 {
      sqlite3_wal db test.db -vfs devsym
      execsql "
        PRAGMA page_size = $pgsz ;

      "
      execsql "
        CREATE TABLE t1(a, b);
        INSERT INTO t1 VALUES(1, 2);
      "
      db close
      file size test.db
................................................................................
  sqlite3_wal db test.db
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
  }
  list [file size test.db] [file size test.db-wal]
} [list 0 [log_file_size 3 1024]]
do_test wal-7.2 {
  execsql { PRAGMA checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list 2048 [log_file_size 3 1024]]

# Execute some transactions in auto-vacuum mode to test database file
# truncation.
#
do_test wal-8.1 {
  reopen_db





  execsql {
    PRAGMA auto_vacuum = 1;

    PRAGMA auto_vacuum;
  }
} {1}
do_test wal-8.2 {
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x);
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 SELECT blob(900) FROM t1;       /*  4 */
................................................................................

# Run some "warm-body" tests to ensure that log-summary files with more
# than 256 entries (log summaries that contain index blocks) work Ok.
#
do_test wal-9.1 {
  reopen_db
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x PRIMARY KEY);
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 SELECT blob(900) FROM t1;       /*  4 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /*  8 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 16 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 32 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 64 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 128 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 256 */
  }
  file size test.db
} 0
do_test wal-9.2 {
  sqlite3_wal db2 test.db
  execsql {PRAGMA integrity_check } db2
} {ok}

do_test wal-9.3 {
  file delete -force test2.db test2.db-wal
................................................................................
  reopen_db
  execsql {
    PRAGMA cache_size = 10;
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x PRIMARY KEY);
  }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {0 3}
do_test wal-11.2 {
  execsql { PRAGMA checkpoint }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [log_file_size 3 1024]]
do_test wal-11.3 {
  execsql { INSERT INTO t1 VALUES( blob(900) ) }
  list [expr [file size test.db]/1024] [file size test.db-wal]
................................................................................
  execsql {
    PRAGMA cache_size = 10;
    BEGIN;
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 32
      SELECT count(*) FROM t1;
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 35 1024]]
do_test wal-11.11 {
  execsql {
      SELECT count(*) FROM t1;
    ROLLBACK;
    SELECT count(*) FROM t1;
  }
} {32 16}
do_test wal-11.12 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 35 1024]]
do_test wal-11.13 {
  execsql {
    INSERT INTO t1 VALUES( blob(900) );
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {17 ok}
do_test wal-11.14 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 35 1024]]


#-------------------------------------------------------------------------
# This block of tests, wal-12.*, tests the fix for a problem that 
# could occur if a log that is a prefix of an older log is written 
# into a reused log file.
#
................................................................................
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x, y);
    CREATE TABLE t2(x, y);
    INSERT INTO t1 VALUES('A', 1);
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 0 [log_file_size 5 1024]]
do_test wal-12.2 {
  db close
  sqlite3_wal db test.db
  execsql {

    UPDATE t1 SET y = 0 WHERE x = 'A';
  }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {3 1}
do_test wal-12.3 {
  execsql { INSERT INTO t2 VALUES('B', 1) }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {3 2}

do_test wal-12.4 {
  file copy -force test.db test2.db
  file copy -force test.db-wal test2.db-wal
  sqlite3_wal db2 test2.db
breakpoint
  execsql { SELECT * FROM t2 } db2
} {B 1}
db2 close

file copy -force test.db-wal A
do_test wal-12.5 {
  execsql {
................................................................................
do_test wal-12.4 {
  file copy -force test.db test2.db
  file copy -force test.db-wal test2.db-wal
  sqlite3_wal db2 test2.db
  execsql { SELECT * FROM t2 } db2
} {B 2}
db2 close


finish_test








>







 







>
>
>






|
>
>
|







 







|

|
>







 







|










>
>
>
>
>


>


|







 







<












|







 







|







 







|









|









|







 







|


|

>













<







 







<



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
..
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
...
278
279
280
281
282
283
284

285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
...
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
...
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
...
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683

684
685
686
687
688
689
690
...
702
703
704
705
706
707
708

709
710
711
proc blob {nByte} {
  incr ::blobcnt
  return [string range [string repeat "${::blobcnt}x" $nByte] 1 $nByte]
}

proc sqlite3_wal {args} {
  eval sqlite3 $args
  [lindex $args 0] eval { PRAGMA page_size = 1024 }
  [lindex $args 0] eval { PRAGMA journal_mode = wal }
  [lindex $args 0] eval { PRAGMA synchronous = normal }
  [lindex $args 0] function blob blob
}

proc log_file_size {nFrame pgsz} {
  expr {12 + ($pgsz+16)*$nFrame}
................................................................................
# wal-6.*: Test creating databases with different page sizes.
#

do_test wal-0.1 {
  execsql { PRAGMA synchronous = normal }
  execsql { PRAGMA journal_mode = wal }
} {wal}
do_test wal-0.2 {
  file size test.db
} {1024}

do_test wal-1.0 {
  execsql { 
    BEGIN;
    CREATE TABLE t1(a, b); 
  }
  list [file exists test.db-journal] \
       [file exists test.db-wal]     \
       [file size test.db]
} {0 1 1024}
do_test wal-1.1 {
  execsql COMMIT
  list [file exists test.db-journal] [file exists test.db-wal]
} {0 1}
do_test wal-1.2 {
  # There are now two pages in the log.
  file size test.db-wal
................................................................................
db close

foreach sector {512 4096} {
  sqlite3_simulate_device -sectorsize $sector
  foreach pgsz {512 1024 2048 4096} {
    file delete -force test.db test.db-wal
    do_test wal-6.$sector.$pgsz.1 {
      sqlite3 db test.db -vfs devsym
      execsql "
        PRAGMA page_size = $pgsz;
        PRAGMA journal_mode = wal;
      "
      execsql "
        CREATE TABLE t1(a, b);
        INSERT INTO t1 VALUES(1, 2);
      "
      db close
      file size test.db
................................................................................
  sqlite3_wal db test.db
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, 2);
  }
  list [file size test.db] [file size test.db-wal]
} [list 1024 [log_file_size 3 1024]]
do_test wal-7.2 {
  execsql { PRAGMA checkpoint }
  list [file size test.db] [file size test.db-wal]
} [list 2048 [log_file_size 3 1024]]

# Execute some transactions in auto-vacuum mode to test database file
# truncation.
#
do_test wal-8.1 {
  reopen_db
  catch { db close }
  file delete -force test.db test.db-wal

  sqlite3 db test.db
  db function blob blob
  execsql {
    PRAGMA auto_vacuum = 1;
    PRAGMA journal_mode = wal;
    PRAGMA auto_vacuum;
  }
} {wal 1}
do_test wal-8.2 {
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x);
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 SELECT blob(900) FROM t1;       /*  4 */
................................................................................

# Run some "warm-body" tests to ensure that log-summary files with more
# than 256 entries (log summaries that contain index blocks) work Ok.
#
do_test wal-9.1 {
  reopen_db
  execsql {

    CREATE TABLE t1(x PRIMARY KEY);
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 VALUES(blob(900));
    INSERT INTO t1 SELECT blob(900) FROM t1;       /*  4 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /*  8 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 16 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 32 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 64 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 128 */
    INSERT INTO t1 SELECT blob(900) FROM t1;       /* 256 */
  }
  file size test.db
} 1024
do_test wal-9.2 {
  sqlite3_wal db2 test.db
  execsql {PRAGMA integrity_check } db2
} {ok}

do_test wal-9.3 {
  file delete -force test2.db test2.db-wal
................................................................................
  reopen_db
  execsql {
    PRAGMA cache_size = 10;
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x PRIMARY KEY);
  }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {1 3}
do_test wal-11.2 {
  execsql { PRAGMA checkpoint }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 3 [log_file_size 3 1024]]
do_test wal-11.3 {
  execsql { INSERT INTO t1 VALUES( blob(900) ) }
  list [expr [file size test.db]/1024] [file size test.db-wal]
................................................................................
  execsql {
    PRAGMA cache_size = 10;
    BEGIN;
      INSERT INTO t1 SELECT blob(900) FROM t1;   -- 32
      SELECT count(*) FROM t1;
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 37 1024]]
do_test wal-11.11 {
  execsql {
      SELECT count(*) FROM t1;
    ROLLBACK;
    SELECT count(*) FROM t1;
  }
} {32 16}
do_test wal-11.12 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 37 1024]]
do_test wal-11.13 {
  execsql {
    INSERT INTO t1 VALUES( blob(900) );
    SELECT count(*) FROM t1;
    PRAGMA integrity_check;
  }
} {17 ok}
do_test wal-11.14 {
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 37 [log_file_size 37 1024]]


#-------------------------------------------------------------------------
# This block of tests, wal-12.*, tests the fix for a problem that 
# could occur if a log that is a prefix of an older log is written 
# into a reused log file.
#
................................................................................
  execsql {
    PRAGMA page_size = 1024;
    CREATE TABLE t1(x, y);
    CREATE TABLE t2(x, y);
    INSERT INTO t1 VALUES('A', 1);
  }
  list [expr [file size test.db]/1024] [file size test.db-wal]
} [list 1 [log_file_size 5 1024]]
do_test wal-12.2 {
  db close
  sqlite3 db test.db
  execsql {
    PRAGMA synchronous = normal;
    UPDATE t1 SET y = 0 WHERE x = 'A';
  }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {3 1}
do_test wal-12.3 {
  execsql { INSERT INTO t2 VALUES('B', 1) }
  list [expr [file size test.db]/1024] [expr [file size test.db-wal]/1044]
} {3 2}

do_test wal-12.4 {
  file copy -force test.db test2.db
  file copy -force test.db-wal test2.db-wal
  sqlite3_wal db2 test2.db

  execsql { SELECT * FROM t2 } db2
} {B 1}
db2 close

file copy -force test.db-wal A
do_test wal-12.5 {
  execsql {
................................................................................
do_test wal-12.4 {
  file copy -force test.db test2.db
  file copy -force test.db-wal test2.db-wal
  sqlite3_wal db2 test2.db
  execsql { SELECT * FROM t2 } db2
} {B 2}
db2 close


finish_test

Added test/walmode.test.



















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2010 April 19
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test walmode-1.1 {
  execsql {
    PRAGMA journal_mode = wal;
  }
} {wal}

finish_test