DBD-SQLite2
view release on metacpan or search on metacpan
** May you share freely, never taking more than you give.
**
*************************************************************************
*
*/
#include "sqliteInt.h"
/*
** Delete a linked list of TriggerStep structures.
*/
void sqliteDeleteTriggerStep(TriggerStep *pTriggerStep){
while( pTriggerStep ){
TriggerStep * pTmp = pTriggerStep;
pTriggerStep = pTriggerStep->pNext;
if( pTmp->target.dyn ) sqliteFree((char*)pTmp->target.z);
sqliteExprDelete(pTmp->pWhere);
sqliteExprListDelete(pTmp->pExprList);
sqliteSelectDelete(pTmp->pSelect);
sqliteIdListDelete(pTmp->pIdList);
sqliteFree(pTmp);
}
}
/*
** 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(
Parse *pParse, /* The parse context of the CREATE TRIGGER statement */
Token *pName, /* The name of the trigger */
int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
IdList *pColumns, /* column list if this is an UPDATE OF trigger */
SrcList *pTableName,/* The name of the table/view the trigger applies to */
int foreach, /* One of TK_ROW or TK_STATEMENT */
Expr *pWhen, /* WHEN clause */
int isTemp /* True if the TEMPORARY keyword is present */
){
Trigger *nt;
Table *tab;
char *zName = 0; /* Name of the trigger */
sqlite *db = pParse->db;
int iDb; /* When database to store the trigger in */
DbFixer sFix;
/* 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;
assert( pTableName->nSrc==1 );
if( db->init.busy
&& sqliteFixInit(&sFix, pParse, db->init.iDb, "trigger", pName)
&& sqliteFixSrcList(&sFix, pTableName)
){
goto trigger_cleanup;
}
tab = sqliteSrcListLookup(pParse, pTableName);
if( !tab ){
goto trigger_cleanup;
}
iDb = isTemp ? 1 : tab->iDb;
if( iDb>=2 && !db->init.busy ){
sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
"database %s", db->aDb[tab->iDb].zName);
goto trigger_cleanup;
}
zName = sqliteStrNDup(pName->z, pName->n);
sqliteDequote(zName);
if( sqliteHashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
sqliteErrorMsg(pParse, "trigger %T already exists", pName);
goto trigger_cleanup;
}
if( sqliteStrNICmp(tab->zName, "sqlite_", 7)==0 ){
sqliteErrorMsg(pParse, "cannot create trigger on system table");
pParse->nErr++;
goto trigger_cleanup;
}
if( tab->pSelect && tr_tm != TK_INSTEAD ){
sqliteErrorMsg(pParse, "cannot create %s trigger on view: %S",
(tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
goto trigger_cleanup;
}
if( !tab->pSelect && tr_tm == TK_INSTEAD ){
sqliteErrorMsg(pParse, "cannot create INSTEAD OF"
" trigger on table: %S", pTableName, 0);
goto trigger_cleanup;
}
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_CREATE_TRIGGER;
const char *zDb = db->aDb[tab->iDb].zName;
const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
if( tab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
if( sqliteAuthCheck(pParse, code, zName, tab->zName, zDbTrig) ){
goto trigger_cleanup;
}
if( sqliteAuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0, zDb)){
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){
tr_tm = TK_BEFORE;
}
/* Build the Trigger object */
nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
if( nt==0 ) goto trigger_cleanup;
nt->name = zName;
zName = 0;
nt->table = sqliteStrDup(pTableName->a[0].zName);
if( sqlite_malloc_failed ) goto trigger_cleanup;
nt->iDb = iDb;
nt->iTabDb = tab->iDb;
nt->op = op;
nt->tr_tm = tr_tm;
nt->pWhen = sqliteExprDup(pWhen);
nt->pColumns = sqliteIdListDup(pColumns);
nt->foreach = foreach;
sqliteTokenCopy(&nt->nameToken,pName);
assert( pParse->pNewTrigger==0 );
pParse->pNewTrigger = nt;
trigger_cleanup:
sqliteFree(zName);
sqliteSrcListDelete(pTableName);
sqliteIdListDelete(pColumns);
sqliteExprDelete(pWhen);
}
/*
** 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(
Parse *pParse, /* Parser context */
TriggerStep *pStepList, /* The triggered program */
Token *pAll /* Token that describes the complete CREATE TRIGGER */
){
Trigger *nt = 0; /* The trigger whose construction is finishing up */
sqlite *db = pParse->db; /* The database */
DbFixer sFix;
if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
nt = pParse->pNewTrigger;
pParse->pNewTrigger = 0;
nt->step_list = pStepList;
while( pStepList ){
pStepList->pTrig = nt;
pStepList = pStepList->pNext;
}
if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken)
&& sqliteFixTriggerStep(&sFix, nt->step_list) ){
goto triggerfinish_cleanup;
}
/* if we are not initializing, and this trigger is not on a TEMP table,
** build the sqlite_master entry
*/
if( !db->init.busy ){
static VdbeOpList insertTrig[] = {
{ 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);
sqliteOpenMasterTable(v, nt->iDb);
addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
sqliteVdbeChangeP3(v, addr+2, nt->name, 0);
sqliteVdbeChangeP3(v, addr+3, nt->table, 0);
sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
if( nt->iDb==0 ){
sqliteChangeCookie(db, v);
}
sqliteVdbeAddOp(v, OP_Close, 0, 0);
sqliteEndWriteOperation(pParse);
}
if( !pParse->explain ){
Table *pTab;
sqliteHashInsert(&db->aDb[nt->iDb].trigHash,
nt->name, strlen(nt->name)+1, nt);
pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
assert( pTab!=0 );
nt->pNext = pTab->pTrigger;
pTab->pTrigger = nt;
nt = 0;
}
triggerfinish_cleanup:
sqliteDeleteTrigger(nt);
sqliteDeleteTrigger(pParse->pNewTrigger);
pParse->pNewTrigger = 0;
sqliteDeleteTriggerStep(pStepList);
}
/*
** 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.
*/
static void sqlitePersistTriggerStep(TriggerStep *p){
if( p->target.z ){
p->target.z = sqliteStrNDup(p->target.z, p->target.n);
( run in 0.707 second using v1.01-cache-2.11-cpan-140bd7fdf52 )