diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 14 | ||||
-rw-r--r-- | src/backend/optimizer/path/joinrels.c | 89 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 17 |
3 files changed, 71 insertions, 49 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 56a50843123..3c9d84f665a 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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); diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 9604a54b776..34cc7dacdf0 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -15,9 +15,7 @@ #include "postgres.h" #include "miscadmin.h" -#include "nodes/nodeFuncs.h" #include "optimizer/appendinfo.h" -#include "optimizer/clauses.h" #include "optimizer/joininfo.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -44,8 +42,6 @@ static void try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo, List *parent_restrictlist); -static void update_child_rel_info(PlannerInfo *root, - RelOptInfo *rel, RelOptInfo *childrel); static SpecialJoinInfo *build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo, Relids left_relids, Relids right_relids); @@ -1405,6 +1401,10 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, { RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts]; RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts]; + bool rel1_empty = (child_rel1 == NULL || + IS_DUMMY_REL(child_rel1)); + bool rel2_empty = (child_rel2 == NULL || + IS_DUMMY_REL(child_rel2)); SpecialJoinInfo *child_sjinfo; List *child_restrictlist; RelOptInfo *child_joinrel; @@ -1413,24 +1413,69 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, int nappinfos; /* - * If a child table has consider_partitionwise_join=false, it means + * Check for cases where we can prove that this segment of the join + * returns no rows, due to one or both inputs being empty (including + * inputs that have been pruned away entirely). If so just ignore it. + * These rules are equivalent to populate_joinrel_with_paths's rules + * for dummy input relations. + */ + switch (parent_sjinfo->jointype) + { + case JOIN_INNER: + case JOIN_SEMI: + if (rel1_empty || rel2_empty) + continue; /* ignore this join segment */ + break; + case JOIN_LEFT: + case JOIN_ANTI: + if (rel1_empty) + continue; /* ignore this join segment */ + break; + case JOIN_FULL: + if (rel1_empty && rel2_empty) + continue; /* ignore this join segment */ + break; + default: + /* other values not expected here */ + elog(ERROR, "unrecognized join type: %d", + (int) parent_sjinfo->jointype); + break; + } + + /* + * If a child has been pruned entirely then we can't generate paths + * for it, so we have to reject partitionwise joining unless we were + * able to eliminate this partition above. + */ + if (child_rel1 == NULL || child_rel2 == NULL) + { + /* + * Mark the joinrel as unpartitioned so that later functions treat + * it correctly. + */ + joinrel->nparts = 0; + return; + } + + /* + * If a leaf relation has consider_partitionwise_join=false, it means * that it's a dummy relation for which we skipped setting up tlist - * expressions and adding EC members in set_append_rel_size(), so do - * that now for use later. + * expressions and adding EC members in set_append_rel_size(), so + * again we have to fail here. */ if (rel1_is_simple && !child_rel1->consider_partitionwise_join) { Assert(child_rel1->reloptkind == RELOPT_OTHER_MEMBER_REL); Assert(IS_DUMMY_REL(child_rel1)); - update_child_rel_info(root, rel1, child_rel1); - child_rel1->consider_partitionwise_join = true; + joinrel->nparts = 0; + return; } if (rel2_is_simple && !child_rel2->consider_partitionwise_join) { Assert(child_rel2->reloptkind == RELOPT_OTHER_MEMBER_REL); Assert(IS_DUMMY_REL(child_rel2)); - update_child_rel_info(root, rel2, child_rel2); - child_rel2->consider_partitionwise_join = true; + joinrel->nparts = 0; + return; } /* We should never try to join two overlapping sets of rels. */ @@ -1475,28 +1520,6 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, } /* - * Set up tlist expressions for the childrel, and add EC members referencing - * the childrel. - */ -static void -update_child_rel_info(PlannerInfo *root, - RelOptInfo *rel, RelOptInfo *childrel) -{ - AppendRelInfo *appinfo = root->append_rel_array[childrel->relid]; - - /* Make child tlist expressions */ - childrel->reltarget->exprs = (List *) - adjust_appendrel_attrs(root, - (Node *) rel->reltarget->exprs, - 1, &appinfo); - - /* Make child entries in the EquivalenceClass as well */ - if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) - add_child_rel_equivalences(root, appinfo, rel, childrel); - childrel->has_eclass_joins = rel->has_eclass_joins; -} - -/* * Construct the SpecialJoinInfo for a child-join by translating * SpecialJoinInfo for the join between parents. left_relids and right_relids * are the relids of left and right side of the join respectively. diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index ca7a0fbbf53..031e7097189 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -6993,6 +6993,10 @@ apply_scanjoin_target_to_paths(PlannerInfo *root, List *child_scanjoin_targets = NIL; ListCell *lc; + /* Pruned or dummy children can be ignored. */ + if (child_rel == NULL || IS_DUMMY_REL(child_rel)) + continue; + /* Translate scan/join targets for this child. */ appinfos = find_appinfos_by_relids(root, child_rel->relids, &nappinfos); @@ -7093,8 +7097,9 @@ create_partitionwise_grouping_paths(PlannerInfo *root, RelOptInfo *child_grouped_rel; RelOptInfo *child_partially_grouped_rel; - /* Input child rel must have a path */ - Assert(child_input_rel->pathlist != NIL); + /* Pruned or dummy children can be ignored. */ + if (child_input_rel == NULL || IS_DUMMY_REL(child_input_rel)) + continue; /* * Copy the given "extra" structure as is and then override the @@ -7136,14 +7141,6 @@ create_partitionwise_grouping_paths(PlannerInfo *root, extra->target_parallel_safe, child_extra.havingQual); - /* Ignore empty children. They contribute nothing. */ - if (IS_DUMMY_REL(child_input_rel)) - { - mark_dummy_rel(child_grouped_rel); - - continue; - } - /* Create grouping paths for this child relation. */ create_ordinary_grouping_paths(root, child_input_rel, child_grouped_rel, |