More fixes and comment updates.
FossilOrigin-Name: 38a9327bad1a01e3d7a47fad44ece2f6c7e88643
This commit is contained in:
+26
-36
@@ -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 );
|
||||
|
||||
+36
-6
@@ -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
|
||||
){
|
||||
|
||||
+20
-24
@@ -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.
|
||||
** 1. The rowid of the row after the update.
|
||||
**
|
||||
** 2. 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);
|
||||
|
||||
+1
-1
@@ -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
|
||||
;
|
||||
|
||||
@@ -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;
|
||||
|
||||
+4
-2
@@ -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;
|
||||
}
|
||||
|
||||
+3
-3
@@ -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--;
|
||||
}
|
||||
|
||||
+11
-6
@@ -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*);
|
||||
|
||||
+60
-47
@@ -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;
|
||||
}
|
||||
|
||||
+18
-11
@@ -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;
|
||||
}
|
||||
|
||||
+16
-4
@@ -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 */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+42
-1
@@ -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;
|
||||
|
||||
+8
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user