]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add RAISE() function, which allows more advanced flow-control in trigger programs...
authordanielk1977 <danielk1977@noemail.net>
Tue, 11 Jun 2002 02:25:40 +0000 (02:25 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Tue, 11 Jun 2002 02:25:40 +0000 (02:25 +0000)
FossilOrigin-Name: d4a2fb10067203a0d49317db747759872e62927e

12 files changed:
manifest
manifest.uuid
src/delete.c
src/expr.c
src/insert.c
src/parse.y
src/sqliteInt.h
src/tokenize.c
src/trigger.c
src/update.c
src/vdbe.c
test/trigger3.test [new file with mode: 0644]

index dac381e6a74cd5b64f86e8cd5b1c568401cc6874..dc20d3b2eff58f5ec503b7ec9f29b603b1031b1d 100644 (file)
--- 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
index d50dd3db15bc0d35e144afbbd6fc9e3bdbd31231..9cf3dde66a6479606bfc35619689e1bf712152f9 100644 (file)
@@ -1 +1 @@
-74d297d97e66452acc5c21048ee8ddf2a90c846f
\ No newline at end of file
+d4a2fb10067203a0d49317db747759872e62927e
\ No newline at end of file
index f48a528ebcc5272144616da8a6a971d5f17438d7..779b572cc42666246b37afbe66187f52126b95a7 100644 (file)
@@ -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 */
index fdd1ad4281cea5a373e9ce068e344074615cf9d8..2d017aa52d85516204528f403584ac6cf0c389e3 100644 (file)
@@ -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;
   }
index 7032da349f49721fca5fb77a6b81e076f6dade35..73ed0563a8ffd0ce4287433f7fc51bdb35f787db 100644 (file)
@@ -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;
     }
   }
index f7f965e9b58c4c9a139f1e274621663419a96310..489c9fd1e02a1310c12a4ed6156b8176c91b7d44 100644 (file)
@@ -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);
index 4e235237521e6be7b646fc44734a8774c02be0f8..85886448c65a87352e5db3e6e15b98076ef4dcea 100644 (file)
@@ -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);
index 5be1b3354561e55c41fa7c0f2892e33337febe4e..ba88164b58c42fc9116fe76a9e8d074e21b64dda 100644 (file)
@@ -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 },
index cf31f1727a4f654b29a6e8ad7a97296670418ffd..95e8fc9f81f76a1a14c73e788e763cb9f53a6f9f 100644 (file)
@@ -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);
index f4f5c66cec2dbb2787cad58130b177b7f08edf0a..fcfe717128863dd4b43906a2c261349b2b698f3f 100644 (file)
@@ -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;
     }
   }
index 16b247caaa2c7ceb454ce7ea1ee599f8fdccd65d..a838c8d061dae5cee73389222ca7997f8da2a564 100644 (file)
@@ -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;
diff --git a/test/trigger3.test b/test/trigger3.test
new file mode 100644 (file)
index 0000000..bb08c71
--- /dev/null
@@ -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; } 
+
+