mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Add RAISE() function, which allows more advanced flow-control in trigger programs (ticket #55) (CVS 614)
FossilOrigin-Name: d4a2fb10067203a0d49317db747759872e62927e
This commit is contained in:
31
manifest
31
manifest
@@ -1,5 +1,5 @@
|
||||
C Fix\sthe\sspelling\sof\ssqliteRegisterBuiltinFunctions().\s(CVS\s613)
|
||||
D 2002-06-09T10:14:19
|
||||
C Add\sRAISE()\sfunction,\swhich\sallows\smore\sadvanced\sflow-control\sin\strigger\sprograms\s(ticket\s#55)\s(CVS\s614)
|
||||
D 2002-06-11T02:25:41
|
||||
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
|
||||
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
|
||||
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
|
||||
@@ -21,38 +21,38 @@ F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
|
||||
F src/btree.c 8b86be8f234c1c5dab3186f69cee2544ec9d7257
|
||||
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
|
||||
F src/build.c 36e42718a7a94f554ea39508993378482f5335c7
|
||||
F src/delete.c a2b098cbbf518e6b641847e26de85827793bc523
|
||||
F src/delete.c 15789fc723a6776309945b13a79f9a0e78275fc0
|
||||
F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
|
||||
F src/expr.c cd2e3311c84533fad19336d3bbfdc3be3400d377
|
||||
F src/expr.c 2dcfcd0b032206954a307d7e2731bf070d58835b
|
||||
F src/func.c b8d0fd3011f53ea0e46b6bab857612eb36b5d1ea
|
||||
F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
|
||||
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
|
||||
F src/insert.c 4b0bd94296fea46ef1b2ed8bfd05e12a38ce2c90
|
||||
F src/insert.c 3a90bb98cd246f88cc26c44f24d5b47760bc5cba
|
||||
F src/main.c 3a4e53122c0b0d0a5719b73fa9ec642761291ba1
|
||||
F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
|
||||
F src/os.c 9cc40c5384baba4a85e160e67807645ca98ba3cc
|
||||
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
|
||||
F src/pager.c 1e41053c949cea1f09d8dafada5fe8f90785e650
|
||||
F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
|
||||
F src/parse.y 3b4989cb81ab2f441ef6c7cbb203829838eb299e
|
||||
F src/parse.y 42920305d49666419358b469e4ec522ac867a39f
|
||||
F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd
|
||||
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
|
||||
F src/select.c 1d5cb1ae0bb3376bedfde7ae22e6e927e4d0b5e2
|
||||
F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
|
||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||
F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
|
||||
F src/sqliteInt.h 470056bd21902e15f5ac459f09f64a717657332b
|
||||
F src/sqliteInt.h 93e0ad1b37658391de6f211f65e7924a2064aa4d
|
||||
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
||||
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
|
||||
F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
|
||||
F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730
|
||||
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
|
||||
F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
|
||||
F src/tokenize.c 35c63867d03fcaf81fe520f8d8206981d0c7270e
|
||||
F src/trigger.c d02f8e3510c7c2ad948a0e8c3bb0cca8adaf80c5
|
||||
F src/update.c f68375173bf5338cae3e97012708e10f206aedd9
|
||||
F src/tokenize.c 890ca022d45f1798dadc300a798951597428853e
|
||||
F src/trigger.c 21342af6ac031fece39c8fc6eabd1739ca5327c1
|
||||
F src/update.c 05431e23a9c83502fd7911e771c8366fc2b90b4c
|
||||
F src/util.c 7cf46b5612f5d12601c697374b9c6b38b2332ce8
|
||||
F src/vdbe.c b315d7ad5086164bb8d8aee8bc9edeafcb68b8ea
|
||||
F src/vdbe.c 836f2c4f823c94c3c3454125d9ba9283e8b22dda
|
||||
F src/vdbe.h 1742d6f8b40f40879475b4c41cf4f9980ceb0e21
|
||||
F src/where.c b7c653054d4941d17f3112776ebcaf00d9613cb7
|
||||
F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
|
||||
@@ -103,6 +103,7 @@ F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6
|
||||
F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee
|
||||
F test/trigger1.test bb63749fa8a395a60541100607d86381604b7194
|
||||
F test/trigger2.test c12759a0d7ba6488d9d24c96a1352ddee995c1ab
|
||||
F test/trigger3.test b4aca721ba92956c7fa16bb0158254f3c1b73efa
|
||||
F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44
|
||||
F test/update.test a0aa0bf83e6fad8407d0e4ad25ebb09b513f5bf4
|
||||
F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
|
||||
@@ -136,7 +137,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
|
||||
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
|
||||
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
|
||||
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
|
||||
P 2a710e18176c486525f0abb06644a511a2cd1d7a
|
||||
R e2c1bfb01c44cc3c89a1b280cee79956
|
||||
U drh
|
||||
Z bd401167cac426bd14d74027f33bb932
|
||||
P 74d297d97e66452acc5c21048ee8ddf2a90c846f
|
||||
R 5c5b230772c3601ecd48523835df13e1
|
||||
U danielk1977
|
||||
Z a3a6e123a401efc55c5086c35a87d954
|
||||
|
@@ -1 +1 @@
|
||||
74d297d97e66452acc5c21048ee8ddf2a90c846f
|
||||
d4a2fb10067203a0d49317db747759872e62927e
|
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.36 2002/05/24 02:04:33 drh Exp $
|
||||
** $Id: delete.c,v 1.37 2002/06/11 02:25:41 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -237,7 +237,8 @@ void sqliteDeleteFrom(
|
||||
sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0);
|
||||
|
||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
|
||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||
addr);
|
||||
}
|
||||
|
||||
/* Open cursors for the table we are deleting from and all its
|
||||
@@ -271,7 +272,8 @@ void sqliteDeleteFrom(
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
|
||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
|
||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||
addr);
|
||||
}
|
||||
|
||||
/* End of the delete loop */
|
||||
|
26
src/expr.c
26
src/expr.c
@@ -12,7 +12,7 @@
|
||||
** This file contains routines used for analyzing expressions and
|
||||
** for generating VDBE code that evaluates expressions in SQLite.
|
||||
**
|
||||
** $Id: expr.c,v 1.70 2002/06/09 01:16:01 drh Exp $
|
||||
** $Id: expr.c,v 1.71 2002/06/11 02:25:41 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -204,7 +204,7 @@ SrcList *sqliteSrcListDup(SrcList *p){
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->nSrc = p->nSrc;
|
||||
pNew->a = sqliteMalloc( p->nSrc*sizeof(p->a[0]) );
|
||||
if( pNew->a==0 ) return 0;
|
||||
if( pNew->a==0 && p->nSrc != 0 ) return 0;
|
||||
for(i=0; i<p->nSrc; i++){
|
||||
pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
|
||||
pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias);
|
||||
@@ -1014,6 +1014,28 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
}
|
||||
sqliteVdbeResolveLabel(v, expr_end_label);
|
||||
break;
|
||||
}
|
||||
case TK_RAISE: {
|
||||
if( !pParse->trigStack ){
|
||||
sqliteSetNString(&pParse->zErrMsg,
|
||||
"RAISE() may only be used within a trigger-program", -1, 0);
|
||||
pParse->nErr++;
|
||||
return;
|
||||
}
|
||||
if( pExpr->iColumn == OE_Rollback ||
|
||||
pExpr->iColumn == OE_Abort ||
|
||||
pExpr->iColumn == OE_Fail ){
|
||||
char * msg = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
|
||||
sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn);
|
||||
sqliteDequote(msg);
|
||||
sqliteVdbeChangeP3(v, -1, msg, 0);
|
||||
sqliteFree(msg);
|
||||
} else {
|
||||
assert( pExpr->iColumn == OE_Ignore );
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
|
||||
sqliteVdbeChangeP3(v, -1, "(IGNORE jump)", -1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle INSERT statements in SQLite.
|
||||
**
|
||||
** $Id: insert.c,v 1.60 2002/06/06 18:54:40 drh Exp $
|
||||
** $Id: insert.c,v 1.61 2002/06/11 02:25:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -235,6 +235,7 @@ void sqliteInsert(
|
||||
iCont = sqliteVdbeCurrentAddr(v);
|
||||
}
|
||||
|
||||
endOfLoop = sqliteVdbeMakeLabel(v);
|
||||
if( row_triggers_exist ){
|
||||
|
||||
/* build the new.* reference row */
|
||||
@@ -262,7 +263,7 @@ void sqliteInsert(
|
||||
|
||||
/* Fire BEFORE triggers */
|
||||
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1,
|
||||
onError) ){
|
||||
onError, endOfLoop) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
|
||||
@@ -336,7 +337,6 @@ void sqliteInsert(
|
||||
/* Generate code to check constraints and generate index keys and
|
||||
** do the insertion.
|
||||
*/
|
||||
endOfLoop = sqliteVdbeMakeLabel(v);
|
||||
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
|
||||
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0);
|
||||
|
||||
@@ -358,7 +358,7 @@ void sqliteInsert(
|
||||
|
||||
/* Code AFTER triggers */
|
||||
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1,
|
||||
onError) ){
|
||||
onError, endOfLoop) ){
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
|
17
src/parse.y
17
src/parse.y
@@ -14,7 +14,7 @@
|
||||
** the parser. Lemon will also generate a header file containing
|
||||
** numeric codes for all of the tokens.
|
||||
**
|
||||
** @(#) $Id: parse.y,v 1.72 2002/06/06 19:04:16 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.73 2002/06/11 02:25:42 danielk1977 Exp $
|
||||
*/
|
||||
%token_prefix TK_
|
||||
%token_type {Token}
|
||||
@@ -120,7 +120,7 @@ id(A) ::= ID(X). {A = X;}
|
||||
ABORT AFTER ASC BEFORE BEGIN CASCADE CLUSTER CONFLICT
|
||||
COPY DEFERRED DELIMITERS DESC EACH END EXPLAIN FAIL FOR
|
||||
FULL IGNORE IMMEDIATE INITIALLY INSTEAD MATCH JOIN KEY
|
||||
OF OFFSET PARTIAL PRAGMA REPLACE RESTRICT ROW STATEMENT
|
||||
OF OFFSET PARTIAL PRAGMA RAISE REPLACE RESTRICT ROW STATEMENT
|
||||
TEMP TRIGGER VACUUM VIEW.
|
||||
|
||||
// And "ids" is an identifer-or-string.
|
||||
@@ -756,6 +756,19 @@ trigger_cmd(A) ::= DELETE FROM ids(X) where_opt(Y).
|
||||
// SELECT
|
||||
trigger_cmd(A) ::= select(X). {A = sqliteTriggerSelectStep(X); }
|
||||
|
||||
// The special RAISE expression that may occur in trigger programs
|
||||
expr(A) ::= RAISE(X) LP IGNORE RP(Y). { A = sqliteExpr(TK_RAISE, 0, 0, 0);
|
||||
A->iColumn = OE_Ignore; sqliteExprSpan(A, &X, &Y);}
|
||||
expr(A) ::= RAISE(X) LP ROLLBACK COMMA ids(Z) RP(Y).
|
||||
{ A = sqliteExpr(TK_RAISE, 0, 0, &Z);
|
||||
A->iColumn = OE_Rollback; sqliteExprSpan(A, &X, &Y);}
|
||||
expr(A) ::= RAISE(X) LP ABORT COMMA ids(Z) RP(Y).
|
||||
{ A = sqliteExpr(TK_RAISE, 0, 0, &Z);
|
||||
A->iColumn = OE_Abort; sqliteExprSpan(A, &X, &Y);}
|
||||
expr(A) ::= RAISE(X) LP FAIL COMMA ids(Z) RP(Y).
|
||||
{ A = sqliteExpr(TK_RAISE, 0, 0, &Z);
|
||||
A->iColumn = OE_Fail; sqliteExprSpan(A, &X, &Y);}
|
||||
|
||||
//////////////////////// DROP TRIGGER statement //////////////////////////////
|
||||
cmd ::= DROP TRIGGER ids(X). {
|
||||
sqliteDropTrigger(pParse,&X,0);
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.122 2002/06/09 10:14:19 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.123 2002/06/11 02:25:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqlite.h"
|
||||
#include "hash.h"
|
||||
@@ -764,6 +764,7 @@ struct TriggerStack {
|
||||
int newIdx; /* Index of vdbe cursor to "new" temp table */
|
||||
int oldIdx; /* Index of vdbe cursor to "old" temp table */
|
||||
int orconf; /* Current orconf policy */
|
||||
int ignoreJump; /* where to jump to for a RAISE(IGNORE) */
|
||||
Trigger *pTrigger;
|
||||
|
||||
TriggerStack *pNext;
|
||||
@@ -895,7 +896,8 @@ void sqliteCreateTrigger(Parse*, Token*, int, int, IdList*, Token*,
|
||||
int, Expr*, TriggerStep*, char const*,int);
|
||||
void sqliteDropTrigger(Parse*, Token*, int);
|
||||
int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
|
||||
int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, int);
|
||||
int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int,
|
||||
int, int);
|
||||
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
|
||||
TriggerStep *sqliteTriggerSelectStep(Select*);
|
||||
TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
|
||||
|
@@ -15,7 +15,7 @@
|
||||
** individual tokens and sends those tokens one-by-one over to the
|
||||
** parser for analysis.
|
||||
**
|
||||
** $Id: tokenize.c,v 1.44 2002/06/02 18:19:00 drh Exp $
|
||||
** $Id: tokenize.c,v 1.45 2002/06/11 02:25:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -105,6 +105,7 @@ static Keyword aKeywordTable[] = {
|
||||
{ "PARTIAL", 0, TK_PARTIAL, 0 },
|
||||
{ "PRAGMA", 0, TK_PRAGMA, 0 },
|
||||
{ "PRIMARY", 0, TK_PRIMARY, 0 },
|
||||
{ "RAISE", 0, TK_RAISE, 0 },
|
||||
{ "REFERENCES", 0, TK_REFERENCES, 0 },
|
||||
{ "REPLACE", 0, TK_REPLACE, 0 },
|
||||
{ "RESTRICT", 0, TK_RESTRICT, 0 },
|
||||
|
@@ -480,14 +480,18 @@ static int codeTriggerProgram(
|
||||
pParse->trigStack->orconf = orconf;
|
||||
switch( pTriggerStep->op ){
|
||||
case TK_SELECT: {
|
||||
sqliteSelect(pParse, pTriggerStep->pSelect, SRT_Discard, 0, 0, 0, 0);
|
||||
Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
|
||||
assert(ss);
|
||||
assert(ss->pSrc);
|
||||
sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
|
||||
sqliteSelectDelete(ss);
|
||||
break;
|
||||
}
|
||||
case TK_UPDATE: {
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
|
||||
sqliteUpdate(pParse, &pTriggerStep->target,
|
||||
sqliteExprListDup(pTriggerStep->pExprList),
|
||||
sqliteExprDup(pTriggerStep->pWhere), orconf);
|
||||
sqliteExprListDup(pTriggerStep->pExprList),
|
||||
sqliteExprDup(pTriggerStep->pWhere), orconf);
|
||||
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
|
||||
break;
|
||||
}
|
||||
@@ -543,7 +547,8 @@ int sqliteCodeRowTrigger(
|
||||
Table *pTab, /* The table to code triggers from */
|
||||
int newIdx, /* The indice of the "new" row to access */
|
||||
int oldIdx, /* The indice of the "old" row to access */
|
||||
int orconf /* ON CONFLICT policy */
|
||||
int orconf, /* ON CONFLICT policy */
|
||||
int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */
|
||||
){
|
||||
Trigger * pTrigger;
|
||||
TriggerStack * pTriggerStack;
|
||||
@@ -588,6 +593,7 @@ int sqliteCodeRowTrigger(
|
||||
pTriggerStack->oldIdx = oldIdx;
|
||||
pTriggerStack->pTab = pTab;
|
||||
pTriggerStack->pNext = pParse->trigStack;
|
||||
pTriggerStack->ignoreJump = ignoreJump;
|
||||
pParse->trigStack = pTriggerStack;
|
||||
|
||||
/* code the WHEN clause */
|
||||
@@ -735,14 +741,14 @@ void sqliteViewTriggers(
|
||||
sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
|
||||
|
||||
sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
|
||||
pTab, newIdx, oldIdx, orconf);
|
||||
pTab, newIdx, oldIdx, orconf, endOfLoop);
|
||||
sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
|
||||
pTab, newIdx, oldIdx, orconf);
|
||||
pTab, newIdx, oldIdx, orconf, endOfLoop);
|
||||
}else{
|
||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
|
||||
orconf);
|
||||
orconf, endOfLoop);
|
||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
|
||||
orconf);
|
||||
orconf, endOfLoop);
|
||||
}
|
||||
|
||||
sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
|
||||
|
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.43 2002/05/24 02:04:34 drh Exp $
|
||||
** $Id: update.c,v 1.44 2002/06/11 02:25:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -243,7 +243,7 @@ void sqliteUpdate(
|
||||
sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
|
||||
|
||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
|
||||
newIdx, oldIdx, onError) ){
|
||||
newIdx, oldIdx, onError, addr) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
@@ -350,7 +350,7 @@ void sqliteUpdate(
|
||||
pParse->nTab = base;
|
||||
|
||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,
|
||||
newIdx, oldIdx, onError) ){
|
||||
newIdx, oldIdx, onError, addr) ){
|
||||
goto update_cleanup;
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@
|
||||
** But other routines are also provided to help in building up
|
||||
** a program instruction by instruction.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.154 2002/06/08 23:25:09 drh Exp $
|
||||
** $Id: vdbe.c,v 1.155 2002/06/11 02:25:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -1395,6 +1395,10 @@ case OP_Halt: {
|
||||
if( pOp->p1!=SQLITE_OK ){
|
||||
rc = pOp->p1;
|
||||
errorAction = pOp->p2;
|
||||
if( pOp->p3 ){
|
||||
sqliteSetString(pzErrMsg, pOp->p3, 0);
|
||||
goto cleanup;
|
||||
}
|
||||
goto abort_due_to_error;
|
||||
}else{
|
||||
pc = p->nOp-1;
|
||||
|
163
test/trigger3.test
Normal file
163
test/trigger3.test
Normal file
@@ -0,0 +1,163 @@
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file tests the RAISE() function.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Test that we can cause ROLLBACK, FAIL and ABORT correctly
|
||||
# catchsql { DROP TABLE tbl; }
|
||||
catchsql { CREATE TABLE tbl (a, b, c) }
|
||||
|
||||
execsql {
|
||||
CREATE TRIGGER before_tbl_insert BEFORE INSERT ON tbl BEGIN SELECT CASE
|
||||
WHEN (new.a = 4) THEN RAISE(IGNORE) END;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER after_tbl_insert AFTER INSERT ON tbl BEGIN SELECT CASE
|
||||
WHEN (new.a = 1) THEN RAISE(ABORT, 'Trigger abort')
|
||||
WHEN (new.a = 2) THEN RAISE(FAIL, 'Trigger fail')
|
||||
WHEN (new.a = 3) THEN RAISE(ROLLBACK, 'Trigger rollback') END;
|
||||
END;
|
||||
}
|
||||
# ABORT
|
||||
do_test trig-raise-1.1 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
INSERT INTO tbl VALUES (5, 5, 6);
|
||||
INSERT INTO tbl VALUES (1, 5, 6);
|
||||
}
|
||||
} {1 {Trigger abort}}
|
||||
|
||||
do_test trig-raise-1.2 {
|
||||
execsql {
|
||||
SELECT * FROM tbl;
|
||||
ROLLBACK;
|
||||
}
|
||||
} {5 5 6}
|
||||
|
||||
# FAIL
|
||||
do_test trig-raise-2.1 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
INSERT INTO tbl VALUES (5, 5, 6);
|
||||
INSERT INTO tbl VALUES (2, 5, 6);
|
||||
}
|
||||
} {1 {Trigger fail}}
|
||||
do_test trig-raise-2.2 {
|
||||
execsql {
|
||||
SELECT * FROM tbl;
|
||||
ROLLBACK;
|
||||
}
|
||||
} {5 5 6 2 5 6}
|
||||
# ROLLBACK
|
||||
do_test trig-raise-3.1 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
INSERT INTO tbl VALUES (5, 5, 6);
|
||||
INSERT INTO tbl VALUES (3, 5, 6);
|
||||
}
|
||||
} {1 {Trigger rollback}}
|
||||
do_test trig-raise-3.2 {
|
||||
execsql {
|
||||
SELECT * FROM tbl;
|
||||
ROLLBACK;
|
||||
}
|
||||
} {}
|
||||
# IGNORE
|
||||
do_test trig-raise-4.1 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
INSERT INTO tbl VALUES (5, 5, 6);
|
||||
INSERT INTO tbl VALUES (4, 5, 6);
|
||||
}
|
||||
} {0 {}}
|
||||
do_test trig-raise-4.2 {
|
||||
execsql {
|
||||
SELECT * FROM tbl;
|
||||
ROLLBACK;
|
||||
}
|
||||
} {5 5 6}
|
||||
|
||||
# Check that we can also do RAISE(IGNORE) for UPDATE and DELETE
|
||||
execsql {DROP TABLE tbl;}
|
||||
execsql {CREATE TABLE tbl (a, b, c);}
|
||||
execsql {INSERT INTO tbl VALUES(1, 2, 3);}
|
||||
execsql {INSERT INTO tbl VALUES(4, 5, 6);}
|
||||
execsql {
|
||||
CREATE TRIGGER before_tbl_update BEFORE UPDATE ON tbl BEGIN
|
||||
SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER before_tbl_delete BEFORE DELETE ON tbl BEGIN
|
||||
SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END;
|
||||
END;
|
||||
}
|
||||
do_test trig-raise-5.1 {
|
||||
execsql {
|
||||
UPDATE tbl SET c = 10;
|
||||
SELECT * FROM tbl;
|
||||
}
|
||||
} {1 2 3 4 5 10}
|
||||
do_test trig-raise-5.2 {
|
||||
execsql {
|
||||
DELETE FROM tbl;
|
||||
SELECT * FROM tbl;
|
||||
}
|
||||
} {1 2 3}
|
||||
|
||||
# Check that RAISE(IGNORE) works correctly for nested triggers:
|
||||
execsql {CREATE TABLE tbl2(a, b, c)}
|
||||
execsql {
|
||||
CREATE TRIGGER after_tbl2_insert AFTER INSERT ON tbl2 BEGIN
|
||||
UPDATE tbl SET c = 10;
|
||||
INSERT INTO tbl2 VALUES (new.a, new.b, new.c);
|
||||
END;
|
||||
}
|
||||
do_test trig-raise-6 {
|
||||
execsql {
|
||||
INSERT INTO tbl2 VALUES (1, 2, 3);
|
||||
SELECT * FROM tbl2;
|
||||
SELECT * FROM tbl;
|
||||
}
|
||||
} {1 2 3 1 2 3 1 2 3}
|
||||
|
||||
# Check that things also work for view-triggers
|
||||
execsql {CREATE VIEW tbl_view AS SELECT * FROM tbl}
|
||||
execsql {
|
||||
CREATE TRIGGER tbl_view_insert INSTEAD OF INSERT ON tbl_view BEGIN
|
||||
SELECT CASE WHEN (new.a = 1) THEN RAISE(ROLLBACK, 'View rollback')
|
||||
WHEN (new.a = 2) THEN RAISE(IGNORE)
|
||||
WHEN (new.a = 3) THEN RAISE(ABORT, 'View abort') END;
|
||||
END;
|
||||
}
|
||||
|
||||
do_test trig-raise-7.1 {
|
||||
catchsql {
|
||||
INSERT INTO tbl_view VALUES(1, 2, 3);
|
||||
}
|
||||
} {1 {View rollback}}
|
||||
do_test trig-raise-7.2 {
|
||||
catchsql {
|
||||
INSERT INTO tbl_view VALUES(2, 2, 3);
|
||||
}
|
||||
} {0 {}}
|
||||
do_test trig-raise-7.3 {
|
||||
catchsql {
|
||||
INSERT INTO tbl_view VALUES(3, 2, 3);
|
||||
}
|
||||
} {1 {View abort}}
|
||||
|
||||
catchsql { DROP TABLE tbl; }
|
||||
catchsql { DROP TABLE tbl2; }
|
||||
catchsql { DROP VIEW tbl_view; }
|
||||
|
||||
|
Reference in New Issue
Block a user