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