aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.c46
-rw-r--r--src/fkey.c3
-rw-r--r--src/parse.y4
-rw-r--r--src/resolve.c11
-rw-r--r--src/sqliteInt.h17
-rw-r--r--src/trigger.c20
-rw-r--r--src/vdbe.h1
-rw-r--r--src/vdbeaux.c14
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.
**