If recursive-triggers are enabled, fire DELETE triggers if database rows are removed as a result of OR REPLACE conflict resolution.
FossilOrigin-Name: 85cb0c94a63eda5f059ebe40887c7af9b4869893
This commit is contained in:
parent
345ba7db59
commit
2283d46cd6
30
manifest
30
manifest
@ -1,8 +1,5 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
C Additional\ssimplifications\sin\ssupport\sof\sstructural\stesting.
|
||||
D 2009-09-08T13:40:17
|
||||
C If\srecursive-triggers\sare\senabled,\sfire\sDELETE\striggers\sif\sdatabase\srows\sare\sremoved\sas\sa\sresult\sof\sOR\sREPLACE\sconflict\sresolution.
|
||||
D 2009-09-08T15:55:16
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in 73ddeec9dd10b85876c5c2ce1fdce627e1dcc7f8
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
@ -116,7 +113,7 @@ F src/build.c df8dfff696329c192240e3d532f9dad0ef5abace
|
||||
F src/callback.c f49c305dc94b78da948953c392963929c0e70f9b
|
||||
F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
|
||||
F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3
|
||||
F src/delete.c 6b95963dabd558d45385e9b5be1fb4aa7ba7fa62
|
||||
F src/delete.c 4c9b899246a12795ae7f145ad7c5c3ac563fa05f
|
||||
F src/expr.c 2605f0f161442e3153e0c41e987525260e9ad306
|
||||
F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
|
||||
F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606
|
||||
@ -124,7 +121,7 @@ F src/global.c 271952d199a8cc59d4ce840b3bbbfd2f30c8ba32
|
||||
F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7
|
||||
F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1
|
||||
F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb
|
||||
F src/insert.c 06fe504934bdd3b3a0fa0e11ccd6506b57114c52
|
||||
F src/insert.c 5cf80f9b4222c2145cab299e9b829385846b6937
|
||||
F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
|
||||
F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6
|
||||
F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d
|
||||
@ -166,7 +163,7 @@ F src/select.c a7a075456d4e640ffd7d0a33202d306c69c88f72
|
||||
F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb
|
||||
F src/sqlite.h.in e5949b46f9a05aadde22848f92fae5c9ba87ee0e
|
||||
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
|
||||
F src/sqliteInt.h 6c03c685f37c2403ae78c4eb34f9ee9082748270
|
||||
F src/sqliteInt.h b39de08df1442d48a6ea85c227c609e696a85b89
|
||||
F src/sqliteLimit.h be44f7f46c14bb4c21870074b1e6f1ac0abd6701
|
||||
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
|
||||
F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
|
||||
@ -685,7 +682,7 @@ F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
|
||||
F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31
|
||||
F test/triggerA.test 0718ad2d9bfef27c7af00e636df79bee6b988da7
|
||||
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
||||
F test/triggerC.test cc43b4a62f447a0b0ec76ce511758c460c049c83
|
||||
F test/triggerC.test 3e13e9a87939797115343b261a3f893c71304106
|
||||
F test/types.test 9a825ec8eea4e965d7113b74c76a78bb5240f2ac
|
||||
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
|
||||
F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
|
||||
@ -753,14 +750,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P b271e16621831957468a1d3925174aac73f58891
|
||||
R 8c612cb15dd060090bb2d4d886dee584
|
||||
U drh
|
||||
Z a9d8abd000fb942a5d5935b1dbcc5c31
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||
|
||||
iD8DBQFKpl7EoxKgR168RlERArvgAJ9VIhI4NUVMwROuivJoGqBtHQo34ACdFY4R
|
||||
tYb9IFwgX3mkui92z9PMIsY=
|
||||
=tFOy
|
||||
-----END PGP SIGNATURE-----
|
||||
P 4ab8c841f818326b0b04b95e3edd828c77f109d9
|
||||
R 3fb4417640a30cdf072206af6c01b085
|
||||
U dan
|
||||
Z 8bcac683a9d824da75eea0a85386a639
|
||||
|
@ -1 +1 @@
|
||||
4ab8c841f818326b0b04b95e3edd828c77f109d9
|
||||
85cb0c94a63eda5f059ebe40887c7af9b4869893
|
129
src/delete.c
129
src/delete.c
@ -338,9 +338,9 @@ void sqlite3DeleteFrom(
|
||||
|
||||
#ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
|
||||
/* Special case: A DELETE without a WHERE clause deletes everything.
|
||||
** It is easier just to erase the whole table. Note, however, that
|
||||
** this means that the row change count will be incorrect.
|
||||
*/
|
||||
** It is easier just to erase the whole table. Prior to version 3.6.5,
|
||||
** this optimization caused the row change count (the value returned by
|
||||
** API function sqlite3_count_changes) to be set incorrectly. */
|
||||
if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) ){
|
||||
assert( !isView );
|
||||
sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
|
||||
@ -357,7 +357,6 @@ void sqlite3DeleteFrom(
|
||||
{
|
||||
int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
|
||||
int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
|
||||
int regOld = pParse->nMem + 1; /* Start of array for old.* (if triggers) */
|
||||
int regRowid; /* Actual register containing rowids */
|
||||
|
||||
/* Collect rowids of every row to be deleted.
|
||||
@ -387,38 +386,6 @@ void sqlite3DeleteFrom(
|
||||
|
||||
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
|
||||
|
||||
/* If there are triggers, populate an array of registers with the
|
||||
** data required by the old.* references in the trigger bodies. */
|
||||
if( pTrigger ){
|
||||
u32 mask = 0; /* Mask of OLD.* columns in use */
|
||||
pParse->nMem += pTab->nCol;
|
||||
|
||||
/* Open the pseudo-table used to store OLD if there are triggers. */
|
||||
mask = sqlite3TriggerOldmask(
|
||||
pParse, pTrigger, TK_DELETE, 0, pTab, OE_Default);
|
||||
|
||||
/* If the record is no longer present in the table, jump to the
|
||||
** next iteration of the loop through the contents of the fifo.
|
||||
*/
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid);
|
||||
|
||||
/* Populate the OLD.* pseudo-table */
|
||||
assert( regOld==iRowid+1 );
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( mask==0xffffffff || mask&(1<<i) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
|
||||
sqlite3ColumnDefault(v, pTab, i, regOld+i);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Affinity, regOld, pTab->nCol);
|
||||
sqlite3TableAffinityStr(v, pTab);
|
||||
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger,
|
||||
TK_DELETE, 0, TRIGGER_BEFORE, pTab, -1, iRowid, OE_Default, addr
|
||||
);
|
||||
}
|
||||
|
||||
if( !isView ){
|
||||
/* Delete the row */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
if( IsVirtual(pTab) ){
|
||||
@ -428,14 +395,9 @@ void sqlite3DeleteFrom(
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, pParse->nested==0);
|
||||
int count = (pParse->nested==0); /* True to count changes */
|
||||
sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default);
|
||||
}
|
||||
}
|
||||
|
||||
/* Code the AFTER triggers. This is a no-op if there are no triggers. */
|
||||
sqlite3CodeRowTrigger(pParse,
|
||||
pTrigger, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, iRowid, OE_Default, addr
|
||||
);
|
||||
|
||||
/* End of the delete loop */
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
|
||||
@ -458,8 +420,7 @@ void sqlite3DeleteFrom(
|
||||
sqlite3AutoincrementEnd(pParse);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the number of rows that were deleted. If this routine is
|
||||
/* Return the number of rows that were deleted. If this routine is
|
||||
** generating code because of a call to sqlite3NestedParse(), do not
|
||||
** invoke the callback function.
|
||||
*/
|
||||
@ -492,28 +453,88 @@ delete_from_cleanup:
|
||||
** 3. The record number of the row to be deleted must be stored in
|
||||
** memory cell iRowid.
|
||||
**
|
||||
** This routine pops the top of the stack to remove the record number
|
||||
** and then generates code to remove both the table record and all index
|
||||
** entries that point to that record.
|
||||
** This routine generates code to remove both the table record and all
|
||||
** index entries that point to that record.
|
||||
*/
|
||||
void sqlite3GenerateRowDelete(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pTab, /* Table containing the row to be deleted */
|
||||
int iCur, /* Cursor number for the table */
|
||||
int iRowid, /* Memory cell that contains the rowid to delete */
|
||||
int count /* Increment the row change counter */
|
||||
int count, /* If non-zero, increment the row change counter */
|
||||
Trigger *pTrigger, /* List of triggers to (potentially) fire */
|
||||
int onconf /* Default ON CONFLICT policy for triggers */
|
||||
){
|
||||
int addr;
|
||||
Vdbe *v;
|
||||
Vdbe *v = pParse->pVdbe; /* Vdbe */
|
||||
int iOld; /* First register in OLD.* array */
|
||||
int iLabel; /* Label resolved to end of generated code */
|
||||
|
||||
v = pParse->pVdbe;
|
||||
addr = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowid);
|
||||
/* Vdbe is guaranteed to have been allocated by this stage. */
|
||||
assert( v );
|
||||
|
||||
/* Seek cursor iCur to the row to delete. If this row no longer exists
|
||||
** (this can happen if a trigger program has already deleted it), do
|
||||
** not attempt to delete it or fire any DELETE triggers. */
|
||||
iLabel = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
|
||||
|
||||
/* If there are any triggers to fire, allocate a range of registers to
|
||||
** use for the old.* references in the triggers. */
|
||||
if( pTrigger ){
|
||||
u32 mask; /* Mask of OLD.* columns in use */
|
||||
int iCol; /* Iterator used while populating OLD.* */
|
||||
|
||||
/* TODO: Could use temporary registers here. Also could attempt to
|
||||
** avoid copying the contents of the rowid register. */
|
||||
mask = sqlite3TriggerOldmask(pParse, pTrigger, TK_DELETE, 0, pTab, onconf);
|
||||
iOld = pParse->nMem+1;
|
||||
pParse->nMem += (1 + pTab->nCol);
|
||||
|
||||
/* Populate the OLD.* pseudo-table register array. These values will be
|
||||
** used by any BEFORE and AFTER triggers that exist. */
|
||||
sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld);
|
||||
for(iCol=0; iCol<pTab->nCol; iCol++){
|
||||
if( mask==0xffffffff || mask&(1<<iCol) ){
|
||||
int iTarget = iOld + iCol + 1;
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iCur, iCol, iTarget);
|
||||
sqlite3ColumnDefault(v, pTab, iCol, iTarget);
|
||||
}
|
||||
}
|
||||
|
||||
/* Invoke any BEFORE trigger programs */
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger,
|
||||
TK_DELETE, 0, TRIGGER_BEFORE, pTab, -1, iOld, onconf, iLabel
|
||||
);
|
||||
|
||||
/* Seek the cursor to the row to be deleted again. It may be that
|
||||
** the BEFORE triggers coded above have already removed the row
|
||||
** being deleted. Do not attempt to delete the row a second time, and
|
||||
** do not fire AFTER triggers. */
|
||||
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
|
||||
}
|
||||
|
||||
/* Delete the index and table entries. Skip this step if pTab is really
|
||||
** a view (in which case the only effect of the DELETE statement is to
|
||||
** fire the INSTEAD OF triggers). */
|
||||
if( pTab->pSelect==0 ){
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
|
||||
if( count ){
|
||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
|
||||
/* Invoke AFTER triggers. */
|
||||
if( pTrigger ){
|
||||
sqlite3CodeRowTrigger(pParse, pTrigger,
|
||||
TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, iOld, onconf, iLabel
|
||||
);
|
||||
}
|
||||
|
||||
/* Jump here if the row had already been deleted before any BEFORE
|
||||
** trigger programs were invoked. Or if a trigger program throws a
|
||||
** RAISE(IGNORE) exception. */
|
||||
sqlite3VdbeResolveLabel(v, iLabel);
|
||||
}
|
||||
|
||||
/*
|
||||
|
28
src/insert.c
28
src/insert.c
@ -1241,7 +1241,26 @@ void sqlite3GenerateConstraintChecks(
|
||||
break;
|
||||
}
|
||||
case OE_Replace: {
|
||||
/* If there are DELETE triggers on this table and the
|
||||
** recursive-triggers flag is set, call GenerateRowDelete() to
|
||||
** remove the conflicting row from the the table. This will fire
|
||||
** the triggers and remove both the table and index b-tree entries.
|
||||
**
|
||||
** Otherwise, if there are no triggers or the recursive-triggers
|
||||
** flag is not set, call GenerateRowIndexDelete(). This removes
|
||||
** the index b-tree entries only. The table b-tree entry will be
|
||||
** replaced by the new entry when it is inserted. */
|
||||
Trigger *pTrigger = 0;
|
||||
if( pParse->db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
if( pTrigger ){
|
||||
sqlite3GenerateRowDelete(
|
||||
pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace
|
||||
);
|
||||
}else{
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0);
|
||||
}
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
@ -1299,7 +1318,6 @@ void sqlite3GenerateConstraintChecks(
|
||||
else if( onError==OE_Fail ) onError = OE_Abort;
|
||||
}
|
||||
|
||||
|
||||
/* Check to see if the new index entry will be unique */
|
||||
regR = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR);
|
||||
@ -1342,8 +1360,14 @@ void sqlite3GenerateConstraintChecks(
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Trigger *pTrigger = 0;
|
||||
assert( onError==OE_Replace );
|
||||
sqlite3GenerateRowDelete(pParse, pTab, baseCur, regR, 0);
|
||||
if( pParse->db->flags&SQLITE_RecTriggers ){
|
||||
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
|
||||
}
|
||||
sqlite3GenerateRowDelete(
|
||||
pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace
|
||||
);
|
||||
seenReplace = 1;
|
||||
break;
|
||||
}
|
||||
|
@ -2647,7 +2647,7 @@ int sqlite3ExprIsConstantNotJoin(Expr*);
|
||||
int sqlite3ExprIsConstantOrFunction(Expr*);
|
||||
int sqlite3ExprIsInteger(Expr*, int*);
|
||||
int sqlite3IsRowid(const char*);
|
||||
void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int);
|
||||
void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int);
|
||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
|
||||
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
|
||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
|
||||
|
@ -17,6 +17,24 @@ ifcapable {!trigger} {
|
||||
return
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test organization:
|
||||
#
|
||||
# triggerC-1.*: Haphazardly designed trigger related tests that were useful
|
||||
# during an upgrade of the triggers sub-system.
|
||||
#
|
||||
# triggerC-2.*:
|
||||
#
|
||||
# triggerC-3.*:
|
||||
#
|
||||
# triggerC-4.*:
|
||||
#
|
||||
# triggerC-5.*: Test that when recursive triggers are enabled DELETE
|
||||
# triggers are fired when rows are deleted as part of OR
|
||||
# REPLACE conflict resolution. And that they are not fired
|
||||
# if recursive triggers are not enabled.
|
||||
#
|
||||
|
||||
# Enable recursive triggers for this file.
|
||||
#
|
||||
execsql { PRAGMA recursive_triggers = on }
|
||||
@ -531,4 +549,116 @@ foreach {n insert log} {
|
||||
} [join $log " "]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This block of tests, triggerC-5.*, test that DELETE triggers are fired
|
||||
# if a row is deleted as a result of OR REPLACE conflict resolution.
|
||||
#
|
||||
do_test triggerC-5.1.0 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t5;
|
||||
CREATE TABLE t5(a INTEGER PRIMARY KEY, b);
|
||||
CREATE UNIQUE INDEX t5i ON t5(b);
|
||||
INSERT INTO t5 VALUES(1, 'a');
|
||||
INSERT INTO t5 VALUES(2, 'b');
|
||||
INSERT INTO t5 VALUES(3, 'c');
|
||||
|
||||
CREATE TABLE t5g(a, b, c);
|
||||
CREATE TRIGGER t5t BEFORE DELETE ON t5 BEGIN
|
||||
INSERT INTO t5g VALUES(old.a, old.b, (SELECT count(*) FROM t5));
|
||||
END;
|
||||
}
|
||||
} {}
|
||||
foreach {n dml t5g t5} {
|
||||
1 "DELETE FROM t5 WHERE a=2" {2 b 3} {1 a 3 c}
|
||||
2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')" {2 b 3} {1 a 2 d 3 c}
|
||||
3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3" {2 b 3} {1 a 2 c}
|
||||
4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')" {2 b 3} {1 a 3 c 4 b}
|
||||
5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'" {2 b 3} {1 a 3 b}
|
||||
6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')" {2 b 3 3 c 2} {1 a 2 c}
|
||||
7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {1 a 3 2 b 2} {1 b}
|
||||
} {
|
||||
do_test triggerC-5.1.$n {
|
||||
execsql "
|
||||
BEGIN;
|
||||
$dml ;
|
||||
SELECT * FROM t5g;
|
||||
SELECT * FROM t5;
|
||||
ROLLBACK;
|
||||
"
|
||||
} [concat $t5g $t5]
|
||||
}
|
||||
do_test triggerC-5.2.0 {
|
||||
execsql {
|
||||
DROP TRIGGER t5t;
|
||||
CREATE TRIGGER t5t AFTER DELETE ON t5 BEGIN
|
||||
INSERT INTO t5g VALUES(old.a, old.b, (SELECT count(*) FROM t5));
|
||||
END;
|
||||
}
|
||||
} {}
|
||||
foreach {n dml t5g t5} {
|
||||
1 "DELETE FROM t5 WHERE a=2" {2 b 2} {1 a 3 c}
|
||||
2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')" {2 b 2} {1 a 2 d 3 c}
|
||||
3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3" {2 b 2} {1 a 2 c}
|
||||
4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')" {2 b 2} {1 a 3 c 4 b}
|
||||
5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'" {2 b 2} {1 a 3 b}
|
||||
6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')" {2 b 2 3 c 1} {1 a 2 c}
|
||||
7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {1 a 2 2 b 1} {1 b}
|
||||
} {
|
||||
do_test triggerC-5.2.$n {
|
||||
execsql "
|
||||
BEGIN;
|
||||
$dml ;
|
||||
SELECT * FROM t5g;
|
||||
SELECT * FROM t5;
|
||||
ROLLBACK;
|
||||
"
|
||||
} [concat $t5g $t5]
|
||||
}
|
||||
do_test triggerC-5.3.0 {
|
||||
execsql { PRAGMA recursive_triggers = off }
|
||||
} {}
|
||||
foreach {n dml t5g t5} {
|
||||
1 "DELETE FROM t5 WHERE a=2" {2 b 2} {1 a 3 c}
|
||||
2 "INSERT OR REPLACE INTO t5 VALUES(2, 'd')" {} {1 a 2 d 3 c}
|
||||
3 "UPDATE OR REPLACE t5 SET a = 2 WHERE a = 3" {} {1 a 2 c}
|
||||
4 "INSERT OR REPLACE INTO t5 VALUES(4, 'b')" {} {1 a 3 c 4 b}
|
||||
5 "UPDATE OR REPLACE t5 SET b = 'b' WHERE b = 'c'" {} {1 a 3 b}
|
||||
6 "INSERT OR REPLACE INTO t5 VALUES(2, 'c')" {} {1 a 2 c}
|
||||
7 "UPDATE OR REPLACE t5 SET a=1, b='b' WHERE a = 3" {} {1 b}
|
||||
} {
|
||||
do_test triggerC-5.3.$n {
|
||||
execsql "
|
||||
BEGIN;
|
||||
$dml ;
|
||||
SELECT * FROM t5g;
|
||||
SELECT * FROM t5;
|
||||
ROLLBACK;
|
||||
"
|
||||
} [concat $t5g $t5]
|
||||
}
|
||||
do_test triggerC-5.3.8 {
|
||||
execsql { PRAGMA recursive_triggers = on }
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This block of tests, triggerC-6.*, tests that "PRAGMA recursive_triggers"
|
||||
# statements return the current value of the recursive triggers flag.
|
||||
#
|
||||
do_test triggerC-6.1 {
|
||||
execsql { PRAGMA recursive_triggers }
|
||||
} {1}
|
||||
do_test triggerC-6.2 {
|
||||
execsql {
|
||||
PRAGMA recursive_triggers = off;
|
||||
PRAGMA recursive_triggers;
|
||||
}
|
||||
} {0}
|
||||
do_test triggerC-6.3 {
|
||||
execsql {
|
||||
PRAGMA recursive_triggers = on;
|
||||
PRAGMA recursive_triggers;
|
||||
}
|
||||
} {1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
Loading…
x
Reference in New Issue
Block a user