diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/delete.c | 7 | ||||
-rw-r--r-- | src/expr.c | 13 | ||||
-rw-r--r-- | src/insert.c | 51 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/pragma.c | 1 | ||||
-rw-r--r-- | src/sqliteInt.h | 1 | ||||
-rw-r--r-- | src/trigger.c | 25 | ||||
-rw-r--r-- | src/update.c | 40 | ||||
-rw-r--r-- | src/vdbe.c | 135 | ||||
-rw-r--r-- | src/vdbeInt.h | 20 | ||||
-rw-r--r-- | src/vdbeaux.c | 10 |
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; |