mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
Fix planner crash from pfree'ing a partial path that a GatherPath uses.
We mustn't run generate_gather_paths() during add_paths_to_joinrel(), because that function can be invoked multiple times for the same target joinrel. Not only is it wasteful to build GatherPaths repeatedly, but a later add_partial_path() could delete the partial path that a previously created GatherPath depends on. Instead establish the convention that we do generate_gather_paths() for a rel only just before set_cheapest(). The code was accidentally not broken for baserels, because as of today there never is more than one partial path for a baserel. But that assumption obviously has a pretty short half-life, so move the generate_gather_paths() calls for those cases as well. Also add some generic comments explaining how and why this all works. Per fuzz testing by Andreas Seltenreich. Report: <871t5pgwdt.fsf@credativ.de>
This commit is contained in:
@ -73,7 +73,7 @@ static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||
Index rti, RangeTblEntry *rte);
|
||||
static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel,
|
||||
RangeTblEntry *rte);
|
||||
static void create_parallel_paths(PlannerInfo *root, RelOptInfo *rel);
|
||||
static void create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel);
|
||||
static void set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
|
||||
RangeTblEntry *rte);
|
||||
static bool function_rte_parallel_ok(RangeTblEntry *rte);
|
||||
@ -447,6 +447,16 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a baserel, consider gathering any partial paths we may have
|
||||
* created for it. (If we tried to gather inheritance children, we could
|
||||
* end up with a very large number of gather nodes, each trying to grab
|
||||
* its own pool of workers, so don't do this for otherrels. Instead,
|
||||
* we'll consider gathering partial paths for the parent appendrel.)
|
||||
*/
|
||||
if (rel->reloptkind == RELOPT_BASEREL)
|
||||
generate_gather_paths(root, rel);
|
||||
|
||||
/*
|
||||
* Allow a plugin to editorialize on the set of Paths for this base
|
||||
* relation. It could add new paths (such as CustomPaths) by calling
|
||||
@ -643,7 +653,7 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
||||
|
||||
/* If appropriate, consider parallel sequential scan */
|
||||
if (rel->consider_parallel && required_outer == NULL)
|
||||
create_parallel_paths(root, rel);
|
||||
create_plain_partial_paths(root, rel);
|
||||
|
||||
/* Consider index scans */
|
||||
create_index_paths(root, rel);
|
||||
@ -653,11 +663,11 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
||||
}
|
||||
|
||||
/*
|
||||
* create_parallel_paths
|
||||
* Build parallel access paths for a plain relation
|
||||
* create_plain_partial_paths
|
||||
* Build partial access paths for parallel scan of a plain relation
|
||||
*/
|
||||
static void
|
||||
create_parallel_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
create_plain_partial_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
{
|
||||
int parallel_degree = 1;
|
||||
|
||||
@ -712,16 +722,6 @@ create_parallel_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
|
||||
/* Add an unordered partial path based on a parallel sequential scan. */
|
||||
add_partial_path(rel, create_seqscan_path(root, rel, NULL, parallel_degree));
|
||||
|
||||
/*
|
||||
* If this is a baserel, consider gathering any partial paths we may have
|
||||
* just created. If we gathered an inheritance child, we could end up
|
||||
* with a very large number of gather nodes, each trying to grab its own
|
||||
* pool of workers, so don't do this in that case. Instead, we'll
|
||||
* consider gathering partial paths for the appendrel.
|
||||
*/
|
||||
if (rel->reloptkind == RELOPT_BASEREL)
|
||||
generate_gather_paths(root, rel);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1262,9 +1262,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||
appendpath = create_append_path(rel, partial_subpaths, NULL,
|
||||
parallel_degree);
|
||||
add_partial_path(rel, (Path *) appendpath);
|
||||
|
||||
/* Consider gathering it. */
|
||||
generate_gather_paths(root, rel);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1970,6 +1967,10 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
||||
* generate_gather_paths
|
||||
* Generate parallel access paths for a relation by pushing a Gather on
|
||||
* top of a partial path.
|
||||
*
|
||||
* This must not be called until after we're done creating all partial paths
|
||||
* for the specified relation. (Otherwise, add_partial_path might delete a
|
||||
* path that some GatherPath has a reference to.)
|
||||
*/
|
||||
void
|
||||
generate_gather_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
@ -1983,7 +1984,9 @@ generate_gather_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
|
||||
/*
|
||||
* The output of Gather is currently always unsorted, so there's only one
|
||||
* partial path of interest: the cheapest one.
|
||||
* partial path of interest: the cheapest one. That will be the one at
|
||||
* the front of partial_pathlist because of the way add_partial_path
|
||||
* works.
|
||||
*
|
||||
* Eventually, we should have a Gather Merge operation that can merge
|
||||
* multiple tuple streams together while preserving their ordering. We
|
||||
@ -2148,12 +2151,19 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
|
||||
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);
|
||||
|
||||
|
Reference in New Issue
Block a user