PostgreSQL Source Code  git master
joinpath.c File Reference
#include "postgres.h"
#include <math.h>
#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/cost.h"
#include "optimizer/optimizer.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/placeholder.h"
#include "optimizer/planmain.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
Include dependency graph for joinpath.c:

Go to the source code of this file.

Macros

#define PATH_PARAM_BY_PARENT(path, rel)
 
#define PATH_PARAM_BY_REL_SELF(path, rel)    ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))
 
#define PATH_PARAM_BY_REL(path, rel)    (PATH_PARAM_BY_REL_SELF(path, rel) || PATH_PARAM_BY_PARENT(path, rel))
 

Functions

static void try_partial_mergejoin_path (PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, List *mergeclauses, List *outersortkeys, List *innersortkeys, JoinType jointype, JoinPathExtraData *extra)
 
static void sort_inner_and_outer (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static bool clause_sides_match_join (RestrictInfo *rinfo, RelOptInfo *outerrel, RelOptInfo *innerrel)
 
static void match_unsorted_outer (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static void consider_parallel_nestloop (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static void consider_parallel_mergejoin (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra, Path *inner_cheapest_total)
 
static void hash_inner_and_outer (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
 
static Listselect_mergejoin_clauses (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype, bool *mergejoin_allowed)
 
static void generate_mergejoin_paths (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *innerrel, Path *outerpath, JoinType jointype, JoinPathExtraData *extra, bool useallclauses, Path *inner_cheapest_total, List *merge_pathkeys, bool is_partial)
 
void add_paths_to_joinrel (PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, SpecialJoinInfo *sjinfo, List *restrictlist)
 
static bool allow_star_schema_join (PlannerInfo *root, Relids outerrelids, Relids inner_paramrels)
 
static bool paraminfo_get_equal_hashops (PlannerInfo *root, ParamPathInfo *param_info, RelOptInfo *outerrel, RelOptInfo *innerrel, List *ph_lateral_vars, List **param_exprs, List **operators, bool *binary_mode)
 
static Listextract_lateral_vars_from_PHVs (PlannerInfo *root, Relids innerrelids)
 
static Pathget_memoize_path (PlannerInfo *root, RelOptInfo *innerrel, RelOptInfo *outerrel, Path *inner_path, Path *outer_path, JoinType jointype, JoinPathExtraData *extra)
 
static void try_nestloop_path (PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, JoinType jointype, JoinPathExtraData *extra)
 
static void try_partial_nestloop_path (PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, JoinType jointype, JoinPathExtraData *extra)
 
static void try_mergejoin_path (PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, List *mergeclauses, List *outersortkeys, List *innersortkeys, JoinType jointype, JoinPathExtraData *extra, bool is_partial)
 
static void try_hashjoin_path (PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *hashclauses, JoinType jointype, JoinPathExtraData *extra)
 
static void try_partial_hashjoin_path (PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *hashclauses, JoinType jointype, JoinPathExtraData *extra, bool parallel_hash)
 

Variables

set_join_pathlist_hook_type set_join_pathlist_hook = NULL
 

Macro Definition Documentation

◆ PATH_PARAM_BY_PARENT

#define PATH_PARAM_BY_PARENT (   path,
  rel 
)
Value:
((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), \
(rel)->top_parent_relids))
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:582
#define PATH_REQ_OUTER(path)
Definition: pathnodes.h:1676

Definition at line 39 of file joinpath.c.

◆ PATH_PARAM_BY_REL

#define PATH_PARAM_BY_REL (   path,
  rel 
)     (PATH_PARAM_BY_REL_SELF(path, rel) || PATH_PARAM_BY_PARENT(path, rel))

Definition at line 45 of file joinpath.c.

◆ PATH_PARAM_BY_REL_SELF

#define PATH_PARAM_BY_REL_SELF (   path,
  rel 
)     ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))

Definition at line 42 of file joinpath.c.

Function Documentation

◆ add_paths_to_joinrel()

void add_paths_to_joinrel ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinType  jointype,
SpecialJoinInfo sjinfo,
List restrictlist 
)

Definition at line 126 of file joinpath.c.

133 {
134  JoinPathExtraData extra;
135  bool mergejoin_allowed = true;
136  ListCell *lc;
137  Relids joinrelids;
138 
139  /*
140  * PlannerInfo doesn't contain the SpecialJoinInfos created for joins
141  * between child relations, even if there is a SpecialJoinInfo node for
142  * the join between the topmost parents. So, while calculating Relids set
143  * representing the restriction, consider relids of topmost parent of
144  * partitions.
145  */
146  if (joinrel->reloptkind == RELOPT_OTHER_JOINREL)
147  joinrelids = joinrel->top_parent_relids;
148  else
149  joinrelids = joinrel->relids;
150 
151  extra.restrictlist = restrictlist;
152  extra.mergeclause_list = NIL;
153  extra.sjinfo = sjinfo;
154  extra.param_source_rels = NULL;
155 
156  /*
157  * See if the inner relation is provably unique for this outer rel.
158  *
159  * We have some special cases: for JOIN_SEMI and JOIN_ANTI, it doesn't
160  * matter since the executor can make the equivalent optimization anyway;
161  * we need not expend planner cycles on proofs. For JOIN_UNIQUE_INNER, we
162  * must be considering a semijoin whose inner side is not provably unique
163  * (else reduce_unique_semijoins would've simplified it), so there's no
164  * point in calling innerrel_is_unique. However, if the LHS covers all of
165  * the semijoin's min_lefthand, then it's appropriate to set inner_unique
166  * because the path produced by create_unique_path will be unique relative
167  * to the LHS. (If we have an LHS that's only part of the min_lefthand,
168  * that is *not* true.) For JOIN_UNIQUE_OUTER, pass JOIN_INNER to avoid
169  * letting that value escape this module.
170  */
171  switch (jointype)
172  {
173  case JOIN_SEMI:
174  case JOIN_ANTI:
175 
176  /*
177  * XXX it may be worth proving this to allow a Memoize to be
178  * considered for Nested Loop Semi/Anti Joins.
179  */
180  extra.inner_unique = false; /* well, unproven */
181  break;
182  case JOIN_UNIQUE_INNER:
183  extra.inner_unique = bms_is_subset(sjinfo->min_lefthand,
184  outerrel->relids);
185  break;
186  case JOIN_UNIQUE_OUTER:
188  joinrel->relids,
189  outerrel->relids,
190  innerrel,
191  JOIN_INNER,
192  restrictlist,
193  false);
194  break;
195  default:
197  joinrel->relids,
198  outerrel->relids,
199  innerrel,
200  jointype,
201  restrictlist,
202  false);
203  break;
204  }
205 
206  /*
207  * Find potential mergejoin clauses. We can skip this if we are not
208  * interested in doing a mergejoin. However, mergejoin may be our only
209  * way of implementing a full outer join, so override enable_mergejoin if
210  * it's a full join.
211  */
212  if (enable_mergejoin || jointype == JOIN_FULL)
214  joinrel,
215  outerrel,
216  innerrel,
217  restrictlist,
218  jointype,
219  &mergejoin_allowed);
220 
221  /*
222  * If it's SEMI, ANTI, or inner_unique join, compute correction factors
223  * for cost estimation. These will be the same for all paths.
224  */
225  if (jointype == JOIN_SEMI || jointype == JOIN_ANTI || extra.inner_unique)
226  compute_semi_anti_join_factors(root, joinrel, outerrel, innerrel,
227  jointype, sjinfo, restrictlist,
228  &extra.semifactors);
229 
230  /*
231  * Decide whether it's sensible to generate parameterized paths for this
232  * joinrel, and if so, which relations such paths should require. There
233  * is usually no need to create a parameterized result path unless there
234  * is a join order restriction that prevents joining one of our input rels
235  * directly to the parameter source rel instead of joining to the other
236  * input rel. (But see allow_star_schema_join().) This restriction
237  * reduces the number of parameterized paths we have to deal with at
238  * higher join levels, without compromising the quality of the resulting
239  * plan. We express the restriction as a Relids set that must overlap the
240  * parameterization of any proposed join path. Note: param_source_rels
241  * should contain only baserels, not OJ relids, so starting from
242  * all_baserels not all_query_rels is correct.
243  */
244  foreach(lc, root->join_info_list)
245  {
246  SpecialJoinInfo *sjinfo2 = (SpecialJoinInfo *) lfirst(lc);
247 
248  /*
249  * SJ is relevant to this join if we have some part of its RHS
250  * (possibly not all of it), and haven't yet joined to its LHS. (This
251  * test is pretty simplistic, but should be sufficient considering the
252  * join has already been proven legal.) If the SJ is relevant, it
253  * presents constraints for joining to anything not in its RHS.
254  */
255  if (bms_overlap(joinrelids, sjinfo2->min_righthand) &&
256  !bms_overlap(joinrelids, sjinfo2->min_lefthand))
258  bms_difference(root->all_baserels,
259  sjinfo2->min_righthand));
260 
261  /* full joins constrain both sides symmetrically */
262  if (sjinfo2->jointype == JOIN_FULL &&
263  bms_overlap(joinrelids, sjinfo2->min_lefthand) &&
264  !bms_overlap(joinrelids, sjinfo2->min_righthand))
266  bms_difference(root->all_baserels,
267  sjinfo2->min_lefthand));
268  }
269 
270  /*
271  * However, when a LATERAL subquery is involved, there will simply not be
272  * any paths for the joinrel that aren't parameterized by whatever the
273  * subquery is parameterized by, unless its parameterization is resolved
274  * within the joinrel. So we might as well allow additional dependencies
275  * on whatever residual lateral dependencies the joinrel will have.
276  */
278  joinrel->lateral_relids);
279 
280  /*
281  * 1. Consider mergejoin paths where both relations must be explicitly
282  * sorted. Skip this if we can't mergejoin.
283  */
284  if (mergejoin_allowed)
285  sort_inner_and_outer(root, joinrel, outerrel, innerrel,
286  jointype, &extra);
287 
288  /*
289  * 2. Consider paths where the outer relation need not be explicitly
290  * sorted. This includes both nestloops and mergejoins where the outer
291  * path is already ordered. Again, skip this if we can't mergejoin.
292  * (That's okay because we know that nestloop can't handle
293  * right/right-anti/right-semi/full joins at all, so it wouldn't work in
294  * the prohibited cases either.)
295  */
296  if (mergejoin_allowed)
297  match_unsorted_outer(root, joinrel, outerrel, innerrel,
298  jointype, &extra);
299 
300 #ifdef NOT_USED
301 
302  /*
303  * 3. Consider paths where the inner relation need not be explicitly
304  * sorted. This includes mergejoins only (nestloops were already built in
305  * match_unsorted_outer).
306  *
307  * Diked out as redundant 2/13/2000 -- tgl. There isn't any really
308  * significant difference between the inner and outer side of a mergejoin,
309  * so match_unsorted_inner creates no paths that aren't equivalent to
310  * those made by match_unsorted_outer when add_paths_to_joinrel() is
311  * invoked with the two rels given in the other order.
312  */
313  if (mergejoin_allowed)
314  match_unsorted_inner(root, joinrel, outerrel, innerrel,
315  jointype, &extra);
316 #endif
317 
318  /*
319  * 4. Consider paths where both outer and inner relations must be hashed
320  * before being joined. As above, disregard enable_hashjoin for full
321  * joins, because there may be no other alternative.
322  */
323  if (enable_hashjoin || jointype == JOIN_FULL)
324  hash_inner_and_outer(root, joinrel, outerrel, innerrel,
325  jointype, &extra);
326 
327  /*
328  * 5. If inner and outer relations are foreign tables (or joins) belonging
329  * to the same server and assigned to the same user to check access
330  * permissions as, give the FDW a chance to push down joins.
331  */
332  if (joinrel->fdwroutine &&
333  joinrel->fdwroutine->GetForeignJoinPaths)
334  joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel,
335  outerrel, innerrel,
336  jointype, &extra);
337 
338  /*
339  * 6. Finally, give extensions a chance to manipulate the path list. They
340  * could add new paths (such as CustomPaths) by calling add_path(), or
341  * add_partial_path() if parallel aware. They could also delete or modify
342  * paths added by the core code.
343  */
345  set_join_pathlist_hook(root, joinrel, outerrel, innerrel,
346  jointype, &extra);
347 }
bool innerrel_is_unique(PlannerInfo *root, Relids joinrelids, Relids outerrelids, RelOptInfo *innerrel, JoinType jointype, List *restrictlist, bool force_cache)
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition: bitmapset.c:1230
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:412
Bitmapset * bms_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:346
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
void compute_semi_anti_join_factors(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, SpecialJoinInfo *sjinfo, List *restrictlist, SemiAntiJoinFactors *semifactors)
Definition: costsize.c:5043
bool enable_hashjoin
Definition: costsize.c:157
bool enable_mergejoin
Definition: costsize.c:156
static List * select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, List *restrictlist, JoinType jointype, bool *mergejoin_allowed)
Definition: joinpath.c:2498
static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:1366
set_join_pathlist_hook_type set_join_pathlist_hook
Definition: joinpath.c:32
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:2229
static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:1821
@ JOIN_SEMI
Definition: nodes.h:307
@ JOIN_FULL
Definition: nodes.h:295
@ JOIN_INNER
Definition: nodes.h:293
@ JOIN_UNIQUE_OUTER
Definition: nodes.h:316
@ JOIN_UNIQUE_INNER
Definition: nodes.h:317
@ JOIN_ANTI
Definition: nodes.h:308
@ RELOPT_OTHER_JOINREL
Definition: pathnodes.h:830
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
tree ctl root
Definition: radixtree.h:1886
List * mergeclause_list
Definition: pathnodes.h:3238
Relids param_source_rels
Definition: pathnodes.h:3242
SemiAntiJoinFactors semifactors
Definition: pathnodes.h:3241
SpecialJoinInfo * sjinfo
Definition: pathnodes.h:3240
Relids relids
Definition: pathnodes.h:871
Relids top_parent_relids
Definition: pathnodes.h:1009
Relids lateral_relids
Definition: pathnodes.h:913
RelOptKind reloptkind
Definition: pathnodes.h:865
Relids min_righthand
Definition: pathnodes.h:2902
JoinType jointype
Definition: pathnodes.h:2905
Relids min_lefthand
Definition: pathnodes.h:2901

References bms_add_members(), bms_difference(), bms_is_subset(), bms_join(), bms_overlap(), compute_semi_anti_join_factors(), enable_hashjoin, enable_mergejoin, hash_inner_and_outer(), JoinPathExtraData::inner_unique, innerrel_is_unique(), JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_SEMI, JOIN_UNIQUE_INNER, JOIN_UNIQUE_OUTER, SpecialJoinInfo::jointype, RelOptInfo::lateral_relids, lfirst, match_unsorted_outer(), JoinPathExtraData::mergeclause_list, SpecialJoinInfo::min_lefthand, SpecialJoinInfo::min_righthand, NIL, JoinPathExtraData::param_source_rels, RelOptInfo::relids, RELOPT_OTHER_JOINREL, RelOptInfo::reloptkind, JoinPathExtraData::restrictlist, root, select_mergejoin_clauses(), JoinPathExtraData::semifactors, set_join_pathlist_hook, JoinPathExtraData::sjinfo, sort_inner_and_outer(), and RelOptInfo::top_parent_relids.

Referenced by populate_joinrel_with_paths().

◆ allow_star_schema_join()

static bool allow_star_schema_join ( PlannerInfo root,
Relids  outerrelids,
Relids  inner_paramrels 
)
inlinestatic

Definition at line 365 of file joinpath.c.

368 {
369  /*
370  * It's a star-schema case if the outer rel provides some but not all of
371  * the inner rel's parameterization.
372  */
373  return (bms_overlap(inner_paramrels, outerrelids) &&
374  bms_nonempty_difference(inner_paramrels, outerrelids));
375 }
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:641

References bms_nonempty_difference(), and bms_overlap().

Referenced by try_nestloop_path().

◆ clause_sides_match_join()

static bool clause_sides_match_join ( RestrictInfo rinfo,
RelOptInfo outerrel,
RelOptInfo innerrel 
)
inlinestatic

Definition at line 1334 of file joinpath.c.

1336 {
1337  if (bms_is_subset(rinfo->left_relids, outerrel->relids) &&
1338  bms_is_subset(rinfo->right_relids, innerrel->relids))
1339  {
1340  /* lefthand side is outer */
1341  rinfo->outer_is_left = true;
1342  return true;
1343  }
1344  else if (bms_is_subset(rinfo->left_relids, innerrel->relids) &&
1345  bms_is_subset(rinfo->right_relids, outerrel->relids))
1346  {
1347  /* righthand side is outer */
1348  rinfo->outer_is_left = false;
1349  return true;
1350  }
1351  return false; /* no good for these input relations */
1352 }

References bms_is_subset(), and RelOptInfo::relids.

Referenced by hash_inner_and_outer(), paraminfo_get_equal_hashops(), and select_mergejoin_clauses().

◆ consider_parallel_mergejoin()

static void consider_parallel_mergejoin ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinType  jointype,
JoinPathExtraData extra,
Path inner_cheapest_total 
)
static

Definition at line 2080 of file joinpath.c.

2087 {
2088  ListCell *lc1;
2089 
2090  /* generate merge join path for each partial outer path */
2091  foreach(lc1, outerrel->partial_pathlist)
2092  {
2093  Path *outerpath = (Path *) lfirst(lc1);
2094  List *merge_pathkeys;
2095 
2096  /*
2097  * Figure out what useful ordering any paths we create will have.
2098  */
2099  merge_pathkeys = build_join_pathkeys(root, joinrel, jointype,
2100  outerpath->pathkeys);
2101 
2102  generate_mergejoin_paths(root, joinrel, innerrel, outerpath, jointype,
2103  extra, false, inner_cheapest_total,
2104  merge_pathkeys, true);
2105  }
2106 }
static void generate_mergejoin_paths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *innerrel, Path *outerpath, JoinType jointype, JoinPathExtraData *extra, bool useallclauses, Path *inner_cheapest_total, List *merge_pathkeys, bool is_partial)
Definition: joinpath.c:1573
List * build_join_pathkeys(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, List *outer_pathkeys)
Definition: pathkeys.c:1293
Definition: pg_list.h:54
List * pathkeys
Definition: pathnodes.h:1672
List * partial_pathlist
Definition: pathnodes.h:900

References build_join_pathkeys(), generate_mergejoin_paths(), lfirst, RelOptInfo::partial_pathlist, Path::pathkeys, and root.

Referenced by match_unsorted_outer().

◆ consider_parallel_nestloop()

static void consider_parallel_nestloop ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 2120 of file joinpath.c.

2126 {
2127  JoinType save_jointype = jointype;
2128  Path *inner_cheapest_total = innerrel->cheapest_total_path;
2129  Path *matpath = NULL;
2130  ListCell *lc1;
2131 
2132  if (jointype == JOIN_UNIQUE_INNER)
2133  jointype = JOIN_INNER;
2134 
2135  /*
2136  * Consider materializing the cheapest inner path, unless: 1) we're doing
2137  * JOIN_UNIQUE_INNER, because in this case we have to unique-ify the
2138  * cheapest inner path, 2) enable_material is off, 3) the cheapest inner
2139  * path is not parallel-safe, 4) the cheapest inner path is parameterized
2140  * by the outer rel, or 5) the cheapest inner path materializes its output
2141  * anyway.
2142  */
2143  if (save_jointype != JOIN_UNIQUE_INNER &&
2144  enable_material && inner_cheapest_total->parallel_safe &&
2145  !PATH_PARAM_BY_REL(inner_cheapest_total, outerrel) &&
2146  !ExecMaterializesOutput(inner_cheapest_total->pathtype))
2147  {
2148  matpath = (Path *)
2149  create_material_path(innerrel, inner_cheapest_total);
2150  Assert(matpath->parallel_safe);
2151  }
2152 
2153  foreach(lc1, outerrel->partial_pathlist)
2154  {
2155  Path *outerpath = (Path *) lfirst(lc1);
2156  List *pathkeys;
2157  ListCell *lc2;
2158 
2159  /* Figure out what useful ordering any paths we create will have. */
2160  pathkeys = build_join_pathkeys(root, joinrel, jointype,
2161  outerpath->pathkeys);
2162 
2163  /*
2164  * Try the cheapest parameterized paths; only those which will produce
2165  * an unparameterized path when joined to this outerrel will survive
2166  * try_partial_nestloop_path. The cheapest unparameterized path is
2167  * also in this list.
2168  */
2169  foreach(lc2, innerrel->cheapest_parameterized_paths)
2170  {
2171  Path *innerpath = (Path *) lfirst(lc2);
2172  Path *mpath;
2173 
2174  /* Can't join to an inner path that is not parallel-safe */
2175  if (!innerpath->parallel_safe)
2176  continue;
2177 
2178  /*
2179  * If we're doing JOIN_UNIQUE_INNER, we can only use the inner's
2180  * cheapest_total_path, and we have to unique-ify it. (We might
2181  * be able to relax this to allow other safe, unparameterized
2182  * inner paths, but right now create_unique_path is not on board
2183  * with that.)
2184  */
2185  if (save_jointype == JOIN_UNIQUE_INNER)
2186  {
2187  if (innerpath != innerrel->cheapest_total_path)
2188  continue;
2189  innerpath = (Path *) create_unique_path(root, innerrel,
2190  innerpath,
2191  extra->sjinfo);
2192  Assert(innerpath);
2193  }
2194 
2195  try_partial_nestloop_path(root, joinrel, outerpath, innerpath,
2196  pathkeys, jointype, extra);
2197 
2198  /*
2199  * Try generating a memoize path and see if that makes the nested
2200  * loop any cheaper.
2201  */
2202  mpath = get_memoize_path(root, innerrel, outerrel,
2203  innerpath, outerpath, jointype,
2204  extra);
2205  if (mpath != NULL)
2206  try_partial_nestloop_path(root, joinrel, outerpath, mpath,
2207  pathkeys, jointype, extra);
2208  }
2209 
2210  /* Also consider materialized form of the cheapest inner path */
2211  if (matpath != NULL)
2212  try_partial_nestloop_path(root, joinrel, outerpath, matpath,
2213  pathkeys, jointype, extra);
2214  }
2215 }
#define Assert(condition)
Definition: c.h:858
bool enable_material
Definition: costsize.c:154
bool ExecMaterializesOutput(NodeTag plantype)
Definition: execAmi.c:635
static Path * get_memoize_path(PlannerInfo *root, RelOptInfo *innerrel, RelOptInfo *outerrel, Path *inner_path, Path *outer_path, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:676
#define PATH_PARAM_BY_REL(path, rel)
Definition: joinpath.c:45
static void try_partial_nestloop_path(PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:948
JoinType
Definition: nodes.h:288
UniquePath * create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, SpecialJoinInfo *sjinfo)
Definition: pathnode.c:1727
MaterialPath * create_material_path(RelOptInfo *rel, Path *subpath)
Definition: pathnode.c:1634
NodeTag pathtype
Definition: pathnodes.h:1632
bool parallel_safe
Definition: pathnodes.h:1661
List * cheapest_parameterized_paths
Definition: pathnodes.h:904
struct Path * cheapest_total_path
Definition: pathnodes.h:902

References Assert, build_join_pathkeys(), RelOptInfo::cheapest_parameterized_paths, RelOptInfo::cheapest_total_path, create_material_path(), create_unique_path(), enable_material, ExecMaterializesOutput(), get_memoize_path(), JOIN_INNER, JOIN_UNIQUE_INNER, lfirst, Path::parallel_safe, RelOptInfo::partial_pathlist, PATH_PARAM_BY_REL, Path::pathkeys, Path::pathtype, root, JoinPathExtraData::sjinfo, and try_partial_nestloop_path().

Referenced by match_unsorted_outer().

◆ extract_lateral_vars_from_PHVs()

static List* extract_lateral_vars_from_PHVs ( PlannerInfo root,
Relids  innerrelids 
)
static

Definition at line 585 of file joinpath.c.

586 {
587  List *ph_lateral_vars = NIL;
588  ListCell *lc;
589 
590  /* Nothing would be found if the query contains no LATERAL RTEs */
591  if (!root->hasLateralRTEs)
592  return NIL;
593 
594  /*
595  * No need to consider PHVs that are due to be evaluated at joinrels,
596  * since we do not add Memoize nodes on top of joinrel paths.
597  */
598  if (bms_membership(innerrelids) == BMS_MULTIPLE)
599  return NIL;
600 
601  foreach(lc, root->placeholder_list)
602  {
603  PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc);
604  List *vars;
605  ListCell *cell;
606 
607  /* PHV is uninteresting if no lateral refs */
608  if (phinfo->ph_lateral == NULL)
609  continue;
610 
611  /* PHV is uninteresting if not due to be evaluated at innerrelids */
612  if (!bms_equal(phinfo->ph_eval_at, innerrelids))
613  continue;
614 
615  /*
616  * If the PHV does not reference any rels in innerrelids, use its
617  * contained expression as a cache key rather than extracting the
618  * Vars/PHVs from it and using those. This can be beneficial in cases
619  * where the expression results in fewer distinct values to cache
620  * tuples for.
621  */
622  if (!bms_overlap(pull_varnos(root, (Node *) phinfo->ph_var->phexpr),
623  innerrelids))
624  {
625  ph_lateral_vars = lappend(ph_lateral_vars, phinfo->ph_var->phexpr);
626  continue;
627  }
628 
629  /* Fetch Vars and PHVs of lateral references within PlaceHolderVars */
630  vars = pull_vars_of_level((Node *) phinfo->ph_var->phexpr, 0);
631  foreach(cell, vars)
632  {
633  Node *node = (Node *) lfirst(cell);
634 
635  if (IsA(node, Var))
636  {
637  Var *var = (Var *) node;
638 
639  Assert(var->varlevelsup == 0);
640 
641  if (bms_is_member(var->varno, phinfo->ph_lateral))
642  ph_lateral_vars = lappend(ph_lateral_vars, node);
643  }
644  else if (IsA(node, PlaceHolderVar))
645  {
646  PlaceHolderVar *phv = (PlaceHolderVar *) node;
647 
648  Assert(phv->phlevelsup == 0);
649 
650  if (bms_is_subset(find_placeholder_info(root, phv)->ph_eval_at,
651  phinfo->ph_lateral))
652  ph_lateral_vars = lappend(ph_lateral_vars, node);
653  }
654  else
655  Assert(false);
656  }
657 
658  list_free(vars);
659  }
660 
661  return ph_lateral_vars;
662 }
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:142
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
BMS_Membership bms_membership(const Bitmapset *a)
Definition: bitmapset.c:781
@ BMS_MULTIPLE
Definition: bitmapset.h:73
List * lappend(List *list, void *datum)
Definition: list.c:339
void list_free(List *list)
Definition: list.c:1546
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
PlaceHolderInfo * find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv)
Definition: placeholder.c:83
Definition: nodes.h:129
Relids ph_lateral
Definition: pathnodes.h:3098
Relids ph_eval_at
Definition: pathnodes.h:3095
PlaceHolderVar * ph_var
Definition: pathnodes.h:3092
Index phlevelsup
Definition: pathnodes.h:2804
Definition: primnodes.h:248
int varno
Definition: primnodes.h:255
Index varlevelsup
Definition: primnodes.h:280
Definition: regcomp.c:281
List * pull_vars_of_level(Node *node, int levelsup)
Definition: var.c:340
Relids pull_varnos(PlannerInfo *root, Node *node)
Definition: var.c:113

References Assert, bms_equal(), bms_is_member(), bms_is_subset(), bms_membership(), BMS_MULTIPLE, bms_overlap(), find_placeholder_info(), IsA, lappend(), lfirst, list_free(), NIL, PlaceHolderInfo::ph_eval_at, PlaceHolderInfo::ph_lateral, PlaceHolderInfo::ph_var, PlaceHolderVar::phlevelsup, pull_varnos(), pull_vars_of_level(), root, Var::varlevelsup, and Var::varno.

Referenced by get_memoize_path().

◆ generate_mergejoin_paths()

static void generate_mergejoin_paths ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo innerrel,
Path outerpath,
JoinType  jointype,
JoinPathExtraData extra,
bool  useallclauses,
Path inner_cheapest_total,
List merge_pathkeys,
bool  is_partial 
)
static

Definition at line 1573 of file joinpath.c.

1583 {
1584  List *mergeclauses;
1585  List *innersortkeys;
1586  List *trialsortkeys;
1587  Path *cheapest_startup_inner;
1588  Path *cheapest_total_inner;
1589  JoinType save_jointype = jointype;
1590  int num_sortkeys;
1591  int sortkeycnt;
1592 
1593  if (jointype == JOIN_UNIQUE_OUTER || jointype == JOIN_UNIQUE_INNER)
1594  jointype = JOIN_INNER;
1595 
1596  /* Look for useful mergeclauses (if any) */
1597  mergeclauses =
1599  outerpath->pathkeys,
1600  extra->mergeclause_list);
1601 
1602  /*
1603  * Done with this outer path if no chance for a mergejoin.
1604  *
1605  * Special corner case: for "x FULL JOIN y ON true", there will be no join
1606  * clauses at all. Ordinarily we'd generate a clauseless nestloop path,
1607  * but since mergejoin is our only join type that supports FULL JOIN
1608  * without any join clauses, it's necessary to generate a clauseless
1609  * mergejoin path instead.
1610  */
1611  if (mergeclauses == NIL)
1612  {
1613  if (jointype == JOIN_FULL)
1614  /* okay to try for mergejoin */ ;
1615  else
1616  return;
1617  }
1618  if (useallclauses &&
1619  list_length(mergeclauses) != list_length(extra->mergeclause_list))
1620  return;
1621 
1622  /* Compute the required ordering of the inner path */
1623  innersortkeys = make_inner_pathkeys_for_merge(root,
1624  mergeclauses,
1625  outerpath->pathkeys);
1626 
1627  /*
1628  * Generate a mergejoin on the basis of sorting the cheapest inner. Since
1629  * a sort will be needed, only cheapest total cost matters. (But
1630  * try_mergejoin_path will do the right thing if inner_cheapest_total is
1631  * already correctly sorted.)
1632  */
1634  joinrel,
1635  outerpath,
1636  inner_cheapest_total,
1637  merge_pathkeys,
1638  mergeclauses,
1639  NIL,
1640  innersortkeys,
1641  jointype,
1642  extra,
1643  is_partial);
1644 
1645  /* Can't do anything else if inner path needs to be unique'd */
1646  if (save_jointype == JOIN_UNIQUE_INNER)
1647  return;
1648 
1649  /*
1650  * Look for presorted inner paths that satisfy the innersortkey list ---
1651  * or any truncation thereof, if we are allowed to build a mergejoin using
1652  * a subset of the merge clauses. Here, we consider both cheap startup
1653  * cost and cheap total cost.
1654  *
1655  * Currently we do not consider parameterized inner paths here. This
1656  * interacts with decisions elsewhere that also discriminate against
1657  * mergejoins with parameterized inputs; see comments in
1658  * src/backend/optimizer/README.
1659  *
1660  * As we shorten the sortkey list, we should consider only paths that are
1661  * strictly cheaper than (in particular, not the same as) any path found
1662  * in an earlier iteration. Otherwise we'd be intentionally using fewer
1663  * merge keys than a given path allows (treating the rest as plain
1664  * joinquals), which is unlikely to be a good idea. Also, eliminating
1665  * paths here on the basis of compare_path_costs is a lot cheaper than
1666  * building the mergejoin path only to throw it away.
1667  *
1668  * If inner_cheapest_total is well enough sorted to have not required a
1669  * sort in the path made above, we shouldn't make a duplicate path with
1670  * it, either. We handle that case with the same logic that handles the
1671  * previous consideration, by initializing the variables that track
1672  * cheapest-so-far properly. Note that we do NOT reject
1673  * inner_cheapest_total if we find it matches some shorter set of
1674  * pathkeys. That case corresponds to using fewer mergekeys to avoid
1675  * sorting inner_cheapest_total, whereas we did sort it above, so the
1676  * plans being considered are different.
1677  */
1678  if (pathkeys_contained_in(innersortkeys,
1679  inner_cheapest_total->pathkeys))
1680  {
1681  /* inner_cheapest_total didn't require a sort */
1682  cheapest_startup_inner = inner_cheapest_total;
1683  cheapest_total_inner = inner_cheapest_total;
1684  }
1685  else
1686  {
1687  /* it did require a sort, at least for the full set of keys */
1688  cheapest_startup_inner = NULL;
1689  cheapest_total_inner = NULL;
1690  }
1691  num_sortkeys = list_length(innersortkeys);
1692  if (num_sortkeys > 1 && !useallclauses)
1693  trialsortkeys = list_copy(innersortkeys); /* need modifiable copy */
1694  else
1695  trialsortkeys = innersortkeys; /* won't really truncate */
1696 
1697  for (sortkeycnt = num_sortkeys; sortkeycnt > 0; sortkeycnt--)
1698  {
1699  Path *innerpath;
1700  List *newclauses = NIL;
1701 
1702  /*
1703  * Look for an inner path ordered well enough for the first
1704  * 'sortkeycnt' innersortkeys. NB: trialsortkeys list is modified
1705  * destructively, which is why we made a copy...
1706  */
1707  trialsortkeys = list_truncate(trialsortkeys, sortkeycnt);
1708  innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist,
1709  trialsortkeys,
1710  NULL,
1711  TOTAL_COST,
1712  is_partial);
1713  if (innerpath != NULL &&
1714  (cheapest_total_inner == NULL ||
1715  compare_path_costs(innerpath, cheapest_total_inner,
1716  TOTAL_COST) < 0))
1717  {
1718  /* Found a cheap (or even-cheaper) sorted path */
1719  /* Select the right mergeclauses, if we didn't already */
1720  if (sortkeycnt < num_sortkeys)
1721  {
1722  newclauses =
1724  mergeclauses,
1725  trialsortkeys);
1726  Assert(newclauses != NIL);
1727  }
1728  else
1729  newclauses = mergeclauses;
1731  joinrel,
1732  outerpath,
1733  innerpath,
1734  merge_pathkeys,
1735  newclauses,
1736  NIL,
1737  NIL,
1738  jointype,
1739  extra,
1740  is_partial);
1741  cheapest_total_inner = innerpath;
1742  }
1743  /* Same on the basis of cheapest startup cost ... */
1744  innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist,
1745  trialsortkeys,
1746  NULL,
1747  STARTUP_COST,
1748  is_partial);
1749  if (innerpath != NULL &&
1750  (cheapest_startup_inner == NULL ||
1751  compare_path_costs(innerpath, cheapest_startup_inner,
1752  STARTUP_COST) < 0))
1753  {
1754  /* Found a cheap (or even-cheaper) sorted path */
1755  if (innerpath != cheapest_total_inner)
1756  {
1757  /*
1758  * Avoid rebuilding clause list if we already made one; saves
1759  * memory in big join trees...
1760  */
1761  if (newclauses == NIL)
1762  {
1763  if (sortkeycnt < num_sortkeys)
1764  {
1765  newclauses =
1767  mergeclauses,
1768  trialsortkeys);
1769  Assert(newclauses != NIL);
1770  }
1771  else
1772  newclauses = mergeclauses;
1773  }
1775  joinrel,
1776  outerpath,
1777  innerpath,
1778  merge_pathkeys,
1779  newclauses,
1780  NIL,
1781  NIL,
1782  jointype,
1783  extra,
1784  is_partial);
1785  }
1786  cheapest_startup_inner = innerpath;
1787  }
1788 
1789  /*
1790  * Don't consider truncated sortkeys if we need all clauses.
1791  */
1792  if (useallclauses)
1793  break;
1794  }
1795 }
static void try_mergejoin_path(PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, List *mergeclauses, List *outersortkeys, List *innersortkeys, JoinType jointype, JoinPathExtraData *extra, bool is_partial)
Definition: joinpath.c:1027
List * list_truncate(List *list, int new_size)
Definition: list.c:631
List * list_copy(const List *oldlist)
Definition: list.c:1573
List * make_inner_pathkeys_for_merge(PlannerInfo *root, List *mergeclauses, List *outer_pathkeys)
Definition: pathkeys.c:1852
Path * get_cheapest_path_for_pathkeys(List *paths, List *pathkeys, Relids required_outer, CostSelector cost_criterion, bool require_parallel_safe)
Definition: pathkeys.c:619
List * find_mergeclauses_for_outer_pathkeys(PlannerInfo *root, List *pathkeys, List *restrictinfos)
Definition: pathkeys.c:1541
List * trim_mergeclauses_for_inner_pathkeys(PlannerInfo *root, List *mergeclauses, List *pathkeys)
Definition: pathkeys.c:1955
bool pathkeys_contained_in(List *keys1, List *keys2)
Definition: pathkeys.c:342
int compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
Definition: pathnode.c:69
@ TOTAL_COST
Definition: pathnodes.h:38
@ STARTUP_COST
Definition: pathnodes.h:38
static int list_length(const List *l)
Definition: pg_list.h:152
List * pathlist
Definition: pathnodes.h:898

References Assert, compare_path_costs(), find_mergeclauses_for_outer_pathkeys(), get_cheapest_path_for_pathkeys(), JOIN_FULL, JOIN_INNER, JOIN_UNIQUE_INNER, JOIN_UNIQUE_OUTER, list_copy(), list_length(), list_truncate(), make_inner_pathkeys_for_merge(), JoinPathExtraData::mergeclause_list, NIL, Path::pathkeys, pathkeys_contained_in(), RelOptInfo::pathlist, root, STARTUP_COST, TOTAL_COST, trim_mergeclauses_for_inner_pathkeys(), and try_mergejoin_path().

Referenced by consider_parallel_mergejoin(), and match_unsorted_outer().

◆ get_memoize_path()

static Path* get_memoize_path ( PlannerInfo root,
RelOptInfo innerrel,
RelOptInfo outerrel,
Path inner_path,
Path outer_path,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 676 of file joinpath.c.

680 {
681  List *param_exprs;
682  List *hash_operators;
683  ListCell *lc;
684  bool binary_mode;
685  List *ph_lateral_vars;
686 
687  /* Obviously not if it's disabled */
688  if (!enable_memoize)
689  return NULL;
690 
691  /*
692  * We can safely not bother with all this unless we expect to perform more
693  * than one inner scan. The first scan is always going to be a cache
694  * miss. This would likely fail later anyway based on costs, so this is
695  * really just to save some wasted effort.
696  */
697  if (outer_path->parent->rows < 2)
698  return NULL;
699 
700  /*
701  * Extract lateral Vars/PHVs within PlaceHolderVars that are due to be
702  * evaluated at innerrel. These lateral Vars/PHVs could be used as
703  * memoize cache keys.
704  */
705  ph_lateral_vars = extract_lateral_vars_from_PHVs(root, innerrel->relids);
706 
707  /*
708  * We can only have a memoize node when there's some kind of cache key,
709  * either parameterized path clauses or lateral Vars. No cache key sounds
710  * more like something a Materialize node might be more useful for.
711  */
712  if ((inner_path->param_info == NULL ||
713  inner_path->param_info->ppi_clauses == NIL) &&
714  innerrel->lateral_vars == NIL &&
715  ph_lateral_vars == NIL)
716  return NULL;
717 
718  /*
719  * Currently we don't do this for SEMI and ANTI joins unless they're
720  * marked as inner_unique. This is because nested loop SEMI/ANTI joins
721  * don't scan the inner node to completion, which will mean memoize cannot
722  * mark the cache entry as complete.
723  *
724  * XXX Currently we don't attempt to mark SEMI/ANTI joins as inner_unique
725  * = true. Should we? See add_paths_to_joinrel()
726  */
727  if (!extra->inner_unique && (jointype == JOIN_SEMI ||
728  jointype == JOIN_ANTI))
729  return NULL;
730 
731  /*
732  * Memoize normally marks cache entries as complete when it runs out of
733  * tuples to read from its subplan. However, with unique joins, Nested
734  * Loop will skip to the next outer tuple after finding the first matching
735  * inner tuple. This means that we may not read the inner side of the
736  * join to completion which leaves no opportunity to mark the cache entry
737  * as complete. To work around that, when the join is unique we
738  * automatically mark cache entries as complete after fetching the first
739  * tuple. This works when the entire join condition is parameterized.
740  * Otherwise, when the parameterization is only a subset of the join
741  * condition, we can't be sure which part of it causes the join to be
742  * unique. This means there are no guarantees that only 1 tuple will be
743  * read. We cannot mark the cache entry as complete after reading the
744  * first tuple without that guarantee. This means the scope of Memoize
745  * node's usefulness is limited to only outer rows that have no join
746  * partner as this is the only case where Nested Loop would exhaust the
747  * inner scan of a unique join. Since the scope is limited to that, we
748  * just don't bother making a memoize path in this case.
749  *
750  * Lateral vars needn't be considered here as they're not considered when
751  * determining if the join is unique.
752  *
753  * XXX this could be enabled if the remaining join quals were made part of
754  * the inner scan's filter instead of the join filter. Maybe it's worth
755  * considering doing that?
756  */
757  if (extra->inner_unique &&
758  (inner_path->param_info == NULL ||
759  bms_num_members(inner_path->param_info->ppi_serials) <
760  list_length(extra->restrictlist)))
761  return NULL;
762 
763  /*
764  * We can't use a memoize node if there are volatile functions in the
765  * inner rel's target list or restrict list. A cache hit could reduce the
766  * number of calls to these functions.
767  */
768  if (contain_volatile_functions((Node *) innerrel->reltarget))
769  return NULL;
770 
771  foreach(lc, innerrel->baserestrictinfo)
772  {
773  RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
774 
775  if (contain_volatile_functions((Node *) rinfo))
776  return NULL;
777  }
778 
779  /*
780  * Also check the parameterized path restrictinfos for volatile functions.
781  * Indexed functions must be immutable so shouldn't have any volatile
782  * functions, however, with a lateral join the inner scan may not be an
783  * index scan.
784  */
785  if (inner_path->param_info != NULL)
786  {
787  foreach(lc, inner_path->param_info->ppi_clauses)
788  {
789  RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
790 
791  if (contain_volatile_functions((Node *) rinfo))
792  return NULL;
793  }
794  }
795 
796  /* Check if we have hash ops for each parameter to the path */
798  inner_path->param_info,
799  outerrel->top_parent ?
800  outerrel->top_parent : outerrel,
801  innerrel,
802  ph_lateral_vars,
803  &param_exprs,
804  &hash_operators,
805  &binary_mode))
806  {
807  return (Path *) create_memoize_path(root,
808  innerrel,
809  inner_path,
810  param_exprs,
811  hash_operators,
812  extra->inner_unique,
813  binary_mode,
814  outer_path->rows);
815  }
816 
817  return NULL;
818 }
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:751
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:538
bool enable_memoize
Definition: costsize.c:155
static List * extract_lateral_vars_from_PHVs(PlannerInfo *root, Relids innerrelids)
Definition: joinpath.c:585
static bool paraminfo_get_equal_hashops(PlannerInfo *root, ParamPathInfo *param_info, RelOptInfo *outerrel, RelOptInfo *innerrel, List *ph_lateral_vars, List **param_exprs, List **operators, bool *binary_mode)
Definition: joinpath.c:441
MemoizePath * create_memoize_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, List *param_exprs, List *hash_operators, bool singlerow, bool binary_mode, double calls)
Definition: pathnode.c:1667
Cardinality rows
Definition: pathnodes.h:1666
List * baserestrictinfo
Definition: pathnodes.h:985
struct PathTarget * reltarget
Definition: pathnodes.h:893
List * lateral_vars
Definition: pathnodes.h:940

References RelOptInfo::baserestrictinfo, bms_num_members(), contain_volatile_functions(), create_memoize_path(), enable_memoize, extract_lateral_vars_from_PHVs(), JoinPathExtraData::inner_unique, JOIN_ANTI, JOIN_SEMI, RelOptInfo::lateral_vars, lfirst, list_length(), NIL, paraminfo_get_equal_hashops(), RelOptInfo::relids, RelOptInfo::reltarget, JoinPathExtraData::restrictlist, root, and Path::rows.

Referenced by consider_parallel_nestloop(), and match_unsorted_outer().

◆ hash_inner_and_outer()

static void hash_inner_and_outer ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 2229 of file joinpath.c.

2235 {
2236  JoinType save_jointype = jointype;
2237  bool isouterjoin = IS_OUTER_JOIN(jointype);
2238  List *hashclauses;
2239  ListCell *l;
2240 
2241  /*
2242  * We need to build only one hashclauses list for any given pair of outer
2243  * and inner relations; all of the hashable clauses will be used as keys.
2244  *
2245  * Scan the join's restrictinfo list to find hashjoinable clauses that are
2246  * usable with this pair of sub-relations.
2247  */
2248  hashclauses = NIL;
2249  foreach(l, extra->restrictlist)
2250  {
2251  RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
2252 
2253  /*
2254  * If processing an outer join, only use its own join clauses for
2255  * hashing. For inner joins we need not be so picky.
2256  */
2257  if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids))
2258  continue;
2259 
2260  if (!restrictinfo->can_join ||
2261  restrictinfo->hashjoinoperator == InvalidOid)
2262  continue; /* not hashjoinable */
2263 
2264  /*
2265  * Check if clause has the form "outer op inner" or "inner op outer".
2266  */
2267  if (!clause_sides_match_join(restrictinfo, outerrel, innerrel))
2268  continue; /* no good for these input relations */
2269 
2270  /*
2271  * If clause has the form "inner op outer", check if its operator has
2272  * valid commutator. This is necessary because hashclauses in this
2273  * form will get commuted in createplan.c to put the outer var on the
2274  * left (see get_switched_clauses). This probably shouldn't ever
2275  * fail, since hashable operators ought to have commutators, but be
2276  * paranoid.
2277  *
2278  * The clause being hashjoinable indicates that it's an OpExpr.
2279  */
2280  if (!restrictinfo->outer_is_left &&
2281  !OidIsValid(get_commutator(castNode(OpExpr, restrictinfo->clause)->opno)))
2282  continue;
2283 
2284  hashclauses = lappend(hashclauses, restrictinfo);
2285  }
2286 
2287  /* If we found any usable hashclauses, make paths */
2288  if (hashclauses)
2289  {
2290  /*
2291  * We consider both the cheapest-total-cost and cheapest-startup-cost
2292  * outer paths. There's no need to consider any but the
2293  * cheapest-total-cost inner path, however.
2294  */
2295  Path *cheapest_startup_outer = outerrel->cheapest_startup_path;
2296  Path *cheapest_total_outer = outerrel->cheapest_total_path;
2297  Path *cheapest_total_inner = innerrel->cheapest_total_path;
2298 
2299  /*
2300  * If either cheapest-total path is parameterized by the other rel, we
2301  * can't use a hashjoin. (There's no use looking for alternative
2302  * input paths, since these should already be the least-parameterized
2303  * available paths.)
2304  */
2305  if (PATH_PARAM_BY_REL(cheapest_total_outer, innerrel) ||
2306  PATH_PARAM_BY_REL(cheapest_total_inner, outerrel))
2307  return;
2308 
2309  /* Unique-ify if need be; we ignore parameterized possibilities */
2310  if (jointype == JOIN_UNIQUE_OUTER)
2311  {
2312  cheapest_total_outer = (Path *)
2313  create_unique_path(root, outerrel,
2314  cheapest_total_outer, extra->sjinfo);
2315  Assert(cheapest_total_outer);
2316  jointype = JOIN_INNER;
2318  joinrel,
2319  cheapest_total_outer,
2320  cheapest_total_inner,
2321  hashclauses,
2322  jointype,
2323  extra);
2324  /* no possibility of cheap startup here */
2325  }
2326  else if (jointype == JOIN_UNIQUE_INNER)
2327  {
2328  cheapest_total_inner = (Path *)
2329  create_unique_path(root, innerrel,
2330  cheapest_total_inner, extra->sjinfo);
2331  Assert(cheapest_total_inner);
2332  jointype = JOIN_INNER;
2334  joinrel,
2335  cheapest_total_outer,
2336  cheapest_total_inner,
2337  hashclauses,
2338  jointype,
2339  extra);
2340  if (cheapest_startup_outer != NULL &&
2341  cheapest_startup_outer != cheapest_total_outer)
2343  joinrel,
2344  cheapest_startup_outer,
2345  cheapest_total_inner,
2346  hashclauses,
2347  jointype,
2348  extra);
2349  }
2350  else
2351  {
2352  /*
2353  * For other jointypes, we consider the cheapest startup outer
2354  * together with the cheapest total inner, and then consider
2355  * pairings of cheapest-total paths including parameterized ones.
2356  * There is no use in generating parameterized paths on the basis
2357  * of possibly cheap startup cost, so this is sufficient.
2358  */
2359  ListCell *lc1;
2360  ListCell *lc2;
2361 
2362  if (cheapest_startup_outer != NULL)
2364  joinrel,
2365  cheapest_startup_outer,
2366  cheapest_total_inner,
2367  hashclauses,
2368  jointype,
2369  extra);
2370 
2371  foreach(lc1, outerrel->cheapest_parameterized_paths)
2372  {
2373  Path *outerpath = (Path *) lfirst(lc1);
2374 
2375  /*
2376  * We cannot use an outer path that is parameterized by the
2377  * inner rel.
2378  */
2379  if (PATH_PARAM_BY_REL(outerpath, innerrel))
2380  continue;
2381 
2382  foreach(lc2, innerrel->cheapest_parameterized_paths)
2383  {
2384  Path *innerpath = (Path *) lfirst(lc2);
2385 
2386  /*
2387  * We cannot use an inner path that is parameterized by
2388  * the outer rel, either.
2389  */
2390  if (PATH_PARAM_BY_REL(innerpath, outerrel))
2391  continue;
2392 
2393  if (outerpath == cheapest_startup_outer &&
2394  innerpath == cheapest_total_inner)
2395  continue; /* already tried it */
2396 
2398  joinrel,
2399  outerpath,
2400  innerpath,
2401  hashclauses,
2402  jointype,
2403  extra);
2404  }
2405  }
2406  }
2407 
2408  /*
2409  * If the joinrel is parallel-safe, we may be able to consider a
2410  * partial hash join. However, we can't handle JOIN_UNIQUE_OUTER,
2411  * because the outer path will be partial, and therefore we won't be
2412  * able to properly guarantee uniqueness. Also, the resulting path
2413  * must not be parameterized.
2414  */
2415  if (joinrel->consider_parallel &&
2416  save_jointype != JOIN_UNIQUE_OUTER &&
2417  outerrel->partial_pathlist != NIL &&
2418  bms_is_empty(joinrel->lateral_relids))
2419  {
2420  Path *cheapest_partial_outer;
2421  Path *cheapest_partial_inner = NULL;
2422  Path *cheapest_safe_inner = NULL;
2423 
2424  cheapest_partial_outer =
2425  (Path *) linitial(outerrel->partial_pathlist);
2426 
2427  /*
2428  * Can we use a partial inner plan too, so that we can build a
2429  * shared hash table in parallel? We can't handle
2430  * JOIN_UNIQUE_INNER because we can't guarantee uniqueness.
2431  */
2432  if (innerrel->partial_pathlist != NIL &&
2433  save_jointype != JOIN_UNIQUE_INNER &&
2435  {
2436  cheapest_partial_inner =
2437  (Path *) linitial(innerrel->partial_pathlist);
2439  cheapest_partial_outer,
2440  cheapest_partial_inner,
2441  hashclauses, jointype, extra,
2442  true /* parallel_hash */ );
2443  }
2444 
2445  /*
2446  * Normally, given that the joinrel is parallel-safe, the cheapest
2447  * total inner path will also be parallel-safe, but if not, we'll
2448  * have to search for the cheapest safe, unparameterized inner
2449  * path. If doing JOIN_UNIQUE_INNER, we can't use any alternative
2450  * inner path. If full, right, right-semi or right-anti join, we
2451  * can't use parallelism (building the hash table in each backend)
2452  * because no one process has all the match bits.
2453  */
2454  if (save_jointype == JOIN_FULL ||
2455  save_jointype == JOIN_RIGHT ||
2456  save_jointype == JOIN_RIGHT_SEMI ||
2457  save_jointype == JOIN_RIGHT_ANTI)
2458  cheapest_safe_inner = NULL;
2459  else if (cheapest_total_inner->parallel_safe)
2460  cheapest_safe_inner = cheapest_total_inner;
2461  else if (save_jointype != JOIN_UNIQUE_INNER)
2462  cheapest_safe_inner =
2464 
2465  if (cheapest_safe_inner != NULL)
2467  cheapest_partial_outer,
2468  cheapest_safe_inner,
2469  hashclauses, jointype, extra,
2470  false /* parallel_hash */ );
2471  }
2472  }
2473 }
#define bms_is_empty(a)
Definition: bitmapset.h:118
#define OidIsValid(objectId)
Definition: c.h:775
bool enable_parallel_hash
Definition: costsize.c:162
static void try_hashjoin_path(PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *hashclauses, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:1200
static bool clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel, RelOptInfo *innerrel)
Definition: joinpath.c:1334
static void try_partial_hashjoin_path(PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *hashclauses, JoinType jointype, JoinPathExtraData *extra, bool parallel_hash)
Definition: joinpath.c:1277
Oid get_commutator(Oid opno)
Definition: lsyscache.c:1509
#define IS_OUTER_JOIN(jointype)
Definition: nodes.h:338
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
@ JOIN_RIGHT
Definition: nodes.h:296
@ JOIN_RIGHT_SEMI
Definition: nodes.h:309
@ JOIN_RIGHT_ANTI
Definition: nodes.h:310
Path * get_cheapest_parallel_safe_total_inner(List *paths)
Definition: pathkeys.c:698
#define RINFO_IS_PUSHED_DOWN(rinfo, joinrelids)
Definition: pathnodes.h:2728
#define linitial(l)
Definition: pg_list.h:178
#define InvalidOid
Definition: postgres_ext.h:36
bool consider_parallel
Definition: pathnodes.h:887
struct Path * cheapest_startup_path
Definition: pathnodes.h:901
Expr * clause
Definition: pathnodes.h:2571

References Assert, bms_is_empty, castNode, RelOptInfo::cheapest_parameterized_paths, RelOptInfo::cheapest_startup_path, RelOptInfo::cheapest_total_path, RestrictInfo::clause, clause_sides_match_join(), RelOptInfo::consider_parallel, create_unique_path(), enable_parallel_hash, get_cheapest_parallel_safe_total_inner(), get_commutator(), InvalidOid, IS_OUTER_JOIN, JOIN_FULL, JOIN_INNER, JOIN_RIGHT, JOIN_RIGHT_ANTI, JOIN_RIGHT_SEMI, JOIN_UNIQUE_INNER, JOIN_UNIQUE_OUTER, lappend(), RelOptInfo::lateral_relids, lfirst, linitial, NIL, OidIsValid, Path::parallel_safe, RelOptInfo::partial_pathlist, PATH_PARAM_BY_REL, RelOptInfo::pathlist, RelOptInfo::relids, JoinPathExtraData::restrictlist, RINFO_IS_PUSHED_DOWN, root, JoinPathExtraData::sjinfo, try_hashjoin_path(), and try_partial_hashjoin_path().

Referenced by add_paths_to_joinrel().

◆ match_unsorted_outer()

static void match_unsorted_outer ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 1821 of file joinpath.c.

1827 {
1828  JoinType save_jointype = jointype;
1829  bool nestjoinOK;
1830  bool useallclauses;
1831  Path *inner_cheapest_total = innerrel->cheapest_total_path;
1832  Path *matpath = NULL;
1833  ListCell *lc1;
1834 
1835  /*
1836  * For now we do not support RIGHT_SEMI join in mergejoin or nestloop
1837  * join.
1838  */
1839  if (jointype == JOIN_RIGHT_SEMI)
1840  return;
1841 
1842  /*
1843  * Nestloop only supports inner, left, semi, and anti joins. Also, if we
1844  * are doing a right, right-anti or full mergejoin, we must use *all* the
1845  * mergeclauses as join clauses, else we will not have a valid plan.
1846  * (Although these two flags are currently inverses, keep them separate
1847  * for clarity and possible future changes.)
1848  */
1849  switch (jointype)
1850  {
1851  case JOIN_INNER:
1852  case JOIN_LEFT:
1853  case JOIN_SEMI:
1854  case JOIN_ANTI:
1855  nestjoinOK = true;
1856  useallclauses = false;
1857  break;
1858  case JOIN_RIGHT:
1859  case JOIN_RIGHT_ANTI:
1860  case JOIN_FULL:
1861  nestjoinOK = false;
1862  useallclauses = true;
1863  break;
1864  case JOIN_UNIQUE_OUTER:
1865  case JOIN_UNIQUE_INNER:
1866  jointype = JOIN_INNER;
1867  nestjoinOK = true;
1868  useallclauses = false;
1869  break;
1870  default:
1871  elog(ERROR, "unrecognized join type: %d",
1872  (int) jointype);
1873  nestjoinOK = false; /* keep compiler quiet */
1874  useallclauses = false;
1875  break;
1876  }
1877 
1878  /*
1879  * If inner_cheapest_total is parameterized by the outer rel, ignore it;
1880  * we will consider it below as a member of cheapest_parameterized_paths,
1881  * but the other possibilities considered in this routine aren't usable.
1882  */
1883  if (PATH_PARAM_BY_REL(inner_cheapest_total, outerrel))
1884  inner_cheapest_total = NULL;
1885 
1886  /*
1887  * If we need to unique-ify the inner path, we will consider only the
1888  * cheapest-total inner.
1889  */
1890  if (save_jointype == JOIN_UNIQUE_INNER)
1891  {
1892  /* No way to do this with an inner path parameterized by outer rel */
1893  if (inner_cheapest_total == NULL)
1894  return;
1895  inner_cheapest_total = (Path *)
1896  create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo);
1897  Assert(inner_cheapest_total);
1898  }
1899  else if (nestjoinOK)
1900  {
1901  /*
1902  * Consider materializing the cheapest inner path, unless
1903  * enable_material is off or the path in question materializes its
1904  * output anyway.
1905  */
1906  if (enable_material && inner_cheapest_total != NULL &&
1907  !ExecMaterializesOutput(inner_cheapest_total->pathtype))
1908  matpath = (Path *)
1909  create_material_path(innerrel, inner_cheapest_total);
1910  }
1911 
1912  foreach(lc1, outerrel->pathlist)
1913  {
1914  Path *outerpath = (Path *) lfirst(lc1);
1915  List *merge_pathkeys;
1916 
1917  /*
1918  * We cannot use an outer path that is parameterized by the inner rel.
1919  */
1920  if (PATH_PARAM_BY_REL(outerpath, innerrel))
1921  continue;
1922 
1923  /*
1924  * If we need to unique-ify the outer path, it's pointless to consider
1925  * any but the cheapest outer. (XXX we don't consider parameterized
1926  * outers, nor inners, for unique-ified cases. Should we?)
1927  */
1928  if (save_jointype == JOIN_UNIQUE_OUTER)
1929  {
1930  if (outerpath != outerrel->cheapest_total_path)
1931  continue;
1932  outerpath = (Path *) create_unique_path(root, outerrel,
1933  outerpath, extra->sjinfo);
1934  Assert(outerpath);
1935  }
1936 
1937  /*
1938  * The result will have this sort order (even if it is implemented as
1939  * a nestloop, and even if some of the mergeclauses are implemented by
1940  * qpquals rather than as true mergeclauses):
1941  */
1942  merge_pathkeys = build_join_pathkeys(root, joinrel, jointype,
1943  outerpath->pathkeys);
1944 
1945  if (save_jointype == JOIN_UNIQUE_INNER)
1946  {
1947  /*
1948  * Consider nestloop join, but only with the unique-ified cheapest
1949  * inner path
1950  */
1952  joinrel,
1953  outerpath,
1954  inner_cheapest_total,
1955  merge_pathkeys,
1956  jointype,
1957  extra);
1958  }
1959  else if (nestjoinOK)
1960  {
1961  /*
1962  * Consider nestloop joins using this outer path and various
1963  * available paths for the inner relation. We consider the
1964  * cheapest-total paths for each available parameterization of the
1965  * inner relation, including the unparameterized case.
1966  */
1967  ListCell *lc2;
1968 
1969  foreach(lc2, innerrel->cheapest_parameterized_paths)
1970  {
1971  Path *innerpath = (Path *) lfirst(lc2);
1972  Path *mpath;
1973 
1975  joinrel,
1976  outerpath,
1977  innerpath,
1978  merge_pathkeys,
1979  jointype,
1980  extra);
1981 
1982  /*
1983  * Try generating a memoize path and see if that makes the
1984  * nested loop any cheaper.
1985  */
1986  mpath = get_memoize_path(root, innerrel, outerrel,
1987  innerpath, outerpath, jointype,
1988  extra);
1989  if (mpath != NULL)
1991  joinrel,
1992  outerpath,
1993  mpath,
1994  merge_pathkeys,
1995  jointype,
1996  extra);
1997  }
1998 
1999  /* Also consider materialized form of the cheapest inner path */
2000  if (matpath != NULL)
2002  joinrel,
2003  outerpath,
2004  matpath,
2005  merge_pathkeys,
2006  jointype,
2007  extra);
2008  }
2009 
2010  /* Can't do anything else if outer path needs to be unique'd */
2011  if (save_jointype == JOIN_UNIQUE_OUTER)
2012  continue;
2013 
2014  /* Can't do anything else if inner rel is parameterized by outer */
2015  if (inner_cheapest_total == NULL)
2016  continue;
2017 
2018  /* Generate merge join paths */
2019  generate_mergejoin_paths(root, joinrel, innerrel, outerpath,
2020  save_jointype, extra, useallclauses,
2021  inner_cheapest_total, merge_pathkeys,
2022  false);
2023  }
2024 
2025  /*
2026  * Consider partial nestloop and mergejoin plan if outerrel has any
2027  * partial path and the joinrel is parallel-safe. However, we can't
2028  * handle JOIN_UNIQUE_OUTER, because the outer path will be partial, and
2029  * therefore we won't be able to properly guarantee uniqueness. Nor can
2030  * we handle joins needing lateral rels, since partial paths must not be
2031  * parameterized. Similarly, we can't handle JOIN_FULL, JOIN_RIGHT and
2032  * JOIN_RIGHT_ANTI, because they can produce false null extended rows.
2033  */
2034  if (joinrel->consider_parallel &&
2035  save_jointype != JOIN_UNIQUE_OUTER &&
2036  save_jointype != JOIN_FULL &&
2037  save_jointype != JOIN_RIGHT &&
2038  save_jointype != JOIN_RIGHT_ANTI &&
2039  outerrel->partial_pathlist != NIL &&
2040  bms_is_empty(joinrel->lateral_relids))
2041  {
2042  if (nestjoinOK)
2043  consider_parallel_nestloop(root, joinrel, outerrel, innerrel,
2044  save_jointype, extra);
2045 
2046  /*
2047  * If inner_cheapest_total is NULL or non parallel-safe then find the
2048  * cheapest total parallel safe path. If doing JOIN_UNIQUE_INNER, we
2049  * can't use any alternative inner path.
2050  */
2051  if (inner_cheapest_total == NULL ||
2052  !inner_cheapest_total->parallel_safe)
2053  {
2054  if (save_jointype == JOIN_UNIQUE_INNER)
2055  return;
2056 
2057  inner_cheapest_total = get_cheapest_parallel_safe_total_inner(innerrel->pathlist);
2058  }
2059 
2060  if (inner_cheapest_total)
2061  consider_parallel_mergejoin(root, joinrel, outerrel, innerrel,
2062  save_jointype, extra,
2063  inner_cheapest_total);
2064  }
2065 }
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
static void try_nestloop_path(PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:826
static void consider_parallel_nestloop(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:2120
static void consider_parallel_mergejoin(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra, Path *inner_cheapest_total)
Definition: joinpath.c:2080
@ JOIN_LEFT
Definition: nodes.h:294

References Assert, bms_is_empty, build_join_pathkeys(), RelOptInfo::cheapest_parameterized_paths, RelOptInfo::cheapest_total_path, RelOptInfo::consider_parallel, consider_parallel_mergejoin(), consider_parallel_nestloop(), create_material_path(), create_unique_path(), elog, enable_material, ERROR, ExecMaterializesOutput(), generate_mergejoin_paths(), get_cheapest_parallel_safe_total_inner(), get_memoize_path(), JOIN_ANTI, JOIN_FULL, JOIN_INNER, JOIN_LEFT, JOIN_RIGHT, JOIN_RIGHT_ANTI, JOIN_RIGHT_SEMI, JOIN_SEMI, JOIN_UNIQUE_INNER, JOIN_UNIQUE_OUTER, RelOptInfo::lateral_relids, lfirst, NIL, Path::parallel_safe, RelOptInfo::partial_pathlist, PATH_PARAM_BY_REL, Path::pathkeys, RelOptInfo::pathlist, Path::pathtype, root, JoinPathExtraData::sjinfo, and try_nestloop_path().

Referenced by add_paths_to_joinrel().

◆ paraminfo_get_equal_hashops()

static bool paraminfo_get_equal_hashops ( PlannerInfo root,
ParamPathInfo param_info,
RelOptInfo outerrel,
RelOptInfo innerrel,
List ph_lateral_vars,
List **  param_exprs,
List **  operators,
bool binary_mode 
)
static

Definition at line 441 of file joinpath.c.

446 {
447  List *lateral_vars;
448  ListCell *lc;
449 
450  *param_exprs = NIL;
451  *operators = NIL;
452  *binary_mode = false;
453 
454  /* Add join clauses from param_info to the hash key */
455  if (param_info != NULL)
456  {
457  List *clauses = param_info->ppi_clauses;
458 
459  foreach(lc, clauses)
460  {
461  RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
462  OpExpr *opexpr;
463  Node *expr;
464  Oid hasheqoperator;
465 
466  opexpr = (OpExpr *) rinfo->clause;
467 
468  /*
469  * Bail if the rinfo is not compatible. We need a join OpExpr
470  * with 2 args.
471  */
472  if (!IsA(opexpr, OpExpr) || list_length(opexpr->args) != 2 ||
473  !clause_sides_match_join(rinfo, outerrel, innerrel))
474  {
475  list_free(*operators);
476  list_free(*param_exprs);
477  return false;
478  }
479 
480  if (rinfo->outer_is_left)
481  {
482  expr = (Node *) linitial(opexpr->args);
483  hasheqoperator = rinfo->left_hasheqoperator;
484  }
485  else
486  {
487  expr = (Node *) lsecond(opexpr->args);
488  hasheqoperator = rinfo->right_hasheqoperator;
489  }
490 
491  /* can't do memoize if we can't hash the outer type */
492  if (!OidIsValid(hasheqoperator))
493  {
494  list_free(*operators);
495  list_free(*param_exprs);
496  return false;
497  }
498 
499  /*
500  * 'expr' may already exist as a parameter from a previous item in
501  * ppi_clauses. No need to include it again, however we'd better
502  * ensure we do switch into binary mode if required. See below.
503  */
504  if (!list_member(*param_exprs, expr))
505  {
506  *operators = lappend_oid(*operators, hasheqoperator);
507  *param_exprs = lappend(*param_exprs, expr);
508  }
509 
510  /*
511  * When the join operator is not hashable then it's possible that
512  * the operator will be able to distinguish something that the
513  * hash equality operator could not. For example with floating
514  * point types -0.0 and +0.0 are classed as equal by the hash
515  * function and equality function, but some other operator may be
516  * able to tell those values apart. This means that we must put
517  * memoize into binary comparison mode so that it does bit-by-bit
518  * comparisons rather than a "logical" comparison as it would
519  * using the hash equality operator.
520  */
521  if (!OidIsValid(rinfo->hashjoinoperator))
522  *binary_mode = true;
523  }
524  }
525 
526  /* Now add any lateral vars to the cache key too */
527  lateral_vars = list_concat(ph_lateral_vars, innerrel->lateral_vars);
528  foreach(lc, lateral_vars)
529  {
530  Node *expr = (Node *) lfirst(lc);
531  TypeCacheEntry *typentry;
532 
533  /* Reject if there are any volatile functions in lateral vars */
534  if (contain_volatile_functions(expr))
535  {
536  list_free(*operators);
537  list_free(*param_exprs);
538  return false;
539  }
540 
541  typentry = lookup_type_cache(exprType(expr),
543 
544  /* can't use memoize without a valid hash proc and equals operator */
545  if (!OidIsValid(typentry->hash_proc) || !OidIsValid(typentry->eq_opr))
546  {
547  list_free(*operators);
548  list_free(*param_exprs);
549  return false;
550  }
551 
552  /*
553  * 'expr' may already exist as a parameter from the ppi_clauses. No
554  * need to include it again, however we'd better ensure we do switch
555  * into binary mode.
556  */
557  if (!list_member(*param_exprs, expr))
558  {
559  *operators = lappend_oid(*operators, typentry->eq_opr);
560  *param_exprs = lappend(*param_exprs, expr);
561  }
562 
563  /*
564  * We must go into binary mode as we don't have too much of an idea of
565  * how these lateral Vars are being used. See comment above when we
566  * set *binary_mode for the non-lateral Var case. This could be
567  * relaxed a bit if we had the RestrictInfos and knew the operators
568  * being used, however for cases like Vars that are arguments to
569  * functions we must operate in binary mode as we don't have
570  * visibility into what the function is doing with the Vars.
571  */
572  *binary_mode = true;
573  }
574 
575  /* We're okay to use memoize */
576  return true;
577 }
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
bool list_member(const List *list, const void *datum)
Definition: list.c:661
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define lsecond(l)
Definition: pg_list.h:183
unsigned int Oid
Definition: postgres_ext.h:31
List * args
Definition: primnodes.h:836
List * ppi_clauses
Definition: pathnodes.h:1587
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:356
#define TYPECACHE_EQ_OPR
Definition: typcache.h:137
#define TYPECACHE_HASH_PROC
Definition: typcache.h:141

References OpExpr::args, RestrictInfo::clause, clause_sides_match_join(), contain_volatile_functions(), TypeCacheEntry::eq_opr, exprType(), TypeCacheEntry::hash_proc, if(), IsA, lappend(), lappend_oid(), RelOptInfo::lateral_vars, lfirst, linitial, list_concat(), list_free(), list_length(), list_member(), lookup_type_cache(), lsecond, NIL, OidIsValid, ParamPathInfo::ppi_clauses, TYPECACHE_EQ_OPR, and TYPECACHE_HASH_PROC.

Referenced by get_memoize_path().

◆ select_mergejoin_clauses()

static List * select_mergejoin_clauses ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
List restrictlist,
JoinType  jointype,
bool mergejoin_allowed 
)
static

Definition at line 2498 of file joinpath.c.

2505 {
2506  List *result_list = NIL;
2507  bool isouterjoin = IS_OUTER_JOIN(jointype);
2508  bool have_nonmergeable_joinclause = false;
2509  ListCell *l;
2510 
2511  /*
2512  * For now we do not support RIGHT_SEMI join in mergejoin: the benefit of
2513  * swapping inputs tends to be small here.
2514  */
2515  if (jointype == JOIN_RIGHT_SEMI)
2516  {
2517  *mergejoin_allowed = false;
2518  return NIL;
2519  }
2520 
2521  foreach(l, restrictlist)
2522  {
2523  RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
2524 
2525  /*
2526  * If processing an outer join, only use its own join clauses in the
2527  * merge. For inner joins we can use pushed-down clauses too. (Note:
2528  * we don't set have_nonmergeable_joinclause here because pushed-down
2529  * clauses will become otherquals not joinquals.)
2530  */
2531  if (isouterjoin && RINFO_IS_PUSHED_DOWN(restrictinfo, joinrel->relids))
2532  continue;
2533 
2534  /* Check that clause is a mergeable operator clause */
2535  if (!restrictinfo->can_join ||
2536  restrictinfo->mergeopfamilies == NIL)
2537  {
2538  /*
2539  * The executor can handle extra joinquals that are constants, but
2540  * not anything else, when doing right/right-anti/full merge join.
2541  * (The reason to support constants is so we can do FULL JOIN ON
2542  * FALSE.)
2543  */
2544  if (!restrictinfo->clause || !IsA(restrictinfo->clause, Const))
2545  have_nonmergeable_joinclause = true;
2546  continue; /* not mergejoinable */
2547  }
2548 
2549  /*
2550  * Check if clause has the form "outer op inner" or "inner op outer".
2551  */
2552  if (!clause_sides_match_join(restrictinfo, outerrel, innerrel))
2553  {
2554  have_nonmergeable_joinclause = true;
2555  continue; /* no good for these input relations */
2556  }
2557 
2558  /*
2559  * If clause has the form "inner op outer", check if its operator has
2560  * valid commutator. This is necessary because mergejoin clauses in
2561  * this form will get commuted in createplan.c to put the outer var on
2562  * the left (see get_switched_clauses). This probably shouldn't ever
2563  * fail, since mergejoinable operators ought to have commutators, but
2564  * be paranoid.
2565  *
2566  * The clause being mergejoinable indicates that it's an OpExpr.
2567  */
2568  if (!restrictinfo->outer_is_left &&
2569  !OidIsValid(get_commutator(castNode(OpExpr, restrictinfo->clause)->opno)))
2570  {
2571  have_nonmergeable_joinclause = true;
2572  continue;
2573  }
2574 
2575  /*
2576  * Insist that each side have a non-redundant eclass. This
2577  * restriction is needed because various bits of the planner expect
2578  * that each clause in a merge be associable with some pathkey in a
2579  * canonical pathkey list, but redundant eclasses can't appear in
2580  * canonical sort orderings. (XXX it might be worth relaxing this,
2581  * but not enough time to address it for 8.3.)
2582  */
2583  update_mergeclause_eclasses(root, restrictinfo);
2584 
2585  if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) ||
2586  EC_MUST_BE_REDUNDANT(restrictinfo->right_ec))
2587  {
2588  have_nonmergeable_joinclause = true;
2589  continue; /* can't handle redundant eclasses */
2590  }
2591 
2592  result_list = lappend(result_list, restrictinfo);
2593  }
2594 
2595  /*
2596  * Report whether mergejoin is allowed (see comment at top of function).
2597  */
2598  switch (jointype)
2599  {
2600  case JOIN_RIGHT:
2601  case JOIN_RIGHT_ANTI:
2602  case JOIN_FULL:
2603  *mergejoin_allowed = !have_nonmergeable_joinclause;
2604  break;
2605  default:
2606  *mergejoin_allowed = true;
2607  break;
2608  }
2609 
2610  return result_list;
2611 }
void update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
Definition: pathkeys.c:1507
#define EC_MUST_BE_REDUNDANT(eclass)
Definition: pathnodes.h:1409

References castNode, RestrictInfo::clause, clause_sides_match_join(), EC_MUST_BE_REDUNDANT, get_commutator(), IS_OUTER_JOIN, IsA, JOIN_FULL, JOIN_RIGHT, JOIN_RIGHT_ANTI, JOIN_RIGHT_SEMI, lappend(), lfirst, NIL, OidIsValid, RelOptInfo::relids, RINFO_IS_PUSHED_DOWN, root, and update_mergeclause_eclasses().

Referenced by add_paths_to_joinrel().

◆ sort_inner_and_outer()

static void sort_inner_and_outer ( PlannerInfo root,
RelOptInfo joinrel,
RelOptInfo outerrel,
RelOptInfo innerrel,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 1366 of file joinpath.c.

1372 {
1373  JoinType save_jointype = jointype;
1374  Path *outer_path;
1375  Path *inner_path;
1376  Path *cheapest_partial_outer = NULL;
1377  Path *cheapest_safe_inner = NULL;
1378  List *all_pathkeys;
1379  ListCell *l;
1380 
1381  /* Nothing to do if there are no available mergejoin clauses */
1382  if (extra->mergeclause_list == NIL)
1383  return;
1384 
1385  /*
1386  * We only consider the cheapest-total-cost input paths, since we are
1387  * assuming here that a sort is required. We will consider
1388  * cheapest-startup-cost input paths later, and only if they don't need a
1389  * sort.
1390  *
1391  * This function intentionally does not consider parameterized input
1392  * paths, except when the cheapest-total is parameterized. If we did so,
1393  * we'd have a combinatorial explosion of mergejoin paths of dubious
1394  * value. This interacts with decisions elsewhere that also discriminate
1395  * against mergejoins with parameterized inputs; see comments in
1396  * src/backend/optimizer/README.
1397  */
1398  outer_path = outerrel->cheapest_total_path;
1399  inner_path = innerrel->cheapest_total_path;
1400 
1401  /*
1402  * If either cheapest-total path is parameterized by the other rel, we
1403  * can't use a mergejoin. (There's no use looking for alternative input
1404  * paths, since these should already be the least-parameterized available
1405  * paths.)
1406  */
1407  if (PATH_PARAM_BY_REL(outer_path, innerrel) ||
1408  PATH_PARAM_BY_REL(inner_path, outerrel))
1409  return;
1410 
1411  /*
1412  * If unique-ification is requested, do it and then handle as a plain
1413  * inner join.
1414  */
1415  if (jointype == JOIN_UNIQUE_OUTER)
1416  {
1417  outer_path = (Path *) create_unique_path(root, outerrel,
1418  outer_path, extra->sjinfo);
1419  Assert(outer_path);
1420  jointype = JOIN_INNER;
1421  }
1422  else if (jointype == JOIN_UNIQUE_INNER)
1423  {
1424  inner_path = (Path *) create_unique_path(root, innerrel,
1425  inner_path, extra->sjinfo);
1426  Assert(inner_path);
1427  jointype = JOIN_INNER;
1428  }
1429 
1430  /*
1431  * If the joinrel is parallel-safe, we may be able to consider a partial
1432  * merge join. However, we can't handle JOIN_UNIQUE_OUTER, because the
1433  * outer path will be partial, and therefore we won't be able to properly
1434  * guarantee uniqueness. Similarly, we can't handle JOIN_FULL, JOIN_RIGHT
1435  * and JOIN_RIGHT_ANTI, because they can produce false null extended rows.
1436  * Also, the resulting path must not be parameterized.
1437  */
1438  if (joinrel->consider_parallel &&
1439  save_jointype != JOIN_UNIQUE_OUTER &&
1440  save_jointype != JOIN_FULL &&
1441  save_jointype != JOIN_RIGHT &&
1442  save_jointype != JOIN_RIGHT_ANTI &&
1443  outerrel->partial_pathlist != NIL &&
1444  bms_is_empty(joinrel->lateral_relids))
1445  {
1446  cheapest_partial_outer = (Path *) linitial(outerrel->partial_pathlist);
1447 
1448  if (inner_path->parallel_safe)
1449  cheapest_safe_inner = inner_path;
1450  else if (save_jointype != JOIN_UNIQUE_INNER)
1451  cheapest_safe_inner =
1453  }
1454 
1455  /*
1456  * Each possible ordering of the available mergejoin clauses will generate
1457  * a differently-sorted result path at essentially the same cost. We have
1458  * no basis for choosing one over another at this level of joining, but
1459  * some sort orders may be more useful than others for higher-level
1460  * mergejoins, so it's worth considering multiple orderings.
1461  *
1462  * Actually, it's not quite true that every mergeclause ordering will
1463  * generate a different path order, because some of the clauses may be
1464  * partially redundant (refer to the same EquivalenceClasses). Therefore,
1465  * what we do is convert the mergeclause list to a list of canonical
1466  * pathkeys, and then consider different orderings of the pathkeys.
1467  *
1468  * Generating a path for *every* permutation of the pathkeys doesn't seem
1469  * like a winning strategy; the cost in planning time is too high. For
1470  * now, we generate one path for each pathkey, listing that pathkey first
1471  * and the rest in random order. This should allow at least a one-clause
1472  * mergejoin without re-sorting against any other possible mergejoin
1473  * partner path. But if we've not guessed the right ordering of secondary
1474  * keys, we may end up evaluating clauses as qpquals when they could have
1475  * been done as mergeclauses. (In practice, it's rare that there's more
1476  * than two or three mergeclauses, so expending a huge amount of thought
1477  * on that is probably not worth it.)
1478  *
1479  * The pathkey order returned by select_outer_pathkeys_for_merge() has
1480  * some heuristics behind it (see that function), so be sure to try it
1481  * exactly as-is as well as making variants.
1482  */
1483  all_pathkeys = select_outer_pathkeys_for_merge(root,
1484  extra->mergeclause_list,
1485  joinrel);
1486 
1487  foreach(l, all_pathkeys)
1488  {
1489  PathKey *front_pathkey = (PathKey *) lfirst(l);
1490  List *cur_mergeclauses;
1491  List *outerkeys;
1492  List *innerkeys;
1493  List *merge_pathkeys;
1494 
1495  /* Make a pathkey list with this guy first */
1496  if (l != list_head(all_pathkeys))
1497  outerkeys = lcons(front_pathkey,
1498  list_delete_nth_cell(list_copy(all_pathkeys),
1499  foreach_current_index(l)));
1500  else
1501  outerkeys = all_pathkeys; /* no work at first one... */
1502 
1503  /* Sort the mergeclauses into the corresponding ordering */
1504  cur_mergeclauses =
1506  outerkeys,
1507  extra->mergeclause_list);
1508 
1509  /* Should have used them all... */
1510  Assert(list_length(cur_mergeclauses) == list_length(extra->mergeclause_list));
1511 
1512  /* Build sort pathkeys for the inner side */
1513  innerkeys = make_inner_pathkeys_for_merge(root,
1514  cur_mergeclauses,
1515  outerkeys);
1516 
1517  /* Build pathkeys representing output sort order */
1518  merge_pathkeys = build_join_pathkeys(root, joinrel, jointype,
1519  outerkeys);
1520 
1521  /*
1522  * And now we can make the path.
1523  *
1524  * Note: it's possible that the cheapest paths will already be sorted
1525  * properly. try_mergejoin_path will detect that case and suppress an
1526  * explicit sort step, so we needn't do so here.
1527  */
1529  joinrel,
1530  outer_path,
1531  inner_path,
1532  merge_pathkeys,
1533  cur_mergeclauses,
1534  outerkeys,
1535  innerkeys,
1536  jointype,
1537  extra,
1538  false);
1539 
1540  /*
1541  * If we have partial outer and parallel safe inner path then try
1542  * partial mergejoin path.
1543  */
1544  if (cheapest_partial_outer && cheapest_safe_inner)
1546  joinrel,
1547  cheapest_partial_outer,
1548  cheapest_safe_inner,
1549  merge_pathkeys,
1550  cur_mergeclauses,
1551  outerkeys,
1552  innerkeys,
1553  jointype,
1554  extra);
1555  }
1556 }
static void try_partial_mergejoin_path(PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, List *pathkeys, List *mergeclauses, List *outersortkeys, List *innersortkeys, JoinType jointype, JoinPathExtraData *extra)
Definition: joinpath.c:1133
List * list_delete_nth_cell(List *list, int n)
Definition: list.c:767
List * lcons(void *datum, List *list)
Definition: list.c:495
List * select_outer_pathkeys_for_merge(PlannerInfo *root, List *mergeclauses, RelOptInfo *joinrel)
Definition: pathkeys.c:1656
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
static ListCell * list_head(const List *l)
Definition: pg_list.h:128

References Assert, bms_is_empty, build_join_pathkeys(), RelOptInfo::cheapest_total_path, RelOptInfo::consider_parallel, create_unique_path(), find_mergeclauses_for_outer_pathkeys(), foreach_current_index, get_cheapest_parallel_safe_total_inner(), JOIN_FULL, JOIN_INNER, JOIN_RIGHT, JOIN_RIGHT_ANTI, JOIN_UNIQUE_INNER, JOIN_UNIQUE_OUTER, RelOptInfo::lateral_relids, lcons(), lfirst, linitial, list_copy(), list_delete_nth_cell(), list_head(), list_length(), make_inner_pathkeys_for_merge(), JoinPathExtraData::mergeclause_list, NIL, Path::parallel_safe, RelOptInfo::partial_pathlist, PATH_PARAM_BY_REL, RelOptInfo::pathlist, root, select_outer_pathkeys_for_merge(), JoinPathExtraData::sjinfo, try_mergejoin_path(), and try_partial_mergejoin_path().

Referenced by add_paths_to_joinrel().

◆ try_hashjoin_path()

static void try_hashjoin_path ( PlannerInfo root,
RelOptInfo joinrel,
Path outer_path,
Path inner_path,
List hashclauses,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 1200 of file joinpath.c.

1207 {
1208  Relids required_outer;
1209  JoinCostWorkspace workspace;
1210 
1211  /*
1212  * If we are forming an outer join at this join, it's nonsensical to use
1213  * an input path that uses the outer join as part of its parameterization.
1214  * (This can happen despite our join order restrictions, since those apply
1215  * to what is in an input relation not what its parameters are.)
1216  */
1217  if (extra->sjinfo->ojrelid != 0 &&
1218  (bms_is_member(extra->sjinfo->ojrelid, PATH_REQ_OUTER(inner_path)) ||
1219  bms_is_member(extra->sjinfo->ojrelid, PATH_REQ_OUTER(outer_path))))
1220  return;
1221 
1222  /*
1223  * Check to see if proposed path is still parameterized, and reject if the
1224  * parameterization wouldn't be sensible.
1225  */
1226  required_outer = calc_non_nestloop_required_outer(outer_path,
1227  inner_path);
1228  if (required_outer &&
1229  !bms_overlap(required_outer, extra->param_source_rels))
1230  {
1231  /* Waste no memory when we reject a path here */
1232  bms_free(required_outer);
1233  return;
1234  }
1235 
1236  /*
1237  * See comments in try_nestloop_path(). Also note that hashjoin paths
1238  * never have any output pathkeys, per comments in create_hashjoin_path.
1239  */
1240  initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
1241  outer_path, inner_path, extra, false);
1242 
1243  if (add_path_precheck(joinrel, workspace.disabled_nodes,
1244  workspace.startup_cost, workspace.total_cost,
1245  NIL, required_outer))
1246  {
1247  add_path(joinrel, (Path *)
1249  joinrel,
1250  jointype,
1251  &workspace,
1252  extra,
1253  outer_path,
1254  inner_path,
1255  false, /* parallel_hash */
1256  extra->restrictlist,
1257  required_outer,
1258  hashclauses));
1259  }
1260  else
1261  {
1262  /* Waste no memory when we reject a path here */
1263  bms_free(required_outer);
1264  }
1265 }
void bms_free(Bitmapset *a)
Definition: bitmapset.c:239
void initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace, JoinType jointype, List *hashclauses, Path *outer_path, Path *inner_path, JoinPathExtraData *extra, bool parallel_hash)
Definition: costsize.c:4115
Relids calc_non_nestloop_required_outer(Path *outer_path, Path *inner_path)
Definition: pathnode.c:2483
HashPath * create_hashjoin_path(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, JoinCostWorkspace *workspace, JoinPathExtraData *extra, Path *outer_path, Path *inner_path, bool parallel_hash, List *restrict_clauses, Relids required_outer, List *hashclauses)
Definition: pathnode.c:2697
void add_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:461
bool add_path_precheck(RelOptInfo *parent_rel, int disabled_nodes, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer)
Definition: pathnode.c:688

References add_path(), add_path_precheck(), bms_free(), bms_is_member(), bms_overlap(), calc_non_nestloop_required_outer(), create_hashjoin_path(), JoinCostWorkspace::disabled_nodes, initial_cost_hashjoin(), NIL, SpecialJoinInfo::ojrelid, JoinPathExtraData::param_source_rels, PATH_REQ_OUTER, JoinPathExtraData::restrictlist, root, JoinPathExtraData::sjinfo, JoinCostWorkspace::startup_cost, and JoinCostWorkspace::total_cost.

Referenced by hash_inner_and_outer().

◆ try_mergejoin_path()

static void try_mergejoin_path ( PlannerInfo root,
RelOptInfo joinrel,
Path outer_path,
Path inner_path,
List pathkeys,
List mergeclauses,
List outersortkeys,
List innersortkeys,
JoinType  jointype,
JoinPathExtraData extra,
bool  is_partial 
)
static

Definition at line 1027 of file joinpath.c.

1038 {
1039  Relids required_outer;
1040  JoinCostWorkspace workspace;
1041 
1042  if (is_partial)
1043  {
1045  joinrel,
1046  outer_path,
1047  inner_path,
1048  pathkeys,
1049  mergeclauses,
1050  outersortkeys,
1051  innersortkeys,
1052  jointype,
1053  extra);
1054  return;
1055  }
1056 
1057  /*
1058  * If we are forming an outer join at this join, it's nonsensical to use
1059  * an input path that uses the outer join as part of its parameterization.
1060  * (This can happen despite our join order restrictions, since those apply
1061  * to what is in an input relation not what its parameters are.)
1062  */
1063  if (extra->sjinfo->ojrelid != 0 &&
1064  (bms_is_member(extra->sjinfo->ojrelid, PATH_REQ_OUTER(inner_path)) ||
1065  bms_is_member(extra->sjinfo->ojrelid, PATH_REQ_OUTER(outer_path))))
1066  return;
1067 
1068  /*
1069  * Check to see if proposed path is still parameterized, and reject if the
1070  * parameterization wouldn't be sensible.
1071  */
1072  required_outer = calc_non_nestloop_required_outer(outer_path,
1073  inner_path);
1074  if (required_outer &&
1075  !bms_overlap(required_outer, extra->param_source_rels))
1076  {
1077  /* Waste no memory when we reject a path here */
1078  bms_free(required_outer);
1079  return;
1080  }
1081 
1082  /*
1083  * If the given paths are already well enough ordered, we can skip doing
1084  * an explicit sort.
1085  */
1086  if (outersortkeys &&
1087  pathkeys_contained_in(outersortkeys, outer_path->pathkeys))
1088  outersortkeys = NIL;
1089  if (innersortkeys &&
1090  pathkeys_contained_in(innersortkeys, inner_path->pathkeys))
1091  innersortkeys = NIL;
1092 
1093  /*
1094  * See comments in try_nestloop_path().
1095  */
1096  initial_cost_mergejoin(root, &workspace, jointype, mergeclauses,
1097  outer_path, inner_path,
1098  outersortkeys, innersortkeys,
1099  extra);
1100 
1101  if (add_path_precheck(joinrel, workspace.disabled_nodes,
1102  workspace.startup_cost, workspace.total_cost,
1103  pathkeys, required_outer))
1104  {
1105  add_path(joinrel, (Path *)
1107  joinrel,
1108  jointype,
1109  &workspace,
1110  extra,
1111  outer_path,
1112  inner_path,
1113  extra->restrictlist,
1114  pathkeys,
1115  required_outer,
1116  mergeclauses,
1117  outersortkeys,
1118  innersortkeys));
1119  }
1120  else
1121  {
1122  /* Waste no memory when we reject a path here */
1123  bms_free(required_outer);
1124  }
1125 }
void initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace, JoinType jointype, List *mergeclauses, Path *outer_path, Path *inner_path, List *outersortkeys, List *innersortkeys, JoinPathExtraData *extra)
Definition: costsize.c:3551
MergePath * create_mergejoin_path(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, JoinCostWorkspace *workspace, JoinPathExtraData *extra, Path *outer_path, Path *inner_path, List *restrict_clauses, List *pathkeys, Relids required_outer, List *mergeclauses, List *outersortkeys, List *innersortkeys)
Definition: pathnode.c:2631

References add_path(), add_path_precheck(), bms_free(), bms_is_member(), bms_overlap(), calc_non_nestloop_required_outer(), create_mergejoin_path(), JoinCostWorkspace::disabled_nodes, initial_cost_mergejoin(), NIL, SpecialJoinInfo::ojrelid, JoinPathExtraData::param_source_rels, PATH_REQ_OUTER, Path::pathkeys, pathkeys_contained_in(), JoinPathExtraData::restrictlist, root, JoinPathExtraData::sjinfo, JoinCostWorkspace::startup_cost, JoinCostWorkspace::total_cost, and try_partial_mergejoin_path().

Referenced by generate_mergejoin_paths(), and sort_inner_and_outer().

◆ try_nestloop_path()

static void try_nestloop_path ( PlannerInfo root,
RelOptInfo joinrel,
Path outer_path,
Path inner_path,
List pathkeys,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 826 of file joinpath.c.

833 {
834  Relids required_outer;
835  JoinCostWorkspace workspace;
836  RelOptInfo *innerrel = inner_path->parent;
837  RelOptInfo *outerrel = outer_path->parent;
838  Relids innerrelids;
839  Relids outerrelids;
840  Relids inner_paramrels = PATH_REQ_OUTER(inner_path);
841  Relids outer_paramrels = PATH_REQ_OUTER(outer_path);
842 
843  /*
844  * If we are forming an outer join at this join, it's nonsensical to use
845  * an input path that uses the outer join as part of its parameterization.
846  * (This can happen despite our join order restrictions, since those apply
847  * to what is in an input relation not what its parameters are.)
848  */
849  if (extra->sjinfo->ojrelid != 0 &&
850  (bms_is_member(extra->sjinfo->ojrelid, inner_paramrels) ||
851  bms_is_member(extra->sjinfo->ojrelid, outer_paramrels)))
852  return;
853 
854  /*
855  * Any parameterization of the input paths refers to topmost parents of
856  * the relevant relations, because reparameterize_path_by_child() hasn't
857  * been called yet. So we must consider topmost parents of the relations
858  * being joined, too, while determining parameterization of the result and
859  * checking for disallowed parameterization cases.
860  */
861  if (innerrel->top_parent_relids)
862  innerrelids = innerrel->top_parent_relids;
863  else
864  innerrelids = innerrel->relids;
865 
866  if (outerrel->top_parent_relids)
867  outerrelids = outerrel->top_parent_relids;
868  else
869  outerrelids = outerrel->relids;
870 
871  /*
872  * Check to see if proposed path is still parameterized, and reject if the
873  * parameterization wouldn't be sensible --- unless allow_star_schema_join
874  * says to allow it anyway. Also, we must reject if have_dangerous_phv
875  * doesn't like the look of it, which could only happen if the nestloop is
876  * still parameterized.
877  */
878  required_outer = calc_nestloop_required_outer(outerrelids, outer_paramrels,
879  innerrelids, inner_paramrels);
880  if (required_outer &&
881  ((!bms_overlap(required_outer, extra->param_source_rels) &&
882  !allow_star_schema_join(root, outerrelids, inner_paramrels)) ||
883  have_dangerous_phv(root, outerrelids, inner_paramrels)))
884  {
885  /* Waste no memory when we reject a path here */
886  bms_free(required_outer);
887  return;
888  }
889 
890  /* If we got past that, we shouldn't have any unsafe outer-join refs */
891  Assert(!have_unsafe_outer_join_ref(root, outerrelids, inner_paramrels));
892 
893  /*
894  * If the inner path is parameterized, it is parameterized by the topmost
895  * parent of the outer rel, not the outer rel itself. We will need to
896  * translate the parameterization, if this path is chosen, during
897  * create_plan(). Here we just check whether we will be able to perform
898  * the translation, and if not avoid creating a nestloop path.
899  */
900  if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent) &&
901  !path_is_reparameterizable_by_child(inner_path, outer_path->parent))
902  {
903  bms_free(required_outer);
904  return;
905  }
906 
907  /*
908  * Do a precheck to quickly eliminate obviously-inferior paths. We
909  * calculate a cheap lower bound on the path's cost and then use
910  * add_path_precheck() to see if the path is clearly going to be dominated
911  * by some existing path for the joinrel. If not, do the full pushup with
912  * creating a fully valid path structure and submitting it to add_path().
913  * The latter two steps are expensive enough to make this two-phase
914  * methodology worthwhile.
915  */
916  initial_cost_nestloop(root, &workspace, jointype,
917  outer_path, inner_path, extra);
918 
919  if (add_path_precheck(joinrel, workspace.disabled_nodes,
920  workspace.startup_cost, workspace.total_cost,
921  pathkeys, required_outer))
922  {
923  add_path(joinrel, (Path *)
925  joinrel,
926  jointype,
927  &workspace,
928  extra,
929  outer_path,
930  inner_path,
931  extra->restrictlist,
932  pathkeys,
933  required_outer));
934  }
935  else
936  {
937  /* Waste no memory when we reject a path here */
938  bms_free(required_outer);
939  }
940 }
void initial_cost_nestloop(PlannerInfo *root, JoinCostWorkspace *workspace, JoinType jointype, Path *outer_path, Path *inner_path, JoinPathExtraData *extra)
Definition: costsize.c:3268
#define PATH_PARAM_BY_PARENT(path, rel)
Definition: joinpath.c:39
static bool allow_star_schema_join(PlannerInfo *root, Relids outerrelids, Relids inner_paramrels)
Definition: joinpath.c:365
bool have_dangerous_phv(PlannerInfo *root, Relids outer_relids, Relids inner_params)
Definition: joinrels.c:1307
NestPath * create_nestloop_path(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, JoinCostWorkspace *workspace, JoinPathExtraData *extra, Path *outer_path, Path *inner_path, List *restrict_clauses, List *pathkeys, Relids required_outer)
Definition: pathnode.c:2535
bool path_is_reparameterizable_by_child(Path *path, RelOptInfo *child_rel)
Definition: pathnode.c:4508
Relids calc_nestloop_required_outer(Relids outerrelids, Relids outer_paramrels, Relids innerrelids, Relids inner_paramrels)
Definition: pathnode.c:2456

References add_path(), add_path_precheck(), allow_star_schema_join(), Assert, bms_free(), bms_is_member(), bms_overlap(), calc_nestloop_required_outer(), create_nestloop_path(), JoinCostWorkspace::disabled_nodes, have_dangerous_phv(), initial_cost_nestloop(), SpecialJoinInfo::ojrelid, JoinPathExtraData::param_source_rels, path_is_reparameterizable_by_child(), PATH_PARAM_BY_PARENT, PATH_REQ_OUTER, RelOptInfo::relids, JoinPathExtraData::restrictlist, root, JoinPathExtraData::sjinfo, JoinCostWorkspace::startup_cost, RelOptInfo::top_parent_relids, and JoinCostWorkspace::total_cost.

Referenced by match_unsorted_outer().

◆ try_partial_hashjoin_path()

static void try_partial_hashjoin_path ( PlannerInfo root,
RelOptInfo joinrel,
Path outer_path,
Path inner_path,
List hashclauses,
JoinType  jointype,
JoinPathExtraData extra,
bool  parallel_hash 
)
static

Definition at line 1277 of file joinpath.c.

1285 {
1286  JoinCostWorkspace workspace;
1287 
1288  /*
1289  * If the inner path is parameterized, we can't use a partial hashjoin.
1290  * Parameterized partial paths are not supported. The caller should
1291  * already have verified that no lateral rels are required here.
1292  */
1293  Assert(bms_is_empty(joinrel->lateral_relids));
1294  Assert(bms_is_empty(PATH_REQ_OUTER(outer_path)));
1295  if (!bms_is_empty(PATH_REQ_OUTER(inner_path)))
1296  return;
1297 
1298  /*
1299  * Before creating a path, get a quick lower bound on what it is likely to
1300  * cost. Bail out right away if it looks terrible.
1301  */
1302  initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
1303  outer_path, inner_path, extra, parallel_hash);
1304  if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
1305  workspace.total_cost, NIL))
1306  return;
1307 
1308  /* Might be good enough to be worth trying, so let's try it. */
1309  add_partial_path(joinrel, (Path *)
1311  joinrel,
1312  jointype,
1313  &workspace,
1314  extra,
1315  outer_path,
1316  inner_path,
1317  parallel_hash,
1318  extra->restrictlist,
1319  NULL,
1320  hashclauses));
1321 }
void add_partial_path(RelOptInfo *parent_rel, Path *new_path)
Definition: pathnode.c:795
bool add_partial_path_precheck(RelOptInfo *parent_rel, int disabled_nodes, Cost total_cost, List *pathkeys)
Definition: pathnode.c:921

References add_partial_path(), add_partial_path_precheck(), Assert, bms_is_empty, create_hashjoin_path(), JoinCostWorkspace::disabled_nodes, initial_cost_hashjoin(), RelOptInfo::lateral_relids, NIL, PATH_REQ_OUTER, JoinPathExtraData::restrictlist, root, and JoinCostWorkspace::total_cost.

Referenced by hash_inner_and_outer().

◆ try_partial_mergejoin_path()

static void try_partial_mergejoin_path ( PlannerInfo root,
RelOptInfo joinrel,
Path outer_path,
Path inner_path,
List pathkeys,
List mergeclauses,
List outersortkeys,
List innersortkeys,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 1133 of file joinpath.c.

1143 {
1144  JoinCostWorkspace workspace;
1145 
1146  /*
1147  * See comments in try_partial_hashjoin_path().
1148  */
1149  Assert(bms_is_empty(joinrel->lateral_relids));
1150  Assert(bms_is_empty(PATH_REQ_OUTER(outer_path)));
1151  if (!bms_is_empty(PATH_REQ_OUTER(inner_path)))
1152  return;
1153 
1154  /*
1155  * If the given paths are already well enough ordered, we can skip doing
1156  * an explicit sort.
1157  */
1158  if (outersortkeys &&
1159  pathkeys_contained_in(outersortkeys, outer_path->pathkeys))
1160  outersortkeys = NIL;
1161  if (innersortkeys &&
1162  pathkeys_contained_in(innersortkeys, inner_path->pathkeys))
1163  innersortkeys = NIL;
1164 
1165  /*
1166  * See comments in try_partial_nestloop_path().
1167  */
1168  initial_cost_mergejoin(root, &workspace, jointype, mergeclauses,
1169  outer_path, inner_path,
1170  outersortkeys, innersortkeys,
1171  extra);
1172 
1173  if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
1174  workspace.total_cost, pathkeys))
1175  return;
1176 
1177  /* Might be good enough to be worth trying, so let's try it. */
1178  add_partial_path(joinrel, (Path *)
1180  joinrel,
1181  jointype,
1182  &workspace,
1183  extra,
1184  outer_path,
1185  inner_path,
1186  extra->restrictlist,
1187  pathkeys,
1188  NULL,
1189  mergeclauses,
1190  outersortkeys,
1191  innersortkeys));
1192 }

References add_partial_path(), add_partial_path_precheck(), Assert, bms_is_empty, create_mergejoin_path(), JoinCostWorkspace::disabled_nodes, initial_cost_mergejoin(), RelOptInfo::lateral_relids, NIL, PATH_REQ_OUTER, Path::pathkeys, pathkeys_contained_in(), JoinPathExtraData::restrictlist, root, and JoinCostWorkspace::total_cost.

Referenced by sort_inner_and_outer(), and try_mergejoin_path().

◆ try_partial_nestloop_path()

static void try_partial_nestloop_path ( PlannerInfo root,
RelOptInfo joinrel,
Path outer_path,
Path inner_path,
List pathkeys,
JoinType  jointype,
JoinPathExtraData extra 
)
static

Definition at line 948 of file joinpath.c.

955 {
956  JoinCostWorkspace workspace;
957 
958  /*
959  * If the inner path is parameterized, the parameterization must be fully
960  * satisfied by the proposed outer path. Parameterized partial paths are
961  * not supported. The caller should already have verified that no lateral
962  * rels are required here.
963  */
965  Assert(bms_is_empty(PATH_REQ_OUTER(outer_path)));
966  if (inner_path->param_info != NULL)
967  {
968  Relids inner_paramrels = inner_path->param_info->ppi_req_outer;
969  RelOptInfo *outerrel = outer_path->parent;
970  Relids outerrelids;
971 
972  /*
973  * The inner and outer paths are parameterized, if at all, by the top
974  * level parents, not the child relations, so we must use those relids
975  * for our parameterization tests.
976  */
977  if (outerrel->top_parent_relids)
978  outerrelids = outerrel->top_parent_relids;
979  else
980  outerrelids = outerrel->relids;
981 
982  if (!bms_is_subset(inner_paramrels, outerrelids))
983  return;
984  }
985 
986  /*
987  * If the inner path is parameterized, it is parameterized by the topmost
988  * parent of the outer rel, not the outer rel itself. We will need to
989  * translate the parameterization, if this path is chosen, during
990  * create_plan(). Here we just check whether we will be able to perform
991  * the translation, and if not avoid creating a nestloop path.
992  */
993  if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent) &&
994  !path_is_reparameterizable_by_child(inner_path, outer_path->parent))
995  return;
996 
997  /*
998  * Before creating a path, get a quick lower bound on what it is likely to
999  * cost. Bail out right away if it looks terrible.
1000  */
1001  initial_cost_nestloop(root, &workspace, jointype,
1002  outer_path, inner_path, extra);
1003  if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
1004  workspace.total_cost, pathkeys))
1005  return;
1006 
1007  /* Might be good enough to be worth trying, so let's try it. */
1008  add_partial_path(joinrel, (Path *)
1010  joinrel,
1011  jointype,
1012  &workspace,
1013  extra,
1014  outer_path,
1015  inner_path,
1016  extra->restrictlist,
1017  pathkeys,
1018  NULL));
1019 }

References add_partial_path(), add_partial_path_precheck(), Assert, bms_is_empty, bms_is_subset(), create_nestloop_path(), JoinCostWorkspace::disabled_nodes, initial_cost_nestloop(), RelOptInfo::lateral_relids, path_is_reparameterizable_by_child(), PATH_PARAM_BY_PARENT, PATH_REQ_OUTER, RelOptInfo::relids, JoinPathExtraData::restrictlist, root, RelOptInfo::top_parent_relids, and JoinCostWorkspace::total_cost.

Referenced by consider_parallel_nestloop().

Variable Documentation

◆ set_join_pathlist_hook

set_join_pathlist_hook_type set_join_pathlist_hook = NULL

Definition at line 32 of file joinpath.c.

Referenced by add_paths_to_joinrel().