mirror of
https://github.com/postgres/postgres.git
synced 2025-11-13 16:22:44 +03:00
First cut at full support for OUTER JOINs. There are still a few loose
ends to clean up (see my message of same date to pghackers), but mostly it works. INITDB REQUIRED!
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.62 2000/05/31 00:28:22 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.63 2000/09/12 21:06:52 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -26,7 +26,9 @@ int geqo_rels = DEFAULT_GEQO_RELS;
|
||||
|
||||
|
||||
static void set_base_rel_pathlist(Query *root);
|
||||
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed);
|
||||
static List *build_jointree_rels(Query *root);
|
||||
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
|
||||
List *initial_rels);
|
||||
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
static void debug_print_rel(Query *root, RelOptInfo *rel);
|
||||
@@ -43,27 +45,38 @@ RelOptInfo *
|
||||
make_one_rel(Query *root)
|
||||
{
|
||||
int levels_needed;
|
||||
List *initial_rels;
|
||||
|
||||
/*
|
||||
* Set the number of join (not nesting) levels yet to be processed.
|
||||
* Count the number of top-level jointree nodes. This is the depth
|
||||
* of the dynamic-programming algorithm we must employ to consider
|
||||
* all ways of joining the top-level nodes. Currently, we build
|
||||
* JoinExpr joins in exactly the order implied by the join expression,
|
||||
* so no dynamic-programming search is needed within a JoinExpr.
|
||||
*/
|
||||
levels_needed = length(root->base_rel_list);
|
||||
levels_needed = length(root->jointree);
|
||||
|
||||
if (levels_needed <= 0)
|
||||
return NULL;
|
||||
return NULL; /* nothing to do? */
|
||||
|
||||
/*
|
||||
* Generate access paths for the base rels.
|
||||
*/
|
||||
set_base_rel_pathlist(root);
|
||||
|
||||
/*
|
||||
* Construct a list of rels corresponding to the toplevel jointree nodes.
|
||||
* This may contain both base rels and rels constructed according to
|
||||
* explicit JOIN directives.
|
||||
*/
|
||||
initial_rels = build_jointree_rels(root);
|
||||
|
||||
if (levels_needed == 1)
|
||||
{
|
||||
|
||||
/*
|
||||
* Single relation, no more processing is required.
|
||||
* Single jointree node, so we're done.
|
||||
*/
|
||||
return (RelOptInfo *) lfirst(root->base_rel_list);
|
||||
return (RelOptInfo *) lfirst(initial_rels);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -71,7 +84,7 @@ make_one_rel(Query *root)
|
||||
/*
|
||||
* Generate join tree.
|
||||
*/
|
||||
return make_one_rel_by_joins(root, levels_needed);
|
||||
return make_one_rel_by_joins(root, levels_needed, initial_rels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,20 +138,47 @@ set_base_rel_pathlist(Query *root)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* build_jointree_rels
|
||||
* Construct a RelOptInfo for each item in the query's jointree.
|
||||
*
|
||||
* At present, we handle explicit joins in the FROM clause exactly as
|
||||
* specified, with no search for other join orders. Only the cross-product
|
||||
* joins at the top level are involved in the dynamic-programming search.
|
||||
*/
|
||||
static List *
|
||||
build_jointree_rels(Query *root)
|
||||
{
|
||||
List *rels = NIL;
|
||||
List *jt;
|
||||
|
||||
foreach(jt, root->jointree)
|
||||
{
|
||||
Node *jtnode = (Node *) lfirst(jt);
|
||||
|
||||
rels = lappend(rels, make_rel_from_jointree(root, jtnode));
|
||||
}
|
||||
return rels;
|
||||
}
|
||||
|
||||
/*
|
||||
* make_one_rel_by_joins
|
||||
* Find all possible joinpaths for a query by successively finding ways
|
||||
* to join component relations into join relations.
|
||||
*
|
||||
* 'levels_needed' is the number of iterations needed, ie, the number of
|
||||
* base relations present in the query
|
||||
* independent jointree items in the query. This is > 1.
|
||||
*
|
||||
* 'initial_rels' is a list of RelOptInfo nodes for each independent
|
||||
* jointree item. These are the components to be joined together.
|
||||
*
|
||||
* Returns the final level of join relations, i.e., the relation that is
|
||||
* the result of joining all the original relations together.
|
||||
*/
|
||||
static RelOptInfo *
|
||||
make_one_rel_by_joins(Query *root, int levels_needed)
|
||||
make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
|
||||
{
|
||||
List **joinitems;
|
||||
int lev;
|
||||
RelOptInfo *rel;
|
||||
|
||||
@@ -152,34 +192,35 @@ make_one_rel_by_joins(Query *root, int levels_needed)
|
||||
|
||||
/*
|
||||
* We employ a simple "dynamic programming" algorithm: we first find
|
||||
* all ways to build joins of two base relations, then all ways to
|
||||
* build joins of three base relations (from two-base-rel joins and
|
||||
* other base rels), then four-base-rel joins, and so on until we have
|
||||
* considered all ways to join all N relations into one rel.
|
||||
* all ways to build joins of two jointree items, then all ways to
|
||||
* build joins of three items (from two-item joins and single items),
|
||||
* then four-item joins, and so on until we have considered all ways
|
||||
* to join all the items into one rel.
|
||||
*
|
||||
* joinitems[j] is a list of all the j-item rels. Initially we set
|
||||
* joinitems[1] to represent all the single-jointree-item relations.
|
||||
*/
|
||||
joinitems = (List **) palloc((levels_needed+1) * sizeof(List *));
|
||||
MemSet(joinitems, 0, (levels_needed+1) * sizeof(List *));
|
||||
|
||||
joinitems[1] = initial_rels;
|
||||
|
||||
for (lev = 2; lev <= levels_needed; lev++)
|
||||
{
|
||||
List *first_old_rel = root->join_rel_list;
|
||||
List *x;
|
||||
|
||||
/*
|
||||
* Determine all possible pairs of relations to be joined at this
|
||||
* level, and build paths for making each one from every available
|
||||
* pair of lower-level relations. Results are prepended to
|
||||
* root->join_rel_list.
|
||||
* pair of lower-level relations.
|
||||
*/
|
||||
make_rels_by_joins(root, lev);
|
||||
joinitems[lev] = make_rels_by_joins(root, lev, joinitems);
|
||||
|
||||
/*
|
||||
* The relations created at the current level will appear at the
|
||||
* front of root->join_rel_list.
|
||||
* Do cleanup work on each just-processed rel.
|
||||
*/
|
||||
foreach(x, root->join_rel_list)
|
||||
foreach(x, joinitems[lev])
|
||||
{
|
||||
if (x == first_old_rel)
|
||||
break; /* no more rels added at this level */
|
||||
|
||||
rel = (RelOptInfo *) lfirst(x);
|
||||
|
||||
#ifdef NOT_USED
|
||||
@@ -202,14 +243,12 @@ make_one_rel_by_joins(Query *root, int levels_needed)
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, the front of the join_rel_list should be the single rel
|
||||
* We should have a single rel at the final level,
|
||||
* representing the join of all the base rels.
|
||||
*/
|
||||
Assert(length(root->join_rel_list) > 0);
|
||||
rel = (RelOptInfo *) lfirst(root->join_rel_list);
|
||||
Assert(length(rel->relids) == levels_needed);
|
||||
Assert(length(root->join_rel_list) == 1 ||
|
||||
length(((RelOptInfo *) lsecond(root->join_rel_list))->relids) < levels_needed);
|
||||
Assert(length(joinitems[levels_needed]) == 1);
|
||||
rel = (RelOptInfo *) lfirst(joinitems[levels_needed]);
|
||||
Assert(length(rel->relids) == length(root->base_rel_list));
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.94 2000/08/24 03:29:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.95 2000/09/12 21:06:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1482,7 +1482,9 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
{
|
||||
List *clausegroup = lfirst(i);
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
List *indexquals;
|
||||
List *indexquals = NIL;
|
||||
bool alljoinquals = true;
|
||||
List *temp;
|
||||
|
||||
/* XXX this code ought to be merged with create_index_path? */
|
||||
|
||||
@@ -1496,7 +1498,16 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
*/
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
indexquals = get_actual_clauses(clausegroup);
|
||||
/* extract bare indexqual clauses, check whether all from JOIN/ON */
|
||||
foreach(temp, clausegroup)
|
||||
{
|
||||
RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
|
||||
|
||||
indexquals = lappend(indexquals, clause->clause);
|
||||
if (! clause->isjoinqual)
|
||||
alljoinquals = false;
|
||||
}
|
||||
|
||||
/* expand special operators to indexquals the executor can handle */
|
||||
indexquals = expand_indexqual_conditions(indexquals);
|
||||
|
||||
@@ -1514,6 +1525,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
/* joinrelids saves the rels needed on the outer side of the join */
|
||||
pathnode->joinrelids = lfirst(outerrelids_list);
|
||||
|
||||
pathnode->alljoinquals = alljoinquals;
|
||||
|
||||
/*
|
||||
* We must compute the estimated number of output rows for the
|
||||
* indexscan. This is less than rel->rows because of the
|
||||
|
||||
@@ -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)
|
||||
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,
|
||||
innerrel->cheapest_startup_path,
|
||||
restrictlist,
|
||||
merge_pathkeys));
|
||||
if (bestinnerjoin != NULL)
|
||||
add_path(joinrel, (Path *)
|
||||
create_nestloop_path(joinrel,
|
||||
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,18 +590,36 @@ 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
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.46 2000/05/30 00:49:47 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.47 2000/09/12 21:06:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -19,62 +19,52 @@
|
||||
|
||||
|
||||
static RelOptInfo *make_join_rel(Query *root, RelOptInfo *rel1,
|
||||
RelOptInfo *rel2);
|
||||
RelOptInfo *rel2, JoinType jointype);
|
||||
|
||||
|
||||
/*
|
||||
* make_rels_by_joins
|
||||
* Consider ways to produce join relations containing exactly 'level'
|
||||
* base relations. (This is one step of the dynamic-programming method
|
||||
* jointree items. (This is one step of the dynamic-programming method
|
||||
* embodied in make_one_rel_by_joins.) Join rel nodes for each feasible
|
||||
* combination of base rels are created and added to the front of the
|
||||
* query's join_rel_list. Implementation paths are created for each
|
||||
* such joinrel, too.
|
||||
* combination of lower-level rels are created and returned in a list.
|
||||
* Implementation paths are created for each such joinrel, too.
|
||||
*
|
||||
* Returns nothing, but adds entries to root->join_rel_list.
|
||||
* level: level of rels we want to make this time.
|
||||
* joinrels[j], 1 <= j < level, is a list of rels containing j items.
|
||||
*/
|
||||
void
|
||||
make_rels_by_joins(Query *root, int level)
|
||||
List *
|
||||
make_rels_by_joins(Query *root, int level, List **joinrels)
|
||||
{
|
||||
List *first_old_rel = root->join_rel_list;
|
||||
List *result_rels = NIL;
|
||||
List *new_rels;
|
||||
List *nr;
|
||||
List *r;
|
||||
int k;
|
||||
|
||||
/*
|
||||
* First, consider left-sided and right-sided plans, in which rels of
|
||||
* exactly level-1 member relations are joined against base relations.
|
||||
* We prefer to join using join clauses, but if we find a rel of
|
||||
* level-1 members that has no join clauses, we will generate
|
||||
* Cartesian-product joins against all base rels not already contained
|
||||
* in it.
|
||||
* exactly level-1 member relations are joined against initial relations.
|
||||
* We prefer to join using join clauses, but if we find a rel of level-1
|
||||
* members that has no join clauses, we will generate Cartesian-product
|
||||
* joins against all initial rels not already contained in it.
|
||||
*
|
||||
* In the first pass (level == 2), we try to join each base rel to each
|
||||
* base rel that appears later in base_rel_list. (The mirror-image
|
||||
* In the first pass (level == 2), we try to join each initial rel to each
|
||||
* initial rel that appears later in joinrels[1]. (The mirror-image
|
||||
* joins are handled automatically by make_join_rel.) In later
|
||||
* passes, we try to join rels of size level-1 from join_rel_list to
|
||||
* each base rel in base_rel_list.
|
||||
*
|
||||
* We assume that the rels already present in join_rel_list appear in
|
||||
* decreasing order of level (number of members). This should be true
|
||||
* since we always add new higher-level rels to the front of the list.
|
||||
* passes, we try to join rels of size level-1 from joinrels[level-1]
|
||||
* to each initial rel in joinrels[1].
|
||||
*/
|
||||
if (level == 2)
|
||||
r = root->base_rel_list;/* level-1 is base rels */
|
||||
else
|
||||
r = root->join_rel_list;
|
||||
for (; r != NIL; r = lnext(r))
|
||||
foreach(r, joinrels[level-1])
|
||||
{
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
int old_level = length(old_rel->relids);
|
||||
List *other_rels;
|
||||
|
||||
if (old_level != level - 1)
|
||||
break;
|
||||
|
||||
if (level == 2)
|
||||
other_rels = lnext(r); /* only consider remaining base
|
||||
other_rels = lnext(r); /* only consider remaining initial
|
||||
* rels */
|
||||
else
|
||||
other_rels = root->base_rel_list; /* consider all base rels */
|
||||
other_rels = joinrels[1]; /* consider all initial rels */
|
||||
|
||||
if (old_rel->joininfo != NIL)
|
||||
{
|
||||
@@ -87,9 +77,9 @@ make_rels_by_joins(Query *root, int level)
|
||||
* have those other rels collected into a join rel. See also
|
||||
* the last-ditch case below.
|
||||
*/
|
||||
make_rels_by_clause_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
new_rels = make_rels_by_clause_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -98,64 +88,90 @@ make_rels_by_joins(Query *root, int level)
|
||||
* Oops, we have a relation that is not joined to any other
|
||||
* relation. Cartesian product time.
|
||||
*/
|
||||
make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
new_rels = make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
}
|
||||
|
||||
/*
|
||||
* At levels above 2 we will generate the same joined relation
|
||||
* in multiple ways --- for example (a join b) join c is the same
|
||||
* RelOptInfo as (b join c) join a, though the second case will
|
||||
* add a different set of Paths to it. To avoid making extra work
|
||||
* for subsequent passes, do not enter the same RelOptInfo into our
|
||||
* output list multiple times.
|
||||
*/
|
||||
foreach(nr, new_rels)
|
||||
{
|
||||
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
|
||||
|
||||
if (!ptrMember(jrel, result_rels))
|
||||
result_rels = lcons(jrel, result_rels);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, consider "bushy plans" in which relations of k base rels are
|
||||
* joined to relations of level-k base rels, for 2 <= k <= level-2.
|
||||
* The previous loop left r pointing to the first rel of level
|
||||
* level-2.
|
||||
* Now, consider "bushy plans" in which relations of k initial rels are
|
||||
* joined to relations of level-k initial rels, for 2 <= k <= level-2.
|
||||
*
|
||||
* We only consider bushy-plan joins for pairs of rels where there is a
|
||||
* suitable join clause, in order to avoid unreasonable growth of
|
||||
* planning time.
|
||||
*/
|
||||
for (; r != NIL; r = lnext(r))
|
||||
for (k = 2; ; k++)
|
||||
{
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
int old_level = length(old_rel->relids);
|
||||
List *r2;
|
||||
int other_level = level - k;
|
||||
|
||||
/*
|
||||
* We can quit once past the halfway point (make_join_rel took
|
||||
* care of making the opposite-direction joins)
|
||||
* Since make_join_rel(x, y) handles both x,y and y,x cases,
|
||||
* we only need to go as far as the halfway point.
|
||||
*/
|
||||
if (old_level * 2 < level)
|
||||
if (k > other_level)
|
||||
break;
|
||||
|
||||
if (old_rel->joininfo == NIL)
|
||||
continue; /* we ignore clauseless joins here */
|
||||
|
||||
foreach(r2, lnext(r))
|
||||
foreach(r, joinrels[k])
|
||||
{
|
||||
RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
|
||||
int new_level = length(new_rel->relids);
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
List *other_rels;
|
||||
List *r2;
|
||||
|
||||
if (old_level + new_level > level)
|
||||
continue; /* scan down to new_rels of right size */
|
||||
if (old_level + new_level < level)
|
||||
break; /* no more new_rels of right size */
|
||||
if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
|
||||
if (old_rel->joininfo == NIL)
|
||||
continue; /* we ignore clauseless joins here */
|
||||
|
||||
if (k == other_level)
|
||||
other_rels = lnext(r); /* only consider remaining rels */
|
||||
else
|
||||
other_rels = joinrels[other_level];
|
||||
|
||||
foreach(r2, other_rels)
|
||||
{
|
||||
List *i;
|
||||
RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
|
||||
|
||||
/*
|
||||
* OK, we can build a rel of the right level from this
|
||||
* pair of rels. Do so if there is at least one usable
|
||||
* join clause.
|
||||
*/
|
||||
foreach(i, old_rel->joininfo)
|
||||
if (nonoverlap_setsi(old_rel->relids, new_rel->relids))
|
||||
{
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
||||
List *i;
|
||||
|
||||
if (is_subseti(joininfo->unjoined_relids, new_rel->relids))
|
||||
/*
|
||||
* OK, we can build a rel of the right level from this
|
||||
* pair of rels. Do so if there is at least one usable
|
||||
* join clause.
|
||||
*/
|
||||
foreach(i, old_rel->joininfo)
|
||||
{
|
||||
make_join_rel(root, old_rel, new_rel);
|
||||
break;
|
||||
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
|
||||
|
||||
if (is_subseti(joininfo->unjoined_relids,
|
||||
new_rel->relids))
|
||||
{
|
||||
RelOptInfo *jrel;
|
||||
|
||||
jrel = make_join_rel(root, old_rel, new_rel,
|
||||
JOIN_INNER);
|
||||
/* Avoid making duplicate entries ... */
|
||||
if (!ptrMember(jrel, result_rels))
|
||||
result_rels = lcons(jrel, result_rels);
|
||||
break; /* need not consider more joininfos */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,39 +190,41 @@ make_rels_by_joins(Query *root, int level)
|
||||
* no choice but to make cartesian joins. We consider only left-sided
|
||||
* and right-sided cartesian joins in this case (no bushy).
|
||||
*/
|
||||
if (root->join_rel_list == first_old_rel)
|
||||
if (result_rels == NIL)
|
||||
{
|
||||
/* This loop is just like the first one, except we always call
|
||||
* make_rels_by_clauseless_joins().
|
||||
*/
|
||||
if (level == 2)
|
||||
r = root->base_rel_list; /* level-1 is base rels */
|
||||
else
|
||||
r = root->join_rel_list;
|
||||
for (; r != NIL; r = lnext(r))
|
||||
foreach(r, joinrels[level-1])
|
||||
{
|
||||
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
|
||||
int old_level = length(old_rel->relids);
|
||||
List *other_rels;
|
||||
|
||||
if (old_level != level - 1)
|
||||
break;
|
||||
|
||||
if (level == 2)
|
||||
other_rels = lnext(r); /* only consider remaining base
|
||||
other_rels = lnext(r); /* only consider remaining initial
|
||||
* rels */
|
||||
else
|
||||
other_rels = root->base_rel_list; /* consider all base rels */
|
||||
other_rels = joinrels[1]; /* consider all initial rels */
|
||||
|
||||
make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
new_rels = make_rels_by_clauseless_joins(root,
|
||||
old_rel,
|
||||
other_rels);
|
||||
|
||||
foreach(nr, new_rels)
|
||||
{
|
||||
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
|
||||
|
||||
if (!ptrMember(jrel, result_rels))
|
||||
result_rels = lcons(jrel, result_rels);
|
||||
}
|
||||
}
|
||||
|
||||
if (root->join_rel_list == first_old_rel)
|
||||
if (result_rels == NIL)
|
||||
elog(ERROR, "make_rels_by_joins: failed to build any %d-way joins",
|
||||
level);
|
||||
}
|
||||
|
||||
return result_rels;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -214,28 +232,23 @@ make_rels_by_joins(Query *root, int level)
|
||||
* Build joins between the given relation 'old_rel' and other relations
|
||||
* that are mentioned within old_rel's joininfo nodes (i.e., relations
|
||||
* that participate in join clauses that 'old_rel' also participates in).
|
||||
* The join rel nodes are added to root->join_rel_list.
|
||||
* The join rel nodes are returned in a list.
|
||||
*
|
||||
* 'old_rel' is the relation entry for the relation to be joined
|
||||
* 'other_rels': other rels to be considered for joining
|
||||
*
|
||||
* Currently, this is only used with base rels in other_rels, but it would
|
||||
* work for joining to joinrels too, if the caller ensures there is no
|
||||
* Currently, this is only used with initial rels in other_rels, but it
|
||||
* will work for joining to joinrels too, if the caller ensures there is no
|
||||
* membership overlap between old_rel and the rels in other_rels. (We need
|
||||
* no extra test for overlap for base rels, since the is_subset test can
|
||||
* no extra test for overlap for initial rels, since the is_subset test can
|
||||
* only succeed when other_rel is not already part of old_rel.)
|
||||
*
|
||||
* Returns NULL if no suitable joins were found, else the last suitable
|
||||
* joinrel processed. (The only caller who checks the return value is
|
||||
* geqo_eval.c, and it sets things up so there can be no more than one
|
||||
* "suitable" joinrel; so we don't bother with returning a list.)
|
||||
*/
|
||||
RelOptInfo *
|
||||
List *
|
||||
make_rels_by_clause_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels)
|
||||
{
|
||||
RelOptInfo *result = NULL;
|
||||
List *result = NIL;
|
||||
List *i,
|
||||
*j;
|
||||
|
||||
@@ -249,7 +262,9 @@ make_rels_by_clause_joins(Query *root,
|
||||
RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);
|
||||
|
||||
if (is_subseti(unjoined_relids, other_rel->relids))
|
||||
result = make_join_rel(root, old_rel, other_rel);
|
||||
result = lcons(make_join_rel(root, old_rel, other_rel,
|
||||
JOIN_INNER),
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,24 +276,20 @@ make_rels_by_clause_joins(Query *root,
|
||||
* Given a relation 'old_rel' and a list of other relations
|
||||
* 'other_rels', create a join relation between 'old_rel' and each
|
||||
* member of 'other_rels' that isn't already included in 'old_rel'.
|
||||
* The join rel nodes are returned in a list.
|
||||
*
|
||||
* 'old_rel' is the relation entry for the relation to be joined
|
||||
* 'other_rels': other rels to be considered for joining
|
||||
*
|
||||
* Currently, this is only used with base rels in other_rels, but it would
|
||||
* Currently, this is only used with initial rels in other_rels, but it would
|
||||
* work for joining to joinrels too.
|
||||
*
|
||||
* Returns NULL if no suitable joins were found, else the last suitable
|
||||
* joinrel processed. (The only caller who checks the return value is
|
||||
* geqo_eval.c, and it sets things up so there can be no more than one
|
||||
* "suitable" joinrel; so we don't bother with returning a list.)
|
||||
*/
|
||||
RelOptInfo *
|
||||
List *
|
||||
make_rels_by_clauseless_joins(Query *root,
|
||||
RelOptInfo *old_rel,
|
||||
List *other_rels)
|
||||
{
|
||||
RelOptInfo *result = NULL;
|
||||
List *result = NIL;
|
||||
List *i;
|
||||
|
||||
foreach(i, other_rels)
|
||||
@@ -286,13 +297,61 @@ make_rels_by_clauseless_joins(Query *root,
|
||||
RelOptInfo *other_rel = (RelOptInfo *) lfirst(i);
|
||||
|
||||
if (nonoverlap_setsi(other_rel->relids, old_rel->relids))
|
||||
result = make_join_rel(root, old_rel, other_rel);
|
||||
result = lcons(make_join_rel(root, old_rel, other_rel,
|
||||
JOIN_INNER),
|
||||
result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_rel_from_jointree
|
||||
* Find or build a RelOptInfojoin rel representing a specific
|
||||
* jointree item. For JoinExprs, we only consider the construction
|
||||
* path that corresponds exactly to what the user wrote.
|
||||
*/
|
||||
RelOptInfo *
|
||||
make_rel_from_jointree(Query *root, Node *jtnode)
|
||||
{
|
||||
if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) jtnode)->rtindex;
|
||||
|
||||
return get_base_rel(root, varno);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
RelOptInfo *rel,
|
||||
*lrel,
|
||||
*rrel;
|
||||
|
||||
/* Recurse */
|
||||
lrel = make_rel_from_jointree(root, j->larg);
|
||||
rrel = make_rel_from_jointree(root, j->rarg);
|
||||
|
||||
/* Make this join rel */
|
||||
rel = make_join_rel(root, lrel, rrel, j->jointype);
|
||||
|
||||
/*
|
||||
* Since we are only going to consider this one way to do it,
|
||||
* we're done generating Paths for this joinrel and can now select
|
||||
* the cheapest. In fact we *must* do so now, since next level up
|
||||
* will need it!
|
||||
*/
|
||||
set_cheapest(rel);
|
||||
|
||||
return rel;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "make_rel_from_jointree: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
return NULL; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_join_rel
|
||||
* Find or create a join RelOptInfo that represents the join of
|
||||
@@ -300,10 +359,10 @@ make_rels_by_clauseless_joins(Query *root,
|
||||
* created with the two rels as outer and inner rel.
|
||||
* (The join rel may already contain paths generated from other
|
||||
* pairs of rels that add up to the same set of base rels.)
|
||||
* The join rel is stored in the query's join_rel_list.
|
||||
*/
|
||||
static RelOptInfo *
|
||||
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2)
|
||||
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
|
||||
JoinType jointype)
|
||||
{
|
||||
RelOptInfo *joinrel;
|
||||
List *restrictlist;
|
||||
@@ -315,10 +374,39 @@ make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2)
|
||||
joinrel = get_join_rel(root, rel1, rel2, &restrictlist);
|
||||
|
||||
/*
|
||||
* We consider paths using each rel as both outer and inner.
|
||||
* Consider paths using each rel as both outer and inner.
|
||||
*/
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, restrictlist);
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER,
|
||||
restrictlist);
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT,
|
||||
restrictlist);
|
||||
break;
|
||||
case JOIN_FULL:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL,
|
||||
restrictlist);
|
||||
break;
|
||||
case JOIN_RIGHT:
|
||||
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_RIGHT,
|
||||
restrictlist);
|
||||
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT,
|
||||
restrictlist);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "make_join_rel: unsupported join type %d",
|
||||
(int) jointype);
|
||||
break;
|
||||
}
|
||||
|
||||
return joinrel;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.40 2000/05/30 00:49:47 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.41 2000/09/12 21:06:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -101,6 +101,7 @@ create_or_index_paths(Query *root,
|
||||
|
||||
/* This isn't a nestloop innerjoin, so: */
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->alljoinquals = false;
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
best_or_subclause_indices(root,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.24 2000/08/08 15:41:31 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.25 2000/09/12 21:06:53 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -694,8 +694,8 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
||||
*
|
||||
* 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
|
||||
* that will be used in a merge join.
|
||||
* 'tlist' is a relation target list for either the inner or outer
|
||||
* side of the proposed join rel. (Not actually needed anymore)
|
||||
* 'rel' is the relation the pathkeys will apply to (ie, either the inner
|
||||
* or outer side of the proposed join rel).
|
||||
*
|
||||
* Returns a pathkeys list that can be applied to the indicated relation.
|
||||
*
|
||||
@@ -706,7 +706,7 @@ find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
|
||||
List *
|
||||
make_pathkeys_for_mergeclauses(Query *root,
|
||||
List *mergeclauses,
|
||||
List *tlist)
|
||||
RelOptInfo *rel)
|
||||
{
|
||||
List *pathkeys = NIL;
|
||||
List *i;
|
||||
@@ -722,30 +722,37 @@ make_pathkeys_for_mergeclauses(Query *root,
|
||||
Assert(restrictinfo->mergejoinoperator != InvalidOid);
|
||||
|
||||
/*
|
||||
* Find the key and sortop needed for this mergeclause.
|
||||
*
|
||||
* Both sides of the mergeclause should appear in one of the query's
|
||||
* pathkey equivalence classes, so it doesn't matter which one we
|
||||
* use here.
|
||||
* Which key and sortop is needed for this relation?
|
||||
*/
|
||||
key = (Node *) get_leftop(restrictinfo->clause);
|
||||
sortop = restrictinfo->left_sortop;
|
||||
if (!IsA(key, Var) ||
|
||||
!intMember(((Var *) key)->varno, rel->relids))
|
||||
{
|
||||
key = (Node *) get_rightop(restrictinfo->clause);
|
||||
sortop = restrictinfo->right_sortop;
|
||||
if (!IsA(key, Var) ||
|
||||
!intMember(((Var *) key)->varno, rel->relids))
|
||||
elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use");
|
||||
}
|
||||
|
||||
/*
|
||||
* Find pathkey sublist for this sort item. We expect to find the
|
||||
* canonical set including the mergeclause's left and right sides;
|
||||
* if we get back just the one item, something is rotten.
|
||||
* Find or create canonical pathkey sublist for this sort item.
|
||||
*/
|
||||
item = makePathKeyItem(key, sortop);
|
||||
pathkey = make_canonical_pathkey(root, item);
|
||||
Assert(length(pathkey) > 1);
|
||||
|
||||
/*
|
||||
* Since the item we just made is not in the returned canonical
|
||||
* set, we can free it --- this saves a useful amount of storage
|
||||
* in a big join tree.
|
||||
* Most of the time we will get back a canonical pathkey set
|
||||
* including both the mergeclause's left and right sides (the only
|
||||
* case where we don't is if the mergeclause appeared in an OUTER
|
||||
* JOIN, which causes us not to generate an equijoin set from it).
|
||||
* Therefore, most of the time the item we just made is not part
|
||||
* of the returned structure, and we can free it. This check
|
||||
* saves a useful amount of storage in a big join tree.
|
||||
*/
|
||||
pfree(item);
|
||||
if (item != (PathKeyItem *) lfirst(pathkey))
|
||||
pfree(item);
|
||||
|
||||
pathkeys = lappend(pathkeys, pathkey);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user