* relation target lists. Also perform opcode lookup and add
* regclass OIDs to root->glob->relationOids.
*
- * This is used in three different scenarios:
+ * This is used in four different scenarios:
* 1) a normal join clause, where all the Vars in the clause *must* be
* replaced by OUTER_VAR or INNER_VAR references. In this case
* acceptable_rel should be zero so that any failure to match a Var will be
* to-be-updated relation) alone. Correspondingly inner_itlist is to be
* EXCLUDED elements, outer_itlist = NULL and acceptable_rel the target
* relation.
+ * 4) MERGE. In this case, references to the source relation are to be
+ * replaced with INNER_VAR references, leaving Vars of the target
+ * relation (the to-be-modified relation) alone. So inner_itlist is to be
+ * the source relation elements, outer_itlist = NULL and acceptable_rel
+ * the target relation.
*
* 'clauses' is the targetlist or list of join clauses
* 'outer_itlist' is the indexed target list of the outer join relation,
root->update_colnos = extract_update_targetlist_colnos(tlist);
/*
- * For non-inherited UPDATE/DELETE, register any junk column(s) needed to
- * allow the executor to identify the rows to be updated or deleted. In
- * the inheritance case, we do nothing now, leaving this to be dealt with
- * when expand_inherited_rtentry() makes the leaf target relations. (But
- * there might not be any leaf target relations, in which case we must do
- * this in distribute_row_identity_vars().)
+ * For non-inherited UPDATE/DELETE/MERGE, register any junk column(s)
+ * needed to allow the executor to identify the rows to be updated or
+ * deleted. In the inheritance case, we do nothing now, leaving this to
+ * be dealt with when expand_inherited_rtentry() makes the leaf target
+ * relations. (But there might not be any leaf target relations, in which
+ * case we must do this in distribute_row_identity_vars().)
*/
- if ((command_type == CMD_UPDATE || command_type == CMD_DELETE) &&
+ if ((command_type == CMD_UPDATE || command_type == CMD_DELETE ||
+ command_type == CMD_MERGE) &&
!target_rte->inh)
{
/* row-identity logic expects to add stuff to processed_tlist */
}
/*
- * For MERGE we need to handle the target list for the target relation,
- * and also target list for each action (only INSERT/UPDATE matter).
+ * For MERGE we also need to handle the target list for each INSERT and
+ * UPDATE action separately. In addition, we examine the qual of each
+ * action and add any Vars there (other than those of the target rel) to
+ * the subplan targetlist.
*/
if (command_type == CMD_MERGE)
{
ListCell *l;
- /*
- * For MERGE, add any junk column(s) needed to allow the executor to
- * identify the rows to be inserted or updated.
- */
- root->processed_tlist = tlist;
- add_row_identity_columns(root, result_relation,
- target_rte, target_relation);
-
- tlist = root->processed_tlist;
-
/*
* For MERGE, handle targetlist of each MergeAction separately. Give
* the same treatment to MergeAction->targetList as we would have
foreach(l, parse->mergeActionList)
{
MergeAction *action = (MergeAction *) lfirst(l);
+ List *vars;
+ ListCell *l2;
if (action->commandType == CMD_INSERT)
action->targetList = expand_insert_targetlist(action->targetList,
else if (action->commandType == CMD_UPDATE)
action->updateColnos =
extract_update_targetlist_colnos(action->targetList);
+
+ /*
+ * Add resjunk entries for any Vars used in each action's
+ * targetlist and WHEN condition that belong to relations other
+ * than target. Note that aggregates, window functions and
+ * placeholder vars are not possible anywhere in MERGE's WHEN
+ * clauses. (PHVs may be added later, but they don't concern us
+ * here.)
+ */
+ vars = pull_var_clause((Node *)
+ list_concat_copy((List *) action->qual,
+ action->targetList),
+ 0);
+ foreach(l2, vars)
+ {
+ Var *var = (Var *) lfirst(l2);
+ TargetEntry *tle;
+
+ if (IsA(var, Var) && var->varno == result_relation)
+ continue; /* don't need it */
+
+ if (tlist_member((Expr *) var, tlist))
+ continue; /* already got it */
+
+ tle = makeTargetEntry((Expr *) var,
+ list_length(tlist) + 1,
+ NULL, true);
+ tlist = lappend(tlist, tle);
+ }
+ list_free(vars);
}
}
#include "access/sysattr.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_collate.h"
#include "parser/parsetree.h"
pstate->p_target_nsitem->p_names->aliasname),
errdetail("The name is used both as MERGE target table and data source."));
- qry->targetList = expandNSItemAttrs(pstate, nsitem, 0, false,
- exprLocation(stmt->sourceRelation));
-
+ /*
+ * There's no need for a targetlist here; it'll be set up by
+ * preprocess_targetlist later.
+ */
+ qry->targetList = NIL;
qry->rtable = pstate->p_rtable;
/*
-> Result
Output: 1, 'cte_basic val'::text
-> Hash Right Join
- Output: (0), ('merge source SubPlan'::text), m.ctid
+ Output: m.ctid, (0), ('merge source SubPlan'::text)
Hash Cond: (m.k = (0))
-> Seq Scan on public.m
Output: m.ctid, m.k
Output: (cte_init.b || ' merge update'::text)
Filter: (cte_init.a = 1)
-> Hash Right Join
- Output: (1), ('merge source InitPlan'::text), m.ctid
+ Output: m.ctid, (1), ('merge source InitPlan'::text)
Hash Cond: (m.k = (1))
-> Seq Scan on public.m
Output: m.ctid, m.k
-> CTE Scan on merge_source_cte merge_source_cte_2
Output: ((merge_source_cte_2.*)::text || ' merge insert'::text)
-> Hash Right Join
- Output: merge_source_cte.a, merge_source_cte.b, m.ctid
+ Output: m.ctid, merge_source_cte.a, merge_source_cte.b
Hash Cond: (m.k = merge_source_cte.a)
-> Seq Scan on public.m
Output: m.ctid, m.k