downgrade memory unlock failures to info level and fix function name in log output
[sqlcipher.git] / test / e_walhook.test
blobb4a31563677b3da088b1e262ea39e291896251d8
1 # 2014 December 04
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
13 set testdir [file dirname $argv0]
14 source $testdir/tester.tcl
15 source $testdir/wal_common.tcl
16 set testprefix e_walhook
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.
23 #   1.1: shows that the wal-hook is not invoked in rollback mode.
24 #   1.2: but is invoked in wal mode.
26 set ::wal_hook_count 0
27 proc my_wal_hook {args} {
28   incr ::wal_hook_count
29   return 0
32 do_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
40 do_test 1.1.2 {
41   execsql { PRAGMA journal_mode = wal }
42   set ::wal_hook_count
43 } 0
45 do_test 1.3 {
46   execsql { INSERT INTO t1 VALUES(2) }
47   set wal_hook_count
48 } 1
50 do_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
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
64 set ::read_ok 0
65 proc 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
72 do_test 2.1 {
73   execsql { INSERT INTO t1 VALUES(5) }
74   set ::read_ok
75 } 1
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.
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.
85 set ::wal_hook_args [list]
86 proc my_wal_hook {dbname nEntry} {
87   set ::wal_hook_args [list $dbname $nEntry]
89 forcedelete test.db2
90 do_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}
98 # Database "aux"
99 do_test 3.1.1 {
100   set wal_hook_args [list]
101   execsql { INSERT INTO t2 VALUES('a') }
102 } {}
103 do_test 3.1.2 {
104   set wal_hook_args
105 } [list aux [wal_frame_count test.db2-wal 1024]]
107 # Database "main"
108 do_test 3.2.1 {
109   set wal_hook_args [list]
110   execsql { INSERT INTO t1 VALUES(6) }
111 } {}
112 do_test 3.1.2 {
113   set wal_hook_args
114 } [list main [wal_frame_count test.db-wal 1024]]
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.
121 proc my_wal_hook {args} { return 1 ;# SQLITE_ERROR }
122 do_catchsql_test 4.1 {
123   INSERT INTO t1 VALUES(7)
124 } {1 {SQL logic error}}
126 proc my_wal_hook {args} { return 5 ;# SQLITE_BUSY }
127 do_catchsql_test 4.2 {
128   INSERT INTO t1 VALUES(8)
129 } {1 {database is locked}}
131 proc my_wal_hook {args} { return 14 ;# SQLITE_CANTOPEN }
132 do_catchsql_test 4.3 {
133   INSERT INTO t1 VALUES(9)
134 } {1 {unable to open database file}}
136 do_execsql_test 4.4 {
137   SELECT * FROM t1
138 } {1 2 3 4 5 6 7 8 9}
140 # EVIDENCE-OF: R-10466-53920 Calling sqlite3_wal_hook() replaces any
141 # previously registered write-ahead log callback.
142 set ::old_wal_hook 0
143 proc my_old_wal_hook {args} {
144   incr ::old_wal_hook 
145   return 0
147 db wal_hook my_old_wal_hook
148 do_test 5.1 {
149   execsql { INSERT INTO t1 VALUES(10) }
150   set ::old_wal_hook
151 } {1}
153 # Replace old_wal_hook. Observe that it is not invoked after it has 
154 # been replaced.
155 proc my_new_wal_hook {args} { return 0 }
156 db wal_hook my_new_wal_hook
157 do_test 5.2 {
158   execsql { INSERT INTO t1 VALUES(11) }
159   set ::old_wal_hook
160 } {1}
164 # EVIDENCE-OF: R-57445-43425 Note that the sqlite3_wal_autocheckpoint()
165 # interface and the wal_autocheckpoint pragma both invoke
166 # sqlite3_wal_hook() and will overwrite any prior sqlite3_wal_hook()
167 # settings.
169 set ::old_wal_hook 0
170 proc my_old_wal_hook {args} { incr ::old_wal_hook ; return 0 }
171 db wal_hook my_old_wal_hook
172 do_test 6.1.1 {
173   execsql { INSERT INTO t1 VALUES(12) }
174   set ::old_wal_hook
175 } {1}
176 do_test 6.1.2 {
177   execsql { PRAGMA wal_autocheckpoint = 1000 }
178   execsql { INSERT INTO t1 VALUES(12) }
179   set ::old_wal_hook
180 } {1}
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.
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.
192 # EVIDENCE-OF: R-23378-42536 The second is a copy of the database
193 # handle.
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.
200 finish_test