* core.c
* Routines copied from PostgreSQL core distribution.
*
+
+ * The main purpose of this files is having access to static functions in core.
+ * Another purpose is tweaking functions behavior by replacing part of them by
+ * macro definitions. See at the end of pg_hint_plan.c for details. Anyway,
+ * this file *must* contain required functions without making any change.
+ *
+ * This file contains the following functions from corresponding files.
+ *
* src/backend/optimizer/path/allpaths.c
+ *
+ * static functions:
+ * set_plain_rel_pathlist()
* set_append_rel_pathlist()
+ * add_paths_to_append_rel()
* generate_mergeappend_paths()
* get_cheapest_parameterized_child_path()
* accumulate_append_subpath()
- * standard_join_search()
+ *
+ * public functions:
+ * standard_join_search(): This funcion is not static. The reason for
+ * including this function is make_rels_by_clause_joins. In order to
+ * avoid generating apparently unwanted join combination, we decided to
+ * change the behavior of make_join_rel, which is called under this
+ * function.
*
* src/backend/optimizer/path/joinrels.c
- * join_search_one_level()
+ *
+ * public functions:
+ * join_search_one_level(): We have to modify this to call my definition of
+ * make_rels_by_clause_joins.
+ *
+ * static functions:
* make_rels_by_clause_joins()
* make_rels_by_clauseless_joins()
* join_is_legal()
* mark_dummy_rel()
* restriction_is_constant_false()
*
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*-------------------------------------------------------------------------
*/
+
+/*
+ * set_plain_rel_pathlist
+ * Build access paths for a plain relation (no subquery, no inheritance)
+ */
+static void
+set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ Relids required_outer;
+
+ /*
+ * We don't support pushing join clauses into the quals of a seqscan, but
+ * it could still have required parameterization due to LATERAL refs in
+ * its tlist.
+ */
+ required_outer = rel->lateral_relids;
+
+ /* Consider sequential scan */
+ add_path(rel, create_seqscan_path(root, rel, required_outer, 0));
+
+ /* If appropriate, consider parallel sequential scan */
+ if (rel->consider_parallel && required_outer == NULL)
+ create_plain_partial_paths(root, rel);
+
+ /* Consider index scans */
+ create_index_paths(root, rel);
+
+ /* Consider TID scans */
+ create_tidscan_paths(root, rel);
+}
+
+
/*
* 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 *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)
childrel = root->simple_rel_array[childRTindex];
/*
+ * If set_append_rel_size() decided the parent appendrel was
+ * parallel-unsafe at some point after visiting this child rel, we
+ * need to propagate the unsafety marking down to the child, so that
+ * we don't generate useless partial paths for it.
+ */
+ if (!rel->consider_parallel)
+ childrel->consider_parallel = false;
+
+ /*
* Compute the child's access paths.
*/
set_rel_pathlist(root, childrel, childRTindex, childRTE);
* 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, "unexpcted 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));
+ else
+ partial_subpaths_valid = false;
+
/*
* Collect lists of all the available path orderings and
* parameterizations for all the children. We use these as a
* 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));
+ add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0,
+ partitioned_rels));
+
+ /*
+ * Consider an append of partial unordered, unparameterized partial paths.
+ */
+ if (partial_subpaths_valid)
+ {
+ AppendPath *appendpath;
+ ListCell *lc;
+ int parallel_workers = 0;
+
+ /*
+ * Decide on the number of workers to request for this append path.
+ * For now, we just use the maximum value from among the members. It
+ * might be useful to use a higher number if the Append node were
+ * smart enough to spread out the workers, but it currently isn't.
+ */
+ foreach(lc, partial_subpaths)
+ {
+ Path *path = lfirst(lc);
+
+ parallel_workers = Max(parallel_workers, path->parallel_workers);
+ }
+ Assert(parallel_workers > 0);
+
+ /* Generate a partial append path. */
+ appendpath = create_append_path(rel, partial_subpaths, NULL,
+ parallel_workers, partitioned_rels);
+ add_partial_path(rel, (Path *) appendpath);
+ }
/*
* Also build unparameterized MergeAppend paths based on the collected
*/
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));
+ 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
join_search_one_level(root, lev);
/*
- * Do cleanup work on each just-processed rel.
+ * Run generate_gather_paths() for each just-processed joinrel. We
+ * could not do this earlier because both regular and partial paths
+ * can get added to a particular joinrel at multiple times within
+ * join_search_one_level. After that, we're done creating paths for
+ * the joinrel, so run set_cheapest().
*/
foreach(lc, root->join_rel_level[lev])
{
rel = (RelOptInfo *) lfirst(lc);
+ /* Create GatherPaths for any useful partial paths for rel */
+ generate_gather_paths(root, rel);
+
/* Find and save the cheapest paths for this rel */
set_cheapest(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));
+}
+
+
+/*
* join_search_one_level
* Consider ways to produce join relations containing exactly 'level'
* jointree items. (This is one step of the dynamic-programming method
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.
*
/* Evict any previously chosen paths */
rel->pathlist = NIL;
+ rel->partial_pathlist = NIL;
/* Set up the dummy path */
- add_path(rel, (Path *) create_append_path(rel, NIL, NULL));
+ 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?
*
*/
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)
continue;