More fixes and comment updates.
FossilOrigin-Name: 38a9327bad1a01e3d7a47fad44ece2f6c7e88643
diff --git a/src/trigger.c b/src/trigger.c
index 964db70..e17b48e 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -666,22 +666,22 @@
}
/*
-** Generate VDBE code for zero or more statements inside the body of a
-** trigger.
+** Generate VDBE code for the statements inside the body of a single
+** trigger.
*/
static int codeTriggerProgram(
Parse *pParse, /* The parser context */
TriggerStep *pStepList, /* List of statements inside the trigger body */
- int orconfin /* Conflict algorithm. (OE_Abort, etc) */
+ int orconf /* Conflict algorithm. (OE_Abort, etc) */
){
- TriggerStep * pStep = pStepList;
+ TriggerStep *pStep;
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
- assert( pParse->pRoot );
- assert( pStep!=0 );
+ assert( pParse->pTriggerTab && pParse->pToplevel );
+ assert( pStepList );
assert( v!=0 );
- while( pStep ){
+ for(pStep=pStepList; pStep; pStep=pStep->pNext){
/* Figure out the ON CONFLICT policy that will be used for this step
** of the trigger program. If the statement that caused this trigger
** to fire had an explicit ON CONFLICT, then use it. Otherwise, use
@@ -695,7 +695,7 @@
** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy
** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy
*/
- pParse->orconf = (orconfin==OE_Default)?pStep->orconf:orconfin;
+ pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:orconf;
switch( pStep->op ){
case TK_UPDATE: {
@@ -703,7 +703,7 @@
targetSrcList(pParse, pStep),
sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3ExprDup(db, pStep->pWhere, 0),
- pParse->orconf
+ pParse->eOrconf
);
break;
}
@@ -713,7 +713,7 @@
sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList),
- pParse->orconf
+ pParse->eOrconf
);
break;
}
@@ -736,7 +736,6 @@
if( pStep->op!=TK_SELECT ){
sqlite3VdbeAddOp1(v, OP_ResetCount, 1);
}
- pStep = pStep->pNext;
}
return 0;
@@ -776,16 +775,19 @@
}
}
+/*
+** Create and populate a new TriggerPrg object with a sub-program
+** implementing trigger pTrigger with ON CONFLICT policy orconf.
+*/
static TriggerPrg *codeRowTrigger(
- Parse *pRoot, /* Root parse context */
Parse *pParse, /* Current parse context */
Trigger *pTrigger, /* Trigger to code */
- int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
- Table *pTab, /* The table to code triggers from */
- int orconf
+ Table *pTab, /* The table pTrigger is attached to */
+ int orconf /* ON CONFLICT policy to code trigger program with */
){
- sqlite3 *db = pParse->db;
- TriggerPrg *pPrg;
+ Parse *pTop = sqlite3ParseToplevel(pParse);
+ sqlite3 *db = pParse->db; /* Database handle */
+ TriggerPrg *pPrg; /* Value to return */
Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */
Vdbe *v; /* Temporary VM */
NameContext sNC; /* Name context for sub-vdbe */
@@ -793,35 +795,41 @@
Parse *pSubParse; /* Parse context for sub-vdbe */
int iEndTrigger = 0; /* Label to jump to if WHEN is false */
+ assert( pTab==tableOfTrigger(pTrigger) );
+
+ /* Allocate the TriggerPrg and SubProgram objects. To ensure that they
+ ** are freed if an error occurs, link them into the Parse.pTriggerPrg
+ ** list of the top-level Parse object sooner rather than later. */
pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg));
if( !pPrg ) return 0;
- pPrg->pNext = pRoot->pTriggerPrg;
- pRoot->pTriggerPrg = pPrg;
+ pPrg->pNext = pTop->pTriggerPrg;
+ pTop->pTriggerPrg = pPrg;
pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram));
if( !pProgram ) return 0;
pProgram->nRef = 1;
- pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
- if( !pSubParse ) return 0;
-
- pPrg->pProgram = pProgram;
pPrg->pTrigger = pTrigger;
pPrg->orconf = orconf;
+ /* Allocate and populate a new Parse context to use for coding the
+ ** trigger sub-program. */
+ pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
+ if( !pSubParse ) return 0;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pSubParse;
pSubParse->db = db;
pSubParse->pTriggerTab = pTab;
- pSubParse->pRoot = pRoot;
+ pSubParse->pToplevel = pTop;
pSubParse->zAuthContext = pTrigger->zName;
+ pSubParse->eTriggerOp = pTrigger->op;
v = sqlite3GetVdbe(pSubParse);
if( v ){
VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)",
pTrigger->zName, onErrorText(orconf),
(pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"),
- (op==TK_UPDATE ? "UPDATE" : ""),
- (op==TK_INSERT ? "INSERT" : ""),
- (op==TK_DELETE ? "DELETE" : ""),
+ (pTrigger->op==TK_UPDATE ? "UPDATE" : ""),
+ (pTrigger->op==TK_INSERT ? "INSERT" : ""),
+ (pTrigger->op==TK_DELETE ? "DELETE" : ""),
pTab->zName
));
#ifndef SQLITE_OMIT_TRACE
@@ -830,9 +838,10 @@
);
#endif
+ /* If one was specified, code the WHEN clause. If it evaluates to false
+ ** (or NULL) the sub-vdbe is immediately halted by jumping to the
+ ** OP_Halt inserted at the end of the program. */
if( pTrigger->pWhen ){
- /* Code the WHEN clause. If it evaluates to false (or NULL) the
- ** sub-vdbe is immediately halted. */
pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0);
if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen)
&& db->mallocFailed==0
@@ -845,6 +854,8 @@
/* Code the trigger program into the sub-vdbe. */
codeTriggerProgram(pSubParse, pTrigger->step_list, orconf);
+
+ /* Insert an OP_Halt at the end of the sub-program. */
if( iEndTrigger ){
sqlite3VdbeResolveLabel(v, iEndTrigger);
}
@@ -853,49 +864,51 @@
transferParseError(pParse, pSubParse);
if( db->mallocFailed==0 ){
- pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pParse->nArg);
+ pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
}
pProgram->nMem = pSubParse->nMem;
pProgram->nCsr = pSubParse->nTab;
pProgram->token = (void *)pTrigger;
pPrg->oldmask = pSubParse->oldmask;
sqlite3VdbeDelete(v);
-
- while( pSubParse->pAinc ){
- AutoincInfo *p = pSubParse->pAinc;
- pSubParse->pAinc = p->pNext;
- sqlite3DbFree(db, p);
- }
}
+
+ assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
+ assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
sqlite3StackFree(db, pSubParse);
return pPrg;
}
+/*
+** Return a pointer to a TriggerPrg object containing the sub-program for
+** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such
+** TriggerPrg object exists, a new object is allocated and populated before
+** being returned.
+*/
static TriggerPrg *getRowTrigger(
- Parse *pParse,
+ Parse *pParse, /* Current parse context */
Trigger *pTrigger, /* Trigger to code */
- int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
- Table *pTab, /* The table to code triggers from */
- int orconf
+ Table *pTab, /* The table trigger pTrigger is attached to */
+ int orconf /* ON CONFLICT algorithm. */
){
+ Parse *pRoot = sqlite3ParseToplevel(pParse);
TriggerPrg *pPrg;
- Parse *pRoot = pParse;
+
+ assert( pTab==tableOfTrigger(pTrigger) );
/* It may be that this trigger has already been coded (or is in the
** process of being coded). If this is the case, then an entry with
** a matching TriggerPrg.pTrigger field will be present somewhere
** in the Parse.pTriggerPrg list. Search for such an entry. */
- if( pParse->pRoot ){
- pRoot = pParse->pRoot;
- }
for(pPrg=pRoot->pTriggerPrg;
pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf);
pPrg=pPrg->pNext
);
+ /* If an existing TriggerPrg could not be located, create a new one. */
if( !pPrg ){
- pPrg = codeRowTrigger(pRoot, pParse, pTrigger, op, pTab, orconf);
+ pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf);
}
return pPrg;
@@ -962,7 +975,7 @@
){
Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */
TriggerPrg *pPrg;
- pPrg = getRowTrigger(pParse, p, op, pTab, orconf);
+ pPrg = getRowTrigger(pParse, p, pTab, orconf);
assert( pPrg || pParse->nErr || pParse->db->mallocFailed );
/* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program
@@ -1011,7 +1024,7 @@
for(p=pTrigger; p; p=p->pNext){
if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){
TriggerPrg *pPrg;
- pPrg = getRowTrigger(pParse, p, op, pTab, orconf);
+ pPrg = getRowTrigger(pParse, p, pTab, orconf);
if( pPrg ){
mask |= pPrg->oldmask;
}