Using results from INSERT ... RETURNING
От | Marko Tiikkaja |
---|---|
Тема | Using results from INSERT ... RETURNING |
Дата | |
Msg-id | 4A53B0BA.6040104@cs.helsinki.fi обсуждение исходный текст |
Ответы |
Re: Using results from INSERT ... RETURNING
(Peter Eisentraut <peter_e@gmx.net>)
Re: Using results from INSERT ... RETURNING (Jaime Casanova <jcasanov@systemguards.com.ec>) |
Список | pgsql-hackers |
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? */
В списке pgsql-hackers по дате отправления:
Следующее
От: Sergey BurladyanДата:
Сообщение: Re: 8.4, One-Time Filter and subquery ( ... FROM function() union all ... )