1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00

Avoid crash in partitionwise join planning under GEQO.

While trying to plan a partitionwise join, we may be faced with cases
where one or both input partitions for a particular segment of the join
have been pruned away.  In HEAD and v11, this is problematic because
earlier processing didn't bother to make a pruned RelOptInfo fully
valid.  With an upcoming patch to make partition pruning more efficient,
this'll be even more problematic because said RelOptInfo won't exist at
all.

The existing code attempts to deal with this by retroactively making the
RelOptInfo fully valid, but that causes crashes under GEQO because join
planning is done in a short-lived memory context.  In v11 we could
probably have fixed this by switching to the planner's main context
while fixing up the RelOptInfo, but that idea doesn't scale well to the
upcoming patch.  It would be better not to mess with the base-relation
data structures during join planning, anyway --- that's just a recipe
for order-of-operations bugs.

In many cases, though, we don't actually need the child RelOptInfo,
because if the input is certainly empty then the join segment's result
is certainly empty, so we can skip making a join plan altogether.  (The
existing code ultimately arrives at the same conclusion, but only after
doing a lot more work.)  This approach works except when the pruned-away
partition is on the nullable side of a LEFT, ANTI, or FULL join, and the
other side isn't pruned.  But in those cases the existing code leaves a
lot to be desired anyway --- the correct output is just the result of
the unpruned side of the join, but we were emitting a useless outer join
against a dummy Result.  Pending somebody writing code to handle that
more nicely, let's just abandon the partitionwise-join optimization in
such cases.

When the modified code skips making a join plan, it doesn't make a
join RelOptInfo either; this requires some upper-level code to
cope with nulls in part_rels[] arrays.  We would have had to have
that anyway after the upcoming patch.

Back-patch to v11 since the crash is demonstrable there.

Discussion: https://postgr.es/m/8305.1553884377@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2019-03-30 12:48:19 -04:00
parent ef6576f537
commit 7ad6498fd5
7 changed files with 157 additions and 165 deletions

View File

@ -1112,11 +1112,11 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
* for partitioned child rels.
*
* Note: here we abuse the consider_partitionwise_join flag by setting
* it *even* for child rels that are not partitioned. In that case,
* we set it to tell try_partitionwise_join() that it doesn't need to
* generate their targetlists and EC entries as they have already been
* generated here, as opposed to the dummy child rels for which the
* flag is left set to false so that it will generate them.
* it for child rels that are not themselves partitioned. We do so to
* tell try_partitionwise_join() that the child rel is sufficiently
* valid to be used as a per-partition input, even if it later gets
* proven to be dummy. (It's not usable until we've set up the
* reltarget and EC entries, which we just did.)
*/
if (rel->consider_partitionwise_join)
childrel->consider_partitionwise_join = true;
@ -3564,7 +3564,9 @@ generate_partitionwise_join_paths(PlannerInfo *root, RelOptInfo *rel)
{
RelOptInfo *child_rel = part_rels[cnt_parts];
Assert(child_rel != NULL);
/* If it's been pruned entirely, it's certainly dummy. */
if (child_rel == NULL)
continue;
/* Add partitionwise join paths for partitioned child-joins. */
generate_partitionwise_join_paths(root, child_rel);