/ Check-in [d4a2fb10]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add RAISE() function, which allows more advanced flow-control in trigger programs (ticket #55) (CVS 614)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:d4a2fb10067203a0d49317db747759872e62927e
User & Date: danielk1977 2002-06-11 02:25:41
Context
2002-06-11
22:33
Documentation that should have been checked in along with checkin (614) (CVS 615) check-in: 10da1361 user: danielk1977 tags: trunk
02:25
Add RAISE() function, which allows more advanced flow-control in trigger programs (ticket #55) (CVS 614) check-in: d4a2fb10 user: danielk1977 tags: trunk
2002-06-09
10:14
Fix the spelling of sqliteRegisterBuiltinFunctions(). (CVS 613) check-in: 74d297d9 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/delete.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains C code routines that are called by the parser
    13     13   ** to handle DELETE FROM statements.
    14     14   **
    15         -** $Id: delete.c,v 1.36 2002/05/24 02:04:33 drh Exp $
           15  +** $Id: delete.c,v 1.37 2002/06/11 02:25:41 danielk1977 Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   
    19     19   
    20     20   /*
    21     21   ** Given a table name, find the corresponding table and make sure the
    22     22   ** table is writeable.  Generate an error and return NULL if not.  If
................................................................................
   233    233         }
   234    234         sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
   235    235         sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
   236    236         sqliteVdbeAddOp(v, OP_Close, base, 0);
   237    237         sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0);
   238    238   
   239    239         sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, 
   240         -          oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
          240  +          oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
          241  +	  addr);
   241    242       }
   242    243   
   243    244       /* Open cursors for the table we are deleting from and all its
   244    245       ** indices.  If there are row triggers, this happens inside the
   245    246       ** OP_ListRead loop because the cursor have to all be closed
   246    247       ** before the trigger fires.  If there are no row triggers, the
   247    248       ** cursors are opened only once on the outside the loop.
................................................................................
   267    268       */
   268    269       if( row_triggers_exist ){
   269    270         for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
   270    271           sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);
   271    272         }
   272    273         sqliteVdbeAddOp(v, OP_Close, base, 0);
   273    274         sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, 
   274         -          oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
          275  +          oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
          276  +	  addr);
   275    277       }
   276    278   
   277    279       /* End of the delete loop */
   278    280       sqliteVdbeAddOp(v, OP_Goto, 0, addr);
   279    281       sqliteVdbeResolveLabel(v, end);
   280    282       sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
   281    283   

Changes to src/expr.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains routines used for analyzing expressions and
    13     13   ** for generating VDBE code that evaluates expressions in SQLite.
    14     14   **
    15         -** $Id: expr.c,v 1.70 2002/06/09 01:16:01 drh Exp $
           15  +** $Id: expr.c,v 1.71 2002/06/11 02:25:41 danielk1977 Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   #include <ctype.h>
    19     19   
    20     20   /*
    21     21   ** Construct a new expression node and return a pointer to it.  Memory
    22     22   ** for this node is obtained from sqliteMalloc().  The calling function
................................................................................
   200    200     SrcList *pNew;
   201    201     int i;
   202    202     if( p==0 ) return 0;
   203    203     pNew = sqliteMalloc( sizeof(*pNew) );
   204    204     if( pNew==0 ) return 0;
   205    205     pNew->nSrc = p->nSrc;
   206    206     pNew->a = sqliteMalloc( p->nSrc*sizeof(p->a[0]) );
   207         -  if( pNew->a==0 ) return 0;
          207  +  if( pNew->a==0 && p->nSrc != 0 ) return 0;
   208    208     for(i=0; i<p->nSrc; i++){
   209    209       pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
   210    210       pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias);
   211    211       pNew->a[i].jointype = p->a[i].jointype;
   212    212       pNew->a[i].pTab = 0;
   213    213       pNew->a[i].pSelect = sqliteSelectDup(p->a[i].pSelect);
   214    214       pNew->a[i].pOn = sqliteExprDup(p->a[i].pOn);
................................................................................
  1010   1010         }
  1011   1011         if( pExpr->pRight ){
  1012   1012           sqliteExprCode(pParse, pExpr->pRight);
  1013   1013         }else{
  1014   1014           sqliteVdbeAddOp(v, OP_String, 0, 0);
  1015   1015         }
  1016   1016         sqliteVdbeResolveLabel(v, expr_end_label);
         1017  +      break;
         1018  +    }
         1019  +    case TK_RAISE: {
         1020  +      if( !pParse->trigStack ){
         1021  +        sqliteSetNString(&pParse->zErrMsg, 
         1022  +		"RAISE() may only be used within a trigger-program", -1, 0);
         1023  +        pParse->nErr++;
         1024  +	return;
         1025  +      }
         1026  +      if( pExpr->iColumn == OE_Rollback ||
         1027  +	  pExpr->iColumn == OE_Abort ||
         1028  +	  pExpr->iColumn == OE_Fail ){
         1029  +	  char * msg = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
         1030  +	  sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn);
         1031  +	  sqliteDequote(msg);
         1032  +	  sqliteVdbeChangeP3(v, -1, msg, 0);
         1033  +	  sqliteFree(msg);
         1034  +      } else {
         1035  +	  assert( pExpr->iColumn == OE_Ignore );
         1036  +	  sqliteVdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
         1037  +	  sqliteVdbeChangeP3(v, -1, "(IGNORE jump)", -1);
         1038  +      }
  1017   1039       }
  1018   1040       break;
  1019   1041     }
  1020   1042   }
  1021   1043   
  1022   1044   /*
  1023   1045   ** Generate code for a boolean expression such that a jump is made

Changes to src/insert.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains C code routines that are called by the parser
    13     13   ** to handle INSERT statements in SQLite.
    14     14   **
    15         -** $Id: insert.c,v 1.60 2002/06/06 18:54:40 drh Exp $
           15  +** $Id: insert.c,v 1.61 2002/06/11 02:25:42 danielk1977 Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   
    19     19   /*
    20     20   ** This routine is call to handle SQL of the following forms:
    21     21   **
    22     22   **    insert into TABLE (IDLIST) values(EXPRLIST)
................................................................................
   231    231     */
   232    232     if( srcTab>=0 ){
   233    233       iBreak = sqliteVdbeMakeLabel(v);
   234    234       sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak);
   235    235       iCont = sqliteVdbeCurrentAddr(v);
   236    236     }
   237    237   
          238  +  endOfLoop = sqliteVdbeMakeLabel(v);
   238    239     if( row_triggers_exist ){
   239    240   
   240    241       /* build the new.* reference row */
   241    242       sqliteVdbeAddOp(v, OP_Integer, 13, 0);
   242    243       for(i=0; i<pTab->nCol; i++){
   243    244         if( pColumn==0 ){
   244    245           j = i;
................................................................................
   258    259       }
   259    260       sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
   260    261       sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
   261    262       sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
   262    263   
   263    264       /* Fire BEFORE triggers */
   264    265       if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1, 
   265         -        onError) ){
          266  +        onError, endOfLoop) ){
   266    267         goto insert_cleanup;
   267    268       }
   268    269   
   269    270       /* Open the tables and indices for the INSERT */
   270    271       if( !pTab->pSelect ){
   271    272         base = pParse->nTab;
   272    273         openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
................................................................................
   332    333           sqliteExprCode(pParse, pList->a[j].pExpr);
   333    334         }
   334    335       }
   335    336   
   336    337       /* Generate code to check constraints and generate index keys and
   337    338       ** do the insertion.
   338    339       */
   339         -    endOfLoop = sqliteVdbeMakeLabel(v);
   340    340       sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
   341    341       sqliteCompleteInsertion(pParse, pTab, base, 0,0,0);
   342    342   
   343    343       /* Update the count of rows that are inserted
   344    344       */
   345    345       if( (db->flags & SQLITE_CountRows)!=0 && !pParse->trigStack){
   346    346         sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
................................................................................
   354    354         for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
   355    355           sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
   356    356         }
   357    357       }
   358    358   
   359    359       /* Code AFTER triggers */
   360    360       if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1, 
   361         -          onError) ){
          361  +          onError, endOfLoop) ){
   362    362         goto insert_cleanup;
   363    363       }
   364    364     }
   365    365   
   366    366     /* The bottom of the loop, if the data source is a SELECT statement
   367    367     */
   368    368     sqliteVdbeResolveLabel(v, endOfLoop);

Changes to src/parse.y.

    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains SQLite's grammar for SQL.  Process this file
    13     13   ** using the lemon parser generator to generate C code that runs
    14     14   ** the parser.  Lemon will also generate a header file containing
    15     15   ** numeric codes for all of the tokens.
    16     16   **
    17         -** @(#) $Id: parse.y,v 1.72 2002/06/06 19:04:16 drh Exp $
           17  +** @(#) $Id: parse.y,v 1.73 2002/06/11 02:25:42 danielk1977 Exp $
    18     18   */
    19     19   %token_prefix TK_
    20     20   %token_type {Token}
    21     21   %default_type {Token}
    22     22   %extra_argument {Parse *pParse}
    23     23   %syntax_error {
    24     24     sqliteSetString(&pParse->zErrMsg,"syntax error",0);
................................................................................
   116    116   // fallback to ID if they will not parse as their original value.
   117    117   // This obviates the need for the "id" nonterminal.
   118    118   //
   119    119   %fallback ID 
   120    120     ABORT AFTER ASC BEFORE BEGIN CASCADE CLUSTER CONFLICT
   121    121     COPY DEFERRED DELIMITERS DESC EACH END EXPLAIN FAIL FOR
   122    122     FULL IGNORE IMMEDIATE INITIALLY INSTEAD MATCH JOIN KEY
   123         -  OF OFFSET PARTIAL PRAGMA REPLACE RESTRICT ROW STATEMENT
          123  +  OF OFFSET PARTIAL PRAGMA RAISE REPLACE RESTRICT ROW STATEMENT
   124    124     TEMP TRIGGER VACUUM VIEW.
   125    125   
   126    126   // And "ids" is an identifer-or-string.
   127    127   //
   128    128   %type ids {Token}
   129    129   ids(A) ::= id(X).        {A = X;}
   130    130   ids(A) ::= STRING(X).    {A = X;}
................................................................................
   751    751   
   752    752   // DELETE
   753    753   trigger_cmd(A) ::= DELETE FROM ids(X) where_opt(Y).
   754    754                  {A = sqliteTriggerDeleteStep(&X, Y);}
   755    755   
   756    756   // SELECT
   757    757   trigger_cmd(A) ::= select(X).  {A = sqliteTriggerSelectStep(X); }
          758  +
          759  +// The special RAISE expression that may occur in trigger programs
          760  +expr(A) ::= RAISE(X) LP IGNORE RP(Y).  { A = sqliteExpr(TK_RAISE, 0, 0, 0); 
          761  +    A->iColumn = OE_Ignore; sqliteExprSpan(A, &X, &Y);}
          762  +expr(A) ::= RAISE(X) LP ROLLBACK COMMA ids(Z) RP(Y).  
          763  +{ A = sqliteExpr(TK_RAISE, 0, 0, &Z); 
          764  +    A->iColumn = OE_Rollback; sqliteExprSpan(A, &X, &Y);}
          765  +expr(A) ::= RAISE(X) LP ABORT COMMA ids(Z) RP(Y).  
          766  +{ A = sqliteExpr(TK_RAISE, 0, 0, &Z); 
          767  +    A->iColumn = OE_Abort; sqliteExprSpan(A, &X, &Y);}
          768  +expr(A) ::= RAISE(X) LP FAIL COMMA ids(Z) RP(Y).  
          769  +{ A = sqliteExpr(TK_RAISE, 0, 0, &Z); 
          770  +    A->iColumn = OE_Fail; sqliteExprSpan(A, &X, &Y);}
   758    771   
   759    772   ////////////////////////  DROP TRIGGER statement //////////////////////////////
   760    773   cmd ::= DROP TRIGGER ids(X). {
   761    774       sqliteDropTrigger(pParse,&X,0);
   762    775   }

Changes to src/sqliteInt.h.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Internal interface definitions for SQLite.
    13     13   **
    14         -** @(#) $Id: sqliteInt.h,v 1.122 2002/06/09 10:14:19 drh Exp $
           14  +** @(#) $Id: sqliteInt.h,v 1.123 2002/06/11 02:25:42 danielk1977 Exp $
    15     15   */
    16     16   #include "sqlite.h"
    17     17   #include "hash.h"
    18     18   #include "vdbe.h"
    19     19   #include "parse.h"
    20     20   #include "btree.h"
    21     21   #include <stdio.h>
................................................................................
   760    760    * recursively. If this condition is detected, the nested trigger is not coded.
   761    761    */
   762    762   struct TriggerStack {
   763    763     Table *pTab;         /* Table that triggers are currently being coded on */
   764    764     int newIdx;          /* Index of vdbe cursor to "new" temp table */
   765    765     int oldIdx;          /* Index of vdbe cursor to "old" temp table */
   766    766     int orconf;          /* Current orconf policy */
          767  +  int ignoreJump;      /* where to jump to for a RAISE(IGNORE) */
   767    768     Trigger *pTrigger;
   768    769   
   769    770     TriggerStack *pNext;
   770    771   };
   771    772   
   772    773   /*
   773    774    * This global flag is set for performance testing of triggers. When it is set
................................................................................
   891    892   int sqliteSafetyOff(sqlite*);
   892    893   int sqliteSafetyCheck(sqlite*);
   893    894   void sqliteChangeCookie(sqlite *);
   894    895   void sqliteCreateTrigger(Parse*, Token*, int, int, IdList*, Token*, 
   895    896                            int, Expr*, TriggerStep*, char const*,int);
   896    897   void sqliteDropTrigger(Parse*, Token*, int);
   897    898   int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
   898         -int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, int);
          899  +int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, 
          900  +                         int, int);
   899    901   void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
   900    902   TriggerStep *sqliteTriggerSelectStep(Select*);
   901    903   TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
   902    904   TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int);
   903    905   TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*);
   904    906   void sqliteDeleteTrigger(Trigger*);
   905    907   int sqliteJoinType(Parse*, Token*, Token*, Token*);

Changes to src/tokenize.c.

    11     11   *************************************************************************
    12     12   ** An tokenizer for SQL
    13     13   **
    14     14   ** This file contains C code that splits an SQL input string up into
    15     15   ** individual tokens and sends those tokens one-by-one over to the
    16     16   ** parser for analysis.
    17     17   **
    18         -** $Id: tokenize.c,v 1.44 2002/06/02 18:19:00 drh Exp $
           18  +** $Id: tokenize.c,v 1.45 2002/06/11 02:25:42 danielk1977 Exp $
    19     19   */
    20     20   #include "sqliteInt.h"
    21     21   #include "os.h"
    22     22   #include <ctype.h>
    23     23   #include <stdlib.h>
    24     24   
    25     25   /*
................................................................................
   101    101     { "OFFSET",            0, TK_OFFSET,           0 },
   102    102     { "ON",                0, TK_ON,               0 },
   103    103     { "OR",                0, TK_OR,               0 },
   104    104     { "ORDER",             0, TK_ORDER,            0 },
   105    105     { "PARTIAL",           0, TK_PARTIAL,          0 },
   106    106     { "PRAGMA",            0, TK_PRAGMA,           0 },
   107    107     { "PRIMARY",           0, TK_PRIMARY,          0 },
          108  +  { "RAISE",             0, TK_RAISE,            0 },
   108    109     { "REFERENCES",        0, TK_REFERENCES,       0 },
   109    110     { "REPLACE",           0, TK_REPLACE,          0 },
   110    111     { "RESTRICT",          0, TK_RESTRICT,         0 },
   111    112     { "ROLLBACK",          0, TK_ROLLBACK,         0 },
   112    113     { "ROW",               0, TK_ROW,              0 },
   113    114     { "SELECT",            0, TK_SELECT,           0 },
   114    115     { "SET",               0, TK_SET,              0 },

Changes to src/trigger.c.

   476    476   
   477    477     while( pTriggerStep ){
   478    478       int saveNTab = pParse->nTab;
   479    479       orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
   480    480       pParse->trigStack->orconf = orconf;
   481    481       switch( pTriggerStep->op ){
   482    482         case TK_SELECT: {
   483         -	sqliteSelect(pParse, pTriggerStep->pSelect, SRT_Discard, 0, 0, 0, 0);
          483  +	Select * ss = sqliteSelectDup(pTriggerStep->pSelect);		  
          484  +	assert(ss);
          485  +	assert(ss->pSrc);
          486  +	sqliteSelect(pParse, ss, SRT_Discard, 0, 0, 0, 0);
          487  +	sqliteSelectDelete(ss);
   484    488   	break;
   485    489         }
   486    490         case TK_UPDATE: {
   487    491           sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
   488    492           sqliteUpdate(pParse, &pTriggerStep->target, 
   489         -        sqliteExprListDup(pTriggerStep->pExprList), 
   490         -        sqliteExprDup(pTriggerStep->pWhere), orconf);
          493  +		sqliteExprListDup(pTriggerStep->pExprList), 
          494  +		sqliteExprDup(pTriggerStep->pWhere), orconf);
   491    495           sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
   492    496           break;
   493    497         }
   494    498         case TK_INSERT: {
   495    499           sqliteInsert(pParse, &pTriggerStep->target, 
   496    500           sqliteExprListDup(pTriggerStep->pExprList), 
   497    501           sqliteSelectDup(pTriggerStep->pSelect), 
................................................................................
   539    543     Parse *pParse,       /* Parse context */
   540    544     int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
   541    545     ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
   542    546     int tr_tm,           /* One of TK_BEFORE, TK_AFTER */
   543    547     Table *pTab,         /* The table to code triggers from */
   544    548     int newIdx,          /* The indice of the "new" row to access */
   545    549     int oldIdx,          /* The indice of the "old" row to access */
   546         -  int orconf           /* ON CONFLICT policy */
          550  +  int orconf,          /* ON CONFLICT policy */
          551  +  int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
   547    552   ){
   548    553     Trigger * pTrigger;
   549    554     TriggerStack * pTriggerStack;
   550    555   
   551    556     assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
   552    557     assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);
   553    558   
................................................................................
   584    589   
   585    590         /* Push an entry on to the trigger stack */
   586    591         pTriggerStack->pTrigger = pTrigger;
   587    592         pTriggerStack->newIdx = newIdx;
   588    593         pTriggerStack->oldIdx = oldIdx;
   589    594         pTriggerStack->pTab = pTab;
   590    595         pTriggerStack->pNext = pParse->trigStack;
          596  +      pTriggerStack->ignoreJump = ignoreJump;
   591    597         pParse->trigStack = pTriggerStack;
   592    598   
   593    599         /* code the WHEN clause */
   594    600         endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
   595    601         whenExpr = sqliteExprDup(pTrigger->pWhen);
   596    602         if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){
   597    603           pParse->trigStack = pParse->trigStack->pNext;
................................................................................
   731    737       }
   732    738   
   733    739       sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
   734    740       sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
   735    741       sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
   736    742   
   737    743       sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, 
   738         -        pTab, newIdx, oldIdx, orconf);
          744  +        pTab, newIdx, oldIdx, orconf, endOfLoop);
   739    745       sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, 
   740         -        pTab, newIdx, oldIdx, orconf);
          746  +        pTab, newIdx, oldIdx, orconf, endOfLoop);
   741    747     }else{
   742    748       sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx, 
   743         -        orconf);
          749  +        orconf, endOfLoop);
   744    750       sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx, 
   745         -        orconf);
          751  +        orconf, endOfLoop);
   746    752     }
   747    753   
   748    754     sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
   749    755   
   750    756     sqliteVdbeResolveLabel(v, endOfLoop);
   751    757     sqliteEndWriteOperation(pParse);
   752    758   

Changes to src/update.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains C code routines that are called by the parser
    13     13   ** to handle UPDATE statements.
    14     14   **
    15         -** $Id: update.c,v 1.43 2002/05/24 02:04:34 drh Exp $
           15  +** $Id: update.c,v 1.44 2002/06/11 02:25:42 danielk1977 Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   
    19     19   /*
    20     20   ** Process an UPDATE statement.
    21     21   */
    22     22   void sqliteUpdate(
................................................................................
   239    239       sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
   240    240       sqliteVdbeAddOp(v, OP_Close, base, 0);
   241    241   
   242    242       sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0);
   243    243       sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
   244    244   
   245    245       if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, 
   246         -          newIdx, oldIdx, onError) ){
          246  +          newIdx, oldIdx, onError, addr) ){
   247    247         goto update_cleanup;
   248    248       }
   249    249     }
   250    250   
   251    251     /* Rewind the list of records that need to be updated and
   252    252     ** open every index that needs updating.  Note that if any
   253    253     ** index could potentially invoke a REPLACE conflict resolution 
................................................................................
   346    346         if( openAll || aIdxUsed[i] )
   347    347           sqliteVdbeAddOp(v, OP_Close, base+i+1, 0);
   348    348       }
   349    349       sqliteVdbeAddOp(v, OP_Close, base, 0);
   350    350       pParse->nTab = base;
   351    351   
   352    352       if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, 
   353         -          newIdx, oldIdx, onError) ){
          353  +          newIdx, oldIdx, onError, addr) ){
   354    354         goto update_cleanup;
   355    355       }
   356    356     }
   357    357   
   358    358     /* Repeat the above with the next record to be updated, until
   359    359     ** all record selected by the WHERE clause have been updated.
   360    360     */

Changes to src/vdbe.c.

    26     26   ** type to the other occurs as necessary.
    27     27   ** 
    28     28   ** Most of the code in this file is taken up by the sqliteVdbeExec()
    29     29   ** function which does the work of interpreting a VDBE program.
    30     30   ** But other routines are also provided to help in building up
    31     31   ** a program instruction by instruction.
    32     32   **
    33         -** $Id: vdbe.c,v 1.154 2002/06/08 23:25:09 drh Exp $
           33  +** $Id: vdbe.c,v 1.155 2002/06/11 02:25:42 danielk1977 Exp $
    34     34   */
    35     35   #include "sqliteInt.h"
    36     36   #include <ctype.h>
    37     37   
    38     38   /*
    39     39   ** The following global variable is incremented every time a cursor
    40     40   ** moves, either by the OP_MoveTo or the OP_Next opcode.  The test
................................................................................
  1391   1391   ** every program.  So a jump past the last instruction of the program
  1392   1392   ** is the same as executing Halt.
  1393   1393   */
  1394   1394   case OP_Halt: {
  1395   1395     if( pOp->p1!=SQLITE_OK ){
  1396   1396       rc = pOp->p1;
  1397   1397       errorAction = pOp->p2;
         1398  +    if( pOp->p3 ){
         1399  +	sqliteSetString(pzErrMsg, pOp->p3, 0);
         1400  +	goto cleanup;
         1401  +    }
  1398   1402       goto abort_due_to_error;
  1399   1403     }else{
  1400   1404       pc = p->nOp-1;
  1401   1405     }
  1402   1406     break;
  1403   1407   }
  1404   1408   

Added test/trigger3.test.

            1  +# The author disclaims copyright to this source code.  In place of
            2  +# a legal notice, here is a blessing:
            3  +#
            4  +#    May you do good and not evil.
            5  +#    May you find forgiveness for yourself and forgive others.
            6  +#    May you share freely, never taking more than you give.
            7  +#
            8  +#***********************************************************************
            9  +#
           10  +# This file tests the RAISE() function.
           11  +#
           12  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +
           16  +# Test that we can cause ROLLBACK, FAIL and ABORT correctly
           17  +# catchsql { DROP TABLE tbl; }
           18  +catchsql { CREATE TABLE tbl (a, b, c) }
           19  +
           20  +execsql {
           21  +    CREATE TRIGGER before_tbl_insert BEFORE INSERT ON tbl BEGIN SELECT CASE 
           22  +	WHEN (new.a = 4) THEN RAISE(IGNORE) END;
           23  +    END;
           24  +
           25  +    CREATE TRIGGER after_tbl_insert AFTER INSERT ON tbl BEGIN SELECT CASE 
           26  +	WHEN (new.a = 1) THEN RAISE(ABORT,    'Trigger abort') 
           27  +	WHEN (new.a = 2) THEN RAISE(FAIL,     'Trigger fail') 
           28  +	WHEN (new.a = 3) THEN RAISE(ROLLBACK, 'Trigger rollback') END;
           29  +    END;
           30  +}
           31  +# ABORT
           32  +do_test trig-raise-1.1 {
           33  +    catchsql {
           34  +	BEGIN;
           35  +        INSERT INTO tbl VALUES (5, 5, 6);
           36  +        INSERT INTO tbl VALUES (1, 5, 6);
           37  +    }
           38  +} {1 {Trigger abort}}
           39  +
           40  +do_test trig-raise-1.2 {
           41  +    execsql {
           42  +	SELECT * FROM tbl;
           43  +	ROLLBACK;
           44  +    }
           45  +} {5 5 6}
           46  +
           47  +# FAIL
           48  +do_test trig-raise-2.1 {
           49  +    catchsql {
           50  +	BEGIN;
           51  +        INSERT INTO tbl VALUES (5, 5, 6);
           52  +        INSERT INTO tbl VALUES (2, 5, 6);
           53  +    }
           54  +} {1 {Trigger fail}}
           55  +do_test trig-raise-2.2 {
           56  +    execsql {
           57  +	SELECT * FROM tbl;
           58  +	ROLLBACK;
           59  +    }
           60  +} {5 5 6 2 5 6}
           61  +# ROLLBACK
           62  +do_test trig-raise-3.1 {
           63  +    catchsql {
           64  +	BEGIN;
           65  +        INSERT INTO tbl VALUES (5, 5, 6);
           66  +        INSERT INTO tbl VALUES (3, 5, 6);
           67  +    }
           68  +} {1 {Trigger rollback}}
           69  +do_test trig-raise-3.2 {
           70  +    execsql {
           71  +	SELECT * FROM tbl;
           72  +	ROLLBACK;
           73  +    }
           74  +} {}
           75  +# IGNORE
           76  +do_test trig-raise-4.1 {
           77  +    catchsql {
           78  +	BEGIN;
           79  +        INSERT INTO tbl VALUES (5, 5, 6);
           80  +        INSERT INTO tbl VALUES (4, 5, 6);
           81  +    }
           82  +} {0 {}}
           83  +do_test trig-raise-4.2 {
           84  +    execsql {
           85  +	SELECT * FROM tbl;
           86  +	ROLLBACK;
           87  +    }
           88  +} {5 5 6}
           89  +
           90  +# Check that we can also do RAISE(IGNORE) for UPDATE and DELETE
           91  +execsql {DROP TABLE tbl;}
           92  +execsql {CREATE TABLE tbl (a, b, c);}
           93  +execsql {INSERT INTO tbl VALUES(1, 2, 3);}
           94  +execsql {INSERT INTO tbl VALUES(4, 5, 6);}
           95  +execsql {
           96  +    CREATE TRIGGER before_tbl_update BEFORE UPDATE ON tbl BEGIN
           97  +	SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END;
           98  +    END;
           99  +
          100  +    CREATE TRIGGER before_tbl_delete BEFORE DELETE ON tbl BEGIN
          101  +	SELECT CASE WHEN (old.a = 1) THEN RAISE(IGNORE) END;
          102  +    END;
          103  +}
          104  +do_test trig-raise-5.1 {
          105  +    execsql {
          106  +	UPDATE tbl SET c = 10;
          107  +	SELECT * FROM tbl;
          108  +    }
          109  +} {1 2 3 4 5 10}
          110  +do_test trig-raise-5.2 {
          111  +    execsql {
          112  +	DELETE FROM tbl;
          113  +	SELECT * FROM tbl;
          114  +    }
          115  +} {1 2 3}
          116  +
          117  +# Check that RAISE(IGNORE) works correctly for nested triggers:
          118  +execsql {CREATE TABLE tbl2(a, b, c)}
          119  +execsql {
          120  +    CREATE TRIGGER after_tbl2_insert AFTER INSERT ON tbl2 BEGIN
          121  +	UPDATE tbl SET c = 10;
          122  +        INSERT INTO tbl2 VALUES (new.a, new.b, new.c);
          123  +    END;
          124  +}
          125  +do_test trig-raise-6 {
          126  +    execsql {
          127  +	INSERT INTO tbl2 VALUES (1, 2, 3);
          128  +	SELECT * FROM tbl2;
          129  +	SELECT * FROM tbl;
          130  +    }
          131  +} {1 2 3 1 2 3 1 2 3}
          132  +
          133  +# Check that things also work for view-triggers
          134  +execsql {CREATE VIEW tbl_view AS SELECT * FROM tbl}
          135  +execsql {
          136  +    CREATE TRIGGER tbl_view_insert INSTEAD OF INSERT ON tbl_view BEGIN
          137  +	SELECT CASE WHEN (new.a = 1) THEN RAISE(ROLLBACK, 'View rollback')
          138  +	            WHEN (new.a = 2) THEN RAISE(IGNORE) 
          139  +	            WHEN (new.a = 3) THEN RAISE(ABORT, 'View abort') END;
          140  +    END;
          141  +}
          142  +
          143  +do_test trig-raise-7.1 {
          144  +    catchsql {
          145  +	INSERT INTO tbl_view VALUES(1, 2, 3);
          146  +    }
          147  +} {1 {View rollback}}
          148  +do_test trig-raise-7.2 {
          149  +    catchsql {
          150  +	INSERT INTO tbl_view VALUES(2, 2, 3);
          151  +    }
          152  +} {0 {}}
          153  +do_test trig-raise-7.3 {
          154  +    catchsql {
          155  +	INSERT INTO tbl_view VALUES(3, 2, 3);
          156  +    }
          157  +} {1 {View abort}}
          158  +
          159  +catchsql { DROP TABLE tbl; } 
          160  +catchsql { DROP TABLE tbl2; } 
          161  +catchsql { DROP VIEW tbl_view; } 
          162  +
          163  +