sl@0: # The author disclaims copyright to this source code. In place of sl@0: # a legal notice, here is a blessing: sl@0: # sl@0: # May you do good and not evil. sl@0: # May you find forgiveness for yourself and forgive others. sl@0: # May you share freely, never taking more than you give. sl@0: # sl@0: #*********************************************************************** sl@0: # sl@0: # Tests to make sure that values returned by changes() and total_changes() sl@0: # are updated properly, especially inside triggers sl@0: # sl@0: # Note 1: changes() remains constant within a statement and only updates sl@0: # once the statement is finished (triggers count as part of sl@0: # statement). sl@0: # Note 2: changes() is changed within the context of a trigger much like sl@0: # last_insert_rowid() (see lastinsert.test), but is restored once sl@0: # the trigger exits. sl@0: # Note 3: changes() is not changed by a change to a view (since everything sl@0: # is done within instead of trigger context). sl@0: # sl@0: sl@0: set testdir [file dirname $argv0] sl@0: source $testdir/tester.tcl sl@0: sl@0: # ---------------------------------------------------------------------------- sl@0: # 1.x - basic tests (no triggers) sl@0: sl@0: # changes() set properly after insert sl@0: do_test laststmtchanges-1.1 { sl@0: catchsql { sl@0: create table t0 (x); sl@0: insert into t0 values (1); sl@0: insert into t0 values (1); sl@0: insert into t0 values (2); sl@0: insert into t0 values (2); sl@0: insert into t0 values (1); sl@0: insert into t0 values (1); sl@0: insert into t0 values (1); sl@0: insert into t0 values (2); sl@0: select changes(), total_changes(); sl@0: } sl@0: } {0 {1 8}} sl@0: sl@0: # changes() set properly after update sl@0: do_test laststmtchanges-1.2 { sl@0: catchsql { sl@0: update t0 set x=3 where x=1; sl@0: select changes(), total_changes(); sl@0: } sl@0: } {0 {5 13}} sl@0: sl@0: # There was some goofy change-counting logic in sqlite3_exec() that sl@0: # appears to have been left over from SQLite version 2. This test sl@0: # makes sure it has been removed. sl@0: # sl@0: do_test laststmtchanges-1.2.1 { sl@0: db cache flush sl@0: sqlite3_exec_printf db {update t0 set x=4 where x=3; select 1;} {} sl@0: execsql {select changes()} sl@0: } {5} sl@0: sl@0: # changes() unchanged within an update statement sl@0: do_test laststmtchanges-1.3 { sl@0: execsql {update t0 set x=3 where x=4} sl@0: catchsql { sl@0: update t0 set x=x+changes() where x=3; sl@0: select count() from t0 where x=8; sl@0: } sl@0: } {0 5} sl@0: sl@0: # changes() set properly after update on table where no rows changed sl@0: do_test laststmtchanges-1.4 { sl@0: catchsql { sl@0: update t0 set x=77 where x=88; sl@0: select changes(); sl@0: } sl@0: } {0 0} sl@0: sl@0: # changes() set properly after delete from table sl@0: do_test laststmtchanges-1.5 { sl@0: catchsql { sl@0: delete from t0 where x=2; sl@0: select changes(); sl@0: } sl@0: } {0 3} sl@0: sl@0: # All remaining tests involve triggers. Skip them if triggers are not sl@0: # supported in this build. sl@0: # sl@0: ifcapable {!trigger} { sl@0: finish_test sl@0: return sl@0: } sl@0: sl@0: sl@0: # ---------------------------------------------------------------------------- sl@0: # 2.x - tests with after insert trigger sl@0: sl@0: # changes() changed properly after insert into table containing after trigger sl@0: do_test laststmtchanges-2.1 { sl@0: set ::tc [db total_changes] sl@0: catchsql { sl@0: create table t1 (k integer primary key); sl@0: create table t2 (k integer primary key, v1, v2); sl@0: create trigger r1 after insert on t1 for each row begin sl@0: insert into t2 values (NULL, changes(), NULL); sl@0: update t0 set x=x; sl@0: update t2 set v2=changes(); sl@0: end; sl@0: insert into t1 values (77); sl@0: select changes(); sl@0: } sl@0: } {0 1} sl@0: sl@0: # changes() unchanged upon entry into after insert trigger sl@0: do_test laststmtchanges-2.2 { sl@0: catchsql { sl@0: select v1 from t2; sl@0: } sl@0: } {0 3} sl@0: sl@0: # changes() changed properly by update within context of after insert trigger sl@0: do_test laststmtchanges-2.3 { sl@0: catchsql { sl@0: select v2 from t2; sl@0: } sl@0: } {0 5} sl@0: sl@0: # Total changes caused by firing the trigger above: sl@0: # sl@0: # 1 from "insert into t1 values(77)" + sl@0: # 1 from "insert into t2 values (NULL, changes(), NULL);" + sl@0: # 5 from "update t0 set x=x;" + sl@0: # 1 from "update t2 set v2=changes();" sl@0: # sl@0: do_test laststmtchanges-2.4 { sl@0: expr [db total_changes] - $::tc sl@0: } {8} sl@0: sl@0: # ---------------------------------------------------------------------------- sl@0: # 3.x - tests with after update trigger sl@0: sl@0: # changes() changed properly after update into table containing after trigger sl@0: do_test laststmtchanges-3.1 { sl@0: catchsql { sl@0: drop trigger r1; sl@0: delete from t2; delete from t2; sl@0: create trigger r1 after update on t1 for each row begin sl@0: insert into t2 values (NULL, changes(), NULL); sl@0: delete from t0 where oid=1 or oid=2; sl@0: update t2 set v2=changes(); sl@0: end; sl@0: update t1 set k=k; sl@0: select changes(); sl@0: } sl@0: } {0 1} sl@0: sl@0: # changes() unchanged upon entry into after update trigger sl@0: do_test laststmtchanges-3.2 { sl@0: catchsql { sl@0: select v1 from t2; sl@0: } sl@0: } {0 0} sl@0: sl@0: # changes() changed properly by delete within context of after update trigger sl@0: do_test laststmtchanges-3.3 { sl@0: catchsql { sl@0: select v2 from t2; sl@0: } sl@0: } {0 2} sl@0: sl@0: # ---------------------------------------------------------------------------- sl@0: # 4.x - tests with before delete trigger sl@0: sl@0: # changes() changed properly on delete from table containing before trigger sl@0: do_test laststmtchanges-4.1 { sl@0: catchsql { sl@0: drop trigger r1; sl@0: delete from t2; delete from t2; sl@0: create trigger r1 before delete on t1 for each row begin sl@0: insert into t2 values (NULL, changes(), NULL); sl@0: insert into t0 values (5); sl@0: update t2 set v2=changes(); sl@0: end; sl@0: delete from t1; sl@0: select changes(); sl@0: } sl@0: } {0 1} sl@0: sl@0: # changes() unchanged upon entry into before delete trigger sl@0: do_test laststmtchanges-4.2 { sl@0: catchsql { sl@0: select v1 from t2; sl@0: } sl@0: } {0 0} sl@0: sl@0: # changes() changed properly by insert within context of before delete trigger sl@0: do_test laststmtchanges-4.3 { sl@0: catchsql { sl@0: select v2 from t2; sl@0: } sl@0: } {0 1} sl@0: sl@0: # ---------------------------------------------------------------------------- sl@0: # 5.x - complex tests with temporary tables and nested instead of triggers sl@0: # These tests cannot run if the library does not have view support enabled. sl@0: sl@0: ifcapable view&&tempdb { sl@0: sl@0: do_test laststmtchanges-5.1 { sl@0: catchsql { sl@0: drop table t0; drop table t1; drop table t2; sl@0: create temp table t0(x); sl@0: create temp table t1 (k integer primary key); sl@0: create temp table t2 (k integer primary key); sl@0: create temp view v1 as select * from t1; sl@0: create temp view v2 as select * from t2; sl@0: create temp table n1 (k integer primary key, n); sl@0: create temp table n2 (k integer primary key, n); sl@0: insert into t0 values (1); sl@0: insert into t0 values (2); sl@0: insert into t0 values (1); sl@0: insert into t0 values (1); sl@0: insert into t0 values (1); sl@0: insert into t0 values (2); sl@0: insert into t0 values (2); sl@0: insert into t0 values (1); sl@0: create temp trigger r1 instead of insert on v1 for each row begin sl@0: insert into n1 values (NULL, changes()); sl@0: update t0 set x=x*10 where x=1; sl@0: insert into n1 values (NULL, changes()); sl@0: insert into t1 values (NEW.k); sl@0: insert into n1 values (NULL, changes()); sl@0: update t0 set x=x*10 where x=0; sl@0: insert into v2 values (100+NEW.k); sl@0: insert into n1 values (NULL, changes()); sl@0: end; sl@0: create temp trigger r2 instead of insert on v2 for each row begin sl@0: insert into n2 values (NULL, changes()); sl@0: insert into t2 values (1000+NEW.k); sl@0: insert into n2 values (NULL, changes()); sl@0: update t0 set x=x*100 where x=0; sl@0: insert into n2 values (NULL, changes()); sl@0: delete from t0 where x=2; sl@0: insert into n2 values (NULL, changes()); sl@0: end; sl@0: insert into t1 values (77); sl@0: select changes(); sl@0: } sl@0: } {0 1} sl@0: sl@0: do_test laststmtchanges-5.2 { sl@0: catchsql { sl@0: delete from t1 where k=88; sl@0: select changes(); sl@0: } sl@0: } {0 0} sl@0: sl@0: do_test laststmtchanges-5.3 { sl@0: catchsql { sl@0: insert into v1 values (5); sl@0: select changes(); sl@0: } sl@0: } {0 0} sl@0: sl@0: do_test laststmtchanges-5.4 { sl@0: catchsql { sl@0: select n from n1; sl@0: } sl@0: } {0 {0 5 1 0}} sl@0: sl@0: do_test laststmtchanges-5.5 { sl@0: catchsql { sl@0: select n from n2; sl@0: } sl@0: } {0 {0 1 0 3}} sl@0: sl@0: } ;# ifcapable view sl@0: sl@0: finish_test