1
0
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:
danielk1977
2002-06-11 02:25:40 +00:00
parent 28f4b6885b
commit 6f34903e85
12 changed files with 256 additions and 42 deletions

View File

@@ -1,5 +1,5 @@
C Fix\sthe\sspelling\sof\ssqliteRegisterBuiltinFunctions().\s(CVS\s613) C Add\sRAISE()\sfunction,\swhich\sallows\smore\sadvanced\sflow-control\sin\strigger\sprograms\s(ticket\s#55)\s(CVS\s614)
D 2002-06-09T10:14:19 D 2002-06-11T02:25:41
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495 F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -21,38 +21,38 @@ F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
F src/btree.c 8b86be8f234c1c5dab3186f69cee2544ec9d7257 F src/btree.c 8b86be8f234c1c5dab3186f69cee2544ec9d7257
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
F src/build.c 36e42718a7a94f554ea39508993378482f5335c7 F src/build.c 36e42718a7a94f554ea39508993378482f5335c7
F src/delete.c a2b098cbbf518e6b641847e26de85827793bc523 F src/delete.c 15789fc723a6776309945b13a79f9a0e78275fc0
F src/encode.c 346b12b46148506c32038524b95c4631ab46d760 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
F src/expr.c cd2e3311c84533fad19336d3bbfdc3be3400d377 F src/expr.c 2dcfcd0b032206954a307d7e2731bf070d58835b
F src/func.c b8d0fd3011f53ea0e46b6bab857612eb36b5d1ea F src/func.c b8d0fd3011f53ea0e46b6bab857612eb36b5d1ea
F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72 F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c 4b0bd94296fea46ef1b2ed8bfd05e12a38ce2c90 F src/insert.c 3a90bb98cd246f88cc26c44f24d5b47760bc5cba
F src/main.c 3a4e53122c0b0d0a5719b73fa9ec642761291ba1 F src/main.c 3a4e53122c0b0d0a5719b73fa9ec642761291ba1
F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
F src/os.c 9cc40c5384baba4a85e160e67807645ca98ba3cc F src/os.c 9cc40c5384baba4a85e160e67807645ca98ba3cc
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10 F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
F src/pager.c 1e41053c949cea1f09d8dafada5fe8f90785e650 F src/pager.c 1e41053c949cea1f09d8dafada5fe8f90785e650
F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
F src/parse.y 3b4989cb81ab2f441ef6c7cbb203829838eb299e F src/parse.y 42920305d49666419358b469e4ec522ac867a39f
F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c 1d5cb1ae0bb3376bedfde7ae22e6e927e4d0b5e2 F src/select.c 1d5cb1ae0bb3376bedfde7ae22e6e927e4d0b5e2
F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15 F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
F src/sqliteInt.h 470056bd21902e15f5ac459f09f64a717657332b F src/sqliteInt.h 93e0ad1b37658391de6f211f65e7924a2064aa4d
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1 F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730 F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
F src/tokenize.c 35c63867d03fcaf81fe520f8d8206981d0c7270e F src/tokenize.c 890ca022d45f1798dadc300a798951597428853e
F src/trigger.c d02f8e3510c7c2ad948a0e8c3bb0cca8adaf80c5 F src/trigger.c 21342af6ac031fece39c8fc6eabd1739ca5327c1
F src/update.c f68375173bf5338cae3e97012708e10f206aedd9 F src/update.c 05431e23a9c83502fd7911e771c8366fc2b90b4c
F src/util.c 7cf46b5612f5d12601c697374b9c6b38b2332ce8 F src/util.c 7cf46b5612f5d12601c697374b9c6b38b2332ce8
F src/vdbe.c b315d7ad5086164bb8d8aee8bc9edeafcb68b8ea F src/vdbe.c 836f2c4f823c94c3c3454125d9ba9283e8b22dda
F src/vdbe.h 1742d6f8b40f40879475b4c41cf4f9980ceb0e21 F src/vdbe.h 1742d6f8b40f40879475b4c41cf4f9980ceb0e21
F src/where.c b7c653054d4941d17f3112776ebcaf00d9613cb7 F src/where.c b7c653054d4941d17f3112776ebcaf00d9613cb7
F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1 F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
@@ -103,6 +103,7 @@ F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6
F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee
F test/trigger1.test bb63749fa8a395a60541100607d86381604b7194 F test/trigger1.test bb63749fa8a395a60541100607d86381604b7194
F test/trigger2.test c12759a0d7ba6488d9d24c96a1352ddee995c1ab F test/trigger2.test c12759a0d7ba6488d9d24c96a1352ddee995c1ab
F test/trigger3.test b4aca721ba92956c7fa16bb0158254f3c1b73efa
F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44 F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44
F test/update.test a0aa0bf83e6fad8407d0e4ad25ebb09b513f5bf4 F test/update.test a0aa0bf83e6fad8407d0e4ad25ebb09b513f5bf4
F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
@@ -136,7 +137,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
P 2a710e18176c486525f0abb06644a511a2cd1d7a P 74d297d97e66452acc5c21048ee8ddf2a90c846f
R e2c1bfb01c44cc3c89a1b280cee79956 R 5c5b230772c3601ecd48523835df13e1
U drh U danielk1977
Z bd401167cac426bd14d74027f33bb932 Z a3a6e123a401efc55c5086c35a87d954

View File

@@ -1 +1 @@
74d297d97e66452acc5c21048ee8ddf2a90c846f d4a2fb10067203a0d49317db747759872e62927e

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements. ** 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" #include "sqliteInt.h"
@@ -237,7 +237,8 @@ void sqliteDeleteFrom(
sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0);
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, 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 /* Open cursors for the table we are deleting from and all its
@@ -271,7 +272,8 @@ void sqliteDeleteFrom(
} }
sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteVdbeAddOp(v, OP_Close, base, 0);
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, 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 */ /* End of the delete loop */

View File

@@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and ** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite. ** 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 "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@@ -204,7 +204,7 @@ SrcList *sqliteSrcListDup(SrcList *p){
if( pNew==0 ) return 0; if( pNew==0 ) return 0;
pNew->nSrc = p->nSrc; pNew->nSrc = p->nSrc;
pNew->a = sqliteMalloc( p->nSrc*sizeof(p->a[0]) ); 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++){ for(i=0; i<p->nSrc; i++){
pNew->a[i].zName = sqliteStrDup(p->a[i].zName); pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias); 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); sqliteVdbeAddOp(v, OP_String, 0, 0);
} }
sqliteVdbeResolveLabel(v, expr_end_label); 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; break;
} }

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite. ** 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" #include "sqliteInt.h"
@@ -235,6 +235,7 @@ void sqliteInsert(
iCont = sqliteVdbeCurrentAddr(v); iCont = sqliteVdbeCurrentAddr(v);
} }
endOfLoop = sqliteVdbeMakeLabel(v);
if( row_triggers_exist ){ if( row_triggers_exist ){
/* build the new.* reference row */ /* build the new.* reference row */
@@ -262,7 +263,7 @@ void sqliteInsert(
/* Fire BEFORE triggers */ /* Fire BEFORE triggers */
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1, if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1,
onError) ){ onError, endOfLoop) ){
goto insert_cleanup; goto insert_cleanup;
} }
@@ -336,7 +337,6 @@ void sqliteInsert(
/* Generate code to check constraints and generate index keys and /* Generate code to check constraints and generate index keys and
** do the insertion. ** do the insertion.
*/ */
endOfLoop = sqliteVdbeMakeLabel(v);
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop); sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0); sqliteCompleteInsertion(pParse, pTab, base, 0,0,0);
@@ -358,7 +358,7 @@ void sqliteInsert(
/* Code AFTER triggers */ /* Code AFTER triggers */
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1, if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1,
onError) ){ onError, endOfLoop) ){
goto insert_cleanup; goto insert_cleanup;
} }
} }

View File

@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing ** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens. ** 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_prefix TK_
%token_type {Token} %token_type {Token}
@@ -120,7 +120,7 @@ id(A) ::= ID(X). {A = X;}
ABORT AFTER ASC BEFORE BEGIN CASCADE CLUSTER CONFLICT ABORT AFTER ASC BEFORE BEGIN CASCADE CLUSTER CONFLICT
COPY DEFERRED DELIMITERS DESC EACH END EXPLAIN FAIL FOR COPY DEFERRED DELIMITERS DESC EACH END EXPLAIN FAIL FOR
FULL IGNORE IMMEDIATE INITIALLY INSTEAD MATCH JOIN KEY 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. TEMP TRIGGER VACUUM VIEW.
// And "ids" is an identifer-or-string. // And "ids" is an identifer-or-string.
@@ -756,6 +756,19 @@ trigger_cmd(A) ::= DELETE FROM ids(X) where_opt(Y).
// SELECT // SELECT
trigger_cmd(A) ::= select(X). {A = sqliteTriggerSelectStep(X); } 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 ////////////////////////////// //////////////////////// DROP TRIGGER statement //////////////////////////////
cmd ::= DROP TRIGGER ids(X). { cmd ::= DROP TRIGGER ids(X). {
sqliteDropTrigger(pParse,&X,0); sqliteDropTrigger(pParse,&X,0);

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** 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 "sqlite.h"
#include "hash.h" #include "hash.h"
@@ -764,6 +764,7 @@ struct TriggerStack {
int newIdx; /* Index of vdbe cursor to "new" temp table */ int newIdx; /* Index of vdbe cursor to "new" temp table */
int oldIdx; /* Index of vdbe cursor to "old" temp table */ int oldIdx; /* Index of vdbe cursor to "old" temp table */
int orconf; /* Current orconf policy */ int orconf; /* Current orconf policy */
int ignoreJump; /* where to jump to for a RAISE(IGNORE) */
Trigger *pTrigger; Trigger *pTrigger;
TriggerStack *pNext; TriggerStack *pNext;
@@ -895,7 +896,8 @@ void sqliteCreateTrigger(Parse*, Token*, int, int, IdList*, Token*,
int, Expr*, TriggerStep*, char const*,int); int, Expr*, TriggerStep*, char const*,int);
void sqliteDropTrigger(Parse*, Token*, int); void sqliteDropTrigger(Parse*, Token*, int);
int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*); 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*); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
TriggerStep *sqliteTriggerSelectStep(Select*); TriggerStep *sqliteTriggerSelectStep(Select*);
TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int); TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);

View File

@@ -15,7 +15,7 @@
** individual tokens and sends those tokens one-by-one over to the ** individual tokens and sends those tokens one-by-one over to the
** parser for analysis. ** 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 "sqliteInt.h"
#include "os.h" #include "os.h"
@@ -105,6 +105,7 @@ static Keyword aKeywordTable[] = {
{ "PARTIAL", 0, TK_PARTIAL, 0 }, { "PARTIAL", 0, TK_PARTIAL, 0 },
{ "PRAGMA", 0, TK_PRAGMA, 0 }, { "PRAGMA", 0, TK_PRAGMA, 0 },
{ "PRIMARY", 0, TK_PRIMARY, 0 }, { "PRIMARY", 0, TK_PRIMARY, 0 },
{ "RAISE", 0, TK_RAISE, 0 },
{ "REFERENCES", 0, TK_REFERENCES, 0 }, { "REFERENCES", 0, TK_REFERENCES, 0 },
{ "REPLACE", 0, TK_REPLACE, 0 }, { "REPLACE", 0, TK_REPLACE, 0 },
{ "RESTRICT", 0, TK_RESTRICT, 0 }, { "RESTRICT", 0, TK_RESTRICT, 0 },

View File

@@ -480,7 +480,11 @@ static int codeTriggerProgram(
pParse->trigStack->orconf = orconf; pParse->trigStack->orconf = orconf;
switch( pTriggerStep->op ){ switch( pTriggerStep->op ){
case TK_SELECT: { 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; break;
} }
case TK_UPDATE: { case TK_UPDATE: {
@@ -543,7 +547,8 @@ int sqliteCodeRowTrigger(
Table *pTab, /* The table to code triggers from */ Table *pTab, /* The table to code triggers from */
int newIdx, /* The indice of the "new" row to access */ int newIdx, /* The indice of the "new" row to access */
int oldIdx, /* The indice of the "old" 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; Trigger * pTrigger;
TriggerStack * pTriggerStack; TriggerStack * pTriggerStack;
@@ -588,6 +593,7 @@ int sqliteCodeRowTrigger(
pTriggerStack->oldIdx = oldIdx; pTriggerStack->oldIdx = oldIdx;
pTriggerStack->pTab = pTab; pTriggerStack->pTab = pTab;
pTriggerStack->pNext = pParse->trigStack; pTriggerStack->pNext = pParse->trigStack;
pTriggerStack->ignoreJump = ignoreJump;
pParse->trigStack = pTriggerStack; pParse->trigStack = pTriggerStack;
/* code the WHEN clause */ /* code the WHEN clause */
@@ -735,14 +741,14 @@ void sqliteViewTriggers(
sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
pTab, newIdx, oldIdx, orconf); pTab, newIdx, oldIdx, orconf, endOfLoop);
sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
pTab, newIdx, oldIdx, orconf); pTab, newIdx, oldIdx, orconf, endOfLoop);
}else{ }else{
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx, sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
orconf); orconf, endOfLoop);
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx, sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
orconf); orconf, endOfLoop);
} }
sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop); sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle UPDATE statements. ** 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" #include "sqliteInt.h"
@@ -243,7 +243,7 @@ void sqliteUpdate(
sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
newIdx, oldIdx, onError) ){ newIdx, oldIdx, onError, addr) ){
goto update_cleanup; goto update_cleanup;
} }
} }
@@ -350,7 +350,7 @@ void sqliteUpdate(
pParse->nTab = base; pParse->nTab = base;
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,
newIdx, oldIdx, onError) ){ newIdx, oldIdx, onError, addr) ){
goto update_cleanup; goto update_cleanup;
} }
} }

View File

@@ -30,7 +30,7 @@
** But other routines are also provided to help in building up ** But other routines are also provided to help in building up
** a program instruction by instruction. ** 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 "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@@ -1395,6 +1395,10 @@ case OP_Halt: {
if( pOp->p1!=SQLITE_OK ){ if( pOp->p1!=SQLITE_OK ){
rc = pOp->p1; rc = pOp->p1;
errorAction = pOp->p2; errorAction = pOp->p2;
if( pOp->p3 ){
sqliteSetString(pzErrMsg, pOp->p3, 0);
goto cleanup;
}
goto abort_due_to_error; goto abort_due_to_error;
}else{ }else{
pc = p->nOp-1; pc = p->nOp-1;

163
test/trigger3.test Normal file
View 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; }