aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <dan@noemail.net>2009-08-30 11:42:51 +0000
committerdan <dan@noemail.net>2009-08-30 11:42:51 +0000
commit76d462ee7851a3ca27e8075cc540b440a4ed0135 (patch)
treee61387b14c264a4b97094703d867c600c5c63969 /src
parent165921a7427ef9dd52a398c9ef7edcbd2feffae4 (diff)
downloadsqlite-76d462ee7851a3ca27e8075cc540b440a4ed0135.tar.gz
sqlite-76d462ee7851a3ca27e8075cc540b440a4ed0135.zip
Fixes for new triggers scheme.
FossilOrigin-Name: 9eb91efda5241609ff18ff15ef5eaa0e86788eab
Diffstat (limited to 'src')
-rw-r--r--src/delete.c7
-rw-r--r--src/expr.c13
-rw-r--r--src/insert.c51
-rw-r--r--src/main.c3
-rw-r--r--src/pragma.c1
-rw-r--r--src/sqliteInt.h1
-rw-r--r--src/trigger.c25
-rw-r--r--src/update.c40
-rw-r--r--src/vdbe.c135
-rw-r--r--src/vdbeInt.h20
-rw-r--r--src/vdbeaux.c10
11 files changed, 158 insertions, 148 deletions
diff --git a/src/delete.c b/src/delete.c
index 1425fea83..d4328d5b0 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -408,11 +408,14 @@ void sqlite3DeleteFrom(
for(i=0; i<pTab->nCol; i++){
if( mask==0xffffffff || mask&(1<<i) ){
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
+ sqlite3ColumnDefault(v, pTab, i, regOld+i);
}
}
+ sqlite3VdbeAddOp2(v, OP_Affinity, regOld, pTab->nCol);
+ sqlite3TableAffinityStr(v, pTab);
sqlite3CodeRowTrigger(pParse, pTrigger,
- TK_DELETE, 0, TRIGGER_BEFORE, pTab, -1, regOld, OE_Default, addr
+ TK_DELETE, 0, TRIGGER_BEFORE, pTab, -1, iRowid, OE_Default, addr
);
}
@@ -432,7 +435,7 @@ void sqlite3DeleteFrom(
/* Code the AFTER triggers. This is a no-op if there are no triggers. */
sqlite3CodeRowTrigger(pParse,
- pTrigger, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, regOld, OE_Default, addr
+ pTrigger, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, iRowid, OE_Default, addr
);
/* End of the delete loop */
diff --git a/src/expr.c b/src/expr.c
index 62616199c..959bc2229 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -90,7 +90,9 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
pColl = p->pColl;
if( pColl ) break;
op = p->op;
- if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER) && p->pTab!=0 ){
+ if( p->pTab!=0 && (
+ op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER
+ )){
/* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
** a TK_COLUMN but was previously evaluated and cached in a register */
const char *zColl;
@@ -2557,11 +2559,12 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
}
case TK_TRIGGER: {
- sqlite3VdbeAddOp3(v, OP_TriggerVal, pExpr->iColumn, target,pExpr->iTable);
- assert( pExpr->pTab );
- VdbeComment((v, "%s.%s",
+ int iVal = pExpr->iTable * (pExpr->pTab->nCol+1) + 1 + pExpr->iColumn;
+ sqlite3VdbeAddOp2(v, OP_Param, iVal, target);
+ VdbeComment((v, "%s.%s -> $%d",
(pExpr->iTable ? "new" : "old"),
- (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName)
+ (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName),
+ target
));
break;
}
diff --git a/src/insert.c b/src/insert.c
index 94a59a3af..7afdd97c5 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -197,20 +197,21 @@ static int autoIncBegin(
){
int memId = 0; /* Register holding maximum rowid */
if( pTab->tabFlags & TF_Autoincrement ){
+ Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse);
AutoincInfo *pInfo;
- pInfo = pParse->pAinc;
+ pInfo = pRoot->pAinc;
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){
pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo));
if( pInfo==0 ) return 0;
- pInfo->pNext = pParse->pAinc;
- pParse->pAinc = pInfo;
+ pInfo->pNext = pRoot->pAinc;
+ pRoot->pAinc = pInfo;
pInfo->pTab = pTab;
pInfo->iDb = iDb;
- pParse->nMem++; /* Register to hold name of table */
- pInfo->regCtr = ++pParse->nMem; /* Max rowid register */
- pParse->nMem++; /* Rowid in sqlite_sequence */
+ pRoot->nMem++; /* Register to hold name of table */
+ pInfo->regCtr = ++pRoot->nMem; /* Max rowid register */
+ pRoot->nMem++; /* Rowid in sqlite_sequence */
}
memId = pInfo->regCtr;
}
@@ -229,6 +230,9 @@ void sqlite3AutoincrementBegin(Parse *pParse){
int addr; /* A VDBE address */
Vdbe *v = pParse->pVdbe; /* VDBE under construction */
+ /* If currently generating a trigger program, this call is a no-op */
+ if( pParse->pTriggerTab ) return;
+
assert( v ); /* We failed long ago if this is not so */
for(p = pParse->pAinc; p; p = p->pNext){
pDb = &db->aDb[p->iDb];
@@ -805,8 +809,7 @@ void sqlite3Insert(
*/
endOfLoop = sqlite3VdbeMakeLabel(v);
if( tmask & TRIGGER_BEFORE ){
- int regTrigRowid;
- int regCols;
+ int regCols = sqlite3GetTempRange(pParse, pTab->nCol+1);
/* build the NEW.* reference row. Note that if there is an INTEGER
** PRIMARY KEY into which a NULL is being inserted, that NULL will be
@@ -814,21 +817,20 @@ void sqlite3Insert(
** we do not know what the unique ID will be (because the insert has
** not happened yet) so we substitute a rowid of -1
*/
- regTrigRowid = sqlite3GetTempReg(pParse);
if( keyColumn<0 ){
- sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid);
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
}else{
int j1;
if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regTrigRowid);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols);
}else{
assert( pSelect==0 ); /* Otherwise useTempTable is true */
- sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regTrigRowid);
+ sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regCols);
}
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regTrigRowid);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols);
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
sqlite3VdbeJumpHere(v, j1);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regTrigRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols);
}
/* Cannot have triggers on a virtual table. If it were possible,
@@ -838,7 +840,6 @@ void sqlite3Insert(
/* Create the new column data
*/
- regCols = sqlite3GetTempRange(pParse, pTab->nCol);
for(i=0; i<pTab->nCol; i++){
if( pColumn==0 ){
j = i;
@@ -848,12 +849,12 @@ void sqlite3Insert(
}
}
if( pColumn && j>=pColumn->nId ){
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i);
+ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1);
}else if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1);
}else{
assert( pSelect==0 ); /* Otherwise useTempTable is true */
- sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i);
+ sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1);
}
}
@@ -863,16 +864,15 @@ void sqlite3Insert(
** table column affinities.
*/
if( !isView ){
- sqlite3VdbeAddOp2(v, OP_Affinity, regCols, pTab->nCol);
+ sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol);
sqlite3TableAffinityStr(v, pTab);
}
/* Fire BEFORE or INSTEAD OF triggers */
sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
- pTab, regCols, -1, onError, endOfLoop);
+ pTab, -1, regCols-pTab->nCol-1, onError, endOfLoop);
- sqlite3ReleaseTempReg(pParse, regTrigRowid);
- sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
+ sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1);
}
/* Push the record number for the new entry onto the stack. The
@@ -994,7 +994,7 @@ void sqlite3Insert(
if( pTrigger ){
/* Code AFTER triggers */
sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
- pTab, regData, -1, onError, endOfLoop);
+ pTab, -1, regData-2-pTab->nCol, onError, endOfLoop);
}
/* The bottom of the main insertion loop, if the data source
@@ -1153,7 +1153,6 @@ void sqlite3GenerateConstraintChecks(
nCol = pTab->nCol;
regData = regRowid + 1;
-
/* Test all NOT NULL constraints.
*/
for(i=0; i<nCol; i++){
@@ -1229,7 +1228,7 @@ void sqlite3GenerateConstraintChecks(
if( onError!=OE_Replace || pTab->pIndex ){
if( isUpdate ){
- j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, regRowid-1);
+ j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng);
}
j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid);
switch( onError ){
diff --git a/src/main.c b/src/main.c
index 6322a4355..ee0e91bf0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1591,6 +1591,9 @@ static int openDatabase(
#ifdef SQLITE_ENABLE_LOAD_EXTENSION
| SQLITE_LoadExtension
#endif
+#ifdef SQLITE_DISABLE_RECURSIVE_TRIGGERS
+ | SQLITE_NoRecTriggers
+#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
diff --git a/src/pragma.c b/src/pragma.c
index 071e961d4..64f433206 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -190,6 +190,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
/* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
** flag if there are any active statements. */
{ "read_uncommitted", SQLITE_ReadUncommitted },
+ { "disable_recursive_triggers", SQLITE_NoRecTriggers },
};
int i;
const struct sPragmaType *p;
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 5482eee9d..278a17981 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -911,6 +911,7 @@ struct sqlite3 {
#define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */
#define SQLITE_ReverseOrder 0x00100000 /* Reverse unordered SELECTs */
+#define SQLITE_NoRecTriggers 0x00200000 /* Disable recursive triggers */
/*
** Possible values for the sqlite.magic field.
diff --git a/src/trigger.c b/src/trigger.c
index e59c53bb0..4b3e4fd57 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -698,6 +698,10 @@ static int codeTriggerProgram(
*/
pParse->orconf = (orconfin==OE_Default)?pStep->orconf:orconfin;
+ if( pStep->op!=TK_SELECT ){
+ sqlite3VdbeAddOp1(v, OP_ResetCount, 0);
+ }
+
switch( pStep->op ){
case TK_UPDATE: {
sqlite3Update(pParse,
@@ -734,6 +738,9 @@ static int codeTriggerProgram(
break;
}
}
+ if( pStep->op!=TK_SELECT ){
+ sqlite3VdbeAddOp1(v, OP_ResetCount, 1);
+ }
pStep = pStep->pNext;
}
/* sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); */
@@ -818,13 +825,19 @@ static CodedTrigger *codeRowTrigger(
v = sqlite3GetVdbe(pSubParse);
if( v ){
- VdbeComment((v, "Trigger: %s (%s %s%s%s ON %s) (%s)", pTrigger->zName,
+ 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" : ""),
- pTab->zName, onErrorText(orconf)
+ pTab->zName
));
+#ifndef SQLITE_OMIT_TRACE
+ sqlite3VdbeChangeP4(v, -1,
+ sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC
+ );
+#endif
if( pTrigger->pWhen ){
/* Code the WHEN clause. If it evaluates to false (or NULL) the
@@ -838,11 +851,13 @@ static CodedTrigger *codeRowTrigger(
}
/* Code the trigger program into the sub-vdbe. */
- codeTriggerProgram(pSubParse, pTrigger->step_list, OE_Default);
+ codeTriggerProgram(pSubParse, pTrigger->step_list, orconf);
if( iEndTrigger ){
sqlite3VdbeResolveLabel(v, iEndTrigger);
}
sqlite3VdbeAddOp0(v, OP_Halt);
+ VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf)));
+
transferParseError(pParse, pSubParse);
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pParse->nArg);
pProgram->nMem = pSubParse->nMem;
@@ -961,10 +976,10 @@ void sqlite3CodeRowTrigger(
/* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program
** is a pointer to the sub-vdbe containing the trigger program. */
if( pC ){
- sqlite3VdbeAddOp3(v, OP_Program, oldIdx, newIdx, ++pParse->nMem);
+ sqlite3VdbeAddOp3(v, OP_Program, oldIdx, ignoreJump, ++pParse->nMem);
pC->pProgram->nRef++;
sqlite3VdbeChangeP4(v, -1, (const char *)pC->pProgram, P4_SUBPROGRAM);
- VdbeComment((v, "Call trigger: %s (%s)", p->zName,onErrorText(orconf)));
+ VdbeComment((v, "Call: %s.%s", p->zName, onErrorText(orconf)));
}
}
}
diff --git a/src/update.c b/src/update.c
index 1f03e31c2..d44aace9a 100644
--- a/src/update.c
+++ b/src/update.c
@@ -130,6 +130,7 @@ void sqlite3Update(
int regNew;
int regOld;
int regRowSet = 0; /* Rowset of rows to be updated */
+ int regRec; /* Register used for new table record to insert */
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
@@ -145,7 +146,7 @@ void sqlite3Update(
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
/* Figure out if we have any triggers and if the table being
- ** updated is a view
+ ** updated is a view.
*/
#ifndef SQLITE_OMIT_TRIGGER
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0);
@@ -282,6 +283,7 @@ void sqlite3Update(
}
regNew = pParse->nMem + 1;
pParse->nMem += pTab->nCol;
+ regRec = ++pParse->nMem;
/* Start the view context. */
if( isView ){
@@ -421,16 +423,29 @@ void sqlite3Update(
}
}
+ /* If this is not a view, create the record that will be inserted into
+ ** the table (assuming no constraint checks fail). A side effect of
+ ** creating the record is applying affinity transformations to the
+ ** array of registers populated by the block above. This needs to be
+ ** done before the BEFORE triggers are fired. */
+#if 0
+ if( !isView ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regNew, pTab->nCol, regRec);
+ sqlite3TableAffinityStr(v, pTab);
+ sqlite3ExprCacheAffinityChange(pParse, regNew, pTab->nCol);
+ }
+#endif
+
/* Fire any BEFORE UPDATE triggers. This happens before constraints are
** verified. One could argue that this is wrong. */
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_BEFORE, pTab, regNew, regOld, onError, addr);
+ TRIGGER_BEFORE, pTab, -1, regOldRowid, onError, addr);
if( !isView ){
/* Do constraint checks. */
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
- aRegIdx, chngRowid, 1, onError, addr, 0);
+ aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0);
/* Delete the index entries associated with the current record. */
j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
@@ -442,8 +457,21 @@ void sqlite3Update(
}
sqlite3VdbeJumpHere(v, j1);
- /* Create the new index entries and the new record. */
- sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx,1,-1,0,0);
+ /* Insert the new index entries and the new record. */
+ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, -1, 0, 0);
+
+#if 0
+ for(i=0; i<nIdx; i++){
+ if( aRegIdx[i] ){
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iCur+1+i, aRegIdx[i]);
+ }
+ }
+ sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, regNewRowid);
+ if( !pParse->nested ){
+ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
+ sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_ISUPDATE);
+ }
+#endif
}
/* Increment the row counter
@@ -453,7 +481,7 @@ void sqlite3Update(
}
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_AFTER, pTab, regNew, regOld, onError, addr);
+ TRIGGER_AFTER, pTab, -1, regOldRowid, onError, addr);
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
diff --git a/src/vdbe.c b/src/vdbe.c
index 92d61cd43..1ce352342 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -852,6 +852,7 @@ case OP_Halt: {
p->nFrame--;
pc = sqlite3VdbeFrameRestore(pFrame);
if( pOp->p2==OE_Ignore ){
+ pc = p->aOp[pc].p2-1;
}
break;
}
@@ -3546,11 +3547,11 @@ case OP_Sequence: { /* out2-prerelease */
** table that cursor P1 points to. The new record number is written
** written to register P2.
**
-** If P3>0 then P3 is a register that holds the largest previously
-** generated record number. No new record numbers are allowed to be less
-** than this value. When this value reaches its maximum, a SQLITE_FULL
-** error is generated. The P3 register is updated with the generated
-** record number. This P3 mechanism is used to help implement the
+** If P3>0 then P3 is a register in the root frame of this VDBE that holds
+** the largest previously generated record number. No new record numbers are
+** allowed to be less than this value. When this value reaches its maximum,
+** a SQLITE_FULL error is generated. The P3 register is updated with the '
+** generated record number. This P3 mechanism is used to help implement the
** AUTOINCREMENT feature.
*/
case OP_NewRowid: { /* out2-prerelease */
@@ -3559,6 +3560,7 @@ case OP_NewRowid: { /* out2-prerelease */
int res; /* Result of an sqlite3BtreeLast() */
int cnt; /* Counter to limit the number of searches */
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
+ VdbeFrame *pFrame; /* Root frame of VDBE */
v = 0;
res = 0;
@@ -3617,9 +3619,16 @@ case OP_NewRowid: { /* out2-prerelease */
#ifndef SQLITE_OMIT_AUTOINCREMENT
if( pOp->p3 ){
- assert( pOp->p3>0 && pOp->p3<=p->nMem ); /* P3 is a valid memory cell */
- pMem = &p->aMem[pOp->p3];
- REGISTER_TRACE(pOp->p3, pMem);
+ if( p->pFrame ){
+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
+ pMem = &pFrame->aMem[pOp->p3];
+ }else{
+ pMem = &p->aMem[pOp->p3];
+ }
+ /* Assert that P3 is a valid memory cell. */
+ assert( pOp->p3>0 && pOp->p3<=(p->pFrame ? pFrame->nMem : p->nMem) );
+
+ REGISTER_TRACE(pOp->p3, pMem);
sqlite3VdbeMemIntegerify(pMem);
assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){
@@ -4740,68 +4749,29 @@ case OP_RowSetTest: { /* jump, in1, in3 */
#ifndef SQLITE_OMIT_TRIGGER
-/* Opcode: ContextPush * * *
-**
-** Save the current Vdbe context such that it can be restored by a ContextPop
-** opcode. The context stores the last insert row id, the last statement change
-** count, and the current statement change count.
-*/
-case OP_ContextPush: {
- int i;
- Context *pContext;
-
- i = p->contextStackTop++;
- assert( i>=0 );
- /* FIX ME: This should be allocated as part of the vdbe at compile-time */
- if( i>=p->contextStackDepth ){
- p->contextStackDepth = i+1;
- p->contextStack = sqlite3DbReallocOrFree(db, p->contextStack,
- sizeof(Context)*(i+1));
- if( p->contextStack==0 ) goto no_mem;
- }
- pContext = &p->contextStack[i];
- pContext->lastRowid = db->lastRowid;
- pContext->nChange = p->nChange;
- break;
-}
-
-/* Opcode: ContextPop * * *
-**
-** Restore the Vdbe context to the state it was in when contextPush was last
-** executed. The context stores the last insert row id, the last statement
-** change count, and the current statement change count.
-*/
-case OP_ContextPop: {
- Context *pContext;
- pContext = &p->contextStack[--p->contextStackTop];
- assert( p->contextStackTop>=0 );
- db->lastRowid = pContext->lastRowid;
- p->nChange = pContext->nChange;
- break;
-}
/* Opcode: Program P1 P2 P3 P4 *
**
-** Execute a trigger program. P1 contains the address of the memory cell
-** that contains the left-most column of the old.* table (unless the trigger
-** program is firing as a result of an INSERT statement). P2 is the address
-** of the corresponding column in the new.* table (unless the trigger
-** program is being fired due to a DELETE).
+** Execute the trigger program passed as P4 (type P4_SUBPROGRAM).
**
-** Register P3 contains the address of a memory cell in this (the parent)
-** VM that is used to allocate the memory required by the sub-vdbe at
-** runtime.
+** P1 contains the address of the memory cell that contains the first memory
+** cell in an array of values used as arguments to the sub-program. P2
+** contains the address to jump to if the sub-program throws an IGNORE
+** exception using the RAISE() function. Register P3 contains the address
+** of a memory cell in this (the parent) VM that is used to allocate the
+** memory required by the sub-vdbe at runtime.
**
** P4 is a pointer to the VM containing the trigger program.
*/
-case OP_Program: {
+case OP_Program: { /* jump */
VdbeFrame *pFrame;
SubProgram *pProgram = pOp->p4.pProgram;
Mem *pRt = &p->aMem[pOp->p3]; /* Register to allocate runtime space */
assert( pProgram->nOp>0 );
- /* If noRecTrigger is true, then recursive invocation of triggers is
- ** disabled for backwards compatibility.
+ /* If the SQLITE_NoRecTriggers flag it set, then recursive invocation of
+ ** triggers is disabled for backwards compatibility (flag set/cleared by
+ ** the "PRAGMA disable_recursive_triggers" command).
**
** It is recursive invocation of triggers, at the SQL level, that is
** disabled. In some cases a single trigger may generate more than one
@@ -4810,7 +4780,7 @@ case OP_Program: {
** single trigger all have the same value for the SubProgram.token
** variable.
*/
- if( 1 || p->noRecTrigger ){
+ if( db->flags&SQLITE_NoRecTriggers ){
void *t = pProgram->token;
for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent);
if( pFrame ) break;
@@ -4874,6 +4844,8 @@ case OP_Program: {
p->nFrame++;
pFrame->pParent = p->pFrame;
+ pFrame->lastRowid = db->lastRowid;
+ pFrame->nChange = p->nChange;
p->pFrame = pFrame;
p->aMem = &VdbeFrameMem(pFrame)[-1];
p->nMem = pFrame->nChildMem;
@@ -4886,26 +4858,21 @@ case OP_Program: {
break;
}
-/* Opcode: TriggerVal P1 P2 P3 * *
+/* Opcode: Param P1 P2 * * *
**
-** Copy a value currently stored in a memory cell of the parent VM to
-** a cell in this VMs address space. This is used by trigger programs
-** to access the new.* and old.* values.
+** This opcode is only ever present in sub-programs called via the
+** OP_Program instruction. Copy a value currently stored in a memory
+** cell of the calling (parent) frame to cell P2 in the current frames
+** address space. This is used by trigger programs to access the new.*
+** and old.* values.
**
-** If parameter P3 is non-zero, then the value read is from the new.*
-** table. If P3 is zero, then the value is read from the old.* table.
-** Parameter P1 is the index of the required new.* or old.* column (or
-** -1 for rowid).
-**
-** Parameter P2 is the index of the memory cell in this VM to copy the
-** value to.
+** The address of the cell in the parent frame is determined by adding
+** the value of the P1 argument to the value of the P1 argument to the
+** calling OP_Program instruction.
*/
-case OP_TriggerVal: { /* out2-prerelease */
- VdbeFrame *pF = p->pFrame;
- Mem *pIn;
- int iFrom = pOp->p1; /* Memory cell in parent frame */
- iFrom += (pOp->p3 ? pF->aOp[pF->pc].p2 : pF->aOp[pF->pc].p1);
- pIn = &pF->aMem[iFrom];
+case OP_Param: { /* out2-prerelease */
+ VdbeFrame *pFrame = p->pFrame;
+ Mem *pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1];
sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem);
break;
}
@@ -4915,13 +4882,23 @@ case OP_TriggerVal: { /* out2-prerelease */
#ifndef SQLITE_OMIT_AUTOINCREMENT
/* Opcode: MemMax P1 P2 * * *
**
-** Set the value of register P1 to the maximum of its current value
-** and the value in register P2.
+** P1 is a register in the root frame of this VM (the root frame is
+** different from the current frame if this instruction is being executed
+** within a sub-program). Set the value of register P1 to the maximum of
+** its current value and the value in register P2.
**
** This instruction throws an error if the memory cell is not initially
** an integer.
*/
-case OP_MemMax: { /* in1, in2 */
+case OP_MemMax: { /* in2 */
+ Mem *pIn1;
+ VdbeFrame *pFrame;
+ if( p->pFrame ){
+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
+ pIn1 = &pFrame->aMem[pOp->p1];
+ }else{
+ pIn1 = &p->aMem[pOp->p1];
+ }
sqlite3VdbeMemIntegerify(pIn1);
sqlite3VdbeMemIntegerify(pIn2);
if( pIn1->u.i<pIn2->u.i){
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index fae1f09c5..c2c19beae 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -103,6 +103,8 @@ struct VdbeFrame {
void *token; /* Copy of SubProgram.token */
int nChildMem; /* Number of memory cells for child frame */
int nChildCsr; /* Number of cursors for child frame */
+ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
+ int nChange; /* Statement changes (Vdbe.nChanges) */
};
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
@@ -244,21 +246,6 @@ struct Set {
};
/*
-** A Context stores the last insert rowid, the last statement change count,
-** and the current statement change count (i.e. changes since last statement).
-** The current keylist is also stored in the context.
-** Elements of Context structure type make up the ContextStack, which is
-** updated by the ContextPush and ContextPop opcodes (used by triggers).
-** The context is pushed before executing a trigger a popped when the
-** trigger finishes.
-*/
-typedef struct Context Context;
-struct Context {
- i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
- int nChange; /* Statement changes (Vdbe.nChanges) */
-};
-
-/*
** An instance of the virtual machine. This structure contains the complete
** state of the virtual machine.
**
@@ -297,9 +284,6 @@ struct Vdbe {
int nMem; /* Number of memory locations currently allocated */
Mem *aMem; /* The memory locations */
int cacheCtr; /* VdbeCursor row cache generation counter */
- int contextStackTop; /* Index of top element in the context stack */
- int contextStackDepth; /* The size of the "context" stack */
- Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/
int pc; /* The program counter */
int rc; /* Value to return */
char *zErrMsg; /* Error message written here */
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 46835e51a..d3e0d9cd9 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -295,7 +295,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
}else if( opcode==OP_Transaction && pOp->p2!=0 ){
p->readOnly = 0;
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( opcode==OP_VUpdate || opcode==OP_VRename ){
+ }else if( opcode==OP_VUpdate || opcode==OP_VRename || opcode==OP_Program ){
doesStatementRollback = 1;
}else if( opcode==OP_VFilter ){
int n;
@@ -1336,6 +1336,8 @@ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
v->nMem = pFrame->nMem;
v->apCsr = pFrame->apCsr;
v->nCursor = pFrame->nCursor;
+ v->db->lastRowid = pFrame->lastRowid;
+ v->nChange = pFrame->nChange;
return pFrame->pc;
}
@@ -1387,12 +1389,6 @@ static void Cleanup(Vdbe *p){
for(i=1; i<=p->nMem; i++){ assert( p->aMem[i].flags==MEM_Null ); }
#endif
- if( p->contextStack ){
- sqlite3DbFree(db, p->contextStack);
- }
- p->contextStack = 0;
- p->contextStackDepth = 0;
- p->contextStackTop = 0;
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
p->pResultSet = 0;