From 3c90dcd039149716454378bd270673f781b5c19f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 21 Jul 2023 12:00:14 -0400 Subject: [PATCH] Fix calculation of relid sets for partitionwise child joins. Applying add_outer_joins_to_relids() to a child join doesn't actually work, even if we've built a SpecialJoinInfo specialized to the child, because that function will also compare the join's relids to elements of the main join_info_list, which only deal in regular relids not child relids. This mistake escaped detection by the existing partitionwise join tests because they didn't test any cases where add_outer_joins_to_relids() needs to add additional OJ relids (that is, any cases where join reordering per identity 3 is possible). Instead, let's apply adjust_child_relids() to the relids of the parent join. This requires minor code reordering to collect the relevant AppendRelInfo structures first, but that's work we'd do shortly anyway. Report and fix by Richard Guo; cosmetic changes by me Discussion: https://postgr.es/m/CAMbWs49NCNbyubZWgci3o=_OTY=snCfAPtMnM-32f3mm-K-Ckw@mail.gmail.com --- src/backend/optimizer/path/joinrels.c | 14 +++--- src/backend/optimizer/util/relnode.c | 18 +++++--- src/test/regress/expected/partition_join.out | 46 ++++++++++++++++++++ src/test/regress/sql/partition_join.sql | 9 ++++ 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 2feab2184f..a3f94be1d6 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -1640,13 +1640,15 @@ try_partitionwise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, child_rel1->relids, child_rel2->relids); - /* Build correct join relids for child join */ - child_joinrelids = bms_union(child_rel1->relids, child_rel2->relids); - child_joinrelids = add_outer_joins_to_relids(root, child_joinrelids, - child_sjinfo, NULL); - /* Find the AppendRelInfo structures */ - appinfos = find_appinfos_by_relids(root, child_joinrelids, &nappinfos); + appinfos = find_appinfos_by_relids(root, + bms_union(child_rel1->relids, + child_rel2->relids), + &nappinfos); + + /* Build correct join relids for child join */ + child_joinrelids = adjust_child_relids(joinrel->relids, + nappinfos, appinfos); /* * Construct restrictions applicable to the child join from those diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 15e3910b79..76dad17e33 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -871,10 +871,19 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, /* The parent joinrel should have consider_partitionwise_join set. */ Assert(parent_joinrel->consider_partitionwise_join); + /* + * Find the AppendRelInfo structures for the child baserels. We'll need + * these for computing the child join's relid set, and later for mapping + * Vars to the child rel. + */ + appinfos = find_appinfos_by_relids(root, + bms_union(outer_rel->relids, + inner_rel->relids), + &nappinfos); + joinrel->reloptkind = RELOPT_OTHER_JOINREL; - joinrel->relids = bms_union(outer_rel->relids, inner_rel->relids); - joinrel->relids = add_outer_joins_to_relids(root, joinrel->relids, sjinfo, - NULL); + joinrel->relids = adjust_child_relids(parent_joinrel->relids, + nappinfos, appinfos); joinrel->rows = 0; /* cheap startup cost is interesting iff not all tuples to be retrieved */ joinrel->consider_startup = (root->tuple_fraction > 0); @@ -935,9 +944,6 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, /* Compute information relevant to foreign relations. */ set_foreign_rel_properties(joinrel, outer_rel, inner_rel); - /* Compute information needed for mapping Vars to the child rel */ - appinfos = find_appinfos_by_relids(root, joinrel->relids, &nappinfos); - /* Set up reltarget struct */ build_child_join_reltarget(root, parent_joinrel, joinrel, nappinfos, appinfos); diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 762cc8e53b..6560fe2416 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -62,6 +62,52 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 450 | 0450 | 450 | 0450 (4 rows) +-- left outer join, 3-way +EXPLAIN (COSTS OFF) +SELECT COUNT(*) FROM prt1 t1 + LEFT JOIN prt1 t2 ON t1.a = t2.a + LEFT JOIN prt1 t3 ON t2.a = t3.a; + QUERY PLAN +-------------------------------------------------------- + Aggregate + -> Append + -> Hash Left Join + Hash Cond: (t2_1.a = t3_1.a) + -> Hash Left Join + Hash Cond: (t1_1.a = t2_1.a) + -> Seq Scan on prt1_p1 t1_1 + -> Hash + -> Seq Scan on prt1_p1 t2_1 + -> Hash + -> Seq Scan on prt1_p1 t3_1 + -> Hash Left Join + Hash Cond: (t2_2.a = t3_2.a) + -> Hash Left Join + Hash Cond: (t1_2.a = t2_2.a) + -> Seq Scan on prt1_p2 t1_2 + -> Hash + -> Seq Scan on prt1_p2 t2_2 + -> Hash + -> Seq Scan on prt1_p2 t3_2 + -> Hash Left Join + Hash Cond: (t2_3.a = t3_3.a) + -> Hash Left Join + Hash Cond: (t1_3.a = t2_3.a) + -> Seq Scan on prt1_p3 t1_3 + -> Hash + -> Seq Scan on prt1_p3 t2_3 + -> Hash + -> Seq Scan on prt1_p3 t3_3 +(29 rows) + +SELECT COUNT(*) FROM prt1 t1 + LEFT JOIN prt1 t2 ON t1.a = t2.a + LEFT JOIN prt1 t3 ON t2.a = t3.a; + count +------- + 300 +(1 row) + -- left outer join, with whole-row reference; partitionwise join does not apply EXPLAIN (COSTS OFF) SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql index 9e16f1ca55..48daf3aee3 100644 --- a/src/test/regress/sql/partition_join.sql +++ b/src/test/regress/sql/partition_join.sql @@ -34,6 +34,15 @@ EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; +-- left outer join, 3-way +EXPLAIN (COSTS OFF) +SELECT COUNT(*) FROM prt1 t1 + LEFT JOIN prt1 t2 ON t1.a = t2.a + LEFT JOIN prt1 t3 ON t2.a = t3.a; +SELECT COUNT(*) FROM prt1 t1 + LEFT JOIN prt1 t2 ON t1.a = t2.a + LEFT JOIN prt1 t3 ON t2.a = t3.a; + -- left outer join, with whole-row reference; partitionwise join does not apply EXPLAIN (COSTS OFF) SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b;