tmp insert_conflict_4
authorAndres Freund <[email protected]>
Wed, 29 Apr 2015 15:51:36 +0000 (17:51 +0200)
committerAndres Freund <[email protected]>
Wed, 29 Apr 2015 16:14:35 +0000 (18:14 +0200)
29 files changed:
src/backend/commands/explain.c
src/backend/executor/execQual.c
src/backend/executor/nodeModifyTable.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/preptlist.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_clause.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_node.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rowsecurity.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/plannodes.h
src/include/optimizer/planmain.h
src/include/optimizer/prep.h
src/include/parser/parse_node.h

index 39fe92eca5e7076e0fbf6180bd5de1232e669190..3166d794fecb6dc0a86234d44e3be91147ca8fbb 100644 (file)
@@ -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;
index 37a515d264aebff296ea1870af2ec3042dd033b3..69ae93807edcc45f398e05da6c5ac9203263df47 100644 (file)
@@ -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;
index 2b3d343a441c101b990ff0ff8f7b42b9514345cf..d0342649443600d7af8875869e2ce153253f868e 100644 (file)
 #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
index cd21194416cf10fb334d7e1896b2ea9e5e1c0690..0b94e38f48b8e5c091d631467d22919cfbb8df73 100644 (file)
@@ -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;
index 83abf9718115e36b31923f30b4bc8daeb262c3a8..9e020fc4d800f0540afe1d11559e5aa25238b679 100644 (file)
@@ -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;
index dbc80b4aa43a6a0651ea45e1e92017818fee6ec5..a43f098e92cc4253ea3556a34eb1bd4b82831476 100644 (file)
@@ -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;
index 00627789f8f58dfb9c69da689b2bef9c1fb7dd43..175d8ad21f552152d9d9f0a187022d942cee6b24 100644 (file)
@@ -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;
index 7aad4469c4b3548fec149cbf347a9489f48b47f4..3073cd631436947f7570a8db1fffde95ce5a5d19 100644 (file)
@@ -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))
index 24ea9f029c6fb28b75bb59d8077f64f16964a638..057c1a5762069dec4cac27ed185ab9d10e141e3d 100644 (file)
@@ -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.
index d770cad01aea2ffcfbd0d681f53ffd0e6dfd9f39..aae6282f273ecf1f4ad22955f4e1d2a1cecce8e6 100644 (file)
@@ -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.
index ce551aa2be93fdf0d78db313725e1a6be73e442d..a1120b687004f5f7e17cd4d9ac5d3be8440a7140 100644 (file)
@@ -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:
index 40bcde92a27e78822a8e865fe15b42b14f3756c6..43ae04c1dc6ed9c2bb9eda6444815c8a7a81f8db 100644 (file)
@@ -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;
 
index 50acfe40e9711019a98cb89cc99802de27fa10b8..e7efec9c603e9673ae262e97432eebb67ca9e17a 100644 (file)
@@ -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);
index 580c8467703ebca23e40090c552af5cebd68401f..2ec24a5304063ad79a467196c84676bd2f583d23 100644 (file)
@@ -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.
index 371e11a36ce4ee89a11165449606d1d6aa3c817b..ad8ca8adf5687e2bb75a48d9b17849a682e87bc2 100644 (file)
@@ -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;
 
index 08e2ef9afc7b7c7c2192c7ed4b713ef86edb6190..f5262c5c35fd01ddbc20723efe04c68296a4d6ff 100644 (file)
@@ -520,7 +520,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>   cte_list
 
 %type <list>   within_group_clause
-%type <node>   OnConflictUpdateStmt
 %type <node>   filter_clause
 %type <list>   window_clause window_definition_list opt_partition_clause
 %type <windef> 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); }
index b559b3d6a557eba0f26285991734280793bb21d2..4ebbf974df7862a9df81ccf89cabdf665d02aa75 100644 (file)
@@ -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
index 1a45dd9d00353a08573ffc04010feb09735e2672..413517ac94767df5ef2985ecdfcaedb82279d601 100644 (file)
@@ -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),
index 6e22ab3927ccb0ec2578d22c50e49aab454609d4..4130cbff5edf69a0361c02d11dede4c2972cc904 100644 (file)
@@ -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);
index 09eb892bdedd0838b3d3b26272025879610ea7a8..ae47ef373d371afd4271cf33456fd4a9994a309f 100644 (file)
@@ -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;
-}
index 06fb5987c2490a6536425ec2e52adaade5e112bf..8449a80eeb0fadb29be46737ce583338c453de63 100644 (file)
@@ -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 */
index 253687af8f68f5a563314946e74aa64aba5fac4e..3fd8eeadad4635a076a277628ba4c22d7a3bc2f0 100644 (file)
@@ -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;
index d7be3d3d358ff2e73fe9edb2a0454f308eeb0c18..759766e7b890bbb1e8be870d4075ce1eb41ae23e 100644 (file)
@@ -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;
 
 /* ----------------
index 0dda3ff4b135d58f63e749c16a4a94983dabf053..92217f35f685d03c232bc85658b25ef59c3c04dd 100644 (file)
@@ -168,7 +168,6 @@ typedef enum NodeTag
    T_CoerceToDomainValue,
    T_SetToDefault,
    T_CurrentOfExpr,
-   T_ExcludedExpr,
    T_InferenceElem,
    T_TargetEntry,
    T_RangeTblRef,
index fa2864b813f1b8cd52316fd7f3e3d979d0afaf9c..5e0b7a7b97ad7996b3f9257d690d6b676c9b2a6c 100644 (file)
@@ -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;
 
index 33b20eb7b559e977d8a035a9bdebbe8d6a2312d7..e36d92e612c62e1c847eb3c27cd404c8b0d179a8 100644 (file)
@@ -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;
 
index 267a1089d02733733f4b40a5371f55a51ca890cd..91f60d61a83329f28dd06126747919a928b57b40 100644 (file)
@@ -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);
 
index 05e46c5b7881568da1b17e36d7a946945ece1d0c..8949beb227dafd4f5567c39a127e9fcebb53e97c 100644 (file)
@@ -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);
 
 /*
index cb00bda131297446aed9cf3f51737ac7f1e8d270..3103b71594471110aacae495150f33b4cb0a36db 100644 (file)
@@ -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;