/ Check-in [9f4f933f]
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:Tests for (and changes to) the code to switch between WAL and rollback modes.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | wal
Files: files | file ages | folders
SHA1: 9f4f933f2c6596064fcfc7fb5add87e8c5b57448
User & Date: dan 2010-04-21 18:37:57
Context
2010-04-22
06:27
Further tests and changes related to switching between WAL and rollback modes. check-in: 12363184 user: dan tags: wal
2010-04-21
18:37
Tests for (and changes to) the code to switch between WAL and rollback modes. check-in: 9f4f933f user: dan tags: wal
11:43
If, after obtaining a SHARED lock, there exists a *-wal file in the file-system, use WAL mode. This is necessary to recover from a crash that damages the first page of the database file. check-in: 33cabf27 user: dan tags: wal
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
....
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998

7999




8000
8001





8002
8003
8004
8005




8006
8007
    ** in WAL mode. If the log is not already open, open it now. Then 
    ** return SQLITE_OK and return without populating BtShared.pPage1.
    ** The caller detects this and calls this function again. This is
    ** required as the version of page 1 currently in the page1 buffer
    ** may not be the latest version - there may be a newer one in the log
    ** file.
    */
    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;
................................................................................
** "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;
}







|







 







|
<


>
|
>
>
>
>


>
>
>
>
>
|
|
|
|
>
>
>
>


2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
....
7988
7989
7990
7991
7992
7993
7994
7995

7996
7997
7998
7999
8000
8001
8002
8003
8004
8005
8006
8007
8008
8009
8010
8011
8012
8013
8014
8015
8016
8017
8018
8019
8020
    ** in WAL mode. If the log is not already open, open it now. Then 
    ** return SQLITE_OK and return without populating BtShared.pPage1.
    ** The caller detects this and calls this function again. This is
    ** required as the version of page 1 currently in the page1 buffer
    ** may not be the latest version - there may be a newer one in the log
    ** file.
    */
    if( page1[19]==2 && pBt->doNotUseWAL==0 ){
      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;
................................................................................
** "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_NONE );

  assert( iVersion==1 || iVersion==2 );

  /* If setting the version fields to 1, do not automatically open the
  ** WAL connection, even if the version fields are currently set to 2.
  */
  pBt->doNotUseWAL = (iVersion==1);

  rc = sqlite3BtreeBeginTrans(pBtree, 0);
  if( rc==SQLITE_OK ){
    u8 *aData = pBt->pPage1->aData;
    if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){
      rc = sqlite3BtreeBeginTrans(pBtree, 1);
      if( rc==SQLITE_OK ){
        rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
        if( rc==SQLITE_OK ){
          aData[18] = (u8)iVersion;
          aData[19] = (u8)iVersion;
        }
      }
    }
  }

  pBt->doNotUseWAL = 0;
  return rc;
}

Changes to src/btreeInt.h.

416
417
418
419
420
421
422

423
424
425
426
427
428
429
  u16 pageSize;         /* Total number of bytes on a page */
  u16 usableSize;       /* Number of usable bytes on each page */
  u16 maxLocal;         /* Maximum local payload in non-LEAFDATA tables */
  u16 minLocal;         /* Minimum local payload in non-LEAFDATA tables */
  u16 maxLeaf;          /* Maximum local payload in a LEAFDATA table */
  u16 minLeaf;          /* Minimum local payload in a LEAFDATA table */
  u8 inTransaction;     /* Transaction state */

  int nTransaction;     /* Number of open transactions (read + write) */
  u32 nPage;            /* Number of pages in the database */
  void *pSchema;        /* Pointer to space allocated by sqlite3BtreeSchema() */
  void (*xFreeSchema)(void*);  /* Destructor for BtShared.pSchema */
  sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
  Bitvec *pHasContent;  /* Set of pages moved to free-list this transaction */
#ifndef SQLITE_OMIT_SHARED_CACHE







>







416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  u16 pageSize;         /* Total number of bytes on a page */
  u16 usableSize;       /* Number of usable bytes on each page */
  u16 maxLocal;         /* Maximum local payload in non-LEAFDATA tables */
  u16 minLocal;         /* Minimum local payload in non-LEAFDATA tables */
  u16 maxLeaf;          /* Maximum local payload in a LEAFDATA table */
  u16 minLeaf;          /* Minimum local payload in a LEAFDATA table */
  u8 inTransaction;     /* Transaction state */
  u8 doNotUseWAL;       /* If true, do not open write-ahead-log file */
  int nTransaction;     /* Number of open transactions (read + write) */
  u32 nPage;            /* Number of pages in the database */
  void *pSchema;        /* Pointer to space allocated by sqlite3BtreeSchema() */
  void (*xFreeSchema)(void*);  /* Destructor for BtShared.pSchema */
  sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
  Bitvec *pHasContent;  /* Set of pages moved to free-list this transaction */
#ifndef SQLITE_OMIT_SHARED_CACHE

Changes to src/log.c.

1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
        logSummaryUnmap(pSummary, 1);
      }else{
        if( rc==SQLITE_BUSY ){
          rc = SQLITE_OK;
        }
        logSummaryUnmap(pSummary, 0);
      }
      sqlite3OsUnlock(pFd, SQLITE_LOCK_NONE);

      sqlite3_mutex_free(pSummary->mutex);
      sqlite3_free(pSummary);
    }else{
      sqlite3_mutex_leave(mutex);
    }








<







1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
        logSummaryUnmap(pSummary, 1);
      }else{
        if( rc==SQLITE_BUSY ){
          rc = SQLITE_OK;
        }
        logSummaryUnmap(pSummary, 0);
      }


      sqlite3_mutex_free(pSummary->mutex);
      sqlite3_free(pSummary);
    }else{
      sqlite3_mutex_leave(mutex);
    }

Changes to src/pager.c.

486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
....
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
....
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
....
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
....
5732
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
....
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774
5775
5776
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788

  /* The changeCountDone flag is always set for temp-files */
  assert( pPager->tempFile==0 || pPager->changeCountDone );

  return 1;
}
#endif

#ifndef NDEBUG 
static void assert_file_lock(Pager *pPager, int eLock){
  int locktype;
  sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCKSTATE, &locktype);
  assert( locktype==eLock );
}
#else
# define assert_file_lock(x,y) 
#endif


/*
** Return true if it is necessary to write page *pPg into the sub-journal.
** A page needs to be written into the sub-journal if there exists one
** or more open savepoints for which:
**
**   * The page-number is less than or equal to PagerSavepoint.nOrig, and
................................................................................
        ** Variable iNextHdrOffset is set to the offset at which this
        ** problematic header will occur, if it exists. aMagic is used 
        ** as a temporary buffer to inspect the first couple of bytes of
        ** the potential journal header.
        */
        i64 iNextHdrOffset;
        u8 aMagic[8];
	u8 zHeader[sizeof(aJournalMagic)+4];

	memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
	put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec);

        iNextHdrOffset = journalHdrOffset(pPager);
        rc = sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset);
        if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){
          static const u8 zerobyte = 0;
          rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset);
        }
................................................................................
          IOTRACE(("JSYNC %p\n", pPager))
          rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
          if( rc!=SQLITE_OK ) return rc;
        }
        IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr));
        rc = sqlite3OsWrite(
            pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr
	);
        if( rc!=SQLITE_OK ) return rc;
      }
      if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
        PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
        IOTRACE(("JSYNC %p\n", pPager))
        rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| 
          (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
................................................................................
  int rc;                         /* Return code */

  /* Check that a SHARED lock is held on the database file. Because an
  ** EXCLUSIVE lock on the db file is required to delete a WAL, this
  ** ensures there is no race condition between the xAccess() below and
  ** an xDelete() being executed by some other connection.
  */
  assert_file_lock(pPager, SQLITE_LOCK_SHARED);

  if( !pPager->tempFile ){
    char *zLog = sqlite3_mprintf("%s-wal", pPager->zFilename);
    if( !zLog ){
      rc = SQLITE_NOMEM;
    }else{
      rc = sqlite3OsAccess(pPager->pVfs, zLog, SQLITE_ACCESS_EXISTS, pExists);
................................................................................
**
** 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 */

  assert_file_lock(pPager, SQLITE_LOCK_SHARED);
  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 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;
    }

    assert_file_lock(pPager, SQLITE_LOCK_EXCLUSIVE);
  }
  return rc;
}

#endif /* SQLITE_OMIT_DISKIO */







<
<
<
<
<
<
<
<
<
<
<







 







|

|
|







 







|







 







|







 







|







 







|









<
<





486
487
488
489
490
491
492











493
494
495
496
497
498
499
....
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
....
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
....
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
....
5721
5722
5723
5724
5725
5726
5727
5728
5729
5730
5731
5732
5733
5734
5735
....
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
5765
5766
5767
5768
5769
5770


5771
5772
5773
5774
5775

  /* The changeCountDone flag is always set for temp-files */
  assert( pPager->tempFile==0 || pPager->changeCountDone );

  return 1;
}
#endif












/*
** Return true if it is necessary to write page *pPg into the sub-journal.
** A page needs to be written into the sub-journal if there exists one
** or more open savepoints for which:
**
**   * The page-number is less than or equal to PagerSavepoint.nOrig, and
................................................................................
        ** Variable iNextHdrOffset is set to the offset at which this
        ** problematic header will occur, if it exists. aMagic is used 
        ** as a temporary buffer to inspect the first couple of bytes of
        ** the potential journal header.
        */
        i64 iNextHdrOffset;
        u8 aMagic[8];
        u8 zHeader[sizeof(aJournalMagic)+4];

        memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic));
        put32bits(&zHeader[sizeof(aJournalMagic)], pPager->nRec);

        iNextHdrOffset = journalHdrOffset(pPager);
        rc = sqlite3OsRead(pPager->jfd, aMagic, 8, iNextHdrOffset);
        if( rc==SQLITE_OK && 0==memcmp(aMagic, aJournalMagic, 8) ){
          static const u8 zerobyte = 0;
          rc = sqlite3OsWrite(pPager->jfd, &zerobyte, 1, iNextHdrOffset);
        }
................................................................................
          IOTRACE(("JSYNC %p\n", pPager))
          rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags);
          if( rc!=SQLITE_OK ) return rc;
        }
        IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr));
        rc = sqlite3OsWrite(
            pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr
        );
        if( rc!=SQLITE_OK ) return rc;
      }
      if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){
        PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager)));
        IOTRACE(("JSYNC %p\n", pPager))
        rc = sqlite3OsSync(pPager->jfd, pPager->sync_flags| 
          (pPager->sync_flags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0)
................................................................................
  int rc;                         /* Return code */

  /* Check that a SHARED lock is held on the database file. Because an
  ** EXCLUSIVE lock on the db file is required to delete a WAL, this
  ** ensures there is no race condition between the xAccess() below and
  ** an xDelete() being executed by some other connection.
  */
  assert( pPager->state>=PAGER_SHARED );

  if( !pPager->tempFile ){
    char *zLog = sqlite3_mprintf("%s-wal", pPager->zFilename);
    if( !zLog ){
      rc = SQLITE_NOMEM;
    }else{
      rc = sqlite3OsAccess(pPager->pVfs, zLog, SQLITE_ACCESS_EXISTS, pExists);
................................................................................
**
** 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 */

  assert( pPager->state>=PAGER_SHARED );
  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 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 = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE);

    /* 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;
    }


  }
  return rc;
}

#endif /* SQLITE_OMIT_DISKIO */

Changes to src/vdbe.c.

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
          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)







<
<
<









|







5256
5257
5258
5259
5260
5261
5262



5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
          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 = 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 = (char *)sqlite3JournalModename(eNew);
  pOut->n = sqlite3Strlen30(pOut->z);
  pOut->enc = SQLITE_UTF8;
  sqlite3VdbeChangeEncoding(pOut, encoding);
  break;
};  

#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)

Changes to test/wal.test.

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
  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.
#







|









|









|







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
  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.
#

Changes to test/walmode.test.

13
14
15
16
17
18
19

20
21





















22

























23



24



















25

# "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








>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>

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

>
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
84
85
86
87
88
89
90
91
92
93
94
95
# "PRAGMA journal_mode=WAL" mode.
#

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

do_test walmode-1.1 {
  set sqlite_sync_count 0
  execsql { PRAGMA page_size = 1024 }
  execsql { PRAGMA journal_mode = wal }
} {wal}
do_test walmode-1.2 {
  file size test.db
} {1024}
do_test walmode-1.3 {
  set sqlite_sync_count
} {4}
do_test walmode-1.4 {
  file exists test.db-wal
} {0}
do_test walmode-1.5 {
  execsql { CREATE TABLE t1(a, b) }
  file size test.db
} {1024}
do_test walmode-1.6 {
  file exists test.db-wal
} {1}
do_test walmode-1.7 {
  db close
  file exists test.db-wal
} {0}

# There is now a database file with the read and write versions set to 2
# in the file system. This file should default to WAL mode.
#
do_test walmode-2.1 {
  sqlite3 db test.db
  file exists test.db-wal
} {0}
do_test walmode-2.2 {
  execsql { SELECT * FROM sqlite_master }
  file exists test.db-wal
} {1}
do_test walmode-2.3 {
  db close
  file exists test.db-wal
} {0}

# If the first statement executed is "PRAGMA journal_mode = wal", and
# the file is already configured for WAL (read and write versions set
# to 2), then there should be no need to write the database. The 
# statement should cause the client to connect to the log file.
#
set sqlite_sync_count 0
do_test walmode-3.1 {
  sqlite3 db test.db
  execsql { PRAGMA journal_mode = wal }
} {wal}
do_test walmode-3.2 {
  list $sqlite_sync_count [file exists test.db-wal] [file size test.db-wal]
} {0 1 0}

do_test walmode-4.1 {
  execsql { INSERT INTO t1 VALUES(1, 2) }
  execsql { PRAGMA journal_mode = persist }
} {persist}
do_test walmode-4.2 {
  list [file exists test.db-journal] [file exists test.db-wal]
} {1 0}
do_test walmode-4.3 {
  execsql { SELECT * FROM t1 }
} {1 2}
do_test walmode-4.4 {
  db close
  sqlite3 db test.db
  execsql { SELECT * FROM t1 }
} {1 2}
do_test walmode-4.5 {
  list [file exists test.db-journal] [file exists test.db-wal]
} {1 0}

finish_test