Hello.
Here's a patch(WIP) that implements INSERT .. RETURNING inside a CTE.
Should apply cleanly against CVS head.
The INSERT query isn't rewritten so rules and default values don't work.
Recursive CTEs don't work either.
Regards,
Marko Tiikkaja
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
***************
*** 651,656 **** explain_outNode(StringInfo str,
--- 651,659 ----
case T_Hash:
pname = "Hash";
break;
+ case T_InsertReturning:
+ pname = "INSERT RETURNING";
+ break;
default:
pname = "???";
break;
*** a/src/backend/executor/Makefile
--- b/src/backend/executor/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
! nodeBitmapAnd.o nodeBitmapOr.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
--- 15,21 ----
OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
! nodeBitmapAnd.o nodeBitmapOr.o nodeInsertReturning.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 86,94 **** static void ExecutePlan(EState *estate, PlanState *planstate,
DestReceiver *dest);
static void ExecSelect(TupleTableSlot *slot,
DestReceiver *dest, EState *estate);
! static void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
TupleTableSlot *planSlot,
! DestReceiver *dest, EState *estate);
static void ExecDelete(ItemPointer tupleid,
TupleTableSlot *planSlot,
DestReceiver *dest, EState *estate);
--- 86,94 ----
DestReceiver *dest);
static void ExecSelect(TupleTableSlot *slot,
DestReceiver *dest, EState *estate);
! void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
TupleTableSlot *planSlot,
! DestReceiver *dest, EState *estate, ResultRelInfo* resultRelInfo, bool clearReturningTuple);
static void ExecDelete(ItemPointer tupleid,
TupleTableSlot *planSlot,
DestReceiver *dest, EState *estate);
***************
*** 98,104 **** static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
static void ExecProcessReturning(ProjectionInfo *projectReturning,
TupleTableSlot *tupleSlot,
TupleTableSlot *planSlot,
! DestReceiver *dest);
static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTPerms(List *rangeTable);
--- 98,105 ----
static void ExecProcessReturning(ProjectionInfo *projectReturning,
TupleTableSlot *tupleSlot,
TupleTableSlot *planSlot,
! DestReceiver *dest,
! bool clearTuple);
static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTPerms(List *rangeTable);
***************
*** 190,196 **** standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
case CMD_SELECT:
/* SELECT INTO and SELECT FOR UPDATE/SHARE need to mark tuples */
if (queryDesc->plannedstmt->intoClause != NULL ||
! queryDesc->plannedstmt->rowMarks != NIL)
estate->es_output_cid = GetCurrentCommandId(true);
break;
--- 191,198 ----
case CMD_SELECT:
/* SELECT INTO and SELECT FOR UPDATE/SHARE need to mark tuples */
if (queryDesc->plannedstmt->intoClause != NULL ||
! queryDesc->plannedstmt->rowMarks != NIL ||
! queryDesc->plannedstmt->hasWritableCtes)
estate->es_output_cid = GetCurrentCommandId(true);
break;
***************
*** 1670,1676 **** lnext: ;
break;
case CMD_INSERT:
! ExecInsert(slot, tupleid, planSlot, dest, estate);
break;
case CMD_DELETE:
--- 1672,1678 ----
break;
case CMD_INSERT:
! ExecInsert(slot, tupleid, planSlot, dest, estate, estate->es_result_relation_info, true);
break;
case CMD_DELETE:
***************
*** 1742,1756 **** ExecSelect(TupleTableSlot *slot,
* index relations.
* ----------------------------------------------------------------
*/
! static void
ExecInsert(TupleTableSlot *slot,
ItemPointer tupleid,
TupleTableSlot *planSlot,
DestReceiver *dest,
! EState *estate)
{
HeapTuple tuple;
- ResultRelInfo *resultRelInfo;
Relation resultRelationDesc;
Oid newId;
--- 1744,1759 ----
* index relations.
* ----------------------------------------------------------------
*/
! void
ExecInsert(TupleTableSlot *slot,
ItemPointer tupleid,
TupleTableSlot *planSlot,
DestReceiver *dest,
! EState *estate,
! ResultRelInfo* resultRelInfo,
! bool clearReturningTuple)
{
HeapTuple tuple;
Relation resultRelationDesc;
Oid newId;
***************
*** 1763,1769 **** ExecInsert(TupleTableSlot *slot,
/*
* get information on the (current) result relation
*/
- resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
--- 1766,1771 ----
***************
*** 1842,1848 **** ExecInsert(TupleTableSlot *slot,
/* Process RETURNING if present */
if (resultRelInfo->ri_projectReturning)
ExecProcessReturning(resultRelInfo->ri_projectReturning,
! slot, planSlot, dest);
}
/* ----------------------------------------------------------------
--- 1844,1850 ----
/* Process RETURNING if present */
if (resultRelInfo->ri_projectReturning)
ExecProcessReturning(resultRelInfo->ri_projectReturning,
! slot, planSlot, dest, clearReturningTuple);
}
/* ----------------------------------------------------------------
***************
*** 1968,1974 **** ldelete:;
ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
ExecProcessReturning(resultRelInfo->ri_projectReturning,
! slot, planSlot, dest);
ExecClearTuple(slot);
ReleaseBuffer(delbuffer);
--- 1970,1976 ----
ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
ExecProcessReturning(resultRelInfo->ri_projectReturning,
! slot, planSlot, dest, true);
ExecClearTuple(slot);
ReleaseBuffer(delbuffer);
***************
*** 2140,2146 **** lreplace:;
/* Process RETURNING if present */
if (resultRelInfo->ri_projectReturning)
ExecProcessReturning(resultRelInfo->ri_projectReturning,
! slot, planSlot, dest);
}
/*
--- 2142,2148 ----
/* Process RETURNING if present */
if (resultRelInfo->ri_projectReturning)
ExecProcessReturning(resultRelInfo->ri_projectReturning,
! slot, planSlot, dest, true);
}
/*
***************
*** 2254,2260 **** static void
ExecProcessReturning(ProjectionInfo *projectReturning,
TupleTableSlot *tupleSlot,
TupleTableSlot *planSlot,
! DestReceiver *dest)
{
ExprContext *econtext = projectReturning->pi_exprContext;
TupleTableSlot *retSlot;
--- 2256,2262 ----
ExecProcessReturning(ProjectionInfo *projectReturning,
TupleTableSlot *tupleSlot,
TupleTableSlot *planSlot,
! DestReceiver *dest, bool clearTuple)
{
ExprContext *econtext = projectReturning->pi_exprContext;
TupleTableSlot *retSlot;
***************
*** 2275,2281 **** ExecProcessReturning(ProjectionInfo *projectReturning,
/* Send to dest */
(*dest->receiveSlot) (retSlot, dest);
! ExecClearTuple(retSlot);
}
/*
--- 2277,2284 ----
/* Send to dest */
(*dest->receiveSlot) (retSlot, dest);
! if (clearTuple)
! ExecClearTuple(retSlot);
}
/*
*** a/src/backend/executor/execProcnode.c
--- b/src/backend/executor/execProcnode.c
***************
*** 286,291 **** ExecInitNode(Plan *node, EState *estate, int eflags)
--- 286,296 ----
estate, eflags);
break;
+ case T_InsertReturning:
+ result = (PlanState *) ExecInitInsertReturning((Limit *) node,
+ estate, eflags);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
result = NULL; /* keep compiler quiet */
***************
*** 451,456 **** ExecProcNode(PlanState *node)
--- 456,465 ----
result = ExecLimit((LimitState *) node);
break;
+ case T_InsertReturningState:
+ result = ExecInsertReturning((InsertReturningState *) node);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
result = NULL;
***************
*** 627,632 **** ExecCountSlotsNode(Plan *node)
--- 636,644 ----
case T_Limit:
return ExecCountSlotsLimit((Limit *) node);
+ case T_InsertReturning:
+ return ExecCountSlotsInsertReturning((InsertReturning *) node);
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
break;
***************
*** 783,788 **** ExecEndNode(PlanState *node)
--- 795,804 ----
ExecEndLimit((LimitState *) node);
break;
+ case T_InsertReturningState:
+ ExecEndInsertReturning((InsertReturningState *) node);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
break;
*** /dev/null
--- b/src/backend/executor/nodeInsertReturning.c
***************
*** 0 ****
--- 1,120 ----
+ #include "postgres.h"
+
+ #include "executor/executor.h"
+ #include "executor/execdebug.h"
+ #include "executor/nodeInsertReturning.h"
+ #include "utils/memutils.h"
+
+ static struct DR_insertReturning
+ {
+ DestReceiver dr;
+ TupleTableSlot **targetSlot;
+ } receiver;
+
+ static void
+ receive(TupleTableSlot *slot, DestReceiver *self)
+ {
+ struct DR_insertReturning *myState = (struct DR_insertReturning *) self;
+
+ *myState->targetSlot = slot;
+ }
+
+ TupleTableSlot *
+ ExecInsertReturning(InsertReturningState *node)
+ {
+ EState *estate = node->ps.state;
+
+ TupleTableSlot *slot;
+
+ /* Get a tuple from the subplan */
+ slot = ExecProcNode(outerPlanState(node));
+
+ if (TupIsNull(slot))
+ return NULL;
+
+ ExecInsert(slot, NULL, slot, (DestReceiver *) &receiver, estate, node->resultRelInfo, false);
+
+ return node->ps.ps_ResultTupleSlot;
+ }
+
+ InsertReturningState *
+ ExecInitInsertReturning(InsertReturning *node, EState *estate, int eflags)
+ {
+ InsertReturningState *resstate;
+ ExprContext *exprContext;
+ ResultRelInfo *resultRelInfo;
+ Relation resultRelation;
+
+ /*
+ * create state structure
+ */
+ resstate = makeNode(InsertReturningState);
+ resstate->ps.plan = (Plan *) node;
+ resstate->ps.state = estate;
+ resstate->ps.targetlist = node->plan.targetlist;
+
+ outerPlanState(resstate) = ExecInitNode(outerPlan(node), estate, eflags);
+
+ /*
+ * Initialize result tuple slot and assign
+ * type from the target list.
+ */
+ ExecInitResultTupleSlot(estate, &resstate->ps);
+ ExecAssignResultTypeFromTL(&resstate->ps);
+
+ /*
+ * Prepare the RETURNING expression tree for execution. This
+ * has to be done after calling ExecAssignResultTypeFromTL().
+ */
+ resstate->ps.targetlist = (List *)
+ ExecInitExpr((Expr *) node->plan.targetlist,
+ (PlanState *) resstate);
+
+ /* Initialize result relation info */
+ resultRelInfo = (ResultRelInfo *) palloc0(sizeof(ResultRelInfo));
+ resultRelation = heap_open(node->resultRelationOid, RowExclusiveLock);
+ InitResultRelInfo(resultRelInfo, resultRelation, node->resultRelationIndex, CMD_INSERT, estate->es_instrument);
+
+ /* Initialize RETURNING projection */
+ exprContext = CreateExprContext(estate);
+ resultRelInfo->ri_projectReturning = ExecBuildProjectionInfo(resstate->ps.targetlist,
+ exprContext,
+ resstate->ps.ps_ResultTupleSlot,
+ NULL);
+
+ resstate->resultRelInfo = resultRelInfo;
+
+ /* Assign tuple receiver info */
+ receiver.dr.receiveSlot = receive;
+ receiver.targetSlot = &resstate->ps.ps_ResultTupleSlot;
+
+ return resstate;
+ }
+
+ int
+ ExecCountSlotsInsertReturning(InsertReturning *node)
+ {
+ return ExecCountSlotsNode(outerPlan(node)) + 2;
+ }
+
+ void
+ ExecEndInsertReturning(InsertReturningState *node)
+ {
+ heap_close(node->resultRelInfo->ri_RelationDesc, NoLock);
+ pfree(node->resultRelInfo);
+
+ /*
+ * Free the exprcontext
+ */
+ ExecFreeExprContext(&node->ps);
+
+ /*
+ * clean out the tuple table
+ */
+ ExecClearTuple(node->ps.ps_ResultTupleSlot);
+
+ /*
+ * shut down subplans
+ */
+ ExecEndNode(outerPlanState(node));
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1391,1396 **** _copyXmlExpr(XmlExpr *from)
--- 1391,1407 ----
return newnode;
}
+
+ static InsertReturning *
+ _copyInsertReturning(InsertReturning *from)
+ {
+ InsertReturning *newnode = makeNode(InsertReturning);
+
+ CopyPlanFields((Plan *) from, (Plan *) newnode);
+
+ return newnode;
+ }
+
/*
* _copyNullIfExpr (same as OpExpr)
***************
*** 4093,4098 **** copyObject(void *from)
--- 4104,4112 ----
case T_XmlSerialize:
retval = _copyXmlSerialize(from);
break;
+ case T_InsertReturning:
+ retval = _copyInsertReturning(from);
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 2354,2359 **** bool
--- 2354,2403 ----
return true;
}
break;
+ case T_InsertStmt:
+ {
+ InsertStmt *stmt = (InsertStmt *) node;
+
+ if (walker(stmt->relation, context))
+ return true;
+ if (walker(stmt->cols, context))
+ return true;
+ if (walker(stmt->selectStmt, context))
+ return true;
+ if (walker(stmt->returningList, context))
+ return true;
+ }
+ break;
+ case T_UpdateStmt:
+ {
+ UpdateStmt *stmt = (UpdateStmt *) node;
+
+ if (walker(stmt->relation, context))
+ return true;
+ if (walker(stmt->targetList, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ if (walker(stmt->fromClause, context))
+ return true;
+ if (walker(stmt->returningList, context))
+ return true;
+ }
+ break;
+ case T_DeleteStmt:
+ {
+ DeleteStmt *stmt = (DeleteStmt *) node;
+
+ if (walker(stmt->relation, context))
+ return true;
+ if (walker(stmt->usingClause, context))
+ return true;
+ if (walker(stmt->whereClause, context))
+ return true;
+ if (walker(stmt->returningList, context))
+ return true;
+ }
+ break;
case T_A_Expr:
{
A_Expr *expr = (A_Expr *) node;
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 155,160 **** standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
--- 155,161 ----
glob->finalrtable = NIL;
glob->relationOids = NIL;
glob->invalItems = NIL;
+ glob->hasWritableCtes = false;
glob->lastPHId = 0;
glob->transientPlan = false;
***************
*** 224,229 **** standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
--- 225,231 ----
result->resultRelations = root->resultRelations;
result->utilityStmt = parse->utilityStmt;
result->intoClause = parse->intoClause;
+ result->hasWritableCtes = glob->hasWritableCtes;
result->subplans = glob->subplans;
result->rewindPlanIDs = glob->rewindPlanIDs;
result->returningLists = root->returningLists;
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 375,380 **** set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
--- 375,393 ----
set_join_references(glob, (Join *) plan, rtoffset);
break;
+ case T_InsertReturning:
+ {
+ /*
+ * grouping_planner() already called
+ * set_returning_clause_references so the targetList's
+ * references are already set.
+ */
+ InsertReturning *splan = (InsertReturning *) plan;
+
+ splan->resultRelationIndex += rtoffset;
+ }
+ break;
+
case T_Hash:
case T_Material:
case T_Sort:
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 880,885 **** SS_process_ctes(PlannerInfo *root)
--- 880,886 ----
Bitmapset *tmpset;
int paramid;
Param *prm;
+ InsertReturning *returningNode;
/*
* Ignore CTEs that are not actually referenced anywhere.
***************
*** 897,902 **** SS_process_ctes(PlannerInfo *root)
--- 898,904 ----
*/
subquery = (Query *) copyObject(cte->ctequery);
+
/*
* Generate the plan for the CTE query. Always plan for full
* retrieval --- we don't have enough info to predict otherwise.
***************
*** 954,959 **** SS_process_ctes(PlannerInfo *root)
--- 956,985 ----
prm = generate_new_param(root, INTERNALOID, -1);
splan->setParam = list_make1_int(prm->paramid);
+ /* Handle INSERT .. RETURNING inside CTE */
+ if (subquery->commandType != CMD_SELECT)
+ {
+ Oid resultRelationOid;
+ Index resultRelationIndex;
+
+ Assert(subquery->commandType == CMD_INSERT);
+
+ Assert(subquery->resultRelation > 0);
+ Assert(list_length(subroot->returningLists) == 1);
+
+ returningNode = makeNode(InsertReturning);
+ returningNode->plan.lefttree = plan;
+
+ resultRelationOid = getrelid(subquery->resultRelation, subquery->rtable);
+ returningNode->resultRelationOid = resultRelationOid;
+
+ returningNode->plan.targetlist = linitial(subroot->returningLists);
+
+ root->glob->hasWritableCtes = true;
+
+ plan = returningNode;
+ }
+
/*
* Add the subplan and its rtable to the global lists.
*/
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 7026,7031 **** common_table_expr: name opt_name_list AS select_with_parens
--- 7026,7058 ----
n->location = @1;
$$ = (Node *) n;
}
+ | name opt_name_list AS '(' InsertStmt ')'
+ {
+ CommonTableExpr *n = makeNode(CommonTableExpr);
+ n->ctename = $1;
+ n->aliascolnames = $2;
+ n->ctequery = $5;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | name opt_name_list AS '(' UpdateStmt ')'
+ {
+ CommonTableExpr *n = makeNode(CommonTableExpr);
+ n->ctename = $1;
+ n->aliascolnames = $2;
+ n->ctequery = $5;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | name opt_name_list AS '(' DeleteStmt ')'
+ {
+ CommonTableExpr *n = makeNode(CommonTableExpr);
+ n->ctename = $1;
+ n->aliascolnames = $2;
+ n->ctequery = $5;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
;
into_clause:
*** a/src/backend/parser/parse_cte.c
--- b/src/backend/parser/parse_cte.c
***************
*** 18,23 ****
--- 18,24 ----
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_cte.h"
+ #include "nodes/plannodes.h"
#include "utils/builtins.h"
***************
*** 246,268 **** transformWithClause(ParseState *pstate, WithClause *withClause)
static void
analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
{
! Query *query;
/* Analysis not done already */
! Assert(IsA(cte->ctequery, SelectStmt));
query = parse_sub_analyze(cte->ctequery, pstate);
cte->ctequery = (Node *) query;
/*
* Check that we got something reasonable. Many of these conditions are
* impossible given restrictions of the grammar, but check 'em anyway.
! * (These are the same checks as in transformRangeSubselect.)
*/
if (!IsA(query, Query) ||
! query->commandType != CMD_SELECT ||
! query->utilityStmt != NULL)
! elog(ERROR, "unexpected non-SELECT command in subquery in WITH");
if (query->intoClause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
--- 247,284 ----
static void
analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
{
! Query *query;
! List *ctelist;
/* Analysis not done already */
! /* This needs to be one of SelectStmt, InsertStmt, UpdateStmt, DeleteStmt instead of:
! * Assert(IsA(cte->ctequery, SelectStmt)); */
query = parse_sub_analyze(cte->ctequery, pstate);
cte->ctequery = (Node *) query;
+ if (query->commandType == CMD_SELECT)
+ ctelist = query->targetList;
+ else
+ ctelist = query->returningList;
+
/*
* Check that we got something reasonable. Many of these conditions are
* impossible given restrictions of the grammar, but check 'em anyway.
! * (In addition to the same checks as in transformRangeSubselect,
! * this adds checks for (INSERT|UPDATE|DELETE)...RETURNING.)
*/
if (!IsA(query, Query) ||
! query->utilityStmt != NULL ||
! (query->commandType != CMD_SELECT &&
! ((query->commandType == CMD_INSERT ||
! query->commandType == CMD_UPDATE ||
! query->commandType == CMD_DELETE) &&
! query->returningList == NULL)))
! ereport(ERROR,
! (errcode(ERRCODE_SYNTAX_ERROR),
! errmsg("unexpected non-row-returning command in subquery in WITH"),
! parser_errposition(pstate, 0)));
if (query->intoClause)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 273,279 **** analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
if (!cte->cterecursive)
{
/* Compute the output column names/types if not done yet */
! analyzeCTETargetList(pstate, cte, query->targetList);
}
else
{
--- 289,295 ----
if (!cte->cterecursive)
{
/* Compute the output column names/types if not done yet */
! analyzeCTETargetList(pstate, cte, ctelist);
}
else
{
***************
*** 291,297 **** analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
lctyp = list_head(cte->ctecoltypes);
lctypmod = list_head(cte->ctecoltypmods);
varattno = 0;
! foreach(lctlist, query->targetList)
{
TargetEntry *te = (TargetEntry *) lfirst(lctlist);
Node *texpr;
--- 307,313 ----
lctyp = list_head(cte->ctecoltypes);
lctypmod = list_head(cte->ctecoltypmods);
varattno = 0;
! foreach(lctlist, ctelist)
{
TargetEntry *te = (TargetEntry *) lfirst(lctlist);
Node *texpr;
*** a/src/backend/parser/parse_relation.c
--- b/src/backend/parser/parse_relation.c
***************
*** 1402,1409 **** addRangeTableEntryForCTE(ParseState *pstate,
rte->ctelevelsup = levelsup;
/* Self-reference if and only if CTE's parse analysis isn't completed */
! rte->self_reference = !IsA(cte->ctequery, Query);
! Assert(cte->cterecursive || !rte->self_reference);
/* Bump the CTE's refcount if this isn't a self-reference */
if (!rte->self_reference)
cte->cterefcount++;
--- 1402,1409 ----
rte->ctelevelsup = levelsup;
/* Self-reference if and only if CTE's parse analysis isn't completed */
! rte->self_reference = !IsA(cte->ctequery, Query) && !IsA(cte->ctequery, InsertReturning);
! Assert(cte->cterecursive || !rte->self_reference || IsA(cte->ctequery, InsertReturning));
/* Bump the CTE's refcount if this isn't a self-reference */
if (!rte->self_reference)
cte->cterefcount++;
*** a/src/backend/parser/parse_target.c
--- b/src/backend/parser/parse_target.c
***************
*** 310,319 **** markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
{
CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
TargetEntry *ste;
/* should be analyzed by now */
Assert(IsA(cte->ctequery, Query));
! ste = get_tle_by_resno(((Query *) cte->ctequery)->targetList,
attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
--- 310,321 ----
{
CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
TargetEntry *ste;
+ Query *query;
/* should be analyzed by now */
Assert(IsA(cte->ctequery, Query));
! query = (Query *) cte->ctequery;
! ste = get_tle_by_resno((query->commandType == CMD_SELECT) ? query->targetList : query->returningList,
attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
***************
*** 1233,1243 **** expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
{
CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
TargetEntry *ste;
/* should be analyzed by now */
Assert(IsA(cte->ctequery, Query));
! ste = get_tle_by_resno(((Query *) cte->ctequery)->targetList,
! attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
--- 1235,1252 ----
{
CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
TargetEntry *ste;
+ Query *query;
+ List *ctelist;
/* should be analyzed by now */
Assert(IsA(cte->ctequery, Query));
! query = (Query *) cte->ctequery;
! if (query->commandType == CMD_SELECT)
! ctelist = query->targetList;
! else
! ctelist = query->returningList;
!
! ste = get_tle_by_resno(ctelist, attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
rte->eref->aliasname, attnum);
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 3800,3808 **** get_name_for_var_field(Var *var, int fieldno,
}
if (lc != NULL)
{
! Query *ctequery = (Query *) cte->ctequery;
! TargetEntry *ste = get_tle_by_resno(ctequery->targetList,
! attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
--- 3800,3814 ----
}
if (lc != NULL)
{
! Query *ctequery = (Query *) cte->ctequery;
! List *ctelist;
!
! if (ctequery->commandType == CMD_SELECT)
! ctelist = ctequery->targetList;
! else
! ctelist = ctequery->returningList;
!
! TargetEntry *ste = get_tle_by_resno(ctelist, attnum);
if (ste == NULL || ste->resjunk)
elog(ERROR, "subquery %s does not have attribute %d",
*** /dev/null
--- b/src/include/executor/nodeInsertReturning.h
***************
*** 0 ****
--- 1,11 ----
+ #ifndef NODEINSERTRETURNING_H
+ #define NODEINSERTRETURNING_H
+
+ #include "nodes/execnodes.h"
+
+ extern int ExecCountSlotsInsertReturning(InsertReturning *node);
+ extern InsertReturningState *ExecInitInsertReturning(InsertReturning *node, EState *estate, int eflags);
+ extern TupleTableSlot *ExecInsertReturning(InsertReturningState *node);
+ extern void ExecEndInsertReturning(InsertReturningState *node);
+
+ #endif
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 978,983 **** typedef struct ResultState
--- 978,994 ----
} ResultState;
/* ----------------
+ * InsertReturningState information
+ * ----------------
+ */
+ typedef struct InsertReturningState
+ {
+ PlanState ps; /* its first field is NodeTag */
+ ResultRelInfo *resultRelInfo;
+ } InsertReturningState;
+
+
+ /* ----------------
* AppendState information
*
* nplans how many plans are in the list
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 71,76 **** typedef enum NodeTag
--- 71,77 ----
T_Hash,
T_SetOp,
T_Limit,
+ T_InsertReturning,
/* this one isn't a subclass of Plan: */
T_PlanInvalItem,
***************
*** 190,195 **** typedef enum NodeTag
--- 191,197 ----
T_NullTestState,
T_CoerceToDomainState,
T_DomainConstraintState,
+ T_InsertReturningState,
/*
* TAGS FOR PLANNER NODES (relation.h)
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 53,58 **** typedef struct PlannedStmt
--- 53,60 ----
IntoClause *intoClause; /* target for SELECT INTO / CREATE TABLE AS */
+ bool hasWritableCtes; /* true if there's an (INSERT|UPDATE|DELETE) .. RETURNING inside a CTE */
+
List *subplans; /* Plan trees for SubPlan expressions */
Bitmapset *rewindPlanIDs; /* indices of subplans that require REWIND */
***************
*** 164,169 **** typedef struct Result
--- 166,179 ----
Node *resconstantqual;
} Result;
+ typedef struct InsertReturning
+ {
+ Plan plan;
+
+ Oid resultRelationOid;
+ int resultRelationIndex; /* rtable index of the result relation*/
+ } InsertReturning;
+
/* ----------------
* Append node -
* Generate the concatenation of the results of sub-plans.
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 76,81 **** typedef struct PlannerGlobal
--- 76,83 ----
List *invalItems; /* other dependencies, as PlanInvalItems */
+ bool hasWritableCtes; /* is there an (INSERT|UPDATE|DELETE) .. RETURNING inside a CTE? */
+
Index lastPHId; /* highest PlaceHolderVar ID assigned */
bool transientPlan; /* redo plan when TransactionXmin changes? */
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers