16e45e0c8Sdan# 2014 December 04 26e45e0c8Sdan# 36e45e0c8Sdan# The author disclaims copyright to this source code. In place of 46e45e0c8Sdan# a legal notice, here is a blessing: 56e45e0c8Sdan# 66e45e0c8Sdan# May you do good and not evil. 76e45e0c8Sdan# May you find forgiveness for yourself and forgive others. 86e45e0c8Sdan# May you share freely, never taking more than you give. 96e45e0c8Sdan# 106e45e0c8Sdan#*********************************************************************** 116e45e0c8Sdan# 126e45e0c8Sdan 136e45e0c8Sdanset testdir [file dirname $argv0] 146e45e0c8Sdansource $testdir/tester.tcl 156e45e0c8Sdansource $testdir/wal_common.tcl 166e45e0c8Sdanset testprefix e_walhook 176e45e0c8Sdan 186e45e0c8Sdan 196e45e0c8Sdan# EVIDENCE-OF: R-00752-43975 The sqlite3_wal_hook() function is used to 206e45e0c8Sdan# register a callback that is invoked each time data is committed to a 216e45e0c8Sdan# database in wal mode. 226e45e0c8Sdan# 236e45e0c8Sdan# 1.1: shows that the wal-hook is not invoked in rollback mode. 246e45e0c8Sdan# 1.2: but is invoked in wal mode. 256e45e0c8Sdan# 266e45e0c8Sdanset ::wal_hook_count 0 276e45e0c8Sdanproc my_wal_hook {args} { 286e45e0c8Sdan incr ::wal_hook_count 296e45e0c8Sdan return 0 306e45e0c8Sdan} 316e45e0c8Sdan 326e45e0c8Sdando_test 1.1.1 { 336e45e0c8Sdan db wal_hook my_wal_hook 346e45e0c8Sdan execsql { 356e45e0c8Sdan CREATE TABLE t1(x); 366e45e0c8Sdan INSERT INTO t1 VALUES(1); 376e45e0c8Sdan } 386e45e0c8Sdan set ::wal_hook_count 396e45e0c8Sdan} 0 406e45e0c8Sdando_test 1.1.2 { 416e45e0c8Sdan execsql { PRAGMA journal_mode = wal } 426e45e0c8Sdan set ::wal_hook_count 436e45e0c8Sdan} 0 446e45e0c8Sdan 456e45e0c8Sdando_test 1.3 { 466e45e0c8Sdan execsql { INSERT INTO t1 VALUES(2) } 476e45e0c8Sdan set wal_hook_count 486e45e0c8Sdan} 1 496e45e0c8Sdan 506e45e0c8Sdando_test 1.4 { 516e45e0c8Sdan execsql { 526e45e0c8Sdan BEGIN; 536e45e0c8Sdan INSERT INTO t1 VALUES(3); 546e45e0c8Sdan INSERT INTO t1 VALUES(4); 556e45e0c8Sdan COMMIT; 566e45e0c8Sdan } 576e45e0c8Sdan set wal_hook_count 586e45e0c8Sdan} 2 596e45e0c8Sdan 606e45e0c8Sdan# EVIDENCE-OF: R-65366-15139 The callback is invoked by SQLite after the 616e45e0c8Sdan# commit has taken place and the associated write-lock on the database 626e45e0c8Sdan# released 636e45e0c8Sdan# 646e45e0c8Sdanset ::read_ok 0 656e45e0c8Sdanproc my_wal_hook {args} { 666e45e0c8Sdan sqlite3 db2 test.db 676e45e0c8Sdan if {[db2 eval { SELECT * FROM t1 }] == "1 2 3 4 5"} { 686e45e0c8Sdan set ::read_ok 1 696e45e0c8Sdan } 706e45e0c8Sdan db2 close 716e45e0c8Sdan} 726e45e0c8Sdando_test 2.1 { 736e45e0c8Sdan execsql { INSERT INTO t1 VALUES(5) } 746e45e0c8Sdan set ::read_ok 756e45e0c8Sdan} 1 766e45e0c8Sdan 776e45e0c8Sdan# EVIDENCE-OF: R-44294-52863 The third parameter is the name of the 786e45e0c8Sdan# database that was written to - either "main" or the name of an 796e45e0c8Sdan# ATTACH-ed database. 806e45e0c8Sdan# 816e45e0c8Sdan# EVIDENCE-OF: R-18913-19355 The fourth parameter is the number of pages 826e45e0c8Sdan# currently in the write-ahead log file, including those that were just 836e45e0c8Sdan# committed. 846e45e0c8Sdan# 856e45e0c8Sdanset ::wal_hook_args [list] 866e45e0c8Sdanproc my_wal_hook {dbname nEntry} { 876e45e0c8Sdan set ::wal_hook_args [list $dbname $nEntry] 886e45e0c8Sdan} 896e45e0c8Sdanforcedelete test.db2 906e45e0c8Sdando_test 3.0 { 916e45e0c8Sdan execsql { 926e45e0c8Sdan ATTACH 'test.db2' AS aux; 936e45e0c8Sdan CREATE TABLE aux.t2(x); 946e45e0c8Sdan PRAGMA aux.journal_mode = wal; 956e45e0c8Sdan } 966e45e0c8Sdan} {wal} 976e45e0c8Sdan 986e45e0c8Sdan# Database "aux" 996e45e0c8Sdando_test 3.1.1 { 1006e45e0c8Sdan set wal_hook_args [list] 1016e45e0c8Sdan execsql { INSERT INTO t2 VALUES('a') } 1026e45e0c8Sdan} {} 1036e45e0c8Sdando_test 3.1.2 { 1046e45e0c8Sdan set wal_hook_args 1056e45e0c8Sdan} [list aux [wal_frame_count test.db2-wal 1024]] 1066e45e0c8Sdan 1076e45e0c8Sdan# Database "main" 1086e45e0c8Sdando_test 3.2.1 { 1096e45e0c8Sdan set wal_hook_args [list] 1106e45e0c8Sdan execsql { INSERT INTO t1 VALUES(6) } 1116e45e0c8Sdan} {} 1126e45e0c8Sdando_test 3.1.2 { 1136e45e0c8Sdan set wal_hook_args 1146e45e0c8Sdan} [list main [wal_frame_count test.db-wal 1024]] 1156e45e0c8Sdan 1166e45e0c8Sdan# EVIDENCE-OF: R-14034-00929 If an error code is returned, that error 1176e45e0c8Sdan# will propagate back up through the SQLite code base to cause the 1186e45e0c8Sdan# statement that provoked the callback to report an error, though the 1196e45e0c8Sdan# commit will have still occurred. 1206e45e0c8Sdan# 1216e45e0c8Sdanproc my_wal_hook {args} { return 1 ;# SQLITE_ERROR } 1226e45e0c8Sdando_catchsql_test 4.1 { 1236e45e0c8Sdan INSERT INTO t1 VALUES(7) 124*a690ff36Sdrh} {1 {SQL logic error}} 1256e45e0c8Sdan 1266e45e0c8Sdanproc my_wal_hook {args} { return 5 ;# SQLITE_BUSY } 1276e45e0c8Sdando_catchsql_test 4.2 { 1286e45e0c8Sdan INSERT INTO t1 VALUES(8) 1296e45e0c8Sdan} {1 {database is locked}} 1306e45e0c8Sdan 1316e45e0c8Sdanproc my_wal_hook {args} { return 14 ;# SQLITE_CANTOPEN } 1326e45e0c8Sdando_catchsql_test 4.3 { 1336e45e0c8Sdan INSERT INTO t1 VALUES(9) 1346e45e0c8Sdan} {1 {unable to open database file}} 1356e45e0c8Sdan 1366e45e0c8Sdando_execsql_test 4.4 { 1376e45e0c8Sdan SELECT * FROM t1 1386e45e0c8Sdan} {1 2 3 4 5 6 7 8 9} 1396e45e0c8Sdan 1406e45e0c8Sdan# EVIDENCE-OF: R-10466-53920 Calling sqlite3_wal_hook() replaces any 1416e45e0c8Sdan# previously registered write-ahead log callback. 1426e45e0c8Sdanset ::old_wal_hook 0 1436e45e0c8Sdanproc my_old_wal_hook {args} { 1446e45e0c8Sdan incr ::old_wal_hook 1456e45e0c8Sdan return 0 1466e45e0c8Sdan} 1476e45e0c8Sdandb wal_hook my_old_wal_hook 1486e45e0c8Sdando_test 5.1 { 1496e45e0c8Sdan execsql { INSERT INTO t1 VALUES(10) } 1506e45e0c8Sdan set ::old_wal_hook 1516e45e0c8Sdan} {1} 1526e45e0c8Sdan 1536e45e0c8Sdan# Replace old_wal_hook. Observe that it is not invoked after it has 1546e45e0c8Sdan# been replaced. 1556e45e0c8Sdanproc my_new_wal_hook {args} { return 0 } 1566e45e0c8Sdandb wal_hook my_new_wal_hook 1576e45e0c8Sdando_test 5.2 { 1586e45e0c8Sdan execsql { INSERT INTO t1 VALUES(11) } 1596e45e0c8Sdan set ::old_wal_hook 1606e45e0c8Sdan} {1} 1616e45e0c8Sdan 1626e45e0c8Sdan 1636e45e0c8Sdan 1644f8f5e44Sdrh# EVIDENCE-OF: R-57445-43425 Note that the sqlite3_wal_autocheckpoint() 1656e45e0c8Sdan# interface and the wal_autocheckpoint pragma both invoke 1664f8f5e44Sdrh# sqlite3_wal_hook() and will overwrite any prior sqlite3_wal_hook() 1674f8f5e44Sdrh# settings. 1686e45e0c8Sdan# 1696e45e0c8Sdanset ::old_wal_hook 0 1706e45e0c8Sdanproc my_old_wal_hook {args} { incr ::old_wal_hook ; return 0 } 1711f03b865Sdandb wal_hook my_old_wal_hook 1726e45e0c8Sdando_test 6.1.1 { 1736e45e0c8Sdan execsql { INSERT INTO t1 VALUES(12) } 1746e45e0c8Sdan set ::old_wal_hook 1756e45e0c8Sdan} {1} 1766e45e0c8Sdando_test 6.1.2 { 1771f03b865Sdan execsql { PRAGMA wal_autocheckpoint = 1000 } 1786e45e0c8Sdan execsql { INSERT INTO t1 VALUES(12) } 1796e45e0c8Sdan set ::old_wal_hook 1806e45e0c8Sdan} {1} 1816e45e0c8Sdan 1826e45e0c8Sdan# EVIDENCE-OF: R-52629-38967 The first parameter passed to the callback 1836e45e0c8Sdan# function when it is invoked is a copy of the third parameter passed to 1846e45e0c8Sdan# sqlite3_wal_hook() when registering the callback. 1856e45e0c8Sdan# 1866e45e0c8Sdan# This is tricky to test using the tcl interface. However, the 1876e45e0c8Sdan# mechanism used to invoke the tcl script registered as a wal-hook 1886e45e0c8Sdan# depends on the context pointer being correctly passed through. And 1896e45e0c8Sdan# since multiple different wal-hook scripts have been successfully 1906e45e0c8Sdan# invoked by this test script, consider this tested. 1916e45e0c8Sdan# 1926e45e0c8Sdan# EVIDENCE-OF: R-23378-42536 The second is a copy of the database 1936e45e0c8Sdan# handle. 1946e45e0c8Sdan# 1956e45e0c8Sdan# There is an assert() in the C wal-hook used by tclsqlite.c to 1966e45e0c8Sdan# prove this. And that hook has been invoked multiple times when 1976e45e0c8Sdan# running this script. So consider this requirement tested as well. 1986e45e0c8Sdan# 1996e45e0c8Sdan 2006e45e0c8Sdanfinish_test 201