diff options
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 90 | ||||
-rw-r--r-- | src/test/regress/expected/rules.out | 51 | ||||
-rw-r--r-- | src/test/regress/sql/rules.sql | 18 |
3 files changed, 144 insertions, 15 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 059eeb3dcc0..09c95600f7b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2878,11 +2878,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; @@ -2895,23 +2956,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; } /* @@ -6474,7 +6525,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)); @@ -6514,6 +6567,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 53a4b2cfb93..0f4b6bd7e4e 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1590,3 +1590,54 @@ select * from only t1_2; 19 (10 rows) +reset constraint_exclusion; +-- +-- 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 5ec8bbb63fa..07ec41a183c 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -942,3 +942,21 @@ update t1 set a = 4 where a = 5; select * from only t1; select * from only t1_1; select * from only t1_2; + +reset constraint_exclusion; + +-- +-- 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; |