Fix EquivalenceClass processing for nested append relations.

The original coding of EquivalenceClasses didn't foresee that appendrel
child relations might themselves be appendrels; but this is possible for
example when a UNION ALL subquery scans a table with inheritance children.
The oversight led to failure to optimize ordering-related issues very well
for the grandchild tables.  After some false starts involving explicitly
flattening the appendrel representation, we found that this could be fixed
easily by removing a few implicit assumptions about appendrel parent rels
not being children themselves.

Kyotaro Horiguchi and Tom Lane, reviewed by Noah Misch
This commit is contained in:
Tom Lane 2014-03-28 11:50:01 -04:00
parent b777be0d48
commit a87c729153
5 changed files with 101 additions and 9 deletions

View File

@ -1021,10 +1021,15 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
* accumulate_append_subpath
* Add a subpath to the list being built for an Append or MergeAppend
*
* It's possible that the child is itself an Append path, in which case
* we can "cut out the middleman" and just add its child paths to our
* own list. (We don't try to do this earlier because we need to
* apply both levels of transformation to the quals.)
* It's possible that the child is itself an Append or MergeAppend path, in
* which case we can "cut out the middleman" and just add its child paths to
* our own list. (We don't try to do this earlier because we need to apply
* both levels of transformation to the quals.)
*
* Note that if we omit a child MergeAppend in this way, we are effectively
* omitting a sort step, which seems fine: if the parent is to be an Append,
* its result would be unsorted anyway, while if the parent is to be a
* MergeAppend, there's no point in a separate sort on a child.
*/
static List *
accumulate_append_subpath(List *subpaths, Path *path)
@ -1036,6 +1041,13 @@ accumulate_append_subpath(List *subpaths, Path *path)
/* list_copy is important here to avoid sharing list substructure */
return list_concat(subpaths, list_copy(apath->subpaths));
}
else if (IsA(path, MergeAppendPath))
{
MergeAppendPath *mpath = (MergeAppendPath *) path;
/* list_copy is important here to avoid sharing list substructure */
return list_concat(subpaths, list_copy(mpath->subpaths));
}
else
return lappend(subpaths, path);
}

View File

@ -1937,16 +1937,20 @@ add_child_rel_equivalences(PlannerInfo *root,
if (cur_ec->ec_has_volatile)
continue;
/* No point in searching if parent rel not mentioned in eclass */
if (!bms_is_subset(parent_rel->relids, cur_ec->ec_relids))
/*
* No point in searching if parent rel not mentioned in eclass; but
* we can't tell that for sure if parent rel is itself a child.
*/
if (parent_rel->reloptkind == RELOPT_BASEREL &&
!bms_is_subset(parent_rel->relids, cur_ec->ec_relids))
continue;
foreach(lc2, cur_ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
if (cur_em->em_is_const || cur_em->em_is_child)
continue; /* ignore consts and children here */
if (cur_em->em_is_const)
continue; /* ignore consts here */
/* Does it reference parent_rel? */
if (bms_overlap(cur_em->em_relids, parent_rel->relids))

View File

@ -751,7 +751,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
/* Compute sort column info, and adjust MergeAppend's tlist as needed */
(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
NULL,
best_path->path.parent->relids,
NULL,
true,
&node->numCols,

View File

@ -502,9 +502,56 @@ explain (costs off)
Index Cond: (ab = 'ab'::text)
(7 rows)
--
-- Test that ORDER BY for UNION ALL can be pushed down to inheritance
-- children.
--
CREATE TEMP TABLE t1c (b text, a text);
ALTER TABLE t1c INHERIT t1;
CREATE TEMP TABLE t2c (primary key (ab)) INHERITS (t2);
INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f');
INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef');
CREATE INDEX t1c_ab_idx on t1c ((a || b));
set enable_seqscan = on;
set enable_indexonlyscan = off;
explain (costs off)
SELECT * FROM
(SELECT a || b AS ab FROM t1
UNION ALL
SELECT ab FROM t2) t
ORDER BY 1 LIMIT 8;
QUERY PLAN
------------------------------------------------
Limit
-> Merge Append
Sort Key: ((t1.a || t1.b))
-> Index Scan using t1_ab_idx on t1
-> Index Scan using t1c_ab_idx on t1c
-> Index Scan using t2_pkey on t2
-> Index Scan using t2c_pkey on t2c
(7 rows)
SELECT * FROM
(SELECT a || b AS ab FROM t1
UNION ALL
SELECT ab FROM t2) t
ORDER BY 1 LIMIT 8;
ab
----
ab
ab
cd
dc
ef
fe
mn
nm
(8 rows)
reset enable_seqscan;
reset enable_indexscan;
reset enable_bitmapscan;
reset enable_indexonlyscan;
-- Test constraint exclusion of UNION ALL subqueries
explain (costs off)
SELECT * FROM

View File

@ -198,9 +198,38 @@ explain (costs off)
SELECT * FROM t2) t
WHERE ab = 'ab';
--
-- Test that ORDER BY for UNION ALL can be pushed down to inheritance
-- children.
--
CREATE TEMP TABLE t1c (b text, a text);
ALTER TABLE t1c INHERIT t1;
CREATE TEMP TABLE t2c (primary key (ab)) INHERITS (t2);
INSERT INTO t1c VALUES ('v', 'w'), ('c', 'd'), ('m', 'n'), ('e', 'f');
INSERT INTO t2c VALUES ('vw'), ('cd'), ('mn'), ('ef');
CREATE INDEX t1c_ab_idx on t1c ((a || b));
set enable_seqscan = on;
set enable_indexonlyscan = off;
explain (costs off)
SELECT * FROM
(SELECT a || b AS ab FROM t1
UNION ALL
SELECT ab FROM t2) t
ORDER BY 1 LIMIT 8;
SELECT * FROM
(SELECT a || b AS ab FROM t1
UNION ALL
SELECT ab FROM t2) t
ORDER BY 1 LIMIT 8;
reset enable_seqscan;
reset enable_indexscan;
reset enable_bitmapscan;
reset enable_indexonlyscan;
-- Test constraint exclusion of UNION ALL subqueries
explain (costs off)