aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/alter.c6
-rw-r--r--src/build.c30
-rw-r--r--src/delete.c104
-rw-r--r--src/expr.c42
-rw-r--r--src/insert.c86
-rw-r--r--src/main.c3
-rw-r--r--src/pragma.c1
-rw-r--r--src/prepare.c7
-rw-r--r--src/resolve.c33
-rw-r--r--src/select.c5
-rw-r--r--src/sqliteInt.h32
-rw-r--r--src/trigger.c389
-rw-r--r--src/update.c283
-rw-r--r--src/vdbe.c201
-rw-r--r--src/vdbe.h24
-rw-r--r--src/vdbeInt.h45
-rw-r--r--src/vdbeapi.c2
-rw-r--r--src/vdbeaux.c201
-rw-r--r--src/vdbeblob.c2
-rw-r--r--src/vdbemem.c8
-rw-r--r--src/vtab.c15
21 files changed, 926 insertions, 593 deletions
diff --git a/src/alter.c b/src/alter.c
index be4a2e086..14b58df33 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -196,10 +196,10 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
if( pTrig->pSchema==pTempSchema ){
if( !zWhere ){
- zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name);
+ zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->zName);
}else{
tmp = zWhere;
- zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->name);
+ zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->zName);
sqlite3DbFree(db, tmp);
}
}
@@ -235,7 +235,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
assert( iTrigDb==iDb || iTrigDb==1 );
- sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0);
+ sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0);
}
#endif
diff --git a/src/build.c b/src/build.c
index 730caf97e..06e5dd563 100644
--- a/src/build.c
+++ b/src/build.c
@@ -69,6 +69,10 @@ void sqlite3TableLock(
TableLock *p;
assert( iDb>=0 );
+
+ if( pParse->pRoot ){
+ pParse = pParse->pRoot;
+ }
for(i=0; i<pParse->nTableLock; i++){
p = &pParse->aTableLock[i];
if( p->iDb==iDb && p->iTab==iTab ){
@@ -78,7 +82,7 @@ void sqlite3TableLock(
}
nBytes = sizeof(TableLock) * (pParse->nTableLock+1);
- pParse->aTableLock =
+ pParse->aTableLock =
sqlite3DbReallocOrFree(pParse->db, pParse->aTableLock, nBytes);
if( pParse->aTableLock ){
p = &pParse->aTableLock[pParse->nTableLock++];
@@ -194,7 +198,7 @@ void sqlite3FinishCoding(Parse *pParse){
#endif
assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
- pParse->nTab, pParse->explain);
+ pParse->nTab, pParse->nArg, pParse->explain);
pParse->rc = SQLITE_DONE;
pParse->colNamesSet = 0;
}else if( pParse->rc==SQLITE_OK ){
@@ -3425,23 +3429,27 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
sqlite3 *db;
Vdbe *v;
int mask;
+ Parse *pRoot = pParse->pRoot; /* Root parse structure */
v = sqlite3GetVdbe(pParse);
if( v==0 ) return; /* This only happens if there was a prior error */
db = pParse->db;
- if( pParse->cookieGoto==0 ){
+ if( pParse->cookieGoto==0 && pRoot==0 ){
pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1;
}
if( iDb>=0 ){
+ if( pRoot==0 ){
+ pRoot = pParse;
+ }
assert( iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 || iDb==1 );
assert( iDb<SQLITE_MAX_ATTACHED+2 );
mask = 1<<iDb;
- if( (pParse->cookieMask & mask)==0 ){
- pParse->cookieMask |= mask;
- pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
+ if( (pRoot->cookieMask & mask)==0 ){
+ pRoot->cookieMask |= mask;
+ pRoot->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
if( !OMIT_TEMPDB && iDb==1 ){
- sqlite3OpenTempDatabase(pParse);
+ sqlite3OpenTempDatabase(pRoot);
}
}
}
@@ -3461,9 +3469,13 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
** necessary to undo a write and the checkpoint should not be set.
*/
void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
+ Parse *pRoot = pParse->pRoot;
sqlite3CodeVerifySchema(pParse, iDb);
- pParse->writeMask |= 1<<iDb;
- if( setStatement && pParse->nested==0 ){
+ if( pRoot==0 ){
+ pRoot = pParse;
+ }
+ pRoot->writeMask |= 1<<iDb;
+ if( setStatement && pParse->nested==0 && pParse->pRoot==0 ){
/* Every place where this routine is called with setStatement!=0 has
** already successfully created a VDBE. */
assert( pParse->pVdbe );
diff --git a/src/delete.c b/src/delete.c
index abf7e0c74..d4328d5b0 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -228,7 +228,6 @@ void sqlite3DeleteFrom(
int iCur; /* VDBE Cursor number for pTab */
sqlite3 *db; /* Main database structure */
AuthContext sContext; /* Authorization context */
- int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
NameContext sNC; /* Name context to resolve expressions in */
int iDb; /* Database number */
int memCnt = -1; /* Memory cell used for change counting */
@@ -238,11 +237,6 @@ void sqlite3DeleteFrom(
int isView; /* True if attempting to delete from a view */
Trigger *pTrigger; /* List of table triggers, if required */
#endif
- int iBeginAfterTrigger = 0; /* Address of after trigger program */
- int iEndAfterTrigger = 0; /* Exit of after trigger program */
- int iBeginBeforeTrigger = 0; /* Address of before trigger program */
- int iEndBeforeTrigger = 0; /* Exit of before trigger program */
- u32 old_col_mask = 0; /* Mask of OLD.* columns in use */
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
@@ -293,12 +287,6 @@ void sqlite3DeleteFrom(
}
assert(!isView || pTrigger);
- /* Allocate a cursor used to store the old.* data for a trigger.
- */
- if( pTrigger ){
- oldIdx = pParse->nTab++;
- }
-
/* Assign cursor number to the table and all its indices.
*/
assert( pTabList->nSrc==1 );
@@ -322,24 +310,6 @@ void sqlite3DeleteFrom(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb);
- if( pTrigger ){
- int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
- int iGoto = sqlite3VdbeAddOp0(v, OP_Goto);
- addr = sqlite3VdbeMakeLabel(v);
-
- iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
- (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
- TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
- iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
-
- iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
- (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0,
- TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
- iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
-
- sqlite3VdbeJumpHere(v, iGoto);
- }
-
/* If we are trying to delete from a view, realize that view into
** a ephemeral table.
*/
@@ -385,8 +355,9 @@ void sqlite3DeleteFrom(
** the table and pick which records to delete.
*/
{
- int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
+ int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
+ int regOld = pParse->nMem + 1; /* Start of array for old.* (if triggers) */
int regRowid; /* Actual register containing rowids */
/* Collect rowids of every row to be deleted.
@@ -401,35 +372,31 @@ void sqlite3DeleteFrom(
}
sqlite3WhereEnd(pWInfo);
- /* Open the pseudo-table used to store OLD if there are triggers.
- */
- if( pTrigger ){
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
- }
-
/* Delete every item whose key was written to the list during the
** database scan. We have to delete items after the scan is complete
- ** because deleting an item can change the scan order.
- */
+ ** because deleting an item can change the scan order. */
end = sqlite3VdbeMakeLabel(v);
+ /* Unless this is a view, open cursors for the table we are
+ ** deleting from and all its indices. If this is a view, then the
+ ** only effect this statement has is to fire the INSTEAD OF
+ ** triggers. */
if( !isView ){
- /* Open cursors for the table we are deleting from and
- ** all its indices.
- */
sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
}
- /* This is the beginning of the delete loop. If a trigger encounters
- ** an IGNORE constraint, it jumps back to here.
- */
- if( pTrigger ){
- sqlite3VdbeResolveLabel(v, addr);
- }
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
+ /* If there are triggers, populate an array of registers with the
+ ** data required by the old.* references in the trigger bodies. */
if( pTrigger ){
- int iData = ++pParse->nMem; /* For storing row data of OLD table */
+ u32 mask = 0; /* Mask of OLD.* columns in use */
+ u32 dummy = 0; /* Unused. Initialized to prevent valgrind error. */
+ pParse->nMem += pTab->nCol;
+
+ /* Open the pseudo-table used to store OLD if there are triggers. */
+ sqlite3TriggerUses(
+ pParse, pTrigger, TK_DELETE, 0, pTab, OE_Default, &mask, &dummy);
/* If the record is no longer present in the table, jump to the
** next iteration of the loop through the contents of the fifo.
@@ -437,16 +404,19 @@ void sqlite3DeleteFrom(
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid);
/* Populate the OLD.* pseudo-table */
- if( old_col_mask ){
- sqlite3VdbeAddOp2(v, OP_RowData, iCur, iData);
- }else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, iData);
+ assert( regOld==iRowid+1 );
+ 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);
+ }
}
- sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, iData, iRowid);
+ sqlite3VdbeAddOp2(v, OP_Affinity, regOld, pTab->nCol);
+ sqlite3TableAffinityStr(v, pTab);
- /* Jump back and run the BEFORE triggers */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger);
- sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
+ sqlite3CodeRowTrigger(pParse, pTrigger,
+ TK_DELETE, 0, TRIGGER_BEFORE, pTab, -1, iRowid, OE_Default, addr
+ );
}
if( !isView ){
@@ -463,21 +433,17 @@ void sqlite3DeleteFrom(
}
}
- /* If there are row triggers, close all cursors then invoke
- ** the AFTER triggers
- */
- if( pTrigger ){
- /* Jump back and run the AFTER triggers */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
- sqlite3VdbeJumpHere(v, iEndAfterTrigger);
- }
+ /* Code the AFTER triggers. This is a no-op if there are no triggers. */
+ sqlite3CodeRowTrigger(pParse,
+ pTrigger, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, iRowid, OE_Default, addr
+ );
/* End of the delete loop */
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
sqlite3VdbeResolveLabel(v, end);
- /* Close the cursors after the loop if there are no row triggers */
- if( !isView && !IsVirtual(pTab) ){
+ /* Close the cursors open on the table and its indexes. */
+ if( !isView && !IsVirtual(pTab) ){
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
}
@@ -489,7 +455,7 @@ void sqlite3DeleteFrom(
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
*/
- if( pParse->nested==0 && pParse->trigStack==0 ){
+ if( pParse->nested==0 && pParse->pTriggerTab==0 ){
sqlite3AutoincrementEnd(pParse);
}
@@ -498,7 +464,7 @@ void sqlite3DeleteFrom(
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
- if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
+ if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
diff --git a/src/expr.c b/src/expr.c
index 0ff234ee9..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;
@@ -1521,7 +1523,7 @@ void sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
- if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->trigStack ){
+ if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
int mem = ++pParse->nMem;
sqlite3VdbeAddOp1(v, OP_If, mem);
testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem);
@@ -2556,6 +2558,18 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
break;
}
+ case TK_TRIGGER: {
+ 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),
+ target
+ ));
+ break;
+ }
+
+
/*
** Form A:
** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
@@ -2640,24 +2654,20 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
}
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
- if( !pParse->trigStack ){
+ int vrc;
+ if( !pParse->pTriggerTab ){
sqlite3ErrorMsg(pParse,
"RAISE() may only be used within a trigger-program");
return 0;
}
- if( pExpr->affinity!=OE_Ignore ){
- assert( pExpr->affinity==OE_Rollback ||
- pExpr->affinity == OE_Abort ||
- pExpr->affinity == OE_Fail );
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0,
- pExpr->u.zToken, 0);
- } else {
- assert( pExpr->affinity == OE_Ignore );
- sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
- VdbeComment((v, "raise(IGNORE)"));
- }
+ assert( pExpr->affinity==OE_Rollback
+ || pExpr->affinity==OE_Abort
+ || pExpr->affinity==OE_Fail
+ || pExpr->affinity==OE_Ignore
+ );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+ vrc = (pExpr->affinity==OE_Ignore ? SQLITE_OK : SQLITE_CONSTRAINT);
+ sqlite3VdbeAddOp4(v, OP_Halt, vrc, pExpr->affinity, 0, pExpr->u.zToken,0);
break;
}
#endif
diff --git a/src/insert.c b/src/insert.c
index 728c06ed9..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];
@@ -536,11 +540,6 @@ void sqlite3Insert(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb);
- /* if there are row triggers, allocate a temp table for new.* references. */
- if( pTrigger ){
- newIdx = pParse->nTab++;
- }
-
#ifndef SQLITE_OMIT_XFER_OPT
/* If the statement is of the form
**
@@ -744,12 +743,6 @@ void sqlite3Insert(
if( pColumn==0 && nColumn>0 ){
keyColumn = pTab->iPKey;
}
-
- /* Open the temp table for FOR EACH ROW triggers
- */
- if( pTrigger ){
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
- }
/* Initialize the count of rows to be inserted
*/
@@ -816,9 +809,7 @@ void sqlite3Insert(
*/
endOfLoop = sqlite3VdbeMakeLabel(v);
if( tmask & TRIGGER_BEFORE ){
- int regTrigRowid;
- int regCols;
- int regRec;
+ 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
@@ -826,31 +817,29 @@ 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,
** this block would have to account for hidden column.
*/
- assert(!IsVirtual(pTab));
+ assert( !IsVirtual(pTab) );
/* Create the new column data
*/
- regCols = sqlite3GetTempRange(pParse, pTab->nCol);
for(i=0; i<pTab->nCol; i++){
if( pColumn==0 ){
j = i;
@@ -860,16 +849,14 @@ 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);
}
}
- regRec = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRec);
/* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
** do not attempt any conversions before assembling the record.
@@ -877,18 +864,15 @@ void sqlite3Insert(
** table column affinities.
*/
if( !isView ){
+ sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol);
sqlite3TableAffinityStr(v, pTab);
}
- sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regTrigRowid);
- sqlite3ReleaseTempReg(pParse, regRec);
- sqlite3ReleaseTempReg(pParse, regTrigRowid);
- sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
/* Fire BEFORE or INSTEAD OF triggers */
- if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
- pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
- goto insert_cleanup;
- }
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
+ pTab, -1, regCols-pTab->nCol-1, onError, endOfLoop);
+
+ sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1);
}
/* Push the record number for the new entry onto the stack. The
@@ -1009,10 +993,8 @@ void sqlite3Insert(
if( pTrigger ){
/* Code AFTER triggers */
- if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
- pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
- goto insert_cleanup;
- }
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
+ pTab, -1, regData-2-pTab->nCol, onError, endOfLoop);
}
/* The bottom of the main insertion loop, if the data source
@@ -1041,7 +1023,7 @@ insert_end:
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
*/
- if( pParse->nested==0 && pParse->trigStack==0 ){
+ if( pParse->nested==0 && pParse->pTriggerTab==0 ){
sqlite3AutoincrementEnd(pParse);
}
@@ -1050,7 +1032,7 @@ insert_end:
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
- if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
+ if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
@@ -1171,7 +1153,6 @@ void sqlite3GenerateConstraintChecks(
nCol = pTab->nCol;
regData = regRowid + 1;
-
/* Test all NOT NULL constraints.
*/
for(i=0; i<nCol; i++){
@@ -1247,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 ){
@@ -1423,11 +1404,6 @@ void sqlite3CompleteInsertion(
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
sqlite3TableAffinityStr(v, pTab);
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
-#ifndef SQLITE_OMIT_TRIGGER
- if( newIdx>=0 ){
- sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid);
- }
-#endif
if( pParse->nested ){
pik_flags = 0;
}else{
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/prepare.c b/src/prepare.c
index e692a28bf..cd25d5a1f 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -676,6 +676,13 @@ static int sqlite3Prepare(
sqlite3Error(db, rc, 0);
}
+ while( pParse->pCodedTrigger ){
+ CodedTrigger *pT = pParse->pCodedTrigger;
+ pParse->pCodedTrigger = pT->pNext;
+ sqlite3VdbeProgramDelete(db, pT->pProgram, 0);
+ sqlite3DbFree(db, pT);
+ }
+
end_prepare:
sqlite3StackFree(db, pParse);
diff --git a/src/resolve.c b/src/resolve.c
index 9ba756765..1fe885f51 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -137,6 +137,7 @@ static int lookupName(
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */
+ int isTrigger = 0; /* True if a new.* or old.* reference. */
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
@@ -222,33 +223,29 @@ static int lookupName(
/* If we have not already resolved the name, then maybe
** it is a new.* or old.* trigger argument reference
*/
- if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){
- TriggerStack *pTriggerStack = pParse->trigStack;
+ if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
Table *pTab = 0;
u32 *piColMask = 0;
- if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){
- pExpr->iTable = pTriggerStack->newIdx;
- assert( pTriggerStack->pTab );
- pTab = pTriggerStack->pTab;
- piColMask = &(pTriggerStack->newColMask);
- }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){
- pExpr->iTable = pTriggerStack->oldIdx;
- assert( pTriggerStack->pTab );
- pTab = pTriggerStack->pTab;
- piColMask = &(pTriggerStack->oldColMask);
+ if( pParse->triggerOp!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
+ pExpr->iTable = 1;
+ pTab = pParse->pTriggerTab;
+ piColMask = &(pParse->newmask);
+ }else if( pParse->triggerOp!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
+ pExpr->iTable = 0;
+ pTab = pParse->pTriggerTab;
+ piColMask = &(pParse->oldmask);
}
if( pTab ){
int iCol;
- Column *pCol = pTab->aCol;
-
pSchema = pTab->pSchema;
cntTab++;
- for(iCol=0; iCol < pTab->nCol; iCol++, pCol++) {
+ isTrigger = 1;
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ Column *pCol = &pTab->aCol[iCol];
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
cnt++;
pExpr->iColumn = iCol==pTab->iPKey ? -1 : (i16)iCol;
- pExpr->pTab = pTab;
testcase( iCol==31 );
testcase( iCol==32 );
if( iCol>=32 ){
@@ -382,6 +379,10 @@ lookupname_end:
if( pTopNC==pNC ) break;
pTopNC = pTopNC->pNext;
}
+ if( isTrigger ){
+ pExpr->pTab = pParse->pTriggerTab;
+ pExpr->op = TK_TRIGGER;
+ }
return WRC_Prune;
} else {
return WRC_Abort;
diff --git a/src/select.c b/src/select.c
index 90fab7bb8..c6940d704 100644
--- a/src/select.c
+++ b/src/select.c
@@ -2733,9 +2733,10 @@ static int flattenSubquery(
*/
if( ALWAYS(pSubitem->pTab!=0) ){
Table *pTabToDel = pSubitem->pTab;
+ Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse);
if( pTabToDel->nRef==1 ){
- pTabToDel->pNextZombie = pParse->pZombieTab;
- pParse->pZombieTab = pTabToDel;
+ pTabToDel->pNextZombie = pRoot->pZombieTab;
+ pRoot->pZombieTab = pTabToDel;
}else{
pTabToDel->nRef--;
}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 4eda0119b..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.
@@ -2015,6 +2016,16 @@ struct AutoincInfo {
# define SQLITE_N_COLCACHE 10
#endif
+typedef struct CodedTrigger CodedTrigger;
+struct CodedTrigger {
+ SubProgram *pProgram;
+ Trigger *pTrigger;
+ u32 oldmask; /* Mask of old.* columns accessed */
+ u32 newmask; /* Mask of new.* columns accessed */
+ int orconf; /* Default ON CONFLICT policy */
+ CodedTrigger *pNext;
+};
+
/*
** An SQL parser context. A copy of this structure is passed through
** the parser and down into all the parser action routine in order to
@@ -2076,6 +2087,15 @@ struct Parse {
int regRoot; /* Register holding root page number for new objects */
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
+ /* Information used while coding trigger programs. */
+ Parse *pRoot; /* Root Parse structure */
+ Table *pTriggerTab; /* Table triggers are being coded for */
+ u32 oldmask;
+ u32 newmask;
+ int triggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
+ int nArg;
+ int orconf; /* Default ON CONFLICT policy for trigger steps */
+
/* Above is constant between recursions. Below is reset before and after
** each recursion */
@@ -2092,7 +2112,9 @@ struct Parse {
const char *zTail; /* All SQL text past the last semicolon parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
+#if 0
TriggerStack *trigStack; /* Trigger actions being coded */
+#endif
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
#ifndef SQLITE_OMIT_VIRTUALTABLE
Token sArg; /* Complete text of a module argument */
@@ -2102,6 +2124,7 @@ struct Parse {
#endif
int nHeight; /* Expression tree height of current sub-select */
Table *pZombieTab; /* List of Table objects to delete after code gen */
+ CodedTrigger *pCodedTrigger; /* Linked list of coded triggers */
};
#ifdef SQLITE_OMIT_VIRTUALTABLE
@@ -2144,7 +2167,7 @@ struct AuthContext {
* containing the SQL statements specified as the trigger program.
*/
struct Trigger {
- char *name; /* The name of the trigger */
+ char *zName; /* The name of the trigger */
char *table; /* The table or view to which the trigger applies */
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
@@ -2687,8 +2710,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
void sqlite3DropTriggerPtr(Parse*, Trigger*);
Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask);
Trigger *sqlite3TriggerList(Parse *, Table *);
- int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
- int, int, int, int, u32*, u32*);
+ void sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
+ int, int, int, int);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
@@ -2698,12 +2721,13 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
void sqlite3DeleteTrigger(sqlite3*, Trigger*);
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
+ void sqlite3TriggerUses(Parse*,Trigger*,int,ExprList*,Table*,int,u32*,u32*);
#else
# define sqlite3TriggersExist(B,C,D,E,F) 0
# define sqlite3DeleteTrigger(A,B)
# define sqlite3DropTriggerPtr(A,B)
# define sqlite3UnlinkAndDeleteTrigger(A,B,C)
-# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K,L) 0
+# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J)
# define sqlite3TriggerList(X, Y) 0
#endif
diff --git a/src/trigger.c b/src/trigger.c
index eb84d9b43..7093cbc7f 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -219,7 +219,7 @@ void sqlite3BeginTrigger(
/* Build the Trigger object */
pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
if( pTrigger==0 ) goto trigger_cleanup;
- pTrigger->name = zName;
+ pTrigger->zName = zName;
zName = 0;
pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
pTrigger->pSchema = db->aDb[iDb].pSchema;
@@ -262,14 +262,14 @@ void sqlite3FinishTrigger(
pTrig = pParse->pNewTrigger;
pParse->pNewTrigger = 0;
if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup;
- zName = pTrig->name;
+ zName = pTrig->zName;
iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
pTrig->step_list = pStepList;
while( pStepList ){
pStepList->pTrig = pTrig;
pStepList = pStepList->pNext;
}
- nameToken.z = pTrig->name;
+ nameToken.z = pTrig->zName;
nameToken.n = sqlite3Strlen30(nameToken.z);
if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken)
&& sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
@@ -451,7 +451,7 @@ TriggerStep *sqlite3TriggerDeleteStep(
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
if( pTrigger==0 ) return;
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
- sqlite3DbFree(db, pTrigger->name);
+ sqlite3DbFree(db, pTrigger->zName);
sqlite3DbFree(db, pTrigger->table);
sqlite3ExprDelete(db, pTrigger->pWhen);
sqlite3IdListDelete(db, pTrigger->pColumns);
@@ -558,11 +558,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3OpenMasterTable(pParse, iDb);
base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
- sqlite3VdbeChangeP4(v, base+1, pTrigger->name, 0);
+ sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, 0);
sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
- sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0);
+ sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0);
if( pParse->nMem<3 ){
pParse->nMem = 3;
}
@@ -674,70 +674,245 @@ static int codeTriggerProgram(
TriggerStep *pStepList, /* List of statements inside the trigger body */
int orconfin /* Conflict algorithm. (OE_Abort, etc) */
){
- TriggerStep * pTriggerStep = pStepList;
- int orconf;
+ TriggerStep * pStep = pStepList;
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
- assert( pTriggerStep!=0 );
+ assert( pParse->pRoot );
+ assert( pStep!=0 );
assert( v!=0 );
- sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0);
- VdbeComment((v, "begin trigger %s", pStepList->pTrig->name));
- while( pTriggerStep ){
- sqlite3ExprCacheClear(pParse);
- orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
- pParse->trigStack->orconf = orconf;
- switch( pTriggerStep->op ){
+/* sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0); */
+ while( pStep ){
+ /* 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
+ ** the ON CONFLICT policy that was specified as part of the trigger
+ ** step statement. Example:
+ **
+ ** CREATE TRIGGER AFTER INSERT ON t1 BEGIN;
+ ** INSERT OR REPLACE INTO t2 VALUES(new.a, new.b);
+ ** END;
+ **
+ ** 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;
+
+ if( pStep->op!=TK_SELECT ){
+ sqlite3VdbeAddOp1(v, OP_ResetCount, 0);
+ }
+
+ switch( pStep->op ){
case TK_UPDATE: {
- SrcList *pSrc;
- pSrc = targetSrcList(pParse, pTriggerStep);
- sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
- sqlite3Update(pParse, pSrc,
- sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
- sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf);
- sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
+ sqlite3Update(pParse,
+ targetSrcList(pParse, pStep),
+ sqlite3ExprListDup(db, pStep->pExprList, 0),
+ sqlite3ExprDup(db, pStep->pWhere, 0),
+ pParse->orconf
+ );
break;
}
case TK_INSERT: {
- SrcList *pSrc;
- pSrc = targetSrcList(pParse, pTriggerStep);
- sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
- sqlite3Insert(pParse, pSrc,
- sqlite3ExprListDup(db, pTriggerStep->pExprList, 0),
- sqlite3SelectDup(db, pTriggerStep->pSelect, 0),
- sqlite3IdListDup(db, pTriggerStep->pIdList), orconf);
- sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
+ sqlite3Insert(pParse,
+ targetSrcList(pParse, pStep),
+ sqlite3ExprListDup(db, pStep->pExprList, 0),
+ sqlite3SelectDup(db, pStep->pSelect, 0),
+ sqlite3IdListDup(db, pStep->pIdList),
+ pParse->orconf
+ );
break;
}
case TK_DELETE: {
- SrcList *pSrc;
- sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
- pSrc = targetSrcList(pParse, pTriggerStep);
- sqlite3DeleteFrom(pParse, pSrc,
- sqlite3ExprDup(db, pTriggerStep->pWhere, 0));
- sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
+ sqlite3DeleteFrom(pParse,
+ targetSrcList(pParse, pStep),
+ sqlite3ExprDup(db, pStep->pWhere, 0)
+ );
break;
}
- default: assert( pTriggerStep->op==TK_SELECT ); {
- Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0);
- if( ss ){
- SelectDest dest;
-
- sqlite3SelectDestInit(&dest, SRT_Discard, 0);
- sqlite3Select(pParse, ss, &dest);
- sqlite3SelectDelete(db, ss);
- }
+ default: assert( pStep->op==TK_SELECT ); {
+ SelectDest sDest;
+ Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0);
+ sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
+ sqlite3Select(pParse, pSelect, &sDest);
+ sqlite3SelectDelete(db, pSelect);
break;
}
}
- pTriggerStep = pTriggerStep->pNext;
+ if( pStep->op!=TK_SELECT ){
+ sqlite3VdbeAddOp1(v, OP_ResetCount, 1);
+ }
+ pStep = pStep->pNext;
}
- sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
- VdbeComment((v, "end trigger %s", pStepList->pTrig->name));
+/* sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); */
return 0;
}
+#ifdef SQLITE_DEBUG
+/*
+** This function is used to add VdbeComment() annotations to a VDBE
+** program. It is not used in production code, only for debugging.
+*/
+static const char *onErrorText(int onError){
+ switch( onError ){
+ case OE_Abort: return "abort";
+ case OE_Rollback: return "rollback";
+ case OE_Fail: return "fail";
+ case OE_Replace: return "replace";
+ case OE_Ignore: return "ignore";
+ case OE_Default: return "default";
+ }
+ return "n/a";
+}
+#endif
+
+/*
+** Parse context structure pFrom has just been used to create a sub-vdbe
+** (trigger program). If an error has occurred, transfer error information
+** from pFrom to pTo.
+*/
+static void transferParseError(Parse *pTo, Parse *pFrom){
+ assert( pFrom->zErrMsg==0 || pFrom->nErr );
+ assert( pTo->zErrMsg==0 || pTo->nErr );
+ if( pTo->nErr==0 ){
+ pTo->zErrMsg = pFrom->zErrMsg;
+ pTo->nErr = pFrom->nErr;
+ }else{
+ sqlite3DbFree(pFrom->db, pFrom->zErrMsg);
+ }
+}
+
+static CodedTrigger *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
+){
+ sqlite3 *db = pParse->db;
+ CodedTrigger *pC;
+ Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */
+ Vdbe *v; /* Temporary VM */
+ AuthContext sContext; /* Auth context for sub-vdbe */
+ NameContext sNC; /* Name context for sub-vdbe */
+ SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */
+ Parse *pSubParse; /* Parse context for sub-vdbe */
+ int iEndTrigger = 0; /* Label to jump to if WHEN is false */
+
+ pC = sqlite3DbMallocZero(db, sizeof(CodedTrigger));
+ if( !pC ) return 0;
+ pC->pNext = pRoot->pCodedTrigger;
+ pRoot->pCodedTrigger = pC;
+ pC->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram));
+ if( !pProgram ) return 0;
+ pProgram->nRef = 1;
+ pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
+ if( !pSubParse ) return 0;
+
+ pC->pProgram = pProgram;
+ pC->pTrigger = pTrigger;
+ pC->orconf = orconf;
+
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pSubParse;
+ pSubParse->db = db;
+ pSubParse->pTriggerTab = pTab;
+ pSubParse->pRoot = pRoot;
+
+ /* Push an entry on to the auth context stack */
+ sqlite3AuthContextPush(pParse, &sContext, pTrigger->name);
+
+ 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" : ""),
+ 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
+ ** sub-vdbe is immediately halted. */
+ pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0);
+ if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen)
+ && db->mallocFailed==0
+ ){
+ iEndTrigger = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL);
+ }
+ sqlite3ExprDelete(db, pWhen);
+ }
+
+ /* Code the trigger program into the sub-vdbe. */
+ 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);
+ if( db->mallocFailed==0 ){
+ pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pParse->nArg);
+ }
+ pProgram->nMem = pSubParse->nMem;
+ pProgram->nCsr = pSubParse->nTab;
+ pProgram->token = (void *)pTrigger;
+ pC->oldmask = pSubParse->oldmask;
+ pC->newmask = pSubParse->newmask;
+ sqlite3VdbeDelete(v);
+
+ while( pSubParse->pAinc ){
+ AutoincInfo *p = pSubParse->pAinc;
+ pSubParse->pAinc = p->pNext;
+ sqlite3DbFree(db, p);
+ }
+ }
+ sqlite3StackFree(db, pSubParse);
+
+ /* Pop the entry off the authorization stack */
+ sqlite3AuthContextPop(&sContext);
+ return pC;
+}
+
+static CodedTrigger *getRowTrigger(
+ Parse *pParse,
+ 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
+){
+ CodedTrigger *pC;
+ Parse *pRoot = pParse;
+
+ /* 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 CodedTrigger.pTrigger field will be present somewhere
+ ** in the Parse.pCodedTrigger list. Search for such an entry. */
+ if( pParse->pRoot ){
+ pRoot = pParse->pRoot;
+ }
+ for(pC=pRoot->pCodedTrigger;
+ pC && (pC->pTrigger!=pTrigger || pC->orconf!=orconf);
+ pC=pC->pNext
+ );
+
+ if( !pC ){
+ pC = codeRowTrigger(pRoot, pParse, pTrigger, op, pTab, orconf);
+ }
+
+ return pC;
+}
+
/*
** This is called to code FOR EACH ROW triggers.
**
@@ -765,7 +940,7 @@ static int codeTriggerProgram(
** output mask is set to the special value 0xffffffff.
**
*/
-int sqlite3CodeRowTrigger(
+void sqlite3CodeRowTrigger(
Parse *pParse, /* Parse context */
Trigger *pTrigger, /* List of triggers on table pTab */
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
@@ -775,100 +950,68 @@ int sqlite3CodeRowTrigger(
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) */
- u32 *piOldColMask, /* OUT: Mask of columns used from the OLD.* table */
- u32 *piNewColMask /* OUT: Mask of columns used from the NEW.* table */
+ int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */
){
Trigger *p;
- sqlite3 *db = pParse->db;
- TriggerStack trigStackEntry;
-
- trigStackEntry.oldColMask = 0;
- trigStackEntry.newColMask = 0;
assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER );
- assert(newIdx != -1 || oldIdx != -1);
-
for(p=pTrigger; p; p=p->pNext){
- int fire_this = 0;
/* Sanity checking: The schema for the trigger and for the table are
** always defined. The trigger must be in the same schema as the table
** or else it must be a TEMP trigger. */
assert( p->pSchema!=0 );
assert( p->pTabSchema!=0 );
- assert( p->pSchema==p->pTabSchema || p->pSchema==db->aDb[1].pSchema );
+ assert( p->pSchema==p->pTabSchema
+ || p->pSchema==pParse->db->aDb[1].pSchema );
/* Determine whether we should code this trigger */
- if(
- p->op==op &&
- p->tr_tm==tr_tm &&
- checkColumnOverlap(p->pColumns,pChanges)
+ if( p->op==op
+ && p->tr_tm==tr_tm
+ && checkColumnOverlap(p->pColumns,pChanges)
){
- TriggerStack *pS; /* Pointer to trigger-stack entry */
- for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){}
- if( !pS ){
- fire_this = 1;
+ Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */
+ CodedTrigger *pC;
+ pC = getRowTrigger(pParse, p, op, pTab, orconf);
+ assert( pC || pParse->nErr || pParse->db->mallocFailed );
+
+ /* 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, ignoreJump, ++pParse->nMem);
+ pC->pProgram->nRef++;
+ sqlite3VdbeChangeP4(v, -1, (const char *)pC->pProgram, P4_SUBPROGRAM);
+ VdbeComment((v, "Call: %s.%s", p->zName, onErrorText(orconf)));
}
-#if 0 /* Give no warning for recursive triggers. Just do not do them */
- else{
- sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)",
- p->name);
- return SQLITE_ERROR;
- }
-#endif
}
-
- if( fire_this ){
- int endTrigger;
- Expr * whenExpr;
- AuthContext sContext;
- NameContext sNC;
-
-#ifndef SQLITE_OMIT_TRACE
- sqlite3VdbeAddOp4(pParse->pVdbe, OP_Trace, 0, 0, 0,
- sqlite3MPrintf(db, "-- TRIGGER %s", p->name),
- P4_DYNAMIC);
-#endif
- memset(&sNC, 0, sizeof(sNC));
- sNC.pParse = pParse;
-
- /* Push an entry on to the trigger stack */
- trigStackEntry.pTrigger = p;
- trigStackEntry.newIdx = newIdx;
- trigStackEntry.oldIdx = oldIdx;
- trigStackEntry.pTab = pTab;
- trigStackEntry.pNext = pParse->trigStack;
- trigStackEntry.ignoreJump = ignoreJump;
- pParse->trigStack = &trigStackEntry;
- sqlite3AuthContextPush(pParse, &sContext, p->name);
-
- /* code the WHEN clause */
- endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
- whenExpr = sqlite3ExprDup(db, p->pWhen, 0);
- if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){
- pParse->trigStack = trigStackEntry.pNext;
- sqlite3ExprDelete(db, whenExpr);
- return 1;
- }
- sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL);
- sqlite3ExprDelete(db, whenExpr);
-
- sqlite3ExprCachePush(pParse);
- codeTriggerProgram(pParse, p->step_list, orconf);
- sqlite3ExprCachePop(pParse, 1);
+ }
+}
- /* Pop the entry off the trigger stack */
- pParse->trigStack = trigStackEntry.pNext;
- sqlite3AuthContextPop(&sContext);
+void sqlite3TriggerUses(
+ Parse *pParse, /* Parse context */
+ Trigger *pTrigger, /* List of triggers on table pTab */
+ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
+ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */
+ Table *pTab, /* The table to code triggers from */
+ int orconf, /* Default ON CONFLICT policy for trigger steps */
+ u32 *piOldColMask, /* OUT: Mask of columns used from the OLD.* table */
+ u32 *piNewColMask /* OUT: Mask of columns used from the NEW.* table */
+){
+ Trigger *p;
+ assert(op==TK_UPDATE || op==TK_INSERT || op==TK_DELETE);
- sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger);
+ for(p=pTrigger; p; p=p->pNext){
+ if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){
+ CodedTrigger *pC;
+ pC = getRowTrigger(pParse, p, op, pTab, orconf);
+ if( pC ){
+ *piOldColMask |= pC->oldmask;
+ *piNewColMask |= pC->newmask;
+ }
}
}
- if( piOldColMask ) *piOldColMask |= trigStackEntry.oldColMask;
- if( piNewColMask ) *piNewColMask |= trigStackEntry.newColMask;
- return 0;
}
+
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
diff --git a/src/update.c b/src/update.c
index dcf861213..d44aace9a 100644
--- a/src/update.c
+++ b/src/update.c
@@ -120,22 +120,17 @@ void sqlite3Update(
int isView; /* Trying to update a view */
Trigger *pTrigger; /* List of triggers on pTab, if required */
#endif
- int iBeginAfterTrigger = 0; /* Address of after trigger program */
- int iEndAfterTrigger = 0; /* Exit of after trigger program */
- int iBeginBeforeTrigger = 0; /* Address of before trigger program */
- int iEndBeforeTrigger = 0; /* Exit of before trigger program */
u32 old_col_mask = 0; /* Mask of OLD.* columns in use */
u32 new_col_mask = 0; /* Mask of NEW.* columns in use */
- int newIdx = -1; /* index of trigger "new" temp table */
- int oldIdx = -1; /* index of trigger "old" temp table */
-
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
int regOldRowid; /* The old rowid */
int regNewRowid; /* The new rowid */
- int regData; /* New data for the row */
+ 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;
@@ -151,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);
@@ -175,14 +170,6 @@ void sqlite3Update(
if( aXRef==0 ) goto update_cleanup;
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
- /* If there are FOR EACH ROW triggers, allocate cursors for the
- ** special OLD and NEW tables
- */
- if( pTrigger ){
- newIdx = pParse->nTab++;
- oldIdx = pParse->nTab++;
- }
-
/* Allocate a cursors for the main database table and for all indices.
** The index cursors might not be used, but if they are used they
** need to occur right after the database cursor. So go ahead and
@@ -268,24 +255,7 @@ void sqlite3Update(
aRegIdx[j] = reg;
}
- /* Allocate a block of register used to store the change record
- ** sent to sqlite3GenerateConstraintChecks(). There are either
- ** one or two registers for holding the rowid. One rowid register
- ** is used if chngRowid is false and two are used if chngRowid is
- ** true. Following these are pTab->nCol register holding column
- ** data.
- */
- regOldRowid = regNewRowid = pParse->nMem + 1;
- pParse->nMem += pTab->nCol + 1;
- if( chngRowid ){
- regNewRowid++;
- pParse->nMem++;
- }
- regData = regNewRowid+1;
-
-
- /* Begin generating code.
- */
+ /* Begin generating code. */
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto update_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
@@ -302,40 +272,28 @@ void sqlite3Update(
}
#endif
- /* Start the view context
- */
+ /* Allocate required registers. */
+ regOldRowid = regNewRowid = ++pParse->nMem;
+ if( pTrigger ){
+ regOld = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol;
+ }
+ if( chngRowid || pTrigger ){
+ regNewRowid = ++pParse->nMem;
+ }
+ regNew = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol;
+ regRec = ++pParse->nMem;
+
+ /* Start the view context. */
if( isView ){
sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
}
- /* Generate the code for triggers.
- */
- if( pTrigger ){
- int iGoto;
-
- /* Create pseudo-tables for NEW and OLD
- */
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
-
- iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- addr = sqlite3VdbeMakeLabel(v);
- iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
- if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr,
- &old_col_mask, &new_col_mask) ){
- goto update_cleanup;
- }
- iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
- if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr,
- &old_col_mask, &new_col_mask) ){
- goto update_cleanup;
- }
- iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- sqlite3VdbeJumpHere(v, iGoto);
- }
+ /* If there are any triggers, set old_col_mask and new_col_mask. */
+ sqlite3TriggerUses(pParse,
+ pTrigger, TK_UPDATE, pChanges, pTab, onError, &old_col_mask, &new_col_mask
+ );
/* If we are trying to update a view, realize that view into
** a ephemeral table.
@@ -374,7 +332,7 @@ void sqlite3Update(
/* Initialize the count of updated rows
*/
- if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
+ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
@@ -407,11 +365,6 @@ void sqlite3Update(
}
}
}
-
- /* Jump back to this point if a trigger encounters an IGNORE constraint. */
- if( pTrigger ){
- sqlite3VdbeResolveLabel(v, addr);
- }
/* Top of the update loop */
if( okOnePass ){
@@ -422,139 +375,113 @@ void sqlite3Update(
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
}
- if( pTrigger ){
- int regRowid;
- int regRow;
- int regCols;
-
- /* Make cursor iCur point to the record that is being updated.
- */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
- /* Generate the OLD table
- */
- regRowid = sqlite3GetTempReg(pParse);
- regRow = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
- if( !old_col_mask ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regRow);
- }else{
- sqlite3VdbeAddOp2(v, OP_RowData, iCur, regRow);
- }
- sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, regRow, regRowid);
+ /* Make cursor iCur point to the record that is being updated. If
+ ** this record does not exist for some reason (deleted by a trigger,
+ ** for example, then jump to the next iteration of the RowSet loop. */
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
- /* Generate the NEW table
- */
- if( chngRowid ){
- sqlite3ExprCodeAndCache(pParse, pRowidExpr, regRowid);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
- }else{
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
- }
- regCols = sqlite3GetTempRange(pParse, pTab->nCol);
+ /* If there are triggers on this table, populate an array of registers
+ ** with the required old.* column data. */
+ if( pTrigger ){
for(i=0; i<pTab->nCol; i++){
- if( i==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
- continue;
- }
- j = aXRef[i];
- if( (i<32 && (new_col_mask&((u32)1<<i))!=0) || new_col_mask==0xffffffff ){
- if( j<0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regCols+i);
- sqlite3ColumnDefault(v, pTab, i, -1);
- }else{
- sqlite3ExprCodeAndCache(pParse, pChanges->a[j].pExpr, regCols+i);
- }
+ if( aXRef[i]<0 || old_col_mask==0xffffffff || (old_col_mask & (1<<i)) ){
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
+ sqlite3ColumnDefault(v, pTab, i, regOld+i);
}else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
}
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRow);
- if( !isView ){
- sqlite3TableAffinityStr(v, pTab);
- sqlite3ExprCacheAffinityChange(pParse, regCols, pTab->nCol);
- }
- sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
- /* if( pParse->nErr ) goto update_cleanup; */
- sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRow, regRowid);
- sqlite3ReleaseTempReg(pParse, regRowid);
- sqlite3ReleaseTempReg(pParse, regRow);
-
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger);
- sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
}
- if( !isView ){
- /* Loop over every record that needs updating. We have to load
- ** the old data for each record to be updated because some columns
- ** might not change and we will need to copy the old value.
- ** Also, the old data is needed to delete the old index entries.
- ** So make the cursor point at the old record.
- */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
- /* If the record number will change, push the record number as it
- ** will be after the update. (The old record number is currently
- ** on top of the stack.)
- */
- if( chngRowid ){
- sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
- }
+ /* If the record number will change, set register regNewRowid to
+ ** contain the new value. If the record number is not being modified,
+ ** then regNewRowid is the same register as regOldRowid, which is
+ ** already populated. */
+ assert( chngRowid || pTrigger || regOldRowid==regNewRowid );
+ if( chngRowid ){
+ sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
+ }else if( pTrigger ){
+ sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
+ }
- /* Compute new data for this record.
- */
- for(i=0; i<pTab->nCol; i++){
- if( i==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regData+i);
- continue;
- }
+ /* Populate the array of registers beginning at regNew with the new
+ ** row data. This array is used to check constaints, create the new
+ ** table and index records, and as the values for any new.* references
+ ** made by triggers. */
+ for(i=0; i<pTab->nCol; i++){
+ if( i==pTab->iPKey ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+ }else{
j = aXRef[i];
if( j<0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regData+i);
- sqlite3ColumnDefault(v, pTab, i, regData+i);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
+ sqlite3ColumnDefault(v, pTab, i, regNew+i);
}else{
- sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regData+i);
+ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
}
}
+ }
- /* Do constraint checks
- */
+ /* 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, -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 old indices for the current record.
- */
+ /* Delete the index entries associated with the current record. */
j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
-
- /* If changing the record number, delete the old record.
- */
+
+ /* If changing the record number, delete the old record. */
if( chngRowid ){
sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
}
sqlite3VdbeJumpHere(v, j1);
+
+ /* Insert the new index entries and the new record. */
+ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, -1, 0, 0);
- /* Create 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
*/
- if( db->flags & SQLITE_CountRows && !pParse->trigStack){
+ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}
- /* If there are triggers, close all the cursors after each iteration
- ** through the loop. The fire the after triggers.
- */
- if( pTrigger ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
- sqlite3VdbeJumpHere(v, iEndAfterTrigger);
- }
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
+ 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.
@@ -569,16 +496,12 @@ void sqlite3Update(
}
}
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
- if( pTrigger ){
- sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0);
- sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0);
- }
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
*/
- if( pParse->nested==0 && pParse->trigStack==0 ){
+ if( pParse->nested==0 && pParse->pTriggerTab==0 ){
sqlite3AutoincrementEnd(pParse);
}
@@ -587,7 +510,7 @@ void sqlite3Update(
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
- if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){
+ if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
diff --git a/src/vdbe.c b/src/vdbe.c
index 44d12f68d..1ce352342 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -846,9 +846,19 @@ case OP_HaltIfNull: { /* in3 */
** is the same as executing Halt.
*/
case OP_Halt: {
+ if( pOp->p1==SQLITE_OK && p->pFrame ){
+ VdbeFrame *pFrame = p->pFrame;
+ p->pFrame = pFrame->pParent;
+ p->nFrame--;
+ pc = sqlite3VdbeFrameRestore(pFrame);
+ if( pOp->p2==OE_Ignore ){
+ pc = p->aOp[pc].p2-1;
+ }
+ break;
+ }
p->rc = pOp->p1;
- p->pc = pc;
p->errorAction = (u8)pOp->p2;
+ p->pc = pc;
if( pOp->p4.z ){
sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
}
@@ -3537,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 */
@@ -3550,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;
@@ -3608,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 ){
@@ -4731,57 +4749,156 @@ case OP_RowSetTest: { /* jump, in1, in3 */
#ifndef SQLITE_OMIT_TRIGGER
-/* Opcode: ContextPush * * *
+
+/* Opcode: Program P1 P2 P3 P4 *
+**
+** Execute the trigger program passed as P4 (type P4_SUBPROGRAM).
**
-** 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.
+** 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_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;
+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 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
+ ** SubProgram (if the trigger may be executed with more than one different
+ ** ON CONFLICT algorithm). SubProgram structures associated with a
+ ** single trigger all have the same value for the SubProgram.token
+ ** variable.
+ */
+ if( db->flags&SQLITE_NoRecTriggers ){
+ void *t = pProgram->token;
+ for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent);
+ if( pFrame ) break;
+ }
+
+ /* TODO: This constant should be configurable. */
+ if( p->nFrame>1000 ){
+ rc = SQLITE_ERROR;
+ sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion");
+ break;
+ }
+
+ /* Register pRt is used to store the memory required to save the state
+ ** of the current program, and the memory required at runtime to execute
+ ** the trigger program. If this trigger has been fired before, then pRt
+ ** is already allocated. Otherwise, it must be initialized. */
+ if( (pRt->flags&MEM_Frame)==0 ){
+ Mem *pMem;
+ Mem *pEnd;
+
+ /* SubProgram.nMem is set to the number of memory cells used by the
+ ** program stored in SubProgram.aOp. As well as these, one memory
+ ** cell is required for each cursor used by the program. Set local
+ ** variable nMem (and later, VdbeFrame.nChildMem) to this value.
+ */
+ int nMem = pProgram->nMem + pProgram->nCsr;
+ int nByte = ROUND8(sizeof(VdbeFrame))
+ + nMem * sizeof(Mem)
+ + pProgram->nCsr * sizeof(VdbeCursor *);
+ pFrame = sqlite3DbMallocZero(db, nByte);
+ if( !pFrame ){
+ goto no_mem;
+ }
+ sqlite3VdbeMemRelease(pRt);
+ pRt->flags = MEM_Frame;
+ pRt->u.pFrame = pFrame;
+
+ pFrame->v = p;
+ pFrame->nChildMem = nMem;
+ pFrame->nChildCsr = pProgram->nCsr;
+ pFrame->pc = pc;
+ pFrame->aMem = p->aMem;
+ pFrame->nMem = p->nMem;
+ pFrame->apCsr = p->apCsr;
+ pFrame->nCursor = p->nCursor;
+ pFrame->aOp = p->aOp;
+ pFrame->nOp = p->nOp;
+ pFrame->token = pProgram->token;
+
+ pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
+ for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
+ pMem->flags = MEM_Null;
+ pMem->db = db;
+ }
+ }else{
+ pFrame = pRt->u.pFrame;
+ assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem );
+ assert( pProgram->nCsr==pFrame->nChildCsr );
+ assert( pc==pFrame->pc );
+ }
+
+ 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;
+ p->nCursor = pFrame->nChildCsr;
+ p->apCsr = (VdbeCursor **)&p->aMem[p->nMem+1];
+ p->aOp = pProgram->aOp;
+ p->nOp = pProgram->nOp;
+ pc = -1;
+
break;
}
-/* Opcode: ContextPop * * *
+/* Opcode: Param P1 P2 * * *
+**
+** 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.
**
-** 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.
+** 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_ContextPop: {
- Context *pContext;
- pContext = &p->contextStack[--p->contextStackTop];
- assert( p->contextStackTop>=0 );
- db->lastRowid = pContext->lastRowid;
- p->nChange = pContext->nChange;
+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;
}
+
#endif /* #ifndef SQLITE_OMIT_TRIGGER */
#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/vdbe.h b/src/vdbe.h
index c72ebe1c8..3fef87382 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -34,6 +34,7 @@ typedef struct Vdbe Vdbe;
*/
typedef struct VdbeFunc VdbeFunc;
typedef struct Mem Mem;
+typedef struct SubProgram SubProgram;
/*
** A single instruction of the virtual machine has an opcode
@@ -48,7 +49,7 @@ struct VdbeOp {
int p1; /* First operand */
int p2; /* Second parameter (often the jump destination) */
int p3; /* The third parameter */
- union { /* forth parameter */
+ union { /* fourth parameter */
int i; /* Integer value if p4type==P4_INT32 */
void *p; /* Generic pointer */
char *z; /* Pointer to data for string (char array) types */
@@ -61,6 +62,7 @@ struct VdbeOp {
VTable *pVtab; /* Used when p4type is P4_VTAB */
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
int *ai; /* Used when p4type is P4_INTARRAY */
+ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
} p4;
#ifdef SQLITE_DEBUG
char *zComment; /* Comment to improve readability */
@@ -72,6 +74,19 @@ struct VdbeOp {
};
typedef struct VdbeOp VdbeOp;
+
+/*
+** A sub-routine used to implement a trigger program.
+*/
+struct SubProgram {
+ VdbeOp *aOp; /* Array of opcodes for sub-program */
+ int nOp; /* Elements in aOp[] */
+ int nMem; /* Number of memory cells required */
+ int nCsr; /* Number of cursors required */
+ int nRef; /* Number of pointers to this structure */
+ void *token; /* id that may be used to recursive triggers */
+};
+
/*
** A smaller version of VdbeOp used for the VdbeAddOpList() function because
** it takes up less space.
@@ -85,7 +100,7 @@ struct VdbeOpList {
typedef struct VdbeOpList VdbeOpList;
/*
-** Allowed values of VdbeOp.p3type
+** Allowed values of VdbeOp.p4type
*/
#define P4_NOTUSED 0 /* The P4 parameter is not used */
#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */
@@ -102,6 +117,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */
#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
+#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
** is made. That copy is freed when the Vdbe is finalized. But if the
@@ -168,7 +184,7 @@ void sqlite3VdbeUsesBtree(Vdbe*, int);
VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
int sqlite3VdbeMakeLabel(Vdbe*);
void sqlite3VdbeDelete(Vdbe*);
-void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int);
+void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int);
int sqlite3VdbeFinalize(Vdbe*);
void sqlite3VdbeResolveLabel(Vdbe*, int);
int sqlite3VdbeCurrentAddr(Vdbe*);
@@ -183,6 +199,8 @@ void sqlite3VdbeCountChanges(Vdbe*);
sqlite3 *sqlite3VdbeDb(Vdbe*);
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
void sqlite3VdbeSwap(Vdbe*,Vdbe*);
+VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
+void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseMemory(int);
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index e44e03a99..c2c19beae 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -89,6 +89,26 @@ struct VdbeCursor {
};
typedef struct VdbeCursor VdbeCursor;
+typedef struct VdbeFrame VdbeFrame;
+struct VdbeFrame {
+ Vdbe *v; /* VM this frame belongs to */
+ int pc; /* Program Counter */
+ Op *aOp; /* Program instructions */
+ int nOp; /* Size of aOp array */
+ Mem *aMem; /* Array of memory cells */
+ int nMem; /* Number of entries in aMem */
+ VdbeCursor **apCsr; /* Element of Vdbe cursors */
+ u16 nCursor; /* Number of entries in apCsr */
+ VdbeFrame *pParent; /* Parent of this frame */
+ 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))])
+
/*
** A value for VdbeCursor.cacheValid that means the cache is always invalid.
*/
@@ -111,6 +131,7 @@ struct Mem {
int nZero; /* Used when bit MEM_Zero is set in flags */
FuncDef *pDef; /* Used only when flags==MEM_Agg */
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
+ VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
} u;
double r; /* Real value */
sqlite3 *db; /* The associated database connection */
@@ -144,6 +165,7 @@ struct Mem {
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
+#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
#define MEM_TypeMask 0x00ff /* Mask of type bits */
/* Whenever Mem contains a valid string or blob representation, one of
@@ -224,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.
**
@@ -277,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 */
@@ -302,6 +306,9 @@ struct Vdbe {
#ifdef SQLITE_DEBUG
FILE *trace; /* Write an execution trace here, if not NULL */
#endif
+ VdbeFrame *pFrame; /* Parent frame */
+ int nFrame; /* Number of frames in pFrame list */
+ u8 noRecTrigger; /* True to disable recursive triggers */
};
/*
@@ -362,6 +369,8 @@ const char *sqlite3OpcodeName(int);
int sqlite3VdbeOpcodeHasProperty(int, int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
int sqlite3VdbeCloseStatement(Vdbe *, int);
+void sqlite3VdbeFrameDelete(VdbeFrame*);
+int sqlite3VdbeFrameRestore(VdbeFrame *);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseBuffers(Vdbe *p);
#endif
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index caaebd816..fc3af4004 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -76,7 +76,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt;
sqlite3_mutex_enter(v->db->mutex);
rc = sqlite3VdbeReset(v);
- sqlite3VdbeMakeReady(v, -1, 0, 0, 0);
+ sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0);
assert( (rc & (v->db->errMask))==rc );
rc = sqlite3ApiExit(v->db, rc);
sqlite3_mutex_leave(v->db->mutex);
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 76d1d8cbe..f48c415b1 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -266,7 +266,7 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){
*/
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int i;
- int nMaxArgs = 0;
+ int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
int *aLabel = p->aLabel;
int doesStatementRollback = 0;
@@ -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;
@@ -339,6 +339,15 @@ int sqlite3VdbeCurrentAddr(Vdbe *p){
return p->nOp;
}
+VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
+ VdbeOp *aOp = p->aOp;
+ assert( aOp && !p->db->mallocFailed );
+ resolveP2Values(p, pnMaxArg);
+ *pnOp = p->nOp;
+ p->aOp = 0;
+ return aOp;
+}
+
/*
** Add a whole list of operations to the operation stack. Return the
** address of the first operation added.
@@ -482,6 +491,39 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
sqlite3VtabUnlock((VTable *)p4);
break;
}
+ case P4_SUBPROGRAM : {
+ sqlite3VdbeProgramDelete(db, (SubProgram *)p4, 1);
+ break;
+ }
+ }
+ }
+}
+
+static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
+ if( aOp ){
+ Op *pOp;
+ for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
+ freeP4(db, pOp->p4type, pOp->p4.p);
+#ifdef SQLITE_DEBUG
+ sqlite3DbFree(db, pOp->zComment);
+#endif
+ }
+ }
+ sqlite3DbFree(db, aOp);
+}
+
+void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){
+ if( p ){
+ assert( p->nRef>0 );
+ if( freeop || p->nRef==1 ){
+ Op *aOp = p->aOp;
+ p->aOp = 0;
+ vdbeFreeOpArray(db, aOp, p->nOp);
+ p->nOp = 0;
+ }
+ p->nRef--;
+ if( p->nRef==0 ){
+ sqlite3DbFree(db, p);
}
}
}
@@ -604,6 +646,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
*/
void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
va_list ap;
+ if( !p ) return;
assert( p->nOp>0 || p->aOp==0 );
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
if( p->nOp ){
@@ -616,6 +659,7 @@ void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
}
void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
va_list ap;
+ if( !p ) return;
sqlite3VdbeAddOp0(p, OP_Noop);
assert( p->nOp>0 || p->aOp==0 );
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
@@ -752,6 +796,10 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
sqlite3_snprintf(nTemp, zTemp, "intarray");
break;
}
+ case P4_SUBPROGRAM: {
+ sqlite3_snprintf(nTemp, zTemp, "program");
+ break;
+ }
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
@@ -826,7 +874,7 @@ static void releaseMemArray(Mem *p, int N){
** with no indexes using a single prepared INSERT statement, bind()
** and reset(). Inserts are grouped into a transaction.
*/
- if( p->flags&(MEM_Agg|MEM_Dyn) ){
+ if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){
sqlite3VdbeMemRelease(p);
}else if( p->zMalloc ){
sqlite3DbFree(db, p->zMalloc);
@@ -839,6 +887,18 @@ static void releaseMemArray(Mem *p, int N){
}
}
+void sqlite3VdbeFrameDelete(VdbeFrame *p){
+ int i;
+ Mem *aMem = VdbeFrameMem(p);
+ VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem];
+ for(i=0; i<p->nChildCsr; i++){
+ sqlite3VdbeFreeCursor(p->v, apCsr[i]);
+ }
+ releaseMemArray(aMem, p->nChildMem);
+ sqlite3DbFree(p->v->db, p);
+}
+
+
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseBuffers(Vdbe *p){
int ii;
@@ -875,6 +935,10 @@ int sqlite3VdbeReleaseBuffers(Vdbe *p){
int sqlite3VdbeList(
Vdbe *p /* The VDBE */
){
+ int nRow; /* Total number of rows to return */
+ int nSub = 0; /* Number of sub-vdbes seen so far */
+ SubProgram **apSub = 0; /* Array of sub-vdbes */
+ Mem *pSub = 0;
sqlite3 *db = p->db;
int i;
int rc = SQLITE_OK;
@@ -889,7 +953,7 @@ int sqlite3VdbeList(
** the result, result columns may become dynamic if the user calls
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
- releaseMemArray(pMem, p->nMem);
+ releaseMemArray(pMem, 8);
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
@@ -898,10 +962,24 @@ int sqlite3VdbeList(
return SQLITE_ERROR;
}
+ /* Figure out total number of rows that will be returned by this
+ ** EXPLAIN program. */
+ nRow = p->nOp;
+ if( p->explain==1 ){
+ pSub = &p->aMem[9];
+ if( pSub->flags&MEM_Blob ){
+ nSub = pSub->n/sizeof(Vdbe*);
+ apSub = (SubProgram **)pSub->z;
+ }
+ for(i=0; i<nSub; i++){
+ nRow += apSub[i]->nOp;
+ }
+ }
+
do{
i = p->pc++;
- }while( i<p->nOp && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
- if( i>=p->nOp ){
+ }while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
+ if( i>=nRow ){
p->rc = SQLITE_OK;
rc = SQLITE_DONE;
}else if( db->u1.isInterrupted ){
@@ -910,7 +988,17 @@ int sqlite3VdbeList(
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
}else{
char *z;
- Op *pOp = &p->aOp[i];
+ Op *pOp;
+ if( i<p->nOp ){
+ pOp = &p->aOp[i];
+ }else{
+ int j;
+ i -= p->nOp;
+ for(j=0; i>=apSub[j]->nOp; j++){
+ i -= apSub[j]->nOp;
+ }
+ pOp = &apSub[j]->aOp[i];
+ }
if( p->explain==1 ){
pMem->flags = MEM_Int;
pMem->type = SQLITE_INTEGER;
@@ -924,6 +1012,20 @@ int sqlite3VdbeList(
pMem->type = SQLITE_TEXT;
pMem->enc = SQLITE_UTF8;
pMem++;
+
+ if( pOp->p4type==P4_SUBPROGRAM ){
+ int nByte = (nSub+1)*sizeof(SubProgram*);
+ int j;
+ for(j=0; j<nSub; j++){
+ if( apSub[j]==pOp->p4.pProgram ) break;
+ }
+ if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, 1) ){
+ apSub = (SubProgram **)pSub->z;
+ apSub[nSub++] = pOp->p4.pProgram;
+ pSub->flags |= MEM_Blob;
+ pSub->n = nSub*sizeof(SubProgram*);
+ }
+ }
}
pMem->flags = MEM_Int;
@@ -1098,6 +1200,7 @@ void sqlite3VdbeMakeReady(
int nVar, /* Number of '?' see in the SQL statement */
int nMem, /* Number of memory cells to allocate */
int nCursor, /* Number of cursors to allocate */
+ int nArg, /* Maximum number of args in SubPrograms */
int isExplain /* True if the EXPLAIN keywords is present */
){
int n;
@@ -1133,7 +1236,6 @@ void sqlite3VdbeMakeReady(
u8 *zCsr = (u8 *)&p->aOp[p->nOp];
u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc];
int nByte;
- int nArg; /* Maximum number of args passed to a user function. */
resolveP2Values(p, &nArg);
if( isExplain && nMem<10 ){
nMem = 10;
@@ -1232,19 +1334,49 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
}
}
+int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
+ Vdbe *v = pFrame->v;
+ v->aOp = pFrame->aOp;
+ v->nOp = pFrame->nOp;
+ v->aMem = pFrame->aMem;
+ v->nMem = pFrame->nMem;
+ v->apCsr = pFrame->apCsr;
+ v->nCursor = pFrame->nCursor;
+ v->db->lastRowid = pFrame->lastRowid;
+ v->nChange = pFrame->nChange;
+ return pFrame->pc;
+}
+
/*
** Close all cursors.
+**
+** Also release any dynamic memory held by the VM in the Vdbe.aMem memory
+** cell array. This is necessary as the memory cell array may contain
+** pointers to VdbeFrame objects, which may in turn contain pointers to
+** open cursors.
*/
static void closeAllCursors(Vdbe *p){
- int i;
- if( p->apCsr==0 ) return;
- for(i=0; i<p->nCursor; i++){
- VdbeCursor *pC = p->apCsr[i];
- if( pC ){
- sqlite3VdbeFreeCursor(p, pC);
- p->apCsr[i] = 0;
+ if( p->pFrame ){
+ VdbeFrame *pFrame = p->pFrame;
+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
+ sqlite3VdbeFrameRestore(pFrame);
+ }
+ p->pFrame = 0;
+ p->nFrame = 0;
+
+ if( p->apCsr ){
+ int i;
+ for(i=0; i<p->nCursor; i++){
+ VdbeCursor *pC = p->apCsr[i];
+ if( pC ){
+ sqlite3VdbeFreeCursor(p, pC);
+ p->apCsr[i] = 0;
+ }
}
}
+ if( p->aMem ){
+ releaseMemArray(&p->aMem[1], p->nMem);
+ }
}
/*
@@ -1255,23 +1387,16 @@ static void closeAllCursors(Vdbe *p){
** variables in the aVar[] array.
*/
static void Cleanup(Vdbe *p){
- int i;
sqlite3 *db = p->db;
- Mem *pMem;
- closeAllCursors(p);
- for(pMem=&p->aMem[1], i=1; i<=p->nMem; i++, pMem++){
- if( pMem->flags & MEM_RowSet ){
- sqlite3RowSetClear(pMem->u.pRowSet);
- }
- MemSetTypeFlag(pMem, MEM_Null);
- }
- releaseMemArray(&p->aMem[1], p->nMem);
- if( p->contextStack ){
- sqlite3DbFree(db, p->contextStack);
- }
- p->contextStack = 0;
- p->contextStackDepth = 0;
- p->contextStackTop = 0;
+
+#ifdef SQLITE_DEBUG
+ /* Execute assert() statements to ensure that the Vdbe.apCsr[] and
+ ** Vdbe.aMem[] arrays have already been cleaned up. */
+ int i;
+ for(i=0; i<p->nCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 );
+ for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null );
+#endif
+
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
p->pResultSet = 0;
@@ -1996,7 +2121,6 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
** Delete an entire VDBE.
*/
void sqlite3VdbeDelete(Vdbe *p){
- int i;
sqlite3 *db;
if( NEVER(p==0) ) return;
@@ -2010,22 +2134,13 @@ void sqlite3VdbeDelete(Vdbe *p){
if( p->pNext ){
p->pNext->pPrev = p->pPrev;
}
- if( p->aOp ){
- Op *pOp = p->aOp;
- for(i=0; i<p->nOp; i++, pOp++){
- freeP4(db, pOp->p4type, pOp->p4.p);
-#ifdef SQLITE_DEBUG
- sqlite3DbFree(db, pOp->zComment);
-#endif
- }
- }
releaseMemArray(p->aVar, p->nVar);
- sqlite3DbFree(db, p->aLabel);
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
+ vdbeFreeOpArray(db, p->aOp, p->nOp);
+ sqlite3DbFree(db, p->aLabel);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
p->magic = VDBE_MAGIC_DEAD;
- sqlite3DbFree(db, p->aOp);
sqlite3DbFree(db, p->pFree);
sqlite3DbFree(db, p);
}
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index 71ce228c5..b17af8acf 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -204,7 +204,7 @@ int sqlite3_blob_open(
sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
sqlite3VdbeChangeP2(v, 7, pTab->nCol);
if( !db->mallocFailed ){
- sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
+ sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0);
}
}
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 3daeebf2c..0995a3b68 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -270,7 +270,7 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
*/
void sqlite3VdbeMemReleaseExternal(Mem *p){
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
- if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet) ){
+ if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){
if( p->flags&MEM_Agg ){
sqlite3VdbeMemFinalize(p, p->u.pDef);
assert( (p->flags & MEM_Agg)==0 );
@@ -281,6 +281,9 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
p->xDel = 0;
}else if( p->flags&MEM_RowSet ){
sqlite3RowSetClear(p->u.pRowSet);
+ }else if( p->flags&MEM_Frame ){
+ sqlite3VdbeFrameDelete(p->u.pFrame);
+ p->flags &= ~MEM_Frame;
}
}
}
@@ -482,6 +485,9 @@ int sqlite3VdbeMemNumerify(Mem *pMem){
** Delete any previous value and set the value stored in *pMem to NULL.
*/
void sqlite3VdbeMemSetNull(Mem *pMem){
+ if( pMem->flags & MEM_Frame ){
+ sqlite3VdbeFrameDelete(pMem->u.pFrame);
+ }
if( pMem->flags & MEM_RowSet ){
sqlite3RowSetClear(pMem->u.pRowSet);
}
diff --git a/src/vtab.c b/src/vtab.c
index e47495152..12fa2f046 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -943,18 +943,19 @@ FuncDef *sqlite3VtabOverloadFunction(
void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
int i, n;
Table **apVtabLock;
+ Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse);
assert( IsVirtual(pTab) );
- for(i=0; i<pParse->nVtabLock; i++){
- if( pTab==pParse->apVtabLock[i] ) return;
+ for(i=0; i<pRoot->nVtabLock; i++){
+ if( pTab==pRoot->apVtabLock[i] ) return;
}
- n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]);
- apVtabLock = sqlite3_realloc(pParse->apVtabLock, n);
+ n = (pRoot->nVtabLock+1)*sizeof(pRoot->apVtabLock[0]);
+ apVtabLock = sqlite3_realloc(pRoot->apVtabLock, n);
if( apVtabLock ){
- pParse->apVtabLock = apVtabLock;
- pParse->apVtabLock[pParse->nVtabLock++] = pTab;
+ pRoot->apVtabLock = apVtabLock;
+ pRoot->apVtabLock[pRoot->nVtabLock++] = pTab;
}else{
- pParse->db->mallocFailed = 1;
+ pRoot->db->mallocFailed = 1;
}
}