]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental change to invoke the preupdate hook when WITHOUT ROWID tables are
authordan <dan@noemail.net>
Mon, 30 Jan 2017 19:44:53 +0000 (19:44 +0000)
committerdan <dan@noemail.net>
Mon, 30 Jan 2017 19:44:53 +0000 (19:44 +0000)
written.

FossilOrigin-Name: 856f8604c59c8fdd9bfb7d86fc0e212f091ab49a

ext/session/session1.test
ext/session/sessionwor.test [new file with mode: 0644]
manifest
manifest.uuid
src/insert.c
src/vdbe.c
src/vdbeInt.h
src/vdbeapi.c
src/vdbeaux.c
test/hook2.test [new file with mode: 0644]

index 5216824bdda8566dad4ba795a8c6785c105104e6..c96c248c5e86d033431ff4b64b6c6ad4f6511d87 100644 (file)
@@ -20,46 +20,61 @@ ifcapable !session {finish_test; return}
 
 set testprefix session1
 
-do_execsql_test 1.0 {
-  CREATE TABLE t1(x PRIMARY KEY, y);
+# Run all tests in this file twice. Once with "WITHOUT ROWID", and once
+# with regular rowid tables.
+#
+foreach {tn trailing} {
+  1 ""
+  2 " WITHOUT ROWID "
+} {
+eval [string map [list %WR% $trailing] {
+
+db close
+forcedelete test.db test.db2
+reset_db
+
+do_execsql_test $tn.1.0 {
+  CREATE TABLE t1(x PRIMARY KEY, y) %WR%;
   INSERT INTO t1 VALUES('abc', 'def');
 }
 
 #-------------------------------------------------------------------------
 # Test creating, attaching tables to and deleting session objects.
 #
-do_test 1.1 { sqlite3session S db main } {S}
-do_test 1.2 { S delete } {}
-do_test 1.3 { sqlite3session S db main } {S}
-do_test 1.4 { S attach t1 } {}
-do_test 1.5 { S delete } {}
-do_test 1.6 { sqlite3session S db main } {S}
-do_test 1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
-do_test 1.8 { S attach t1 ; S attach t2 ; S attach t3 } {}
-do_test 1.9 { S delete } {}
-do_test 1.10 {
+do_test $tn.1.1 { sqlite3session S db main } {S}
+do_test $tn.1.2 { S delete } {}
+do_test $tn.1.3 { sqlite3session S db main } {S}
+do_test $tn.1.4 { S attach t1 } {}
+do_test $tn.1.5 { S delete } {}
+do_test $tn.1.6 { sqlite3session S db main } {S}
+do_test $tn.1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
+do_test $tn.1.8 { S attach t1 ; S attach t2 ; S attach t3 } {}
+do_test $tn.1.9 { S delete } {}
+do_test $tn.1.10 {
   sqlite3session S db main
   S attach t1
   execsql { INSERT INTO t1 VALUES('ghi', 'jkl') }
 } {}
-do_test 1.11 { S delete } {}
-do_test 1.12 {
-  sqlite3session S db main
-  S attach t1
-  execsql { INSERT INTO t1 VALUES('mno', 'pqr') }
-  execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 }
-  execsql { DELETE FROM t1 WHERE rowid = 2 }
-} {}
-do_test 1.13 {
-  S changeset
-  S delete
-} {}
+do_test $tn.1.11 { S delete } {}
+if {$tn==1} {
+  do_test $tn.1.12 {
+    sqlite3session S db main
+    S attach t1
+    execsql { INSERT INTO t1 VALUES('mno', 'pqr') }
+    execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 }
+    execsql { DELETE FROM t1 WHERE rowid = 2 }
+  } {}
+  do_test $tn.1.13 {
+    S changeset
+    S delete
+  } {}
+}
 
 #-------------------------------------------------------------------------
 # Simple changeset tests. Also test the sqlite3changeset_invert() 
 # function.
 #
-do_test 2.1.1 {
+do_test $tn.2.1.1 {
   execsql { DELETE FROM t1 }
   sqlite3session S db main
   S attach t1
@@ -67,36 +82,36 @@ do_test 2.1.1 {
   execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
   execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
 } {}
-do_changeset_test 2.1.2 S {
+do_changeset_test $tn.2.1.2 S {
   {INSERT t1 0 X. {} {i 1 t Sukhothai}}
   {INSERT t1 0 X. {} {i 2 t Ayutthaya}}
   {INSERT t1 0 X. {} {i 3 t Thonburi}}
 }
-do_changeset_invert_test 2.1.3 S {
+do_changeset_invert_test $tn.2.1.3 S {
   {DELETE t1 0 X. {i 1 t Sukhothai} {}}
   {DELETE t1 0 X. {i 2 t Ayutthaya} {}}
   {DELETE t1 0 X. {i 3 t Thonburi} {}}
 }
-do_test 2.1.4 { S delete } {}
+do_test $tn.2.1.4 { S delete } {}
 
-do_test 2.2.1 {
+do_test $tn.2.2.1 {
   sqlite3session S db main
   S attach t1
   execsql { DELETE FROM t1 WHERE 1 }
 } {}
-do_changeset_test 2.2.2 S {
+do_changeset_test $tn.2.2.2 S {
   {DELETE t1 0 X. {i 1 t Sukhothai} {}}
   {DELETE t1 0 X. {i 2 t Ayutthaya} {}}
   {DELETE t1 0 X. {i 3 t Thonburi} {}}
 }
-do_changeset_invert_test 2.2.3 S {
+do_changeset_invert_test $tn.2.2.3 S {
   {INSERT t1 0 X. {} {i 1 t Sukhothai}}
   {INSERT t1 0 X. {} {i 2 t Ayutthaya}}
   {INSERT t1 0 X. {} {i 3 t Thonburi}}
 }
-do_test 2.2.4 { S delete } {}
+do_test $tn.2.2.4 { S delete } {}
 
-do_test 2.3.1 {
+do_test $tn.2.3.1 {
   execsql { DELETE FROM t1 }
   sqlite3session S db main
   execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
@@ -110,7 +125,7 @@ do_test 2.3.1 {
   }
 } {}
 
-do_changeset_test 2.3.2 S {
+do_changeset_test $tn.2.3.2 S {
   {INSERT t1 0 X. {} {i 10 t Sukhothai}} 
   {DELETE t1 0 X. {i 1 t Sukhothai} {}} 
   {UPDATE t1 0 X. {i 2 t Ayutthaya} {{} {} t Surin}} 
@@ -118,24 +133,24 @@ do_changeset_test 2.3.2 S {
   {INSERT t1 0 X. {} {i 20 t Thapae}} 
 }
 
-do_changeset_invert_test 2.3.3 S {
+do_changeset_invert_test $tn.2.3.3 S {
   {DELETE t1 0 X. {i 10 t Sukhothai} {}} 
   {INSERT t1 0 X. {} {i 1 t Sukhothai}} 
   {UPDATE t1 0 X. {i 2 t Surin} {{} {} t Ayutthaya}} 
   {INSERT t1 0 X. {} {i 3 t Thonburi}} 
   {DELETE t1 0 X. {i 20 t Thapae} {}}
 }
-do_test 2.3.4 { S delete } {}
+do_test $tn.2.3.4 { S delete } {}
 
-do_test 2.4.1 {
+do_test $tn.2.4.1 {
   sqlite3session S db main
   S attach t1
   execsql { INSERT INTO t1 VALUES(100, 'Bangkok') }
   execsql { DELETE FROM t1 WHERE x = 100 }
 } {}
-do_changeset_test 2.4.2 S {}
-do_changeset_invert_test 2.4.3 S {}
-do_test 2.4.4 { S delete } {}
+do_changeset_test $tn.2.4.2 S {}
+do_changeset_invert_test $tn.2.4.3 S {}
+do_test $tn.2.4.4 { S delete } {}
 
 #-------------------------------------------------------------------------
 # Test the application of simple changesets. These tests also test that
@@ -189,16 +204,16 @@ proc do_db2_test {testname sql {result {}}} {
 
 # Test INSERT changesets.
 #
-do_test 3.1.0 {
-  execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) } db2
+do_test $tn.3.1.0 {
+  execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) %WR% } db2
   execsql { 
-    CREATE TABLE t1(a PRIMARY KEY, b);
+    CREATE TABLE t1(a PRIMARY KEY, b) %WR%;
     INSERT INTO t1 VALUES(1, 'one');
     INSERT INTO t1 VALUES(2, 'two');
   } db 
 } {}
-do_db2_test 3.1.1 "INSERT INTO t1 VALUES(6, 'VI')"
-do_conflict_test 3.1.2 -tables t1 -sql {
+do_db2_test $tn.3.1.1 "INSERT INTO t1 VALUES(6, 'VI')"
+do_conflict_test $tn.3.1.2 -tables t1 -sql {
   INSERT INTO t1 VALUES(3, 'three');
   INSERT INTO t1 VALUES(4, 'four');
   INSERT INTO t1 VALUES(5, 'five');
@@ -210,34 +225,34 @@ do_conflict_test 3.1.2 -tables t1 -sql {
   {INSERT t1 CONSTRAINT {i 8 n {}}}
 }
 
-do_db2_test 3.1.3 "SELECT * FROM t1" {
-  6 VI 3 three 4 four 5 five 7 seven
+do_db2_test $tn.3.1.3 "SELECT * FROM t1 ORDER BY a" {
+  3 three 4 four 5 five 6 VI 7 seven
 }
-do_execsql_test 3.1.4 "SELECT * FROM t1" {
+do_execsql_test $tn.3.1.4 "SELECT * FROM t1" {
   1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 {}
 }
 
 # Test DELETE changesets.
 #
-do_execsql_test 3.2.1 {
+do_execsql_test $tn.3.2.1 {
   PRAGMA foreign_keys = on;
-  CREATE TABLE t2(a PRIMARY KEY, b);
+  CREATE TABLE t2(a PRIMARY KEY, b)%WR%;
   CREATE TABLE t3(c, d REFERENCES t2);
   INSERT INTO t2 VALUES(1, 'one');
   INSERT INTO t2 VALUES(2, 'two');
   INSERT INTO t2 VALUES(3, 'three');
   INSERT INTO t2 VALUES(4, 'four');
 }
-do_db2_test 3.2.2 {
+do_db2_test $tn.3.2.2 {
   PRAGMA foreign_keys = on;
-  CREATE TABLE t2(a PRIMARY KEY, b);
+  CREATE TABLE t2(a PRIMARY KEY, b)%WR%;
   CREATE TABLE t3(c, d REFERENCES t2);
   INSERT INTO t2 VALUES(1, 'one');
   INSERT INTO t2 VALUES(2, 'two');
   INSERT INTO t2 VALUES(4, 'five');
   INSERT INTO t3 VALUES('i', 1);
 }
-do_conflict_test 3.2.3 -tables t2 -sql {
+do_conflict_test $tn.3.2.3 -tables t2 -sql {
   DELETE FROM t2 WHERE a = 1;
   DELETE FROM t2 WHERE a = 2;
   DELETE FROM t2 WHERE a = 3;
@@ -247,26 +262,26 @@ do_conflict_test 3.2.3 -tables t2 -sql {
   {DELETE t2 DATA {i 4 t four} {i 4 t five}}
   {FOREIGN_KEY 1}
 }
-do_execsql_test 3.2.4 "SELECT * FROM t2" {}
-do_db2_test     3.2.5 "SELECT * FROM t2" {4 five}
+do_execsql_test $tn.3.2.4 "SELECT * FROM t2" {}
+do_db2_test $tn.3.2.5 "SELECT * FROM t2" {4 five}
 
 # Test UPDATE changesets.
 #
-do_execsql_test 3.3.1 {
-  CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c));
+do_execsql_test $tn.3.3.1 {
+  CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c))%WR%;
   INSERT INTO t4 VALUES(1, 2, 3);
   INSERT INTO t4 VALUES(4, 5, 6);
   INSERT INTO t4 VALUES(7, 8, 9);
   INSERT INTO t4 VALUES(10, 11, 12);
 }
-do_db2_test 3.3.2 {
-  CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c));
+do_db2_test $tn.3.3.2 {
+  CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c))%WR%;
   INSERT INTO t4 VALUES(0, 2, 3);
   INSERT INTO t4 VALUES(4, 5, 7);
   INSERT INTO t4 VALUES(7, 8, 9);
   INSERT INTO t4 VALUES(10, 11, 12);
 }
-do_conflict_test 3.3.3 -tables t4 -sql {
+do_conflict_test $tn.3.3.3 -tables t4 -sql {
   UPDATE t4 SET a = -1 WHERE b = 2;
   UPDATE t4 SET a = -1 WHERE b = 5;
   UPDATE t4 SET a = NULL WHERE c = 9;
@@ -276,8 +291,8 @@ do_conflict_test 3.3.3 -tables t4 -sql {
   {UPDATE t4 NOTFOUND {i 4 i 5 i 6} {i -1 {} {} {} {}}}
   {UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}}
 }
-do_db2_test     3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12}
-do_execsql_test 3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12}
+do_db2_test $tn.3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12}
+do_execsql_test $tn.3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12}
 
 #-------------------------------------------------------------------------
 # This next block of tests verifies that values returned by the conflict
@@ -297,16 +312,16 @@ proc xConflict {args} {
   return $::conflict_return
 }
 
-foreach {tn conflict_return after} {
+foreach {tn2 conflict_return after} {
   1 OMIT      {1 2 value1   4 5 7       10 x x}
   2 REPLACE   {1 2 value1   4 5 value2  10 8 9}
 } {
   test_reset
 
-  do_test 4.$tn.1 {
+  do_test $tn.4.$tn2.1 {
     foreach db {db db2} {
       execsql { 
-        CREATE TABLE t1(a, b, c, PRIMARY KEY(a));
+        CREATE TABLE t1(a, b, c, PRIMARY KEY(a))%WR%;
         INSERT INTO t1 VALUES(1, 2, 3);
         INSERT INTO t1 VALUES(4, 5, 6);
         INSERT INTO t1 VALUES(7, 8, 9);
@@ -318,7 +333,7 @@ foreach {tn conflict_return after} {
     } db2
   } {}
 
-  do_conflict_test 4.$tn.2 -tables t1 -sql {
+  do_conflict_test $tn.4.$tn2.2 -tables t1 -sql {
     UPDATE t1 SET c = 'value1' WHERE a = 1;       -- no conflict
     UPDATE t1 SET c = 'value2' WHERE a = 4;       -- DATA conflict
     UPDATE t1 SET a = 10 WHERE a = 7;             -- CONFLICT conflict
@@ -327,19 +342,19 @@ foreach {tn conflict_return after} {
     {UPDATE t1 DATA {i 4 {} {} i 6} {{} {} {} {} t value2} {i 4 i 5 i 7}}
   }
 
-  do_db2_test 4.$tn.3 "SELECT * FROM t1 ORDER BY a" $after
+  do_db2_test $tn.4.$tn2.3 "SELECT * FROM t1 ORDER BY a" $after
 }
 
-foreach {tn conflict_return} {
+foreach {tn2 conflict_return} {
   1 OMIT
   2 REPLACE
 } {
   test_reset
 
-  do_test 5.$tn.1 {
+  do_test $tn.5.$tn2.1 {
     # Create an identical schema in both databases.
     set schema {
-      CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y));
+      CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y))%WR%;
     }
     execsql $schema db
     execsql $schema db2
@@ -354,7 +369,7 @@ foreach {tn conflict_return} {
 
   } {}
 
-  do_conflict_test 5.$tn.2 -tables {{'foolish name'}} -sql {
+  do_conflict_test $tn.5.$tn2.2 -tables {{'foolish name'}} -sql {
     INSERT INTO "'foolish name'" VALUES('one', 'two', 2);
   } -conflicts {
     {INSERT {'foolish name'} CONFLICT {t one t two i 2} {t one t two t i}}
@@ -362,14 +377,14 @@ foreach {tn conflict_return} {
 
   set res(REPLACE) {one one ii one two 2 two two ii}
   set res(OMIT)    {one one ii one two i two two ii}
-  do_db2_test 5.$tn.3 {
+  do_db2_test $tn.5.$tn2.3 {
     SELECT * FROM "'foolish name'" ORDER BY x, y
   } $res($conflict_return)
 
 
-  do_test 5.$tn.1 {
+  do_test $tn.5.$tn2.1 {
     set schema {
-      CREATE TABLE d1("z""z" PRIMARY KEY, y);
+      CREATE TABLE d1("z""z" PRIMARY KEY, y)%WR%;
       INSERT INTO d1 VALUES(1, 'one');
       INSERT INTO d1 VALUES(2, 'two');
     }
@@ -382,7 +397,7 @@ foreach {tn conflict_return} {
 
   } {}
 
-  do_conflict_test 5.$tn.2 -tables d1 -sql {
+  do_conflict_test $tn.5.$tn2.2 -tables d1 -sql {
     DELETE FROM d1 WHERE "z""z" = 2;
   } -conflicts {
     {DELETE d1 DATA {i 2 t two} {i 2 t TWO}}
@@ -390,7 +405,7 @@ foreach {tn conflict_return} {
 
   set res(REPLACE) {1 one}
   set res(OMIT)    {1 one 2 TWO}
-  do_db2_test 5.$tn.3 "SELECT * FROM d1" $res($conflict_return)
+  do_db2_test $tn.5.$tn2.3 "SELECT * FROM d1" $res($conflict_return)
 }
 
 #-------------------------------------------------------------------------
@@ -398,10 +413,10 @@ foreach {tn conflict_return} {
 #
 test_reset
 set schema {
-  CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b);
-  CREATE TABLE t2(a, b PRIMARY KEY);
+  CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b)%WR%;
+  CREATE TABLE t2(a, b PRIMARY KEY)%WR%;
 }
-do_test 6.0 {
+do_test $tn.6.0 {
   execsql $schema db
   execsql $schema db2
   execsql {
@@ -411,7 +426,7 @@ do_test 6.0 {
 } {}
 
 set conflict_return ""
-do_conflict_test 6.1 -tables {t1 t2} -sql {
+do_conflict_test $tn.6.1 -tables {t1 t2} -sql {
   INSERT INTO t1 VALUES('1', '2');
   INSERT INTO t1 VALUES('A', 'B');
   INSERT INTO t2 VALUES('A', 'B');
@@ -419,8 +434,8 @@ do_conflict_test 6.1 -tables {t1 t2} -sql {
   {INSERT t1 CONFLICT {t A t B} {t a t b}}
 }
 
-do_db2_test 6.2 "SELECT * FROM t1" {a b 1 2}
-do_db2_test 6.3 "SELECT * FROM t2" {a b A B}
+do_db2_test $tn.6.2 "SELECT * FROM t1 ORDER BY a" {1 2 a b}
+do_db2_test $tn.6.3 "SELECT * FROM t2 ORDER BY a" {A B a b}
 
 #-------------------------------------------------------------------------
 # Test that session objects are not confused by changes to table in
@@ -429,10 +444,10 @@ do_db2_test 6.3 "SELECT * FROM t2" {a b A B}
 catch { db2 close }
 drop_all_tables
 forcedelete test.db2
-do_iterator_test 7.1 * {
+do_iterator_test $tn.7.1 * {
   ATTACH 'test.db2' AS aux;
-  CREATE TABLE main.t1(x PRIMARY KEY, y);
-  CREATE TABLE aux.t1(x PRIMARY KEY, y);
+  CREATE TABLE main.t1(x PRIMARY KEY, y)%WR%;
+  CREATE TABLE aux.t1(x PRIMARY KEY, y)%WR%;
 
   INSERT INTO main.t1 VALUES('one', 1);
   INSERT INTO main.t1 VALUES('two', 2);
@@ -446,10 +461,10 @@ do_iterator_test 7.1 * {
 #-------------------------------------------------------------------------
 # Test the sqlite3session_isempty() function.
 #
-do_test 8.1 {
+do_test $tn.8.1 {
   execsql {
-    CREATE TABLE t5(x PRIMARY KEY, y);
-    CREATE TABLE t6(x PRIMARY KEY, y);
+    CREATE TABLE t5(x PRIMARY KEY, y)%WR%;
+    CREATE TABLE t6(x PRIMARY KEY, y)%WR%;
     INSERT INTO t5 VALUES('a', 'b');
     INSERT INTO t6 VALUES('a', 'b');
   }
@@ -458,20 +473,20 @@ do_test 8.1 {
 
   S isempty
 } {1}
-do_test 8.2 {
+do_test $tn.8.2 {
   execsql { DELETE FROM t5 }
   S isempty
 } {0}
-do_test 8.3 {
+do_test $tn.8.3 {
   S delete
   sqlite3session S db main
   S attach t5
   execsql { DELETE FROM t5 }
   S isempty
 } {1}
-do_test 8.4 { S delete } {}
+do_test $tn.8.4 { S delete } {}
 
-do_test 8.5 {
+do_test $tn.8.5 {
   sqlite3session S db main
   S attach t5
   S attach t6
@@ -479,7 +494,7 @@ do_test 8.5 {
   S isempty
 } {0}
 
-do_test 8.6 {
+do_test $tn.8.6 {
   S delete
   sqlite3session S db main
   S attach t5
@@ -487,20 +502,20 @@ do_test 8.6 {
   execsql { INSERT INTO t6 VALUES(1, 2) }
   S isempty
 } {0}
-do_test 8.7 { S delete } {}
+do_test $tn.8.7 { S delete } {}
 
 #-------------------------------------------------------------------------
 #
-do_execsql_test 9.1 {
-  CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g);
+do_execsql_test $tn.9.1 {
+  CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g)%WR%;
   INSERT INTO t7 VALUES(1, 1, 1, 1, 1, 1, 1);
 }
-do_test 9.2 { 
+do_test $tn.9.2 { 
   sqlite3session S db main 
   S attach *
   execsql { UPDATE t7 SET b=2, d=2 }
 } {}
-do_changeset_test 9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}}
+do_changeset_test $tn.9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}}
 S delete
 catch { db2 close }
  
@@ -509,9 +524,9 @@ catch { db2 close }
 #
 reset_db
 set tblname [string repeat tblname123 100]
-do_test 10.1.1 {
+do_test $tn.10.1.1 {
   execsql "
-    CREATE TABLE $tblname (a PRIMARY KEY, b);
+    CREATE TABLE $tblname (a PRIMARY KEY, b)%WR%;
     INSERT INTO $tblname VALUES('xyz', 'def');
   "
   sqlite3session S db main
@@ -522,18 +537,18 @@ do_test 10.1.1 {
   "
 } {}
 breakpoint
-do_changeset_test 10.1.2 S "
+do_changeset_test $tn.10.1.2 S "
   {INSERT $tblname 0 X. {} {t uvw t abc}}
   {DELETE $tblname 0 X. {t xyz t def} {}}
 "
-do_test 10.1.4 { S delete } {}
+do_test $tn.10.1.4 { S delete } {}
 
 #---------------------------------------------------------------
 reset_db
-do_execsql_test 11.1 {
+do_execsql_test $tn.11.1 {
   CREATE TABLE t1(a, b);
 }
-do_test 11.2 {
+do_test $tn.11.2 {
   sqlite3session S db main
   S attach t1
   execsql {
@@ -550,9 +565,9 @@ S delete
 #
 reset_db
 set tblname [string repeat tblname123 100]
-do_test 10.1.1 {
+do_test $tn.10.1.1 {
   execsql "
-    CREATE TABLE $tblname (a PRIMARY KEY, b);
+    CREATE TABLE $tblname (a PRIMARY KEY, b)%WR%;
     INSERT INTO $tblname VALUES('xyz', 'def');
   "
   sqlite3session S db main
@@ -563,35 +578,35 @@ do_test 10.1.1 {
   "
 } {}
 breakpoint
-do_changeset_test 10.1.2 S "
+do_changeset_test $tn.10.1.2 S "
   {INSERT $tblname 0 X. {} {t uvw t abc}}
   {DELETE $tblname 0 X. {t xyz t def} {}}
 "
-do_test 10.1.4 { S delete } {}
+do_test $tn.10.1.4 { S delete } {}
 
 #-------------------------------------------------------------------------
 # Test the effect of updating a column from 0.0 to 0.0.
 #
 reset_db
-do_execsql_test 11.1 {
-  CREATE TABLE t1(a INTEGER PRIMARY KEY, b REAL);
+do_execsql_test $tn.11.1 {
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b REAL)%WR%;
   INSERT INTO t1 VALUES(1, 0.0);
 }
-do_iterator_test 11.2 * {
+do_iterator_test $tn.11.2 * {
   UPDATE t1 SET b = 0.0;
 } {
 }
 
 reset_db
-do_execsql_test 12.1 {
-  CREATE TABLE t1(r INTEGER PRIMARY KEY, a, b);
+do_execsql_test $tn.12.1 {
+  CREATE TABLE t1(r INTEGER PRIMARY KEY, a, b)%WR%;
   CREATE INDEX i1 ON t1(a);
   INSERT INTO t1 VALUES(1, 1, 1);
   INSERT INTO t1 VALUES(2, 1, 2);
   INSERT INTO t1 VALUES(3, 1, 3);
 }
 
-do_iterator_test 12.2 * {
+do_iterator_test $tn.12.2 * {
   UPDATE t1 SET b='one' WHERE a=1;
 } {
   {UPDATE t1 0 X.. {i 1 {} {} i 1} {{} {} {} {} t one}}
@@ -599,6 +614,8 @@ do_iterator_test 12.2 * {
   {UPDATE t1 0 X.. {i 3 {} {} i 3} {{} {} {} {} t one}}
 }
 
+}]
+}
 
 
 finish_test
diff --git a/ext/session/sessionwor.test b/ext/session/sessionwor.test
new file mode 100644 (file)
index 0000000..d906a92
--- /dev/null
@@ -0,0 +1,57 @@
+# 2017 Jan 31
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the session module. Specifically,
+# testing support for WITHOUT ROWID tables.
+#
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+} 
+source [file join [file dirname [info script]] session_common.tcl]
+source $testdir/tester.tcl
+ifcapable !session {finish_test; return}
+
+set testprefix sessionwor
+
+proc test_reset {} {
+  catch { db close }
+  catch { db2 close }
+  forcedelete test.db test.db2
+  sqlite3 db test.db
+  sqlite3 db2 test.db2
+}
+
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT ROWID;
+}
+
+do_iterator_test 1.1 t1 {
+  INSERT INTO t1 VALUES('one', 'two');
+} {
+  {INSERT t1 0 X. {} {t one t two}}
+}
+
+do_iterator_test 1.2 t1 {
+  UPDATE t1 SET b='three'
+} {
+  {UPDATE t1 0 X. {t one t two} {{} {} t three}}
+}
+
+do_iterator_test 1.3 t1 {
+  DELETE FROM t1;
+} {
+  {DELETE t1 0 X. {t one t three} {}}
+}
+
+finish_test
+
index 229623bcc51eb95c8be29000cd9d579178366455..c93ba97d64faf347a3c4e59fd21fc939833e2ba0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sbuilding\swith\sSQLITE_OMIT_FOREIGN_KEY\sdefined.
-D 2017-01-30T11:38:19.468
+C Experimental\schange\sto\sinvoke\sthe\spreupdate\shook\swhen\sWITHOUT\sROWID\stables\sare\nwritten.
+D 2017-01-30T19:44:53.682
 F Makefile.in 5f415e7867296d678fed2e6779aea10c1318b4bc
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
@@ -288,7 +288,7 @@ F ext/rtree/sqlite3rtree.h 9c5777af3d2921c7b4ae4954e8e5697502289d28
 F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
 F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
 F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a
-F ext/session/session1.test 482e9b861169d0b7e283e1563a28c0fa32779fa5
+F ext/session/session1.test c8a50e0e8581dc1a00e832aa59bb61f180404d44
 F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
 F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01
 F ext/session/session4.test 457b02bdc349eb01151e54de014df77abd3c08c8
@@ -306,6 +306,7 @@ F ext/session/sessionG.test 01ef705096a9d3984eebdcca79807a211dee1b60
 F ext/session/session_common.tcl 9b696a341cf1d3744823715ed92bb19749b6c3d4
 F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
 F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
+F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
 F ext/session/sqlite3session.c c61a43396368ec00dc127f7bc647e9bd6a4ee5fb
 F ext/session/sqlite3session.h 9345166bd8f80562145586cf817f707de5ecada2
 F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386
@@ -353,7 +354,7 @@ F src/hash.c 63d0ee752a3b92d4695b2b1f5259c4621b2cfebd
 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c 8183e1778ec42d26732bec8c98aeffa8af13038f
+F src/insert.c 444354c23d4d140a57d6eb46f34e376a7f8f62e8
 F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
 F src/loadext.c a68d8d1d14cf7488bb29dc5311cb1ce9a4404258
 F src/main.c e207b81542d13b9f13d61e78ca441f9781f055b0
@@ -458,11 +459,11 @@ F src/update.c 456d4a4656f8a03c2abc88a51b19172197400e58
 F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
 F src/util.c a88b0466fddf445ce752226d4698ca3faada620a
 F src/vacuum.c 33c174b28886b2faf26e503b5a49a1c01a9b1c16
-F src/vdbe.c c27cc34be1d9169c1c191238025781684bdcd4ec
+F src/vdbe.c 57f41c8c74ceb757abbd4cda72c44fc40d57f425
 F src/vdbe.h 59998ffd71d7caa8886bc78dafaf8caeccd4c13c
-F src/vdbeInt.h 281cb70332dc8b593b8c7afe776f3a2ba7d4255e
-F src/vdbeapi.c 7a65f10684982daecfce50f557f2632b7f20b198
-F src/vdbeaux.c 6847b02aa2db536ed15d90f1fdc2923afef93c5b
+F src/vdbeInt.h 4e4b15b2e1330e1636e4e01974eab2b0b985092f
+F src/vdbeapi.c 3e4a8893feeb78620f4aac4ac5b85d92255b97e1
+F src/vdbeaux.c b9a36e530e6525ca9d9a685bc7b1d01fa77b5cf8
 F src/vdbeblob.c 2b3d1ad915dbe5dc92c48759dc18fa8c697e78e5
 F src/vdbemem.c 3b5a9a5b375458d3e12a50ae1aaa41eeec2175fd
 F src/vdbesort.c eda25cb2d1727efca6f7862fea32b8aa33c0face
@@ -834,6 +835,7 @@ F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
 F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
 F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
 F test/hook.test dbc0b87756e1e20e7497b56889c9e9cd2f8cc2b5
+F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
 F test/icu.test 73956798bace8982909c00476b216714a6d0559a
 F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607
 F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
@@ -1548,7 +1550,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 4fe879d4b5da6ae0688a7a99004683a234966597
-R c0ed5c24fdff676195cd77401e89700b
+P e93d2c49a44af994ff10cc9cc7eafacd5a4f73ab
+R f7cc20c31f0badea537c676e1225b282
+T *branch * preupdate-without-rowid
+T *sym-preupdate-without-rowid *
+T -sym-trunk *
 U dan
-Z 6d1b138d17ccacee0c622f2ed331ca89
+Z 812bce2fe1496eddb1cec5705157fb44
index eec33fc0a4a047270b6fe43386b3f4d6db4c1687..67552c167292466bb224388e4be244c9be4b98e1 100644 (file)
@@ -1 +1 @@
-e93d2c49a44af994ff10cc9cc7eafacd5a4f73ab
\ No newline at end of file
+856f8604c59c8fdd9bfb7d86fc0e212f091ab49a
\ No newline at end of file
index 5370ef77edb02dc9176ece6a3bf5a0cb2459326d..894bfc2cc17b3e81b5629e822460288e771729f7 100644 (file)
@@ -1730,16 +1730,23 @@ void sqlite3CompleteInsertion(
       sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
       VdbeCoverage(v);
     }
-    sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
-                         aRegIdx[i]+1,
-                         pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn);
-    pik_flags = 0;
-    if( useSeekResult ) pik_flags = OPFLAG_USESEEKRESULT;
+    pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0);
     if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
       assert( pParse->nested==0 );
       pik_flags |= OPFLAG_NCHANGE;
       pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+      if( update_flags==0 ){
+        sqlite3VdbeAddOp4(v, OP_InsertInt, 
+            iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE
+        );
+        sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
+      }
+#endif
     }
+    sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
+                         aRegIdx[i]+1,
+                         pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn);
     sqlite3VdbeChangeP5(v, pik_flags);
   }
   if( !HasRowid(pTab) ) return;
index ded44623981e9fa68a2129112335d80e963e1709..5462e541c5a52a740cf653185cc70f7af97d214d 100644 (file)
@@ -4384,7 +4384,7 @@ case OP_InsertInt: {
   assert( pC!=0 );
   assert( pC->eCurType==CURTYPE_BTREE );
   assert( pC->uc.pCursor!=0 );
-  assert( pC->isTable );
+  assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable );
   assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
   REGISTER_TRACE(pOp->p2, pData);
 
@@ -4400,11 +4400,10 @@ case OP_InsertInt: {
   }
 
   if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
-    assert( pC->isTable );
     assert( pC->iDb>=0 );
     zDb = db->aDb[pC->iDb].zDbSName;
     pTab = pOp->p4.pTab;
-    assert( HasRowid(pTab) );
+    assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) );
     op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
   }else{
     pTab = 0; /* Not needed.  Silence a comiler warning. */
@@ -4419,6 +4418,7 @@ case OP_InsertInt: {
   ){
     sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey, pOp->p2);
   }
+  if( pOp->p5 & OPFLAG_ISNOOP ) break;
 #endif
 
   if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
@@ -4531,8 +4531,11 @@ case OP_Delete: {
 
 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
   /* Invoke the pre-update-hook if required. */
-  if( db->xPreUpdateCallback && pOp->p4.pTab && HasRowid(pTab) ){
-    assert( !(opflags & OPFLAG_ISUPDATE) || (aMem[pOp->p3].flags & MEM_Int) );
+  if( db->xPreUpdateCallback && pOp->p4.pTab ){
+    assert( !(opflags & OPFLAG_ISUPDATE) 
+         || HasRowid(pTab)==0 
+         || (aMem[pOp->p3].flags & MEM_Int) 
+    );
     sqlite3VdbePreUpdateHook(p, pC,
         (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE, 
         zDb, pTab, pC->movetoTarget,
index 3e6bb068890efa9cc33f0aec19357cf8b7ea5df9..989cdfd34617c9a19ec738c7cc1b7f1ad9030edc 100644 (file)
@@ -434,6 +434,7 @@ struct PreUpdate {
   i64 iKey2;                      /* Second key value passed to hook */
   Mem *aNew;                      /* Array of new.* values */
   Table *pTab;                    /* Schema object being upated */          
+  Index *pPk;                     /* PK index if pTab is WITHOUT ROWID */
 };
 
 /*
index 7ecdac87c3f745c2121ed959796695d1f961a4bd..6eb97f1d1ddb0ce90158d866d0a5dd9cdd3e7610 100644 (file)
@@ -1669,6 +1669,9 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
     rc = SQLITE_MISUSE_BKPT;
     goto preupdate_old_out;
   }
+  if( p->pPk ){
+    iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx);
+  }
   if( iIdx>=p->pCsr->nField || iIdx<0 ){
     rc = SQLITE_RANGE;
     goto preupdate_old_out;
@@ -1754,6 +1757,9 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
     rc = SQLITE_MISUSE_BKPT;
     goto preupdate_new_out;
   }
+  if( p->pPk && p->op!=SQLITE_UPDATE ){
+    iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx);
+  }
   if( iIdx>=p->pCsr->nField || iIdx<0 ){
     rc = SQLITE_RANGE;
     goto preupdate_new_out;
index 23c31d4029d47ca8caadb29356d86ead27df966b..68f6a5acc84196d391e7c6ac0e75200c542d995d 100644 (file)
@@ -4620,10 +4620,15 @@ void sqlite3VdbePreUpdateHook(
 
   assert( db->pPreUpdate==0 );
   memset(&preupdate, 0, sizeof(PreUpdate));
-  if( op==SQLITE_UPDATE ){
-    iKey2 = v->aMem[iReg].u.i;
+  if( HasRowid(pTab)==0 ){
+    iKey1 = iKey2 = 0;
+    preupdate.pPk = sqlite3PrimaryKeyIndex(pTab);
   }else{
-    iKey2 = iKey1;
+    if( op==SQLITE_UPDATE ){
+      iKey2 = v->aMem[iReg].u.i;
+    }else{
+      iKey2 = iKey1;
+    }
   }
 
   assert( pCsr->nField==pTab->nCol 
diff --git a/test/hook2.test b/test/hook2.test
new file mode 100644 (file)
index 0000000..9ae1103
--- /dev/null
@@ -0,0 +1,218 @@
+# 2017 Jan 30
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# The tests in this file focus on the pre-update hook.
+# 
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix hook2
+
+ifcapable !preupdate {
+  finish_test
+  return
+}
+
+#-------------------------------------------------------------------------
+proc do_preupdate_test {tn sql x} {
+  set X [list]
+  foreach elem $x {lappend X $elem}
+  uplevel do_test $tn [list "
+    set ::preupdate \[list\]
+    execsql { $sql }
+    set ::preupdate
+  "] [list $X]
+}
+
+proc preupdate_hook {args} {
+  set type [lindex $args 0]
+  eval lappend ::preupdate $args
+  if {$type != "INSERT"} {
+    for {set i 0} {$i < [db preupdate count]} {incr i} {
+      lappend ::preupdate [db preupdate old $i]
+    }
+  }
+  if {$type != "DELETE"} {
+    for {set i 0} {$i < [db preupdate count]} {incr i} {
+      set rc [catch { db preupdate new $i } v]
+      lappend ::preupdate $v
+    }
+  }
+}
+
+#-------------------------------------------------------------------------
+# Simple tests - INSERT, UPDATE and DELETE on a WITHOUT ROWID table.
+#
+db preupdate hook preupdate_hook
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT ROWID;
+}
+do_preupdate_test 1.1 {
+  INSERT INTO t1 VALUES('one', 1);
+} {
+  INSERT main t1 0 0  one 1
+}
+do_preupdate_test 1.2 {
+  UPDATE t1 SET b=2 WHERE a='one';
+} {
+  UPDATE main t1 0 0  one 1 one 2
+}
+do_preupdate_test 1.3 {
+  DELETE FROM t1 WHERE a='one';
+} {
+  DELETE main t1 0 0  one 2
+}
+
+#-------------------------------------------------------------------------
+# Some more complex tests for the pre-update callback on WITHOUT ROWID
+# tables.
+#
+#   2.1.1 - INSERT statement.
+#   2.1.2 - INSERT INTO ... SELECT statement.
+#   2.1.3 - REPLACE INTO ... (PK conflict)
+#   2.1.4 - REPLACE INTO ... (other index conflicts)
+#   2.1.5 - REPLACE INTO ... (both PK and other index conflicts)
+#
+#   2.2.1 - DELETE statement.
+#   2.2.2 - DELETE statement that uses the truncate optimization.
+#
+#   2.3.1 - UPDATE statement.
+#   2.3.2 - UPDATE statement that modifies the PK.
+#   2.3.3 - UPDATE OR REPLACE ... (PK conflict).
+#   2.3.4 - UPDATE OR REPLACE ... (other index conflicts)
+#   2.3.4 - UPDATE OR REPLACE ... (both PK and other index conflicts)
+#
+do_execsql_test 2.0 {
+  CREATE TABLE t2(a DEFAULT 4, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID;
+  CREATE UNIQUE INDEX t2a ON t2(a);
+}
+
+do_preupdate_test 2.1.1 {
+  INSERT INTO t2(b, c) VALUES(1, 1);
+} {
+  INSERT main t2 0 0  4 1 1
+}
+
+do_execsql_test 2.1.2.0 {
+  CREATE TABLE d1(a DEFAULT 4, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID;
+  CREATE UNIQUE INDEX d1a ON d1(a);
+  INSERT INTO d1 VALUES(1, 2, 3);
+  INSERT INTO d1 VALUES(11, 12, 13);
+}
+do_preupdate_test 2.1.2.1 {
+  INSERT INTO t2 SELECT * FROM d1;
+} {
+  INSERT main t2 0 0  1 2 3
+  INSERT main t2 0 0  11 12 13
+}
+do_preupdate_test 2.1.2.2 {
+  INSERT INTO t2 SELECT a+20, b+20, c+20 FROM d1;
+} {
+  INSERT main t2 0 0  21 22 23
+  INSERT main t2 0 0  31 32 33
+}
+do_execsql_test 2.1.2.3 {
+  SELECT * FROM t2 ORDER BY b, c;
+} {
+  4 1 1
+  1 2 3
+  11 12 13
+  21 22 23
+  31 32 33
+}
+do_preupdate_test 2.1.3 {
+  REPLACE INTO t2 VALUES(45, 22, 23);
+} {
+  DELETE main t2 0 0 21 22 23
+  INSERT main t2 0 0 45 22 23
+}
+do_preupdate_test 2.1.4 {
+  REPLACE INTO t2 VALUES(11, 100, 100);
+} {
+  DELETE main t2 0 0 11 12 13
+  INSERT main t2 0 0 11 100 100
+}
+do_preupdate_test 2.1.5 {
+  REPLACE INTO t2(c, b) VALUES(33, 32)
+} {
+  DELETE main t2 0 0 4 1 1 
+  DELETE main t2 0 0 31 32 33
+  INSERT main t2 0 0 4 32 33
+}
+
+do_execsql_test 2.2.0 {
+  SELECT * FROM t2 ORDER BY b,c;
+} {
+  1    2   3 
+  45  22  23 
+  4   32  33 
+  11 100 100
+}
+do_preupdate_test 2.2.1 {
+  DELETE FROM t2 WHERE b=22;
+} {
+  DELETE main t2 0 0  45 22 23
+}
+do_preupdate_test 2.2.2 {
+  DELETE FROM t2;
+} {
+  DELETE main t2 0 0 1 2 3 
+  DELETE main t2 0 0 4 32 33 
+  DELETE main t2 0 0 11 100 100
+}
+
+do_execsql_test 2.3.0 {
+  CREATE TABLE t3(x, y PRIMARY KEY, z UNIQUE) WITHOUT ROWID;
+  INSERT INTO t3 VALUES('a', 'b', 'c');
+  INSERT INTO t3 VALUES('d', 'e', 'f');
+
+  INSERT INTO t3 VALUES(1, 1, 1);
+  INSERT INTO t3 VALUES(2, 2, 2);
+  INSERT INTO t3 VALUES(3, 3, 3);
+}
+
+do_preupdate_test 2.3.1 {
+  UPDATE t3 SET x=4 WHERE y IN ('b', 'e', 'x');
+} {
+  UPDATE main t3 0 0  a b c   4 b c
+  UPDATE main t3 0 0  d e f   4 e f
+}
+
+do_preupdate_test 2.3.2 {
+  UPDATE t3 SET y=y||y WHERE z IN('c', 'f');
+} {
+  UPDATE main t3 0 0  4 b c   4 bb c
+  UPDATE main t3 0 0  4 e f   4 ee f
+}
+
+do_preupdate_test 2.3.3 {
+  UPDATE OR REPLACE t3 SET y='bb' WHERE z='f'
+} {
+  DELETE main t3 0 0  4 bb c
+  UPDATE main t3 0 0  4 ee f   4 bb f
+}
+
+do_preupdate_test 2.3.4 {
+  UPDATE OR REPLACE t3 SET z=2 WHERE y=1;
+} {
+  DELETE main t3 0 0  2 2 2
+  UPDATE main t3 0 0  1 1 1  1 1 2
+}
+
+do_preupdate_test 2.3.5 {
+  UPDATE OR REPLACE t3 SET z=2, y='bb' WHERE y=3;
+} {
+  DELETE main t3 0 0  1 1 2
+  DELETE main t3 0 0  4 bb f
+  UPDATE main t3 0 0  3 3 3  3 bb 2
+}
+  
+
+finish_test