]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add another test case file for WITHOUT ROWID and fix the bugs that the new
authordrh <drh@noemail.net>
Mon, 4 Nov 2013 18:34:46 +0000 (18:34 +0000)
committerdrh <drh@noemail.net>
Mon, 4 Nov 2013 18:34:46 +0000 (18:34 +0000)
test file uncovered.

FossilOrigin-Name: bc2a06eb8e57573d08e77800a7937eee5af3f035

manifest
manifest.uuid
src/build.c
src/update.c
test/without_rowid4.test [new file with mode: 0644]

index 13f441bf85016e88ddd48d0b2a4612bdb082f88c..ad2634b5b1bbe242c10ca1dc1c7d653d05490662 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sproblem\swith\sprocessing\sINTEGER\sPRIMARY\sKEY\son\sa\sWITHOUT\sROWID\stable.
-D 2013-11-04T17:00:50.157
+C Add\sanother\stest\scase\sfile\sfor\sWITHOUT\sROWID\sand\sfix\sthe\sbugs\sthat\sthe\snew\s\ntest\sfile\suncovered.
+D 2013-11-04T18:34:46.599
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -168,7 +168,7 @@ F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
 F src/btree.c 509722ce305471b626d3401c0631a808fd33237b
 F src/btree.h bfe0e8c5759b4ec77b0d18390064a6ef3cdffaaf
 F src/btreeInt.h f038e818bfadf75afbd09819ed93c26a333d39e0
-F src/build.c 0610712b057ea6dedc6486b50337804e28171238
+F src/build.c 10b7e687299ffeafd52d14f8844bf3385a0ca338
 F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2
 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
 F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c
@@ -275,7 +275,7 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c ec4c1a62b890bf1dbcdb966399e140b904c700a4
 F src/trigger.c 53d6b5d50b3b23d4fcd0a36504feb5cff9aed716
-F src/update.c 94d63d3e06b09df3618655a841dc95d5b9466dc6
+F src/update.c a804e71818927510d351e4ed14d29f75416a7a91
 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
 F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918
 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
@@ -1079,6 +1079,7 @@ F test/win32longpath.test e2aafc07e6990fe86c69be22a3d1a0e210cd329b
 F test/without_rowid1.test de6f9e6ea36a7e4b087e44084abed3c0456f7dfe
 F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
 F test/without_rowid3.test 8ad27697fbe319d0e858b790c0ec53abf4f8617b
+F test/without_rowid4.test 3c6ee0ab3d49944cb4be1b59361b693266f53083
 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
 F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
 F tool/build-all-msvc.bat 1bac6adc3fdb4d9204f21d17b14be25778370e48 x
@@ -1130,7 +1131,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P d072bcd0a8692d590c13c2bf458454c10c12a3e2
-R c7d9ca7d9f7c49d0a3951ad46262529a
+P 89098e6d18dacd1554cf4471b5f035db85d1f327
+R 4fbdd86f5ad03355f6f11feb2ddb3d95
 U drh
-Z 9724dea81d81466a0359ccb3798ff5e3
+Z 1acf97ed6e77633885d90ac086f2b0ff
index 96b196086930fd9577c6b1cb57d1cfd8974c2eb3..479d71aef009a5d6a91c1fd19dcbb72f5abd1e8a 100644 (file)
@@ -1 +1 @@
-89098e6d18dacd1554cf4471b5f035db85d1f327
\ No newline at end of file
+bc2a06eb8e57573d08e77800a7937eee5af3f035
\ No newline at end of file
index 0b5e349dc62a5883ed1ce463f62d7078b009f891..41e738cac984ee4b2276bc4bbcd943948bfb8f8c 100644 (file)
@@ -1709,6 +1709,8 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
     }
     assert( pPk->nColumn==j );
     assert( pTab->nCol==j );
+  }else{
+    pPk->nColumn = pTab->nCol;
   }
 }
 
index a631101698f7a920daf40fd31fb8c8157ed82725..82975c6504c70b49968cdec58272ff14e38f1d4f 100644 (file)
@@ -95,7 +95,7 @@ void sqlite3Update(
 ){
   int i, j;              /* Loop counters */
   Table *pTab;           /* The table to be updated */
-  int addr = 0;          /* VDBE instruction address of the start of the loop */
+  int addrTop = 0;       /* VDBE instruction address of the start of the loop */
   WhereInfo *pWInfo;     /* Information about the WHERE clause */
   Vdbe *v;               /* The virtual database engine */
   Index *pIdx;           /* For looping over indices */
@@ -419,19 +419,18 @@ void sqlite3Update(
 
   /* Top of the update loop */
   labelBreak = sqlite3VdbeMakeLabel(v);
-  labelContinue = sqlite3VdbeMakeLabel(v);
   if( pPk ){
+    labelContinue = sqlite3VdbeMakeLabel(v);
     sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak);
-    addr = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey);
+    addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey);
     sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
   }else if( okOnePass ){
-    int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
-    addr = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelBreak);
-    sqlite3VdbeJumpHere(v, a1);
+    labelContinue = labelBreak;
+    sqlite3VdbeAddOp2(v, OP_IsNull, regOldRowid, labelBreak);
   }else{
-    addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
+    labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
                              regOldRowid);
-    sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addr, regOldRowid);
+    sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
   }
 
   /* If the record number will change, set register regNewRowid to
@@ -510,7 +509,7 @@ void sqlite3Update(
     sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol);
     sqlite3TableAffinityStr(v, pTab);
     sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, 
-        TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);
+        TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
 
     /* The row-trigger may have deleted the row being updated. In this
     ** case, jump to the next row. No updates or AFTER triggers are 
@@ -521,7 +520,7 @@ void sqlite3Update(
     if( pPk ){
       sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
     }else{
-      sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addr, regOldRowid);
+      sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
     }
 
     /* If it did not delete it, the row-trigger may still have modified 
@@ -542,7 +541,7 @@ void sqlite3Update(
     /* Do constraint checks. */
     assert( regOldRowid>0 );
     sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
-        regNewRowid, regOldRowid, chngKey, onError, addr, 0);
+        regNewRowid, regOldRowid, chngKey, onError, labelContinue, 0);
 
     /* Do FK constraint checks. */
     if( hasFK ){
@@ -590,16 +589,16 @@ void sqlite3Update(
   }
 
   sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, 
-      TRIGGER_AFTER, pTab, regOldRowid, onError, addr);
+      TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue);
 
   /* Repeat the above with the next record to be updated, until
   ** all record selected by the WHERE clause have been updated.
   */
   if( pPk ){
     sqlite3VdbeResolveLabel(v, labelContinue);
-    sqlite3VdbeAddOp2(v, OP_Next, iEph, addr);
+    sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop);
   }else if( !okOnePass ){
-    sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
+    sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue);
   }
   sqlite3VdbeResolveLabel(v, labelBreak);
 
diff --git a/test/without_rowid4.test b/test/without_rowid4.test
new file mode 100644 (file)
index 0000000..6c8fdb4
--- /dev/null
@@ -0,0 +1,764 @@
+# 2013-11-04
+#
+# 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.
+#
+#***********************************************************************
+#
+# Regression testing of FOR EACH ROW table triggers on WITHOUT ROWID
+# tables.
+#
+# 1. Trigger execution order tests. 
+# These tests ensure that BEFORE and AFTER triggers are fired at the correct
+# times relative to each other and the triggering statement. 
+#
+# without_rowid4-1.1.*: ON UPDATE trigger execution model.
+# without_rowid4-1.2.*: DELETE trigger execution model.
+# without_rowid4-1.3.*: INSERT trigger execution model.
+#
+# 2. Trigger program execution tests.
+# These tests ensure that trigger programs execute correctly (ie. that a
+# trigger program can correctly execute INSERT, UPDATE, DELETE * SELECT
+# statements, and combinations thereof).
+#
+# 3. Selective trigger execution 
+# This tests that conditional triggers (ie. UPDATE OF triggers and triggers
+# with WHEN clauses) are fired only fired when they are supposed to be.
+#
+# without_rowid4-3.1: UPDATE OF triggers
+# without_rowid4-3.2: WHEN clause
+#
+# 4. Cascaded trigger execution 
+# Tests that trigger-programs may cause other triggers to fire. Also that a 
+# trigger-program is never executed recursively.
+# 
+# without_rowid4-4.1: Trivial cascading trigger
+# without_rowid4-4.2: Trivial recursive trigger handling 
+#
+# 5. Count changes behaviour.
+# Verify that rows altered by triggers are not included in the return value
+# of the "count changes" interface.
+#
+# 6. ON CONFLICT clause handling
+# without_rowid4-6.1[a-f]: INSERT statements
+# without_rowid4-6.2[a-f]: UPDATE statements
+#
+# 7. & 8. Triggers on views fire correctly.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+  finish_test
+  return
+}
+
+# The tests in this file were written before SQLite supported recursive
+# trigger invocation, and some tests depend on that to pass. So disable
+# recursive triggers for this file.
+catchsql { pragma recursive_triggers = off } 
+
+# 1.
+ifcapable subquery {
+  set ii 0
+  set tbl_definitions [list \
+       {CREATE TABLE tbl (a INTEGER PRIMARY KEY, b) WITHOUT rowid;}    \
+        {CREATE TABLE tbl (a, b PRIMARY KEY) WITHOUT rowid;}            \
+       {CREATE TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid;
+         CREATE INDEX tbl_idx ON tbl(b);}      \
+  ]
+  ifcapable tempdb {
+    lappend tbl_definitions \
+        {CREATE TEMP TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid;
+         CREATE INDEX tbl_idx ON tbl(b);} 
+    lappend tbl_definitions \
+        {CREATE TEMP TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid}
+    lappend tbl_definitions \
+        {CREATE TEMPORARY TABLE tbl (a INTEGER PRIMARY KEY, b) WITHOUT rowid;}
+  }
+  foreach tbl_defn $tbl_definitions {
+    incr ii
+    catchsql { DROP INDEX tbl_idx; }
+    catchsql {
+      DROP TABLE rlog;
+      DROP TABLE clog;
+      DROP TABLE tbl;
+      DROP TABLE other_tbl;
+    }
+  
+    execsql $tbl_defn
+  
+    execsql {
+      INSERT INTO tbl VALUES(1, 2);
+      INSERT INTO tbl VALUES(3, 4);
+  
+      CREATE TABLE rlog (idx, old_a, old_b, db_sum_a, db_sum_b, new_a, new_b);
+      CREATE TABLE clog (idx, old_a, old_b, db_sum_a, db_sum_b, new_a, new_b);
+  
+      CREATE TRIGGER before_update_row BEFORE UPDATE ON tbl FOR EACH ROW 
+        BEGIN
+        INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
+         old.a, old.b, 
+         (SELECT coalesce(sum(a),0) FROM tbl),
+          (SELECT coalesce(sum(b),0) FROM tbl), 
+         new.a, new.b);
+      END;
+  
+      CREATE TRIGGER after_update_row AFTER UPDATE ON tbl FOR EACH ROW 
+        BEGIN
+        INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
+         old.a, old.b, 
+         (SELECT coalesce(sum(a),0) FROM tbl),
+          (SELECT coalesce(sum(b),0) FROM tbl), 
+         new.a, new.b);
+      END;
+  
+      CREATE TRIGGER conditional_update_row AFTER UPDATE ON tbl FOR EACH ROW
+        WHEN old.a = 1
+        BEGIN
+        INSERT INTO clog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM clog), 
+         old.a, old.b, 
+         (SELECT coalesce(sum(a),0) FROM tbl),
+          (SELECT coalesce(sum(b),0) FROM tbl), 
+         new.a, new.b);
+      END;
+    }
+  
+    do_test without_rowid4-1.$ii.1 {
+      set r {}
+      foreach v [execsql { 
+        UPDATE tbl SET a = a * 10, b = b * 10;
+        SELECT * FROM rlog ORDER BY idx;
+        SELECT * FROM clog ORDER BY idx;
+      }] {
+        lappend r [expr {int($v)}]
+      }
+      set r
+    } [list 1 1 2  4  6 10 20 \
+            2 1 2 13 24 10 20 \
+           3 3 4 13 24 30 40 \
+           4 3 4 40 60 30 40 \
+            1 1 2 13 24 10 20 ]
+  
+    execsql {
+      DELETE FROM rlog;
+      DELETE FROM tbl;
+      INSERT INTO tbl VALUES (100, 100);
+      INSERT INTO tbl VALUES (300, 200);
+      CREATE TRIGGER delete_before_row BEFORE DELETE ON tbl FOR EACH ROW
+        BEGIN
+        INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
+         old.a, old.b, 
+         (SELECT coalesce(sum(a),0) FROM tbl),
+          (SELECT coalesce(sum(b),0) FROM tbl), 
+         0, 0);
+      END;
+  
+      CREATE TRIGGER delete_after_row AFTER DELETE ON tbl FOR EACH ROW
+        BEGIN
+        INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
+         old.a, old.b, 
+         (SELECT coalesce(sum(a),0) FROM tbl),
+          (SELECT coalesce(sum(b),0) FROM tbl), 
+         0, 0);
+      END;
+    }
+    do_test without_rowid4-1.$ii.2 {
+      set r {}
+      foreach v [execsql {
+        DELETE FROM tbl;
+        SELECT * FROM rlog;
+      }] {
+        lappend r [expr {int($v)}]
+      }
+      set r
+    } [list 1 100 100 400 300 0 0 \
+            2 100 100 300 200 0 0 \
+            3 300 200 300 200 0 0 \
+            4 300 200 0 0 0 0 ]
+  
+    execsql {
+      DELETE FROM rlog;
+      CREATE TRIGGER insert_before_row BEFORE INSERT ON tbl FOR EACH ROW
+        BEGIN
+        INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
+         0, 0,
+         (SELECT coalesce(sum(a),0) FROM tbl),
+          (SELECT coalesce(sum(b),0) FROM tbl), 
+         new.a, new.b);
+      END;
+  
+      CREATE TRIGGER insert_after_row AFTER INSERT ON tbl FOR EACH ROW
+        BEGIN
+        INSERT INTO rlog VALUES ( (SELECT coalesce(max(idx),0) + 1 FROM rlog), 
+         0, 0,
+         (SELECT coalesce(sum(a),0) FROM tbl),
+          (SELECT coalesce(sum(b),0) FROM tbl), 
+         new.a, new.b);
+      END;
+    }
+    do_test without_rowid4-1.$ii.3 {
+      execsql {
+  
+        CREATE TABLE other_tbl(a, b);
+        INSERT INTO other_tbl VALUES(1, 2);
+        INSERT INTO other_tbl VALUES(3, 4);
+        -- INSERT INTO tbl SELECT * FROM other_tbl;
+        INSERT INTO tbl VALUES(5, 6);
+        DROP TABLE other_tbl;
+  
+        SELECT * FROM rlog;
+      }
+    } [list 1 0 0 0 0 5 6 \
+            2 0 0 5 6 5 6 ]
+  
+    integrity_check without_rowid4-1.$ii.4
+  }
+  catchsql {
+    DROP TABLE rlog;
+    DROP TABLE clog;
+    DROP TABLE tbl;
+    DROP TABLE other_tbl;
+  }
+}
+
+# 2.
+set ii 0
+foreach tr_program {
+  {UPDATE tbl SET b = old.b;}
+  {INSERT INTO log VALUES(new.c, 2, 3);}
+  {DELETE FROM log WHERE a = 1;}
+  {INSERT INTO tbl VALUES(500, new.b * 10, 700); 
+    UPDATE tbl SET c = old.c; 
+    DELETE FROM log;}
+  {INSERT INTO log select * from tbl;} 
+} {
+  foreach test_varset [ list \
+    {
+      set statement {UPDATE tbl SET c = 10 WHERE a = 1;} 
+      set prep      {INSERT INTO tbl VALUES(1, 2, 3);}
+      set newC 10
+      set newB 2
+      set newA 1
+      set oldA 1
+      set oldB 2
+      set oldC 3
+    } \
+    {
+      set statement {DELETE FROM tbl WHERE a = 1;}
+      set prep      {INSERT INTO tbl VALUES(1, 2, 3);}
+      set oldA 1
+      set oldB 2
+      set oldC 3
+    } \
+    {
+      set statement {INSERT INTO tbl VALUES(1, 2, 3);}
+      set newA 1
+      set newB 2
+      set newC 3
+    }
+  ] \
+  {
+    set statement {}
+    set prep {}
+    set newA {''}
+    set newB {''}
+    set newC {''}
+    set oldA {''}
+    set oldB {''}
+    set oldC {''}
+
+    incr ii
+
+    eval $test_varset
+
+    set statement_type [string range $statement 0 5]
+    set tr_program_fixed $tr_program
+    if {$statement_type == "DELETE"} {
+      regsub -all new\.a $tr_program_fixed {''} tr_program_fixed 
+      regsub -all new\.b $tr_program_fixed {''} tr_program_fixed 
+      regsub -all new\.c $tr_program_fixed {''} tr_program_fixed 
+    }
+    if {$statement_type == "INSERT"} {
+      regsub -all old\.a $tr_program_fixed {''} tr_program_fixed 
+      regsub -all old\.b $tr_program_fixed {''} tr_program_fixed 
+      regsub -all old\.c $tr_program_fixed {''} tr_program_fixed 
+    }
+
+
+    set tr_program_cooked $tr_program
+    regsub -all new\.a $tr_program_cooked $newA tr_program_cooked 
+    regsub -all new\.b $tr_program_cooked $newB tr_program_cooked 
+    regsub -all new\.c $tr_program_cooked $newC tr_program_cooked 
+    regsub -all old\.a $tr_program_cooked $oldA tr_program_cooked 
+    regsub -all old\.b $tr_program_cooked $oldB tr_program_cooked 
+    regsub -all old\.c $tr_program_cooked $oldC tr_program_cooked 
+
+    catchsql {
+      DROP TABLE tbl;
+      DROP TABLE log;
+    }
+
+    execsql {
+      CREATE TABLE tbl(a PRIMARY KEY, b, c) WITHOUT rowid;
+      CREATE TABLE log(a, b, c);
+    }
+
+    set query {SELECT * FROM tbl; SELECT * FROM log;}
+    set prep "$prep; INSERT INTO log VALUES(1, 2, 3);\
+             INSERT INTO log VALUES(10, 20, 30);"
+
+# Check execution of BEFORE programs:
+
+    set before_data [ execsql "$prep $tr_program_cooked $statement $query" ]
+
+    execsql "DELETE FROM tbl; DELETE FROM log; $prep";
+    execsql "CREATE TRIGGER the_trigger BEFORE [string range $statement 0 6]\
+             ON tbl BEGIN $tr_program_fixed END;"
+
+    do_test without_rowid4-2.$ii-before "execsql {$statement $query}" $before_data
+
+    execsql "DROP TRIGGER the_trigger;"
+    execsql "DELETE FROM tbl; DELETE FROM log;"
+
+# Check execution of AFTER programs
+    set after_data [ execsql "$prep $statement $tr_program_cooked $query" ]
+
+    execsql "DELETE FROM tbl; DELETE FROM log; $prep";
+    execsql "CREATE TRIGGER the_trigger AFTER [string range $statement 0 6]\
+             ON tbl BEGIN $tr_program_fixed END;"
+
+    do_test without_rowid4-2.$ii-after "execsql {$statement $query}" $after_data
+    execsql "DROP TRIGGER the_trigger;"
+
+    integrity_check without_rowid4-2.$ii-integrity
+  }
+}
+catchsql {
+  DROP TABLE tbl;
+  DROP TABLE log;
+}
+
+# 3.
+
+# without_rowid4-3.1: UPDATE OF triggers
+execsql {
+  CREATE TABLE tbl (a, b, c, d, PRIMARY KEY(a,b,c,d)) WITHOUT rowid;
+  CREATE TABLE log (a);
+  INSERT INTO log VALUES (0);
+  INSERT INTO tbl VALUES (0, 0, 0, 0);
+  INSERT INTO tbl VALUES (1, 0, 0, 0);
+  CREATE TRIGGER tbl_after_update_cd BEFORE UPDATE OF c, d ON tbl
+    BEGIN
+      UPDATE log SET a = a + 1;
+    END;
+}
+do_test without_rowid4-3.1 {
+  execsql {
+    UPDATE tbl SET b = 1, c = 10; -- 2
+    UPDATE tbl SET b = 10; -- 0
+    UPDATE tbl SET d = 4 WHERE a = 0; --1
+    UPDATE tbl SET a = 4, b = 10; --0
+    SELECT * FROM log;
+  }
+} {3}
+execsql {
+  DROP TABLE tbl;
+  DROP TABLE log;
+}
+
+# without_rowid4-3.2: WHEN clause
+set when_triggers [list {t1 BEFORE INSERT ON tbl WHEN new.a > 20}]
+ifcapable subquery {
+  lappend when_triggers \
+      {t2 BEFORE INSERT ON tbl WHEN (SELECT count(*) FROM tbl) = 0}
+}
+
+execsql {
+  CREATE TABLE tbl (a, b, c, d);
+  CREATE TABLE log (a);
+  INSERT INTO log VALUES (0);
+}
+
+foreach trig $when_triggers {
+  execsql "CREATE TRIGGER $trig BEGIN UPDATE log set a = a + 1; END;"
+}
+
+ifcapable subquery {
+  set t232 {1 0 1}
+} else {
+  set t232 {0 0 1}
+}
+do_test without_rowid4-3.2 {
+  execsql { 
+
+    INSERT INTO tbl VALUES(0, 0, 0, 0);     -- 1 (ifcapable subquery)
+    SELECT * FROM log;
+    UPDATE log SET a = 0;
+
+    INSERT INTO tbl VALUES(0, 0, 0, 0);     -- 0
+    SELECT * FROM log;
+    UPDATE log SET a = 0;
+
+    INSERT INTO tbl VALUES(200, 0, 0, 0);     -- 1
+    SELECT * FROM log;
+    UPDATE log SET a = 0;
+  }
+} $t232
+execsql {
+  DROP TABLE tbl;
+  DROP TABLE log;
+}
+integrity_check without_rowid4-3.3
+
+# Simple cascaded trigger
+execsql {
+  CREATE TABLE tblA(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+  CREATE TABLE tblB(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+  CREATE TABLE tblC(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+
+  CREATE TRIGGER tr1 BEFORE INSERT ON tblA BEGIN
+    INSERT INTO tblB values(new.a, new.b);
+  END;
+
+  CREATE TRIGGER tr2 BEFORE INSERT ON tblB BEGIN
+    INSERT INTO tblC values(new.a, new.b);
+  END;
+}
+do_test without_rowid4-4.1 {
+  execsql {
+    INSERT INTO tblA values(1, 2);
+    SELECT * FROM tblA;
+    SELECT * FROM tblB;
+    SELECT * FROM tblC;
+  }
+} {1 2 1 2 1 2}
+execsql {
+  DROP TABLE tblA;
+  DROP TABLE tblB;
+  DROP TABLE tblC;
+}
+
+# Simple recursive trigger
+execsql {
+  CREATE TABLE tbl(a, b, c, PRIMARY KEY(c,a,b)) WITHOUT rowid;
+  CREATE TRIGGER tbl_trig BEFORE INSERT ON tbl 
+    BEGIN
+      INSERT INTO tbl VALUES (new.a, new.b, new.c+1);
+    END;
+}
+do_test without_rowid4-4.2 {
+  execsql {
+    INSERT INTO tbl VALUES (1, 2, 3);
+    select * from tbl;
+  }
+} {1 2 3 1 2 4}
+execsql {
+  DROP TABLE tbl;
+}
+
+# 5.
+execsql {
+  CREATE TABLE tbl(a, b, c, PRIMARY KEY(c,a,b)) WITHOUT rowid;
+  CREATE TRIGGER tbl_trig BEFORE INSERT ON tbl 
+    BEGIN
+      INSERT INTO tbl VALUES (1, 2, 3);
+      INSERT INTO tbl VALUES (2, 2, 3);
+      UPDATE tbl set b = 10 WHERE a = 1;
+      DELETE FROM tbl WHERE a = 1;
+      DELETE FROM tbl;
+    END;
+}
+do_test without_rowid4-5 {
+  execsql {
+    INSERT INTO tbl VALUES(100, 200, 300);
+  }
+  db changes
+} {1}
+execsql {
+  DROP TABLE tbl;
+}
+
+ifcapable conflict {
+  # Handling of ON CONFLICT by INSERT statements inside triggers
+  execsql {
+    CREATE TABLE tbl (a PRIMARY KEY, b, c) WITHOUT rowid;
+    CREATE TRIGGER ai_tbl AFTER INSERT ON tbl BEGIN
+      INSERT OR IGNORE INTO tbl values (new.a, 0, 0);
+    END;
+  }
+  do_test without_rowid4-6.1a {
+    execsql {
+      BEGIN;
+      INSERT INTO tbl values (1, 2, 3);
+      SELECT * from tbl;
+    }
+  } {1 2 3}
+  do_test without_rowid4-6.1b {
+    catchsql {
+      INSERT OR ABORT INTO tbl values (2, 2, 3);
+    }
+  } {1 {column a is not unique}}
+  do_test without_rowid4-6.1c {
+    execsql {
+      SELECT * from tbl;
+    }
+  } {1 2 3}
+  do_test without_rowid4-6.1d {
+    catchsql {
+      INSERT OR FAIL INTO tbl values (2, 2, 3);
+    }
+  } {1 {column a is not unique}}
+  do_test without_rowid4-6.1e {
+    execsql {
+      SELECT * from tbl;
+    }
+  } {1 2 3 2 2 3}
+  do_test without_rowid4-6.1f {
+    execsql {
+      INSERT OR REPLACE INTO tbl values (2, 2, 3);
+      SELECT * from tbl;
+    }
+  } {1 2 3 2 0 0}
+  do_test without_rowid4-6.1g {
+    catchsql {
+      INSERT OR ROLLBACK INTO tbl values (3, 2, 3);
+    }
+  } {1 {column a is not unique}}
+  do_test without_rowid4-6.1h {
+    execsql {
+      SELECT * from tbl;
+    }
+  } {}
+  execsql {DELETE FROM tbl}
+  
+  
+  # Handling of ON CONFLICT by UPDATE statements inside triggers
+  execsql {
+    INSERT INTO tbl values (4, 2, 3);
+    INSERT INTO tbl values (6, 3, 4);
+    CREATE TRIGGER au_tbl AFTER UPDATE ON tbl BEGIN
+      UPDATE OR IGNORE tbl SET a = new.a, c = 10;
+    END;
+  }
+  do_test without_rowid4-6.2a {
+    execsql {
+      BEGIN;
+      UPDATE tbl SET a = 1 WHERE a = 4;
+      SELECT * from tbl;
+    }
+  } {1 2 10 6 3 4}
+  do_test without_rowid4-6.2b {
+    catchsql {
+      UPDATE OR ABORT tbl SET a = 4 WHERE a = 1;
+    }
+  } {1 {column a is not unique}}
+  do_test without_rowid4-6.2c {
+    execsql {
+      SELECT * from tbl;
+    }
+  } {1 2 10 6 3 4}
+  do_test without_rowid4-6.2d {
+    catchsql {
+      UPDATE OR FAIL tbl SET a = 4 WHERE a = 1;
+    }
+  } {1 {column a is not unique}}
+  do_test without_rowid4-6.2e {
+    execsql {
+      SELECT * from tbl;
+    }
+  } {4 2 10 6 3 4}
+  do_test without_rowid4-6.2f.1 {
+    execsql {
+      UPDATE OR REPLACE tbl SET a = 1 WHERE a = 4;
+      SELECT * from tbl;
+    }
+  } {1 3 10}
+  do_test without_rowid4-6.2f.2 {
+    execsql {
+      INSERT INTO tbl VALUES (2, 3, 4);
+      SELECT * FROM tbl;
+    }
+  } {1 3 10 2 3 4}
+  do_test without_rowid4-6.2g {
+    catchsql {
+      UPDATE OR ROLLBACK tbl SET a = 4 WHERE a = 1;
+    }
+  } {1 {column a is not unique}}
+  do_test without_rowid4-6.2h {
+    execsql {
+      SELECT * from tbl;
+    }
+  } {4 2 3 6 3 4}
+  execsql {
+    DROP TABLE tbl;
+  }
+} ; # ifcapable conflict
+
+# 7. Triggers on views
+ifcapable view {
+
+do_test without_rowid4-7.1 {
+  execsql {
+  CREATE TABLE ab(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+  CREATE TABLE cd(c, d, PRIMARY KEY(c,d)) WITHOUT rowid;
+  INSERT INTO ab VALUES (1, 2);
+  INSERT INTO ab VALUES (0, 0);
+  INSERT INTO cd VALUES (3, 4);
+
+  CREATE TABLE tlog(ii INTEGER PRIMARY KEY, 
+      olda, oldb, oldc, oldd, newa, newb, newc, newd);
+
+  CREATE VIEW abcd AS SELECT a, b, c, d FROM ab, cd;
+
+  CREATE TRIGGER before_update INSTEAD OF UPDATE ON abcd BEGIN
+    INSERT INTO tlog VALUES(NULL, 
+       old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d);
+  END;
+  CREATE TRIGGER after_update INSTEAD OF UPDATE ON abcd BEGIN
+    INSERT INTO tlog VALUES(NULL, 
+       old.a, old.b, old.c, old.d, new.a, new.b, new.c, new.d);
+  END;
+
+  CREATE TRIGGER before_delete INSTEAD OF DELETE ON abcd BEGIN
+    INSERT INTO tlog VALUES(NULL, 
+       old.a, old.b, old.c, old.d, 0, 0, 0, 0);
+  END;
+  CREATE TRIGGER after_delete INSTEAD OF DELETE ON abcd BEGIN
+    INSERT INTO tlog VALUES(NULL, 
+       old.a, old.b, old.c, old.d, 0, 0, 0, 0);
+  END;
+
+  CREATE TRIGGER before_insert INSTEAD OF INSERT ON abcd BEGIN
+    INSERT INTO tlog VALUES(NULL, 
+       0, 0, 0, 0, new.a, new.b, new.c, new.d);
+  END;
+   CREATE TRIGGER after_insert INSTEAD OF INSERT ON abcd BEGIN
+    INSERT INTO tlog VALUES(NULL, 
+       0, 0, 0, 0, new.a, new.b, new.c, new.d);
+   END;
+  }
+} {};
+
+do_test without_rowid4-7.2 {
+  execsql {
+    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
+    DELETE FROM abcd WHERE a = 1;
+    INSERT INTO abcd VALUES(10, 20, 30, 40);
+    SELECT * FROM tlog;
+  }
+} [ list 1 1 2 3 4 100 25 3 4 \
+         2 1 2 3 4 100 25 3 4 \
+        3 1 2 3 4 0 0 0 0 \
+        4 1 2 3 4 0 0 0 0 \
+        5 0 0 0 0 10 20 30 40 \
+        6 0 0 0 0 10 20 30 40 ]
+
+do_test without_rowid4-7.3 {
+  execsql {
+    DELETE FROM tlog;
+    INSERT INTO abcd VALUES(10, 20, 30, 40);
+    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
+    DELETE FROM abcd WHERE a = 1;
+    SELECT * FROM tlog;
+  }
+} [ list \
+   1 0 0 0 0 10 20 30 40 \
+   2 0 0 0 0 10 20 30 40 \
+   3 1 2 3 4 100 25 3 4 \
+   4 1 2 3 4 100 25 3 4 \
+   5 1 2 3 4 0 0 0 0 \
+   6 1 2 3 4 0 0 0 0 \
+]
+do_test without_rowid4-7.4 {
+  execsql {
+    DELETE FROM tlog;
+    DELETE FROM abcd WHERE a = 1;
+    INSERT INTO abcd VALUES(10, 20, 30, 40);
+    UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
+    SELECT * FROM tlog;
+  }
+} [ list \
+   1 1 2 3 4 0 0 0 0 \
+   2 1 2 3 4 0 0 0 0 \
+   3 0 0 0 0 10 20 30 40 \
+   4 0 0 0 0 10 20 30 40 \
+   5 1 2 3 4 100 25 3 4 \
+   6 1 2 3 4 100 25 3 4 \
+]
+
+do_test without_rowid4-8.1 {
+  execsql {
+    CREATE TABLE t1(a,b,c, PRIMARY KEY(a,b,c)) WITHOUT rowid;
+    INSERT INTO t1 VALUES(1,2,3);
+    CREATE VIEW v1 AS
+      SELECT a+b AS x, b+c AS y, a+c AS z FROM t1;
+    SELECT * FROM v1;
+  }
+} {3 5 4}
+do_test without_rowid4-8.2 {
+  execsql {
+    CREATE TABLE v1log(a,b,c,d,e,f);
+    CREATE TRIGGER r1 INSTEAD OF DELETE ON v1 BEGIN
+      INSERT INTO v1log VALUES(OLD.x,NULL,OLD.y,NULL,OLD.z,NULL);
+    END;
+    DELETE FROM v1 WHERE x=1;
+    SELECT * FROM v1log;
+  }
+} {}
+do_test without_rowid4-8.3 {
+  execsql {
+    DELETE FROM v1 WHERE x=3;
+    SELECT * FROM v1log;
+  }
+} {3 {} 5 {} 4 {}}
+do_test without_rowid4-8.4 {
+  execsql {
+    INSERT INTO t1 VALUES(4,5,6);
+    DELETE FROM v1log;
+    DELETE FROM v1 WHERE y=11;
+    SELECT * FROM v1log;
+  }
+} {9 {} 11 {} 10 {}}
+do_test without_rowid4-8.5 {
+  execsql {
+    CREATE TRIGGER r2 INSTEAD OF INSERT ON v1 BEGIN
+      INSERT INTO v1log VALUES(NULL,NEW.x,NULL,NEW.y,NULL,NEW.z);
+    END;
+    DELETE FROM v1log;
+    INSERT INTO v1 VALUES(1,2,3);
+    SELECT * FROM v1log;
+  }
+} {{} 1 {} 2 {} 3}
+do_test without_rowid4-8.6 {
+  execsql {
+    CREATE TRIGGER r3 INSTEAD OF UPDATE ON v1 BEGIN
+      INSERT INTO v1log VALUES(OLD.x,NEW.x,OLD.y,NEW.y,OLD.z,NEW.z);
+    END;
+    DELETE FROM v1log;
+    UPDATE v1 SET x=x+100, y=y+200, z=z+300;
+    SELECT * FROM v1log;
+  }
+} {3 103 5 205 4 304 9 109 11 211 10 310}
+
+# At one point the following was causing a segfault.
+do_test without_rowid4-9.1 {
+  execsql {
+    CREATE TABLE t3(a TEXT, b TEXT);
+    CREATE VIEW v3 AS SELECT t3.a FROM t3;
+    CREATE TRIGGER trig1 INSTEAD OF DELETE ON v3 BEGIN
+      SELECT 1;
+    END;
+    DELETE FROM v3 WHERE a = 1;
+  }
+} {}
+
+} ;# ifcapable view
+
+integrity_check without_rowid4-9.9
+
+finish_test