mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-07-22 22:49:38 +02:00
Fix asymmetry in setting EquivalenceClass.ec_sortref
0452b461bc
made get_eclass_for_sort_expr() always set
EquivalenceClass.ec_sortref if it's not done yet. This leads to an asymmetric
situation when whoever first looks for the EquivalenceClass sets the
ec_sortref. It is also counterintuitive that get_eclass_for_sort_expr()
performs modification of data structures.
This commit makes make_pathkeys_for_sortclauses_extended() responsible for
setting EquivalenceClass.ec_sortref. Now we set the
EquivalenceClass.ec_sortref's needed to explore alternative GROUP BY ordering
specifically during building pathkeys by the list of grouping clauses.
Discussion: https://postgr.es/m/17037754-f187-4138-8285-0e2bfebd0dea%40postgrespro.ru
Reported-by: Tom Lane
Author: Andrei Lepikhov
Reviewed-by: Alexander Korotkov, Pavel Borisov
This commit is contained in:
parent
f654f000dd
commit
199012a3d8
@ -652,18 +652,7 @@ get_eclass_for_sort_expr(PlannerInfo *root,
|
|||||||
|
|
||||||
if (opcintype == cur_em->em_datatype &&
|
if (opcintype == cur_em->em_datatype &&
|
||||||
equal(expr, cur_em->em_expr))
|
equal(expr, cur_em->em_expr))
|
||||||
{
|
return cur_ec; /* Match! */
|
||||||
/*
|
|
||||||
* Match!
|
|
||||||
*
|
|
||||||
* Copy the sortref if it wasn't set yet. That may happen if
|
|
||||||
* the ec was constructed from a WHERE clause, i.e. it doesn't
|
|
||||||
* have a target reference at all.
|
|
||||||
*/
|
|
||||||
if (cur_ec->ec_sortref == 0 && sortref > 0)
|
|
||||||
cur_ec->ec_sortref = sortref;
|
|
||||||
return cur_ec;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1355,7 +1355,8 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
|
|||||||
&sortclauses,
|
&sortclauses,
|
||||||
tlist,
|
tlist,
|
||||||
false,
|
false,
|
||||||
&sortable);
|
&sortable,
|
||||||
|
false);
|
||||||
/* It's caller error if not all clauses were sortable */
|
/* It's caller error if not all clauses were sortable */
|
||||||
Assert(sortable);
|
Assert(sortable);
|
||||||
return result;
|
return result;
|
||||||
@ -1379,13 +1380,17 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
|
|||||||
* to remove any clauses that can be proven redundant via the eclass logic.
|
* to remove any clauses that can be proven redundant via the eclass logic.
|
||||||
* Even though we'll have to hash in that case, we might as well not hash
|
* Even though we'll have to hash in that case, we might as well not hash
|
||||||
* redundant columns.)
|
* redundant columns.)
|
||||||
|
*
|
||||||
|
* If set_ec_sortref is true then sets the value of the pathkey's
|
||||||
|
* EquivalenceClass unless it's already initialized.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
make_pathkeys_for_sortclauses_extended(PlannerInfo *root,
|
make_pathkeys_for_sortclauses_extended(PlannerInfo *root,
|
||||||
List **sortclauses,
|
List **sortclauses,
|
||||||
List *tlist,
|
List *tlist,
|
||||||
bool remove_redundant,
|
bool remove_redundant,
|
||||||
bool *sortable)
|
bool *sortable,
|
||||||
|
bool set_ec_sortref)
|
||||||
{
|
{
|
||||||
List *pathkeys = NIL;
|
List *pathkeys = NIL;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
@ -1409,6 +1414,15 @@ make_pathkeys_for_sortclauses_extended(PlannerInfo *root,
|
|||||||
sortcl->nulls_first,
|
sortcl->nulls_first,
|
||||||
sortcl->tleSortGroupRef,
|
sortcl->tleSortGroupRef,
|
||||||
true);
|
true);
|
||||||
|
if (pathkey->pk_eclass->ec_sortref == 0 && set_ec_sortref)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Copy the sortref if it hasn't been set yet. That may happen if
|
||||||
|
* the EquivalenceClass was constructed from a WHERE clause, i.e.
|
||||||
|
* it doesn't have a target reference at all.
|
||||||
|
*/
|
||||||
|
pathkey->pk_eclass->ec_sortref = sortcl->tleSortGroupRef;
|
||||||
|
}
|
||||||
|
|
||||||
/* Canonical form eliminates redundant ordering keys */
|
/* Canonical form eliminates redundant ordering keys */
|
||||||
if (!pathkey_is_redundant(pathkey, pathkeys))
|
if (!pathkey_is_redundant(pathkey, pathkeys))
|
||||||
|
@ -3395,12 +3395,17 @@ standard_qp_callback(PlannerInfo *root, void *extra)
|
|||||||
*/
|
*/
|
||||||
bool sortable;
|
bool sortable;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert group clauses into pathkeys. Set the ec_sortref field of
|
||||||
|
* EquivalenceClass'es if it's not set yet.
|
||||||
|
*/
|
||||||
root->group_pathkeys =
|
root->group_pathkeys =
|
||||||
make_pathkeys_for_sortclauses_extended(root,
|
make_pathkeys_for_sortclauses_extended(root,
|
||||||
&root->processed_groupClause,
|
&root->processed_groupClause,
|
||||||
tlist,
|
tlist,
|
||||||
true,
|
true,
|
||||||
&sortable);
|
&sortable,
|
||||||
|
true);
|
||||||
if (!sortable)
|
if (!sortable)
|
||||||
{
|
{
|
||||||
/* Can't sort; no point in considering aggregate ordering either */
|
/* Can't sort; no point in considering aggregate ordering either */
|
||||||
@ -3450,7 +3455,8 @@ standard_qp_callback(PlannerInfo *root, void *extra)
|
|||||||
&root->processed_distinctClause,
|
&root->processed_distinctClause,
|
||||||
tlist,
|
tlist,
|
||||||
true,
|
true,
|
||||||
&sortable);
|
&sortable,
|
||||||
|
false);
|
||||||
if (!sortable)
|
if (!sortable)
|
||||||
root->distinct_pathkeys = NIL;
|
root->distinct_pathkeys = NIL;
|
||||||
}
|
}
|
||||||
@ -3476,7 +3482,8 @@ standard_qp_callback(PlannerInfo *root, void *extra)
|
|||||||
&groupClauses,
|
&groupClauses,
|
||||||
tlist,
|
tlist,
|
||||||
false,
|
false,
|
||||||
&sortable);
|
&sortable,
|
||||||
|
false);
|
||||||
if (!sortable)
|
if (!sortable)
|
||||||
root->setop_pathkeys = NIL;
|
root->setop_pathkeys = NIL;
|
||||||
}
|
}
|
||||||
@ -6061,7 +6068,8 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
|
|||||||
&wc->partitionClause,
|
&wc->partitionClause,
|
||||||
tlist,
|
tlist,
|
||||||
true,
|
true,
|
||||||
&sortable);
|
&sortable,
|
||||||
|
false);
|
||||||
|
|
||||||
Assert(sortable);
|
Assert(sortable);
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,8 @@ extern List *make_pathkeys_for_sortclauses_extended(PlannerInfo *root,
|
|||||||
List **sortclauses,
|
List **sortclauses,
|
||||||
List *tlist,
|
List *tlist,
|
||||||
bool remove_redundant,
|
bool remove_redundant,
|
||||||
bool *sortable);
|
bool *sortable,
|
||||||
|
bool set_ec_sortref);
|
||||||
extern void initialize_mergeclause_eclasses(PlannerInfo *root,
|
extern void initialize_mergeclause_eclasses(PlannerInfo *root,
|
||||||
RestrictInfo *restrictinfo);
|
RestrictInfo *restrictinfo);
|
||||||
extern void update_mergeclause_eclasses(PlannerInfo *root,
|
extern void update_mergeclause_eclasses(PlannerInfo *root,
|
||||||
|
@ -2917,6 +2917,53 @@ GROUP BY c1.w, c1.z;
|
|||||||
5.0000000000000000
|
5.0000000000000000
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
-- Pathkeys, built in a subtree, can be used to optimize GROUP-BY clause
|
||||||
|
-- ordering. Also, here we check that it doesn't depend on the initial clause
|
||||||
|
-- order in the GROUP-BY list.
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT c1.y,c1.x FROM group_agg_pk c1
|
||||||
|
JOIN group_agg_pk c2
|
||||||
|
ON c1.x = c2.x
|
||||||
|
GROUP BY c1.y,c1.x,c2.x;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------------
|
||||||
|
Group
|
||||||
|
Group Key: c1.x, c1.y
|
||||||
|
-> Incremental Sort
|
||||||
|
Sort Key: c1.x, c1.y
|
||||||
|
Presorted Key: c1.x
|
||||||
|
-> Merge Join
|
||||||
|
Merge Cond: (c1.x = c2.x)
|
||||||
|
-> Sort
|
||||||
|
Sort Key: c1.x
|
||||||
|
-> Seq Scan on group_agg_pk c1
|
||||||
|
-> Sort
|
||||||
|
Sort Key: c2.x
|
||||||
|
-> Seq Scan on group_agg_pk c2
|
||||||
|
(13 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT c1.y,c1.x FROM group_agg_pk c1
|
||||||
|
JOIN group_agg_pk c2
|
||||||
|
ON c1.x = c2.x
|
||||||
|
GROUP BY c1.y,c2.x,c1.x;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------------
|
||||||
|
Group
|
||||||
|
Group Key: c2.x, c1.y
|
||||||
|
-> Incremental Sort
|
||||||
|
Sort Key: c2.x, c1.y
|
||||||
|
Presorted Key: c2.x
|
||||||
|
-> Merge Join
|
||||||
|
Merge Cond: (c1.x = c2.x)
|
||||||
|
-> Sort
|
||||||
|
Sort Key: c1.x
|
||||||
|
-> Seq Scan on group_agg_pk c1
|
||||||
|
-> Sort
|
||||||
|
Sort Key: c2.x
|
||||||
|
-> Seq Scan on group_agg_pk c2
|
||||||
|
(13 rows)
|
||||||
|
|
||||||
RESET enable_nestloop;
|
RESET enable_nestloop;
|
||||||
RESET enable_hashjoin;
|
RESET enable_hashjoin;
|
||||||
DROP TABLE group_agg_pk;
|
DROP TABLE group_agg_pk;
|
||||||
|
@ -1263,6 +1263,20 @@ SELECT avg(c1.f ORDER BY c1.x, c1.y)
|
|||||||
FROM group_agg_pk c1 JOIN group_agg_pk c2 ON c1.x = c2.x
|
FROM group_agg_pk c1 JOIN group_agg_pk c2 ON c1.x = c2.x
|
||||||
GROUP BY c1.w, c1.z;
|
GROUP BY c1.w, c1.z;
|
||||||
|
|
||||||
|
-- Pathkeys, built in a subtree, can be used to optimize GROUP-BY clause
|
||||||
|
-- ordering. Also, here we check that it doesn't depend on the initial clause
|
||||||
|
-- order in the GROUP-BY list.
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT c1.y,c1.x FROM group_agg_pk c1
|
||||||
|
JOIN group_agg_pk c2
|
||||||
|
ON c1.x = c2.x
|
||||||
|
GROUP BY c1.y,c1.x,c2.x;
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT c1.y,c1.x FROM group_agg_pk c1
|
||||||
|
JOIN group_agg_pk c2
|
||||||
|
ON c1.x = c2.x
|
||||||
|
GROUP BY c1.y,c2.x,c1.x;
|
||||||
|
|
||||||
RESET enable_nestloop;
|
RESET enable_nestloop;
|
||||||
RESET enable_hashjoin;
|
RESET enable_hashjoin;
|
||||||
DROP TABLE group_agg_pk;
|
DROP TABLE group_agg_pk;
|
||||||
|
Loading…
Reference in New Issue
Block a user