Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -2859,10 +2859,35 @@ sqlite3BtreeLeave(p); return rc; #endif } +/* +** If the user has not set the safety-level for this database connection +** using "PRAGMA synchronous", and if the safety-level is not already +** set to the value passed to this function as the second parameter, +** set it so. +*/ +#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS +static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){ + sqlite3 *db; + Db *pDb; + if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){ + while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; } + if( pDb->bSyncSet==0 + && pDb->safety_level!=safety_level + && pDb!=&db->aDb[1] + ){ + pDb->safety_level = safety_level; + sqlite3PagerSetFlags(pBt->pPager, + pDb->safety_level | (db->flags & PAGER_FLAGS_MASK)); + } + } +} +#else +# define setDefaultSyncFlag(pBt,safety_level) +#endif /* ** Get a reference to pPage1 of the database file. This will ** also acquire a readlock on that file. ** @@ -2932,30 +2957,19 @@ int isOpen = 0; rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen); if( rc!=SQLITE_OK ){ goto page1_init_failed; }else{ -#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS - sqlite3 *db; - Db *pDb; - if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){ - while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; } - if( pDb->bSyncSet==0 - && pDb->safety_level==SQLITE_DEFAULT_SYNCHRONOUS+1 - ){ - pDb->safety_level = SQLITE_DEFAULT_WAL_SYNCHRONOUS+1; - sqlite3PagerSetFlags(pBt->pPager, - pDb->safety_level | (db->flags & PAGER_FLAGS_MASK)); - } - } -#endif + setDefaultSyncFlag(pBt, SQLITE_DEFAULT_WAL_SYNCHRONOUS+1); if( isOpen==0 ){ releasePage(pPage1); return SQLITE_OK; } } rc = SQLITE_NOTADB; + }else{ + setDefaultSyncFlag(pBt, SQLITE_DEFAULT_SYNCHRONOUS+1); } #endif /* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload ** fractions and the leaf payload fraction values must be 64, 32, and 32. Index: src/ctime.c ================================================================== --- src/ctime.c +++ src/ctime.c @@ -64,10 +64,16 @@ "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), #endif #if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc) "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), #endif +#if SQLITE_DEFAULT_SYNCHRONOUS + "DEFAULT_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_SYNCHRONOUS), +#endif +#if SQLITE_DEFAULT_WAL_SYNCHRONOUS + "DEFAULT_WAL_SYNCHRONOUS=" CTIMEOPT_VAL(SQLITE_DEFAULT_WAL_SYNCHRONOUS), +#endif #if SQLITE_DIRECT_OVERFLOW_READ "DIRECT_OVERFLOW_READ", #endif #if SQLITE_DISABLE_DIRSYNC "DISABLE_DIRSYNC", Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -738,10 +738,12 @@ LINKVAR( MAX_LIKE_PATTERN_LENGTH ); LINKVAR( MAX_TRIGGER_DEPTH ); LINKVAR( DEFAULT_CACHE_SIZE ); LINKVAR( DEFAULT_PAGE_SIZE ); LINKVAR( DEFAULT_FILE_FORMAT ); + LINKVAR( DEFAULT_SYNCHRONOUS ); + LINKVAR( DEFAULT_WAL_SYNCHRONOUS ); LINKVAR( MAX_ATTACHED ); LINKVAR( MAX_DEFAULT_PAGE_SIZE ); LINKVAR( MAX_WORKER_THREADS ); { ADDED test/sync2.test Index: test/sync2.test ================================================================== --- /dev/null +++ test/sync2.test @@ -0,0 +1,153 @@ +# 2017 March 16 +# +# 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. +# +# Specificly, it tests that "PRAGMA synchronous" appears to work. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix sync2 + +# +# These tests are only applicable when pager pragma are +# enabled. Also, since every test uses an ATTACHed database, they +# are only run when ATTACH is enabled. +# +ifcapable !pager_pragmas||!attach { + finish_test + return +} + +proc execsql_sync {sql} { + set s $::sqlite_sync_count + set res [execsql $sql] + concat [expr $::sqlite_sync_count-$s] $res +} + +proc do_execsql_sync_test {tn sql res} { + uplevel [list do_test $tn [list execsql_sync $sql] [list {*}$res]] +} + +#----------------------------------------------------------------------- +# Tests for journal mode. +# +sqlite3 db test.db +do_execsql_test 1.0 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); +} + +do_execsql_sync_test 1.1 { INSERT INTO t1 VALUES(3, 4) } 4 + +# synchronous=normal. So, 1 sync on the directory, 1 on the journal, 1 +# on the db file. 3 in total. +do_execsql_test 1.2.1 { PRAGMA main.synchronous = NORMAL } +do_execsql_test 1.2.2 { PRAGMA main.synchronous } 1 +do_execsql_sync_test 1.2.3 { INSERT INTO t1 VALUES(5, 6) } 3 + +# synchronous=off. No syncs. +do_execsql_test 1.3.1 { PRAGMA main.synchronous = OFF } +do_execsql_test 1.3.2 { PRAGMA main.synchronous } 0 +do_execsql_sync_test 1.3.3 { INSERT INTO t1 VALUES(7, 8) } 0 + +# synchronous=full, journal_mode=delete. So, 1 sync on the directory, +# 2 on the journal, 1 on the db file. 4 in total. +do_execsql_test 1.4.1 { PRAGMA main.synchronous = FULL } +do_execsql_test 1.4.2 { PRAGMA main.synchronous } 2 +do_execsql_sync_test 1.4.3 { INSERT INTO t1 VALUES(9, 10) } 4 + +#----------------------------------------------------------------------- +# Tests for wal mode. +# +do_execsql_test 1.5 { PRAGMA journal_mode = wal } {wal} + +# sync=full, journal_mode=wal. One sync on the directory, two on the +# wal file. +do_execsql_sync_test 1.6 { INSERT INTO t1 VALUES(11, 12) } 3 + +# One sync on the wal file. +do_execsql_sync_test 1.7 { INSERT INTO t1 VALUES(13, 14) } 1 + +# No syncs. +do_execsql_test 1.8.1 { PRAGMA main.synchronous = NORMAL } +do_execsql_test 1.8.2 { PRAGMA main.synchronous } 1 +do_execsql_sync_test 1.8.3 { INSERT INTO t1 VALUES(15, 16) } 0 + +# One sync on wal file, one on the db file. +do_execsql_sync_test 1.9 { PRAGMA wal_checkpoint } {2 0 3 3} + +# No syncs. +do_execsql_test 1.10.1 { PRAGMA main.synchronous = OFF } +do_execsql_test 1.10.2 { PRAGMA main.synchronous } 0 +do_execsql_sync_test 1.10.3 { INSERT INTO t1 VALUES(17, 18) } 0 + +#----------------------------------------------------------------------- +# Tests for the compile time settings SQLITE_DEFAULT_SYNCHRONOUS and +# SQLITE_DEFAULT_WAL_SYNCHRONOUS. These tests only run if the former +# is set to "2" and the latter to "1". This is not the default, but +# it is currently the recommended configuration. +# +# https://sqlite.org/compile.html#recommended_compile_time_options +# +if {$SQLITE_DEFAULT_SYNCHRONOUS==2 && $SQLITE_DEFAULT_WAL_SYNCHRONOUS==1} { + + db close + sqlite3 db test.db + + # Wal mode, sync=normal. The first transaction does one sync on directory, + # one on the wal file. The second does no syncs. + do_execsql_sync_test 1.11.1 { INSERT INTO t1 VALUES(19, 20) } 2 + do_execsql_sync_test 1.11.2 { INSERT INTO t1 VALUES(21, 22) } 0 + do_execsql_test 1.11.3 { PRAGMA main.synchronous } 1 + + # One sync on wal file, one on the db file. + do_execsql_sync_test 1.12 { PRAGMA wal_checkpoint } {2 0 2 2} + + # First transaction syncs the wal file once, the second not at all. + # one on the wal file. The second does no syncs. + do_execsql_sync_test 1.13.1 { INSERT INTO t1 VALUES(22, 23) } 1 + do_execsql_sync_test 1.13.2 { INSERT INTO t1 VALUES(24, 25) } 0 + + do_execsql_test 1.14 { PRAGMA journal_mode = delete } {delete} + + # Delete mode, sync=full. The first transaction does one sync on + # directory, two on the journal file, one on the db. The second does + # the same. + do_execsql_sync_test 1.15.1 { INSERT INTO t1 VALUES(26, 27) } 4 + do_execsql_sync_test 1.15.2 { INSERT INTO t1 VALUES(28, 29) } 4 + do_execsql_test 1.15.3 { PRAGMA main.synchronous } 2 + + # Switch back to wal mode. + do_execsql_test 1.16 { PRAGMA journal_mode = wal } {wal} + + do_execsql_sync_test 1.17.1 { INSERT INTO t1 VALUES(30, 31) } 2 + do_execsql_sync_test 1.17.2 { INSERT INTO t1 VALUES(32, 33) } 0 + do_execsql_test 1.17.3 { PRAGMA main.synchronous } 1 + + # Now set synchronous=off, then switch back to delete mode. Check + # that the db handle is still using synchronous=off. + do_execsql_test 1.18.3 { PRAGMA main.synchronous=off } + do_execsql_test 1.18 { PRAGMA journal_mode = delete } {delete} + + do_execsql_sync_test 1.19.1 { INSERT INTO t1 VALUES(34, 35) } 0 + do_execsql_sync_test 1.19.2 { INSERT INTO t1 VALUES(36, 37) } 0 + do_execsql_test 1.19.3 { PRAGMA main.synchronous } 0 + + # Close and reopen the db. Back to synchronous=normal. + db close + sqlite3 db test.db + do_execsql_sync_test 1.20.1 { INSERT INTO t1 VALUES(38, 39) } 4 + do_execsql_sync_test 1.20.2 { INSERT INTO t1 VALUES(40, 41) } 4 + do_execsql_test 1.20.3 { PRAGMA main.synchronous } 2 +} + +finish_test