xref: /sqlite-3.40.0/test/e_walhook.test (revision 45f31be8)
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