diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index da8ccc4ad27fcd350f0f0ce173668e1a0702f702..4df1105e02b7f9245f52caf42112370513a70049 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2933,11 +2933,72 @@ get_select_query_def(Query *query, deparse_context *context, context->windowTList = save_windowtlist; } +/* + * Detect whether query looks like SELECT ... FROM VALUES(); + * if so, return the VALUES RTE. Otherwise return NULL. + */ +static RangeTblEntry * +get_simple_values_rte(Query *query) +{ + RangeTblEntry *result = NULL; + ListCell *lc; + + /* + * 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. + */ + foreach(lc, query->rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + + if (rte->rtekind == RTE_VALUES && rte->inFromCl) + { + if (result) + return NULL; /* multiple VALUES (probably not possible) */ + result = rte; + } + else if (rte->rtekind == RTE_RELATION && !rte->inFromCl) + continue; /* ignore rule entries */ + 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; +} + static void get_basic_select_query(Query *query, deparse_context *context, TupleDesc resultDesc) { StringInfo buf = context->buf; + RangeTblEntry *values_rte; char *sep; ListCell *l; @@ -2950,23 +3011,13 @@ get_basic_select_query(Query *query, deparse_context *context, /* * If the query looks like SELECT * FROM (VALUES ...), then print just the * VALUES part. This reverses what transformValuesClause() did at parse - * time. If the jointree contains just a single VALUES RTE, we assume - * this case applies (without looking at the targetlist...) + * time. */ - if (list_length(query->jointree->fromlist) == 1) + values_rte = get_simple_values_rte(query); + if (values_rte) { - RangeTblRef *rtr = (RangeTblRef *) linitial(query->jointree->fromlist); - - if (IsA(rtr, RangeTblRef)) - { - RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); - - if (rte->rtekind == RTE_VALUES) - { - get_values_def(rte->values_lists, context); - return; - } - } + get_values_def(values_rte->values_lists, context); + return; } /* @@ -6681,7 +6732,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)); @@ -6721,6 +6774,13 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) quote_identifier(rte->eref->aliasname)); gavealias = true; } + else if (rte->rtekind == RTE_VALUES) + { + /* Alias is syntactically required for VALUES */ + appendStringInfo(buf, " %s", + quote_identifier(rte->eref->aliasname)); + gavealias = true; + } if (rte->rtekind == RTE_FUNCTION) { diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 20c42a53871ebbf3c2e5ec584bb7127a39573021..f7ad30b450e6c424e9a147717a407a7bd8b53277 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1623,3 +1623,53 @@ select pg_get_viewdef('shoe'::regclass,0) as prettier; WHERE sh.slunit = un.un_name; (1 row) +-- +-- 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 0c86a1d6e1bf4a75ea0652265cab81f6075e4f57..af46b612edde050fbadd63936668ab8630e45c81 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -948,3 +948,19 @@ select * from only t1_2; select pg_get_viewdef('shoe'::regclass) as unpretty; select pg_get_viewdef('shoe'::regclass,true) as pretty; select pg_get_viewdef('shoe'::regclass,0) as prettier; + +-- +-- 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;