* src/backend/optimizer/path/allpaths.c
*
* static functions:
- * set_plain_rel_pathlist()
+ * set_plain_rel_pathlist()
+ * create_plain_partial_paths()
* set_append_rel_pathlist()
+ * add_paths_to_append_rel()
* generate_mergeappend_paths()
* get_cheapest_parameterized_child_path()
* accumulate_append_subpath()
create_tidscan_paths(root, rel);
}
+
+/*
+ * create_plain_partial_paths
+ * Build partial access paths for parallel scan of a plain relation
+ */
+static void
+create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel)
+{
+ int parallel_workers;
+
+ parallel_workers = compute_parallel_worker(rel, rel->pages, -1);
+
+ /* If any limit was set to zero, the user doesn't want a parallel scan. */
+ if (parallel_workers <= 0)
+ return;
+
+ /* Add an unordered partial path based on a parallel sequential scan. */
+ add_partial_path(rel, create_seqscan_path(root, rel, NULL, parallel_workers));
+}
+
+
/*
* set_append_rel_pathlist
* Build access paths for an "append relation"
{
int parentRTindex = rti;
List *live_childrels = NIL;
- List *subpaths = NIL;
- bool subpaths_valid = true;
- List *partial_subpaths = NIL;
- bool partial_subpaths_valid = true;
- List *all_child_pathkeys = NIL;
- List *all_child_outers = NIL;
ListCell *l;
/*
* Generate access paths for each member relation, and remember the
- * cheapest path for each one. Also, identify all pathkeys (orderings)
- * and parameterizations (required_outer sets) available for the member
- * relations.
+ * non-dummy children.
*/
foreach(l, root->append_rel_list)
{
int childRTindex;
RangeTblEntry *childRTE;
RelOptInfo *childrel;
- ListCell *lcp;
/* append_rel_list contains all append rels; ignore others */
if (appinfo->parent_relid != parentRTindex)
* Child is live, so add it to the live_childrels list for use below.
*/
live_childrels = lappend(live_childrels, childrel);
+ }
+
+ /* Add paths to the "append" relation. */
+ add_paths_to_append_rel(root, rel, live_childrels);
+}
+
+/*
+ * add_paths_to_append_rel
+ * Generate paths for given "append" relation given the set of non-dummy
+ * child rels.
+ *
+ * The function collects all parameterizations and orderings supported by the
+ * non-dummy children. For every such parameterization or ordering, it creates
+ * an append path collecting one path from each non-dummy child with given
+ * parameterization or ordering. Similarly it collects partial paths from
+ * non-dummy children to create partial append paths.
+ */
+static void
+add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
+ List *live_childrels)
+{
+ List *subpaths = NIL;
+ bool subpaths_valid = true;
+ List *partial_subpaths = NIL;
+ bool partial_subpaths_valid = true;
+ List *all_child_pathkeys = NIL;
+ List *all_child_outers = NIL;
+ ListCell *l;
+ List *partitioned_rels = NIL;
+ RangeTblEntry *rte;
+ bool build_partitioned_rels = false;
+
+ /*
+ * A plain relation will already have a PartitionedChildRelInfo if it is
+ * partitioned. For a subquery RTE, no PartitionedChildRelInfo exists; we
+ * collect all partitioned_rels associated with any child. (This assumes
+ * that we don't need to look through multiple levels of subquery RTEs; if
+ * we ever do, we could create a PartitionedChildRelInfo with the
+ * accumulated list of partitioned_rels which would then be found when
+ * populated our parent rel with paths. For the present, that appears to
+ * be unnecessary.)
+ */
+ rte = planner_rt_fetch(rel->relid, root);
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ if (rte->relkind == RELKIND_PARTITIONED_TABLE)
+ {
+ partitioned_rels =
+ get_partitioned_child_rels(root, rel->relid);
+ Assert(list_length(partitioned_rels) >= 1);
+ }
+ break;
+ case RTE_SUBQUERY:
+ build_partitioned_rels = true;
+ break;
+ default:
+ elog(ERROR, "unexpected rtekind: %d", (int) rte->rtekind);
+ }
+
+ /*
+ * For every non-dummy child, remember the cheapest path. Also, identify
+ * all pathkeys (orderings) and parameterizations (required_outer sets)
+ * available for the non-dummy member relations.
+ */
+ foreach(l, live_childrels)
+ {
+ RelOptInfo *childrel = lfirst(l);
+ ListCell *lcp;
+
+ /*
+ * If we need to build partitioned_rels, accumulate the partitioned
+ * rels for this child.
+ */
+ if (build_partitioned_rels)
+ {
+ List *cprels;
+
+ cprels = get_partitioned_child_rels(root, childrel->relid);
+ partitioned_rels = list_concat(partitioned_rels,
+ list_copy(cprels));
+ }
/*
* If child has an unparameterized cheapest-total path, add that to
*/
if (childrel->cheapest_total_path->param_info == NULL)
subpaths = accumulate_append_subpath(subpaths,
- childrel->cheapest_total_path);
+ childrel->cheapest_total_path);
else
subpaths_valid = false;
/* Same idea, but for a partial plan. */
if (childrel->partial_pathlist != NIL)
partial_subpaths = accumulate_append_subpath(partial_subpaths,
- linitial(childrel->partial_pathlist));
+ linitial(childrel->partial_pathlist));
else
partial_subpaths_valid = false;
* if we have zero or one live subpath due to constraint exclusion.)
*/
if (subpaths_valid)
- add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0));
+ add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0,
+ partitioned_rels));
/*
* Consider an append of partial unordered, unparameterized partial paths.
/* Generate a partial append path. */
appendpath = create_append_path(rel, partial_subpaths, NULL,
- parallel_workers);
+ parallel_workers, partitioned_rels);
add_partial_path(rel, (Path *) appendpath);
}
*/
if (subpaths_valid)
generate_mergeappend_paths(root, rel, live_childrels,
- all_child_pathkeys);
+ all_child_pathkeys,
+ partitioned_rels);
/*
* Build Append paths for each parameterization seen among the child rels.
if (subpaths_valid)
add_path(rel, (Path *)
- create_append_path(rel, subpaths, required_outer, 0));
+ create_append_path(rel, subpaths, required_outer, 0,
+ partitioned_rels));
}
}
+
/*
* generate_mergeappend_paths
* Generate MergeAppend paths for an append relation
static void
generate_mergeappend_paths(PlannerInfo *root, RelOptInfo *rel,
List *live_childrels,
- List *all_child_pathkeys)
+ List *all_child_pathkeys,
+ List *partitioned_rels)
{
ListCell *lcp;
get_cheapest_path_for_pathkeys(childrel->pathlist,
pathkeys,
NULL,
- STARTUP_COST);
+ STARTUP_COST,
+ false);
cheapest_total =
get_cheapest_path_for_pathkeys(childrel->pathlist,
pathkeys,
NULL,
- TOTAL_COST);
+ TOTAL_COST,
+ false);
/*
* If we can't find any paths with the right order just use the
rel,
startup_subpaths,
pathkeys,
- NULL));
+ NULL,
+ partitioned_rels));
if (startup_neq_total)
add_path(rel, (Path *) create_merge_append_path(root,
rel,
total_subpaths,
pathkeys,
- NULL));
+ NULL,
+ partitioned_rels));
}
}
+
/*
* get_cheapest_parameterized_child_path
* Get cheapest path for this relation that has exactly the requested
cheapest = get_cheapest_path_for_pathkeys(rel->pathlist,
NIL,
required_outer,
- TOTAL_COST);
+ TOTAL_COST,
+ false);
Assert(cheapest != NULL);
if (bms_equal(PATH_REQ_OUTER(cheapest), required_outer))
return cheapest;
return cheapest;
}
+
/*
* accumulate_append_subpath
* Add a subpath to the list being built for an Append or MergeAppend
return lappend(subpaths, path);
}
+
/*
* standard_join_search
* Find possible joinpaths for a query by successively finding ways
return rel;
}
-/*
- * create_plain_partial_paths
- * Build partial access paths for parallel scan of a plain relation
- */
-static void
-create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel)
-{
- int parallel_workers;
-
- parallel_workers = compute_parallel_worker(rel, rel->pages);
-
- /* If any limit was set to zero, the user doesn't want a parallel scan. */
- if (parallel_workers <= 0)
- return;
-
- /* Add an unordered partial path based on a parallel sequential scan. */
- add_partial_path(rel, create_seqscan_path(root, rel, NULL, parallel_workers));
-}
-
-/*
- * Compute the number of parallel workers that should be used to scan a
- * relation. "pages" is the number of pages from the relation that we
- * expect to scan.
- */
-static int
-compute_parallel_worker(RelOptInfo *rel, BlockNumber pages)
-{
- int parallel_workers;
-
- /*
- * If the user has set the parallel_workers reloption, use that; otherwise
- * select a default number of workers.
- */
- if (rel->rel_parallel_workers != -1)
- parallel_workers = rel->rel_parallel_workers;
- else
- {
- int parallel_threshold;
-
- /*
- * If this relation is too small to be worth a parallel scan, just
- * return without doing anything ... unless it's an inheritance child.
- * In that case, we want to generate a parallel path here anyway. It
- * might not be worthwhile just for this relation, but when combined
- * with all of its inheritance siblings it may well pay off.
- */
- if (pages < (BlockNumber) min_parallel_relation_size &&
- rel->reloptkind == RELOPT_BASEREL)
- return 0;
-
- /*
- * Select the number of workers based on the log of the size of the
- * relation. This probably needs to be a good deal more
- * sophisticated, but we need something here for now. Note that the
- * upper limit of the min_parallel_relation_size GUC is chosen to
- * prevent overflow here.
- */
- parallel_workers = 1;
- parallel_threshold = Max(min_parallel_relation_size, 1);
- while (pages >= (BlockNumber) (parallel_threshold * 3))
- {
- parallel_workers++;
- parallel_threshold *= 3;
- if (parallel_threshold > INT_MAX / 3)
- break; /* avoid overflow */
- }
- }
-
- /*
- * In no case use more than max_parallel_workers_per_gather workers.
- */
- parallel_workers = Min(parallel_workers, max_parallel_workers_per_gather);
-
- return parallel_workers;
-}
-
/*
* join_search_one_level
if (level == 2) /* consider remaining initial rels */
other_rels = lnext(r);
- else /* consider all initial rels */
+ else /* consider all initial rels */
other_rels = list_head(joinrels[1]);
make_rels_by_clause_joins(root,
}
}
+
/*
* make_rels_by_clause_joins
* Build joins between the given relation 'old_rel' and other relations
}
}
+
/*
* make_rels_by_clauseless_joins
* Given a relation 'old_rel' and a list of other relations
}
}
+
/*
* join_is_legal
* Determine whether a proposed join is legal given the query's
!bms_is_subset(sjinfo->min_righthand, join_plus_rhs))
{
join_plus_rhs = bms_add_members(join_plus_rhs,
- sjinfo->min_righthand);
+ sjinfo->min_righthand);
more = true;
}
/* full joins constrain both sides symmetrically */
return true;
}
+
/*
* has_join_restriction
* Detect whether the specified relation has join-order restrictions,
return false;
}
+
/*
* is_dummy_rel --- has relation been proven empty?
*/
return IS_DUMMY_REL(rel);
}
+
/*
* Mark a relation as proven empty.
*
rel->partial_pathlist = NIL;
/* Set up the dummy path */
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0));
+ add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL));
/* Set or update cheapest_total_path and related fields */
set_cheapest(rel);
MemoryContextSwitchTo(oldcontext);
}
+
/*
- * restriction_is_constant_false --- is a restrictlist just FALSE?
+ * restriction_is_constant_false --- is a restrictlist just false?
*
- * In cases where a qual is provably constant FALSE, eval_const_expressions
+ * In cases where a qual is provably constant false, eval_const_expressions
* will generally have thrown away anything that's ANDed with it. In outer
* join situations this will leave us computing cartesian products only to
* decide there's no match for an outer row, which is pretty stupid. So,
* we need to detect the case.
*
- * If only_pushed_down is TRUE, then consider only pushed-down quals.
+ * If only_pushed_down is true, then consider only quals that are pushed-down
+ * from the point of view of the joinrel.
*/
static bool
-restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
+restriction_is_constant_false(List *restrictlist,
+ RelOptInfo *joinrel,
+ bool only_pushed_down)
{
ListCell *lc;
*/
foreach(lc, restrictlist)
{
- RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
- Assert(IsA(rinfo, RestrictInfo));
- if (only_pushed_down && !rinfo->is_pushed_down)
+ if (only_pushed_down && !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
continue;
if (rinfo->clause && IsA(rinfo->clause, Const))