1# 2014 December 04 2# 3# The author disclaims copyright to this source code. In place of 4# a legal notice, here is a blessing: 5# 6# May you do good and not evil. 7# May you find forgiveness for yourself and forgive others. 8# May you share freely, never taking more than you give. 9# 10#*********************************************************************** 11# 12 13set testdir [file dirname $argv0] 14source $testdir/tester.tcl 15source $testdir/wal_common.tcl 16set testprefix e_walhook 17 18 19# EVIDENCE-OF: R-00752-43975 The sqlite3_wal_hook() function is used to 20# register a callback that is invoked each time data is committed to a 21# database in wal mode. 22# 23# 1.1: shows that the wal-hook is not invoked in rollback mode. 24# 1.2: but is invoked in wal mode. 25# 26set ::wal_hook_count 0 27proc my_wal_hook {args} { 28 incr ::wal_hook_count 29 return 0 30} 31 32do_test 1.1.1 { 33 db wal_hook my_wal_hook 34 execsql { 35 CREATE TABLE t1(x); 36 INSERT INTO t1 VALUES(1); 37 } 38 set ::wal_hook_count 39} 0 40do_test 1.1.2 { 41 execsql { PRAGMA journal_mode = wal } 42 set ::wal_hook_count 43} 0 44 45do_test 1.3 { 46 execsql { INSERT INTO t1 VALUES(2) } 47 set wal_hook_count 48} 1 49 50do_test 1.4 { 51 execsql { 52 BEGIN; 53 INSERT INTO t1 VALUES(3); 54 INSERT INTO t1 VALUES(4); 55 COMMIT; 56 } 57 set wal_hook_count 58} 2 59 60# EVIDENCE-OF: R-65366-15139 The callback is invoked by SQLite after the 61# commit has taken place and the associated write-lock on the database 62# released 63# 64set ::read_ok 0 65proc my_wal_hook {args} { 66 sqlite3 db2 test.db 67 if {[db2 eval { SELECT * FROM t1 }] == "1 2 3 4 5"} { 68 set ::read_ok 1 69 } 70 db2 close 71} 72do_test 2.1 { 73 execsql { INSERT INTO t1 VALUES(5) } 74 set ::read_ok 75} 1 76 77# EVIDENCE-OF: R-44294-52863 The third parameter is the name of the 78# database that was written to - either "main" or the name of an 79# ATTACH-ed database. 80# 81# EVIDENCE-OF: R-18913-19355 The fourth parameter is the number of pages 82# currently in the write-ahead log file, including those that were just 83# committed. 84# 85set ::wal_hook_args [list] 86proc my_wal_hook {dbname nEntry} { 87 set ::wal_hook_args [list $dbname $nEntry] 88} 89forcedelete test.db2 90do_test 3.0 { 91 execsql { 92 ATTACH 'test.db2' AS aux; 93 CREATE TABLE aux.t2(x); 94 PRAGMA aux.journal_mode = wal; 95 } 96} {wal} 97 98# Database "aux" 99do_test 3.1.1 { 100 set wal_hook_args [list] 101 execsql { INSERT INTO t2 VALUES('a') } 102} {} 103do_test 3.1.2 { 104 set wal_hook_args 105} [list aux [wal_frame_count test.db2-wal 1024]] 106 107# Database "main" 108do_test 3.2.1 { 109 set wal_hook_args [list] 110 execsql { INSERT INTO t1 VALUES(6) } 111} {} 112do_test 3.1.2 { 113 set wal_hook_args 114} [list main [wal_frame_count test.db-wal 1024]] 115 116# EVIDENCE-OF: R-14034-00929 If an error code is returned, that error 117# will propagate back up through the SQLite code base to cause the 118# statement that provoked the callback to report an error, though the 119# commit will have still occurred. 120# 121proc my_wal_hook {args} { return 1 ;# SQLITE_ERROR } 122do_catchsql_test 4.1 { 123 INSERT INTO t1 VALUES(7) 124} {1 {SQL logic error or missing database}} 125 126proc my_wal_hook {args} { return 5 ;# SQLITE_BUSY } 127do_catchsql_test 4.2 { 128 INSERT INTO t1 VALUES(8) 129} {1 {database is locked}} 130 131proc my_wal_hook {args} { return 14 ;# SQLITE_CANTOPEN } 132do_catchsql_test 4.3 { 133 INSERT INTO t1 VALUES(9) 134} {1 {unable to open database file}} 135 136do_execsql_test 4.4 { 137 SELECT * FROM t1 138} {1 2 3 4 5 6 7 8 9} 139 140# EVIDENCE-OF: R-10466-53920 Calling sqlite3_wal_hook() replaces any 141# previously registered write-ahead log callback. 142set ::old_wal_hook 0 143proc my_old_wal_hook {args} { 144 incr ::old_wal_hook 145 return 0 146} 147db wal_hook my_old_wal_hook 148do_test 5.1 { 149 execsql { INSERT INTO t1 VALUES(10) } 150 set ::old_wal_hook 151} {1} 152 153# Replace old_wal_hook. Observe that it is not invoked after it has 154# been replaced. 155proc my_new_wal_hook {args} { return 0 } 156db wal_hook my_new_wal_hook 157do_test 5.2 { 158 execsql { INSERT INTO t1 VALUES(11) } 159 set ::old_wal_hook 160} {1} 161 162 163 164# EVIDENCE-OF: R-42842-27162 Note that the sqlite3_wal_autocheckpoint() 165# interface and the wal_autocheckpoint pragma both invoke 166# sqlite3_wal_hook() and will those overwrite any prior 167# sqlite3_wal_hook() settings. 168# 169set ::old_wal_hook 0 170proc my_old_wal_hook {args} { incr ::old_wal_hook ; return 0 } 171db wal_hook my_old_wal_hook 172do_test 6.1.1 { 173 execsql { INSERT INTO t1 VALUES(12) } 174 set ::old_wal_hook 175} {1} 176do_test 6.1.2 { 177 execsql { PRAGMA wal_autocheckpoint = 1000 } 178 execsql { INSERT INTO t1 VALUES(12) } 179 set ::old_wal_hook 180} {1} 181 182# EVIDENCE-OF: R-52629-38967 The first parameter passed to the callback 183# function when it is invoked is a copy of the third parameter passed to 184# sqlite3_wal_hook() when registering the callback. 185# 186# This is tricky to test using the tcl interface. However, the 187# mechanism used to invoke the tcl script registered as a wal-hook 188# depends on the context pointer being correctly passed through. And 189# since multiple different wal-hook scripts have been successfully 190# invoked by this test script, consider this tested. 191# 192# EVIDENCE-OF: R-23378-42536 The second is a copy of the database 193# handle. 194# 195# There is an assert() in the C wal-hook used by tclsqlite.c to 196# prove this. And that hook has been invoked multiple times when 197# running this script. So consider this requirement tested as well. 198# 199 200finish_test 201