aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/optimizer/plan/planner.c21
-rw-r--r--src/backend/optimizer/util/tlist.c29
-rw-r--r--src/include/optimizer/tlist.h1
-rw-r--r--src/test/regress/expected/rangefuncs.out11
-rw-r--r--src/test/regress/sql/rangefuncs.sql7
5 files changed, 66 insertions, 3 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index f2a03a5622a..8443a82b2d8 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -2508,7 +2508,8 @@ choose_hashed_distinct(PlannerInfo *root,
* 'groupColIdx' receives an array of column numbers for the GROUP BY
* expressions (if there are any) in the subplan's target list.
* 'need_tlist_eval' is set true if we really need to evaluate the
- * result tlist.
+ * returned tlist as-is. (Note: locate_grouping_columns assumes
+ * that if this is FALSE, all grouping columns are simple Vars.)
*
* The result is the targetlist to be passed to the subplan.
*/
@@ -2605,6 +2606,7 @@ make_subplanTargetList(PlannerInfo *root,
* This is only needed if we don't use the sub_tlist chosen by
* make_subplanTargetList. We have to forget the column indexes found
* by that routine and re-locate the grouping exprs in the real sub_tlist.
+ * We assume the grouping exprs are just Vars (see make_subplanTargetList).
*/
static void
locate_grouping_columns(PlannerInfo *root,
@@ -2628,11 +2630,24 @@ locate_grouping_columns(PlannerInfo *root,
foreach(gl, root->parse->groupClause)
{
SortGroupClause *grpcl = (SortGroupClause *) lfirst(gl);
- Node *groupexpr = get_sortgroupclause_expr(grpcl, tlist);
- TargetEntry *te = tlist_member(groupexpr, sub_tlist);
+ Var *groupexpr = (Var *) get_sortgroupclause_expr(grpcl, tlist);
+ TargetEntry *te;
+ /*
+ * The grouping column returned by create_plan might not have the same
+ * typmod as the original Var. (This can happen in cases where a
+ * set-returning function has been inlined, so that we now have more
+ * knowledge about what it returns than we did when the original Var
+ * was created.) So we can't use tlist_member() to search the tlist;
+ * instead use tlist_member_match_var. For safety, still check that
+ * the vartype matches.
+ */
+ if (!(groupexpr && IsA(groupexpr, Var)))
+ elog(ERROR, "grouping column is not a Var as expected");
+ te = tlist_member_match_var(groupexpr, sub_tlist);
if (!te)
elog(ERROR, "failed to locate grouping columns");
+ Assert(((Var *) te->expr)->vartype == groupexpr->vartype);
groupColIdx[keyno++] = te->resno;
}
}
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 37ac8aaea22..1e44f683e17 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -74,6 +74,35 @@ tlist_member_ignore_relabel(Node *node, List *targetlist)
}
/*
+ * tlist_member_match_var
+ * Same as above, except that we match the provided Var on the basis
+ * of varno/varattno/varlevelsup only, rather than using full equal().
+ *
+ * This is needed in some cases where we can't be sure of an exact typmod
+ * match. It's probably a good idea to check the vartype anyway, but
+ * we leave it to the caller to apply any suitable sanity checks.
+ */
+TargetEntry *
+tlist_member_match_var(Var *var, List *targetlist)
+{
+ ListCell *temp;
+
+ foreach(temp, targetlist)
+ {
+ TargetEntry *tlentry = (TargetEntry *) lfirst(temp);
+ Var *tlvar = (Var *) tlentry->expr;
+
+ if (!tlvar || !IsA(tlvar, Var))
+ continue;
+ if (var->varno == tlvar->varno &&
+ var->varattno == tlvar->varattno &&
+ var->varlevelsup == tlvar->varlevelsup)
+ return tlentry;
+ }
+ return NULL;
+}
+
+/*
* flatten_tlist
* Create a target list that only contains unique variables.
*
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index 22223a1c1cb..b84df2f6109 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -19,6 +19,7 @@
extern TargetEntry *tlist_member(Node *node, List *targetlist);
extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist);
+extern TargetEntry *tlist_member_match_var(Var *var, List *targetlist);
extern List *flatten_tlist(List *tlist);
extern List *add_to_flat_tlist(List *tlist, List *exprs);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index c6a3e575afb..25ba6b45233 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -574,6 +574,17 @@ SELECT * FROM foo(3);
(9 rows)
DROP FUNCTION foo(int);
+-- case that causes change of typmod knowledge during inlining
+CREATE OR REPLACE FUNCTION foo()
+RETURNS TABLE(a varchar(5))
+AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE;
+SELECT * FROM foo() GROUP BY 1;
+ a
+-------
+ hello
+(1 row)
+
+DROP FUNCTION foo();
--
-- some tests on SQL functions with RETURNING
--
diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql
index 46aaf335d09..72f0ce9aff7 100644
--- a/src/test/regress/sql/rangefuncs.sql
+++ b/src/test/regress/sql/rangefuncs.sql
@@ -286,6 +286,13 @@ AS $$ SELECT a, b
SELECT * FROM foo(3);
DROP FUNCTION foo(int);
+-- case that causes change of typmod knowledge during inlining
+CREATE OR REPLACE FUNCTION foo()
+RETURNS TABLE(a varchar(5))
+AS $$ SELECT 'hello'::varchar(5) $$ LANGUAGE sql STABLE;
+SELECT * FROM foo() GROUP BY 1;
+DROP FUNCTION foo();
+
--
-- some tests on SQL functions with RETURNING
--