Skip to content

Commit 70ebdd0

Browse files
committed
In locate_grouping_columns(), don't expect an exact match of Var typmods.
It's possible that inlining of SQL functions (or perhaps other changes?) has exposed typmod information not known at parse time. In such cases, Vars generated by query_planner might have valid typmod values while the original grouping columns only have typmod -1. This isn't a semantic problem since the behavior of grouping only depends on type not typmod, but it breaks locate_grouping_columns' use of tlist_member to locate the matching entry in query_planner's result tlist. We can fix this without an excessive amount of new code or complexity by relying on the fact that locate_grouping_columns only gets called when make_subplanTargetList has set need_tlist_eval == false, and that can only happen if all the grouping columns are simple Vars. Therefore we only need to search the sub_tlist for a matching Var, and we can reasonably define a "match" as being a match of the Var identity fields varno/varattno/varlevelsup. The code still Asserts that vartype matches, but ignores vartypmod. Per bug #8393 from Evan Martin. The added regression test case is basically the same as his example. This has been broken for a very long time, so back-patch to all supported branches.
1 parent 8396d23 commit 70ebdd0

File tree

5 files changed

+66
-3
lines changed

5 files changed

+66
-3
lines changed

src/backend/optimizer/plan/planner.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2201,7 +2201,8 @@ choose_hashed_distinct(PlannerInfo *root,
22012201
* 'groupColIdx' receives an array of column numbers for the GROUP BY
22022202
* expressions (if there are any) in the subplan's target list.
22032203
* 'need_tlist_eval' is set true if we really need to evaluate the
2204-
* result tlist.
2204+
* returned tlist as-is. (Note: locate_grouping_columns assumes
2205+
* that if this is FALSE, all grouping columns are simple Vars.)
22052206
*
22062207
* The result is the targetlist to be passed to the subplan.
22072208
*---------------
@@ -2299,6 +2300,7 @@ make_subplanTargetList(PlannerInfo *root,
22992300
* This is only needed if we don't use the sub_tlist chosen by
23002301
* make_subplanTargetList. We have to forget the column indexes found
23012302
* by that routine and re-locate the grouping exprs in the real sub_tlist.
2303+
* We assume the grouping exprs are just Vars (see make_subplanTargetList).
23022304
*/
23032305
static void
23042306
locate_grouping_columns(PlannerInfo *root,
@@ -2322,11 +2324,24 @@ locate_grouping_columns(PlannerInfo *root,
23222324
foreach(gl, root->parse->groupClause)
23232325
{
23242326
SortGroupClause *grpcl = (SortGroupClause *) lfirst(gl);
2325-
Node *groupexpr = get_sortgroupclause_expr(grpcl, tlist);
2326-
TargetEntry *te = tlist_member(groupexpr, sub_tlist);
2327+
Var *groupexpr = (Var *) get_sortgroupclause_expr(grpcl, tlist);
2328+
TargetEntry *te;
23272329

2330+
/*
2331+
* The grouping column returned by create_plan might not have the same
2332+
* typmod as the original Var. (This can happen in cases where a
2333+
* set-returning function has been inlined, so that we now have more
2334+
* knowledge about what it returns than we did when the original Var
2335+
* was created.) So we can't use tlist_member() to search the tlist;
2336+
* instead use tlist_member_match_var. For safety, still check that
2337+
* the vartype matches.
2338+
*/
2339+
if (!(groupexpr && IsA(groupexpr, Var)))
2340+
elog(ERROR, "grouping column is not a Var as expected");
2341+
te = tlist_member_match_var(groupexpr, sub_tlist);
23282342
if (!te)
23292343
elog(ERROR, "failed to locate grouping columns");
2344+
Assert(((Var *) te->expr)->vartype == groupexpr->vartype);
23302345
groupColIdx[keyno++] = te->resno;
23312346
}
23322347
}

src/backend/optimizer/util/tlist.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,35 @@ tlist_member_ignore_relabel(Node *node, List *targetlist)
7373
return NULL;
7474
}
7575

76+
/*
77+
* tlist_member_match_var
78+
* Same as above, except that we match the provided Var on the basis
79+
* of varno/varattno/varlevelsup only, rather than using full equal().
80+
*
81+
* This is needed in some cases where we can't be sure of an exact typmod
82+
* match. It's probably a good idea to check the vartype anyway, but
83+
* we leave it to the caller to apply any suitable sanity checks.
84+
*/
85+
TargetEntry *
86+
tlist_member_match_var(Var *var, List *targetlist)
87+
{
88+
ListCell *temp;
89+
90+
foreach(temp, targetlist)
91+
{
92+
TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
93+
Var *tlvar = (Var *) tlentry->expr;
94+
95+
if (!tlvar || !IsA(tlvar, Var))
96+
continue;
97+
if (var->varno == tlvar->varno &&
98+
var->varattno == tlvar->varattno &&
99+
var->varlevelsup == tlvar->varlevelsup)
100+
return tlentry;
101+
}
102+
return NULL;
103+
}
104+
76105
/*
77106
* flatten_tlist
78107
* Create a target list that only contains unique variables.

src/include/optimizer/tlist.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
extern TargetEntry *tlist_member(Node *node, List *targetlist);
2121
extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist);
22+
extern TargetEntry *tlist_member_match_var(Var *var, List *targetlist);
2223

2324
extern List *flatten_tlist(List *tlist);
2425
extern List *add_to_flat_tlist(List *tlist, List *exprs);

src/test/regress/expected/rangefuncs.out

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,17 @@ SELECT * FROM foo(3);
567567
(9 rows)
568568

569569
DROP FUNCTION foo(int);
570+
-- case that causes change of typmod knowledge during inlining
571+
CREATE OR REPLACE FUNCTION foo()
572+
RETURNS TABLE(a varchar(5))
573+
AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE;
574+
SELECT * FROM foo() GROUP BY 1;
575+
a
576+
-------
577+
hello
578+
(1 row)
579+
580+
DROP FUNCTION foo();
570581
--
571582
-- some tests on SQL functions with RETURNING
572583
--

src/test/regress/sql/rangefuncs.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,13 @@ AS $$ SELECT a, b
280280
SELECT * FROM foo(3);
281281
DROP FUNCTION foo(int);
282282

283+
-- case that causes change of typmod knowledge during inlining
284+
CREATE OR REPLACE FUNCTION foo()
285+
RETURNS TABLE(a varchar(5))
286+
AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE;
287+
SELECT * FROM foo() GROUP BY 1;
288+
DROP FUNCTION foo();
289+
283290
--
284291
-- some tests on SQL functions with RETURNING
285292
--

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy