xref: /sqlite-3.40.0/test/hook.test (revision 37f3ac8f)
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