mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Preliminary changes for a new implementation of RETURNING that captures all
results in a buffer and plays them all back after the DML statement completes. This avoids problems with interleaved DML statements. This particular check-in is a non-functional work in progress. FossilOrigin-Name: 04b77d63216ce11b4e797946953bcde504fc005807c7a5ac757fbf47d78698dc
This commit is contained in:
100
src/trigger.c
100
src/trigger.c
@@ -70,6 +70,8 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
|
||||
pTrig->pNext = pList;
|
||||
pList = pTrig;
|
||||
}else if( pTrig->op==TK_RETURNING ){
|
||||
assert( pParse->bReturning );
|
||||
assert( &(pParse->u1.pReturning->retTrig) == pTrig );
|
||||
pTrig->table = pTab->zName;
|
||||
pTrig->pTabSchema = pTab->pSchema;
|
||||
pTrig->pNext = pList;
|
||||
@@ -857,6 +859,55 @@ static ExprList *sqlite3ExpandReturning(
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for the RETURNING trigger. Unlike other triggers
|
||||
** that invoke a subprogram in the bytecode, the code for RETURNING
|
||||
** is generated in-line.
|
||||
*/
|
||||
static void codeReturningTrigger(
|
||||
Parse *pParse, /* Parse context */
|
||||
Trigger *pTrigger, /* The trigger step that defines the RETURNING */
|
||||
Table *pTab, /* The table to code triggers from */
|
||||
int reg /* The first in an array of registers */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
ExprList *pNew;
|
||||
Returning *pReturning;
|
||||
|
||||
assert( v!=0 );
|
||||
assert( pParse->bReturning );
|
||||
pReturning = pParse->u1.pReturning;
|
||||
assert( pTrigger == &(pReturning->retTrig) );
|
||||
sqlite3VdbeAddOp0(v, OP_Noop);
|
||||
VdbeComment((v, "RETURNING trigger goes here"));
|
||||
pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
|
||||
if( pNew ){
|
||||
pReturning->nRetCol = pNew->nExpr;
|
||||
pReturning->iRetCur = pParse->nTab++;
|
||||
sqlite3ExprListDelete(pParse->db, pNew);
|
||||
}
|
||||
|
||||
#if 0
|
||||
pSelect->pEList =
|
||||
sqlite3ExpandReturning(pParse, pList, pParse->pTriggerTab);
|
||||
sqlite3SelectDestInit(&sDest, SRT_Output, 0);
|
||||
pNew = sqlite3SelectDup(db, pSelect, 0);
|
||||
if( pNew ){
|
||||
sqlite3Select(pParse, pNew, &sDest);
|
||||
if( pNew->selFlags & (SF_Aggregate|SF_HasAgg|SF_WinRewrite) ){
|
||||
sqlite3ErrorMsg(pParse, "aggregates not allowed in RETURNING");
|
||||
}
|
||||
sqlite3SelectDelete(db, pNew);
|
||||
}
|
||||
sqlite3ExprListDelete(db, pSelect->pEList);
|
||||
pStep->pSelect->pEList = pList;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Generate VDBE code for the statements inside the body of a single
|
||||
** trigger.
|
||||
@@ -928,7 +979,7 @@ static int codeTriggerProgram(
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
break;
|
||||
}
|
||||
case TK_SELECT: {
|
||||
default: assert( pStep->op==TK_SELECT ); {
|
||||
SelectDest sDest;
|
||||
Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0);
|
||||
sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
|
||||
@@ -936,26 +987,6 @@ static int codeTriggerProgram(
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
break;
|
||||
}
|
||||
default: assert( pStep->op==TK_RETURNING ); {
|
||||
Select *pSelect = pStep->pSelect;
|
||||
ExprList *pList = pSelect->pEList;
|
||||
SelectDest sDest;
|
||||
Select *pNew;
|
||||
pSelect->pEList =
|
||||
sqlite3ExpandReturning(pParse, pList, pParse->pTriggerTab);
|
||||
sqlite3SelectDestInit(&sDest, SRT_Output, 0);
|
||||
pNew = sqlite3SelectDup(db, pSelect, 0);
|
||||
if( pNew ){
|
||||
sqlite3Select(pParse, pNew, &sDest);
|
||||
if( pNew->selFlags & (SF_Aggregate|SF_HasAgg|SF_WinRewrite) ){
|
||||
sqlite3ErrorMsg(pParse, "aggregates not allowed in RETURNING");
|
||||
}
|
||||
sqlite3SelectDelete(db, pNew);
|
||||
}
|
||||
sqlite3ExprListDelete(db, pSelect->pEList);
|
||||
pStep->pSelect->pEList = pList;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1208,7 +1239,7 @@ void sqlite3CodeRowTriggerDirect(
|
||||
** ... ...
|
||||
** reg+N OLD.* value of right-most column of pTab
|
||||
** reg+N+1 NEW.rowid
|
||||
** reg+N+2 OLD.* value of left-most column of pTab
|
||||
** reg+N+2 NEW.* value of left-most column of pTab
|
||||
** ... ...
|
||||
** reg+N+N+1 NEW.* value of right-most column of pTab
|
||||
**
|
||||
@@ -1261,12 +1292,12 @@ void sqlite3CodeRowTrigger(
|
||||
if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE))
|
||||
&& p->tr_tm==tr_tm
|
||||
&& checkColumnOverlap(p->pColumns, pChanges)
|
||||
&& (sqlite3IsToplevel(pParse) || !p->bReturning)
|
||||
){
|
||||
u8 origOp = p->op;
|
||||
p->op = op;
|
||||
sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
|
||||
p->op = origOp;
|
||||
if( !p->bReturning ){
|
||||
sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
|
||||
}else if( sqlite3IsToplevel(pParse) ){
|
||||
codeReturningTrigger(pParse, p, pTab, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1311,13 +1342,18 @@ u32 sqlite3TriggerColmask(
|
||||
|
||||
assert( isNew==1 || isNew==0 );
|
||||
for(p=pTrigger; p; p=p->pNext){
|
||||
if( p->op==op && (tr_tm&p->tr_tm)
|
||||
if( p->op==op
|
||||
&& (tr_tm&p->tr_tm)
|
||||
&& checkColumnOverlap(p->pColumns,pChanges)
|
||||
){
|
||||
TriggerPrg *pPrg;
|
||||
pPrg = getRowTrigger(pParse, p, pTab, orconf);
|
||||
if( pPrg ){
|
||||
mask |= pPrg->aColmask[isNew];
|
||||
if( p->bReturning ){
|
||||
mask = 0xffffffff;
|
||||
}else{
|
||||
TriggerPrg *pPrg;
|
||||
pPrg = getRowTrigger(pParse, p, pTab, orconf);
|
||||
if( pPrg ){
|
||||
mask |= pPrg->aColmask[isNew];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user