} Clump;
static List *merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump,
- int num_gene, bool force);
+ int num_gene, bool force, unsigned jsa_mask);
static bool desirable_join(PlannerInfo *root,
RelOptInfo *outer_rel, RelOptInfo *inner_rel);
* returns DBL_MAX.
*/
Cost
-geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
+geqo_eval(PlannerInfo *root, Gene *tour, int num_gene, unsigned jsa_mask)
{
MemoryContext mycontext;
MemoryContext oldcxt;
root->join_rel_hash = NULL;
/* construct the best path for the given combination of relations */
- joinrel = gimme_tree(root, tour, num_gene);
+ joinrel = gimme_tree(root, tour, num_gene, jsa_mask);
/*
* compute fitness, if we found a valid join
* since there's no provision for un-clumping, this must lead to failure.)
*/
RelOptInfo *
-gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
+gimme_tree(PlannerInfo *root, Gene *tour, int num_gene, unsigned jsa_mask)
{
GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
List *clumps;
cur_clump->size = 1;
/* Merge it into the clumps list, using only desirable joins */
- clumps = merge_clump(root, clumps, cur_clump, num_gene, false);
+ clumps = merge_clump(root, clumps, cur_clump, num_gene, false,
+ jsa_mask);
}
if (list_length(clumps) > 1)
{
Clump *clump = (Clump *) lfirst(lc);
- fclumps = merge_clump(root, fclumps, clump, num_gene, true);
+ fclumps = merge_clump(root, fclumps, clump, num_gene, true,
+ jsa_mask);
}
clumps = fclumps;
}
*/
static List *
merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, int num_gene,
- bool force)
+ bool force, unsigned jsa_mask)
{
ListCell *lc;
int pos;
*/
joinrel = make_join_rel(root,
old_clump->joinrel,
- new_clump->joinrel);
+ new_clump->joinrel,
+ jsa_mask);
/* Keep searching if join order is not valid */
if (joinrel)
* others. When no further merge is possible, we'll reinsert
* it into the list.
*/
- return merge_clump(root, clumps, old_clump, num_gene, force);
+ return merge_clump(root, clumps, old_clump, num_gene, force,
+ jsa_mask);
}
}
}
*/
RelOptInfo *
-geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
+geqo(PlannerInfo *root, int number_of_rels, List *initial_rels,
+ unsigned jsa_mask)
{
GeqoPrivateData private;
int generation;
pool = alloc_pool(root, pool_size, number_of_rels);
/* random initialization of the pool */
- random_init_pool(root, pool);
+ random_init_pool(root, pool, jsa_mask);
/* sort the pool according to cheapest path as fitness */
sort_pool(root, pool); /* we have to do it only one time, since all
/* EVALUATE FITNESS */
- kid->worth = geqo_eval(root, kid->string, pool->string_length);
+ kid->worth = geqo_eval(root, kid->string, pool->string_length,
+ jsa_mask);
/* push the kid into the wilderness of life according to its worth */
spread_chromo(root, kid, pool);
*/
best_tour = (Gene *) pool->data[0].string;
- best_rel = gimme_tree(root, best_tour, pool->string_length);
+ best_rel = gimme_tree(root, best_tour, pool->string_length, jsa_mask);
if (best_rel == NULL)
elog(ERROR, "geqo failed to make a valid plan");
* initialize genetic pool
*/
void
-random_init_pool(PlannerInfo *root, Pool *pool)
+random_init_pool(PlannerInfo *root, Pool *pool, unsigned jsa_mask)
{
Chromosome *chromo = (Chromosome *) pool->data;
int i;
{
init_tour(root, chromo[i].string, pool->string_length);
pool->data[i].worth = geqo_eval(root, chromo[i].string,
- pool->string_length);
+ pool->string_length, jsa_mask);
if (pool->data[i].worth < DBL_MAX)
i++;
else
}
else
{
+ unsigned jsa_mask;
+
+ /* Compute the initial join strategy advice mask. */
+ jsa_mask = JSA_FOREIGN;
+ if (enable_hashjoin)
+ jsa_mask |= JSA_HASHJOIN;
+ if (enable_material)
+ jsa_mask |= JSA_NESTLOOP_MATERIALIZE;
+ if (enable_mergejoin)
+ jsa_mask |= JSA_MERGEJOIN;
+ if (enable_nestloop)
+ jsa_mask |= JSA_NESTLOOP;
+ if (enable_partitionwise_join)
+ jsa_mask |= JSA_PARTITIONWISE;
+
/*
* Consider the different orders in which we could join the rels,
* using a plugin, GEQO, or the regular join search code.
root->initial_rels = initial_rels;
if (join_search_hook)
- return (*join_search_hook) (root, levels_needed, initial_rels);
+ return (*join_search_hook) (root, levels_needed, initial_rels,
+ jsa_mask);
else if (enable_geqo && levels_needed >= geqo_threshold)
- return geqo(root, levels_needed, initial_rels);
+ return geqo(root, levels_needed, initial_rels, jsa_mask);
else
- return standard_join_search(root, levels_needed, initial_rels);
+ return standard_join_search(root, levels_needed, initial_rels,
+ jsa_mask);
}
}
* original states of those data structures. See geqo_eval() for an example.
*/
RelOptInfo *
-standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
+standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels,
+ unsigned jsa_mask)
{
int lev;
RelOptInfo *rel;
* level, and build paths for making each one from every available
* pair of lower-level relations.
*/
- join_search_one_level(root, lev);
+ join_search_one_level(root, lev, jsa_mask);
/*
* Run generate_partitionwise_join_paths() and
Cost inner_rescan_run_cost;
/* Count up disabled nodes. */
- disabled_nodes = enable_nestloop ? 0 : 1;
+ disabled_nodes = (extra->jsa_mask & JSA_NESTLOOP) == 0 ? 1 : 0;
disabled_nodes += inner_path->disabled_nodes;
disabled_nodes += outer_path->disabled_nodes;
Assert(outerstartsel <= outerendsel);
Assert(innerstartsel <= innerendsel);
- disabled_nodes = enable_mergejoin ? 0 : 1;
+ disabled_nodes = (extra->jsa_mask & JSA_MERGEJOIN) == 0 ? 1 : 0;
/* cost of source data */
size_t space_allowed; /* unused */
/* Count up disabled nodes. */
- disabled_nodes = enable_hashjoin ? 0 : 1;
+ disabled_nodes = (extra->jsa_mask & JSA_HASHJOIN) == 0 ? 1 : 0;
disabled_nodes += inner_path->disabled_nodes;
disabled_nodes += outer_path->disabled_nodes;
#include "optimizer/planmain.h"
#include "utils/typcache.h"
-/* Hook for plugins to get control in add_paths_to_joinrel() */
+/* Hooks for plugins to get control in add_paths_to_joinrel() */
set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
+join_path_setup_hook_type join_path_setup_hook = NULL;
/*
* Paths parameterized by a parent rel can be considered to be parameterized
RelOptInfo *innerrel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
- List *restrictlist)
+ List *restrictlist,
+ unsigned jsa_mask)
{
JoinPathExtraData extra;
bool mergejoin_allowed = true;
extra.mergeclause_list = NIL;
extra.sjinfo = sjinfo;
extra.param_source_rels = NULL;
+ extra.jsa_mask = jsa_mask;
/*
* See if the inner relation is provably unique for this outer rel.
break;
}
+ /*
+ * Give extensions a chance to take control. In particular, an extension
+ * might want to modify extra.jsa_mask so as to provide join strategy
+ * advice. An extension can also override jsa_mask on a query-wide basis
+ * by using join_search_hook, but extensions that want to provide
+ * different advice when joining different rels, or even different advice
+ * for the same joinrel based on the choice of innerrel and outerrel, need
+ * to use this hook.
+ *
+ * A very simple way for an extension to use this hook is to set
+ * extra.jsa_mask = 0, if it simply doesn't want any of the paths
+ * generated by this call to add_paths_to_joinrel() to be selected. An
+ * extension could use this technique to constrain the join order, since
+ * it could thereby arrange to reject all paths from join orders that it
+ * does not like. An extension can also selectively clear bits from
+ * extra.jsa_mask to rule out specific techniques for specific joins, or
+ * even replace the mask entirely.
+ *
+ * NB: Below this point, this function should be careful to reference only
+ * extra.jsa_mask, and not jsa_mask directly, to avoid disregarding any
+ * changes made by the hook we're about to call.
+ */
+ if (join_path_setup_hook)
+ join_path_setup_hook(root, joinrel, outerrel, innerrel,
+ jointype, &extra);
+
/*
* Find potential mergejoin clauses. We can skip this if we are not
* interested in doing a mergejoin. However, mergejoin may be our only
- * way of implementing a full outer join, so override enable_mergejoin if
- * it's a full join.
+ * way of implementing a full outer join, so in that case we don't care
+ * whether mergejoins are disabled.
*/
- if (enable_mergejoin || jointype == JOIN_FULL)
+ if ((extra.jsa_mask & JSA_MERGEJOIN) != 0 || jointype == JOIN_FULL)
extra.mergeclause_list = select_mergejoin_clauses(root,
joinrel,
outerrel,
/*
* 4. Consider paths where both outer and inner relations must be hashed
- * before being joined. As above, disregard enable_hashjoin for full
- * joins, because there may be no other alternative.
+ * before being joined. As above, when it's a full join, we must try this
+ * even when the path type is disabled, because it may be our only option.
*/
- if (enable_hashjoin || jointype == JOIN_FULL)
+ if ((extra.jsa_mask & JSA_HASHJOIN) != 0 || jointype == JOIN_FULL)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
jointype, &extra);
* to the same server and assigned to the same user to check access
* permissions as, give the FDW a chance to push down joins.
*/
- if (joinrel->fdwroutine &&
+ if ((extra.jsa_mask & JSA_FOREIGN) != 0 && joinrel->fdwroutine &&
joinrel->fdwroutine->GetForeignJoinPaths)
joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel,
outerrel, innerrel,
/*
* 6. Finally, give extensions a chance to manipulate the path list. They
* could add new paths (such as CustomPaths) by calling add_path(), or
- * add_partial_path() if parallel aware. They could also delete or modify
- * paths added by the core code.
+ * add_partial_path() if parallel aware.
+ *
+ * In theory, extensions could also use this hook to delete or modify
+ * paths added by the core code, but in practice this is difficult to make
+ * work, since it's too late to get back any paths that have already been
+ * discarded by add_path() or add_partial_path(). If you're trying to
+ * suppress paths, consider using join_path_setup_hook instead.
*/
if (set_join_pathlist_hook)
set_join_pathlist_hook(root, joinrel, outerrel, innerrel,
if (inner_cheapest_total == NULL)
return;
inner_cheapest_total = (Path *)
- create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo);
+ create_unique_path(root, innerrel, inner_cheapest_total,
+ extra->sjinfo);
Assert(inner_cheapest_total);
}
else if (nestjoinOK)
{
/*
- * Consider materializing the cheapest inner path, unless
- * enable_material is off or the path in question materializes its
- * output anyway.
+ * Consider materializing the cheapest inner path, unless that is
+ * disabled or the path in question materializes its output anyway.
*/
- if (enable_material && inner_cheapest_total != NULL &&
+ if ((extra->jsa_mask & JSA_NESTLOOP_MATERIALIZE) != 0 &&
+ inner_cheapest_total != NULL &&
!ExecMaterializesOutput(inner_cheapest_total->pathtype))
+ {
matpath = (Path *)
create_material_path(innerrel, inner_cheapest_total);
+
+ /*
+ * Ignore create_material_path()'s idea of whether the path is
+ * disabled in general; JSA_NESTLOOP_MATERIALIZE means it is
+ * acceptable here.
+ */
+ matpath->disabled_nodes = inner_cheapest_total->disabled_nodes;
+ }
}
foreach(lc1, outerrel->pathlist)
/*
* Consider materializing the cheapest inner path, unless: 1) we're doing
* JOIN_UNIQUE_INNER, because in this case we have to unique-ify the
- * cheapest inner path, 2) enable_material is off, 3) the cheapest inner
- * path is not parallel-safe, 4) the cheapest inner path is parameterized
- * by the outer rel, or 5) the cheapest inner path materializes its output
- * anyway.
+ * cheapest inner path, 2) materialization is disabled here, 3) the
+ * cheapest inner path is not parallel-safe, 4) the cheapest inner path is
+ * parameterized by the outer rel, or 5) the cheapest inner path
+ * materializes its output anyway.
*/
if (save_jointype != JOIN_UNIQUE_INNER &&
- enable_material && inner_cheapest_total->parallel_safe &&
+ (extra->jsa_mask & JSA_NESTLOOP_MATERIALIZE) != 0 &&
+ inner_cheapest_total->parallel_safe &&
!PATH_PARAM_BY_REL(inner_cheapest_total, outerrel) &&
!ExecMaterializesOutput(inner_cheapest_total->pathtype))
{
matpath = (Path *)
create_material_path(innerrel, inner_cheapest_total);
Assert(matpath->parallel_safe);
+
+ /*
+ * Ignore create_material_path()'s idea of whether the path is
+ * disabled in general; JSA_NESTLOOP_MATERIALIZE means it is
+ * acceptable here.
+ */
+ matpath->disabled_nodes = inner_cheapest_total->disabled_nodes;
}
foreach(lc1, outerrel->partial_pathlist)
static void make_rels_by_clause_joins(PlannerInfo *root,
RelOptInfo *old_rel,
List *other_rels,
- int first_rel_idx);
+ int first_rel_idx,
+ unsigned jsa_mask);
static void make_rels_by_clauseless_joins(PlannerInfo *root,
RelOptInfo *old_rel,
- List *other_rels);
+ List *other_rels,
+ unsigned jsa_mask);
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
static bool restriction_is_constant_false(List *restrictlist,
bool only_pushed_down);
static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
RelOptInfo *rel2, RelOptInfo *joinrel,
- SpecialJoinInfo *sjinfo, List *restrictlist);
+ SpecialJoinInfo *sjinfo,
+ List *restrictlist, unsigned jsa_mask);
static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1,
RelOptInfo *rel2, RelOptInfo *joinrel,
SpecialJoinInfo *parent_sjinfo,
- List *parent_restrictlist);
+ List *parent_restrictlist,
+ unsigned jsa_mask);
static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root,
SpecialJoinInfo *parent_sjinfo,
Relids left_relids, Relids right_relids);
* The result is returned in root->join_rel_level[level].
*/
void
-join_search_one_level(PlannerInfo *root, int level)
+join_search_one_level(PlannerInfo *root, int level, unsigned jsa_mask)
{
List **joinrels = root->join_rel_level;
ListCell *r;
else
first_rel = 0;
- make_rels_by_clause_joins(root, old_rel, joinrels[1], first_rel);
+ make_rels_by_clause_joins(root, old_rel, joinrels[1], first_rel,
+ jsa_mask);
}
else
{
*/
make_rels_by_clauseless_joins(root,
old_rel,
- joinrels[1]);
+ joinrels[1],
+ jsa_mask);
}
}
if (have_relevant_joinclause(root, old_rel, new_rel) ||
have_join_order_restriction(root, old_rel, new_rel))
{
- (void) make_join_rel(root, old_rel, new_rel);
+ (void) make_join_rel(root, old_rel, new_rel, jsa_mask);
}
}
}
make_rels_by_clauseless_joins(root,
old_rel,
- joinrels[1]);
+ joinrels[1],
+ jsa_mask);
}
/*----------
make_rels_by_clause_joins(PlannerInfo *root,
RelOptInfo *old_rel,
List *other_rels,
- int first_rel_idx)
+ int first_rel_idx,
+ unsigned jsa_mask)
{
ListCell *l;
(have_relevant_joinclause(root, old_rel, other_rel) ||
have_join_order_restriction(root, old_rel, other_rel)))
{
- (void) make_join_rel(root, old_rel, other_rel);
+ (void) make_join_rel(root, old_rel, other_rel, jsa_mask);
}
}
}
static void
make_rels_by_clauseless_joins(PlannerInfo *root,
RelOptInfo *old_rel,
- List *other_rels)
+ List *other_rels,
+ unsigned jsa_mask)
{
ListCell *l;
if (!bms_overlap(other_rel->relids, old_rel->relids))
{
- (void) make_join_rel(root, old_rel, other_rel);
+ (void) make_join_rel(root, old_rel, other_rel, jsa_mask);
}
}
}
* turned into joins.
*/
RelOptInfo *
-make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
+make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
+ unsigned jsa_mask)
{
Relids joinrelids;
SpecialJoinInfo *sjinfo;
*/
joinrel = build_join_rel(root, joinrelids, rel1, rel2,
sjinfo, pushed_down_joins,
- &restrictlist);
+ &restrictlist, jsa_mask);
/*
* If we've already proven this join is empty, we needn't consider any
/* Add paths to the join relation. */
populate_joinrel_with_paths(root, rel1, rel2, joinrel, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
bms_free(joinrelids);
static void
populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1,
RelOptInfo *rel2, RelOptInfo *joinrel,
- SpecialJoinInfo *sjinfo, List *restrictlist)
+ SpecialJoinInfo *sjinfo, List *restrictlist,
+ unsigned jsa_mask)
{
/*
* Consider paths using each rel as both outer and inner. Depending on
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_INNER, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_INNER, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
break;
case JOIN_LEFT:
if (is_dummy_rel(rel1) ||
mark_dummy_rel(rel2);
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_LEFT, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_RIGHT, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
break;
case JOIN_FULL:
if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) ||
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_FULL, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_FULL, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
/*
* If there are join quals that aren't mergeable or hashable, we
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_SEMI, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_RIGHT_SEMI, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
}
/*
}
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_UNIQUE_INNER, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_UNIQUE_OUTER, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
}
break;
case JOIN_ANTI:
mark_dummy_rel(rel2);
add_paths_to_joinrel(root, joinrel, rel1, rel2,
JOIN_ANTI, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
add_paths_to_joinrel(root, joinrel, rel2, rel1,
JOIN_RIGHT_ANTI, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
break;
default:
/* other values not expected here */
}
/* Apply partitionwise join technique, if possible. */
- try_partitionwise_join(root, rel1, rel2, joinrel, sjinfo, restrictlist);
+ try_partitionwise_join(root, rel1, rel2, joinrel, sjinfo, restrictlist,
+ jsa_mask);
}
static void
try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2,
RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo,
- List *parent_restrictlist)
+ List *parent_restrictlist, unsigned jsa_mask)
{
bool rel1_is_simple = IS_SIMPLE_REL(rel1);
bool rel2_is_simple = IS_SIMPLE_REL(rel2);
{
child_joinrel = build_child_join_rel(root, child_rel1, child_rel2,
joinrel, child_restrictlist,
- child_sjinfo, nappinfos, appinfos);
+ child_sjinfo, nappinfos,
+ appinfos, jsa_mask);
joinrel->part_rels[cnt_parts] = child_joinrel;
joinrel->live_parts = bms_add_member(joinrel->live_parts, cnt_parts);
joinrel->all_partrels = bms_add_members(joinrel->all_partrels,
/* And make paths for the child join */
populate_joinrel_with_paths(root, child_rel1, child_rel2,
child_joinrel, child_sjinfo,
- child_restrictlist);
+ child_restrictlist, jsa_mask);
/*
* When there are thousands of partitions involved, this loop will
RelOptInfo *joinrel,
RelOptInfo *outer_rel, RelOptInfo *inner_rel,
SpecialJoinInfo *sjinfo,
- List *restrictlist);
+ List *restrictlist,
+ unsigned jsa_mask);
static bool have_partkey_equi_join(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *rel1, RelOptInfo *rel2,
JoinType jointype, List *restrictlist);
RelOptInfo *inner_rel,
SpecialJoinInfo *sjinfo,
List *pushed_down_joins,
- List **restrictlist_ptr)
+ List **restrictlist_ptr,
+ unsigned jsa_mask)
{
RelOptInfo *joinrel;
List *restrictlist;
/* Store the partition information. */
build_joinrel_partition_info(root, joinrel, outer_rel, inner_rel, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
/*
* Set estimates of the joinrel's size.
build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
RelOptInfo *inner_rel, RelOptInfo *parent_joinrel,
List *restrictlist, SpecialJoinInfo *sjinfo,
- int nappinfos, AppendRelInfo **appinfos)
+ int nappinfos, AppendRelInfo **appinfos,
+ unsigned jsa_mask)
{
RelOptInfo *joinrel = makeNode(RelOptInfo);
/* Is the join between partitions itself partitioned? */
build_joinrel_partition_info(root, joinrel, outer_rel, inner_rel, sjinfo,
- restrictlist);
+ restrictlist, jsa_mask);
/* Child joinrel is parallel safe if parent is parallel safe. */
joinrel->consider_parallel = parent_joinrel->consider_parallel;
build_joinrel_partition_info(PlannerInfo *root,
RelOptInfo *joinrel, RelOptInfo *outer_rel,
RelOptInfo *inner_rel, SpecialJoinInfo *sjinfo,
- List *restrictlist)
+ List *restrictlist, unsigned jsa_mask)
{
PartitionScheme part_scheme;
/* Nothing to do if partitionwise join technique is disabled. */
- if (!enable_partitionwise_join)
+ if ((jsa_mask & JSA_PARTITIONWISE) == 0)
{
Assert(!IS_PARTITIONED_REL(joinrel));
return;
* sjinfo is extra info about special joins for selectivity estimation
* semifactors is as shown above (only valid for SEMI/ANTI/inner_unique joins)
* param_source_rels are OK targets for parameterization of result paths
+ * jsa_mask is a bitmask of JSA_* constants to direct the join strategy
*/
typedef struct JoinPathExtraData
{
SpecialJoinInfo *sjinfo;
SemiAntiJoinFactors semifactors;
Relids param_source_rels;
+ unsigned jsa_mask;
} JoinPathExtraData;
/*
/* routines in geqo_main.c */
extern RelOptInfo *geqo(PlannerInfo *root,
- int number_of_rels, List *initial_rels);
+ int number_of_rels, List *initial_rels,
+ unsigned jsa_mask);
/* routines in geqo_eval.c */
-extern Cost geqo_eval(PlannerInfo *root, Gene *tour, int num_gene);
-extern RelOptInfo *gimme_tree(PlannerInfo *root, Gene *tour, int num_gene);
+extern Cost geqo_eval(PlannerInfo *root, Gene *tour, int num_gene,
+ unsigned jsa_mask);
+extern RelOptInfo *gimme_tree(PlannerInfo *root, Gene *tour, int num_gene,
+ unsigned jsa_mask);
#endif /* GEQO_H */
extern Pool *alloc_pool(PlannerInfo *root, int pool_size, int string_length);
extern void free_pool(PlannerInfo *root, Pool *pool);
-extern void random_init_pool(PlannerInfo *root, Pool *pool);
+extern void random_init_pool(PlannerInfo *root, Pool *pool, unsigned jsa_mask);
extern Chromosome *alloc_chromo(PlannerInfo *root, int string_length);
extern void free_chromo(PlannerInfo *root, Chromosome *chromo);
RelOptInfo *inner_rel,
SpecialJoinInfo *sjinfo,
List *pushed_down_joins,
- List **restrictlist_ptr);
+ List **restrictlist_ptr,
+ unsigned jsa_mask);
extern Relids min_join_parameterization(PlannerInfo *root,
Relids joinrelids,
RelOptInfo *outer_rel,
RelOptInfo *outer_rel, RelOptInfo *inner_rel,
RelOptInfo *parent_joinrel, List *restrictlist,
SpecialJoinInfo *sjinfo,
- int nappinfos, AppendRelInfo **appinfos);
+ int nappinfos, AppendRelInfo **appinfos,
+ unsigned jsa_mask);
#endif /* PATHNODE_H */
#include "nodes/pathnodes.h"
+/*
+ * Join strategy advice.
+ *
+ * Paths that don't match the join strategy advice will be either be disabled
+ * or will not be generated in the first place. It's only permissible to skip
+ * generating a path if doing so can't result in planner failure. The initial
+ * mask is computed on the basis of the various enable_* GUCs, and can be
+ * overriden by hooks.
+ *
+ * JSA_FOREIGN means that it's OK for a FDW to produce join paths.
+ *
+ * JSA_MERGEJOIN means that a mergejoin is OK.
+ *
+ * JSA_NESTLOOP means that a nested loop is OK in general.
+ *
+ * JSA_NESTLOOP_MATERIALIZE and JSA_NESTLOOP_NO_MATERIALIZE mean that it's
+ * OK for a nested loop to materialize or not materialize the inner side.
+ * JSA_NESTLOOP_MEMOIZE and JSA_NESTLOOP_NO_MEMOIZE mean that it's OK for
+ * a nested loop to memoize or not memoize the inner side.
+ *
+ * JSA_HASHJOIN means that a hash join is OK.
+ *
+ * JSA_PARTITIONWISE means that a partitionwise join is OK.
+ */
+#define JSA_FOREIGN 0x0001
+#define JSA_MERGEJOIN 0x0002
+#define JSA_NESTLOOP 0x0004
+#define JSA_NESTLOOP_MATERIALIZE 0x0008
+#define JSA_NESTLOOP_NO_MATERIALIZE 0x0010
+#define JSA_NESTLOOP_MEMOIZE 0x0020
+#define JSA_NESTLOOP_NO_MEMOIZE 0x0040
+#define JSA_HASHJOIN 0x0080
+#define JSA_PARTITIONWISE 0x0100
/*
* allpaths.c
*/
+
extern PGDLLIMPORT bool enable_geqo;
extern PGDLLIMPORT int geqo_threshold;
extern PGDLLIMPORT int min_parallel_table_scan_size;
RangeTblEntry *rte);
extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook;
-/* Hook for plugins to get control in add_paths_to_joinrel() */
+/* Hooks for plugins to get control in add_paths_to_joinrel() */
+typedef void (*join_path_setup_hook_type) (PlannerInfo *root,
+ RelOptInfo *joinrel,
+ RelOptInfo *outerrel,
+ RelOptInfo *innerrel,
+ JoinType jointype,
+ JoinPathExtraData *extra);
+extern PGDLLIMPORT join_path_setup_hook_type join_path_setup_hook;
typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
/* Hook for plugins to replace standard_join_search() */
typedef RelOptInfo *(*join_search_hook_type) (PlannerInfo *root,
int levels_needed,
- List *initial_rels);
+ List *initial_rels,
+ unsigned jsa_mask);
extern PGDLLIMPORT join_search_hook_type join_search_hook;
extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist);
extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
- List *initial_rels);
+ List *initial_rels, unsigned jsa_mask);
extern void generate_gather_paths(PlannerInfo *root, RelOptInfo *rel,
bool override_rows);
extern void add_paths_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinType jointype, SpecialJoinInfo *sjinfo,
- List *restrictlist);
+ List *restrictlist, unsigned jsa_mask);
/*
* joinrels.c
* routines to determine which relations to join
*/
-extern void join_search_one_level(PlannerInfo *root, int level);
+extern void join_search_one_level(PlannerInfo *root, int level,
+ unsigned jsa_mask);
extern RelOptInfo *make_join_rel(PlannerInfo *root,
- RelOptInfo *rel1, RelOptInfo *rel2);
+ RelOptInfo *rel1, RelOptInfo *rel2,
+ unsigned jsa_mask);
extern Relids add_outer_joins_to_relids(PlannerInfo *root, Relids input_relids,
SpecialJoinInfo *sjinfo,
List **pushed_down_joins);