000001 # 2014 December 04 000002 # 000003 # The author disclaims copyright to this source code. In place of 000004 # a legal notice, here is a blessing: 000005 # 000006 # May you do good and not evil. 000007 # May you find forgiveness for yourself and forgive others. 000008 # May you share freely, never taking more than you give. 000009 # 000010 #*********************************************************************** 000011 # 000012 000013 set testdir [file dirname $argv0] 000014 source $testdir/tester.tcl 000015 source $testdir/wal_common.tcl 000016 set testprefix e_walhook 000017 000018 000019 # EVIDENCE-OF: R-00752-43975 The sqlite3_wal_hook() function is used to 000020 # register a callback that is invoked each time data is committed to a 000021 # database in wal mode. 000022 # 000023 # 1.1: shows that the wal-hook is not invoked in rollback mode. 000024 # 1.2: but is invoked in wal mode. 000025 # 000026 set ::wal_hook_count 0 000027 proc my_wal_hook {args} { 000028 incr ::wal_hook_count 000029 return 0 000030 } 000031 000032 do_test 1.1.1 { 000033 db wal_hook my_wal_hook 000034 execsql { 000035 CREATE TABLE t1(x); 000036 INSERT INTO t1 VALUES(1); 000037 } 000038 set ::wal_hook_count 000039 } 0 000040 do_test 1.1.2 { 000041 execsql { PRAGMA journal_mode = wal } 000042 set ::wal_hook_count 000043 } 0 000044 000045 do_test 1.3 { 000046 execsql { INSERT INTO t1 VALUES(2) } 000047 set wal_hook_count 000048 } 1 000049 000050 do_test 1.4 { 000051 execsql { 000052 BEGIN; 000053 INSERT INTO t1 VALUES(3); 000054 INSERT INTO t1 VALUES(4); 000055 COMMIT; 000056 } 000057 set wal_hook_count 000058 } 2 000059 000060 # EVIDENCE-OF: R-65366-15139 The callback is invoked by SQLite after the 000061 # commit has taken place and the associated write-lock on the database 000062 # released 000063 # 000064 set ::read_ok 0 000065 proc my_wal_hook {args} { 000066 sqlite3 db2 test.db 000067 if {[db2 eval { SELECT * FROM t1 }] == "1 2 3 4 5"} { 000068 set ::read_ok 1 000069 } 000070 db2 close 000071 } 000072 do_test 2.1 { 000073 execsql { INSERT INTO t1 VALUES(5) } 000074 set ::read_ok 000075 } 1 000076 000077 # EVIDENCE-OF: R-44294-52863 The third parameter is the name of the 000078 # database that was written to - either "main" or the name of an 000079 # ATTACH-ed database. 000080 # 000081 # EVIDENCE-OF: R-18913-19355 The fourth parameter is the number of pages 000082 # currently in the write-ahead log file, including those that were just 000083 # committed. 000084 # 000085 set ::wal_hook_args [list] 000086 proc my_wal_hook {dbname nEntry} { 000087 set ::wal_hook_args [list $dbname $nEntry] 000088 } 000089 forcedelete test.db2 000090 do_test 3.0 { 000091 execsql { 000092 ATTACH 'test.db2' AS aux; 000093 CREATE TABLE aux.t2(x); 000094 PRAGMA aux.journal_mode = wal; 000095 } 000096 } {wal} 000097 000098 # Database "aux" 000099 do_test 3.1.1 { 000100 set wal_hook_args [list] 000101 execsql { INSERT INTO t2 VALUES('a') } 000102 } {} 000103 do_test 3.1.2 { 000104 set wal_hook_args 000105 } [list aux [wal_frame_count test.db2-wal 1024]] 000106 000107 # Database "main" 000108 do_test 3.2.1 { 000109 set wal_hook_args [list] 000110 execsql { INSERT INTO t1 VALUES(6) } 000111 } {} 000112 do_test 3.1.2 { 000113 set wal_hook_args 000114 } [list main [wal_frame_count test.db-wal 1024]] 000115 000116 # EVIDENCE-OF: R-14034-00929 If an error code is returned, that error 000117 # will propagate back up through the SQLite code base to cause the 000118 # statement that provoked the callback to report an error, though the 000119 # commit will have still occurred. 000120 # 000121 proc my_wal_hook {args} { return 1 ;# SQLITE_ERROR } 000122 do_catchsql_test 4.1 { 000123 INSERT INTO t1 VALUES(7) 000124 } {1 {SQL logic error or missing database}} 000125 000126 proc my_wal_hook {args} { return 5 ;# SQLITE_BUSY } 000127 do_catchsql_test 4.2 { 000128 INSERT INTO t1 VALUES(8) 000129 } {1 {database is locked}} 000130 000131 proc my_wal_hook {args} { return 14 ;# SQLITE_CANTOPEN } 000132 do_catchsql_test 4.3 { 000133 INSERT INTO t1 VALUES(9) 000134 } {1 {unable to open database file}} 000135 000136 do_execsql_test 4.4 { 000137 SELECT * FROM t1 000138 } {1 2 3 4 5 6 7 8 9} 000139 000140 # EVIDENCE-OF: R-10466-53920 Calling sqlite3_wal_hook() replaces any 000141 # previously registered write-ahead log callback. 000142 set ::old_wal_hook 0 000143 proc my_old_wal_hook {args} { 000144 incr ::old_wal_hook 000145 return 0 000146 } 000147 db wal_hook my_old_wal_hook 000148 do_test 5.1 { 000149 execsql { INSERT INTO t1 VALUES(10) } 000150 set ::old_wal_hook 000151 } {1} 000152 000153 # Replace old_wal_hook. Observe that it is not invoked after it has 000154 # been replaced. 000155 proc my_new_wal_hook {args} { return 0 } 000156 db wal_hook my_new_wal_hook 000157 do_test 5.2 { 000158 execsql { INSERT INTO t1 VALUES(11) } 000159 set ::old_wal_hook 000160 } {1} 000161 000162 000163 000164 # EVIDENCE-OF: R-57445-43425 Note that the sqlite3_wal_autocheckpoint() 000165 # interface and the wal_autocheckpoint pragma both invoke 000166 # sqlite3_wal_hook() and will overwrite any prior sqlite3_wal_hook() 000167 # settings. 000168 # 000169 set ::old_wal_hook 0 000170 proc my_old_wal_hook {args} { incr ::old_wal_hook ; return 0 } 000171 db wal_hook my_old_wal_hook 000172 do_test 6.1.1 { 000173 execsql { INSERT INTO t1 VALUES(12) } 000174 set ::old_wal_hook 000175 } {1} 000176 do_test 6.1.2 { 000177 execsql { PRAGMA wal_autocheckpoint = 1000 } 000178 execsql { INSERT INTO t1 VALUES(12) } 000179 set ::old_wal_hook 000180 } {1} 000181 000182 # EVIDENCE-OF: R-52629-38967 The first parameter passed to the callback 000183 # function when it is invoked is a copy of the third parameter passed to 000184 # sqlite3_wal_hook() when registering the callback. 000185 # 000186 # This is tricky to test using the tcl interface. However, the 000187 # mechanism used to invoke the tcl script registered as a wal-hook 000188 # depends on the context pointer being correctly passed through. And 000189 # since multiple different wal-hook scripts have been successfully 000190 # invoked by this test script, consider this tested. 000191 # 000192 # EVIDENCE-OF: R-23378-42536 The second is a copy of the database 000193 # handle. 000194 # 000195 # There is an assert() in the C wal-hook used by tclsqlite.c to 000196 # prove this. And that hook has been invoked multiple times when 000197 # running this script. So consider this requirement tested as well. 000198 # 000199 000200 finish_test