Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -617,10 +617,11 @@ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */ u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ + u8 ckptFullSync; /* True to pass SYNC_FULL to WalCheckpoint() */ /************************************************************************** ** The following block contains those class members that change during ** routine opertion. Class members not in this block are either fixed ** when the pager is first created or else only change when there is a @@ -3080,10 +3081,23 @@ } } } return rc; } + +/* +** Return the value of the flags parameter that should be passed to +** sqlite3OsSync() when checkpointing a WAL file. +*/ +static int walCkptSyncFlags(Pager *pPager){ + int flags = 0; + if( pPager->noSync==0 ){ + flags = pPager->sync_flags | (pPager->ckptFullSync?SQLITE_SYNC_FULL:0); + } + return flags; +} + #endif /* ** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback ** the entire master journal file. The case pSavepoint==NULL occurs when @@ -3260,14 +3274,20 @@ ** ** Numeric values associated with these states are OFF==1, NORMAL=2, ** and FULL=3. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS -void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int bFullFsync){ +void sqlite3PagerSetSafetyLevel(Pager *pPager, int level, int fullFsync){ + assert( 0==(fullFsync & ~(SQLITE_FullFSync|SQLITE_CkptFullFSync)) ); pPager->noSync = (level==1 || pPager->tempFile) ?1:0; pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0; - pPager->sync_flags = (bFullFsync?SQLITE_SYNC_FULL:SQLITE_SYNC_NORMAL); + if( fullFsync & SQLITE_FullFSync ){ + pPager->sync_flags = SQLITE_SYNC_FULL; + }else{ + pPager->sync_flags = SQLITE_SYNC_NORMAL; + } + pPager->ckptFullSync = (fullFsync & SQLITE_CkptFullFSync)!=0; } #endif /* ** The following global variable is incremented whenever the library @@ -3649,12 +3669,11 @@ disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL - sqlite3WalClose(pPager->pWal, - (pPager->noSync ? 0 : pPager->sync_flags), + sqlite3WalClose(pPager->pWal, walCkptSyncFlags(pPager), pPager->pageSize, pTmp ); pPager->pWal = 0; #endif pager_reset(pPager); @@ -6519,14 +6538,12 @@ */ int sqlite3PagerCheckpoint(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pWal ){ u8 *zBuf = (u8 *)pPager->pTmpSpace; - rc = sqlite3WalCheckpoint(pPager->pWal, - (pPager->noSync ? 0 : pPager->sync_flags), - pPager->pageSize, zBuf - ); + int flags = walCkptSyncFlags(pPager); + rc = sqlite3WalCheckpoint(pPager->pWal, flags, pPager->pageSize, zBuf); } return rc; } int sqlite3PagerWalCallback(Pager *pPager){ @@ -6627,13 +6644,12 @@ ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc==SQLITE_OK ){ - rc = sqlite3WalClose(pPager->pWal, - (pPager->noSync ? 0 : pPager->sync_flags), - pPager->pageSize, (u8*)pPager->pTmpSpace + rc = sqlite3WalClose(pPager->pWal, walCkptSyncFlags(pPager), + pPager->pageSize, (u8*)pPager->pTmpSpace ); pPager->pWal = 0; }else{ /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock ** that we did get back to SHARED. */ Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -170,10 +170,11 @@ { "short_column_names", SQLITE_ShortColNames }, { "count_changes", SQLITE_CountRows }, { "empty_result_callbacks", SQLITE_NullCallback }, { "legacy_file_format", SQLITE_LegacyFileFmt }, { "fullfsync", SQLITE_FullFSync }, + { "checkpoint_fullfsync", SQLITE_CkptFullFSync }, { "reverse_unordered_selects", SQLITE_ReverseOrder }, #ifndef SQLITE_OMIT_AUTOMATIC_INDEX { "automatic_index", SQLITE_AutoIndex }, #endif #ifdef SQLITE_DEBUG @@ -1507,14 +1508,14 @@ ** setting changed. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS if( db->autoCommit ){ sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level, - (db->flags&SQLITE_FullFSync)!=0); + db->flags&(SQLITE_FullFSync|SQLITE_CkptFullFSync)); } #endif pragma_out: sqlite3DbFree(db, zLeft); sqlite3DbFree(db, zRight); } #endif /* SQLITE_OMIT_PRAGMA */ Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -912,10 +912,11 @@ #define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ #define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ #define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */ #define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */ +#define SQLITE_CkptFullFSync 0x20000000 /* Use full fsync on checkpoint */ /* ** Bits of the sqlite3.flags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. ** These must be the low-order bits of the flags field. Index: test/pragma.test ================================================================== --- test/pragma.test +++ test/pragma.test @@ -1291,11 +1291,11 @@ sqlite3 dbX :memory: dbX eval {PRAGMA temp_store_directory = ""} dbX close set skip_lock_proxy_tests [path_is_dos "."] -ifcapable !lock_proxy_pragmas&&prefer_proxy_locking { +ifcapable !lock_proxy_pragmas||prefer_proxy_locking { set skip_lock_proxy_tests 1 } if !$skip_lock_proxy_tests { set sqlite_hostid_num 1 Index: test/wal2.test ================================================================== --- test/wal2.test +++ test/wal2.test @@ -1150,7 +1150,62 @@ } $b($can_read,$can_write) } catch { db close } } } + +#------------------------------------------------------------------------- +# Test that "PRAGMA checkpoint_fullsync" appears to be working. +# +foreach {tn sql reslist} { + 1 { } {8 0 3 0 5 0} + 2 { PRAGMA checkpoint_fullfsync = 1 } {8 4 3 2 5 2} + 3 { PRAGMA checkpoint_fullfsync = 0 } {8 0 3 0 5 0} +} { + faultsim_delete_and_reopen + + execsql $sql + do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal} + + set sqlite_sync_count 0 + set sqlite_fullsync_count 0 + + do_execsql_test wal2-14.$tn.2 { + PRAGMA wal_autocheckpoint = 10; + CREATE TABLE t1(a, b); -- 2 wal syncs + INSERT INTO t1 VALUES(1, 2); -- 1 wal sync + PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync + BEGIN; + INSERT INTO t1 VALUES(3, 4); + INSERT INTO t1 VALUES(5, 6); + COMMIT; -- 1 wal sync + PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync + } {10} + + do_test wal2-14.$tn.3 { + list $sqlite_sync_count $sqlite_fullsync_count + } [lrange $reslist 0 1] + + set sqlite_sync_count 0 + set sqlite_fullsync_count 0 + + do_test wal2-14.$tn.4 { + execsql { INSERT INTO t1 VALUES(7, zeroblob(12*4096)) } + list $sqlite_sync_count $sqlite_fullsync_count + } [lrange $reslist 2 3] + + set sqlite_sync_count 0 + set sqlite_fullsync_count 0 + + do_test wal2-14.$tn.5 { + execsql { PRAGMA wal_autocheckpoint = 1000 } + execsql { INSERT INTO t1 VALUES(9, 10) } + execsql { INSERT INTO t1 VALUES(11, 12) } + execsql { INSERT INTO t1 VALUES(13, 14) } + db close + list $sqlite_sync_count $sqlite_fullsync_count + } [lrange $reslist 4 5] +} + + finish_test