Skip to content

Commit 0d49135

Browse files
committed
Fix eclass_useful_for_merging to give valid results for appendrel children.
Formerly, this function would always return "true" for an appendrel child relation, because it would think that the appendrel parent was a potential join target for the child. In principle that should only lead to some inefficiency in planning, but fuzz testing by Andreas Seltenreich disclosed that it could lead to "could not find pathkey item to sort" planner errors in odd corner cases. Specifically, we would think that all columns of a child table's multicolumn index were interesting pathkeys, causing us to generate a MergeAppend path that sorts by all the columns. However, if any of those columns weren't actually used above the level of the appendrel, they would not get added to that rel's targetlist, which would result in being unable to resolve the MergeAppend's sort keys against its targetlist during createplan.c. Backpatch to 9.3. In older versions, columns of an appendrel get added to its targetlist even if they're not mentioned above the scan level, so that the failure doesn't occur. It might be worth back-patching this fix to older versions anyway, but I'll refrain for the moment.
1 parent 3e79144 commit 0d49135

File tree

5 files changed

+69
-5
lines changed

5 files changed

+69
-5
lines changed

src/backend/optimizer/path/equivclass.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,9 +2286,11 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
22862286
* from actually being generated.
22872287
*/
22882288
bool
2289-
eclass_useful_for_merging(EquivalenceClass *eclass,
2289+
eclass_useful_for_merging(PlannerInfo *root,
2290+
EquivalenceClass *eclass,
22902291
RelOptInfo *rel)
22912292
{
2293+
Relids relids;
22922294
ListCell *lc;
22932295

22942296
Assert(!eclass->ec_merged);
@@ -2306,8 +2308,14 @@ eclass_useful_for_merging(EquivalenceClass *eclass,
23062308
* possibly-overoptimistic heuristic.
23072309
*/
23082310

2311+
/* If specified rel is a child, we must consider the topmost parent rel */
2312+
if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
2313+
relids = find_childrel_top_parent(root, rel)->relids;
2314+
else
2315+
relids = rel->relids;
2316+
23092317
/* If rel already includes all members of eclass, no point in searching */
2310-
if (bms_is_subset(eclass->ec_relids, rel->relids))
2318+
if (bms_is_subset(eclass->ec_relids, relids))
23112319
return false;
23122320

23132321
/* To join, we need a member not in the given rel */
@@ -2318,7 +2326,7 @@ eclass_useful_for_merging(EquivalenceClass *eclass,
23182326
if (cur_em->em_is_child)
23192327
continue; /* ignore children here */
23202328

2321-
if (!bms_overlap(cur_em->em_relids, rel->relids))
2329+
if (!bms_overlap(cur_em->em_relids, relids))
23222330
return true;
23232331
}
23242332

src/backend/optimizer/path/pathkeys.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1322,7 +1322,7 @@ pathkeys_useful_for_merging(PlannerInfo *root, RelOptInfo *rel, List *pathkeys)
13221322
* surely possible to generate a mergejoin clause using them.
13231323
*/
13241324
if (rel->has_eclass_joins &&
1325-
eclass_useful_for_merging(pathkey->pk_eclass, rel))
1325+
eclass_useful_for_merging(root, pathkey->pk_eclass, rel))
13261326
matched = true;
13271327
else
13281328
{

src/include/optimizer/paths.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ extern bool have_relevant_eclass_joinclause(PlannerInfo *root,
139139
RelOptInfo *rel1, RelOptInfo *rel2);
140140
extern bool has_relevant_eclass_joinclause(PlannerInfo *root,
141141
RelOptInfo *rel1);
142-
extern bool eclass_useful_for_merging(EquivalenceClass *eclass,
142+
extern bool eclass_useful_for_merging(PlannerInfo *root,
143+
EquivalenceClass *eclass,
143144
RelOptInfo *rel);
144145
extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
145146

src/test/regress/expected/inherit.out

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,40 @@ DETAIL: drop cascades to table matest1
13201320
drop cascades to table matest2
13211321
drop cascades to table matest3
13221322
--
1323+
-- Check that use of an index with an extraneous column doesn't produce
1324+
-- a plan with extraneous sorting
1325+
--
1326+
create table matest0 (a int, b int, c int, d int);
1327+
create table matest1 () inherits(matest0);
1328+
create index matest0i on matest0 (b, c);
1329+
create index matest1i on matest1 (b, c);
1330+
set enable_nestloop = off; -- we want a plan with two MergeAppends
1331+
explain (costs off)
1332+
select t1.* from matest0 t1, matest0 t2
1333+
where t1.b = t2.b and t2.c = t2.d
1334+
order by t1.b limit 10;
1335+
QUERY PLAN
1336+
-------------------------------------------------------------------
1337+
Limit
1338+
-> Merge Join
1339+
Merge Cond: (t1.b = t2.b)
1340+
-> Merge Append
1341+
Sort Key: t1.b
1342+
-> Index Scan using matest0i on matest0 t1
1343+
-> Index Scan using matest1i on matest1 t1_1
1344+
-> Materialize
1345+
-> Merge Append
1346+
Sort Key: t2.b
1347+
-> Index Scan using matest0i on matest0 t2
1348+
Filter: (c = d)
1349+
-> Index Scan using matest1i on matest1 t2_1
1350+
Filter: (c = d)
1351+
(14 rows)
1352+
1353+
reset enable_nestloop;
1354+
drop table matest0 cascade;
1355+
NOTICE: drop cascades to table matest1
1356+
--
13231357
-- Test merge-append for UNION ALL append relations
13241358
--
13251359
set enable_seqscan = off;

src/test/regress/sql/inherit.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,27 @@ reset enable_seqscan;
402402

403403
drop table matest0 cascade;
404404

405+
--
406+
-- Check that use of an index with an extraneous column doesn't produce
407+
-- a plan with extraneous sorting
408+
--
409+
410+
create table matest0 (a int, b int, c int, d int);
411+
create table matest1 () inherits(matest0);
412+
create index matest0i on matest0 (b, c);
413+
create index matest1i on matest1 (b, c);
414+
415+
set enable_nestloop = off; -- we want a plan with two MergeAppends
416+
417+
explain (costs off)
418+
select t1.* from matest0 t1, matest0 t2
419+
where t1.b = t2.b and t2.c = t2.d
420+
order by t1.b limit 10;
421+
422+
reset enable_nestloop;
423+
424+
drop table matest0 cascade;
425+
405426
--
406427
-- Test merge-append for UNION ALL append relations
407428
--

0 commit comments

Comments
 (0)