diff options
author | dan <dan@noemail.net> | 2009-09-01 12:16:01 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2009-09-01 12:16:01 +0000 |
commit | 65a7cd16315f08db25385fa5ea340e2fa066e4fa (patch) | |
tree | 825573dbbf8c96cc031f07ba228700d2ebf82cad /src | |
parent | 2832ad4221828a7346a903a38fc1fc3eb16188d6 (diff) | |
download | sqlite-65a7cd16315f08db25385fa5ea340e2fa066e4fa.tar.gz sqlite-65a7cd16315f08db25385fa5ea340e2fa066e4fa.zip |
More fixes and comment updates.
FossilOrigin-Name: 38a9327bad1a01e3d7a47fad44ece2f6c7e88643
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 62 | ||||
-rw-r--r-- | src/expr.c | 42 | ||||
-rw-r--r-- | src/insert.c | 44 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/prepare.c | 1 | ||||
-rw-r--r-- | src/resolve.c | 6 | ||||
-rw-r--r-- | src/select.c | 6 | ||||
-rw-r--r-- | src/sqliteInt.h | 17 | ||||
-rw-r--r-- | src/trigger.c | 107 | ||||
-rw-r--r-- | src/vdbe.c | 29 | ||||
-rw-r--r-- | src/vdbeInt.h | 20 | ||||
-rw-r--r-- | src/vdbeaux.c | 43 | ||||
-rw-r--r-- | src/vtab.c | 16 |
13 files changed, 246 insertions, 149 deletions
diff --git a/src/build.c b/src/build.c index 06e5dd563..431d5b5cd 100644 --- a/src/build.c +++ b/src/build.c @@ -64,35 +64,32 @@ void sqlite3TableLock( u8 isWriteLock, /* True for a write lock */ const char *zName /* Name of the table to be locked */ ){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); int i; int nBytes; TableLock *p; - assert( iDb>=0 ); - if( pParse->pRoot ){ - pParse = pParse->pRoot; - } - for(i=0; i<pParse->nTableLock; i++){ - p = &pParse->aTableLock[i]; + for(i=0; i<pToplevel->nTableLock; i++){ + p = &pToplevel->aTableLock[i]; if( p->iDb==iDb && p->iTab==iTab ){ p->isWriteLock = (p->isWriteLock || isWriteLock); return; } } - nBytes = sizeof(TableLock) * (pParse->nTableLock+1); - pParse->aTableLock = - sqlite3DbReallocOrFree(pParse->db, pParse->aTableLock, nBytes); - if( pParse->aTableLock ){ - p = &pParse->aTableLock[pParse->nTableLock++]; + nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); + pToplevel->aTableLock = + sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); + if( pToplevel->aTableLock ){ + p = &pToplevel->aTableLock[pToplevel->nTableLock++]; p->iDb = iDb; p->iTab = iTab; p->isWriteLock = isWriteLock; p->zName = zName; }else{ - pParse->nTableLock = 0; - pParse->db->mallocFailed = 1; + pToplevel->nTableLock = 0; + pToplevel->db->mallocFailed = 1; } } @@ -198,7 +195,7 @@ void sqlite3FinishCoding(Parse *pParse){ #endif assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem, - pParse->nTab, pParse->nArg, pParse->explain); + pParse->nTab, pParse->nMaxArg, pParse->explain); pParse->rc = SQLITE_DONE; pParse->colNamesSet = 0; }else if( pParse->rc==SQLITE_OK ){ @@ -3426,30 +3423,26 @@ int sqlite3OpenTempDatabase(Parse *pParse){ ** early in the code, before we know if any database tables will be used. */ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ - sqlite3 *db; - Vdbe *v; - int mask; - Parse *pRoot = pParse->pRoot; /* Root parse structure */ + Parse *pToplevel = sqlite3ParseToplevel(pParse); - v = sqlite3GetVdbe(pParse); - if( v==0 ) return; /* This only happens if there was a prior error */ - db = pParse->db; - if( pParse->cookieGoto==0 && pRoot==0 ){ - pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; + if( pToplevel->cookieGoto==0 ){ + Vdbe *v = sqlite3GetVdbe(pToplevel); + if( v==0 ) return; /* This only happens if there was a prior error */ + pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; } if( iDb>=0 ){ - if( pRoot==0 ){ - pRoot = pParse; - } + sqlite3 *db = pToplevel->db; + int mask; + assert( iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDb<SQLITE_MAX_ATTACHED+2 ); mask = 1<<iDb; - if( (pRoot->cookieMask & mask)==0 ){ - pRoot->cookieMask |= mask; - pRoot->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; + if( (pToplevel->cookieMask & mask)==0 ){ + pToplevel->cookieMask |= mask; + pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; if( !OMIT_TEMPDB && iDb==1 ){ - sqlite3OpenTempDatabase(pRoot); + sqlite3OpenTempDatabase(pToplevel); } } } @@ -3469,13 +3462,10 @@ 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; + Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); - if( pRoot==0 ){ - pRoot = pParse; - } - pRoot->writeMask |= 1<<iDb; - if( setStatement && pParse->nested==0 && pParse->pRoot==0 ){ + pToplevel->writeMask |= 1<<iDb; + if( setStatement && pParse->nested==0 && pParse==pToplevel ){ /* Every place where this routine is called with setStatement!=0 has ** already successfully created a VDBE. */ assert( pParse->pVdbe ); diff --git a/src/expr.c b/src/expr.c index c50a2c077..08c22fffc 100644 --- a/src/expr.c +++ b/src/expr.c @@ -1398,7 +1398,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ if( iCol<0 ){ int iMem = ++pParse->nMem; int iAddr; - sqlite3VdbeUsesBtree(v, iDb); iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); @@ -1432,9 +1431,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ char *pKey; pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); - iDb = sqlite3SchemaToIndex(db, pIdx->pSchema); - sqlite3VdbeUsesBtree(v, iDb); - iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); @@ -2559,14 +2555,48 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){ } case TK_TRIGGER: { + /* If the opcode is TK_TRIGGER, then the expression is a reference + ** to a column in the new.* or old.* pseudo-tables available to + ** trigger programs. In this case Expr.iTable is set to 1 for the + ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn + ** is set to the column of the pseudo-table to read, or to -1 to + ** read the rowid field. + ** + ** The expression is implemented using an OP_Param opcode. The p1 + ** parameter is set to 0 for an old.rowid reference, or to (i+1) + ** to reference another column of the old.* pseudo-table, where + ** i is the index of the column. For a new.rowid reference, p1 is + ** set to (n+1), where n is the number of columns in each pseudo-table. + ** For a reference to any other column in the new.* pseudo-table, p1 + ** is set to (n+2+i), where n and i are as defined previously. For + ** example, if the table on which triggers are being fired is + ** declared as: + ** + ** CREATE TABLE t1(a, b); + ** + ** Then p1 is interpreted as follows: + ** + ** p1==0 -> old.rowid p1==3 -> new.rowid + ** p1==1 -> old.a p1==4 -> new.a + ** p1==2 -> old.b p1==5 -> new.b + */ Table *pTab = pExpr->pTab; - int iVal = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; - sqlite3VdbeAddOp2(v, OP_Param, iVal, target); + int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; + + assert( pExpr->iTable==0 || pExpr->iTable==1 ); + assert( pExpr->iColumn>=-1 && pExpr->iColumn<pTab->nCol ); + assert( pTab->iPKey<0 || pExpr->iColumn!=pTab->iPKey ); + assert( p1>=0 && p1<(pTab->nCol*2+2) ); + + sqlite3VdbeAddOp2(v, OP_Param, p1, target); VdbeComment((v, "%s.%s -> $%d", (pExpr->iTable ? "new" : "old"), (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName), target )); + + /* If the column has REAL affinity, it may currently be stored as an + ** integer. Use OP_RealAffinity to make sure it is really real. */ if( pExpr->iColumn>=0 && pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL ){ diff --git a/src/insert.c b/src/insert.c index 95da84ee8..d373a1d5b 100644 --- a/src/insert.c +++ b/src/insert.c @@ -197,21 +197,21 @@ static int autoIncBegin( ){ int memId = 0; /* Register holding maximum rowid */ if( pTab->tabFlags & TF_Autoincrement ){ - Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse); + Parse *pToplevel = sqlite3ParseToplevel(pParse); AutoincInfo *pInfo; - pInfo = pRoot->pAinc; + pInfo = pToplevel->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 = pRoot->pAinc; - pRoot->pAinc = pInfo; + pInfo->pNext = pToplevel->pAinc; + pToplevel->pAinc = pInfo; pInfo->pTab = pTab; pInfo->iDb = iDb; - pRoot->nMem++; /* Register to hold name of table */ - pInfo->regCtr = ++pRoot->nMem; /* Max rowid register */ - pRoot->nMem++; /* Rowid in sqlite_sequence */ + pToplevel->nMem++; /* Register to hold name of table */ + pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ + pToplevel->nMem++; /* Rowid in sqlite_sequence */ } memId = pInfo->regCtr; } @@ -454,7 +454,6 @@ void sqlite3Insert( int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ int addrSelect = 0; /* Address of coroutine that implements the SELECT */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */ - int newIdx = -1; /* Cursor for the NEW pseudo-table */ int iDb; /* Index of database holding TABLE */ Db *pDb; /* The database containing table being inserted into */ int appendFlag = 0; /* True if the insert is likely to be an append */ @@ -470,7 +469,6 @@ void sqlite3Insert( int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ - #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ @@ -1050,26 +1048,24 @@ insert_cleanup: ** ** The input is a range of consecutive registers as follows: ** -** 1. The rowid of the row to be updated before the update. This -** value is omitted unless we are doing an UPDATE that involves a -** change to the record number or writing to a virtual table. -** -** 2. The rowid of the row after the update. +** 1. The rowid of the row after the update. ** -** 3. The data in the first column of the entry after the update. +** 2. The data in the first column of the entry after the update. ** ** i. Data from middle columns... ** ** N. The data in the last column of the entry after the update. ** -** The regRowid parameter is the index of the register containing (2). +** The regRowid parameter is the index of the register containing (1). ** -** The old rowid shown as entry (1) above is omitted unless both isUpdate -** and rowidChng are 1. isUpdate is true for UPDATEs and false for -** INSERTs. RowidChng means that the new rowid is explicitly specified by -** the update or insert statement. If rowidChng is false, it means that -** the rowid is computed automatically in an insert or that the rowid value -** is not modified by the update. +** If isUpdate is true and rowidChng is non-zero, then rowidChng contains +** the address of a register containing the rowid before the update takes +** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate +** is false, indicating an INSERT statement, then a non-zero rowidChng +** indicates that the rowid was explicitly specified as part of the +** INSERT statement. If rowidChng is false, it means that the rowid is +** computed automatically in an insert or that the rowid value is not +** modified by an update. ** ** The code generated by this routine store new index entries into ** registers identified by aRegIdx[]. No index entry is created for @@ -1144,7 +1140,7 @@ void sqlite3GenerateConstraintChecks( int iCur; /* Table cursor number */ Index *pIdx; /* Pointer to one of the indices */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ - int hasTwoRowids = (isUpdate && rowidChng); + int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; v = sqlite3GetVdbe(pParse); assert( v!=0 ); @@ -1304,7 +1300,7 @@ void sqlite3GenerateConstraintChecks( /* Check to see if the new index entry will be unique */ regR = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR); + sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR); j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0, regR, SQLITE_INT_TO_PTR(regIdx), P4_INT32); diff --git a/src/main.c b/src/main.c index ee0e91bf0..38d4117f1 100644 --- a/src/main.c +++ b/src/main.c @@ -1591,7 +1591,7 @@ static int openDatabase( #ifdef SQLITE_ENABLE_LOAD_EXTENSION | SQLITE_LoadExtension #endif -#ifdef SQLITE_DISABLE_RECURSIVE_TRIGGERS +#if 1 || defined(SQLITE_DISABLE_RECURSIVE_TRIGGERS) | SQLITE_NoRecTriggers #endif ; diff --git a/src/prepare.c b/src/prepare.c index a1e5b3f8e..b4bb6512d 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -676,6 +676,7 @@ static int sqlite3Prepare( sqlite3Error(db, rc, 0); } + /* Delete any TriggerPrg structures allocated while parsing this statement. */ while( pParse->pTriggerPrg ){ TriggerPrg *pT = pParse->pTriggerPrg; pParse->pTriggerPrg = pT->pNext; diff --git a/src/resolve.c b/src/resolve.c index b8e520254..857b67d81 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -224,11 +224,13 @@ static int lookupName( ** it is a new.* or old.* trigger argument reference */ if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){ + int op = pParse->eTriggerOp; Table *pTab = 0; - if( pParse->triggerOp!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ + assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); + if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ pExpr->iTable = 1; pTab = pParse->pTriggerTab; - }else if( pParse->triggerOp!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ + }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ pExpr->iTable = 0; pTab = pParse->pTriggerTab; } diff --git a/src/select.c b/src/select.c index c6940d704..dcc4374c1 100644 --- a/src/select.c +++ b/src/select.c @@ -2733,10 +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 = pRoot->pZombieTab; - pRoot->pZombieTab = pTabToDel; + Parse *pToplevel = sqlite3ParseToplevel(pParse); + pTabToDel->pNextZombie = pToplevel->pZombieTab; + pToplevel->pZombieTab = pTabToDel; }else{ pTabToDel->nRef--; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c394e4aa0..9efb02397 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2029,6 +2029,10 @@ struct AutoincInfo { ** TriggerPrg.orconf, is stored in the TriggerPrg.pProgram variable. ** The Parse.pTriggerPrg list never contains two entries with the same ** values for both pTrigger and orconf. +** +** The TriggerPrg.oldmask variable is set to a mask of old.* columns +** accessed (or set to 0 for triggers fired as a result of INSERT +** statements). */ struct TriggerPrg { Trigger *pTrigger; /* Trigger this program was coded from */ @@ -2098,15 +2102,14 @@ struct Parse { int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRoot; /* Register holding root page number for new objects */ AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ + int nMaxArg; /* Max args passed to user function by sub-program */ /* Information used while coding trigger programs. */ - Parse *pRoot; /* Root Parse structure */ + Parse *pToplevel; /* Parse structure for main program (or NULL) */ 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 */ + u32 oldmask; /* Mask of old.* columns referenced */ + u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ + u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ @@ -2692,6 +2695,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); void sqlite3DeleteTrigger(sqlite3*, Trigger*); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); u32 sqlite3TriggerOldmask(Parse*,Trigger*,int,ExprList*,Table*,int); +# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) #else # define sqlite3TriggersExist(B,C,D,E,F) 0 # define sqlite3DeleteTrigger(A,B) @@ -2699,6 +2703,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int); # define sqlite3UnlinkAndDeleteTrigger(A,B,C) # define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J) # define sqlite3TriggerList(X, Y) 0 +# define sqlite3ParseToplevel(p) p #endif int sqlite3JoinType(Parse*, Token*, Token*, Token*); diff --git a/src/trigger.c b/src/trigger.c index 964db70de..e17b48e74 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -666,22 +666,22 @@ static SrcList *targetSrcList( } /* -** Generate VDBE code for zero or more statements inside the body of a -** trigger. +** Generate VDBE code for the statements inside the body of a single +** trigger. */ static int codeTriggerProgram( Parse *pParse, /* The parser context */ TriggerStep *pStepList, /* List of statements inside the trigger body */ - int orconfin /* Conflict algorithm. (OE_Abort, etc) */ + int orconf /* Conflict algorithm. (OE_Abort, etc) */ ){ - TriggerStep * pStep = pStepList; + TriggerStep *pStep; Vdbe *v = pParse->pVdbe; sqlite3 *db = pParse->db; - assert( pParse->pRoot ); - assert( pStep!=0 ); + assert( pParse->pTriggerTab && pParse->pToplevel ); + assert( pStepList ); assert( v!=0 ); - while( pStep ){ + for(pStep=pStepList; pStep; pStep=pStep->pNext){ /* Figure out the ON CONFLICT policy that will be used for this step ** of the trigger program. If the statement that caused this trigger ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use @@ -695,7 +695,7 @@ static int codeTriggerProgram( ** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy ** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy */ - pParse->orconf = (orconfin==OE_Default)?pStep->orconf:orconfin; + pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:orconf; switch( pStep->op ){ case TK_UPDATE: { @@ -703,7 +703,7 @@ static int codeTriggerProgram( targetSrcList(pParse, pStep), sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3ExprDup(db, pStep->pWhere, 0), - pParse->orconf + pParse->eOrconf ); break; } @@ -713,7 +713,7 @@ static int codeTriggerProgram( sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3SelectDup(db, pStep->pSelect, 0), sqlite3IdListDup(db, pStep->pIdList), - pParse->orconf + pParse->eOrconf ); break; } @@ -736,7 +736,6 @@ static int codeTriggerProgram( if( pStep->op!=TK_SELECT ){ sqlite3VdbeAddOp1(v, OP_ResetCount, 1); } - pStep = pStep->pNext; } return 0; @@ -776,16 +775,19 @@ static void transferParseError(Parse *pTo, Parse *pFrom){ } } +/* +** Create and populate a new TriggerPrg object with a sub-program +** implementing trigger pTrigger with ON CONFLICT policy orconf. +*/ static TriggerPrg *codeRowTrigger( - Parse *pRoot, /* Root parse context */ Parse *pParse, /* Current parse context */ Trigger *pTrigger, /* Trigger to code */ - int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ - Table *pTab, /* The table to code triggers from */ - int orconf + Table *pTab, /* The table pTrigger is attached to */ + int orconf /* ON CONFLICT policy to code trigger program with */ ){ - sqlite3 *db = pParse->db; - TriggerPrg *pPrg; + Parse *pTop = sqlite3ParseToplevel(pParse); + sqlite3 *db = pParse->db; /* Database handle */ + TriggerPrg *pPrg; /* Value to return */ Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */ Vdbe *v; /* Temporary VM */ NameContext sNC; /* Name context for sub-vdbe */ @@ -793,35 +795,41 @@ static TriggerPrg *codeRowTrigger( Parse *pSubParse; /* Parse context for sub-vdbe */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */ + assert( pTab==tableOfTrigger(pTrigger) ); + + /* Allocate the TriggerPrg and SubProgram objects. To ensure that they + ** are freed if an error occurs, link them into the Parse.pTriggerPrg + ** list of the top-level Parse object sooner rather than later. */ pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg)); if( !pPrg ) return 0; - pPrg->pNext = pRoot->pTriggerPrg; - pRoot->pTriggerPrg = pPrg; + pPrg->pNext = pTop->pTriggerPrg; + pTop->pTriggerPrg = pPrg; pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram)); if( !pProgram ) return 0; pProgram->nRef = 1; - pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); - if( !pSubParse ) return 0; - - pPrg->pProgram = pProgram; pPrg->pTrigger = pTrigger; pPrg->orconf = orconf; + /* Allocate and populate a new Parse context to use for coding the + ** trigger sub-program. */ + pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); + if( !pSubParse ) return 0; memset(&sNC, 0, sizeof(sNC)); sNC.pParse = pSubParse; pSubParse->db = db; pSubParse->pTriggerTab = pTab; - pSubParse->pRoot = pRoot; + pSubParse->pToplevel = pTop; pSubParse->zAuthContext = pTrigger->zName; + pSubParse->eTriggerOp = pTrigger->op; v = sqlite3GetVdbe(pSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), - (op==TK_UPDATE ? "UPDATE" : ""), - (op==TK_INSERT ? "INSERT" : ""), - (op==TK_DELETE ? "DELETE" : ""), + (pTrigger->op==TK_UPDATE ? "UPDATE" : ""), + (pTrigger->op==TK_INSERT ? "INSERT" : ""), + (pTrigger->op==TK_DELETE ? "DELETE" : ""), pTab->zName )); #ifndef SQLITE_OMIT_TRACE @@ -830,9 +838,10 @@ static TriggerPrg *codeRowTrigger( ); #endif + /* If one was specified, code the WHEN clause. If it evaluates to false + ** (or NULL) the sub-vdbe is immediately halted by jumping to the + ** OP_Halt inserted at the end of the program. */ if( pTrigger->pWhen ){ - /* Code the WHEN clause. If it evaluates to false (or NULL) the - ** sub-vdbe is immediately halted. */ pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0); if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) && db->mallocFailed==0 @@ -845,6 +854,8 @@ static TriggerPrg *codeRowTrigger( /* Code the trigger program into the sub-vdbe. */ codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); + + /* Insert an OP_Halt at the end of the sub-program. */ if( iEndTrigger ){ sqlite3VdbeResolveLabel(v, iEndTrigger); } @@ -853,49 +864,51 @@ static TriggerPrg *codeRowTrigger( transferParseError(pParse, pSubParse); if( db->mallocFailed==0 ){ - pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pParse->nArg); + pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); } pProgram->nMem = pSubParse->nMem; pProgram->nCsr = pSubParse->nTab; pProgram->token = (void *)pTrigger; pPrg->oldmask = pSubParse->oldmask; sqlite3VdbeDelete(v); - - while( pSubParse->pAinc ){ - AutoincInfo *p = pSubParse->pAinc; - pSubParse->pAinc = p->pNext; - sqlite3DbFree(db, p); - } } + + assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); + assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); sqlite3StackFree(db, pSubParse); return pPrg; } +/* +** Return a pointer to a TriggerPrg object containing the sub-program for +** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such +** TriggerPrg object exists, a new object is allocated and populated before +** being returned. +*/ static TriggerPrg *getRowTrigger( - Parse *pParse, + Parse *pParse, /* Current parse context */ Trigger *pTrigger, /* Trigger to code */ - int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ - Table *pTab, /* The table to code triggers from */ - int orconf + Table *pTab, /* The table trigger pTrigger is attached to */ + int orconf /* ON CONFLICT algorithm. */ ){ + Parse *pRoot = sqlite3ParseToplevel(pParse); TriggerPrg *pPrg; - Parse *pRoot = pParse; + + assert( pTab==tableOfTrigger(pTrigger) ); /* It may be that this trigger has already been coded (or is in the ** process of being coded). If this is the case, then an entry with ** a matching TriggerPrg.pTrigger field will be present somewhere ** in the Parse.pTriggerPrg list. Search for such an entry. */ - if( pParse->pRoot ){ - pRoot = pParse->pRoot; - } for(pPrg=pRoot->pTriggerPrg; pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf); pPrg=pPrg->pNext ); + /* If an existing TriggerPrg could not be located, create a new one. */ if( !pPrg ){ - pPrg = codeRowTrigger(pRoot, pParse, pTrigger, op, pTab, orconf); + pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); } return pPrg; @@ -962,7 +975,7 @@ void sqlite3CodeRowTrigger( ){ Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ TriggerPrg *pPrg; - pPrg = getRowTrigger(pParse, p, op, pTab, orconf); + pPrg = getRowTrigger(pParse, p, pTab, orconf); assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program @@ -1011,7 +1024,7 @@ u32 sqlite3TriggerOldmask( for(p=pTrigger; p; p=p->pNext){ if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){ TriggerPrg *pPrg; - pPrg = getRowTrigger(pParse, p, op, pTab, orconf); + pPrg = getRowTrigger(pParse, p, pTab, orconf); if( pPrg ){ mask |= pPrg->oldmask; } diff --git a/src/vdbe.c b/src/vdbe.c index bd5138607..600b88ad8 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -4772,9 +4772,17 @@ case OP_RowSetTest: { /* jump, in1, in3 */ ** P4 is a pointer to the VM containing the trigger program. */ case OP_Program: { /* jump */ - VdbeFrame *pFrame; - SubProgram *pProgram = pOp->p4.pProgram; - Mem *pRt = &p->aMem[pOp->p3]; /* Register to allocate runtime space */ + int nMem; /* Number of memory registers for sub-program */ + int nByte; /* Bytes of runtime space required for sub-program */ + Mem *pRt; /* Register to allocate runtime space */ + Mem *pMem; /* Used to iterate through memory cells */ + Mem *pEnd; /* Last memory cell in new array */ + VdbeFrame *pFrame; /* New vdbe frame to execute in */ + SubProgram *pProgram; /* Sub-program to execute */ + void *t; /* Token identifying trigger */ + + pProgram = pOp->p4.pProgram; + pRt = &p->aMem[pOp->p3]; assert( pProgram->nOp>0 ); /* If the SQLITE_NoRecTriggers flag it set, then recursive invocation of @@ -4789,7 +4797,7 @@ case OP_Program: { /* jump */ ** variable. */ if( db->flags&SQLITE_NoRecTriggers ){ - void *t = pProgram->token; + t = pProgram->token; for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent); if( pFrame ) break; } @@ -4806,16 +4814,13 @@ case OP_Program: { /* jump */ ** 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 = pProgram->nMem + pProgram->nCsr; + nByte = ROUND8(sizeof(VdbeFrame)) + nMem * sizeof(Mem) + pProgram->nCsr * sizeof(VdbeCursor *); pFrame = sqlite3DbMallocZero(db, nByte); @@ -4880,8 +4885,10 @@ case OP_Program: { /* jump */ ** calling OP_Program instruction. */ case OP_Param: { /* out2-prerelease */ - VdbeFrame *pFrame = p->pFrame; - Mem *pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; + VdbeFrame *pFrame; + Mem *pIn; + pFrame = p->pFrame; + pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); break; } diff --git a/src/vdbeInt.h b/src/vdbeInt.h index c2c19beae..40aab1a39 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -89,6 +89,19 @@ struct VdbeCursor { }; typedef struct VdbeCursor VdbeCursor; +/* +** When a sub-program is executed (OP_Program), a structure of this type +** is allocated to store the current value of the program counter, as +** well as the current memory cell array and various other frame specific +** values stored in the Vdbe struct. When the sub-program is finished, +** these values are copied back to the Vdbe from the VdbeFrame structure, +** restoring the state of the VM to as it was before the sub-program +** began executing. +** +** Frames are stored in a linked list headed at Vdbe.pParent. Vdbe.pParent +** is the parent of the current frame, or zero if the current frame +** is the main Vdbe program. +*/ typedef struct VdbeFrame VdbeFrame; struct VdbeFrame { Vdbe *v; /* VM this frame belongs to */ @@ -99,12 +112,12 @@ struct VdbeFrame { 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) */ + i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ + int nChange; /* Statement changes (Vdbe.nChanges) */ + VdbeFrame *pParent; /* Parent of this frame */ }; #define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) @@ -308,7 +321,6 @@ struct Vdbe { #endif VdbeFrame *pFrame; /* Parent frame */ int nFrame; /* Number of frames in pFrame list */ - u8 noRecTrigger; /* True to disable recursive triggers */ }; /* diff --git a/src/vdbeaux.c b/src/vdbeaux.c index f48c415b1..546c16fe9 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -339,9 +339,24 @@ int sqlite3VdbeCurrentAddr(Vdbe *p){ return p->nOp; } +/* +** This function returns a pointer to the array of opcodes associated with +** the Vdbe passed as the first argument. It is the callers responsibility +** to arrange for the returned array to be eventually freed using the +** vdbeFreeOpArray() function. +** +** Before returning, *pnOp is set to the number of entries in the returned +** array. Also, *pnMaxArg is set to the larger of its current value and +** the number of entries in the Vdbe.apArg[] array required to execute the +** returned program. +*/ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ VdbeOp *aOp = p->aOp; assert( aOp && !p->db->mallocFailed ); + + /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ + assert( p->aMutex.nMutex==0 ); + resolveP2Values(p, pnMaxArg); *pnOp = p->nOp; p->aOp = 0; @@ -499,6 +514,11 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ } } +/* +** Free the space allocated for aOp and any p4 values allocated for the +** opcodes contained within. If aOp is not NULL it is assumed to contain +** nOp entries. +*/ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ if( aOp ){ Op *pOp; @@ -512,6 +532,19 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ sqlite3DbFree(db, aOp); } +/* +** Decrement the ref-count on the SubProgram structure passed as the +** second argument. If the ref-count reaches zero, free the structure. +** +** The array of VDBE opcodes stored as SubProgram.aOp is freed if +** either the ref-count reaches zero or parameter freeop is non-zero. +** +** Since the array of opcodes pointed to by SubProgram.aOp may directly +** or indirectly contain a reference to the SubProgram structure itself. +** By passing a non-zero freeop parameter, the caller may ensure that all +** SubProgram structures and their aOp arrays are freed, even when there +** are such circular references. +*/ void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){ if( p ){ assert( p->nRef>0 ); @@ -815,7 +848,6 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. -** */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ int mask; @@ -887,6 +919,10 @@ static void releaseMemArray(Mem *p, int N){ } } +/* +** Delete a VdbeFrame object and its contents. VdbeFrame objects are +** allocated by the OP_Program opcode in sqlite3VdbeExec(). +*/ void sqlite3VdbeFrameDelete(VdbeFrame *p){ int i; Mem *aMem = VdbeFrameMem(p); @@ -1334,6 +1370,11 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){ } } +/* +** Copy the values stored in the VdbeFrame structure to its Vdbe. This +** is used, for example, when a trigger sub-program is halted to restore +** control to the main program. +*/ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ Vdbe *v = pFrame->v; v->aOp = pFrame->aOp; diff --git a/src/vtab.c b/src/vtab.c index 12fa2f046..117f36183 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -941,21 +941,21 @@ FuncDef *sqlite3VtabOverloadFunction( ** is a no-op. */ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); int i, n; Table **apVtabLock; - Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse); assert( IsVirtual(pTab) ); - for(i=0; i<pRoot->nVtabLock; i++){ - if( pTab==pRoot->apVtabLock[i] ) return; + for(i=0; i<pToplevel->nVtabLock; i++){ + if( pTab==pToplevel->apVtabLock[i] ) return; } - n = (pRoot->nVtabLock+1)*sizeof(pRoot->apVtabLock[0]); - apVtabLock = sqlite3_realloc(pRoot->apVtabLock, n); + n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); + apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n); if( apVtabLock ){ - pRoot->apVtabLock = apVtabLock; - pRoot->apVtabLock[pRoot->nVtabLock++] = pTab; + pToplevel->apVtabLock = apVtabLock; + pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; }else{ - pRoot->db->mallocFailed = 1; + pToplevel->db->mallocFailed = 1; } } |