aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/optimizer/path/allpaths.c14
-rw-r--r--src/backend/optimizer/path/joinrels.c89
-rw-r--r--src/backend/optimizer/plan/planner.c17
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,