aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2013-08-23 17:31:06 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2013-08-23 17:31:06 -0400
commitfb5941d323816ba0141fbd5a6df1d14155bfd006 (patch)
tree6c30c23ff52a8bc1a6871a8fe89a3d31920ceca4
parente9e387a5b1dde0b738d712cffe16c962e87b0094 (diff)
downloadpostgresql-fb5941d323816ba0141fbd5a6df1d14155bfd006.tar.gz
postgresql-fb5941d323816ba0141fbd5a6df1d14155bfd006.zip
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.
-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
--