diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c1d860ceff..2fa30be401 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -4498,10 +4498,7 @@ get_simple_values_rte(Query *query) /* * We want to return TRUE even if the Query also contains OLD or NEW rule * RTEs. So the idea is to scan the rtable and see if there is only one - * inFromCl RTE that is a VALUES RTE. We don't look at the targetlist at - * all. This is okay because parser/analyze.c will never generate a - * "bare" VALUES RTE --- they only appear inside auto-generated - * sub-queries with very restricted structure. + * inFromCl RTE that is a VALUES RTE. */ foreach(lc, query->rtable) { @@ -4518,6 +4515,33 @@ get_simple_values_rte(Query *query) else return NULL; /* something else -> not simple VALUES */ } + + /* + * We don't need to check the targetlist in any great detail, because + * parser/analyze.c will never generate a "bare" VALUES RTE --- they only + * appear inside auto-generated sub-queries with very restricted + * structure. However, DefineView might have modified the tlist by + * injecting new column aliases; so compare tlist resnames against the + * RTE's names to detect that. + */ + if (result) + { + ListCell *lcn; + + if (list_length(query->targetList) != list_length(result->eref->colnames)) + return NULL; /* this probably cannot happen */ + forboth(lc, query->targetList, lcn, result->eref->colnames) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + char *cname = strVal(lfirst(lcn)); + + if (tle->resjunk) + return NULL; /* this probably cannot happen */ + if (tle->resname == NULL || strcmp(tle->resname, cname) != 0) + return NULL; /* column name has been changed */ + } + } + return result; } @@ -8517,7 +8541,9 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) break; case RTE_VALUES: /* Values list RTE */ + appendStringInfoChar(buf, '('); get_values_def(rte->values_lists, context); + appendStringInfoChar(buf, ')'); break; case RTE_CTE: appendStringInfoString(buf, quote_identifier(rte->ctename)); @@ -8559,6 +8585,11 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) */ printalias = true; } + else if (rte->rtekind == RTE_VALUES) + { + /* Alias is syntactically required for VALUES */ + printalias = true; + } else if (rte->rtekind == RTE_CTE) { /* diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index d50b103f15..26c60e4153 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2507,6 +2507,7 @@ select * from only t1_2; 19 (10 rows) +reset constraint_exclusion; -- test various flavors of pg_get_viewdef() select pg_get_viewdef('shoe'::regclass) as unpretty; unpretty @@ -2678,3 +2679,56 @@ ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renam ERROR: renaming an ON SELECT rule is not allowed DROP VIEW rule_v1; DROP TABLE rule_t1; +-- +-- check display of VALUES in view definitions +-- +create view rule_v1 as values(1,2); +\d+ rule_v1 + View "public.rule_v1" + Column | Type | Modifiers | Storage | Description +---------+---------+-----------+---------+------------- + column1 | integer | | plain | + column2 | integer | | plain | +View definition: + VALUES (1,2); + +drop view rule_v1; +create view rule_v1(x) as values(1,2); +\d+ rule_v1 + View "public.rule_v1" + Column | Type | Modifiers | Storage | Description +---------+---------+-----------+---------+------------- + x | integer | | plain | + column2 | integer | | plain | +View definition: + SELECT "*VALUES*".column1 AS x, + "*VALUES*".column2 + FROM (VALUES (1,2)) "*VALUES*"; + +drop view rule_v1; +create view rule_v1(x) as select * from (values(1,2)) v; +\d+ rule_v1 + View "public.rule_v1" + Column | Type | Modifiers | Storage | Description +---------+---------+-----------+---------+------------- + x | integer | | plain | + column2 | integer | | plain | +View definition: + SELECT v.column1 AS x, + v.column2 + FROM ( VALUES (1,2)) v; + +drop view rule_v1; +create view rule_v1(x) as select * from (values(1,2)) v(q,w); +\d+ rule_v1 + View "public.rule_v1" + Column | Type | Modifiers | Storage | Description +--------+---------+-----------+---------+------------- + x | integer | | plain | + w | integer | | plain | +View definition: + SELECT v.q AS x, + v.w + FROM ( VALUES (1,2)) v(q, w); + +drop view rule_v1; diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql index 1e15f84dc8..c385e41457 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -953,6 +953,8 @@ select * from only t1; select * from only t1_1; select * from only t1_2; +reset constraint_exclusion; + -- test various flavors of pg_get_viewdef() select pg_get_viewdef('shoe'::regclass) as unpretty; @@ -1007,3 +1009,19 @@ ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renam DROP VIEW rule_v1; DROP TABLE rule_t1; + +-- +-- check display of VALUES in view definitions +-- +create view rule_v1 as values(1,2); +\d+ rule_v1 +drop view rule_v1; +create view rule_v1(x) as values(1,2); +\d+ rule_v1 +drop view rule_v1; +create view rule_v1(x) as select * from (values(1,2)) v; +\d+ rule_v1 +drop view rule_v1; +create view rule_v1(x) as select * from (values(1,2)) v(q,w); +\d+ rule_v1 +drop view rule_v1;