mirror of
https://github.com/postgres/postgres.git
synced 2025-07-05 07:21:24 +03:00
Optimize joins when the inner relation can be proven unique.
If there can certainly be no more than one matching inner row for a given outer row, then the executor can move on to the next outer row as soon as it's found one match; there's no need to continue scanning the inner relation for this outer row. This saves useless scanning in nestloop and hash joins. In merge joins, it offers the opportunity to skip mark/restore processing, because we know we have not advanced past the first possible match for the next outer row. Of course, the devil is in the details: the proof of uniqueness must depend only on joinquals (not otherquals), and if we want to skip mergejoin mark/restore then it must depend only on merge clauses. To avoid adding more planning overhead than absolutely necessary, the present patch errs in the conservative direction: there are cases where inner_unique or skip_mark_restore processing could be used, but it will not do so because it's not sure that the uniqueness proof depended only on "safe" clauses. This could be improved later. David Rowley, reviewed and rather heavily editorialized on by me Discussion: https://postgr.es/m/CAApHDvqF6Sw-TK98bW48TdtFJ+3a7D2mFyZ7++=D-RyPsL76gw@mail.gmail.com
This commit is contained in:
@ -2049,8 +2049,7 @@ calc_non_nestloop_required_outer(Path *outer_path, Path *inner_path)
|
||||
* 'joinrel' is the join relation.
|
||||
* 'jointype' is the type of join required
|
||||
* 'workspace' is the result from initial_cost_nestloop
|
||||
* 'sjinfo' is extra info about the join for selectivity estimation
|
||||
* 'semifactors' contains valid data if jointype is SEMI or ANTI
|
||||
* 'extra' contains various information about the join
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
|
||||
@ -2064,8 +2063,7 @@ create_nestloop_path(PlannerInfo *root,
|
||||
RelOptInfo *joinrel,
|
||||
JoinType jointype,
|
||||
JoinCostWorkspace *workspace,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
SemiAntiJoinFactors *semifactors,
|
||||
JoinPathExtraData *extra,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
@ -2109,7 +2107,7 @@ create_nestloop_path(PlannerInfo *root,
|
||||
joinrel,
|
||||
outer_path,
|
||||
inner_path,
|
||||
sjinfo,
|
||||
extra->sjinfo,
|
||||
required_outer,
|
||||
&restrict_clauses);
|
||||
pathnode->path.parallel_aware = false;
|
||||
@ -2119,11 +2117,12 @@ create_nestloop_path(PlannerInfo *root,
|
||||
pathnode->path.parallel_workers = outer_path->parallel_workers;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
pathnode->jointype = jointype;
|
||||
pathnode->inner_unique = extra->inner_unique;
|
||||
pathnode->outerjoinpath = outer_path;
|
||||
pathnode->innerjoinpath = inner_path;
|
||||
pathnode->joinrestrictinfo = restrict_clauses;
|
||||
|
||||
final_cost_nestloop(root, pathnode, workspace, sjinfo, semifactors);
|
||||
final_cost_nestloop(root, pathnode, workspace, extra);
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
@ -2136,7 +2135,7 @@ create_nestloop_path(PlannerInfo *root,
|
||||
* 'joinrel' is the join relation
|
||||
* 'jointype' is the type of join required
|
||||
* 'workspace' is the result from initial_cost_mergejoin
|
||||
* 'sjinfo' is extra info about the join for selectivity estimation
|
||||
* 'extra' contains various information about the join
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
|
||||
@ -2152,7 +2151,7 @@ create_mergejoin_path(PlannerInfo *root,
|
||||
RelOptInfo *joinrel,
|
||||
JoinType jointype,
|
||||
JoinCostWorkspace *workspace,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
JoinPathExtraData *extra,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
@ -2172,7 +2171,7 @@ create_mergejoin_path(PlannerInfo *root,
|
||||
joinrel,
|
||||
outer_path,
|
||||
inner_path,
|
||||
sjinfo,
|
||||
extra->sjinfo,
|
||||
required_outer,
|
||||
&restrict_clauses);
|
||||
pathnode->jpath.path.parallel_aware = false;
|
||||
@ -2182,15 +2181,17 @@ create_mergejoin_path(PlannerInfo *root,
|
||||
pathnode->jpath.path.parallel_workers = outer_path->parallel_workers;
|
||||
pathnode->jpath.path.pathkeys = pathkeys;
|
||||
pathnode->jpath.jointype = jointype;
|
||||
pathnode->jpath.inner_unique = extra->inner_unique;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.joinrestrictinfo = restrict_clauses;
|
||||
pathnode->path_mergeclauses = mergeclauses;
|
||||
pathnode->outersortkeys = outersortkeys;
|
||||
pathnode->innersortkeys = innersortkeys;
|
||||
/* pathnode->skip_mark_restore will be set by final_cost_mergejoin */
|
||||
/* pathnode->materialize_inner will be set by final_cost_mergejoin */
|
||||
|
||||
final_cost_mergejoin(root, pathnode, workspace, sjinfo);
|
||||
final_cost_mergejoin(root, pathnode, workspace, extra);
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
@ -2202,8 +2203,7 @@ create_mergejoin_path(PlannerInfo *root,
|
||||
* 'joinrel' is the join relation
|
||||
* 'jointype' is the type of join required
|
||||
* 'workspace' is the result from initial_cost_hashjoin
|
||||
* 'sjinfo' is extra info about the join for selectivity estimation
|
||||
* 'semifactors' contains valid data if jointype is SEMI or ANTI
|
||||
* 'extra' contains various information about the join
|
||||
* 'outer_path' is the cheapest outer path
|
||||
* 'inner_path' is the cheapest inner path
|
||||
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
|
||||
@ -2216,8 +2216,7 @@ create_hashjoin_path(PlannerInfo *root,
|
||||
RelOptInfo *joinrel,
|
||||
JoinType jointype,
|
||||
JoinCostWorkspace *workspace,
|
||||
SpecialJoinInfo *sjinfo,
|
||||
SemiAntiJoinFactors *semifactors,
|
||||
JoinPathExtraData *extra,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *restrict_clauses,
|
||||
@ -2234,7 +2233,7 @@ create_hashjoin_path(PlannerInfo *root,
|
||||
joinrel,
|
||||
outer_path,
|
||||
inner_path,
|
||||
sjinfo,
|
||||
extra->sjinfo,
|
||||
required_outer,
|
||||
&restrict_clauses);
|
||||
pathnode->jpath.path.parallel_aware = false;
|
||||
@ -2256,13 +2255,14 @@ create_hashjoin_path(PlannerInfo *root,
|
||||
*/
|
||||
pathnode->jpath.path.pathkeys = NIL;
|
||||
pathnode->jpath.jointype = jointype;
|
||||
pathnode->jpath.inner_unique = extra->inner_unique;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.joinrestrictinfo = restrict_clauses;
|
||||
pathnode->path_hashclauses = hashclauses;
|
||||
/* final_cost_hashjoin will fill in pathnode->num_batches */
|
||||
|
||||
final_cost_hashjoin(root, pathnode, workspace, sjinfo, semifactors);
|
||||
final_cost_hashjoin(root, pathnode, workspace, extra);
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
|
Reference in New Issue
Block a user