mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix lateral references in sort keys added by createplan.c
When prepare_sort_from_pathkeys() cannot find a pathkey's expression in the input plan's targetlist, it adds a resjunk entry computing an EquivalenceClass member's expression. It copied that expression as-is, without replacing lateral references with nestloop Params the way build_path_tlist() does for the rest of the targetlist. This matters when the sort key contains a PlaceHolderVar whose expression has a lateral reference, and the sort is placed directly atop the plan node that evaluates the PlaceHolderVar. The targetlist already contains the PlaceHolderVar, so the sort key is considered computable, but the entry added for it still has the outer relation's Var inside the PlaceHolderVar. If the node is a join, setrefs.c fails with "variable not found in subplan target lists". If it is a scan, setrefs.c leaves the Var in place, and the executor fetches it from the scan tuple, leading to wrong results. To fix, pass root to prepare_sort_from_pathkeys() and apply replace_nestloop_params() to the expression before adding it to the targetlist. Back-patch to all supported branches. Author: Richard Guo <guofenglinux@gmail.com> Discussion: https://postgr.es/m/CAMbWs49LWNm7_OxuW1V71wLi+Ev3KbfY1bMY36uydNKwSPnbtQ@mail.gmail.com Backpatch-through: 14
This commit is contained in:
@@ -261,7 +261,8 @@ static IncrementalSort *make_incrementalsort(Plan *lefttree,
|
||||
int numCols, int nPresortedCols,
|
||||
AttrNumber *sortColIdx, Oid *sortOperators,
|
||||
Oid *collations, bool *nullsFirst);
|
||||
static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
|
||||
Plan *lefttree, List *pathkeys,
|
||||
Relids relids,
|
||||
const AttrNumber *reqColIdx,
|
||||
bool adjust_tlist_in_place,
|
||||
@@ -270,10 +271,13 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
Oid **p_sortOperators,
|
||||
Oid **p_collations,
|
||||
bool **p_nullsFirst);
|
||||
static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
Relids relids);
|
||||
static IncrementalSort *make_incrementalsort_from_pathkeys(Plan *lefttree,
|
||||
List *pathkeys, Relids relids, int nPresortedCols);
|
||||
static Sort *make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
|
||||
List *pathkeys, Relids relids);
|
||||
static IncrementalSort *make_incrementalsort_from_pathkeys(PlannerInfo *root,
|
||||
Plan *lefttree,
|
||||
List *pathkeys,
|
||||
Relids relids,
|
||||
int nPresortedCols);
|
||||
static Sort *make_sort_from_groupcols(List *groupcls,
|
||||
AttrNumber *grpColIdx,
|
||||
Plan *lefttree);
|
||||
@@ -1262,7 +1266,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
|
||||
* function result; it must be the same plan node. However, we then
|
||||
* need to detect whether any tlist entries were added.
|
||||
*/
|
||||
(void) prepare_sort_from_pathkeys((Plan *) plan, pathkeys,
|
||||
(void) prepare_sort_from_pathkeys(root, (Plan *) plan, pathkeys,
|
||||
best_path->path.parent->relids,
|
||||
NULL,
|
||||
true,
|
||||
@@ -1306,7 +1310,7 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
|
||||
* don't need an explicit sort, to make sure they are returning
|
||||
* the same sort key columns the Append expects.
|
||||
*/
|
||||
subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
|
||||
subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
|
||||
subpath->parent->relids,
|
||||
nodeSortColIdx,
|
||||
false,
|
||||
@@ -1447,7 +1451,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
|
||||
* function result; it must be the same plan node. However, we then need
|
||||
* to detect whether any tlist entries were added.
|
||||
*/
|
||||
(void) prepare_sort_from_pathkeys(plan, pathkeys,
|
||||
(void) prepare_sort_from_pathkeys(root, plan, pathkeys,
|
||||
best_path->path.parent->relids,
|
||||
NULL,
|
||||
true,
|
||||
@@ -1478,7 +1482,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path,
|
||||
subplan = create_plan_recurse(root, subpath, CP_EXACT_TLIST);
|
||||
|
||||
/* Compute sort column info, and adjust subplan's tlist as needed */
|
||||
subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
|
||||
subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
|
||||
subpath->parent->relids,
|
||||
node->sortColIdx,
|
||||
false,
|
||||
@@ -1957,7 +1961,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path)
|
||||
Assert(pathkeys != NIL);
|
||||
|
||||
/* Compute sort column info, and adjust subplan's tlist as needed */
|
||||
subplan = prepare_sort_from_pathkeys(subplan, pathkeys,
|
||||
subplan = prepare_sort_from_pathkeys(root, subplan, pathkeys,
|
||||
best_path->subpath->parent->relids,
|
||||
gm_plan->sortColIdx,
|
||||
false,
|
||||
@@ -2176,7 +2180,7 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags)
|
||||
* relids. Thus, if this sort path is based on a child relation, we must
|
||||
* pass its relids.
|
||||
*/
|
||||
plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys,
|
||||
plan = make_sort_from_pathkeys(root, subplan, best_path->path.pathkeys,
|
||||
IS_OTHER_REL(best_path->subpath->parent) ?
|
||||
best_path->path.parent->relids : NULL);
|
||||
|
||||
@@ -2200,7 +2204,7 @@ create_incrementalsort_plan(PlannerInfo *root, IncrementalSortPath *best_path,
|
||||
/* See comments in create_sort_plan() above */
|
||||
subplan = create_plan_recurse(root, best_path->spath.subpath,
|
||||
flags | CP_SMALL_TLIST);
|
||||
plan = make_incrementalsort_from_pathkeys(subplan,
|
||||
plan = make_incrementalsort_from_pathkeys(root, subplan,
|
||||
best_path->spath.path.pathkeys,
|
||||
IS_OTHER_REL(best_path->spath.subpath->parent) ?
|
||||
best_path->spath.path.parent->relids : NULL,
|
||||
@@ -4493,7 +4497,7 @@ create_mergejoin_plan(PlannerInfo *root,
|
||||
if (best_path->outersortkeys)
|
||||
{
|
||||
Relids outer_relids = outer_path->parent->relids;
|
||||
Sort *sort = make_sort_from_pathkeys(outer_plan,
|
||||
Sort *sort = make_sort_from_pathkeys(root, outer_plan,
|
||||
best_path->outersortkeys,
|
||||
outer_relids);
|
||||
|
||||
@@ -4507,7 +4511,7 @@ create_mergejoin_plan(PlannerInfo *root,
|
||||
if (best_path->innersortkeys)
|
||||
{
|
||||
Relids inner_relids = inner_path->parent->relids;
|
||||
Sort *sort = make_sort_from_pathkeys(inner_plan,
|
||||
Sort *sort = make_sort_from_pathkeys(root, inner_plan,
|
||||
best_path->innersortkeys,
|
||||
inner_relids);
|
||||
|
||||
@@ -6095,6 +6099,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
|
||||
* adjusts the plan targetlist if needed to add resjunk sort columns.
|
||||
*
|
||||
* Input parameters:
|
||||
* 'root' is the PlannerInfo, needed to replace lateral references
|
||||
* 'lefttree' is the plan node which yields input tuples
|
||||
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
|
||||
* 'relids' identifies the relation being sorted, or is NULL
|
||||
@@ -6130,7 +6135,7 @@ make_incrementalsort(Plan *lefttree, int numCols, int nPresortedCols,
|
||||
* or a Result stacked atop lefttree).
|
||||
*/
|
||||
static Plan *
|
||||
prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
|
||||
Relids relids,
|
||||
const AttrNumber *reqColIdx,
|
||||
bool adjust_tlist_in_place,
|
||||
@@ -6165,6 +6170,7 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
EquivalenceClass *ec = pathkey->pk_eclass;
|
||||
EquivalenceMember *em;
|
||||
TargetEntry *tle = NULL;
|
||||
Node *sortexpr;
|
||||
Oid pk_datatype = InvalidOid;
|
||||
Oid sortop;
|
||||
ListCell *j;
|
||||
@@ -6262,9 +6268,11 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
adjust_tlist_in_place = true;
|
||||
|
||||
/*
|
||||
* Add resjunk entry to input's tlist
|
||||
* Add resjunk entry to input's tlist, replacing lateral
|
||||
* references.
|
||||
*/
|
||||
tle = makeTargetEntry(copyObject(em->em_expr),
|
||||
sortexpr = replace_nestloop_params(root, (Node *) em->em_expr);
|
||||
tle = makeTargetEntry(copyObject((Expr *) sortexpr),
|
||||
list_length(tlist) + 1,
|
||||
NULL,
|
||||
true);
|
||||
@@ -6307,12 +6315,14 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
* make_sort_from_pathkeys
|
||||
* Create sort plan to sort according to given pathkeys
|
||||
*
|
||||
* 'root' is the PlannerInfo required by prepare_sort_from_pathkeys()
|
||||
* 'lefttree' is the node which yields input tuples
|
||||
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
|
||||
* 'relids' is the set of relations required by prepare_sort_from_pathkeys()
|
||||
*/
|
||||
static Sort *
|
||||
make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
|
||||
make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
|
||||
Relids relids)
|
||||
{
|
||||
int numsortkeys;
|
||||
AttrNumber *sortColIdx;
|
||||
@@ -6321,7 +6331,7 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
|
||||
bool *nullsFirst;
|
||||
|
||||
/* Compute sort column info, and adjust lefttree as needed */
|
||||
lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
|
||||
lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
|
||||
relids,
|
||||
NULL,
|
||||
false,
|
||||
@@ -6341,14 +6351,16 @@ make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids)
|
||||
* make_incrementalsort_from_pathkeys
|
||||
* Create sort plan to sort according to given pathkeys
|
||||
*
|
||||
* 'root' is the PlannerInfo required by prepare_sort_from_pathkeys()
|
||||
* 'lefttree' is the node which yields input tuples
|
||||
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
|
||||
* 'relids' is the set of relations required by prepare_sort_from_pathkeys()
|
||||
* 'nPresortedCols' is the number of presorted columns in input tuples
|
||||
*/
|
||||
static IncrementalSort *
|
||||
make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
Relids relids, int nPresortedCols)
|
||||
make_incrementalsort_from_pathkeys(PlannerInfo *root, Plan *lefttree,
|
||||
List *pathkeys, Relids relids,
|
||||
int nPresortedCols)
|
||||
{
|
||||
int numsortkeys;
|
||||
AttrNumber *sortColIdx;
|
||||
@@ -6357,7 +6369,7 @@ make_incrementalsort_from_pathkeys(Plan *lefttree, List *pathkeys,
|
||||
bool *nullsFirst;
|
||||
|
||||
/* Compute sort column info, and adjust lefttree as needed */
|
||||
lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys,
|
||||
lefttree = prepare_sort_from_pathkeys(root, lefttree, pathkeys,
|
||||
relids,
|
||||
NULL,
|
||||
false,
|
||||
|
||||
@@ -8645,6 +8645,92 @@ order by 1, 2;
|
||||
0 | 4567890123456789 | 1
|
||||
(5 rows)
|
||||
|
||||
-- check that lateral references in a sort key added below a mergejoin are
|
||||
-- replaced with nestloop params
|
||||
set enable_hashjoin to off;
|
||||
set enable_nestloop to off;
|
||||
explain (verbose, costs off)
|
||||
select * from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1 from int4_tbl t3 where t3.f1 = 0) s(x) on true
|
||||
join int4_tbl t2 on -s.x = t2.f1
|
||||
where t1.f1 = 123456;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------
|
||||
Nested Loop
|
||||
Output: t1.f1, (t1.f1), t2.f1
|
||||
-> Seq Scan on public.int4_tbl t1
|
||||
Output: t1.f1
|
||||
Filter: (t1.f1 = 123456)
|
||||
-> Merge Join
|
||||
Output: (t1.f1), t2.f1
|
||||
Merge Cond: (((- t1.f1)) = t2.f1)
|
||||
-> Sort
|
||||
Output: (t1.f1), ((- t1.f1))
|
||||
Sort Key: ((- t1.f1))
|
||||
-> Seq Scan on public.int4_tbl t3
|
||||
Output: t1.f1, (- t1.f1)
|
||||
Filter: (t3.f1 = 0)
|
||||
-> Sort
|
||||
Output: t2.f1
|
||||
Sort Key: t2.f1
|
||||
-> Seq Scan on public.int4_tbl t2
|
||||
Output: t2.f1
|
||||
(19 rows)
|
||||
|
||||
select * from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1 from int4_tbl t3 where t3.f1 = 0) s(x) on true
|
||||
join int4_tbl t2 on -s.x = t2.f1
|
||||
where t1.f1 = 123456;
|
||||
f1 | x | f1
|
||||
--------+--------+---------
|
||||
123456 | 123456 | -123456
|
||||
(1 row)
|
||||
|
||||
reset enable_nestloop;
|
||||
-- same, with the sort key computed atop a join
|
||||
explain (verbose, costs off)
|
||||
select count(*) from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1, t3.g from generate_series(1, 1000) t3(g), int4_tbl t4) s(x, y) on true
|
||||
join tenk1 t2 on s.x + s.y = t2.unique1
|
||||
where t1.f1 = 0;
|
||||
QUERY PLAN
|
||||
------------------------------------------------------------------------------
|
||||
Aggregate
|
||||
Output: count(*)
|
||||
-> Nested Loop
|
||||
-> Seq Scan on public.int4_tbl t1
|
||||
Output: t1.f1
|
||||
Filter: (t1.f1 = 0)
|
||||
-> Merge Join
|
||||
Merge Cond: (((t1.f1 + t3.g)) = t2.unique1)
|
||||
-> Sort
|
||||
Output: t3.g, (t1.f1), ((t1.f1 + t3.g))
|
||||
Sort Key: ((t1.f1 + t3.g))
|
||||
-> Nested Loop
|
||||
Output: t3.g, t1.f1, (t1.f1 + t3.g)
|
||||
-> Function Scan on pg_catalog.generate_series t3
|
||||
Output: t3.g
|
||||
Function Call: generate_series(1, 1000)
|
||||
-> Materialize
|
||||
-> Seq Scan on public.int4_tbl t4
|
||||
-> Index Only Scan using tenk1_unique1 on public.tenk1 t2
|
||||
Output: t2.unique1
|
||||
(20 rows)
|
||||
|
||||
select count(*) from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1, t3.g from generate_series(1, 1000) t3(g), int4_tbl t4) s(x, y) on true
|
||||
join tenk1 t2 on s.x + s.y = t2.unique1
|
||||
where t1.f1 = 0;
|
||||
count
|
||||
-------
|
||||
5000
|
||||
(1 row)
|
||||
|
||||
reset enable_hashjoin;
|
||||
-- test some error cases where LATERAL should have been used but wasn't
|
||||
select f1,g from int4_tbl a, (select f1 as g) ss;
|
||||
ERROR: column "f1" does not exist
|
||||
|
||||
@@ -3116,6 +3116,40 @@ from int4_tbl i4,
|
||||
where i4.f1 = 0
|
||||
order by 1, 2;
|
||||
|
||||
-- check that lateral references in a sort key added below a mergejoin are
|
||||
-- replaced with nestloop params
|
||||
set enable_hashjoin to off;
|
||||
set enable_nestloop to off;
|
||||
|
||||
explain (verbose, costs off)
|
||||
select * from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1 from int4_tbl t3 where t3.f1 = 0) s(x) on true
|
||||
join int4_tbl t2 on -s.x = t2.f1
|
||||
where t1.f1 = 123456;
|
||||
select * from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1 from int4_tbl t3 where t3.f1 = 0) s(x) on true
|
||||
join int4_tbl t2 on -s.x = t2.f1
|
||||
where t1.f1 = 123456;
|
||||
|
||||
reset enable_nestloop;
|
||||
|
||||
-- same, with the sort key computed atop a join
|
||||
explain (verbose, costs off)
|
||||
select count(*) from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1, t3.g from generate_series(1, 1000) t3(g), int4_tbl t4) s(x, y) on true
|
||||
join tenk1 t2 on s.x + s.y = t2.unique1
|
||||
where t1.f1 = 0;
|
||||
select count(*) from int4_tbl t1
|
||||
left join lateral
|
||||
(select t1.f1, t3.g from generate_series(1, 1000) t3(g), int4_tbl t4) s(x, y) on true
|
||||
join tenk1 t2 on s.x + s.y = t2.unique1
|
||||
where t1.f1 = 0;
|
||||
|
||||
reset enable_hashjoin;
|
||||
|
||||
-- test some error cases where LATERAL should have been used but wasn't
|
||||
select f1,g from int4_tbl a, (select f1 as g) ss;
|
||||
select f1,g from int4_tbl a, (select a.f1 as g) ss;
|
||||
|
||||
Reference in New Issue
Block a user