diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 46 | ||||
-rw-r--r-- | src/fkey.c | 3 | ||||
-rw-r--r-- | src/parse.y | 4 | ||||
-rw-r--r-- | src/resolve.c | 11 | ||||
-rw-r--r-- | src/sqliteInt.h | 17 | ||||
-rw-r--r-- | src/trigger.c | 20 | ||||
-rw-r--r-- | src/vdbe.h | 1 | ||||
-rw-r--r-- | src/vdbeaux.c | 14 |
8 files changed, 103 insertions, 13 deletions
diff --git a/src/build.c b/src/build.c index 5b4bbe499..99ecad4d6 100644 --- a/src/build.c +++ b/src/build.c @@ -1244,12 +1244,54 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ #endif /* +** Name of the magic TEMP trigger used to implement RETURNING +*/ +#define RETURNING_TRIGGER "sqlite_returning" + +/* +** Delete the data structures associated with the RETURNING clause. +*/ +static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){ + Hash *pHash; + pHash = &(db->aDb[1].pSchema->trigHash); + assert( sqlite3HashFind(pHash, RETURNING_TRIGGER)==&pRet->retTrig ); + sqlite3HashInsert(pHash, RETURNING_TRIGGER, 0); + sqlite3ExprListDelete(db, pRet->pReturnEL); + sqlite3DbFree(db, pRet); +} + +/* ** Add the RETURNING clause to the parser currently underway. */ void sqlite3AddReturning(Parse *pParse, ExprList *pList){ + Returning *pRet; + Hash *pHash; + sqlite3 *db = pParse->db; + pRet = sqlite3DbMallocZero(db, sizeof(*pRet)); + if( pRet==0 ){ + sqlite3ExprListDelete(db, pList); + return; + } + pRet->pParse = pParse; + pRet->pReturnEL = pList; sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprListDelete, pList); - pParse->pReturning = pList; + (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet); + pRet->retTrig.zName = "sqlite_returning"; + pRet->retTrig.op = TK_RETURNING; + pRet->retTrig.tr_tm = TRIGGER_AFTER; + pRet->retTrig.bReturning = 1; + pRet->retTrig.pSchema = db->aDb[1].pSchema; + pRet->retTrig.step_list = &pRet->retTStep; + pRet->retTStep.op = TK_SELECT; + pRet->retTStep.eTrigDest = SRT_Output; + pRet->retTStep.pTrig = &pRet->retTrig; + pRet->retTStep.pSelect = &pRet->retSel; + pRet->retSel.op = TK_ALL; + pRet->retSel.pEList = pList; + pRet->retSel.pSrc = (SrcList*)&pRet->retSrcList; + pHash = &(db->aDb[1].pSchema->trigHash); + assert( sqlite3HashFind(pHash, RETURNING_TRIGGER)==0 ); + sqlite3HashInsert(pHash, "sqlite_returning", &pRet->retTrig); } /* diff --git a/src/fkey.c b/src/fkey.c index 959e994d1..4cb92e291 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -1352,7 +1352,8 @@ static Trigger *fkActionTrigger( switch( action ){ case OE_Restrict: - pStep->op = TK_SELECT; + pStep->op = TK_SELECT; + pStep->eTrigDest = SRT_Discard; break; case OE_Cascade: if( !pChanges ){ diff --git a/src/parse.y b/src/parse.y index 4c79d4a87..591cde3b9 100644 --- a/src/parse.y +++ b/src/parse.y @@ -905,7 +905,7 @@ where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y). // %if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) - where_opt(W) orderby_opt(O) limit_opt(L). { + where_opt_ret(W) orderby_opt(O) limit_opt(L). { sqlite3SrcListIndexedBy(pParse, X, &I); X = sqlite3SrcListAppendList(pParse, X, F); sqlite3ExprListCheckLength(pParse,Y,"set list"); @@ -920,7 +920,7 @@ cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) } %else cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F) - where_opt(W). { + where_opt_ret(W). { sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); X = sqlite3SrcListAppendList(pParse, X, F); diff --git a/src/resolve.c b/src/resolve.c index b55bdc418..fa46954da 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -371,23 +371,26 @@ static int lookupName( ** it is a new.* or old.* trigger argument reference. Or ** maybe it is an excluded.* from an upsert. */ - if( zDb==0 && zTab!=0 && cntTab==0 ){ + if( zDb==0 && cntTab==0 ){ pTab = 0; #ifndef SQLITE_OMIT_TRIGGER if( pParse->pTriggerTab!=0 ){ int op = pParse->eTriggerOp; assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); - if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ + if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){ pExpr->iTable = 1; pTab = pParse->pTriggerTab; - }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ + }else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){ pExpr->iTable = 0; pTab = pParse->pTriggerTab; + }else if( pParse->bReturning ){ + pExpr->iTable = op!=TK_DELETE; + pTab = pParse->pTriggerTab; } } #endif /* SQLITE_OMIT_TRIGGER */ #ifndef SQLITE_OMIT_UPSERT - if( (pNC->ncFlags & NC_UUpsert)!=0 ){ + if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab ){ Upsert *pUpsert = pNC->uNC.pUpsert; if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){ pTab = pUpsert->pUpsertSrc->a[0].pTab; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c66b44290..231ead506 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1159,6 +1159,7 @@ typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; typedef struct PrintfArguments PrintfArguments; typedef struct RenameToken RenameToken; +typedef struct Returning Returning; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; @@ -3429,6 +3430,7 @@ struct Parse { u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ + u8 bReturning; /* Coding a RETURNING trigger */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ @@ -3441,7 +3443,6 @@ struct Parse { int aTempReg[8]; /* Holding area for temporary registers */ Token sNameToken; /* Token with unqualified schema object name */ - ExprList *pReturning; /* The RETURNING clause, if any */ /************************************************************************ ** Above is constant between recursions. Below is reset before and after @@ -3579,6 +3580,7 @@ struct 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 */ + u8 bReturning; /* This trigger implements a RETURNING clause */ Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */ IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger, the <column-list> is stored here */ @@ -3639,6 +3641,7 @@ struct Trigger { struct TriggerStep { u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ u8 orconf; /* OE_Rollback etc. */ + u8 eTrigDest; /* SRT_ destination value for SELECT */ Trigger *pTrig; /* The trigger that this step is a part of */ Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */ char *zTarget; /* Target table for DELETE, UPDATE, INSERT */ @@ -3653,6 +3656,18 @@ struct TriggerStep { }; /* +** Information about a RETURNING clause +*/ +struct Returning { + Parse *pParse; /* The parse that includes the RETURNING clause */ + ExprList *pReturnEL; /* List of expressions to return */ + Trigger retTrig; /* The transient trigger that implements RETURNING */ + TriggerStep retTStep; /* The trigger step */ + Select retSel; /* The SELECT statement that implements RETURNING */ + u64 retSrcList; /* The empty FROM clause of the SELECT */ +}; + +/* ** An objected used to accumulate the text of a string where we ** do not necessarily know how big the string will be in the end. */ diff --git a/src/trigger.c b/src/trigger.c index a9378fd3a..5508e4d15 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -65,11 +65,16 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ while( p ){ Trigger *pTrig = (Trigger *)sqliteHashData(p); if( pTrig->pTabSchema==pTab->pSchema - && 0==sqlite3StrICmp(pTrig->table, pTab->zName) + && 0==sqlite3StrICmp(pTrig->table, pTab->zName) ){ pTrig->pNext = pList; pList = pTrig; - } + }else if( pTrig->op==TK_RETURNING ){ + pTrig->table = pTab->zName; + pTrig->pTabSchema = pTab->pSchema; + pTrig->pNext = pList; + pList = pTrig; + } p = sqliteHashNext(p); } } @@ -405,6 +410,7 @@ TriggerStep *sqlite3TriggerSelectStep( return 0; } pTriggerStep->op = TK_SELECT; + pTriggerStep->eTrigDest = SRT_Discard; pTriggerStep->pSelect = pSelect; pTriggerStep->orconf = OE_Default; pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd); @@ -563,6 +569,7 @@ TriggerStep *sqlite3TriggerDeleteStep( */ void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){ if( pTrigger==0 ) return; + assert( !pTrigger->bReturning ); sqlite3DeleteTriggerStep(db, pTrigger->step_list); sqlite3DbFree(db, pTrigger->zName); sqlite3DbFree(db, pTrigger->table); @@ -734,6 +741,9 @@ Trigger *sqlite3TriggersExist( for(p=pList; p; p=p->pNext){ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ mask |= p->tr_tm; + }else if( p->op==TK_RETURNING ){ + p->op = op; + mask |= TRIGGER_AFTER; } } if( pMask ){ @@ -849,7 +859,7 @@ static int codeTriggerProgram( default: assert( pStep->op==TK_SELECT ); { SelectDest sDest; Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0); - sqlite3SelectDestInit(&sDest, SRT_Discard, 0); + sqlite3SelectDestInit(&sDest, pStep->eTrigDest, 0); sqlite3Select(pParse, pSelect, &sDest); sqlite3SelectDelete(db, pSelect); break; @@ -947,6 +957,7 @@ static TriggerPrg *codeRowTrigger( pSubParse->pToplevel = pTop; pSubParse->zAuthContext = pTrigger->zName; pSubParse->eTriggerOp = pTrigger->op; + pSubParse->bReturning = pTrigger->bReturning; pSubParse->nQueryLoop = pParse->nQueryLoop; pSubParse->disableVtab = pParse->disableVtab; @@ -996,6 +1007,9 @@ static TriggerPrg *codeRowTrigger( if( db->mallocFailed==0 && pParse->nErr==0 ){ pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); } + if( pTrigger->bReturning ){ + sqlite3VdbeColumnInfoXfer(pParse->pVdbe, v); + } pProgram->nMem = pSubParse->nMem; pProgram->nCsr = pSubParse->nTab; pProgram->token = (void *)pTrigger; diff --git a/src/vdbe.h b/src/vdbe.h index 17f11fdd7..48be53df7 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -259,6 +259,7 @@ void sqlite3VdbeResetStepResult(Vdbe*); void sqlite3VdbeRewind(Vdbe*); int sqlite3VdbeReset(Vdbe*); void sqlite3VdbeSetNumCols(Vdbe*,int); +void sqlite3VdbeColumnInfoXfer(Vdbe*,Vdbe*); int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); void sqlite3VdbeCountChanges(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 7b9b79205..9ea20628f 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -2596,6 +2596,20 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ } /* +** Transfer the column count and name information from one Vdbe to +** another. +*/ +void sqlite3VdbeColumnInfoXfer(Vdbe *pTo, Vdbe *pFrom){ + sqlite3 *db = pTo->db; + assert( db==pFrom->db ); + sqlite3DbFree(db, pTo->aColName); + pTo->aColName = pFrom->aColName; + pFrom->aColName = 0; + pTo->nResColumn = pFrom->nResColumn; + pFrom->nResColumn = 0; +} + +/* ** Set the name of the idx'th column to be returned by the SQL statement. ** zName must be a pointer to a nul terminated string. ** |