diff --git a/manifest b/manifest index dac381e6a7..dc20d3b2ef 100644 --- a/manifest +++ b/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 diff --git a/manifest.uuid b/manifest.uuid index d50dd3db15..9cf3dde66a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -74d297d97e66452acc5c21048ee8ddf2a90c846f \ No newline at end of file +d4a2fb10067203a0d49317db747759872e62927e \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index f48a528ebc..779b572cc4 100644 --- a/src/delete.c +++ b/src/delete.c @@ -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 */ diff --git a/src/expr.c b/src/expr.c index fdd1ad4281..2d017aa52d 100644 --- a/src/expr.c +++ b/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 @@ -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; inSrc; 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; } diff --git a/src/insert.c b/src/insert.c index 7032da349f..73ed0563a8 100644 --- a/src/insert.c +++ b/src/insert.c @@ -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; } } diff --git a/src/parse.y b/src/parse.y index f7f965e9b5..489c9fd1e0 100644 --- a/src/parse.y +++ b/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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4e23523752..85886448c6 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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); diff --git a/src/tokenize.c b/src/tokenize.c index 5be1b33545..ba88164b58 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -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 }, diff --git a/src/trigger.c b/src/trigger.c index cf31f1727a..95e8fc9f81 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -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); diff --git a/src/update.c b/src/update.c index f4f5c66cec..fcfe717128 100644 --- a/src/update.c +++ b/src/update.c @@ -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; } } diff --git a/src/vdbe.c b/src/vdbe.c index 16b247caaa..a838c8d061 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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 @@ -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; diff --git a/test/trigger3.test b/test/trigger3.test new file mode 100644 index 0000000000..bb08c71bed --- /dev/null +++ b/test/trigger3.test @@ -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; } + +