aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path')
-rw-r--r--src/backend/optimizer/path/equivclass.c7
-rw-r--r--src/backend/optimizer/path/joinpath.c145
-rw-r--r--src/backend/optimizer/path/joinrels.c12
3 files changed, 69 insertions, 95 deletions
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 7dffe18c973..fd38e8bd7a9 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -889,8 +889,8 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
* combination for which an opfamily member operator exists. If we have
* choices, we prefer simple Var members (possibly with RelabelType) since
* these are (a) cheapest to compute at runtime and (b) most likely to
- * have useful statistics. Also, if enable_hashjoin is on, we prefer
- * operators that are also hashjoinable.
+ * have useful statistics. Also, prefer operators that are also
+ * hashjoinable.
*/
if (outer_members && inner_members)
{
@@ -925,8 +925,7 @@ generate_join_implied_equalities_normal(PlannerInfo *root,
(IsA(inner_em->em_expr, RelabelType) &&
IsA(((RelabelType *) inner_em->em_expr)->arg, Var)))
score++;
- if (!enable_hashjoin ||
- op_hashjoinable(eq_op,
+ if (op_hashjoinable(eq_op,
exprType((Node *) outer_em->em_expr)))
score++;
if (score > best_score)
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index bea134b3f1a..54257c05c50 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -41,7 +41,8 @@ static List *select_mergejoin_clauses(PlannerInfo *root,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
- JoinType jointype);
+ JoinType jointype,
+ bool *have_nonmergeable_clause);
/*
@@ -77,12 +78,13 @@ add_paths_to_joinrel(PlannerInfo *root,
List *restrictlist)
{
List *mergeclause_list = NIL;
+ bool have_nonmergeable_clause = false;
/*
* Find potential mergejoin clauses. We can skip this if we are not
- * interested in doing a mergejoin. However, mergejoin is currently our
- * only way of implementing full outer joins, so override mergejoin
- * disable if it's a full join.
+ * interested in doing a mergejoin. However, mergejoin may be our only
+ * way of implementing a full outer join, so override enable_mergejoin if
+ * it's a full join.
*/
if (enable_mergejoin || jointype == JOIN_FULL)
mergeclause_list = select_mergejoin_clauses(root,
@@ -90,22 +92,27 @@ add_paths_to_joinrel(PlannerInfo *root,
outerrel,
innerrel,
restrictlist,
- jointype);
+ jointype,
+ &have_nonmergeable_clause);
/*
* 1. Consider mergejoin paths where both relations must be explicitly
- * sorted.
+ * sorted. Skip this if we can't mergejoin.
*/
- sort_inner_and_outer(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list, jointype, sjinfo);
+ if (!have_nonmergeable_clause)
+ sort_inner_and_outer(root, joinrel, outerrel, innerrel,
+ restrictlist, mergeclause_list, jointype, sjinfo);
/*
* 2. Consider paths where the outer relation need not be explicitly
* sorted. This includes both nestloops and mergejoins where the outer
- * path is already ordered.
+ * path is already ordered. Again, skip this if we can't mergejoin.
+ * (That's okay because we know that nestloop can't handle right/full
+ * joins at all, so it wouldn't work in those cases either.)
*/
- match_unsorted_outer(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list, jointype, sjinfo);
+ if (!have_nonmergeable_clause)
+ match_unsorted_outer(root, joinrel, outerrel, innerrel,
+ restrictlist, mergeclause_list, jointype, sjinfo);
#ifdef NOT_USED
@@ -120,15 +127,17 @@ add_paths_to_joinrel(PlannerInfo *root,
* those made by match_unsorted_outer when add_paths_to_joinrel() is
* invoked with the two rels given in the other order.
*/
- match_unsorted_inner(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list, jointype, sjinfo);
+ if (!have_nonmergeable_clause)
+ match_unsorted_inner(root, joinrel, outerrel, innerrel,
+ restrictlist, mergeclause_list, jointype, sjinfo);
#endif
/*
* 4. Consider paths where both outer and inner relations must be hashed
- * before being joined.
+ * before being joined. As above, disregard enable_hashjoin for full
+ * joins, because there may be no other alternative.
*/
- if (enable_hashjoin)
+ if (enable_hashjoin || jointype == JOIN_FULL)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
restrictlist, jointype, sjinfo);
}
@@ -189,38 +198,12 @@ sort_inner_and_outer(PlannerInfo *root,
JoinType jointype,
SpecialJoinInfo *sjinfo)
{
- bool useallclauses;
Path *outer_path;
Path *inner_path;
List *all_pathkeys;
ListCell *l;
/*
- * If we are doing a right or full join, we must use *all* the
- * mergeclauses as join clauses, else we will not have a valid plan.
- */
- switch (jointype)
- {
- case JOIN_INNER:
- case JOIN_LEFT:
- case JOIN_SEMI:
- case JOIN_ANTI:
- case JOIN_UNIQUE_OUTER:
- case JOIN_UNIQUE_INNER:
- useallclauses = false;
- break;
- case JOIN_RIGHT:
- case JOIN_FULL:
- useallclauses = true;
- break;
- default:
- elog(ERROR, "unrecognized join type: %d",
- (int) jointype);
- useallclauses = false; /* keep compiler quiet */
- break;
- }
-
- /*
* We only consider the cheapest-total-cost input paths, since we are
* assuming here that a sort is required. We will consider
* cheapest-startup-cost input paths later, and only if they don't need a
@@ -390,9 +373,9 @@ match_unsorted_outer(PlannerInfo *root,
/*
* Nestloop only supports inner, left, semi, and anti joins. Also, if we
- * are doing a right or full join, we must use *all* the mergeclauses as
- * join clauses, else we will not have a valid plan. (Although these two
- * flags are currently inverses, keep them separate for clarity and
+ * are doing a right or full mergejoin, we must use *all* the mergeclauses
+ * as join clauses, else we will not have a valid plan. (Although these
+ * two flags are currently inverses, keep them separate for clarity and
* possible future changes.)
*/
switch (jointype)
@@ -574,8 +557,8 @@ match_unsorted_outer(PlannerInfo *root,
* Special corner case: for "x FULL JOIN y ON true", there will be no
* join clauses at all. Ordinarily we'd generate a clauseless
* nestloop path, but since mergejoin is our only join type that
- * supports FULL JOIN, it's necessary to generate a clauseless
- * mergejoin path instead.
+ * supports FULL JOIN without any join clauses, it's necessary to
+ * generate a clauseless mergejoin path instead.
*/
if (mergeclauses == NIL)
{
@@ -781,30 +764,11 @@ hash_inner_and_outer(PlannerInfo *root,
JoinType jointype,
SpecialJoinInfo *sjinfo)
{
- bool isouterjoin;
+ bool isouterjoin = IS_OUTER_JOIN(jointype);
List *hashclauses;
ListCell *l;
/*
- * Hashjoin only supports inner, left, semi, and anti joins.
- */
- switch (jointype)
- {
- case JOIN_INNER:
- case JOIN_SEMI:
- case JOIN_UNIQUE_OUTER:
- case JOIN_UNIQUE_INNER:
- isouterjoin = false;
- break;
- case JOIN_LEFT:
- case JOIN_ANTI:
- isouterjoin = true;
- break;
- default:
- return;
- }
-
- /*
* We need to build only one hashpath for any given pair of outer and
* inner relations; all of the hashable clauses will be used as keys.
*
@@ -963,6 +927,11 @@ best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
* Select mergejoin clauses that are usable for a particular join.
* Returns a list of RestrictInfo nodes for those clauses.
*
+ * *have_nonmergeable_clause is set TRUE if this is a right/full join and
+ * there are nonmergejoinable join clauses. The executor's mergejoin
+ * machinery cannot handle such cases, so we have to avoid generating a
+ * mergejoin plan.
+ *
* We also mark each selected RestrictInfo to show which side is currently
* being considered as outer. These are transient markings that are only
* good for the duration of the current add_paths_to_joinrel() call!
@@ -977,13 +946,15 @@ select_mergejoin_clauses(PlannerInfo *root,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
- JoinType jointype)
+ JoinType jointype,
+ bool *have_nonmergeable_clause)
{
List *result_list = NIL;
bool isouterjoin = IS_OUTER_JOIN(jointype);
- bool have_nonmergeable_joinclause = false;
ListCell *l;
+ *have_nonmergeable_clause = false;
+
foreach(l, restrictlist)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
@@ -991,7 +962,7 @@ select_mergejoin_clauses(PlannerInfo *root,
/*
* If processing an outer join, only use its own join clauses in the
* merge. For inner joins we can use pushed-down clauses too. (Note:
- * we don't set have_nonmergeable_joinclause here because pushed-down
+ * we don't set have_nonmergeable_clause here because pushed-down
* clauses will become otherquals not joinquals.)
*/
if (isouterjoin && restrictinfo->is_pushed_down)
@@ -1008,7 +979,7 @@ select_mergejoin_clauses(PlannerInfo *root,
* FALSE.)
*/
if (!restrictinfo->clause || !IsA(restrictinfo->clause, Const))
- have_nonmergeable_joinclause = true;
+ *have_nonmergeable_clause = true;
continue; /* not mergejoinable */
}
@@ -1017,7 +988,7 @@ select_mergejoin_clauses(PlannerInfo *root,
*/
if (!clause_sides_match_join(restrictinfo, outerrel, innerrel))
{
- have_nonmergeable_joinclause = true;
+ *have_nonmergeable_clause = true;
continue; /* no good for these input relations */
}
@@ -1046,7 +1017,7 @@ select_mergejoin_clauses(PlannerInfo *root,
if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) ||
EC_MUST_BE_REDUNDANT(restrictinfo->right_ec))
{
- have_nonmergeable_joinclause = true;
+ *have_nonmergeable_clause = true;
continue; /* can't handle redundant eclasses */
}
@@ -1054,27 +1025,19 @@ select_mergejoin_clauses(PlannerInfo *root,
}
/*
- * If it is a right/full join then *all* the explicit join clauses must be
- * mergejoinable, else the executor will fail. If we are asked for a right
- * join then just return NIL to indicate no mergejoin is possible (we can
- * handle it as a left join instead). If we are asked for a full join then
- * emit an error, because there is no fallback.
+ * If it is not a right/full join then we don't need to insist on all the
+ * joinclauses being mergejoinable, so reset the flag. This simplifies
+ * the logic in add_paths_to_joinrel.
*/
- if (have_nonmergeable_joinclause)
+ switch (jointype)
{
- switch (jointype)
- {
- case JOIN_RIGHT:
- return NIL; /* not mergejoinable */
- case JOIN_FULL:
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("FULL JOIN is only supported with merge-joinable join conditions")));
- break;
- default:
- /* otherwise, it's OK to have nonmergeable join quals */
- break;
- }
+ case JOIN_RIGHT:
+ case JOIN_FULL:
+ break;
+ default:
+ /* otherwise, it's OK to have nonmergeable join quals */
+ *have_nonmergeable_clause = false;
+ break;
}
return result_list;
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 197c49d774c..605a32d52d6 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -658,6 +658,18 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_FULL, sjinfo,
restrictlist);
+
+ /*
+ * If there are join quals that aren't mergeable or hashable, we
+ * may not be able to build any valid plan. Complain here so that
+ * we can give a somewhat-useful error message. (Since we have no
+ * flexibility of planning for a full join, there's no chance of
+ * succeeding later with another pair of input rels.)
+ */
+ if (joinrel->pathlist == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("FULL JOIN is only supported with merge-joinable or hash-joinable join conditions")));
break;
case JOIN_SEMI: