1aa940eacSdrh# 2004 Jan 14 2aa940eacSdrh# 3aa940eacSdrh# The author disclaims copyright to this source code. In place of 4aa940eacSdrh# a legal notice, here is a blessing: 5aa940eacSdrh# 6aa940eacSdrh# May you do good and not evil. 7aa940eacSdrh# May you find forgiveness for yourself and forgive others. 8aa940eacSdrh# May you share freely, never taking more than you give. 9aa940eacSdrh# 10aa940eacSdrh#*********************************************************************** 11aa940eacSdrh# This file implements regression tests for TCL interface to the 12aa940eacSdrh# SQLite library. 13aa940eacSdrh# 14aa940eacSdrh# The focus of the tests in this file is the following interface: 15aa940eacSdrh# 1671fd80bfSdanielk1977# sqlite_commit_hook (tests hook-1..hook-3 inclusive) 1771fd80bfSdanielk1977# sqlite_update_hook (tests hook-4-*) 1871fd80bfSdanielk1977# sqlite_rollback_hook (tests hook-5.*) 19aa940eacSdrh# 202943c372Sdanielk1977# $Id: hook.test,v 1.15 2009/04/07 14:14:23 danielk1977 Exp $ 21aa940eacSdrh 22aa940eacSdrhset testdir [file dirname $argv0] 23aa940eacSdrhsource $testdir/tester.tcl 2446c47d46Sdanset ::testprefix hook 25aa940eacSdrh 26aa940eacSdrhdo_test hook-1.2 { 27aa940eacSdrh db commit_hook 28aa940eacSdrh} {} 29aa940eacSdrh 30aa940eacSdrh 31aa940eacSdrhdo_test hook-3.1 { 32aa940eacSdrh set commit_cnt 0 33aa940eacSdrh proc commit_hook {} { 34aa940eacSdrh incr ::commit_cnt 35aa940eacSdrh return 0 36aa940eacSdrh } 37aa940eacSdrh db commit_hook ::commit_hook 38aa940eacSdrh db commit_hook 39aa940eacSdrh} {::commit_hook} 40aa940eacSdrhdo_test hook-3.2 { 41aa940eacSdrh set commit_cnt 42aa940eacSdrh} {0} 43aa940eacSdrhdo_test hook-3.3 { 44aa940eacSdrh execsql { 45aa940eacSdrh CREATE TABLE t2(a,b); 46aa940eacSdrh } 47aa940eacSdrh set commit_cnt 48aa940eacSdrh} {1} 49aa940eacSdrhdo_test hook-3.4 { 50aa940eacSdrh execsql { 51aa940eacSdrh INSERT INTO t2 VALUES(1,2); 52aa940eacSdrh INSERT INTO t2 SELECT a+1, b+1 FROM t2; 53aa940eacSdrh INSERT INTO t2 SELECT a+2, b+2 FROM t2; 54aa940eacSdrh } 55aa940eacSdrh set commit_cnt 56aa940eacSdrh} {4} 57aa940eacSdrhdo_test hook-3.5 { 58aa940eacSdrh set commit_cnt {} 59aa940eacSdrh proc commit_hook {} { 60aa940eacSdrh set ::commit_cnt [execsql {SELECT * FROM t2}] 61aa940eacSdrh return 0 62aa940eacSdrh } 63aa940eacSdrh execsql { 64aa940eacSdrh INSERT INTO t2 VALUES(5,6); 65aa940eacSdrh } 66aa940eacSdrh set commit_cnt 67aa940eacSdrh} {1 2 2 3 3 4 4 5 5 6} 68aa940eacSdrhdo_test hook-3.6 { 69aa940eacSdrh set commit_cnt {} 70aa940eacSdrh proc commit_hook {} { 71aa940eacSdrh set ::commit_cnt [execsql {SELECT * FROM t2}] 72aa940eacSdrh return 1 73aa940eacSdrh } 74aa940eacSdrh catchsql { 75aa940eacSdrh INSERT INTO t2 VALUES(6,7); 76aa940eacSdrh } 77aa940eacSdrh} {1 {constraint failed}} 78433dccfbSdrhverify_ex_errcode hook-3.6b SQLITE_CONSTRAINT_COMMITHOOK 79aa940eacSdrhdo_test hook-3.7 { 801d850a72Sdanielk1977 set ::commit_cnt 81aa940eacSdrh} {1 2 2 3 3 4 4 5 5 6 6 7} 82aa940eacSdrhdo_test hook-3.8 { 83aa940eacSdrh execsql {SELECT * FROM t2} 84aa940eacSdrh} {1 2 2 3 3 4 4 5 5 6} 85aa940eacSdrh 860f14e2ebSdrh# Test turnning off the commit hook 870f14e2ebSdrh# 880f14e2ebSdrhdo_test hook-3.9 { 890f14e2ebSdrh db commit_hook {} 900f14e2ebSdrh set ::commit_cnt {} 910f14e2ebSdrh execsql { 920f14e2ebSdrh INSERT INTO t2 VALUES(7,8); 930f14e2ebSdrh } 940f14e2ebSdrh set ::commit_cnt 950f14e2ebSdrh} {} 96aa940eacSdrh 97853799a2Sdrh# Ticket #3564. 98853799a2Sdrh# 99853799a2Sdrhdo_test hook-3.10 { 100fda06befSmistachkin forcedelete test2.db test2.db-journal 101853799a2Sdrh sqlite3 db2 test2.db 102853799a2Sdrh proc commit_hook {} { 103853799a2Sdrh set y [db2 one {SELECT y FROM t3 WHERE y>10}] 104853799a2Sdrh return [expr {$y>10}] 105853799a2Sdrh } 106853799a2Sdrh db2 eval {CREATE TABLE t3(x,y)} 107853799a2Sdrh db2 commit_hook commit_hook 108853799a2Sdrh catchsql {INSERT INTO t3 VALUES(1,2)} db2 109853799a2Sdrh catchsql {INSERT INTO t3 VALUES(11,12)} db2 110853799a2Sdrh catchsql {INSERT INTO t3 VALUES(3,4)} db2 111853799a2Sdrh db2 eval { 112853799a2Sdrh SELECT * FROM t3 ORDER BY x; 113853799a2Sdrh } 114853799a2Sdrh} {1 2 3 4} 115853799a2Sdrhdb2 close 116853799a2Sdrh 117853799a2Sdrh 11871fd80bfSdanielk1977#---------------------------------------------------------------------------- 11971fd80bfSdanielk1977# Tests for the update-hook. 12094eb6a14Sdanielk1977# 12171fd80bfSdanielk1977# 4.1.* - Very simple tests. Test that the update hook is invoked correctly 12271fd80bfSdanielk1977# for INSERT, DELETE and UPDATE statements, including DELETE 12371fd80bfSdanielk1977# statements with no WHERE clause. 12471fd80bfSdanielk1977# 4.2.* - Check that the update-hook is invoked for rows modified by trigger 12571fd80bfSdanielk1977# bodies. Also that the database name is correctly reported when 12671fd80bfSdanielk1977# an attached database is modified. 12771fd80bfSdanielk1977# 4.3.* - Do some sorting, grouping, compound queries, population and 12871fd80bfSdanielk1977# depopulation of indices, to make sure the update-hook is not 12971fd80bfSdanielk1977# invoked incorrectly. 13071fd80bfSdanielk1977# 131ef1bd970Sdrh# EVIDENCE-OF: R-21999-45122 The sqlite3_update_hook() interface 132ef1bd970Sdrh# registers a callback function with the database connection identified 133ef1bd970Sdrh# by the first argument to be invoked whenever a row is updated, 134ef1bd970Sdrh# inserted or deleted in a rowid table. 13571fd80bfSdanielk1977 13671fd80bfSdanielk1977# Simple tests 137ef1bd970Sdrhdo_test hook-4.1.1a { 13894eb6a14Sdanielk1977 catchsql { 13994eb6a14Sdanielk1977 DROP TABLE t1; 14094eb6a14Sdanielk1977 } 141ef1bd970Sdrh unset -nocomplain ::update_hook 142ef1bd970Sdrh set ::update_hook {} 143ef1bd970Sdrh db update_hook [list lappend ::update_hook] 144ef1bd970Sdrh # 145d43c0cbeSdrh # EVIDENCE-OF: R-24531-54682 The update hook is not invoked when 146d43c0cbeSdrh # internal system tables are modified (i.e. sqlite_sequence). 147ef1bd970Sdrh # 14894eb6a14Sdanielk1977 execsql { 14994eb6a14Sdanielk1977 CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 150bbbb0e80Sdrh CREATE TABLE t1w(a INT PRIMARY KEY, b) WITHOUT ROWID; 151ef1bd970Sdrh } 152ef1bd970Sdrh set ::update_hook 153ef1bd970Sdrh} {} 154ef1bd970Sdrhdo_test hook-4.1.1b { 155ef1bd970Sdrh execsql { 15694eb6a14Sdanielk1977 INSERT INTO t1 VALUES(1, 'one'); 15794eb6a14Sdanielk1977 INSERT INTO t1 VALUES(2, 'two'); 15894eb6a14Sdanielk1977 INSERT INTO t1 VALUES(3, 'three'); 159bbbb0e80Sdrh INSERT INTO t1w SELECT * FROM t1; 16094eb6a14Sdanielk1977 } 16194eb6a14Sdanielk1977} {} 162ef1bd970Sdrh 163ef1bd970Sdrh# EVIDENCE-OF: R-15506-57666 The second callback argument is one of 164ef1bd970Sdrh# SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE, depending on the 165ef1bd970Sdrh# operation that caused the callback to be invoked. 166ef1bd970Sdrh# 167ef1bd970Sdrh# EVIDENCE-OF: R-29213-61195 The third and fourth arguments to the 168ef1bd970Sdrh# callback contain pointers to the database and table name containing 169ef1bd970Sdrh# the affected row. 170ef1bd970Sdrh# 171ef1bd970Sdrh# EVIDENCE-OF: R-30809-57812 The final callback parameter is the rowid 172ef1bd970Sdrh# of the row. 173ef1bd970Sdrh# 17471fd80bfSdanielk1977do_test hook-4.1.2 { 175ef1bd970Sdrh set ::update_hook {} 17694eb6a14Sdanielk1977 execsql { 17794eb6a14Sdanielk1977 INSERT INTO t1 VALUES(4, 'four'); 17894eb6a14Sdanielk1977 DELETE FROM t1 WHERE b = 'two'; 17994eb6a14Sdanielk1977 UPDATE t1 SET b = '' WHERE a = 1 OR a = 3; 180d78901daSdrh DELETE FROM t1 WHERE 1; -- Avoid the truncate optimization (for now) 18194eb6a14Sdanielk1977 } 18294eb6a14Sdanielk1977 set ::update_hook 18394eb6a14Sdanielk1977} [list \ 18494eb6a14Sdanielk1977 INSERT main t1 4 \ 18594eb6a14Sdanielk1977 DELETE main t1 2 \ 18694eb6a14Sdanielk1977 UPDATE main t1 1 \ 18794eb6a14Sdanielk1977 UPDATE main t1 3 \ 18894eb6a14Sdanielk1977 DELETE main t1 1 \ 18994eb6a14Sdanielk1977 DELETE main t1 3 \ 19094eb6a14Sdanielk1977 DELETE main t1 4 \ 19194eb6a14Sdanielk1977] 19294eb6a14Sdanielk1977 193bbbb0e80Sdrh# EVIDENCE-OF: R-61808-14344 The sqlite3_update_hook() interface does 194bbbb0e80Sdrh# not fire callbacks for changes to a WITHOUT ROWID table. 195bbbb0e80Sdrh# 196ef1bd970Sdrh# EVIDENCE-OF: R-33257-44249 The update hook is not invoked when WITHOUT 197ef1bd970Sdrh# ROWID tables are modified. 198ef1bd970Sdrh# 199bbbb0e80Sdrhdo_test hook-4.1.2w { 200bbbb0e80Sdrh set ::update_hook {} 201bbbb0e80Sdrh execsql { 202bbbb0e80Sdrh INSERT INTO t1w VALUES(4, 'four'); 203bbbb0e80Sdrh DELETE FROM t1w WHERE b = 'two'; 204bbbb0e80Sdrh UPDATE t1w SET b = '' WHERE a = 1 OR a = 3; 205bbbb0e80Sdrh DELETE FROM t1w WHERE 1; -- Avoid the truncate optimization (for now) 206bbbb0e80Sdrh } 207bbbb0e80Sdrh set ::update_hook 208bbbb0e80Sdrh} {} 209bbbb0e80Sdrh 2102943c372Sdanielk1977ifcapable trigger { 21191fd4d46Sdrh # Update hook is not invoked for changes to sqlite_master 21291fd4d46Sdrh # 21391fd4d46Sdrh do_test hook-4.1.3 { 21491fd4d46Sdrh set ::update_hook {} 21591fd4d46Sdrh execsql { 21691fd4d46Sdrh CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN SELECT RAISE(IGNORE); END; 21791fd4d46Sdrh } 21891fd4d46Sdrh set ::update_hook 21991fd4d46Sdrh } {} 22091fd4d46Sdrh do_test hook-4.1.4 { 22191fd4d46Sdrh set ::update_hook {} 22291fd4d46Sdrh execsql { 22391fd4d46Sdrh DROP TRIGGER r1; 22491fd4d46Sdrh } 22591fd4d46Sdrh set ::update_hook 22691fd4d46Sdrh } {} 22791fd4d46Sdrh 22894eb6a14Sdanielk1977 set ::update_hook {} 22971fd80bfSdanielk1977 do_test hook-4.2.1 { 23094eb6a14Sdanielk1977 catchsql { 23194eb6a14Sdanielk1977 DROP TABLE t2; 23294eb6a14Sdanielk1977 } 23394eb6a14Sdanielk1977 execsql { 23494eb6a14Sdanielk1977 CREATE TABLE t2(c INTEGER PRIMARY KEY, d); 23594eb6a14Sdanielk1977 CREATE TRIGGER t1_trigger AFTER INSERT ON t1 BEGIN 23694eb6a14Sdanielk1977 INSERT INTO t2 VALUES(new.a, new.b); 23794eb6a14Sdanielk1977 UPDATE t2 SET d = d || ' via trigger' WHERE new.a = c; 23894eb6a14Sdanielk1977 DELETE FROM t2 WHERE new.a = c; 23994eb6a14Sdanielk1977 END; 24094eb6a14Sdanielk1977 } 24194eb6a14Sdanielk1977 } {} 24271fd80bfSdanielk1977 do_test hook-4.2.2 { 24394eb6a14Sdanielk1977 execsql { 24494eb6a14Sdanielk1977 INSERT INTO t1 VALUES(1, 'one'); 24594eb6a14Sdanielk1977 INSERT INTO t1 VALUES(2, 'two'); 24694eb6a14Sdanielk1977 } 24794eb6a14Sdanielk1977 set ::update_hook 24894eb6a14Sdanielk1977 } [list \ 24994eb6a14Sdanielk1977 INSERT main t1 1 \ 25094eb6a14Sdanielk1977 INSERT main t2 1 \ 25194eb6a14Sdanielk1977 UPDATE main t2 1 \ 25294eb6a14Sdanielk1977 DELETE main t2 1 \ 25394eb6a14Sdanielk1977 INSERT main t1 2 \ 25494eb6a14Sdanielk1977 INSERT main t2 2 \ 25594eb6a14Sdanielk1977 UPDATE main t2 2 \ 25694eb6a14Sdanielk1977 DELETE main t2 2 \ 25794eb6a14Sdanielk1977 ] 2583bdca9c9Sdanielk1977} else { 2593bdca9c9Sdanielk1977 execsql { 2603bdca9c9Sdanielk1977 INSERT INTO t1 VALUES(1, 'one'); 2613bdca9c9Sdanielk1977 INSERT INTO t1 VALUES(2, 'two'); 2623bdca9c9Sdanielk1977 } 2633bdca9c9Sdanielk1977} 26494eb6a14Sdanielk1977 2653bdca9c9Sdanielk1977# Update-hook + ATTACH 26694eb6a14Sdanielk1977set ::update_hook {} 2675a8f9374Sdanielk1977ifcapable attach { 26871fd80bfSdanielk1977 do_test hook-4.2.3 { 269fda06befSmistachkin forcedelete test2.db 27094eb6a14Sdanielk1977 execsql { 27194eb6a14Sdanielk1977 ATTACH 'test2.db' AS aux; 27294eb6a14Sdanielk1977 CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b); 27394eb6a14Sdanielk1977 INSERT INTO aux.t3 SELECT * FROM t1; 27494eb6a14Sdanielk1977 UPDATE t3 SET b = 'two or so' WHERE a = 2; 275d78901daSdrh DELETE FROM t3 WHERE 1; -- Avoid the truncate optimization (for now) 27694eb6a14Sdanielk1977 } 27794eb6a14Sdanielk1977 set ::update_hook 27894eb6a14Sdanielk1977 } [list \ 27994eb6a14Sdanielk1977 INSERT aux t3 1 \ 28094eb6a14Sdanielk1977 INSERT aux t3 2 \ 28194eb6a14Sdanielk1977 UPDATE aux t3 2 \ 28294eb6a14Sdanielk1977 DELETE aux t3 1 \ 28394eb6a14Sdanielk1977 DELETE aux t3 2 \ 28494eb6a14Sdanielk1977 ] 2855a8f9374Sdanielk1977} 28694eb6a14Sdanielk1977 2873bdca9c9Sdanielk1977ifcapable trigger { 2883bdca9c9Sdanielk1977 execsql { 2893bdca9c9Sdanielk1977 DROP TRIGGER t1_trigger; 2903bdca9c9Sdanielk1977 } 2913bdca9c9Sdanielk1977} 2923bdca9c9Sdanielk1977 29371fd80bfSdanielk1977# Test that other vdbe operations involving btree structures do not 29471fd80bfSdanielk1977# incorrectly invoke the update-hook. 29594eb6a14Sdanielk1977set ::update_hook {} 29671fd80bfSdanielk1977do_test hook-4.3.1 { 29794eb6a14Sdanielk1977 execsql { 29894eb6a14Sdanielk1977 CREATE INDEX t1_i ON t1(b); 29994eb6a14Sdanielk1977 INSERT INTO t1 VALUES(3, 'three'); 30094eb6a14Sdanielk1977 UPDATE t1 SET b = ''; 30194eb6a14Sdanielk1977 DELETE FROM t1 WHERE a > 1; 30294eb6a14Sdanielk1977 } 30394eb6a14Sdanielk1977 set ::update_hook 30494eb6a14Sdanielk1977} [list \ 30594eb6a14Sdanielk1977 INSERT main t1 3 \ 30694eb6a14Sdanielk1977 UPDATE main t1 1 \ 30794eb6a14Sdanielk1977 UPDATE main t1 2 \ 30894eb6a14Sdanielk1977 UPDATE main t1 3 \ 30994eb6a14Sdanielk1977 DELETE main t1 2 \ 31094eb6a14Sdanielk1977 DELETE main t1 3 \ 31194eb6a14Sdanielk1977] 31294eb6a14Sdanielk1977set ::update_hook {} 3135a8f9374Sdanielk1977ifcapable compound&&attach { 31471fd80bfSdanielk1977 do_test hook-4.3.2 { 31594eb6a14Sdanielk1977 execsql { 31694eb6a14Sdanielk1977 SELECT * FROM t1 UNION SELECT * FROM t3; 31794eb6a14Sdanielk1977 SELECT * FROM t1 UNION ALL SELECT * FROM t3; 31894eb6a14Sdanielk1977 SELECT * FROM t1 INTERSECT SELECT * FROM t3; 31994eb6a14Sdanielk1977 SELECT * FROM t1 EXCEPT SELECT * FROM t3; 32094eb6a14Sdanielk1977 SELECT * FROM t1 ORDER BY b; 32194eb6a14Sdanielk1977 SELECT * FROM t1 GROUP BY b; 32294eb6a14Sdanielk1977 } 32394eb6a14Sdanielk1977 set ::update_hook 32494eb6a14Sdanielk1977 } [list] 325ff890793Sdanielk1977} 326382874fcSdan 327382874fcSdando_test hook-4.4 { 328382874fcSdan execsql { 329382874fcSdan CREATE TABLE t4(a UNIQUE, b); 330382874fcSdan INSERT INTO t4 VALUES(1, 'a'); 331382874fcSdan INSERT INTO t4 VALUES(2, 'b'); 332382874fcSdan } 333382874fcSdan set ::update_hook [list] 334382874fcSdan execsql { 335382874fcSdan REPLACE INTO t4 VALUES(1, 'c'); 336382874fcSdan } 337382874fcSdan set ::update_hook 338382874fcSdan} [list INSERT main t4 3 ] 339382874fcSdando_execsql_test hook-4.4.1 { 340382874fcSdan SELECT * FROM t4 ORDER BY a; 341382874fcSdan} {1 c 2 b} 342382874fcSdando_test hook-4.4.2 { 343382874fcSdan set ::update_hook [list] 344382874fcSdan execsql { 345382874fcSdan PRAGMA recursive_triggers = on; 346382874fcSdan REPLACE INTO t4 VALUES(1, 'd'); 347382874fcSdan } 348382874fcSdan set ::update_hook 349382874fcSdan} [list INSERT main t4 4 ] 350382874fcSdando_execsql_test hook-4.4.3 { 351382874fcSdan SELECT * FROM t4 ORDER BY a; 352382874fcSdan} {1 d 2 b} 353382874fcSdan 35471fd80bfSdanielk1977db update_hook {} 35571fd80bfSdanielk1977# 35671fd80bfSdanielk1977#---------------------------------------------------------------------------- 35771fd80bfSdanielk1977 35871fd80bfSdanielk1977#---------------------------------------------------------------------------- 35971fd80bfSdanielk1977# Test the rollback-hook. The rollback-hook is a bit more complicated than 36071fd80bfSdanielk1977# either the commit or update hooks because a rollback can happen 36171fd80bfSdanielk1977# explicitly (an sql ROLLBACK statement) or implicitly (a constraint or 36271fd80bfSdanielk1977# error condition). 36371fd80bfSdanielk1977# 36471fd80bfSdanielk1977# hook-5.1.* - Test explicit rollbacks. 365f3f06bb3Sdanielk1977# hook-5.2.* - Test implicit rollbacks caused by constraint failure. 366f3f06bb3Sdanielk1977# 367f3f06bb3Sdanielk1977# hook-5.3.* - Test implicit rollbacks caused by IO errors. 368f3f06bb3Sdanielk1977# hook-5.4.* - Test implicit rollbacks caused by malloc() failure. 369f3f06bb3Sdanielk1977# hook-5.5.* - Test hot-journal rollbacks. Or should the rollback hook 370f3f06bb3Sdanielk1977# not be called for these? 37171fd80bfSdanielk1977# 37271fd80bfSdanielk1977 37371fd80bfSdanielk1977do_test hook-5.0 { 37471fd80bfSdanielk1977 # Configure the rollback hook to increment global variable 37571fd80bfSdanielk1977 # $::rollback_hook each time it is invoked. 37671fd80bfSdanielk1977 set ::rollback_hook 0 37771fd80bfSdanielk1977 db rollback_hook [list incr ::rollback_hook] 37871fd80bfSdanielk1977} {} 37971fd80bfSdanielk1977 38071fd80bfSdanielk1977# Test explicit rollbacks. Not much can really go wrong here. 381f3f06bb3Sdanielk1977# 38271fd80bfSdanielk1977do_test hook-5.1.1 { 38371fd80bfSdanielk1977 set ::rollback_hook 0 38471fd80bfSdanielk1977 execsql { 38571fd80bfSdanielk1977 BEGIN; 38671fd80bfSdanielk1977 ROLLBACK; 38771fd80bfSdanielk1977 } 38871fd80bfSdanielk1977 set ::rollback_hook 38971fd80bfSdanielk1977} {1} 39071fd80bfSdanielk1977 39171fd80bfSdanielk1977# Test implicit rollbacks caused by constraints. 392f3f06bb3Sdanielk1977# 39371fd80bfSdanielk1977do_test hook-5.2.1 { 39471fd80bfSdanielk1977 set ::rollback_hook 0 39571fd80bfSdanielk1977 catchsql { 39671fd80bfSdanielk1977 DROP TABLE t1; 39771fd80bfSdanielk1977 CREATE TABLE t1(a PRIMARY KEY, b); 39871fd80bfSdanielk1977 INSERT INTO t1 VALUES('one', 'I'); 39971fd80bfSdanielk1977 INSERT INTO t1 VALUES('one', 'I'); 40071fd80bfSdanielk1977 } 40171fd80bfSdanielk1977 set ::rollback_hook 40271fd80bfSdanielk1977} {1} 40371fd80bfSdanielk1977do_test hook-5.2.2 { 40471fd80bfSdanielk1977 # Check that the INSERT transaction above really was rolled back. 40571fd80bfSdanielk1977 execsql { 40671fd80bfSdanielk1977 SELECT count(*) FROM t1; 40771fd80bfSdanielk1977 } 40871fd80bfSdanielk1977} {1} 40971fd80bfSdanielk1977 41071fd80bfSdanielk1977# 411f3f06bb3Sdanielk1977# End rollback-hook testing. 41271fd80bfSdanielk1977#---------------------------------------------------------------------------- 41394eb6a14Sdanielk1977 414c9206ed5Sdan#---------------------------------------------------------------------------- 415c9206ed5Sdan# Test that if a commit-hook returns non-zero (causing a rollback), the 416c9206ed5Sdan# rollback-hook is invoked. 417c9206ed5Sdan# 418c9206ed5Sdanproc commit_hook {} { 419c9206ed5Sdan lappend ::hooks COMMIT 420c9206ed5Sdan return 1 421c9206ed5Sdan} 422c9206ed5Sdanproc rollback_hook {} { 423c9206ed5Sdan lappend ::hooks ROLLBACK 424c9206ed5Sdan} 425c9206ed5Sdando_test hook-6.1 { 426c9206ed5Sdan set ::hooks [list] 427c9206ed5Sdan db commit_hook commit_hook 428c9206ed5Sdan db rollback_hook rollback_hook 429c9206ed5Sdan catchsql { 430c9206ed5Sdan BEGIN; 431c9206ed5Sdan INSERT INTO t1 VALUES('two', 'II'); 432c9206ed5Sdan COMMIT; 433c9206ed5Sdan } 434c9206ed5Sdan execsql { SELECT * FROM t1 } 435c9206ed5Sdan} {one I} 436c9206ed5Sdando_test hook-6.2 { 437c9206ed5Sdan set ::hooks 438c9206ed5Sdan} {COMMIT ROLLBACK} 439c9206ed5Sdanunset ::hooks 440c9206ed5Sdan 44146c47d46Sdan#---------------------------------------------------------------------------- 44246c47d46Sdan# The following tests - hook-7.* - test the pre-update hook. 44346c47d46Sdan# 4449b1c62d4Sdrhifcapable !preupdate { 4459b1c62d4Sdrh finish_test 4469b1c62d4Sdrh return 4479b1c62d4Sdrh} 4489b1c62d4Sdrh# 44946c47d46Sdan# 7.1.1 - INSERT statement. 45046c47d46Sdan# 7.1.2 - INSERT INTO ... SELECT statement. 45146c47d46Sdan# 7.1.3 - REPLACE INTO ... (rowid conflict) 45246c47d46Sdan# 7.1.4 - REPLACE INTO ... (other index conflicts) 45346c47d46Sdan# 7.1.5 - REPLACE INTO ... (both rowid and other index conflicts) 45446c47d46Sdan# 45546c47d46Sdan# 7.2.1 - DELETE statement. 45646c47d46Sdan# 7.2.2 - DELETE statement that uses the truncate optimization. 45746c47d46Sdan# 45846c47d46Sdan# 7.3.1 - UPDATE statement. 45946c47d46Sdan# 7.3.2 - UPDATE statement that modifies the rowid. 46046c47d46Sdan# 7.3.3 - UPDATE OR REPLACE ... (rowid conflict). 46146c47d46Sdan# 7.3.4 - UPDATE OR REPLACE ... (other index conflicts) 46246c47d46Sdan# 7.3.4 - UPDATE OR REPLACE ... (both rowid and other index conflicts) 46346c47d46Sdan# 46446c47d46Sdan# 7.4.1 - Test that the pre-update-hook is invoked only once if a row being 46546c47d46Sdan# deleted is removed by a BEFORE trigger. 46646c47d46Sdan# 46746c47d46Sdan# 7.4.2 - Test that the pre-update-hook is invoked if a BEFORE trigger 46846c47d46Sdan# removes a row being updated. In this case the update hook should 46946c47d46Sdan# be invoked with SQLITE_INSERT as the opcode when inserting the 47046c47d46Sdan# new version of the row. 47146c47d46Sdan# 47246c47d46Sdan# TODO: Short records (those created before a column is added to a table 47346c47d46Sdan# using ALTER TABLE) 47446c47d46Sdan# 47546c47d46Sdan 47646c47d46Sdanproc do_preupdate_test {tn sql x} { 47746c47d46Sdan set X [list] 47846c47d46Sdan foreach elem $x {lappend X $elem} 47946c47d46Sdan uplevel do_test $tn [list " 48046c47d46Sdan set ::preupdate \[list\] 48146c47d46Sdan execsql { $sql } 48246c47d46Sdan set ::preupdate 48346c47d46Sdan "] [list $X] 48446c47d46Sdan} 48546c47d46Sdan 48646c47d46Sdanproc preupdate_hook {args} { 48746c47d46Sdan set type [lindex $args 0] 48846c47d46Sdan eval lappend ::preupdate $args 48937db03bfSdan if {$type != "INSERT"} { 49046c47d46Sdan for {set i 0} {$i < [db preupdate count]} {incr i} { 49146c47d46Sdan lappend ::preupdate [db preupdate old $i] 49246c47d46Sdan } 49346c47d46Sdan } 49437db03bfSdan if {$type != "DELETE"} { 49537db03bfSdan for {set i 0} {$i < [db preupdate count]} {incr i} { 49637db03bfSdan set rc [catch { db preupdate new $i } v] 49737db03bfSdan lappend ::preupdate $v 49837db03bfSdan } 49937db03bfSdan } 50046c47d46Sdan} 50146c47d46Sdan 50246c47d46Sdandb close 50346c47d46Sdanforcedelete test.db 50446c47d46Sdansqlite3 db test.db 50546c47d46Sdandb preupdate hook preupdate_hook 50646c47d46Sdan 50746c47d46Sdan# Set up a schema to use for tests 7.1.* to 7.3.*. 50846c47d46Sdando_execsql_test 7.0 { 50946c47d46Sdan CREATE TABLE t1(a, b); 51046c47d46Sdan CREATE TABLE t2(x, y); 51146c47d46Sdan CREATE TABLE t3(i, j, UNIQUE(i)); 51246c47d46Sdan 51346c47d46Sdan INSERT INTO t2 VALUES('a', 'b'); 51446c47d46Sdan INSERT INTO t2 VALUES('c', 'd'); 51546c47d46Sdan 51646c47d46Sdan INSERT INTO t3 VALUES(4, 16); 51746c47d46Sdan INSERT INTO t3 VALUES(5, 25); 51846c47d46Sdan INSERT INTO t3 VALUES(6, 36); 51946c47d46Sdan} 52046c47d46Sdan 52146c47d46Sdando_preupdate_test 7.1.1 { 52246c47d46Sdan INSERT INTO t1 VALUES('x', 'y') 52337db03bfSdan} {INSERT main t1 1 1 x y} 52446c47d46Sdan 52546c47d46Sdan# 7.1.2.1 does not use the xfer optimization. 7.1.2.2 does. 52646c47d46Sdando_preupdate_test 7.1.2.1 { 52746c47d46Sdan INSERT INTO t1 SELECT y, x FROM t2; 52837db03bfSdan} {INSERT main t1 2 2 b a INSERT main t1 3 3 d c} 52946c47d46Sdando_preupdate_test 7.1.2.2 { 53046c47d46Sdan INSERT INTO t1 SELECT * FROM t2; 53137db03bfSdan} {INSERT main t1 4 4 a b INSERT main t1 5 5 c d} 53246c47d46Sdan 53346c47d46Sdando_preupdate_test 7.1.3 { 53446c47d46Sdan REPLACE INTO t1(rowid, a, b) VALUES(1, 1, 1); 53546c47d46Sdan} { 53646c47d46Sdan DELETE main t1 1 1 x y 53737db03bfSdan INSERT main t1 1 1 1 1 53846c47d46Sdan} 53946c47d46Sdan 54046c47d46Sdando_preupdate_test 7.1.4 { 54146c47d46Sdan REPLACE INTO t3 VALUES(4, NULL); 54246c47d46Sdan} { 54346c47d46Sdan DELETE main t3 1 1 4 16 54437db03bfSdan INSERT main t3 4 4 4 {} 54546c47d46Sdan} 54646c47d46Sdan 54746c47d46Sdando_preupdate_test 7.1.5 { 54846c47d46Sdan REPLACE INTO t3(rowid, i, j) VALUES(2, 6, NULL); 54946c47d46Sdan} { 55046c47d46Sdan DELETE main t3 2 2 5 25 55146c47d46Sdan DELETE main t3 3 3 6 36 55237db03bfSdan INSERT main t3 2 2 6 {} 55346c47d46Sdan} 55446c47d46Sdan 55546c47d46Sdando_execsql_test 7.2.0 { SELECT rowid FROM t1 } {1 2 3 4 5} 55646c47d46Sdan 55746c47d46Sdando_preupdate_test 7.2.1 { 55846c47d46Sdan DELETE FROM t1 WHERE rowid = 3 55946c47d46Sdan} { 56046c47d46Sdan DELETE main t1 3 3 d c 56146c47d46Sdan} 56246c47d46Sdando_preupdate_test 7.2.2 { 56346c47d46Sdan DELETE FROM t1 56446c47d46Sdan} { 56567c495acSdrh DELETE main t1 1 1 1 1 56667c495acSdrh DELETE main t1 2 2 b a 56767c495acSdrh DELETE main t1 4 4 a b 56867c495acSdrh DELETE main t1 5 5 c d 56946c47d46Sdan} 57046c47d46Sdan 57146c47d46Sdando_execsql_test 7.3.0 { 57246c47d46Sdan DELETE FROM t1; 57346c47d46Sdan DELETE FROM t2; 57446c47d46Sdan DELETE FROM t3; 57546c47d46Sdan 57646c47d46Sdan INSERT INTO t2 VALUES('a', 'b'); 57746c47d46Sdan INSERT INTO t2 VALUES('c', 'd'); 57846c47d46Sdan 57946c47d46Sdan INSERT INTO t3 VALUES(4, 16); 58046c47d46Sdan INSERT INTO t3 VALUES(5, 25); 58146c47d46Sdan INSERT INTO t3 VALUES(6, 36); 58246c47d46Sdan} 58346c47d46Sdan 58446c47d46Sdando_preupdate_test 7.3.1 { 58546c47d46Sdan UPDATE t2 SET y = y||y; 58646c47d46Sdan} { 58737db03bfSdan UPDATE main t2 1 1 a b a bb 58837db03bfSdan UPDATE main t2 2 2 c d c dd 58946c47d46Sdan} 59046c47d46Sdan 59146c47d46Sdando_preupdate_test 7.3.2 { 59246c47d46Sdan UPDATE t2 SET rowid = rowid-1; 59346c47d46Sdan} { 59437db03bfSdan UPDATE main t2 1 0 a bb a bb 59537db03bfSdan UPDATE main t2 2 1 c dd c dd 59646c47d46Sdan} 59746c47d46Sdan 59846c47d46Sdando_preupdate_test 7.3.3 { 59946c47d46Sdan UPDATE OR REPLACE t2 SET rowid = 1 WHERE x = 'a' 60046c47d46Sdan} { 60146c47d46Sdan DELETE main t2 1 1 c dd 60237db03bfSdan UPDATE main t2 0 1 a bb a bb 60346c47d46Sdan} 60446c47d46Sdan 60546c47d46Sdando_preupdate_test 7.3.4.1 { 60646c47d46Sdan UPDATE OR REPLACE t3 SET i = 5 WHERE i = 6 60746c47d46Sdan} { 60846c47d46Sdan DELETE main t3 2 2 5 25 60937db03bfSdan UPDATE main t3 3 3 6 36 5 36 61046c47d46Sdan} 61146c47d46Sdan 61246c47d46Sdando_execsql_test 7.3.4.2 { 61346c47d46Sdan INSERT INTO t3 VALUES(10, 100); 61446c47d46Sdan SELECT rowid, * FROM t3; 61546c47d46Sdan} {1 4 16 3 5 36 4 10 100} 61646c47d46Sdan 61746c47d46Sdando_preupdate_test 7.3.5 { 61846c47d46Sdan UPDATE OR REPLACE t3 SET rowid = 1, i = 5 WHERE j = 100; 61946c47d46Sdan} { 62046c47d46Sdan DELETE main t3 1 1 4 16 62146c47d46Sdan DELETE main t3 3 3 5 36 62237db03bfSdan UPDATE main t3 4 1 10 100 5 100 62346c47d46Sdan} 62446c47d46Sdan 62546c47d46Sdando_execsql_test 7.4.1.0 { 62646c47d46Sdan CREATE TABLE t4(a, b); 62746c47d46Sdan INSERT INTO t4 VALUES('a', 1); 62846c47d46Sdan INSERT INTO t4 VALUES('b', 2); 62946c47d46Sdan INSERT INTO t4 VALUES('c', 3); 63046c47d46Sdan 63146c47d46Sdan CREATE TRIGGER t4t BEFORE DELETE ON t4 BEGIN 63246c47d46Sdan DELETE FROM t4 WHERE b = 1; 63346c47d46Sdan END; 63446c47d46Sdan} 63546c47d46Sdan 63646c47d46Sdando_preupdate_test 7.4.1.1 { 63746c47d46Sdan DELETE FROM t4 WHERE b = 3 63846c47d46Sdan} { 63946c47d46Sdan DELETE main t4 1 1 a 1 64046c47d46Sdan DELETE main t4 3 3 c 3 64146c47d46Sdan} 64246c47d46Sdan 64346c47d46Sdando_execsql_test 7.4.1.2 { 64446c47d46Sdan INSERT INTO t4(rowid, a, b) VALUES(1, 'a', 1); 64546c47d46Sdan INSERT INTO t4(rowid, a, b) VALUES(3, 'c', 3); 64646c47d46Sdan} 64746c47d46Sdando_preupdate_test 7.4.1.3 { 64846c47d46Sdan DELETE FROM t4 WHERE b = 1 64946c47d46Sdan} { 65046c47d46Sdan DELETE main t4 1 1 a 1 65146c47d46Sdan} 65246c47d46Sdan 65346c47d46Sdando_execsql_test 7.4.2.0 { 65446c47d46Sdan CREATE TABLE t5(a, b); 65546c47d46Sdan INSERT INTO t5 VALUES('a', 1); 65646c47d46Sdan INSERT INTO t5 VALUES('b', 2); 65746c47d46Sdan INSERT INTO t5 VALUES('c', 3); 65846c47d46Sdan 65946c47d46Sdan CREATE TRIGGER t5t BEFORE UPDATE ON t5 BEGIN 66046c47d46Sdan DELETE FROM t5 WHERE b = 1; 66146c47d46Sdan END; 66246c47d46Sdan} 66346c47d46Sdando_preupdate_test 7.4.2.1 { 66446c47d46Sdan UPDATE t5 SET b = 4 WHERE a = 'c' 66546c47d46Sdan} { 66646c47d46Sdan DELETE main t5 1 1 a 1 66737db03bfSdan UPDATE main t5 3 3 c 3 c 4 66846c47d46Sdan} 66946c47d46Sdan 67046c47d46Sdando_execsql_test 7.4.2.2 { 67146c47d46Sdan INSERT INTO t5(rowid, a, b) VALUES(1, 'a', 1); 67246c47d46Sdan} 67346c47d46Sdan 67446c47d46Sdando_preupdate_test 7.4.2.3 { 67546c47d46Sdan UPDATE t5 SET b = 5 WHERE a = 'a' 67646c47d46Sdan} { 67746c47d46Sdan DELETE main t5 1 1 a 1 67846c47d46Sdan} 67946c47d46Sdan 680*37f3ac8fSdanifcapable altertable { 68146c47d46Sdan do_execsql_test 7.5.1.0 { 68246c47d46Sdan CREATE TABLE t7(a, b); 68346c47d46Sdan INSERT INTO t7 VALUES('one', 'two'); 68446c47d46Sdan INSERT INTO t7 VALUES('three', 'four'); 68546c47d46Sdan ALTER TABLE t7 ADD COLUMN c DEFAULT NULL; 68646c47d46Sdan } 68746c47d46Sdan 68846c47d46Sdan do_preupdate_test 7.5.1.1 { 68946c47d46Sdan DELETE FROM t7 WHERE a = 'one' 69046c47d46Sdan } { 69146c47d46Sdan DELETE main t7 1 1 one two {} 69246c47d46Sdan } 69346c47d46Sdan 69446c47d46Sdan do_preupdate_test 7.5.1.2 { 69546c47d46Sdan UPDATE t7 SET b = 'five' 69646c47d46Sdan } { 69737db03bfSdan UPDATE main t7 2 2 three four {} three five {} 69846c47d46Sdan } 69946c47d46Sdan 70046c47d46Sdan do_execsql_test 7.5.2.0 { 70146c47d46Sdan CREATE TABLE t8(a, b); 70246c47d46Sdan INSERT INTO t8 VALUES('one', 'two'); 70346c47d46Sdan INSERT INTO t8 VALUES('three', 'four'); 70446c47d46Sdan ALTER TABLE t8 ADD COLUMN c DEFAULT 'xxx'; 70546c47d46Sdan } 706*37f3ac8fSdan} 70746c47d46Sdan 70805accd22Sdanif 0 { 709319eeb7bSdan # At time of writing, these two are broken. They demonstrate that the 71021e8d012Sdan # sqlite3_preupdate_old() method does not handle the case where ALTER TABLE 71121e8d012Sdan # has been used to add a column with a default value other than NULL. 71221e8d012Sdan # 71346c47d46Sdan do_preupdate_test 7.5.2.1 { 71446c47d46Sdan DELETE FROM t8 WHERE a = 'one' 71546c47d46Sdan } { 71646c47d46Sdan DELETE main t8 1 1 one two xxx 71746c47d46Sdan } 71846c47d46Sdan do_preupdate_test 7.5.2.2 { 71946c47d46Sdan UPDATE t8 SET b = 'five' 72046c47d46Sdan } { 72137db03bfSdan UPDATE main t8 2 2 three four xxx three five xxx 72246c47d46Sdan } 7233a432b0bSdrh} 72446c47d46Sdan 725319eeb7bSdan# This block of tests verifies that IPK values are correctly reported 726319eeb7bSdan# by the sqlite3_preupdate_old() and sqlite3_preupdate_new() functions. 727319eeb7bSdan# 728319eeb7bSdando_execsql_test 7.6.1 { CREATE TABLE t9(a, b INTEGER PRIMARY KEY, c) } 729319eeb7bSdando_preupdate_test 7.6.2 { 730319eeb7bSdan INSERT INTO t9 VALUES(1, 2, 3); 731319eeb7bSdan UPDATE t9 SET b = b+1, c = c+1; 732319eeb7bSdan DELETE FROM t9 WHERE a = 1; 733319eeb7bSdan} { 734319eeb7bSdan INSERT main t9 2 2 1 2 3 735319eeb7bSdan UPDATE main t9 2 3 1 2 3 1 3 4 736319eeb7bSdan DELETE main t9 3 3 1 3 4 737319eeb7bSdan} 738319eeb7bSdan 7391e7a2d43Sdan#-------------------------------------------------------------------------- 7401e7a2d43Sdan# Test that the sqlite3_preupdate_depth() API seems to work. 7411e7a2d43Sdan# 7421e7a2d43Sdanproc preupdate_hook {args} { 7431e7a2d43Sdan set type [lindex $args 0] 7441e7a2d43Sdan eval lappend ::preupdate $args 7451e7a2d43Sdan eval lappend ::preupdate [db preupdate depth] 7461e7a2d43Sdan 7471e7a2d43Sdan if {$type != "INSERT"} { 7481e7a2d43Sdan for {set i 0} {$i < [db preupdate count]} {incr i} { 7491e7a2d43Sdan lappend ::preupdate [db preupdate old $i] 7501e7a2d43Sdan } 7511e7a2d43Sdan } 7521e7a2d43Sdan if {$type != "DELETE"} { 7531e7a2d43Sdan for {set i 0} {$i < [db preupdate count]} {incr i} { 7541e7a2d43Sdan set rc [catch { db preupdate new $i } v] 7551e7a2d43Sdan lappend ::preupdate $v 7561e7a2d43Sdan } 7571e7a2d43Sdan } 7581e7a2d43Sdan} 7591e7a2d43Sdan 7601e7a2d43Sdandb close 7611e7a2d43Sdanforcedelete test.db 7621e7a2d43Sdansqlite3 db test.db 7631e7a2d43Sdandb preupdate hook preupdate_hook 7641e7a2d43Sdan 7651e7a2d43Sdando_execsql_test 7.6.1 { 7661e7a2d43Sdan CREATE TABLE t1(x PRIMARY KEY); 7671e7a2d43Sdan CREATE TABLE t2(x PRIMARY KEY); 7681e7a2d43Sdan CREATE TABLE t3(x PRIMARY KEY); 7691e7a2d43Sdan CREATE TABLE t4(x PRIMARY KEY); 7701e7a2d43Sdan 7711e7a2d43Sdan CREATE TRIGGER a AFTER INSERT ON t1 BEGIN INSERT INTO t2 VALUES(new.x); END; 7721e7a2d43Sdan CREATE TRIGGER b AFTER INSERT ON t2 BEGIN INSERT INTO t3 VALUES(new.x); END; 7731e7a2d43Sdan CREATE TRIGGER c AFTER INSERT ON t3 BEGIN INSERT INTO t4 VALUES(new.x); END; 7741e7a2d43Sdan 7751e7a2d43Sdan CREATE TRIGGER d AFTER UPDATE ON t1 BEGIN UPDATE t2 SET x = new.x; END; 7761e7a2d43Sdan CREATE TRIGGER e AFTER UPDATE ON t2 BEGIN UPDATE t3 SET x = new.x; END; 7771e7a2d43Sdan CREATE TRIGGER f AFTER UPDATE ON t3 BEGIN UPDATE t4 SET x = new.x; END; 7781e7a2d43Sdan 7791e7a2d43Sdan CREATE TRIGGER g AFTER DELETE ON t1 BEGIN DELETE FROM t2 WHERE 1; END; 7801e7a2d43Sdan CREATE TRIGGER h AFTER DELETE ON t2 BEGIN DELETE FROM t3 WHERE 1; END; 7811e7a2d43Sdan CREATE TRIGGER i AFTER DELETE ON t3 BEGIN DELETE FROM t4 WHERE 1; END; 7821e7a2d43Sdan} 7831e7a2d43Sdan 7841e7a2d43Sdando_preupdate_test 7.6.2 { 7851e7a2d43Sdan INSERT INTO t1 VALUES('xyz'); 7861e7a2d43Sdan} { 7871e7a2d43Sdan INSERT main t1 1 1 0 xyz 7881e7a2d43Sdan INSERT main t2 1 1 1 xyz 7891e7a2d43Sdan INSERT main t3 1 1 2 xyz 7901e7a2d43Sdan INSERT main t4 1 1 3 xyz 7911e7a2d43Sdan} 7921e7a2d43Sdando_preupdate_test 7.6.3 { 7931e7a2d43Sdan UPDATE t1 SET x = 'abc'; 7941e7a2d43Sdan} { 7951e7a2d43Sdan UPDATE main t1 1 1 0 xyz abc 7961e7a2d43Sdan UPDATE main t2 1 1 1 xyz abc 7971e7a2d43Sdan UPDATE main t3 1 1 2 xyz abc 7981e7a2d43Sdan UPDATE main t4 1 1 3 xyz abc 7991e7a2d43Sdan} 8001e7a2d43Sdando_preupdate_test 7.6.4 { 8011e7a2d43Sdan DELETE FROM t1 WHERE 1; 8021e7a2d43Sdan} { 8031e7a2d43Sdan DELETE main t1 1 1 0 abc 8041e7a2d43Sdan DELETE main t2 1 1 1 abc 8051e7a2d43Sdan DELETE main t3 1 1 2 abc 8061e7a2d43Sdan DELETE main t4 1 1 3 abc 8071e7a2d43Sdan} 8081e7a2d43Sdan 8091e7a2d43Sdando_execsql_test 7.6.5 { 8101e7a2d43Sdan DROP TRIGGER a; DROP TRIGGER b; DROP TRIGGER c; 8111e7a2d43Sdan DROP TRIGGER d; DROP TRIGGER e; DROP TRIGGER f; 8121e7a2d43Sdan DROP TRIGGER g; DROP TRIGGER h; DROP TRIGGER i; 8131e7a2d43Sdan 8141e7a2d43Sdan CREATE TRIGGER a BEFORE INSERT ON t1 BEGIN INSERT INTO t2 VALUES(new.x); END; 8151e7a2d43Sdan CREATE TRIGGER b BEFORE INSERT ON t2 BEGIN INSERT INTO t3 VALUES(new.x); END; 8161e7a2d43Sdan CREATE TRIGGER c BEFORE INSERT ON t3 BEGIN INSERT INTO t4 VALUES(new.x); END; 8171e7a2d43Sdan 8181e7a2d43Sdan CREATE TRIGGER d BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET x = new.x; END; 8191e7a2d43Sdan CREATE TRIGGER e BEFORE UPDATE ON t2 BEGIN UPDATE t3 SET x = new.x; END; 8201e7a2d43Sdan CREATE TRIGGER f BEFORE UPDATE ON t3 BEGIN UPDATE t4 SET x = new.x; END; 8211e7a2d43Sdan 8221e7a2d43Sdan CREATE TRIGGER g BEFORE DELETE ON t1 BEGIN DELETE FROM t2 WHERE 1; END; 8231e7a2d43Sdan CREATE TRIGGER h BEFORE DELETE ON t2 BEGIN DELETE FROM t3 WHERE 1; END; 8241e7a2d43Sdan CREATE TRIGGER i BEFORE DELETE ON t3 BEGIN DELETE FROM t4 WHERE 1; END; 8251e7a2d43Sdan} 8261e7a2d43Sdan 8271e7a2d43Sdando_preupdate_test 7.6.6 { 8281e7a2d43Sdan INSERT INTO t1 VALUES('xyz'); 8291e7a2d43Sdan} { 8301e7a2d43Sdan INSERT main t4 1 1 3 xyz 8311e7a2d43Sdan INSERT main t3 1 1 2 xyz 8321e7a2d43Sdan INSERT main t2 1 1 1 xyz 8331e7a2d43Sdan INSERT main t1 1 1 0 xyz 8341e7a2d43Sdan} 8351e7a2d43Sdando_preupdate_test 7.6.3 { 8361e7a2d43Sdan UPDATE t1 SET x = 'abc'; 8371e7a2d43Sdan} { 8381e7a2d43Sdan UPDATE main t4 1 1 3 xyz abc 8391e7a2d43Sdan UPDATE main t3 1 1 2 xyz abc 8401e7a2d43Sdan UPDATE main t2 1 1 1 xyz abc 8411e7a2d43Sdan UPDATE main t1 1 1 0 xyz abc 8421e7a2d43Sdan} 8431e7a2d43Sdando_preupdate_test 7.6.4 { 8441e7a2d43Sdan DELETE FROM t1 WHERE 1; 8451e7a2d43Sdan} { 8461e7a2d43Sdan DELETE main t4 1 1 3 abc 8471e7a2d43Sdan DELETE main t3 1 1 2 abc 8481e7a2d43Sdan DELETE main t2 1 1 1 abc 8491e7a2d43Sdan DELETE main t1 1 1 0 abc 8501e7a2d43Sdan} 851319eeb7bSdan 8521cb3c7c4Sdan# No preupdate callbacks for modifying sqlite_master. 853*37f3ac8fSdanifcapable altertable { 85450133deaSdan do_preupdate_test 8.1 { CREATE TABLE x1(x, y); } { } 85550133deaSdan do_preupdate_test 8.2 { ALTER TABLE x1 ADD COLUMN z } { } 85650133deaSdan do_preupdate_test 8.3 { ALTER TABLE x1 RENAME TO y1 } { } 85750133deaSdan do_preupdate_test 8.4 { CREATE INDEX y1x ON y1(x) } { } 85850133deaSdan do_preupdate_test 8.5 { CREATE VIEW v1 AS SELECT * FROM y1 } { } 85950133deaSdan do_preupdate_test 8.6 { DROP TABLE y1 } { } 860*37f3ac8fSdan} 8611cb3c7c4Sdan 8622a86c196Sdan#------------------------------------------------------------------------- 8632a86c196Sdanreset_db 8642a86c196Sdandb preupdate hook preupdate_hook 865*37f3ac8fSdan 866*37f3ac8fSdanifcapable altertable { 8672a86c196Sdan do_execsql_test 9.0 { 8682a86c196Sdan CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); 8692a86c196Sdan CREATE TABLE t2(a, b INTEGER PRIMARY KEY); 8702a86c196Sdan } 8712a86c196Sdan do_preupdate_test 9.1 { 8722a86c196Sdan INSERT INTO t1 VALUES(456, NULL, NULL); 8732a86c196Sdan } { 8742a86c196Sdan INSERT main t1 456 456 0 456 {} {} 8752a86c196Sdan } 8762a86c196Sdan do_execsql_test 9.2 { 8772a86c196Sdan ALTER TABLE t1 ADD COLUMN d; 8782a86c196Sdan } 8792a86c196Sdan do_preupdate_test 9.3 { 8802a86c196Sdan INSERT INTO t1(a, b, c) VALUES(457, NULL, NULL); 8812a86c196Sdan } { 8822a86c196Sdan INSERT main t1 457 457 0 457 {} {} {} 8832a86c196Sdan } 8842a86c196Sdan do_preupdate_test 9.4 { 8852a86c196Sdan DELETE FROM t1 WHERE a=456 8862a86c196Sdan } { 8872a86c196Sdan DELETE main t1 456 456 0 456 {} {} {} 8882a86c196Sdan } 8892a86c196Sdan do_preupdate_test 9.5 { 8902a86c196Sdan INSERT INTO t2 DEFAULT VALUES; 8912a86c196Sdan } { 8922a86c196Sdan INSERT main t2 1 1 0 {} 1 8932a86c196Sdan } 8942a86c196Sdan do_preupdate_test 9.6 { 8952a86c196Sdan INSERT INTO t1 DEFAULT VALUES; 8962a86c196Sdan } { 8972a86c196Sdan INSERT main t1 458 458 0 458 {} {} {} 8982a86c196Sdan } 899*37f3ac8fSdan} 9002a86c196Sdan 9017271d7a1Sdan 9027271d7a1Sdando_execsql_test 10.0 { 9037271d7a1Sdan CREATE TABLE t3(a, b INTEGER PRIMARY KEY); 9047271d7a1Sdan} 9057271d7a1Sdando_preupdate_test 10.1 { 9067271d7a1Sdan INSERT INTO t3 DEFAULT VALUES 9077271d7a1Sdan} { 9087271d7a1Sdan INSERT main t3 1 1 0 {} 1 9097271d7a1Sdan} 9107271d7a1Sdando_execsql_test 10.2 { SELECT * FROM t3 } {{} 1} 9117271d7a1Sdando_preupdate_test 10.3 { 9127271d7a1Sdan DELETE FROM t3 WHERE b=1 9137271d7a1Sdan} {DELETE main t3 1 1 0 {} 1} 9147271d7a1Sdan 915614efe2bSdan#------------------------------------------------------------------------- 916614efe2bSdan# Test that the "update" hook is not fired for operations on the 917614efe2bSdan# sqlite_stat1 table performed by ANALYZE, even if a pre-update hook is 918614efe2bSdan# registered. 919614efe2bSdanifcapable analyze { 920614efe2bSdan reset_db 921614efe2bSdan do_execsql_test 11.1 { 922614efe2bSdan CREATE TABLE t1(a, b); 923614efe2bSdan CREATE INDEX idx1 ON t1(a); 924614efe2bSdan CREATE INDEX idx2 ON t1(b); 925614efe2bSdan 926614efe2bSdan INSERT INTO t1 VALUES(1, 2); 927614efe2bSdan INSERT INTO t1 VALUES(3, 4); 928614efe2bSdan INSERT INTO t1 VALUES(5, 6); 929614efe2bSdan INSERT INTO t1 VALUES(7, 8); 930614efe2bSdan } 931614efe2bSdan 932614efe2bSdan db preupdate hook preupdate_cb 933614efe2bSdan db update_hook update_cb 934614efe2bSdan 935614efe2bSdan proc preupdate_cb {args} { lappend ::res "preupdate" $args } 936614efe2bSdan proc update_cb {args} { lappend ::res "update" $args } 937614efe2bSdan 938614efe2bSdan set ::res [list] 939614efe2bSdan do_test 11.2 { 940614efe2bSdan execsql ANALYZE 941614efe2bSdan set ::res 942614efe2bSdan } [list {*}{ 943614efe2bSdan preupdate {INSERT main sqlite_stat1 1 1} 944614efe2bSdan preupdate {INSERT main sqlite_stat1 2 2} 945614efe2bSdan }] 946614efe2bSdan 947614efe2bSdan do_execsql_test 11.3 { 948614efe2bSdan INSERT INTO t1 VALUES(9, 10); 949614efe2bSdan INSERT INTO t1 VALUES(11, 12); 950614efe2bSdan INSERT INTO t1 VALUES(13, 14); 951614efe2bSdan INSERT INTO t1 VALUES(15, 16); 952614efe2bSdan } 953614efe2bSdan 954614efe2bSdan set ::res [list] 955614efe2bSdan do_test 11.4 { 956614efe2bSdan execsql ANALYZE 957614efe2bSdan set ::res 958614efe2bSdan } [list {*}{ 959614efe2bSdan preupdate {DELETE main sqlite_stat1 1 1} 960614efe2bSdan preupdate {DELETE main sqlite_stat1 2 2} 961614efe2bSdan preupdate {INSERT main sqlite_stat1 1 1} 962614efe2bSdan preupdate {INSERT main sqlite_stat1 2 2} 963614efe2bSdan }] 964614efe2bSdan} 965614efe2bSdan 966fadc0e34Sdan#------------------------------------------------------------------------- 967fadc0e34Sdan# Test that the pre-update hook is fired for INSERT statements that use 968fadc0e34Sdan# the xfer optimization on without rowid tables. 969fadc0e34Sdan# 970fadc0e34Sdanreset_db 971fadc0e34Sdando_execsql_test 12.1 { 972fadc0e34Sdan CREATE TABLE t1(a INTEGER PRIMARY KEY, b); 973fadc0e34Sdan CREATE TABLE t2(a INTEGER PRIMARY KEY, b) WITHOUT ROWID; 974fadc0e34Sdan 975fadc0e34Sdan INSERT INTO t1 VALUES(1, 2); 976fadc0e34Sdan INSERT INTO t1 VALUES(3, 4); 977fadc0e34Sdan INSERT INTO t2 VALUES(5, 6); 978fadc0e34Sdan INSERT INTO t2 VALUES(7, 8); 979fadc0e34Sdan 980fadc0e34Sdan CREATE TABLE t3 (a INTEGER PRIMARY KEY, b) WITHOUT ROWID; 981fadc0e34Sdan} 982fadc0e34Sdan 983fadc0e34Sdandb preupdate hook preupdate_cb 984fadc0e34Sdandb update_hook update_cb 985fadc0e34Sdan 986fadc0e34Sdanproc preupdate_cb {args} { lappend ::res "preupdate" $args } 987fadc0e34Sdanproc update_cb {args} { lappend ::res "update" $args } 988fadc0e34Sdan 989a55a839aSdanset ::res [list] 990a55a839aSdando_test 12.2 { 991a55a839aSdan execsql VACUUM 992a55a839aSdan set ::res 993a55a839aSdan} {} 994fadc0e34Sdan 995a55a839aSdando_test 12.3 { 996fadc0e34Sdan set ::res [list] 997fadc0e34Sdan execsql { INSERT INTO t3 SELECT a, b FROM t2 } 998fadc0e34Sdan set ::res 999fadc0e34Sdan} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}} 1000fadc0e34Sdan 1001a55a839aSdando_test 12.4 { 1002fadc0e34Sdan execsql { DELETE FROM t3 } 1003fadc0e34Sdan set ::res [list] 1004fadc0e34Sdan execsql { INSERT INTO t3 SELECT * FROM t2 } 1005fadc0e34Sdan set ::res 1006fadc0e34Sdan} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}} 10077271d7a1Sdan 1008a55a839aSdando_execsql_test 12.5 { 1009a55a839aSdan CREATE TABLE t4(a COLLATE nocase PRIMARY KEY, b) WITHOUT ROWID; 1010a55a839aSdan INSERT INTO t4 VALUES('abc', 1); 1011a55a839aSdan INSERT INTO t4 VALUES('DEF', 2); 1012a55a839aSdan} 1013a55a839aSdan 1014a55a839aSdanset ::res [list] 1015a55a839aSdando_test 12.6 { 1016a55a839aSdan execsql VACUUM 1017a55a839aSdan set ::res 1018a55a839aSdan} {} 1019a55a839aSdan 1020a55a839aSdando_catchsql_test 12.6 { 1021a55a839aSdan INSERT INTO t4 VALUES('def', 3); 1022a55a839aSdan} {1 {UNIQUE constraint failed: t4.a}} 1023a55a839aSdan 1024aa940eacSdrhfinish_test 1025