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 |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
d4a2fb10067203a0d49317db74775987 |
User & Date: | danielk1977 2002-06-11 02:25:41.000 |
Context
2002-06-11
| ||
22:33 | Documentation that should have been checked in along with checkin (614) (CVS 615) (check-in: 10da136125 user: danielk1977 tags: trunk) | |
02:25 | Add RAISE() function, which allows more advanced flow-control in trigger programs (ticket #55) (CVS 614) (check-in: d4a2fb1006 user: danielk1977 tags: trunk) | |
2002-06-09
| ||
10:14 | Fix the spelling of sqliteRegisterBuiltinFunctions(). (CVS 613) (check-in: 74d297d97e user: drh tags: trunk) | |
Changes
Changes to src/delete.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** ** $Id: delete.c,v 1.37 2002/06/11 02:25:41 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** Given a table name, find the corresponding table and make sure the ** table is writeable. Generate an error and return NULL if not. If |
︙ | ︙ | |||
233 234 235 236 237 238 239 | } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0); sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0); sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, | | > | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0); sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0); sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, addr); } /* Open cursors for the table we are deleting from and all its ** indices. If there are row triggers, this happens inside the ** OP_ListRead loop because the cursor have to all be closed ** before the trigger fires. If there are no row triggers, the ** cursors are opened only once on the outside the loop. |
︙ | ︙ | |||
267 268 269 270 271 272 273 | */ if( row_triggers_exist ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum); } sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, | | > | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | */ if( row_triggers_exist ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum); } sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default, addr); } /* End of the delete loop */ sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeResolveLabel(v, end); sqliteVdbeAddOp(v, OP_ListReset, 0, 0); |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** ** $Id: expr.c,v 1.71 2002/06/11 02:25:41 danielk1977 Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** Construct a new expression node and return a pointer to it. Memory ** for this node is obtained from sqliteMalloc(). The calling function |
︙ | ︙ | |||
200 201 202 203 204 205 206 | SrcList *pNew; int i; if( p==0 ) return 0; pNew = sqliteMalloc( sizeof(*pNew) ); if( pNew==0 ) return 0; pNew->nSrc = p->nSrc; pNew->a = sqliteMalloc( p->nSrc*sizeof(p->a[0]) ); | | | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | SrcList *pNew; int i; if( p==0 ) return 0; pNew = sqliteMalloc( sizeof(*pNew) ); if( pNew==0 ) return 0; pNew->nSrc = p->nSrc; pNew->a = sqliteMalloc( p->nSrc*sizeof(p->a[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); pNew->a[i].jointype = p->a[i].jointype; pNew->a[i].pTab = 0; pNew->a[i].pSelect = sqliteSelectDup(p->a[i].pSelect); pNew->a[i].pOn = sqliteExprDup(p->a[i].pOn); |
︙ | ︙ | |||
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 | } if( pExpr->pRight ){ sqliteExprCode(pParse, pExpr->pRight); }else{ sqliteVdbeAddOp(v, OP_String, 0, 0); } sqliteVdbeResolveLabel(v, expr_end_label); } break; } } /* ** Generate code for a boolean expression such that a jump is made | > > > > > > > > > > > > > > > > > > > > > > | 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 | } if( pExpr->pRight ){ sqliteExprCode(pParse, pExpr->pRight); }else{ 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; } } /* ** Generate code for a boolean expression such that a jump is made |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** ** $Id: insert.c,v 1.61 2002/06/11 02:25:42 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** This routine is call to handle SQL of the following forms: ** ** insert into TABLE (IDLIST) values(EXPRLIST) |
︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 | */ if( srcTab>=0 ){ iBreak = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak); iCont = sqliteVdbeCurrentAddr(v); } if( row_triggers_exist ){ /* build the new.* reference row */ sqliteVdbeAddOp(v, OP_Integer, 13, 0); for(i=0; i<pTab->nCol; i++){ if( pColumn==0 ){ j = i; | > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | */ if( srcTab>=0 ){ iBreak = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, srcTab, iBreak); iCont = sqliteVdbeCurrentAddr(v); } endOfLoop = sqliteVdbeMakeLabel(v); if( row_triggers_exist ){ /* build the new.* reference row */ sqliteVdbeAddOp(v, OP_Integer, 13, 0); for(i=0; i<pTab->nCol; i++){ if( pColumn==0 ){ j = i; |
︙ | ︙ | |||
258 259 260 261 262 263 264 | } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); /* Fire BEFORE triggers */ if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1, | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); /* Fire BEFORE triggers */ if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab, newIdx, -1, onError, endOfLoop) ){ goto insert_cleanup; } /* Open the tables and indices for the INSERT */ if( !pTab->pSelect ){ base = pParse->nTab; openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; |
︙ | ︙ | |||
332 333 334 335 336 337 338 | sqliteExprCode(pParse, pList->a[j].pExpr); } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ | < | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | sqliteExprCode(pParse, pList->a[j].pExpr); } } /* Generate code to check constraints and generate index keys and ** do the insertion. */ sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop); sqliteCompleteInsertion(pParse, pTab, base, 0,0,0); /* Update the count of rows that are inserted */ if( (db->flags & SQLITE_CountRows)!=0 && !pParse->trigStack){ sqliteVdbeAddOp(v, OP_AddImm, 1, 0); |
︙ | ︙ | |||
354 355 356 357 358 359 360 | for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ sqliteVdbeAddOp(v, OP_Close, idx+base, 0); } } /* Code AFTER triggers */ if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1, | | | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 | for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){ sqliteVdbeAddOp(v, OP_Close, idx+base, 0); } } /* Code AFTER triggers */ if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1, onError, endOfLoop) ){ goto insert_cleanup; } } /* The bottom of the loop, if the data source is a SELECT statement */ sqliteVdbeResolveLabel(v, endOfLoop); |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
10 11 12 13 14 15 16 | ** ************************************************************************* ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | ** ************************************************************************* ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** ** @(#) $Id: parse.y,v 1.73 2002/06/11 02:25:42 danielk1977 Exp $ */ %token_prefix TK_ %token_type {Token} %default_type {Token} %extra_argument {Parse *pParse} %syntax_error { sqliteSetString(&pParse->zErrMsg,"syntax error",0); |
︙ | ︙ | |||
116 117 118 119 120 121 122 | // fallback to ID if they will not parse as their original value. // This obviates the need for the "id" nonterminal. // %fallback ID 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 | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | // fallback to ID if they will not parse as their original value. // This obviates the need for the "id" nonterminal. // %fallback ID 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 RAISE REPLACE RESTRICT ROW STATEMENT TEMP TRIGGER VACUUM VIEW. // And "ids" is an identifer-or-string. // %type ids {Token} ids(A) ::= id(X). {A = X;} ids(A) ::= STRING(X). {A = X;} |
︙ | ︙ | |||
751 752 753 754 755 756 757 758 759 760 761 762 | // DELETE trigger_cmd(A) ::= DELETE FROM ids(X) where_opt(Y). {A = sqliteTriggerDeleteStep(&X, Y);} // SELECT trigger_cmd(A) ::= select(X). {A = sqliteTriggerSelectStep(X); } //////////////////////// DROP TRIGGER statement ////////////////////////////// cmd ::= DROP TRIGGER ids(X). { sqliteDropTrigger(pParse,&X,0); } | > > > > > > > > > > > > > | 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 | // DELETE trigger_cmd(A) ::= DELETE FROM ids(X) where_opt(Y). {A = sqliteTriggerDeleteStep(&X, 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); } |
Changes to src/sqliteInt.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* ** Internal interface definitions for SQLite. ** ** @(#) $Id: sqliteInt.h,v 1.123 2002/06/11 02:25:42 danielk1977 Exp $ */ #include "sqlite.h" #include "hash.h" #include "vdbe.h" #include "parse.h" #include "btree.h" #include <stdio.h> |
︙ | ︙ | |||
760 761 762 763 764 765 766 767 768 769 770 771 772 773 | * recursively. If this condition is detected, the nested trigger is not coded. */ struct TriggerStack { Table *pTab; /* Table that triggers are currently being coded on */ 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 */ Trigger *pTrigger; TriggerStack *pNext; }; /* * This global flag is set for performance testing of triggers. When it is set | > | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 | * recursively. If this condition is detected, the nested trigger is not coded. */ struct TriggerStack { Table *pTab; /* Table that triggers are currently being coded on */ 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; }; /* * This global flag is set for performance testing of triggers. When it is set |
︙ | ︙ | |||
891 892 893 894 895 896 897 | int sqliteSafetyOff(sqlite*); int sqliteSafetyCheck(sqlite*); void sqliteChangeCookie(sqlite *); 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*); | | > | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 | int sqliteSafetyOff(sqlite*); int sqliteSafetyCheck(sqlite*); void sqliteChangeCookie(sqlite *); 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); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); TriggerStep *sqliteTriggerSelectStep(Select*); TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int); TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int); TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*); void sqliteDeleteTrigger(Trigger*); int sqliteJoinType(Parse*, Token*, Token*, Token*); |
Changes to src/tokenize.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 | ************************************************************************* ** An tokenizer for SQL ** ** This file contains C code that splits an SQL input string up into ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ************************************************************************* ** An tokenizer for SQL ** ** This file contains C code that splits an SQL input string up into ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** ** $Id: tokenize.c,v 1.45 2002/06/11 02:25:42 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" #include <ctype.h> #include <stdlib.h> /* |
︙ | ︙ | |||
101 102 103 104 105 106 107 108 109 110 111 112 113 114 | { "OFFSET", 0, TK_OFFSET, 0 }, { "ON", 0, TK_ON, 0 }, { "OR", 0, TK_OR, 0 }, { "ORDER", 0, TK_ORDER, 0 }, { "PARTIAL", 0, TK_PARTIAL, 0 }, { "PRAGMA", 0, TK_PRAGMA, 0 }, { "PRIMARY", 0, TK_PRIMARY, 0 }, { "REFERENCES", 0, TK_REFERENCES, 0 }, { "REPLACE", 0, TK_REPLACE, 0 }, { "RESTRICT", 0, TK_RESTRICT, 0 }, { "ROLLBACK", 0, TK_ROLLBACK, 0 }, { "ROW", 0, TK_ROW, 0 }, { "SELECT", 0, TK_SELECT, 0 }, { "SET", 0, TK_SET, 0 }, | > | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | { "OFFSET", 0, TK_OFFSET, 0 }, { "ON", 0, TK_ON, 0 }, { "OR", 0, TK_OR, 0 }, { "ORDER", 0, TK_ORDER, 0 }, { "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 }, { "ROLLBACK", 0, TK_ROLLBACK, 0 }, { "ROW", 0, TK_ROW, 0 }, { "SELECT", 0, TK_SELECT, 0 }, { "SET", 0, TK_SET, 0 }, |
︙ | ︙ |
Changes to src/trigger.c.
︙ | ︙ | |||
476 477 478 479 480 481 482 | while( pTriggerStep ){ int saveNTab = pParse->nTab; orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; pParse->trigStack->orconf = orconf; switch( pTriggerStep->op ){ case TK_SELECT: { | > > > | > | | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | while( pTriggerStep ){ int saveNTab = pParse->nTab; orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; pParse->trigStack->orconf = orconf; switch( pTriggerStep->op ){ case TK_SELECT: { 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); sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0); break; } case TK_INSERT: { sqliteInsert(pParse, &pTriggerStep->target, sqliteExprListDup(pTriggerStep->pExprList), sqliteSelectDup(pTriggerStep->pSelect), |
︙ | ︙ | |||
539 540 541 542 543 544 545 | Parse *pParse, /* Parse context */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TK_BEFORE, TK_AFTER */ 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 */ | | > | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | Parse *pParse, /* Parse context */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TK_BEFORE, TK_AFTER */ 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 ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ ){ Trigger * pTrigger; TriggerStack * pTriggerStack; assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER); |
︙ | ︙ | |||
584 585 586 587 588 589 590 591 592 593 594 595 596 597 | /* Push an entry on to the trigger stack */ pTriggerStack->pTrigger = pTrigger; pTriggerStack->newIdx = newIdx; pTriggerStack->oldIdx = oldIdx; pTriggerStack->pTab = pTab; pTriggerStack->pNext = pParse->trigStack; pParse->trigStack = pTriggerStack; /* code the WHEN clause */ endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe); whenExpr = sqliteExprDup(pTrigger->pWhen); if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){ pParse->trigStack = pParse->trigStack->pNext; | > | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 | /* Push an entry on to the trigger stack */ pTriggerStack->pTrigger = pTrigger; pTriggerStack->newIdx = newIdx; pTriggerStack->oldIdx = oldIdx; pTriggerStack->pTab = pTab; pTriggerStack->pNext = pParse->trigStack; pTriggerStack->ignoreJump = ignoreJump; pParse->trigStack = pTriggerStack; /* code the WHEN clause */ endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe); whenExpr = sqliteExprDup(pTrigger->pWhen); if( sqliteExprResolveIds(pParse, 0, &dummyTablist, 0, whenExpr) ){ pParse->trigStack = pParse->trigStack->pNext; |
︙ | ︙ | |||
731 732 733 734 735 736 737 | } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, | | | | | | 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, newIdx, oldIdx, orconf, endOfLoop); sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, newIdx, oldIdx, orconf, endOfLoop); }else{ sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx, orconf, endOfLoop); sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx, orconf, endOfLoop); } sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop); sqliteVdbeResolveLabel(v, endOfLoop); sqliteEndWriteOperation(pParse); |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** ** $Id: update.c,v 1.44 2002/06/11 02:25:42 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** Process an UPDATE statement. */ void sqliteUpdate( |
︙ | ︙ | |||
239 240 241 242 243 244 245 | sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, | | | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0); sqliteVdbeAddOp(v, OP_Close, base, 0); sqliteVdbeAddOp(v, OP_Rewind, oldIdx, 0); sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0); if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab, newIdx, oldIdx, onError, addr) ){ goto update_cleanup; } } /* Rewind the list of records that need to be updated and ** open every index that needs updating. Note that if any ** index could potentially invoke a REPLACE conflict resolution |
︙ | ︙ | |||
346 347 348 349 350 351 352 | if( openAll || aIdxUsed[i] ) sqliteVdbeAddOp(v, OP_Close, base+i+1, 0); } sqliteVdbeAddOp(v, OP_Close, base, 0); pParse->nTab = base; if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, | | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | if( openAll || aIdxUsed[i] ) sqliteVdbeAddOp(v, OP_Close, base+i+1, 0); } sqliteVdbeAddOp(v, OP_Close, base, 0); pParse->nTab = base; if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab, newIdx, oldIdx, onError, addr) ){ goto update_cleanup; } } /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. */ |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | ** type to the other occurs as necessary. ** ** Most of the code in this file is taken up by the sqliteVdbeExec() ** function which does the work of interpreting a VDBE program. ** But other routines are also provided to help in building up ** a program instruction by instruction. ** | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ** type to the other occurs as necessary. ** ** Most of the code in this file is taken up by the sqliteVdbeExec() ** function which does the work of interpreting a VDBE program. ** But other routines are also provided to help in building up ** a program instruction by instruction. ** ** $Id: vdbe.c,v 1.155 2002/06/11 02:25:42 danielk1977 Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_MoveTo or the OP_Next opcode. The test |
︙ | ︙ | |||
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 | ** every program. So a jump past the last instruction of the program ** is the same as executing Halt. */ case OP_Halt: { if( pOp->p1!=SQLITE_OK ){ rc = pOp->p1; errorAction = pOp->p2; goto abort_due_to_error; }else{ pc = p->nOp-1; } break; } | > > > > | 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 | ** every program. So a jump past the last instruction of the program ** is the same as executing Halt. */ 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; } break; } |
︙ | ︙ |
Added test/trigger3.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 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; } |