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