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:
parent
b777be0d48
commit
a87c729153
@ -1021,10 +1021,15 @@ get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
* accumulate_append_subpath
|
* accumulate_append_subpath
|
||||||
* Add a subpath to the list being built for an Append or MergeAppend
|
* 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
|
* It's possible that the child is itself an Append or MergeAppend path, in
|
||||||
* we can "cut out the middleman" and just add its child paths to our
|
* which case we can "cut out the middleman" and just add its child paths to
|
||||||
* own list. (We don't try to do this earlier because we need to
|
* our own list. (We don't try to do this earlier because we need to apply
|
||||||
* apply both levels of transformation to the quals.)
|
* 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 *
|
static List *
|
||||||
accumulate_append_subpath(List *subpaths, Path *path)
|
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 */
|
/* list_copy is important here to avoid sharing list substructure */
|
||||||
return list_concat(subpaths, list_copy(apath->subpaths));
|
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
|
else
|
||||||
return lappend(subpaths, path);
|
return lappend(subpaths, path);
|
||||||
}
|
}
|
||||||
|
@ -1937,16 +1937,20 @@ add_child_rel_equivalences(PlannerInfo *root,
|
|||||||
if (cur_ec->ec_has_volatile)
|
if (cur_ec->ec_has_volatile)
|
||||||
continue;
|
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;
|
continue;
|
||||||
|
|
||||||
foreach(lc2, cur_ec->ec_members)
|
foreach(lc2, cur_ec->ec_members)
|
||||||
{
|
{
|
||||||
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
|
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
|
||||||
|
|
||||||
if (cur_em->em_is_const || cur_em->em_is_child)
|
if (cur_em->em_is_const)
|
||||||
continue; /* ignore consts and children here */
|
continue; /* ignore consts here */
|
||||||
|
|
||||||
/* Does it reference parent_rel? */
|
/* Does it reference parent_rel? */
|
||||||
if (bms_overlap(cur_em->em_relids, parent_rel->relids))
|
if (bms_overlap(cur_em->em_relids, parent_rel->relids))
|
||||||
|
@ -751,7 +751,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
|
|||||||
|
|
||||||
/* Compute sort column info, and adjust MergeAppend's tlist as needed */
|
/* Compute sort column info, and adjust MergeAppend's tlist as needed */
|
||||||
(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
|
(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
|
||||||
NULL,
|
best_path->path.parent->relids,
|
||||||
NULL,
|
NULL,
|
||||||
true,
|
true,
|
||||||
&node->numCols,
|
&node->numCols,
|
||||||
|
@ -502,9 +502,56 @@ explain (costs off)
|
|||||||
Index Cond: (ab = 'ab'::text)
|
Index Cond: (ab = 'ab'::text)
|
||||||
(7 rows)
|
(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_seqscan;
|
||||||
reset enable_indexscan;
|
reset enable_indexscan;
|
||||||
reset enable_bitmapscan;
|
reset enable_bitmapscan;
|
||||||
|
reset enable_indexonlyscan;
|
||||||
-- Test constraint exclusion of UNION ALL subqueries
|
-- Test constraint exclusion of UNION ALL subqueries
|
||||||
explain (costs off)
|
explain (costs off)
|
||||||
SELECT * FROM
|
SELECT * FROM
|
||||||
|
@ -198,9 +198,38 @@ explain (costs off)
|
|||||||
SELECT * FROM t2) t
|
SELECT * FROM t2) t
|
||||||
WHERE ab = 'ab';
|
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_seqscan;
|
||||||
reset enable_indexscan;
|
reset enable_indexscan;
|
||||||
reset enable_bitmapscan;
|
reset enable_bitmapscan;
|
||||||
|
reset enable_indexonlyscan;
|
||||||
|
|
||||||
-- Test constraint exclusion of UNION ALL subqueries
|
-- Test constraint exclusion of UNION ALL subqueries
|
||||||
explain (costs off)
|
explain (costs off)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user