diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/alter.c | 6 | ||||
-rw-r--r-- | src/build.c | 30 | ||||
-rw-r--r-- | src/delete.c | 104 | ||||
-rw-r--r-- | src/expr.c | 42 | ||||
-rw-r--r-- | src/insert.c | 86 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/pragma.c | 1 | ||||
-rw-r--r-- | src/prepare.c | 7 | ||||
-rw-r--r-- | src/resolve.c | 33 | ||||
-rw-r--r-- | src/select.c | 5 | ||||
-rw-r--r-- | src/sqliteInt.h | 32 | ||||
-rw-r--r-- | src/trigger.c | 389 | ||||
-rw-r--r-- | src/update.c | 283 | ||||
-rw-r--r-- | src/vdbe.c | 201 | ||||
-rw-r--r-- | src/vdbe.h | 24 | ||||
-rw-r--r-- | src/vdbeInt.h | 45 | ||||
-rw-r--r-- | src/vdbeapi.c | 2 | ||||
-rw-r--r-- | src/vdbeaux.c | 201 | ||||
-rw-r--r-- | src/vdbeblob.c | 2 | ||||
-rw-r--r-- | src/vdbemem.c | 8 | ||||
-rw-r--r-- | src/vtab.c | 15 |
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; } } |