#pragma ident "%Z%%M% %I% %E% SMI"
/*
**
** 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.
**
*************************************************************************
*
*/
#include "sqliteInt.h"
/*
** Delete a linked list of TriggerStep structures.
*/
while( pTriggerStep ){
}
}
/*
** This is called by the parser when it sees a CREATE TRIGGER statement
** up to the point of the BEGIN before the trigger actions. A Trigger
** structure is generated based on the information available and stored
** in pParse->pNewTrigger. After the trigger actions have been parsed, the
** sqliteFinishTrigger() function is called to complete the trigger
** construction process.
*/
void sqliteBeginTrigger(
int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
int foreach, /* One of TK_ROW or TK_STATEMENT */
int isTemp /* True if the TEMPORARY keyword is present */
){
/* Check that:
** 1. the trigger name does not already exist.
** 2. the table (or view) does exist in the same database as the trigger.
** 3. that we are not trying to create a trigger on the sqlite_master table
** 4. That we are not trying to create an INSTEAD OF trigger on a table.
** 5. That we are not trying to create a BEFORE or AFTER trigger on a view.
*/
if( sqlite_malloc_failed ) goto trigger_cleanup;
){
goto trigger_cleanup;
}
if( !tab ){
goto trigger_cleanup;
}
goto trigger_cleanup;
}
goto trigger_cleanup;
}
goto trigger_cleanup;
}
goto trigger_cleanup;
}
" trigger on table: %S", pTableName, 0);
goto trigger_cleanup;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
{
goto trigger_cleanup;
}
goto trigger_cleanup;
}
}
#endif
/* INSTEAD OF triggers can only appear on views and BEGIN triggers
** cannot appear on views. So we might as well translate every
** INSTEAD OF trigger into a BEFORE trigger. It simplifies code
** elsewhere.
*/
if (tr_tm == TK_INSTEAD){
}
/* Build the Trigger object */
if( nt==0 ) goto trigger_cleanup;
zName = 0;
if( sqlite_malloc_failed ) goto trigger_cleanup;
}
/*
** This routine is called after all of the trigger actions have been parsed
** in order to complete the process of building the trigger.
*/
void sqliteFinishTrigger(
){
pParse->pNewTrigger = 0;
while( pStepList ){
}
goto triggerfinish_cleanup;
}
/* if we are not initializing, and this trigger is not on a TEMP table,
** build the sqlite_master entry
*/
{ OP_NewRecno, 0, 0, 0 },
{ OP_String, 0, 0, "trigger" },
{ OP_String, 0, 0, 0 }, /* 2: trigger name */
{ OP_String, 0, 0, 0 }, /* 3: table name */
{ OP_Integer, 0, 0, 0 },
{ OP_String, 0, 0, 0 }, /* 5: SQL */
{ OP_MakeRecord, 5, 0, 0 },
{ OP_PutIntKey, 0, 0, 0 },
};
int addr;
Vdbe *v;
/* Make an entry in the sqlite_master table */
v = sqliteGetVdbe(pParse);
if( v==0 ) goto triggerfinish_cleanup;
sqliteBeginWriteOperation(pParse, 0, 0);
sqliteChangeCookie(db, v);
}
sqliteVdbeAddOp(v, OP_Close, 0, 0);
}
nt = 0;
}
pParse->pNewTrigger = 0;
}
/*
** Make a copy of all components of the given trigger step. This has
** the effect of copying all Expr.token.z values into memory obtained
** from sqliteMalloc(). As initially created, the Expr.token.z values
** all point to the input string that was fed to the parser. But that
** string is ephemeral - it will go away as soon as the sqlite_exec()
** call that started the parser exits. This routine makes a persistent
** copy of all the Expr.token.z strings so that the TriggerStep structure
** will be valid even after the sqlite_exec() call returns.
*/
if( p->target.z ){
}
if( p->pSelect ){
}
if( p->pWhere ){
sqliteExprDelete(p->pWhere);
}
if( p->pExprList ){
}
if( p->pIdList ){
}
}
/*
** Turn a SELECT statement (that the pSelect parameter points to) into
** a trigger step. Return a pointer to a TriggerStep structure.
**
** The parser calls this routine when it finds a SELECT statement in
** body of a TRIGGER.
*/
if( pTriggerStep==0 ) return 0;
return pTriggerStep;
}
/*
** Build a trigger step out of an INSERT statement. Return a pointer
** to the new trigger step.
**
** The parser calls this routine when it sees an INSERT inside the
** body of a trigger.
*/
int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
){
if( pTriggerStep==0 ) return 0;
return pTriggerStep;
}
/*
** Construct a trigger step that implements an UPDATE statement and return
** a pointer to that trigger step. The parser calls this routine when it
** sees an UPDATE statement inside the body of a CREATE TRIGGER.
*/
int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
){
if( pTriggerStep==0 ) return 0;
return pTriggerStep;
}
/*
** Construct a trigger step that implements a DELETE statement and return
** a pointer to that trigger step. The parser calls this routine when it
** sees a DELETE statement inside the body of a CREATE TRIGGER.
*/
if( pTriggerStep==0 ) return 0;
return pTriggerStep;
}
/*
** Recursively delete a Trigger structure
*/
if( pTrigger==0 ) return;
}
/*
* This function is called to drop a trigger from the database schema.
*
* This may be called directly from the parser and therefore identifies
* the trigger by name. The sqliteDropTriggerPtr() routine does the
* same job as this routine except it take a spointer to the trigger
* instead of the trigger name.
*
* Note that this function does not delete the trigger entirely. Instead it
* removes it from the internal schema and places it in the trigDrop hash
* table. This is so that the trigger can be restored into the database schema
* if the transaction is rolled back.
*/
int i;
const char *zDb;
const char *zName;
int nName;
if( sqlite_malloc_failed ) goto drop_trigger_cleanup;
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
if( pTrigger ) break;
}
if( !pTrigger ){
goto drop_trigger_cleanup;
}
}
/*
** Drop a trigger given a pointer to that trigger. If nested is false,
** then also generate code to remove the trigger from the SQLITE_MASTER
** table.
*/
Vdbe *v;
return;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
{
return;
}
}
#endif
/* Generate code to destroy the database record of the trigger.
*/
int base;
{ OP_String, 0, 0, 0}, /* 1 */
{ OP_Column, 0, 1, 0},
{ OP_String, 0, 0, "trigger"},
{ OP_Column, 0, 0, 0},
{ OP_Delete, 0, 0, 0},
};
sqliteBeginWriteOperation(pParse, 0, 0);
sqliteChangeCookie(db, v);
}
sqliteVdbeAddOp(v, OP_Close, 0, 0);
}
/*
* If this is not an "explain", then delete the trigger structure.
*/
}else{
while( cc ){
break;
}
}
}
}
}
/*
** pEList is the SET clause of an UPDATE statement. Each entry
** in pEList is of the format <id>=<expr>. If any of the entries
** in pEList have an <id> which matches an identifier in pIdList,
** then return TRUE. If pIdList==NULL, then it is considered a
** wildcard that matches anything. Likewise if pEList==NULL then
** it matches anything so always return true. Return false only
** if there is no match.
*/
int e;
}
return 0;
}
/* A global variable that is TRUE if we should always set up temp tables for
* for triggers, even if there are no triggers to code. This is used to test
* how much overhead the triggers algorithm is causing.
*
* This flag can be set or cleared using the "trigger_overhead_test" pragma.
* The pragma is not documented since it is not really part of the interface
* to SQLite, just the test procedure.
*/
int always_code_trigger_setup = 0;
/*
* Returns true if a trigger matching op, tr_tm and foreach that is NOT already
* on the Parse objects trigger-stack (to prevent recursive trigger firing) is
* found in the list specified as pTrigger.
*/
int sqliteTriggersExist(
int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
int tr_tm, /* one of TK_BEFORE, TK_AFTER */
int foreach, /* one of TK_ROW or TK_STATEMENT */
){
if( always_code_trigger_setup ){
return 1;
}
while( pTriggerCursor ){
}
if( !ss )return 1;
}
}
return 0;
}
/*
** Convert the pStep->target token into a SrcList and return a pointer
** to that SrcList.
**
** This routine adds a specific database name, if needed, to the target when
** forming the SrcList. This prevents a trigger in one database from
** referring to a target in another database. An exception is when the
** trigger is in TEMP in which case it can refer to any other database it
** wants.
*/
){
} else {
}
return pSrc;
}
/*
** Generate VDBE code for zero or more statements inside the body of a
** trigger.
*/
static int codeTriggerProgram(
int orconfin /* Conflict algorithm. (OE_Abort, etc) */
){
int orconf;
while( pTriggerStep ){
switch( pTriggerStep->op ){
case TK_SELECT: {
break;
}
case TK_UPDATE: {
break;
}
case TK_INSERT: {
break;
}
case TK_DELETE: {
break;
}
default:
assert(0);
}
}
return 0;
}
/*
** This is called to code FOR EACH ROW triggers.
**
** When the code that this function generates is executed, the following
** must be true:
**
** 1. No cursors may be open in the main database. (But newIdx and oldIdx
** can be indices of cursors in temporary tables. See below.)
**
** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
** a temporary vdbe cursor (index newIdx) must be open and pointing at
** a row containing values to be substituted for new.* expressions in the
** trigger program(s).
**
** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
** a temporary vdbe cursor (index oldIdx) must be open and pointing at
** a row containing values to be substituted for old.* expressions in the
** trigger program(s).
**
*/
int sqliteCodeRowTrigger(
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
int tr_tm, /* One of TK_BEFORE, TK_AFTER */
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) */
){
while( pTrigger ){
int fire_this = 0;
/* determine whether we should code this trigger */
fire_this = 1;
while( pTriggerStack ){
fire_this = 0;
}
}
fire_this = 0;
}
}
int endTrigger;
dummyTablist.nSrc = 0;
/* Push an entry on to the trigger stack */
/* code the WHEN clause */
return 1;
}
/* Pop the entry off the trigger stack */
}
}
return 0;
}