From 18faae5fdfa4ec421c135985a3801e957727d62d Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Wed, 29 Apr 2015 17:51:36 +0200 Subject: [PATCH] tmp --- src/backend/commands/explain.c | 2 +- src/backend/executor/execQual.c | 57 ----- src/backend/executor/nodeModifyTable.c | 272 +++++++++++----------- src/backend/nodes/copyfuncs.c | 26 +-- src/backend/nodes/equalfuncs.c | 17 +- src/backend/nodes/nodeFuncs.c | 49 +--- src/backend/nodes/outfuncs.c | 10 +- src/backend/nodes/readfuncs.c | 18 +- src/backend/optimizer/plan/createplan.c | 8 +- src/backend/optimizer/plan/planner.c | 96 ++++---- src/backend/optimizer/plan/setrefs.c | 47 ++-- src/backend/optimizer/plan/subselect.c | 8 +- src/backend/optimizer/prep/prepjointree.c | 4 + src/backend/optimizer/prep/preptlist.c | 14 ++ src/backend/parser/analyze.c | 160 ++++++++----- src/backend/parser/gram.y | 25 +- src/backend/parser/parse_clause.c | 55 ++--- src/backend/parser/parse_expr.c | 4 +- src/backend/parser/parse_node.c | 8 +- src/backend/rewrite/rewriteHandler.c | 147 +++--------- src/backend/rewrite/rowsecurity.c | 2 + src/backend/utils/adt/ruleutils.c | 21 -- src/include/nodes/execnodes.h | 17 +- src/include/nodes/nodes.h | 1 - src/include/nodes/parsenodes.h | 6 +- src/include/nodes/plannodes.h | 4 +- src/include/optimizer/planmain.h | 4 +- src/include/optimizer/prep.h | 7 + src/include/parser/parse_node.h | 1 - 29 files changed, 441 insertions(+), 649 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 39fe92eca5..3166d794fe 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1648,7 +1648,7 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_ModifyTable: ExplainMemberNodes(((ModifyTable *) plan)->plans, ((ModifyTableState *) planstate)->mt_plans, - ((ModifyTableState *) planstate)->onConflict, + NULL, ancestors, es); break; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 37a515d264..69ae93807e 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -181,9 +181,6 @@ static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalExcluded(ExcludedExprState *excludedExpr, - ExprContext *econtext, bool *isNull, - ExprDoneCond *isDone); /* ---------------------------------------------------------------- @@ -4336,35 +4333,6 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, return 0; /* keep compiler quiet */ } -/* ---------------------------------------------------------------- - * ExecEvalExcluded - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalExcluded(ExcludedExprState *excludedExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) -{ - /* - * ExcludedExpr is essentially an expression that adapts its single Var - * argument to refer to the expression context inner slot's tuple, which is - * reserved for the purpose of referencing EXCLUDED.* tuples within ON - * CONFLICT DO UPDATE auxiliary queries' EPQ expression context (this makes - * special use of the EvalPlanQual() mechanism to UPDATE). - * - * nodeModifyTable.c assigns its own table slot in the auxiliary queries' - * EPQ expression state (originating in the parent INSERT node) on the - * assumption that it may only be used by ExcludedExpr, and on the - * assumption that the inner slot is not otherwise useful. This occurs in - * advance of the expression evaluation for UPDATE (which calls here are - * part of) once per slot proposed for insertion, and works because of - * restrictions on the structure of ON CONFLICT DO UPDATE auxiliary - * queries. - * - * Just evaluate nested Var. - */ - return ExecEvalScalarVar(excludedExpr->arg, econtext, isNull, isDone); -} - /* * ExecEvalExprSwitchContext * @@ -5096,31 +5064,6 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) makeNode(ExprState); state->evalfunc = ExecEvalCurrentOfExpr; break; - case T_ExcludedExpr: - { - ExcludedExpr *excludedexpr = (ExcludedExpr *) node; - ExcludedExprState *cstate = makeNode(ExcludedExprState); - Var *contained = (Var *) excludedexpr->arg; - - /* - * varno forced to INNER_VAR -- see remarks within - * ExecLockUpdateTuple(). - * - * We rely on the assumption that the only place that - * ExcludedExpr may appear is where EXCLUDED Var references - * originally appeared after parse analysis. The rewriter - * replaces these with ExcludedExpr that reference the - * corresponding Var within the ON CONFLICT DO UPDATE target - * RTE. - */ - Assert(IsA(contained, Var)); - - contained->varno = INNER_VAR; - cstate->arg = ExecInitExpr((Expr *) contained, parent); - state = (ExprState *) cstate; - state->evalfunc = (ExprStateEvalFunc) ExecEvalExcluded; - } - break; case T_TargetEntry: { TargetEntry *tle = (TargetEntry *) node; diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 2b3d343a44..d034264944 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -53,11 +53,12 @@ #include "utils/tqual.h" -static bool ExecOnConflictUpdate(ResultRelInfo *resultRelInfo, +static bool ExecOnConflictUpdate(ModifyTableState *mtstate, + ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *planSlot, TupleTableSlot *insertSlot, - ModifyTableState *onConflict, + EPQState *epqstate, EState *estate, bool canSetTag, TupleTableSlot **returning); @@ -200,9 +201,10 @@ ExecCheckHeapTupleVisible(EState *estate, * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecInsert(TupleTableSlot *slot, +ExecInsert(ModifyTableState *mtstate, + TupleTableSlot *slot, TupleTableSlot *planSlot, - ModifyTableState *onConflict, + EPQState *epqstate, List *arbiterIndexes, ConfCmd conf, EState *estate, @@ -353,8 +355,8 @@ vlock: { TupleTableSlot *returning = NULL; - if (ExecOnConflictUpdate(resultRelInfo, &conflictTid, - planSlot, slot, onConflict, + if (ExecOnConflictUpdate(mtstate, resultRelInfo, &conflictTid, + planSlot, slot, epqstate, estate, canSetTag, &returning)) return returning; else @@ -1005,11 +1007,12 @@ lreplace:; * the caller must retry the INSERT from scratch. */ static bool -ExecOnConflictUpdate(ResultRelInfo *resultRelInfo, +ExecOnConflictUpdate(ModifyTableState *mtstate, + ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *planSlot, TupleTableSlot *insertSlot, - ModifyTableState *onConflict, + EPQState *epqstate, EState *estate, bool canSetTag, TupleTableSlot **returning) @@ -1020,9 +1023,8 @@ ExecOnConflictUpdate(ResultRelInfo *resultRelInfo, HeapUpdateFailureData hufd; HTSU_Result test; Buffer buffer; - TupleTableSlot *slot; - ExprContext *econtext; LockTupleMode lockmode; + TupleTableSlot *slot; /* Determine lock mode to use */ lockmode = ExecUpdateLockMode(estate, resultRelInfo); @@ -1089,6 +1091,10 @@ ExecOnConflictUpdate(ResultRelInfo *resultRelInfo, return false; /* keep compiler quiet */ case HeapTupleMayBeUpdated: + slot = mtstate->mt_fuck; + ExecSetSlotDescriptor(slot, RelationGetDescr(relation)); + ExecStoreTuple(&tuple, slot, buffer, false); + /* * Success -- we're done, as tuple is locked. Verify that the * tuple is visible to our MVCC snapshot if the current isolation @@ -1121,117 +1127,67 @@ ExecOnConflictUpdate(ResultRelInfo *resultRelInfo, * matters. */ - /* must provide our own instrumentation support */ - if (onConflict->ps.instrument) - InstrStartNode(onConflict->ps.instrument); - - /* - * Conceptually, the parent ModifyTable is like a relation scan - * node that uses a dirty snapshot, returning rows which the - * auxiliary plan must operate on. - * - * Note that this code path merely re-uses some parts of the - * EvalPlanQual() infrastructure; it does not really use - * EvalPlanQual(), because the UPDATE chain is not actually walked. - */ - EvalPlanQualBegin(&onConflict->mt_epqstate, onConflict->ps.state); - - /* - * Save EPQ expression context. Auxiliary plan's scan node (which - * would have been just initialized by EvalPlanQualBegin() on the - * first time through here per query) cannot fail to provide one. - */ - econtext = onConflict->mt_epqstate.planstate->ps_ExprContext; + { + ExprContext *econtext = mtstate->ps.ps_ExprContext; - /* - * UPDATE affects the same ResultRelation as INSERT in the context - * of ON CONFLICT DO UPDATE, so parent's target rti can be used - */ - Assert(resultRelInfo->ri_RangeTableIndex == - onConflict->ps.state->es_result_relation_info->ri_RangeTableIndex); + /* + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous cycle. + */ + ResetExprContext(econtext); - EvalPlanQualSetTuple(&onConflict->mt_epqstate, - resultRelInfo->ri_RangeTableIndex, copyTuple); + /* Make tuple and any needed join variables available to ExecProject */ + econtext->ecxt_scantuple = slot; + econtext->ecxt_outertuple = insertSlot; + econtext->ecxt_innertuple = NULL; - /* - * Make available excluded-from-insertion tuple (EXCLUDED.*) for - * referencing within UPDATE expression (that is, make available a - * slot with that tuple, possibly already modified by BEFORE INSERT - * row triggers). - * - * This is for the benefit of any ExcludedExpr that may appear - * within the auxiliary UPDATE's targetlist or WHERE clause (the - * EXCLUDED.* tuple is referenced as an ExcludedExpr). The nested - * ExcludedExpr's Var is set during ExcludedExpr initialization to - * have an INNER_VAR varno on the assumption that the inner slot of - * the EPQ scanstate's expression context will be found to contain - * this excluded tuple slot (that is, on the assumption that during - * expression evaluation, ecxt_innertuple will have been assigned - * the insertSlot here, after ExcludedExpr initialization but in - * advance of this expression evaluation). - * - * See handling of ExcludedExpr within handleRewrite.c and - * execQual.c. - */ - econtext->ecxt_innertuple = insertSlot; + ExecProject(resultRelInfo->ri_onConflictSetProj, NULL); + slot = mtstate->mt_outertupleslot; - /* - * Execute auxiliary UPDATE for slot proposed for insertion, to - * generate new tuple version to supersede TARGET.* tuple. - * - * This may include expression evaluation of ExcludedExpr (which - * occurs in no other context). - */ - slot = EvalPlanQualNext(&onConflict->mt_epqstate); + if (resultRelInfo->ri_WithCheckOptions != NIL) + { + TupleTableSlot *opts; + + /* Construct temp slot for locked tuple from target */ + opts = MakeSingleTupleTableSlot(slot->tts_tupleDescriptor); + ExecStoreTuple(copyTuple, opts, InvalidBuffer, false); + + /* + * Check existing/TARGET.* tuple against UPDATE-applicable + * USING security barrier quals (if any), enforced here as RLS + * checks/WCOs. + * + * The rewriter creates UPDATE RLS checks/WCOs for UPDATE + * security quals, and stores them as WCOs of "kind" + * WCO_RLS_CONFLICT_CHECK, but that's almost the extent of its + * special handling for ON CONFLICT DO UPDATE. + * + * Because the auxiliary query shares its parent's target RTE + * (and because auxiliary planstate shares its parent's + * resultRelinfo), the rewriter will also have associated + * UPDATE applicable straight RLS checks/WCOs for the benefit + * of ExecUpdate(). INSERTs and UPDATEs naturally have + * mutually exclusive WCO kinds, so there is no danger of + * spurious over-enforcement in the INSERT or UPDATE path. + */ + ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo, + slot, mtstate->ps.state); + } - if (resultRelInfo->ri_WithCheckOptions != NIL) - { - TupleTableSlot *opts; + if (!TupIsNull(slot)) + *returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, + slot, planSlot, + &mtstate->mt_epqstate, + mtstate->ps.state, canSetTag); - /* Construct temp slot for locked tuple from target */ - opts = MakeSingleTupleTableSlot(slot->tts_tupleDescriptor); - ExecStoreTuple(copyTuple, opts, InvalidBuffer, false); + ReleaseBuffer(buffer); /* - * Check existing/TARGET.* tuple against UPDATE-applicable - * USING security barrier quals (if any), enforced here as RLS - * checks/WCOs. - * - * The rewriter creates UPDATE RLS checks/WCOs for UPDATE - * security quals, and stores them as WCOs of "kind" - * WCO_RLS_CONFLICT_CHECK, but that's almost the extent of its - * special handling for ON CONFLICT DO UPDATE. - * - * Because the auxiliary query shares its parent's target RTE - * (and because auxiliary planstate shares its parent's - * resultRelinfo), the rewriter will also have associated - * UPDATE applicable straight RLS checks/WCOs for the benefit - * of ExecUpdate(). INSERTs and UPDATEs naturally have - * mutually exclusive WCO kinds, so there is no danger of - * spurious over-enforcement in the INSERT or UPDATE path. + * As when executing an UPDATE's ModifyTable node in the + * conventional manner, reset the per-output-tuple ExprContext */ - ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo, - slot, onConflict->ps.state); + //ResetExprContext(econtext); } - - if (!TupIsNull(slot)) - *returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, - slot, planSlot, - &onConflict->mt_epqstate, - onConflict->ps.state, canSetTag); - - ReleaseBuffer(buffer); - - /* - * As when executing an UPDATE's ModifyTable node in the - * conventional manner, reset the per-output-tuple ExprContext - */ - ResetPerTupleExprContext(onConflict->ps.state); - - /* must provide our own instrumentation support */ - if (onConflict->ps.instrument) - InstrStopNode(onConflict->ps.instrument, *returning ? 1 : 0); - return true; case HeapTupleUpdated: if (IsolationUsesXactSnapshot()) @@ -1284,7 +1240,7 @@ fireBSTriggers(ModifyTableState *node) case CMD_INSERT: ExecBSInsertTriggers(node->ps.state, node->resultRelInfo); if (node->conf == CONF_INSERT) - ExecBSUpdateTriggers(node->onConflict->state, + ExecBSUpdateTriggers(node->ps.state, node->resultRelInfo); break; case CMD_UPDATE: @@ -1309,7 +1265,7 @@ fireASTriggers(ModifyTableState *node) { case CMD_INSERT: if (node->conf == CONF_INSERT) - ExecASUpdateTriggers(node->onConflict->state, + ExecASUpdateTriggers(node->ps.state, node->resultRelInfo); ExecASInsertTriggers(node->ps.state, node->resultRelInfo); break; @@ -1338,7 +1294,6 @@ ExecModifyTable(ModifyTableState *node) { EState *estate = node->ps.state; CmdType operation = node->operation; - ModifyTableState *onConflict = (ModifyTableState *) node->onConflict; ResultRelInfo *saved_resultRelInfo; ResultRelInfo *resultRelInfo; PlanState *subplanstate; @@ -1509,7 +1464,8 @@ ExecModifyTable(ModifyTableState *node) switch (operation) { case CMD_INSERT: - slot = ExecInsert(slot, planSlot, onConflict, + slot = ExecInsert(node, slot, planSlot, + &node->mt_epqstate, node->arbiterIndexes, node->conf, estate, node->canSetTag); break; @@ -1559,7 +1515,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) { ModifyTableState *mtstate; CmdType operation = node->operation; - Plan *onConflictPlan = node->onConflictPlan; int nplans = list_length(node->plans); ResultRelInfo *saved_resultRelInfo; ResultRelInfo *resultRelInfo; @@ -1762,6 +1717,74 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } } + + if (mtstate->conf != CONF_NONE) + { + /* already exists if created by RETURNING processing above */ + if (mtstate->ps.ps_ExprContext == NULL) + ExecAssignExprContext(estate, &mtstate->ps); + } + + /* + * Initialize target list and qual for ON CONFLICT .. UPDATE .... [WHERE] + * + * ATM there will never be more than one child here, but we might want to + * expand that at some point. So just do it right from the start. + */ +#ifdef NOT_YET + resultRelInfo = mtstate->resultRelInfo; + i = 0; + foreach(l, node->onConflictSetWheres) + { + Node *qual; + List *expr; + + qual = (Node *) lfirst(l); + + Assert(IsA(qual, Expr)); + + expr = (List *) ExecInitExpr((Expr *) qual, mtstate->mt_plans[i]); + + resultRelInfo->ri_onConflictSetWhere = expr; + resultRelInfo++; + i++; + } +#endif + + if (node->onConflictSets) + { + TupleTableSlot *slot; + ExprContext *econtext; + + Assert(mtstate->ps.ps_ExprContext != NULL); + + /* take the first one... */ + tupDesc = ExecTypeFromTL((List *) linitial(node->onConflictSets), false); + + slot = ExecInitExtraTupleSlot(mtstate->ps.state); + ExecSetSlotDescriptor(slot, tupDesc);/* FIXME: arguably wrong? */ + mtstate->mt_outertupleslot = slot; + mtstate->mt_fuck = ExecInitExtraTupleSlot(mtstate->ps.state); + + econtext = mtstate->ps.ps_ExprContext; + + resultRelInfo = mtstate->resultRelInfo; + foreach(l, node->onConflictSets) + { + List *rlist = (List *) lfirst(l); + List *rliststate; + + rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps); + + resultRelInfo->ri_onConflictSetProj = + ExecBuildProjectionInfo(rliststate, econtext, slot, + resultRelInfo->ri_RelationDesc->rd_att); + + resultRelInfo->ri_onConflictSet = rlist; + resultRelInfo++; + } + } + /* select first subplan */ mtstate->mt_whichplan = 0; subplan = (Plan *) linitial(node->plans); @@ -1799,8 +1822,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } break; case CMD_UPDATE: - junk_filter_needed = (mtstate->conf == CONF_NONE); - break; case CMD_DELETE: junk_filter_needed = true; break; @@ -1866,19 +1887,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) } } - /* Initialize auxiliary ModifyTable node, for ON CONFLICT DO UPDATE */ - if (onConflictPlan) - { - Assert(mtstate->conf == CONF_INSERT); - - /* - * ExecModifyTable() is never called for auxiliary update - * ModifyTableState. Execution of the auxiliary plan is driven by its - * parent in an ad-hoc fashion. - */ - mtstate->onConflict = ExecInitNode(onConflictPlan, estate, eflags); - } - /* * Set up a tuple table slot for use for trigger output tuples. In a plan * containing multiple ModifyTable nodes, all can share one such slot, so @@ -1954,8 +1962,6 @@ ExecEndModifyTable(ModifyTableState *node) */ for (i = 0; i < node->mt_nplans; i++) ExecEndNode(node->mt_plans[i]); - - ExecEndNode(node->onConflict); } void diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index cd21194416..0b94e38f48 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -182,7 +182,9 @@ _copyModifyTable(const ModifyTable *from) COPY_NODE_FIELD(plans); COPY_SCALAR_FIELD(conf); COPY_NODE_FIELD(arbiterIndexes); - COPY_NODE_FIELD(onConflictPlan); + COPY_NODE_FIELD(onConflictSets); + COPY_NODE_FIELD(onConflictSetWheres); + COPY_SCALAR_FIELD(excludedRel); COPY_NODE_FIELD(withCheckOptionLists); COPY_NODE_FIELD(returningLists); COPY_NODE_FIELD(fdwPrivLists); @@ -1784,19 +1786,6 @@ _copyCurrentOfExpr(const CurrentOfExpr *from) return newnode; } -/* - * _copyExcludedExpr - */ -static ExcludedExpr * -_copyExcludedExpr(const ExcludedExpr *from) -{ - ExcludedExpr *newnode = makeNode(ExcludedExpr); - - COPY_NODE_FIELD(arg); - - return newnode; -} - /* * _copyInferenceElem */ @@ -2181,7 +2170,8 @@ _copyConflictClause(const ConflictClause *from) COPY_SCALAR_FIELD(conf); COPY_NODE_FIELD(infer); - COPY_NODE_FIELD(updatequery); + COPY_NODE_FIELD(targetList); + COPY_NODE_FIELD(whereClause); COPY_LOCATION_FIELD(location); return newnode; @@ -2607,7 +2597,8 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(conf); COPY_NODE_FIELD(arbiterElems); COPY_NODE_FIELD(arbiterWhere); - COPY_NODE_FIELD(onConflict); + COPY_NODE_FIELD(onConflictSet); + COPY_NODE_FIELD(onConflictSetWhere); COPY_NODE_FIELD(returningList); COPY_NODE_FIELD(groupClause); COPY_NODE_FIELD(havingQual); @@ -4326,9 +4317,6 @@ copyObject(const void *from) case T_CurrentOfExpr: retval = _copyCurrentOfExpr(from); break; - case T_ExcludedExpr: - retval = _copyExcludedExpr(from); - break; case T_InferenceElem: retval = _copyInferenceElem(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 83abf97181..9e020fc4d8 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -693,14 +693,6 @@ _equalInferenceElem(const InferenceElem *a, const InferenceElem *b) return true; } -static bool -_equalExcludedExpr(const ExcludedExpr *a, const ExcludedExpr *b) -{ - COMPARE_NODE_FIELD(arg); - - return true; -} - static bool _equalTargetEntry(const TargetEntry *a, const TargetEntry *b) { @@ -890,7 +882,8 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(conf); COMPARE_NODE_FIELD(arbiterElems); COMPARE_NODE_FIELD(arbiterWhere); - COMPARE_NODE_FIELD(onConflict); + COMPARE_NODE_FIELD(onConflictSet); + COMPARE_NODE_FIELD(onConflictSetWhere); COMPARE_NODE_FIELD(returningList); COMPARE_NODE_FIELD(groupClause); COMPARE_NODE_FIELD(havingQual); @@ -2460,7 +2453,8 @@ _equalConflictClause(const ConflictClause *a, const ConflictClause *b) { COMPARE_SCALAR_FIELD(conf); COMPARE_NODE_FIELD(infer); - COMPARE_NODE_FIELD(updatequery); + COMPARE_NODE_FIELD(targetList); + COMPARE_NODE_FIELD(whereClause); COMPARE_LOCATION_FIELD(location); return true; @@ -2745,9 +2739,6 @@ equal(const void *a, const void *b) case T_CurrentOfExpr: retval = _equalCurrentOfExpr(a, b); break; - case T_ExcludedExpr: - retval = _equalExcludedExpr(a, b); - break; case T_InferenceElem: retval = _equalInferenceElem(a, b); break; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index dbc80b4aa4..a43f098e92 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -235,13 +235,6 @@ exprType(const Node *expr) case T_CurrentOfExpr: type = BOOLOID; break; - case T_ExcludedExpr: - { - const ExcludedExpr *n = (const ExcludedExpr *) expr; - - type = exprType((Node *) n->arg); - } - break; case T_InferenceElem: { const InferenceElem *n = (const InferenceElem *) expr; @@ -483,12 +476,6 @@ exprTypmod(const Node *expr) return ((const CoerceToDomainValue *) expr)->typeMod; case T_SetToDefault: return ((const SetToDefault *) expr)->typeMod; - case T_ExcludedExpr: - { - const ExcludedExpr *n = (const ExcludedExpr *) expr; - - return ((const Var *) n->arg)->vartypmod; - } case T_PlaceHolderVar: return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr); default: @@ -914,9 +901,6 @@ exprCollation(const Node *expr) case T_CurrentOfExpr: coll = InvalidOid; /* result is always boolean */ break; - case T_ExcludedExpr: - coll = exprCollation((Node *) ((const ExcludedExpr *) expr)->arg); - break; case T_InferenceElem: coll = exprCollation((Node *) ((const InferenceElem *) expr)->expr); break; @@ -1115,12 +1099,6 @@ exprSetCollation(Node *expr, Oid collation) case T_CurrentOfExpr: Assert(!OidIsValid(collation)); /* result is always boolean */ break; - case T_ExcludedExpr: - { - Var *v = (Var *) ((ExcludedExpr *) expr)->arg; - v->varcollid = collation; - } - break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); break; @@ -1529,10 +1507,6 @@ exprLocation(const Node *expr) /* just use argument's location */ loc = exprLocation((Node *) ((const PlaceHolderVar *) expr)->phexpr); break; - case T_ExcludedExpr: - /* just use nested expr's location */ - loc = exprLocation((Node *) ((const ExcludedExpr *) expr)->arg); - break; case T_InferenceElem: /* just use nested expr's location */ loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr); @@ -1966,8 +1940,6 @@ expression_tree_walker(Node *node, break; case T_PlaceHolderVar: return walker(((PlaceHolderVar *) node)->phexpr, context); - case T_ExcludedExpr: - return walker(((ExcludedExpr *) node)->arg, context); case T_InferenceElem: return walker(((InferenceElem *) node)->expr, context); case T_AppendRelInfo: @@ -2022,7 +1994,9 @@ query_tree_walker(Query *query, return true; if (walker(query->arbiterWhere, context)) return true; - if (walker(query->onConflict, context)) + if (walker(query->onConflictSet, context)) + return true; + if (walker(query->onConflictSetWhere, context)) return true; if (walker((Node *) query->returningList, context)) return true; @@ -2686,16 +2660,6 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; - case T_ExcludedExpr: - { - ExcludedExpr *excludedexpr = (ExcludedExpr *) node; - ExcludedExpr *newnode; - - FLATCOPY(newnode, excludedexpr, ExcludedExpr); - MUTATE(newnode->arg, newnode->arg, Node *); - return (Node *) newnode; - } - break; case T_InferenceElem: { InferenceElem *inferenceelemdexpr = (InferenceElem *) node; @@ -2787,7 +2751,8 @@ query_tree_mutator(Query *query, MUTATE(query->withCheckOptions, query->withCheckOptions, List *); MUTATE(query->arbiterElems, query->arbiterElems, List *); MUTATE(query->arbiterWhere, query->arbiterWhere, Node *); - MUTATE(query->onConflict, query->onConflict, Node *); + MUTATE(query->onConflictSet, query->onConflictSet, List *); + MUTATE(query->onConflictSetWhere, query->onConflictSetWhere, Node *); MUTATE(query->returningList, query->returningList, List *); MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->setOperations, query->setOperations, Node *); @@ -3314,7 +3279,9 @@ raw_expression_tree_walker(Node *node, if (walker(stmt->infer, context)) return true; - if (walker(stmt->updatequery, context)) + if (walker(stmt->whereClause, context)) + return true; + if (walker(stmt->targetList, context)) return true; } break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 00627789f8..175d8ad21f 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -334,11 +334,13 @@ _outModifyTable(StringInfo str, const ModifyTable *node) WRITE_NODE_FIELD(plans); WRITE_ENUM_FIELD(conf, ConfType); WRITE_NODE_FIELD(arbiterIndexes); - WRITE_NODE_FIELD(onConflictPlan); WRITE_NODE_FIELD(withCheckOptionLists); WRITE_NODE_FIELD(returningLists); WRITE_NODE_FIELD(fdwPrivLists); WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(onConflictSets); + WRITE_NODE_FIELD(onConflictSetWheres); + WRITE_INT_FIELD(excludedRel); WRITE_INT_FIELD(epqParam); } @@ -2339,7 +2341,8 @@ _outQuery(StringInfo str, const Query *node) WRITE_ENUM_FIELD(conf, ConfType); WRITE_NODE_FIELD(arbiterElems); WRITE_NODE_FIELD(arbiterWhere); - WRITE_NODE_FIELD(onConflict); + WRITE_NODE_FIELD(onConflictSet); + WRITE_NODE_FIELD(onConflictSetWhere); WRITE_NODE_FIELD(returningList); WRITE_NODE_FIELD(groupClause); WRITE_NODE_FIELD(havingQual); @@ -3133,9 +3136,6 @@ _outNode(StringInfo str, const void *obj) case T_CurrentOfExpr: _outCurrentOfExpr(str, obj); break; - case T_ExcludedExpr: - _outExcludedExpr(str, obj); - break; case T_InferenceElem: _outInferenceElem(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 7aad4469c4..3073cd6314 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -217,7 +217,8 @@ _readQuery(void) READ_ENUM_FIELD(conf, ConfCmd); READ_NODE_FIELD(arbiterElems); READ_NODE_FIELD(arbiterWhere); - READ_NODE_FIELD(onConflict); + READ_NODE_FIELD(onConflictSet); + READ_NODE_FIELD(onConflictSetWhere); READ_NODE_FIELD(returningList); READ_NODE_FIELD(groupClause); READ_NODE_FIELD(havingQual); @@ -1134,19 +1135,6 @@ _readCurrentOfExpr(void) READ_DONE(); } -/* - * _readExcludedExpr - */ -static ExcludedExpr * -_readExcludedExpr(void) -{ - READ_LOCALS(ExcludedExpr); - - READ_NODE_FIELD(arg); - - READ_DONE(); -} - /* * _readInferenceElem */ @@ -1428,8 +1416,6 @@ parseNodeString(void) return_value = _readSetToDefault(); else if (MATCH("CURRENTOFEXPR", 13)) return_value = _readCurrentOfExpr(); - else if (MATCH("EXCLUDED", 8)) - return_value = _readExcludedExpr(); else if (MATCH("INFERENCEELEM", 13)) return_value = _readInferenceElem(); else if (MATCH("TARGETENTRY", 11)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 24ea9f029c..057c1a5762 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4815,7 +4815,8 @@ make_modifytable(PlannerInfo *root, Index nominalRelation, List *resultRelations, List *subplans, List *withCheckOptionLists, List *returningLists, - List *rowMarks, Plan *onConflictPlan, ConfCmd conf, + List *rowMarks, List *onConflictSets, + List *onConflictSetWheres, ConfCmd conf, int epqParam) { ModifyTable *node = makeNode(ModifyTable); @@ -4868,12 +4869,13 @@ make_modifytable(PlannerInfo *root, node->plans = subplans; node->conf = conf; node->arbiterIndexes = NIL; - node->onConflictPlan = onConflictPlan; node->withCheckOptionLists = withCheckOptionLists; node->returningLists = returningLists; node->rowMarks = rowMarks; + node->onConflictSets = onConflictSets; + node->onConflictSetWheres = onConflictSetWheres; node->epqParam = epqParam; - + node->excludedRel = nominalRelation + 1; /* * For each result relation that is a foreign table, allow the FDW to * construct private plan data, and accumulate it all into a list. diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index d770cad01a..aae6282f27 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -462,6 +462,16 @@ subquery_planner(PlannerGlobal *glob, Query *parse, parse->limitCount = preprocess_expression(root, parse->limitCount, EXPRKIND_LIMIT); + /* XXX */ + parse->onConflictSet = (List *) + preprocess_expression(root, (Node *) parse->onConflictSet, + EXPRKIND_TARGET); + + parse->onConflictSetWhere = (Node *) + preprocess_expression(root, (Node *) parse->onConflictSetWhere, + EXPRKIND_QUAL); + + root->append_rel_list = (List *) preprocess_expression(root, (Node *) root->append_rel_list, EXPRKIND_APPINFO); @@ -578,6 +588,8 @@ subquery_planner(PlannerGlobal *glob, Query *parse, List *withCheckOptionLists; List *returningLists; List *rowMarks; + List *onConflictSets; + List *onConflictSetWheres; /* * Set up the WITH CHECK OPTION and RETURNING lists-of-lists, if @@ -603,6 +615,17 @@ subquery_planner(PlannerGlobal *glob, Query *parse, else rowMarks = root->rowMarks; + if (parse->onConflictSet) + onConflictSets = list_make1(parse->onConflictSet); + else + onConflictSets = NIL; + + if (parse->onConflictSetWhere) + onConflictSetWheres = list_make1(parse->onConflictSetWhere); + else + onConflictSetWheres = NIL; + + plan = (Plan *) make_modifytable(root, parse->commandType, parse->canSetTag, @@ -612,58 +635,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, withCheckOptionLists, returningLists, rowMarks, - NULL, + onConflictSets, + onConflictSetWheres, parse->conf, SS_assign_special_param(root)); - - if (parse->onConflict) - { - Query *conflictQry = (Query *) parse->onConflict; - ModifyTable *parent = (ModifyTable *) plan; - - /* - * An ON CONFLICT DO UPDATE query is a subquery of its parent - * INSERT ModifyTable, but isn't formally a subplan -- it's an - * "auxiliary" plan. - * - * During execution, the auxiliary plan state is used to - * execute the UPDATE query in an ad-hoc manner, driven by the - * parent. The executor will only ever execute the auxiliary - * plan through its parent. onConflictPlan is "auxiliary" to - * its parent in the sense that it's strictly encapsulated - * from other code (for example, the executor does not - * separately track it within estate as a plan that needs to - * have execution finished when it appears within a - * data-modifying CTE -- only the parent is specifically - * tracked for that purpose). - * - * There is a fundamental nexus between parent and auxiliary - * plans that makes a fully unified representation seem - * compelling (a "CMD_UPSERT" ModifyTable plan and Query). - * That would obviate the need to specially track auxiliary - * state across all stages of execution just for this case; - * the optimizer would then not have to generate a - * fully-formed, independent UPDATE subquery plan (with a - * scanstate only useful for EvalPlanQual() re-evaluation). - * However, it's convenient to plan each ModifyTable - * separately, as doing so maximizes code reuse. The - * alternative must be to introduce abstractions that (for - * example) allow a single "CMD_UPSERT" ModifyTable to have - * two distinct types of targetlist (that will need to be - * processed differently during parsing and rewriting anyway). - * The auxiliary UPDATE plan is a good trade-off between a - * fully-fledged "CMD_UPSERT" representation, and the opposite - * extreme of tracking two separate ModifyTable nodes, joined - * by a contrived join type, with (for example) odd properties - * around tuple visibility not well encapsulated. A contrived - * join based design would also necessitate teaching - * ModifyTable nodes to support rescan just for the benefit of - * ON CONFLICT DO UPDATE. - */ - parent->onConflictPlan = subquery_planner(glob, conflictQry, - root, hasRecursion, - 0, NULL); - } } } @@ -850,6 +825,8 @@ inheritance_planner(PlannerInfo *root) List *resultRelations = NIL; List *withCheckOptionLists = NIL; List *returningLists = NIL; + List *onConflictSets = NIL; + List *onConflictSetWheres = NIL; List *rowMarks; ListCell *lc; @@ -1097,6 +1074,12 @@ inheritance_planner(PlannerInfo *root) if (parse->returningList) returningLists = lappend(returningLists, subroot.parse->returningList); + if (parse->onConflictSet) + onConflictSets = lappend(onConflictSets, + subroot.parse->onConflictSet); + if (parse->onConflictSetWhere) + onConflictSetWheres = lappend(onConflictSetWheres, + subroot.parse->onConflictSetWhere); } /* Mark result as unordered (probably unnecessary) */ @@ -1146,7 +1129,8 @@ inheritance_planner(PlannerInfo *root) withCheckOptionLists, returningLists, rowMarks, - NULL, + onConflictSets, + onConflictSetWheres, parse->conf, SS_assign_special_param(root)); } @@ -1295,6 +1279,16 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) /* Preprocess targetlist */ tlist = preprocess_targetlist(root, tlist); + + if (parse->onConflictSet) + { + parse->onConflictSet = preprocess_targetlist_cmd(root, + parse->onConflictSet, + CMD_UPDATE, + parse->resultRelation, + parse->rtable); + } + /* * Expand any rangetable entries that have security barrier quals. * This may add new security barrier subquery RTEs to the rangetable. diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index ce551aa2be..a1120b6870 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -26,7 +26,7 @@ #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" - +#include "rewrite/rewriteManip.h" /* FIXME: move */ typedef struct { @@ -134,6 +134,7 @@ static bool fix_opfuncids_walker(Node *node, void *context); static bool extract_query_dependencies_walker(Node *node, PlannerInfo *context); +static indexed_tlist *build_tlist_index_other_vars(List *tlist, Index ignore_rel); /***************************************************************************** * @@ -759,6 +760,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) { lfirst_int(l) += rtoffset; } + foreach(l, splan->rowMarks) { PlanRowMark *rc = (PlanRowMark *) lfirst(l); @@ -773,6 +775,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) rtoffset); } + splan->excludedRel += rtoffset; + + fix_scan_list(root, splan->onConflictSets, rtoffset); + + /* FIXME: This should be moved away from here */ + /* + * Setup pseudo relation EXCLUDED in the outer slot. + */ + ChangeVarNodes((Node *) splan->onConflictSets, + splan->excludedRel, OUTER_VAR, 0); + /* * Append this ModifyTable node's final result relation RT * index(es) to the global list for the plan, and set its @@ -780,35 +793,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) * global list. */ splan->resultRelIndex = list_length(root->glob->resultRelations); - - if (!splan->onConflictPlan) - { - /* - * Only actually append result relation for non-auxiliary - * ModifyTable plans - */ - root->glob->resultRelations = - list_concat(root->glob->resultRelations, - list_copy(splan->resultRelations)); - } - else - { - /* - * Adjust rtoffset passed to child, to compensate for - * dummy RTE left by EXCLUDED.* alias in auxiliary plan. - * Auxiliary plan will target same resultRelation from - * flattened range table as its parent. - */ - splan->onConflictPlan = - set_plan_refs(root, splan->onConflictPlan, - rtoffset - PRS2_OLD_VARNO); - - /* - * Set up the visible plan targetlist as being the same as - * the parent. Again, this is for the use of EXPLAIN only. - */ - splan->onConflictPlan->targetlist = splan->plan.targetlist; - } + root->glob->resultRelations = + list_concat(root->glob->resultRelations, + list_copy(splan->resultRelations)); } break; case T_Append: diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 40bcde92a2..43ae04c1dc 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -2340,6 +2340,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, locally_added_param); finalize_primnode((Node *) mtplan->returningLists, &context); + finalize_primnode((Node *) mtplan->onConflictSets, + &context); foreach(l, mtplan->plans) { context.paramids = @@ -2349,12 +2351,6 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, valid_params, scan_params)); } - - /* - * No need to directly handle onConflictPlan here, since it - * cannot have params (due to parse analysis enforced - * restrictions prohibiting subqueries). - */ } break; diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 50acfe40e9..e7efec9c60 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1030,6 +1030,8 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, pullup_replace_vars((Node *) parse->targetList, &rvcontext); parse->returningList = (List *) pullup_replace_vars((Node *) parse->returningList, &rvcontext); + parse->onConflictSet = (List *) + pullup_replace_vars((Node *) parse->onConflictSet, &rvcontext); replace_vars_in_jointree((Node *) parse->jointree, &rvcontext, lowest_nulling_outer_join); Assert(parse->setOperations == NULL); @@ -1605,6 +1607,8 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) pullup_replace_vars((Node *) parse->targetList, &rvcontext); parse->returningList = (List *) pullup_replace_vars((Node *) parse->returningList, &rvcontext); + parse->onConflictSet = (List *) + pullup_replace_vars((Node *) parse->onConflictSet, &rvcontext); replace_vars_in_jointree((Node *) parse->jointree, &rvcontext, NULL); Assert(parse->setOperations == NULL); parse->havingQual = pullup_replace_vars(parse->havingQual, &rvcontext); diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 580c846770..2ec24a5304 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -40,6 +40,20 @@ static List *expand_targetlist(List *tlist, int command_type, Index result_relation, List *range_table); +List * +preprocess_targetlist_cmd(PlannerInfo *root, + List *tlist, + int command_type, + int result_relation, + List *range_table) +{ + + tlist = expand_targetlist(tlist, command_type, + result_relation, range_table); + + return tlist; +} + /* * preprocess_targetlist * Driver for preprocessing the parse tree targetlist. diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 371e11a36c..ad8ca8adf5 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -388,7 +388,6 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->conf = CONF_NONE; - qry->onConflict = NULL; qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -422,13 +421,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ListCell *icols; ListCell *attnos; ListCell *lc; + RangeTblEntry *exclRte; + /* There can't be any outer WITH to worry about */ Assert(pstate->p_ctenamespace == NIL); qry->commandType = CMD_INSERT; pstate->p_is_insert = true; - pstate->p_is_on_conflict = conf != CONF_NONE; /* process the WITH clause independently of all else */ if (stmt->withClause) @@ -490,6 +490,34 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false, ACL_INSERT); + + if (stmt->confClause) + { + addRTEtoQuery(pstate, pstate->p_target_rangetblentry, false, true, true); + + } + + if (conf == CONF_INSERT) + { + pstate->p_target_rangetblentry->requiredPerms |= ACL_UPDATE; + + exclRte = addRangeTableEntryForRelation(pstate, + pstate->p_target_relation, + makeAlias("excluded", NIL), + false, false); + + /* + * Add EXCLUDED RTE to namespace. It does not matter that the RTE is + * not added to the Query joinlist, since its Vars are merely + * placeholders for ExcludedExpr. + */ + addRTEtoQuery(pstate, exclRte, false, true, true); + + /* Append parent/our target to Query rtable (should be last) */ + /* FRAKME pstate->p_rtable = lappend(pstate->p_rtable, exclRte); */ + pstate->p_rtable = lappend(pstate->p_rtable, exclRte); + } + /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); Assert(list_length(icolumns) == list_length(attrnos)); @@ -749,6 +777,53 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) attnos = lnext(attnos); } + if (stmt->confClause && list_length(stmt->confClause->targetList) > 0) + { + ListCell *tlo, *tln; + Node *qual; + + qry->onConflictSet = + transformTargetList(pstate, stmt->confClause->targetList, + EXPR_KIND_UPDATE_SOURCE); + + Assert(list_length(stmt->confClause->targetList) == + list_length(qry->onConflictSet)); + + forboth(tln, qry->onConflictSet, tlo, stmt->confClause->targetList) + { + TargetEntry *tle = (TargetEntry *) lfirst(tln); + ResTarget *origTarget = (ResTarget *) lfirst(tlo); + int attrno; + + if (tle->resjunk) + elog(ERROR, "not expecting resjunks here"); + + attrno = attnameAttNum(pstate->p_target_relation, + origTarget->name, true); + if (attrno == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + origTarget->name, + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, origTarget->location))); + + updateTargetListEntry(pstate, tle, origTarget->name, + attrno, + origTarget->indirection, + origTarget->location); + + /* Mark the target column as requiring update permissions */ + rte->updatedCols = bms_add_member(rte->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); + + } + + qual = transformWhereClause(pstate, stmt->confClause->whereClause, + EXPR_KIND_WHERE, "WHERE"); + qry->onConflictSetWhere = qual; + } + /* * If we have a RETURNING clause, or there are inference elements used as * for ON CONFLICT, we need to add the target relation to the query @@ -758,11 +833,27 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) */ if (stmt->returningList || stmt->confClause) { + /* FIXME */ pstate->p_namespace = NIL; addRTEtoQuery(pstate, pstate->p_target_rangetblentry, false, true, true); - qry->returningList = transformReturningList(pstate, - stmt->returningList); + + if (stmt->returningList) + qry->returningList = transformReturningList(pstate, + stmt->returningList); + + if (stmt->confClause) + { + /* + * Perform parse analysis of arbiter columns/expressions. These are + * later used to infer a unique index which arbitrates whether or not + * to take the alternative ON CONFLICT path (i.e. whether or not to + * INSERT or take the alternative path in respect of each slot proposed + * for insertion). + */ + transformConflictClause(pstate, stmt->confClause, &qry->arbiterElems, + &qry->arbiterWhere); + } } /* done building the range table and jointree */ @@ -771,48 +862,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) qry->conf = conf; qry->hasSubLinks = pstate->p_hasSubLinks; - qry->onConflict = NULL; - - if (stmt->confClause) - { - /* - * ON CONFLICT DO UPDATE requires special parse analysis of auxiliary - * update Query - */ - if (stmt->confClause->updatequery) - { - ParseState *sub_pstate = make_parsestate(pstate); - Query *uqry; - - /* - * The optimizer is not prepared to accept a subquery RTE for a - * non-CMD_SELECT Query. The CMD_UPDATE Query is tracked as - * special auxiliary state, while there is more or less analogous - * auxiliary state tracked in later stages of query execution. - * - * Parent canSetTag only ever actually consulted, so no need to - * set that here. - */ - uqry = transformStmt(sub_pstate, stmt->confClause->updatequery); - Assert(uqry->commandType == CMD_UPDATE && - uqry->conf == CONF_UPDATE); - - /* Save auxiliary query */ - qry->onConflict = (Node *) uqry; - - free_parsestate(sub_pstate); - } - - /* - * Perform parse analysis of arbiter columns/expressions. These are - * later used to infer a unique index which arbitrates whether or not - * to take the alternative ON CONFLICT path (i.e. whether or not to - * INSERT or take the alternative path in respect of each slot proposed - * for insertion). - */ - transformConflictClause(pstate, stmt->confClause, &qry->arbiterElems, - &qry->arbiterWhere); - } assign_query_collations(pstate, qry); @@ -1060,7 +1109,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->conf = CONF_NONE; - qry->onConflict = NULL; qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -1963,9 +2011,6 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->commandType = CMD_UPDATE; pstate->p_is_update = true; - /* for auxiliary UPDATEs, visit parent INSERT to set target table */ - pstate->p_is_on_conflict = (stmt->relation == NULL); - /* process the WITH clause independently of all else */ if (stmt->withClause) { @@ -1974,19 +2019,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } - if (!pstate->p_is_on_conflict) - { - InhOption = interpretInhOption(stmt->relation->inhOpt); + InhOption = interpretInhOption(stmt->relation->inhOpt); - qry->conf = CONF_NONE; - } - else - { - /* auxiliary UPDATE does not accept ONLY */ - InhOption = false; - - qry->conf = CONF_UPDATE; - } + qry->conf = CONF_NONE; qry->resultRelation = setTargetTable(pstate, stmt->relation, InhOption, @@ -2020,7 +2055,6 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); - qry->onConflict = NULL; qry->hasSubLinks = pstate->p_hasSubLinks; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 08e2ef9afc..f5262c5c35 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -520,7 +520,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type cte_list %type within_group_clause -%type OnConflictUpdateStmt %type filter_clause %type window_clause window_definition_list opt_partition_clause %type window_definition over_clause window_specification @@ -9410,12 +9409,13 @@ insert_column_item: ; opt_on_conflict: - ON CONFLICT opt_conf_expr DO OnConflictUpdateStmt + ON CONFLICT opt_conf_expr DO UPDATE SET set_clause_list where_clause { $$ = makeNode(ConflictClause); $$->conf = CONF_INSERT; $$->infer = $3; - $$->updatequery = $5; + $$->targetList = $7; + $$->whereClause = $8; $$->location = @1; } | @@ -9424,7 +9424,8 @@ opt_on_conflict: $$ = makeNode(ConflictClause); $$->conf = CONF_NOTHING; $$->infer = $3; - $$->updatequery = NULL; + $$->targetList = NIL; + $$->whereClause = NULL; $$->location = @1; } | /*EMPTY*/ @@ -9546,22 +9547,6 @@ UpdateStmt: opt_with_clause UPDATE relation_expr_opt_alias } ; -OnConflictUpdateStmt: UPDATE - SET set_clause_list - where_clause - { - UpdateStmt *n = makeNode(UpdateStmt); - /* NULL relation conveys auxiliary */ - n->relation = NULL; - n->targetList = $3; - n->fromClause = NULL; - n->whereClause = $4; - n->returningList = NULL; - n->withClause = NULL; - $$ = (Node *)n; - } - ; - set_clause_list: set_clause { $$ = $1; } | set_clause_list ',' set_clause { $$ = list_concat($1,$3); } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index b559b3d6a5..4ebbf974df 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -149,9 +149,7 @@ transformFromClause(ParseState *pstate, List *frmList) * We also open the target relation and acquire a write lock on it. * This must be done before processing the FROM list, in case the target * is also mentioned as a source relation --- we want to be sure to grab - * the write lock before any read lock. Note that when called during - * the parse analysis of an auxiliary UPDATE query, relation may be - * NULL, and the details are acquired from the parent. + * the write lock before any read lock. * * If alsoSource is true, add the target to the query's joinlist and * namespace. For INSERT, we don't want the target to be joined to; @@ -178,35 +176,20 @@ setTargetTable(ParseState *pstate, RangeVar *relation, /* * Open target rel and grab suitable lock (which we will hold till end of - * transaction), iff this is not an auxiliary ON CONFLICT DO UPDATE. + * transaction). * * free_parsestate() will eventually do the corresponding heap_close(), - * but *not* release the lock (again, iff this is not an auxiliary ON - * CONFLICT DO UPDATE). + * but *not* release the lock. */ - if (!pstate->p_is_on_conflict || pstate->p_is_insert) - { - pstate->p_target_relation = parserOpenTable(pstate, relation, - RowExclusiveLock); - - /* - * Now build an RTE. - */ - rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation, - relation->alias, inh, false); + pstate->p_target_relation = parserOpenTable(pstate, relation, + RowExclusiveLock); + /* + * Now build an RTE. + */ + rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation, + relation->alias, inh, false); - /* - * Override addRangeTableEntry's default ACL_SELECT permissions check, - * and instead mark target table as requiring exactly the specified - * permissions. - * - * If we find an explicit reference to the rel later during parse - * analysis, we will add the ACL_SELECT bit back again; see - * markVarForSelectPriv and its callers. - */ - rte->requiredPerms = requiredPerms; - } - else +#ifdef FIXME { RangeTblEntry *exclRte; @@ -251,8 +234,10 @@ setTargetTable(ParseState *pstate, RangeVar *relation, addRTEtoQuery(pstate, exclRte, false, true, true); /* Append parent/our target to Query rtable (should be last) */ + /* FRACKME */ pstate->p_rtable = lappend(pstate->p_rtable, rte); } +#endif pstate->p_target_rangetblentry = rte; @@ -260,6 +245,17 @@ setTargetTable(ParseState *pstate, RangeVar *relation, rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); + /* + * Override addRangeTableEntry's default ACL_SELECT permissions check, + * and instead mark target table as requiring exactly the specified + * permissions. + * + * If we find an explicit reference to the rel later during parse + * analysis, we will add the ACL_SELECT bit back again; see + * markVarForSelectPriv and its callers. + */ + rte->requiredPerms = requiredPerms; + /* * If UPDATE/DELETE, add table to joinlist and namespace. * @@ -2346,9 +2342,6 @@ transformConflictClause(ParseState *pstate, ConflictClause *confClause, parser_errposition(pstate, exprLocation((Node *) confClause)))); - Assert(confClause->conf != CONF_INSERT || - confClause->updatequery != NULL); - /* * To simplify certain aspects of its design, speculative insertion into * system catalogs is disallowed diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 1a45dd9d00..413517ac94 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -1652,7 +1652,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) * Check to see if the sublink is in an invalid place within the query. We * allow sublinks everywhere in SELECT/INSERT/UPDATE/DELETE, but generally * not in utility statements. They're also disallowed within auxiliary ON - * CONFLICT DO UPDATE commands, which we check for here. + * CONFLICT DO UPDATE commands, which we check for here. XXX */ err = NULL; switch (pstate->p_expr_kind) @@ -1720,8 +1720,10 @@ transformSubLink(ParseState *pstate, SubLink *sublink) */ } +#ifdef FIXME if (pstate->p_is_on_conflict && pstate->p_is_update) err = _("cannot use subquery in ON CONFLICT DO UPDATE"); +#endif if (err) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 6e22ab3927..4130cbff5e 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -84,13 +84,7 @@ free_parsestate(ParseState *pstate) errmsg("target lists can have at most %d entries", MaxTupleAttributeNumber))); - /* - * Don't close target relation for auxiliary ON CONFLICT DO UPDATE, since - * it is managed by parent INSERT directly - */ - if (pstate->p_target_relation != NULL && - (!pstate->p_is_on_conflict || - pstate->p_is_insert)) + if (pstate->p_target_relation != NULL) heap_close(pstate->p_target_relation, NoLock); pfree(pstate); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 09eb892bde..ae47ef373d 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -43,12 +43,6 @@ typedef struct acquireLocksOnSubLinks_context bool for_execute; /* AcquireRewriteLocks' forExecute param */ } acquireLocksOnSubLinks_context; -typedef struct excluded_replace_context -{ - int varno; /* varno of EXCLUDED.* Vars */ - int rvarno; /* replace varno */ -} excluded_replace_context; - static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context); static Query *rewriteRuleAction(Query *parsetree, @@ -58,7 +52,10 @@ static Query *rewriteRuleAction(Query *parsetree, CmdType event, bool *returning_flag); static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index); -static void rewriteTargetListIU(Query *parsetree, Relation target_relation, +static List *rewriteTargetListIU(List *targetList, + CmdType commandType, + Relation target_relation, + int result_rti, List **attrno_list); static TargetEntry *process_matched_tle(TargetEntry *src_tle, TargetEntry *prior_tle, @@ -77,10 +74,6 @@ static Query *fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown); static bool view_has_instead_trigger(Relation view, CmdType event); static Bitmapset *adjust_view_column_set(Bitmapset *cols, List *targetlist); -static Node *excluded_replace_vars(Node *expr, - excluded_replace_context * context); -static Node *excluded_replace_vars_callback(Var *var, - replace_rte_variables_context *context); /* @@ -689,11 +682,13 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index) * order of the original tlist's non-junk entries. This is needed for * processing VALUES RTEs. */ -static void -rewriteTargetListIU(Query *parsetree, Relation target_relation, +static List* +rewriteTargetListIU(List *targetList, + CmdType commandType, + Relation target_relation, + int result_rti, List **attrno_list) { - CmdType commandType = parsetree->commandType; TargetEntry **new_tles; List *new_tlist = NIL; List *junk_tlist = NIL; @@ -719,7 +714,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation, new_tles = (TargetEntry **) palloc0(numattrs * sizeof(TargetEntry *)); next_junk_attrno = numattrs + 1; - foreach(temp, parsetree->targetList) + foreach(temp, targetList) { TargetEntry *old_tle = (TargetEntry *) lfirst(temp); @@ -837,7 +832,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation, { Node *new_expr; - new_expr = (Node *) makeVar(parsetree->resultRelation, + new_expr = (Node *) makeVar(result_rti, attrno, att_tup->atttypid, att_tup->atttypmod, @@ -856,7 +851,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation, pfree(new_tles); - parsetree->targetList = list_concat(new_tlist, junk_tlist); + return list_concat(new_tlist, junk_tlist); } @@ -3139,67 +3134,41 @@ RewriteQuery(Query *parsetree, List *rewrite_events) List *attrnos; /* Process the main targetlist ... */ - rewriteTargetListIU(parsetree, rt_entry_relation, &attrnos); + parsetree->targetList = rewriteTargetListIU(parsetree->targetList, + parsetree->commandType, + rt_entry_relation, + parsetree->resultRelation, + &attrnos); /* ... and the VALUES expression lists */ rewriteValuesRTE(values_rte, rt_entry_relation, attrnos); } else { /* Process just the main targetlist */ - rewriteTargetListIU(parsetree, rt_entry_relation, NULL); + parsetree->targetList = rewriteTargetListIU(parsetree->targetList, + parsetree->commandType, + rt_entry_relation, + parsetree->resultRelation, + NULL); } if (parsetree->conf == CONF_INSERT) { - Query *qry; - excluded_replace_context context; - - /* - * While user-defined rules will never be applied in the - * auxiliary update query, normalization of tlist is still - * required - */ - qry = (Query *) parsetree->onConflict; - rewriteTargetListIU(qry, rt_entry_relation, NULL); - - /* - * Replace OLD Vars (associated with the EXCLUDED.* alias) - * with first (and only) "real" relation RTE in rtable. This - * allows the implementation to treat EXCLUDED.* as an alias - * for the target relation, which is useful during parse - * analysis, while ultimately having those references - * rewritten as special ExcludedExpr references to the - * corresponding Var in the target RTE. - * - * This is necessary because while we want a join-like syntax, - * the resemblance is superficial. This allows the optimizer - * to produce a simple sequential scan based auxiliary UPDATE - * plan, sufficient only for EvalPlanQaul() execution from the - * parent (a plan involving no actual join). - * - * This is a kludge, but appears necessary, since the slot - * made available for referencing via ExcludedExpr is in fact - * the slot just excluded from insertion by speculative - * insertion (with the effects of BEFORE ROW INSERT triggers - * carried). An ad-hoc method for making the EXCLUDED.* tuple - * available within the auxiliary expression context is - * appropriate -- the parent INSERT injects it immediately - * before auxiliary expression evaluation, because that is the - * only convenient time it is available. This seems far - * preferable to teaching the ModifyTable plans to behave like - * scan nodes (with rescanning required) just for the benefit - * of ON CONFLICT DO UPDATE. - */ - context.varno = PRS2_OLD_VARNO; - context.rvarno = PRS2_NEW_VARNO; - - parsetree->onConflict = - excluded_replace_vars(parsetree->onConflict, &context); + parsetree->onConflictSet = + rewriteTargetListIU(parsetree->onConflictSet, + CMD_UPDATE, + rt_entry_relation, + parsetree->resultRelation, + NULL); } } else if (event == CMD_UPDATE) { - rewriteTargetListIU(parsetree, rt_entry_relation, NULL); + parsetree->targetList = rewriteTargetListIU(parsetree->targetList, + parsetree->commandType, + rt_entry_relation, + parsetree->resultRelation, + NULL); rewriteTargetListUD(parsetree, rt_entry, rt_entry_relation); } else if (event == CMD_DELETE) @@ -3516,53 +3485,3 @@ QueryRewrite(Query *parsetree) return results; } - -/* - * Apply pullup variable replacement throughout an expression tree - * - * Returns modified tree, with user-specified rvarno replaced with varno. - */ -static Node * -excluded_replace_vars(Node *expr, excluded_replace_context *context) -{ - /* - * Don't recurse into subqueries; they're forbidden in auxiliary ON - * CONFLICT query - */ - return replace_rte_variables(expr, - context->varno, 0, - excluded_replace_vars_callback, - (void *) context, - NULL); -} - -static Node * -excluded_replace_vars_callback(Var *var, - replace_rte_variables_context *context) -{ - ExcludedExpr *n = makeNode(ExcludedExpr); - excluded_replace_context *rcon = - (excluded_replace_context *) context->callback_arg; - - /* Replace with an enclosing ExcludedExpr */ - var->varno = rcon->rvarno; - n->arg = (Node *) var; - - /* - * Would have to adjust varlevelsup if referenced item is from higher - * query (should not happen) - */ - Assert(var->varlevelsup == 0); - - if (var->varattno < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("cannot reference system column using EXCLUDED.* alias"))); - - if (var->varattno == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("cannot reference whole-row using EXCLUDED.* alias"))); - - return (Node *) n; -} diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 06fb5987c2..8449a80eeb 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -320,6 +320,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index, *withCheckOptions = lappend(*withCheckOptions, wco); } +#ifdef FIXME /* * ON CONFLICT DO UPDATE has an RTE that is subject to both INSERT and * UPDATE RLS enforcement. Security quals on the auxiliary UPDATE seem @@ -374,6 +375,7 @@ get_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index, *withCheckOptions = lappend(*withCheckOptions, wco); } } +#endif /* FIXME */ } /* For SELECT, UPDATE, and DELETE, set the security quals */ diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 253687af8f..3fd8eeadad 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -6433,7 +6433,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_CoerceToDomainValue: case T_SetToDefault: case T_CurrentOfExpr: - case T_ExcludedExpr: /* single words: always simple */ return true; @@ -7659,26 +7658,6 @@ get_rule_expr(Node *node, deparse_context *context, } break; - case T_ExcludedExpr: - { - ExcludedExpr *excludedexpr = (ExcludedExpr *) node; - Var *variable = (Var *) excludedexpr->arg; - bool save_varprefix; - - /* - * Force parentheses because our caller probably assumed our - * Var is a simple expression. - */ - appendStringInfoChar(buf, '('); - save_varprefix = context->varprefix; - /* Ensure EXCLUDED.* prefix is always visible */ - context->varprefix = true; - get_rule_expr((Node *) variable, context, true); - context->varprefix = save_varprefix; - appendStringInfoChar(buf, ')'); - } - break; - case T_List: { char *sep; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index d7be3d3d35..759766e7b8 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -314,6 +314,7 @@ typedef struct JunkFilter * ConstraintExprs array of constraint-checking expr states * junkFilter for removing junk attributes from tuples * projectReturning for computing a RETURNING list + * FIXME * ---------------- */ typedef struct ResultRelInfo @@ -335,6 +336,9 @@ typedef struct ResultRelInfo List **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; + List *ri_onConflictSet; + ProjectionInfo *ri_onConflictSetProj; + List *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -977,16 +981,6 @@ typedef struct DomainConstraintState ExprState *check_expr; /* for CHECK, a boolean expression */ } DomainConstraintState; -/* ---------------- - * ExcludedExprState node - * ---------------- - */ -typedef struct ExcludedExprState -{ - ExprState xprstate; - ExprState *arg; /* the argument */ -} ExcludedExprState; - /* ---------------------------------------------------------------- * Executor State Trees @@ -1110,9 +1104,10 @@ typedef struct ModifyTableState List **mt_arowmarks; /* per-subplan ExecAuxRowMark lists */ ConfCmd conf; /* ON CONFLICT type */ List *arbiterIndexes; /* unique index OIDs to arbitrate taking alt path */ - PlanState *onConflict; /* associated OnConflict state */ EPQState mt_epqstate; /* for evaluating EvalPlanQual rechecks */ bool fireBSTriggers; /* do we need to fire stmt triggers? */ + TupleTableSlot *mt_outertupleslot; + TupleTableSlot *mt_fuck; } ModifyTableState; /* ---------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 0dda3ff4b1..92217f35f6 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -168,7 +168,6 @@ typedef enum NodeTag T_CoerceToDomainValue, T_SetToDefault, T_CurrentOfExpr, - T_ExcludedExpr, T_InferenceElem, T_TargetEntry, T_RangeTblRef, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index fa2864b813..5e0b7a7b97 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -133,9 +133,10 @@ typedef struct Query List *withCheckOptions; /* a list of WithCheckOption's */ ConfCmd conf; /* ON CONFLICT type */ + List *onConflictSet; /* FIXME */ + Node *onConflictSetWhere; /* FIXME */ List *arbiterElems; /* unique index arbiter list (of InferenceElem's) */ Node *arbiterWhere; /* unique index arbiter WHERE clause */ - Node *onConflict; /* ON CONFLICT Query */ List *returningList; /* return-values list (of TargetEntry) */ @@ -1056,7 +1057,8 @@ typedef struct ConflictClause NodeTag type; ConfCmd conf; /* ON CONFLICT type */ InferClause *infer; /* Optional index inference clause */ - Node *updatequery; /* Update parse stmt */ + List *targetList; /* the target list (of ResTarget) */ + Node *whereClause; /* qualifications */ int location; /* token location, or -1 if unknown */ } ConflictClause; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 33b20eb7b5..e36d92e612 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -180,11 +180,13 @@ typedef struct ModifyTable List *plans; /* plan(s) producing source data */ ConfCmd conf; /* ON CONFLICT type */ List *arbiterIndexes; /* List of ON CONFLICT arbiter index OIDs */ - Plan *onConflictPlan; /* Plan for ON CONFLICT DO UPDATE query */ List *withCheckOptionLists; /* per-target-table WCO lists */ List *returningLists; /* per-target-table RETURNING tlists */ List *fdwPrivLists; /* per-target-table FDW private data lists */ List *rowMarks; /* PlanRowMarks (non-locking only) */ + List *onConflictSets; /* per-target-table SET for ON CONFLICT clause */ + List *onConflictSetWheres;/* per-target-table WHERE for ON CONFLICT clause */ + Index excludedRel; /* Fixme */ int epqParam; /* ID of Param for EvalPlanQual re-eval */ } ModifyTable; diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 267a1089d0..91f60d61a8 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -85,7 +85,9 @@ extern ModifyTable *make_modifytable(PlannerInfo *root, Index nominalRelation, List *resultRelations, List *subplans, List *withCheckOptionLists, List *returningLists, - List *rowMarks, Plan *onConflictPlan, ConfCmd conf, + List *rowMarks, + List *onConflictSets, List *onConflictSetWheres, + ConfCmd conf, int epqParam); extern bool is_projection_capable_plan(Plan *plan); diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index 05e46c5b78..8949beb227 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -45,6 +45,13 @@ extern void expand_security_quals(PlannerInfo *root, List *tlist); */ extern List *preprocess_targetlist(PlannerInfo *root, List *tlist); +List *preprocess_targetlist_cmd(PlannerInfo *root, + List *tlist, + int command_type, + int result_relation, + List *range_table); + + extern PlanRowMark *get_plan_rowmark(List *rowmarks, Index rtindex); /* diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index cb00bda131..3103b71594 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -153,7 +153,6 @@ struct ParseState bool p_hasModifyingCTE; bool p_is_insert; bool p_is_update; - bool p_is_on_conflict; bool p_locked_from_parent; Relation p_target_relation; RangeTblEntry *p_target_rangetblentry; -- 2.39.5