diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 19b5cf7b612a2f8d3d79abf5cca90dc19baa5722..fb1d070500cc790807e72bff338941452c7c9850 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -3196,10 +3196,19 @@ eval_const_expressions_mutator(Node *node, * But it can arise while simplifying functions.) Also, we * can optimize field selection from a RowExpr construct. * - * We must however check that the declared type of the field - * is still the same as when the FieldSelect was created --- - * this can change if someone did ALTER COLUMN TYPE on the - * rowtype. + * However, replacing a whole-row Var in this way has a + * pitfall: if we've already built the reltargetlist for the + * source relation, then the whole-row Var is scheduled to be + * produced by the relation scan, but the simple Var probably + * isn't, which will lead to a failure in setrefs.c. This is + * not a problem when handling simple single-level queries, in + * which expression simplification always happens first. It + * is a risk for lateral references from subqueries, though. + * To avoid such failures, don't optimize uplevel references. + * + * We must also check that the declared type of the field is + * still the same as when the FieldSelect was created --- this + * can change if someone did ALTER COLUMN TYPE on the rowtype. */ FieldSelect *fselect = (FieldSelect *) node; FieldSelect *newfselect; @@ -3208,7 +3217,8 @@ eval_const_expressions_mutator(Node *node, arg = eval_const_expressions_mutator((Node *) fselect->arg, context); if (arg && IsA(arg, Var) && - ((Var *) arg)->varattno == InvalidAttrNumber) + ((Var *) arg)->varattno == InvalidAttrNumber && + ((Var *) arg)->varlevelsup == 0) { if (rowtype_field_matches(((Var *) arg)->vartype, fselect->fieldnum, diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 774e75e2ac4f613125485a7af8470d88a3746e24..7991e993f1477d48b2bcebeb27a0a8821752b816 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -2006,3 +2006,55 @@ FROM 3 | FROM 10000000876 | from 10000000876 (3 rows) +-- check whole-row-Var handling in nested lateral functions (bug #11703) +create function extractq2(t int8_tbl) returns int8 as $$ + select t.q2 +$$ language sql immutable; +explain (verbose, costs off) +select x from int8_tbl, extractq2(int8_tbl) f(x); + QUERY PLAN +------------------------------------------ + Nested Loop + Output: f.x + -> Seq Scan on public.int8_tbl + Output: int8_tbl.q1, int8_tbl.q2 + -> Function Scan on f + Output: f.x + Function Call: int8_tbl.q2 +(7 rows) + +select x from int8_tbl, extractq2(int8_tbl) f(x); + x +------------------- + 456 + 4567890123456789 + 123 + 4567890123456789 + -4567890123456789 +(5 rows) + +create function extractq2_2(t int8_tbl) returns table(ret1 int8) as $$ + select extractq2(t) +$$ language sql immutable; +explain (verbose, costs off) +select x from int8_tbl, extractq2_2(int8_tbl) f(x); + QUERY PLAN +----------------------------------- + Nested Loop + Output: ((int8_tbl.*).q2) + -> Seq Scan on public.int8_tbl + Output: int8_tbl.* + -> Result + Output: (int8_tbl.*).q2 +(6 rows) + +select x from int8_tbl, extractq2_2(int8_tbl) f(x); + x +------------------- + 456 + 4567890123456789 + 123 + 4567890123456789 + -4567890123456789 +(5 rows) + diff --git a/src/test/regress/sql/rangefuncs.sql b/src/test/regress/sql/rangefuncs.sql index 07d2e1a490e6636882ff6db5a8e09861b13a2400..470571b0fb6362bad966ecd8e46afcc956f5cf8b 100644 --- a/src/test/regress/sql/rangefuncs.sql +++ b/src/test/regress/sql/rangefuncs.sql @@ -608,3 +608,23 @@ SELECT *, END) FROM (VALUES (1,''), (2,'0000000049404'), (3,'FROM 10000000876')) v(id, str); + +-- check whole-row-Var handling in nested lateral functions (bug #11703) + +create function extractq2(t int8_tbl) returns int8 as $$ + select t.q2 +$$ language sql immutable; + +explain (verbose, costs off) +select x from int8_tbl, extractq2(int8_tbl) f(x); + +select x from int8_tbl, extractq2(int8_tbl) f(x); + +create function extractq2_2(t int8_tbl) returns table(ret1 int8) as $$ + select extractq2(t) +$$ language sql immutable; + +explain (verbose, costs off) +select x from int8_tbl, extractq2_2(int8_tbl) f(x); + +select x from int8_tbl, extractq2_2(int8_tbl) f(x);