diff options
Diffstat (limited to 'src/backend/optimizer/prep/prepjointree.c')
-rw-r--r-- | src/backend/optimizer/prep/prepjointree.c | 51 |
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); |