mirror of
https://github.com/postgres/postgres.git
synced 2025-06-27 23:21:58 +03:00
Improve my initial, rather hacky implementation of joins to append
relations: fix the executor so that we can have an Append plan on the inside of a nestloop and still pass down outer index keys to index scans within the Append, then generate such plans as if they were regular inner indexscans. This avoids the need to evaluate the outer relation multiple times.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.101 2006/02/04 23:03:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/joinpath.c,v 1.102 2006/02/05 02:59:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -35,9 +35,8 @@ static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
List *restrictlist, JoinType jointype);
|
||||
static void join_before_append(PlannerInfo *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel,
|
||||
JoinType jointype);
|
||||
static Path *best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
|
||||
Relids outer_relids, JoinType jointype);
|
||||
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
@ -118,13 +117,6 @@ add_paths_to_joinrel(PlannerInfo *root,
|
||||
if (enable_hashjoin)
|
||||
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
|
||||
restrictlist, jointype);
|
||||
|
||||
/*
|
||||
* 5. If the inner relation is an append relation, consider joining
|
||||
* the outer rel to each append member and then appending the results.
|
||||
*/
|
||||
if (innerrel->cheapest_total_path->pathtype == T_Append)
|
||||
join_before_append(root, joinrel, outerrel, innerrel, jointype);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -405,8 +397,17 @@ match_unsorted_outer(PlannerInfo *root,
|
||||
* Get the best innerjoin indexpath (if any) for this outer rel. It's
|
||||
* the same for all outer paths.
|
||||
*/
|
||||
bestinnerjoin = best_inner_indexscan(root, innerrel,
|
||||
outerrel->relids, jointype);
|
||||
if (innerrel->reloptkind != RELOPT_JOINREL)
|
||||
{
|
||||
if (IsA(inner_cheapest_total, AppendPath))
|
||||
bestinnerjoin = best_appendrel_indexscan(root, innerrel,
|
||||
outerrel->relids,
|
||||
jointype);
|
||||
else if (innerrel->rtekind == RTE_RELATION)
|
||||
bestinnerjoin = best_inner_indexscan(root, innerrel,
|
||||
outerrel->relids,
|
||||
jointype);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(l, outerrel->pathlist)
|
||||
@ -788,75 +789,27 @@ hash_inner_and_outer(PlannerInfo *root,
|
||||
}
|
||||
|
||||
/*
|
||||
* join_before_append
|
||||
* Creates possible join paths for processing a single join relation
|
||||
* 'joinrel' when the inner input is an append relation.
|
||||
*
|
||||
* The idea here is to swap the order of the APPEND and JOIN operators.
|
||||
* This is only really helpful if it allows us to reduce the cost of
|
||||
* scanning the members of the append relation, and so we only consider
|
||||
* plans involving nestloops with inner indexscans. Also, since the APPEND
|
||||
* will certainly yield an unsorted result, there's no point in considering
|
||||
* any but the cheapest-total outer path.
|
||||
*
|
||||
* XXX this is a bit of a kluge, because the resulting plan has to evaluate
|
||||
* the outer relation multiple times. Would be better to allow
|
||||
* best_inner_indexscan to generate an AppendPath and not have this routine
|
||||
* at all. But we can't do that without some executor changes (need a way
|
||||
* to pass outer keys down through Append). FIXME later.
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'jointype' is the type of join to do
|
||||
* best_appendrel_indexscan
|
||||
* Finds the best available set of inner indexscans for a nestloop join
|
||||
* with the given append relation on the inside and the given outer_relids
|
||||
* outside. Returns an AppendPath comprising the best inner scans, or
|
||||
* NULL if there are no possible inner indexscans.
|
||||
*/
|
||||
static void
|
||||
join_before_append(PlannerInfo *root,
|
||||
RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel,
|
||||
RelOptInfo *innerrel,
|
||||
JoinType jointype)
|
||||
static Path *
|
||||
best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
|
||||
Relids outer_relids, JoinType jointype)
|
||||
{
|
||||
Path *outer_cheapest_total = outerrel->cheapest_total_path;
|
||||
int parentRTindex = innerrel->relid;
|
||||
int parentRTindex = rel->relid;
|
||||
List *append_paths = NIL;
|
||||
bool found_indexscan = false;
|
||||
ListCell *l;
|
||||
|
||||
/*
|
||||
* Swapping JOIN with APPEND only works for inner joins, not outer joins.
|
||||
* However, we can also handle a unique-ified outer path.
|
||||
*/
|
||||
switch (jointype)
|
||||
{
|
||||
case JOIN_INNER:
|
||||
break;
|
||||
case JOIN_UNIQUE_OUTER:
|
||||
outer_cheapest_total = (Path *)
|
||||
create_unique_path(root, outerrel, outer_cheapest_total);
|
||||
break;
|
||||
case JOIN_LEFT:
|
||||
case JOIN_RIGHT:
|
||||
case JOIN_FULL:
|
||||
case JOIN_IN:
|
||||
case JOIN_UNIQUE_INNER:
|
||||
return; /* can't join this way */
|
||||
default:
|
||||
elog(ERROR, "unrecognized join type: %d",
|
||||
(int) jointype);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate suitable access paths for each member relation.
|
||||
*/
|
||||
foreach(l, root->append_rel_list)
|
||||
{
|
||||
AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
|
||||
int childRTindex;
|
||||
RelOptInfo *childrel;
|
||||
Path *bestinnerjoin;
|
||||
RelOptInfo *this_joinrel;
|
||||
List *this_restrictlist;
|
||||
|
||||
/* append_rel_list contains all append rels; ignore others */
|
||||
if (appinfo->parent_relid != parentRTindex)
|
||||
@ -876,42 +829,30 @@ join_before_append(PlannerInfo *root,
|
||||
continue; /* OK, we can ignore it */
|
||||
|
||||
/*
|
||||
* Get the best innerjoin indexpath (if any) for this outer rel.
|
||||
* Get the best innerjoin indexpath (if any) for this child rel.
|
||||
*/
|
||||
bestinnerjoin = best_inner_indexscan(root, childrel,
|
||||
outerrel->relids, JOIN_INNER);
|
||||
outer_relids, jointype);
|
||||
|
||||
/*
|
||||
* If no luck on an indexpath for this rel, we'll still consider
|
||||
* an Append substituting the cheapest-total inner path. This
|
||||
* is only likely to win if there's at least one member rel for
|
||||
* which an indexscan path does exist.
|
||||
* an Append substituting the cheapest-total inner path. However
|
||||
* we must find at least one indexpath, else there's not going to
|
||||
* be any improvement over the base path for the appendrel.
|
||||
*/
|
||||
if (!bestinnerjoin)
|
||||
if (bestinnerjoin)
|
||||
found_indexscan = true;
|
||||
else
|
||||
bestinnerjoin = childrel->cheapest_total_path;
|
||||
|
||||
/*
|
||||
* We need a joinrel that describes this join accurately. Although
|
||||
* the joinrel won't ever be used by the join path search algorithm
|
||||
* in joinrels.c, it provides necessary context for the Path,
|
||||
* such as properly-translated target and quals lists.
|
||||
*/
|
||||
this_joinrel = translate_join_rel(root, joinrel, appinfo,
|
||||
outerrel, childrel, jointype,
|
||||
&this_restrictlist);
|
||||
|
||||
/* Build Path for join and add to result list */
|
||||
append_paths = lappend(append_paths,
|
||||
create_nestloop_path(root,
|
||||
this_joinrel,
|
||||
JOIN_INNER,
|
||||
outer_cheapest_total,
|
||||
bestinnerjoin,
|
||||
this_restrictlist,
|
||||
NIL));
|
||||
append_paths = lappend(append_paths, bestinnerjoin);
|
||||
}
|
||||
|
||||
/* Form the completed Append path and add it to the join relation. */
|
||||
add_path(joinrel, (Path *) create_append_path(joinrel, append_paths));
|
||||
if (!found_indexscan)
|
||||
return NULL;
|
||||
|
||||
/* Form and return the completed Append path. */
|
||||
return (Path *) create_append_path(rel, append_paths);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user