From 0c8647a7ad5280068a79ef99d5a350fd7e6ee1ed Mon Sep 17 00:00:00 2001 From: amit Date: Thu, 27 Jun 2019 14:23:42 +0900 Subject: [PATCH v3 3/3] Add multi-relation EC child members in a separate pass A given multi-relation EC member should be translated when building the child joinrel that will be able to supply the necessary child expression. --- src/backend/optimizer/path/allpaths.c | 2 +- src/backend/optimizer/path/equivclass.c | 68 ++++++-- src/backend/optimizer/util/relnode.c | 20 ++- src/include/optimizer/paths.h | 3 +- src/test/regress/expected/partition_join.out | 243 ++++++++++++++++++++++++++- 5 files changed, 304 insertions(+), 32 deletions(-) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index db3a68a51d..70d0be691e 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1067,7 +1067,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, * paths that produce those sort orderings). */ if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) - add_child_rel_equivalences(root, appinfo, rel, childrel); + add_child_rel_equivalences(root, &appinfo, 1, rel, childrel); childrel->has_eclass_joins = rel->has_eclass_joins; /* diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index ccc07ba9f0..67756ab671 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -1193,6 +1193,20 @@ generate_join_implied_equalities(PlannerInfo *root, result = list_concat(result, sublist); } + /* + * Remember the EC indexes that were found to refer to this joinrel. This + * is useful in add_child_rel_equivalences() where we need to add child + * versions of the expressions in each of these ECs. + */ + if (!bms_is_empty(matching_ecs)) + { + RelOptInfo *joinrel = find_join_rel(root, join_relids); + + if (joinrel) + joinrel->eclass_indexes = bms_add_members(joinrel->eclass_indexes, + matching_ecs); + } + return result; } @@ -2215,12 +2229,15 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root, * Note that this function won't be called at all unless we have at least some * reason to believe that the EC members it generates will be useful. * - * parent_rel and child_rel could be derived from appinfo, but since the + * parent_rel and child_rel could be derived from appinfos, but since the * caller has already computed them, we might as well just pass them in. + * Note that parent_rel and child_rel are either BASEREL and OTHER_MEMBER_REL, + * respectively, or JOINREL and OTHER_JOINREL. */ void add_child_rel_equivalences(PlannerInfo *root, - AppendRelInfo *appinfo, + AppendRelInfo **appinfos, + int nappinfos, RelOptInfo *parent_rel, RelOptInfo *child_rel) { @@ -2231,13 +2248,14 @@ add_child_rel_equivalences(PlannerInfo *root, * eclass_indexes to avoid searching all of root->eq_classes. */ Assert(root->ec_merging_done); - Assert(IS_SIMPLE_REL(parent_rel)); + Assert(IS_SIMPLE_REL(parent_rel) || IS_JOIN_REL(parent_rel)); i = -1; while ((i = bms_next_member(parent_rel->eclass_indexes, i)) >= 0) { EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i); int num_members; + EquivalenceMember *first_em; /* * If this EC contains a volatile expression, then generating child @@ -2247,8 +2265,21 @@ add_child_rel_equivalences(PlannerInfo *root, if (cur_ec->ec_has_volatile) continue; + first_em = (EquivalenceMember *) linitial(cur_ec->ec_members); + + /* + * Only translate ECs whose members expressions could possibly match + * the parent relation. That is, for "baserel" parent and child + * relations only consider ECs that contain single-relation members, + * whereas, for "joinrel" parent and child relations, only consider ECs + * that contain multi-relation members. Note that looking at the first + * EC member is enough, because others should look the same. + */ + if (bms_membership(parent_rel->relids) != bms_membership(first_em->em_relids)) + continue; + /* Sanity check eclass_indexes only contain ECs for parent_rel */ - Assert(bms_is_subset(child_rel->top_parent_relids, cur_ec->ec_relids)); + Assert(bms_overlap(child_rel->top_parent_relids, cur_ec->ec_relids)); /* * We don't use foreach() here because there's no point in scanning @@ -2263,34 +2294,31 @@ add_child_rel_equivalences(PlannerInfo *root, if (cur_em->em_is_const) continue; /* ignore consts here */ - /* - * We consider only original EC members here, not - * already-transformed child members. Otherwise, if some original - * member expression references more than one appendrel, we'd get - * an O(N^2) explosion of useless derived expressions for - * combinations of children. - */ - if (cur_em->em_is_child) - continue; /* ignore children here */ - /* Does this member reference child's topmost parent rel? */ - if (bms_overlap(cur_em->em_relids, child_rel->top_parent_relids)) + if (bms_is_subset(cur_em->em_relids, child_rel->top_parent_relids)) { /* Yes, generate transformed child version */ Expr *child_expr; Relids new_relids; Relids new_nullable_relids; - if (parent_rel->reloptkind == RELOPT_BASEREL) + /* + * If the parent_rel is itself the topmost parent rel, transform + * directly. + */ + if (parent_rel->reloptkind == RELOPT_BASEREL || + parent_rel->reloptkind == RELOPT_JOINREL) { /* Simple single-level transformation */ child_expr = (Expr *) adjust_appendrel_attrs(root, (Node *) cur_em->em_expr, - 1, &appinfo); + nappinfos, appinfos); } else { + Assert(parent_rel->reloptkind == RELOPT_OTHER_MEMBER_REL || + parent_rel->reloptkind == RELOPT_OTHER_JOINREL); /* Must do multi-level transformation */ child_expr = (Expr *) adjust_appendrel_attrs_multilevel(root, @@ -2329,6 +2357,12 @@ add_child_rel_equivalences(PlannerInfo *root, /* Record this EC index for the child rel */ child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i); + + /* + * There aren't going to be more expressions to translate in + * the same EC. + */ + break; } } } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 3417438108..7cbd9ebe94 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -671,6 +671,9 @@ build_join_rel(PlannerInfo *root, joinrel->nullable_partexprs = NULL; joinrel->partitioned_child_rels = NIL; + /* Add the joinrel to the PlannerInfo. */ + add_join_rel(root, joinrel); + /* Compute information relevant to the foreign relations. */ set_foreign_rel_properties(joinrel, outer_rel, inner_rel); @@ -744,9 +747,6 @@ build_join_rel(PlannerInfo *root, is_parallel_safe(root, (Node *) joinrel->reltarget->exprs)) joinrel->consider_parallel = true; - /* Add the joinrel to the PlannerInfo. */ - add_join_rel(root, joinrel); - /* * Also, if dynamic-programming join search is active, add the new joinrel * to the appropriate sublist. Note: you might think the Assert on number @@ -864,6 +864,16 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, (Node *) parent_joinrel->joininfo, nappinfos, appinfos); + + /* + * If the parent joinrel has pending equivalence classes, so does the + * child. + */ + if (parent_joinrel->has_eclass_joins || + has_useful_pathkeys(root, parent_joinrel)) + add_child_rel_equivalences(root, appinfos, nappinfos, + parent_joinrel, joinrel); + pfree(appinfos); /* @@ -873,10 +883,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, joinrel->direct_lateral_relids = (Relids) bms_copy(parent_joinrel->direct_lateral_relids); joinrel->lateral_relids = (Relids) bms_copy(parent_joinrel->lateral_relids); - /* - * If the parent joinrel has pending equivalence classes, so does the - * child. - */ joinrel->has_eclass_joins = parent_joinrel->has_eclass_joins; /* Is the join between partitions itself partitioned? */ diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 54610b8656..ca507f3ee7 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -147,7 +147,8 @@ extern EquivalenceClass *match_eclasses_to_foreign_key_col(PlannerInfo *root, ForeignKeyOptInfo *fkinfo, int colno); extern void add_child_rel_equivalences(PlannerInfo *root, - AppendRelInfo *appinfo, + AppendRelInfo **appinfos, + int nappinfos, RelOptInfo *parent_rel, RelOptInfo *child_rel); extern List *generate_implied_equalities_for_column(PlannerInfo *root, diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 3260a345ff..875a90fd62 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -2190,19 +2190,250 @@ SELECT * FROM prt1_n t1 INNER JOIN prt1_n t2 USING (c) FULL JOIN prt1_n t3 USING SET enable_hashjoin TO off; EXPLAIN (COSTS OFF) SELECT * FROM prt1_n t1 FULL JOIN prt1_n t2 USING (c) FULL JOIN prt1_n t3 USING (c) FULL JOIN prt1_n t4 USING (c) ORDER BY c LIMIT 5; -ERROR: could not find pathkey item to sort + QUERY PLAN +--------------------------------------------------------------------------------------------------------- + Limit + -> Sort + Sort Key: (COALESCE(COALESCE(COALESCE(t1.c, t2.c), t3.c), t4.c)) + -> Append + -> Merge Full Join + Merge Cond: (((COALESCE(COALESCE(t1.c, t2.c), t3.c))::text) = (t4.c)::text) + -> Sort + Sort Key: ((COALESCE(COALESCE(t1.c, t2.c), t3.c))::text) + -> Merge Full Join + Merge Cond: (((COALESCE(t1.c, t2.c))::text) = (t3.c)::text) + -> Sort + Sort Key: ((COALESCE(t1.c, t2.c))::text) + -> Merge Full Join + Merge Cond: ((t1.c)::text = (t2.c)::text) + -> Sort + Sort Key: t1.c + -> Seq Scan on prt1_n_p1 t1 + -> Sort + Sort Key: t2.c + -> Seq Scan on prt1_n_p1 t2 + -> Sort + Sort Key: t3.c + -> Seq Scan on prt1_n_p1 t3 + -> Sort + Sort Key: t4.c + -> Seq Scan on prt1_n_p1 t4 + -> Merge Full Join + Merge Cond: (((COALESCE(COALESCE(t1_1.c, t2_1.c), t3_1.c))::text) = (t4_1.c)::text) + -> Sort + Sort Key: ((COALESCE(COALESCE(t1_1.c, t2_1.c), t3_1.c))::text) + -> Merge Full Join + Merge Cond: (((COALESCE(t1_1.c, t2_1.c))::text) = (t3_1.c)::text) + -> Sort + Sort Key: ((COALESCE(t1_1.c, t2_1.c))::text) + -> Merge Full Join + Merge Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Sort + Sort Key: t1_1.c + -> Seq Scan on prt1_n_p2_1 t1_1 + -> Sort + Sort Key: t2_1.c + -> Seq Scan on prt1_n_p2_1 t2_1 + -> Sort + Sort Key: t3_1.c + -> Seq Scan on prt1_n_p2_1 t3_1 + -> Sort + Sort Key: t4_1.c + -> Seq Scan on prt1_n_p2_1 t4_1 + -> Merge Full Join + Merge Cond: (((COALESCE(COALESCE(t1_2.c, t2_2.c), t3_2.c))::text) = (t4_2.c)::text) + -> Sort + Sort Key: ((COALESCE(COALESCE(t1_2.c, t2_2.c), t3_2.c))::text) + -> Merge Full Join + Merge Cond: (((COALESCE(t1_2.c, t2_2.c))::text) = (t3_2.c)::text) + -> Sort + Sort Key: ((COALESCE(t1_2.c, t2_2.c))::text) + -> Merge Full Join + Merge Cond: ((t1_2.c)::text = (t2_2.c)::text) + -> Sort + Sort Key: t1_2.c + -> Seq Scan on prt1_n_p2_2 t1_2 + -> Sort + Sort Key: t2_2.c + -> Seq Scan on prt1_n_p2_2 t2_2 + -> Sort + Sort Key: t3_2.c + -> Seq Scan on prt1_n_p2_2 t3_2 + -> Sort + Sort Key: t4_2.c + -> Seq Scan on prt1_n_p2_2 t4_2 +(70 rows) + SELECT * FROM prt1_n t1 FULL JOIN prt1_n t2 USING (c) FULL JOIN prt1_n t3 USING (c) FULL JOIN prt1_n t4 USING (c) ORDER BY c LIMIT 5; -ERROR: could not find pathkey item to sort + c | a | b | a | b | a | b | a | b +------+---+---+---+---+---+---+---+--- + 0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 + 0002 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 + 0004 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 + 0006 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 + 0008 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 +(5 rows) + EXPLAIN (COSTS OFF) SELECT * FROM prt1_n t1 LEFT JOIN prt1_n t2 USING (c) FULL JOIN prt1_n t3 USING (c) FULL JOIN prt1_n t4 USING (c) ORDER BY c LIMIT 5; -ERROR: could not find pathkey item to sort + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + -> Sort + Sort Key: (COALESCE(COALESCE(t1.c, t3.c), t4.c)) + -> Append + -> Merge Full Join + Merge Cond: (((COALESCE(t1.c, t3.c))::text) = (t4.c)::text) + -> Sort + Sort Key: ((COALESCE(t1.c, t3.c))::text) + -> Merge Full Join + Merge Cond: ((t1.c)::text = (t3.c)::text) + -> Merge Left Join + Merge Cond: ((t1.c)::text = (t2.c)::text) + -> Sort + Sort Key: t1.c + -> Seq Scan on prt1_n_p1 t1 + -> Sort + Sort Key: t2.c + -> Seq Scan on prt1_n_p1 t2 + -> Sort + Sort Key: t3.c + -> Seq Scan on prt1_n_p1 t3 + -> Sort + Sort Key: t4.c + -> Seq Scan on prt1_n_p1 t4 + -> Merge Full Join + Merge Cond: (((COALESCE(t1_1.c, t3_1.c))::text) = (t4_1.c)::text) + -> Sort + Sort Key: ((COALESCE(t1_1.c, t3_1.c))::text) + -> Merge Full Join + Merge Cond: ((t1_1.c)::text = (t3_1.c)::text) + -> Merge Left Join + Merge Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Sort + Sort Key: t1_1.c + -> Seq Scan on prt1_n_p2_1 t1_1 + -> Sort + Sort Key: t2_1.c + -> Seq Scan on prt1_n_p2_1 t2_1 + -> Sort + Sort Key: t3_1.c + -> Seq Scan on prt1_n_p2_1 t3_1 + -> Sort + Sort Key: t4_1.c + -> Seq Scan on prt1_n_p2_1 t4_1 + -> Merge Full Join + Merge Cond: (((COALESCE(t1_2.c, t3_2.c))::text) = (t4_2.c)::text) + -> Sort + Sort Key: ((COALESCE(t1_2.c, t3_2.c))::text) + -> Merge Full Join + Merge Cond: ((t1_2.c)::text = (t3_2.c)::text) + -> Merge Left Join + Merge Cond: ((t1_2.c)::text = (t2_2.c)::text) + -> Sort + Sort Key: t1_2.c + -> Seq Scan on prt1_n_p2_2 t1_2 + -> Sort + Sort Key: t2_2.c + -> Seq Scan on prt1_n_p2_2 t2_2 + -> Sort + Sort Key: t3_2.c + -> Seq Scan on prt1_n_p2_2 t3_2 + -> Sort + Sort Key: t4_2.c + -> Seq Scan on prt1_n_p2_2 t4_2 +(64 rows) + SELECT * FROM prt1_n t1 LEFT JOIN prt1_n t2 USING (c) FULL JOIN prt1_n t3 USING (c) FULL JOIN prt1_n t4 USING (c) ORDER BY c LIMIT 5; -ERROR: could not find pathkey item to sort + c | a | b | a | b | a | b | a | b +------+---+---+---+---+---+---+---+--- + 0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 + 0002 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 + 0004 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 + 0006 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 + 0008 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 +(5 rows) + EXPLAIN (COSTS OFF) SELECT * FROM prt1_n t1 INNER JOIN prt1_n t2 USING (c) FULL JOIN prt1_n t3 USING (c) FULL JOIN prt1_n t4 USING (c) ORDER BY c LIMIT 5; -ERROR: could not find pathkey item to sort + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + -> Sort + Sort Key: (COALESCE(COALESCE(t1.c, t3.c), t4.c)) + -> Append + -> Merge Full Join + Merge Cond: (((COALESCE(t1.c, t3.c))::text) = (t4.c)::text) + -> Sort + Sort Key: ((COALESCE(t1.c, t3.c))::text) + -> Merge Full Join + Merge Cond: ((t1.c)::text = (t3.c)::text) + -> Merge Join + Merge Cond: ((t1.c)::text = (t2.c)::text) + -> Sort + Sort Key: t1.c + -> Seq Scan on prt1_n_p1 t1 + -> Sort + Sort Key: t2.c + -> Seq Scan on prt1_n_p1 t2 + -> Sort + Sort Key: t3.c + -> Seq Scan on prt1_n_p1 t3 + -> Sort + Sort Key: t4.c + -> Seq Scan on prt1_n_p1 t4 + -> Merge Full Join + Merge Cond: (((COALESCE(t1_1.c, t3_1.c))::text) = (t4_1.c)::text) + -> Sort + Sort Key: ((COALESCE(t1_1.c, t3_1.c))::text) + -> Merge Full Join + Merge Cond: ((t1_1.c)::text = (t3_1.c)::text) + -> Merge Join + Merge Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Sort + Sort Key: t1_1.c + -> Seq Scan on prt1_n_p2_1 t1_1 + -> Sort + Sort Key: t2_1.c + -> Seq Scan on prt1_n_p2_1 t2_1 + -> Sort + Sort Key: t3_1.c + -> Seq Scan on prt1_n_p2_1 t3_1 + -> Sort + Sort Key: t4_1.c + -> Seq Scan on prt1_n_p2_1 t4_1 + -> Merge Full Join + Merge Cond: (((COALESCE(t1_2.c, t3_2.c))::text) = (t4_2.c)::text) + -> Sort + Sort Key: ((COALESCE(t1_2.c, t3_2.c))::text) + -> Merge Full Join + Merge Cond: ((t1_2.c)::text = (t3_2.c)::text) + -> Merge Join + Merge Cond: ((t1_2.c)::text = (t2_2.c)::text) + -> Sort + Sort Key: t1_2.c + -> Seq Scan on prt1_n_p2_2 t1_2 + -> Sort + Sort Key: t2_2.c + -> Seq Scan on prt1_n_p2_2 t2_2 + -> Sort + Sort Key: t3_2.c + -> Seq Scan on prt1_n_p2_2 t3_2 + -> Sort + Sort Key: t4_2.c + -> Seq Scan on prt1_n_p2_2 t4_2 +(64 rows) + SELECT * FROM prt1_n t1 INNER JOIN prt1_n t2 USING (c) FULL JOIN prt1_n t3 USING (c) FULL JOIN prt1_n t4 USING (c) ORDER BY c LIMIT 5; -ERROR: could not find pathkey item to sort + c | a | b | a | b | a | b | a | b +------+---+---+---+---+---+---+---+--- + 0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 + 0002 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 + 0004 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 + 0006 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 + 0008 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 +(5 rows) + -- Beware of non-key join columns sneaking in the manually written COALESCE -- expressions; can't use partitionwise join in that case. ALTER TABLE prt1_n ADD d varchar DEFAULT '0002'; -- 2.11.0