summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/prep/prepjointree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/prep/prepjointree.c')
-rw-r--r--src/backend/optimizer/prep/prepjointree.c51
1 files changed, 42 insertions, 9 deletions
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 300691cc4db..4badb6ff587 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -153,9 +153,11 @@ transform_MERGE_to_join(Query *parse)
{
RangeTblEntry *joinrte;
JoinExpr *joinexpr;
+ bool have_action[3];
JoinType jointype;
int joinrti;
List *vars;
+ RangeTblRef *rtr;
if (parse->commandType != CMD_MERGE)
return;
@@ -164,11 +166,27 @@ transform_MERGE_to_join(Query *parse)
vars = NIL;
/*
- * When any WHEN NOT MATCHED THEN INSERT clauses exist, we need to use an
- * outer join so that we process all unmatched tuples from the source
- * relation. If none exist, we can use an inner join.
+ * Work out what kind of join is required. If there any WHEN NOT MATCHED
+ * BY SOURCE/TARGET actions, an outer join is required so that we process
+ * all unmatched tuples from the source and/or target relations.
+ * Otherwise, we can use an inner join.
*/
- if (parse->mergeUseOuterJoin)
+ have_action[MERGE_WHEN_MATCHED] = false;
+ have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] = false;
+ have_action[MERGE_WHEN_NOT_MATCHED_BY_TARGET] = false;
+
+ foreach_node(MergeAction, action, parse->mergeActionList)
+ {
+ if (action->commandType != CMD_NOTHING)
+ have_action[action->matchKind] = true;
+ }
+
+ if (have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] &&
+ have_action[MERGE_WHEN_NOT_MATCHED_BY_TARGET])
+ jointype = JOIN_FULL;
+ else if (have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE])
+ jointype = JOIN_LEFT;
+ else if (have_action[MERGE_WHEN_NOT_MATCHED_BY_TARGET])
jointype = JOIN_RIGHT;
else
jointype = JOIN_INNER;
@@ -203,17 +221,21 @@ transform_MERGE_to_join(Query *parse)
* regular table, this will equal parse->resultRelation, but for a
* trigger-updatable view, it will be the expanded view subquery that we
* need to pull data from.
+ *
+ * The source relation is in parse->jointree->fromlist, but any quals in
+ * parse->jointree->quals are restrictions on the target relation (if the
+ * target relation is an auto-updatable view).
*/
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = parse->mergeTargetRelation;
joinexpr = makeNode(JoinExpr);
joinexpr->jointype = jointype;
joinexpr->isNatural = false;
- joinexpr->larg = (Node *) makeNode(RangeTblRef);
- ((RangeTblRef *) joinexpr->larg)->rtindex = parse->mergeTargetRelation;
- joinexpr->rarg = linitial(parse->jointree->fromlist); /* original join */
+ joinexpr->larg = (Node *) makeFromExpr(list_make1(rtr), parse->jointree->quals);
+ joinexpr->rarg = linitial(parse->jointree->fromlist); /* source rel */
joinexpr->usingClause = NIL;
joinexpr->join_using_alias = NULL;
- /* The quals are removed from the jointree and into this specific join */
- joinexpr->quals = parse->jointree->quals;
+ joinexpr->quals = parse->mergeJoinCondition;
joinexpr->alias = NULL;
joinexpr->rtindex = joinrti;
@@ -233,6 +255,15 @@ transform_MERGE_to_join(Query *parse)
add_nulling_relids((Node *) parse->targetList,
bms_make_singleton(parse->mergeTargetRelation),
bms_make_singleton(joinrti));
+
+ /*
+ * If there are any WHEN NOT MATCHED BY SOURCE actions, the executor will
+ * use the join condition to distinguish between MATCHED and NOT MATCHED
+ * BY SOURCE cases. Otherwise, it's no longer needed, and we set it to
+ * NULL, saving cycles during planning and execution.
+ */
+ if (!have_action[MERGE_WHEN_NOT_MATCHED_BY_SOURCE])
+ parse->mergeJoinCondition = NULL;
}
/*
@@ -2173,6 +2204,8 @@ perform_pullup_replace_vars(PlannerInfo *root,
pullup_replace_vars((Node *) action->targetList, rvcontext);
}
}
+ parse->mergeJoinCondition = pullup_replace_vars(parse->mergeJoinCondition,
+ rvcontext);
replace_vars_in_jointree((Node *) parse->jointree, rvcontext);
Assert(parse->setOperations == NULL);
parse->havingQual = pullup_replace_vars(parse->havingQual, rvcontext);