aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/joinpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path/joinpath.c')
-rw-r--r--src/backend/optimizer/path/joinpath.c235
1 files changed, 182 insertions, 53 deletions
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index c2ca38490e3..367e1ac9767 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.55 2000/05/30 00:49:47 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.56 2000/09/12 21:06:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,27 +25,32 @@
#include "utils/lsyscache.h"
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
- RelOptInfo *outerrel, RelOptInfo *innerrel,
- List *restrictlist, List *mergeclause_list);
+ RelOptInfo *outerrel, RelOptInfo *innerrel,
+ List *restrictlist, List *mergeclause_list,
+ JoinType jointype);
static void match_unsorted_outer(Query *root, RelOptInfo *joinrel,
- RelOptInfo *outerrel, RelOptInfo *innerrel,
- List *restrictlist, List *mergeclause_list);
+ RelOptInfo *outerrel, RelOptInfo *innerrel,
+ List *restrictlist, List *mergeclause_list,
+ JoinType jointype);
#ifdef NOT_USED
static void match_unsorted_inner(Query *root, RelOptInfo *joinrel,
- RelOptInfo *outerrel, RelOptInfo *innerrel,
- List *restrictlist, List *mergeclause_list);
+ RelOptInfo *outerrel, RelOptInfo *innerrel,
+ List *restrictlist, List *mergeclause_list,
+ JoinType jointype);
#endif
static void hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
- RelOptInfo *outerrel, RelOptInfo *innerrel,
- List *restrictlist);
-static Path *best_innerjoin(List *join_paths, List *outer_relid);
+ RelOptInfo *outerrel, RelOptInfo *innerrel,
+ List *restrictlist, JoinType jointype);
+static Path *best_innerjoin(List *join_paths, List *outer_relid,
+ JoinType jointype);
static Selectivity estimate_disbursion(Query *root, Var *var);
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
- RelOptInfo *outerrel,
- RelOptInfo *innerrel,
- List *restrictlist);
+ RelOptInfo *outerrel,
+ RelOptInfo *innerrel,
+ List *restrictlist,
+ JoinType jointype);
/*
@@ -64,6 +69,7 @@ add_paths_to_joinrel(Query *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
+ JoinType jointype,
List *restrictlist)
{
List *mergeclause_list = NIL;
@@ -75,14 +81,15 @@ add_paths_to_joinrel(Query *root,
mergeclause_list = select_mergejoin_clauses(joinrel,
outerrel,
innerrel,
- restrictlist);
+ restrictlist,
+ jointype);
/*
* 1. Consider mergejoin paths where both relations must be explicitly
* sorted.
*/
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list);
+ restrictlist, mergeclause_list, jointype);
/*
* 2. Consider paths where the outer relation need not be explicitly
@@ -90,7 +97,7 @@ add_paths_to_joinrel(Query *root,
* path is already ordered.
*/
match_unsorted_outer(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list);
+ restrictlist, mergeclause_list, jointype);
#ifdef NOT_USED
@@ -107,7 +114,7 @@ add_paths_to_joinrel(Query *root,
* other order.
*/
match_unsorted_inner(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list);
+ restrictlist, mergeclause_list, jointype);
#endif
/*
@@ -116,7 +123,7 @@ add_paths_to_joinrel(Query *root,
*/
if (enable_hashjoin)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
- restrictlist);
+ restrictlist, jointype);
}
/*
@@ -131,6 +138,7 @@ add_paths_to_joinrel(Query *root,
* clauses that apply to this join
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses in this join
+ * 'jointype' is the type of join to do
*/
static void
sort_inner_and_outer(Query *root,
@@ -138,7 +146,8 @@ sort_inner_and_outer(Query *root,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
- List *mergeclause_list)
+ List *mergeclause_list,
+ JoinType jointype)
{
List *i;
@@ -187,10 +196,10 @@ sort_inner_and_outer(Query *root,
*/
outerkeys = make_pathkeys_for_mergeclauses(root,
curclause_list,
- outerrel->targetlist);
+ outerrel);
innerkeys = make_pathkeys_for_mergeclauses(root,
curclause_list,
- innerrel->targetlist);
+ innerrel);
/* Build pathkeys representing output sort order. */
merge_pathkeys = build_join_pathkeys(outerkeys,
joinrel->targetlist,
@@ -204,6 +213,7 @@ sort_inner_and_outer(Query *root,
*/
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
+ jointype,
outerrel->cheapest_total_path,
innerrel->cheapest_total_path,
restrictlist,
@@ -243,6 +253,7 @@ sort_inner_and_outer(Query *root,
* clauses that apply to this join
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses in this join
+ * 'jointype' is the type of join to do
*/
static void
match_unsorted_outer(Query *root,
@@ -250,16 +261,33 @@ match_unsorted_outer(Query *root,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
- List *mergeclause_list)
+ List *mergeclause_list,
+ JoinType jointype)
{
+ bool nestjoinOK;
Path *bestinnerjoin;
List *i;
/*
+ * Nestloop only supports inner and left joins.
+ */
+ switch (jointype)
+ {
+ case JOIN_INNER:
+ case JOIN_LEFT:
+ nestjoinOK = true;
+ break;
+ default:
+ nestjoinOK = false;
+ break;
+ }
+
+ /*
* Get the best innerjoin indexpath (if any) for this outer rel. It's
* the same for all outer paths.
*/
- bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids);
+ bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids,
+ jointype);
foreach(i, outerrel->pathlist)
{
@@ -282,31 +310,38 @@ match_unsorted_outer(Query *root,
joinrel->targetlist,
root->equi_key_list);
- /*
- * Always consider a nestloop join with this outer and cheapest-
- * total-cost inner. Consider nestloops using the cheapest-
- * startup-cost inner as well, and the best innerjoin indexpath.
- */
- add_path(joinrel, (Path *)
- create_nestloop_path(joinrel,
- outerpath,
- innerrel->cheapest_total_path,
- restrictlist,
- merge_pathkeys));
- if (innerrel->cheapest_startup_path != innerrel->cheapest_total_path)
- add_path(joinrel, (Path *)
- create_nestloop_path(joinrel,
- outerpath,
- innerrel->cheapest_startup_path,
- restrictlist,
- merge_pathkeys));
- if (bestinnerjoin != NULL)
+ if (nestjoinOK)
+ {
+ /*
+ * Always consider a nestloop join with this outer and cheapest-
+ * total-cost inner. Consider nestloops using the cheapest-
+ * startup-cost inner as well, and the best innerjoin indexpath.
+ */
add_path(joinrel, (Path *)
create_nestloop_path(joinrel,
+ jointype,
outerpath,
- bestinnerjoin,
+ innerrel->cheapest_total_path,
restrictlist,
merge_pathkeys));
+ if (innerrel->cheapest_startup_path !=
+ innerrel->cheapest_total_path)
+ add_path(joinrel, (Path *)
+ create_nestloop_path(joinrel,
+ jointype,
+ outerpath,
+ innerrel->cheapest_startup_path,
+ restrictlist,
+ merge_pathkeys));
+ if (bestinnerjoin != NULL)
+ add_path(joinrel, (Path *)
+ create_nestloop_path(joinrel,
+ jointype,
+ outerpath,
+ bestinnerjoin,
+ restrictlist,
+ merge_pathkeys));
+ }
/* Look for useful mergeclauses (if any) */
mergeclauses = find_mergeclauses_for_pathkeys(outerpath->pathkeys,
@@ -319,7 +354,7 @@ match_unsorted_outer(Query *root,
/* Compute the required ordering of the inner path */
innersortkeys = make_pathkeys_for_mergeclauses(root,
mergeclauses,
- innerrel->targetlist);
+ innerrel);
/*
* Generate a mergejoin on the basis of sorting the cheapest
@@ -328,6 +363,7 @@ match_unsorted_outer(Query *root,
*/
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
+ jointype,
outerpath,
innerrel->cheapest_total_path,
restrictlist,
@@ -373,6 +409,7 @@ match_unsorted_outer(Query *root,
newclauses = mergeclauses;
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
+ jointype,
outerpath,
innerpath,
restrictlist,
@@ -409,6 +446,7 @@ match_unsorted_outer(Query *root,
}
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
+ jointype,
outerpath,
innerpath,
restrictlist,
@@ -437,6 +475,7 @@ match_unsorted_outer(Query *root,
* clauses that apply to this join
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses in this join
+ * 'jointype' is the type of join to do
*/
static void
match_unsorted_inner(Query *root,
@@ -444,7 +483,8 @@ match_unsorted_inner(Query *root,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
- List *mergeclause_list)
+ List *mergeclause_list,
+ JoinType jointype)
{
List *i;
@@ -466,7 +506,7 @@ match_unsorted_inner(Query *root,
/* Compute the required ordering of the outer path */
outersortkeys = make_pathkeys_for_mergeclauses(root,
mergeclauses,
- outerrel->targetlist);
+ outerrel);
/*
* Generate a mergejoin on the basis of sorting the cheapest
@@ -478,6 +518,7 @@ match_unsorted_inner(Query *root,
root->equi_key_list);
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
+ jointype,
outerrel->cheapest_total_path,
innerpath,
restrictlist,
@@ -506,6 +547,7 @@ match_unsorted_inner(Query *root,
root->equi_key_list);
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
+ jointype,
totalouterpath,
innerpath,
restrictlist,
@@ -524,6 +566,7 @@ match_unsorted_inner(Query *root,
root->equi_key_list);
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
+ jointype,
startupouterpath,
innerpath,
restrictlist,
@@ -547,19 +590,37 @@ match_unsorted_inner(Query *root,
* 'innerrel' is the inner join relation
* 'restrictlist' contains all of the RestrictInfo nodes for restriction
* clauses that apply to this join
+ * 'jointype' is the type of join to do
*/
static void
hash_inner_and_outer(Query *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
- List *restrictlist)
+ List *restrictlist,
+ JoinType jointype)
{
Relids outerrelids = outerrel->relids;
Relids innerrelids = innerrel->relids;
+ bool isouterjoin;
List *i;
/*
+ * Hashjoin only supports inner and left joins.
+ */
+ switch (jointype)
+ {
+ case JOIN_INNER:
+ isouterjoin = false;
+ break;
+ case JOIN_LEFT:
+ isouterjoin = true;
+ break;
+ default:
+ return;
+ }
+
+ /*
* Scan the join's restrictinfo list to find hashjoinable clauses that
* are usable with this pair of sub-relations. Since we currently
* accept only var-op-var clauses as hashjoinable, we need only check
@@ -581,6 +642,13 @@ hash_inner_and_outer(Query *root,
if (restrictinfo->hashjoinoperator == InvalidOid)
continue; /* not hashjoinable */
+ /*
+ * If processing an outer join, only use explicit join clauses for
+ * hashing. For inner joins we need not be so picky.
+ */
+ if (isouterjoin && !restrictinfo->isjoinqual)
+ continue;
+
clause = restrictinfo->clause;
/* these must be OK, since check_hashjoinable accepted the clause */
left = get_leftop(clause);
@@ -609,6 +677,7 @@ hash_inner_and_outer(Query *root,
*/
add_path(joinrel, (Path *)
create_hashjoin_path(joinrel,
+ jointype,
outerrel->cheapest_total_path,
innerrel->cheapest_total_path,
restrictlist,
@@ -617,6 +686,7 @@ hash_inner_and_outer(Query *root,
if (outerrel->cheapest_startup_path != outerrel->cheapest_total_path)
add_path(joinrel, (Path *)
create_hashjoin_path(joinrel,
+ jointype,
outerrel->cheapest_startup_path,
innerrel->cheapest_total_path,
restrictlist,
@@ -641,26 +711,49 @@ hash_inner_and_outer(Query *root,
* usable path.
*/
static Path *
-best_innerjoin(List *join_paths, Relids outer_relids)
+best_innerjoin(List *join_paths, Relids outer_relids, JoinType jointype)
{
Path *cheapest = (Path *) NULL;
+ bool isouterjoin;
List *join_path;
+ /*
+ * Nestloop only supports inner and left joins.
+ */
+ switch (jointype)
+ {
+ case JOIN_INNER:
+ isouterjoin = false;
+ break;
+ case JOIN_LEFT:
+ isouterjoin = true;
+ break;
+ default:
+ return NULL;
+ }
+
foreach(join_path, join_paths)
{
- Path *path = (Path *) lfirst(join_path);
+ IndexPath *path = (IndexPath *) lfirst(join_path);
Assert(IsA(path, IndexPath));
/*
+ * If processing an outer join, only use explicit join clauses in the
+ * inner indexscan. For inner joins we need not be so picky.
+ */
+ if (isouterjoin && !path->alljoinquals)
+ continue;
+
+ /*
* path->joinrelids is the set of base rels that must be part of
* outer_relids in order to use this inner path, because those
* rels are used in the index join quals of this inner path.
*/
- if (is_subseti(((IndexPath *) path)->joinrelids, outer_relids) &&
+ if (is_subseti(path->joinrelids, outer_relids) &&
(cheapest == NULL ||
- compare_path_costs(path, cheapest, TOTAL_COST) < 0))
- cheapest = path;
+ compare_path_costs((Path *) path, cheapest, TOTAL_COST) < 0))
+ cheapest = (Path *) path;
}
return cheapest;
}
@@ -684,6 +777,9 @@ estimate_disbursion(Query *root, Var *var)
relid = getrelid(var->varno, root->rtable);
+ if (relid == InvalidOid)
+ return 0.1;
+
return (Selectivity) get_attdisbursion(relid, var->varattno, 0.1);
}
@@ -707,11 +803,13 @@ static List *
select_mergejoin_clauses(RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
- List *restrictlist)
+ List *restrictlist,
+ JoinType jointype)
{
List *result_list = NIL;
Relids outerrelids = outerrel->relids;
Relids innerrelids = innerrel->relids;
+ bool isouterjoin = IS_OUTER_JOIN(jointype);
List *i;
foreach(i, restrictlist)
@@ -721,6 +819,37 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
Var *left,
*right;
+ /*
+ * If processing an outer join, only use explicit join clauses in the
+ * merge. For inner joins we need not be so picky.
+ *
+ * Furthermore, 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 (isouterjoin)
+ {
+ if (!restrictinfo->isjoinqual)
+ continue;
+ switch (jointype)
+ {
+ case JOIN_RIGHT:
+ if (restrictinfo->mergejoinoperator == InvalidOid)
+ return NIL; /* not mergejoinable */
+ break;
+ case JOIN_FULL:
+ if (restrictinfo->mergejoinoperator == InvalidOid)
+ elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
+ break;
+ default:
+ /* otherwise, it's OK to have nonmergeable join quals */
+ break;
+ }
+ }
+
if (restrictinfo->mergejoinoperator == InvalidOid)
continue; /* not mergejoinable */