Skip to content

Commit 7a501bc

Browse files
committed
Fix dumping of views that are just VALUES(...) but have column aliases.
The "simple" path for printing VALUES clauses doesn't work if we need to attach nondefault column aliases, because there's noplace to do that in the minimal VALUES() syntax. So modify get_simple_values_rte() to detect nondefault aliases and treat that as a non-simple case. This further exposes that the "non-simple" path never actually worked; it didn't produce valid syntax. Fix that too. Per bug #12789 from Curtis McEnroe, and analysis by Andrew Gierth. Back-patch to all supported branches. Before 9.3, this also requires back-patching the part of commit 092d7de that created get_simple_values_rte() to begin with; inserting the extra test into the old factorization of that logic would've been too messy.
1 parent 87b7fcc commit 7a501bc

File tree

3 files changed

+144
-15
lines changed

3 files changed

+144
-15
lines changed

src/backend/utils/adt/ruleutils.c

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2687,11 +2687,72 @@ get_select_query_def(Query *query, deparse_context *context,
26872687
context->windowTList = save_windowtlist;
26882688
}
26892689

2690+
/*
2691+
* Detect whether query looks like SELECT ... FROM VALUES();
2692+
* if so, return the VALUES RTE. Otherwise return NULL.
2693+
*/
2694+
static RangeTblEntry *
2695+
get_simple_values_rte(Query *query)
2696+
{
2697+
RangeTblEntry *result = NULL;
2698+
ListCell *lc;
2699+
2700+
/*
2701+
* We want to return TRUE even if the Query also contains OLD or NEW rule
2702+
* RTEs. So the idea is to scan the rtable and see if there is only one
2703+
* inFromCl RTE that is a VALUES RTE.
2704+
*/
2705+
foreach(lc, query->rtable)
2706+
{
2707+
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
2708+
2709+
if (rte->rtekind == RTE_VALUES && rte->inFromCl)
2710+
{
2711+
if (result)
2712+
return NULL; /* multiple VALUES (probably not possible) */
2713+
result = rte;
2714+
}
2715+
else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
2716+
continue; /* ignore rule entries */
2717+
else
2718+
return NULL; /* something else -> not simple VALUES */
2719+
}
2720+
2721+
/*
2722+
* We don't need to check the targetlist in any great detail, because
2723+
* parser/analyze.c will never generate a "bare" VALUES RTE --- they only
2724+
* appear inside auto-generated sub-queries with very restricted
2725+
* structure. However, DefineView might have modified the tlist by
2726+
* injecting new column aliases; so compare tlist resnames against the
2727+
* RTE's names to detect that.
2728+
*/
2729+
if (result)
2730+
{
2731+
ListCell *lcn;
2732+
2733+
if (list_length(query->targetList) != list_length(result->eref->colnames))
2734+
return NULL; /* this probably cannot happen */
2735+
forboth(lc, query->targetList, lcn, result->eref->colnames)
2736+
{
2737+
TargetEntry *tle = (TargetEntry *) lfirst(lc);
2738+
char *cname = strVal(lfirst(lcn));
2739+
2740+
if (tle->resjunk)
2741+
return NULL; /* this probably cannot happen */
2742+
if (tle->resname == NULL || strcmp(tle->resname, cname) != 0)
2743+
return NULL; /* column name has been changed */
2744+
}
2745+
}
2746+
2747+
return result;
2748+
}
2749+
26902750
static void
26912751
get_basic_select_query(Query *query, deparse_context *context,
26922752
TupleDesc resultDesc)
26932753
{
26942754
StringInfo buf = context->buf;
2755+
RangeTblEntry *values_rte;
26952756
char *sep;
26962757
ListCell *l;
26972758

@@ -2704,23 +2765,13 @@ get_basic_select_query(Query *query, deparse_context *context,
27042765
/*
27052766
* If the query looks like SELECT * FROM (VALUES ...), then print just the
27062767
* VALUES part. This reverses what transformValuesClause() did at parse
2707-
* time. If the jointree contains just a single VALUES RTE, we assume
2708-
* this case applies (without looking at the targetlist...)
2768+
* time.
27092769
*/
2710-
if (list_length(query->jointree->fromlist) == 1)
2770+
values_rte = get_simple_values_rte(query);
2771+
if (values_rte)
27112772
{
2712-
RangeTblRef *rtr = (RangeTblRef *) linitial(query->jointree->fromlist);
2713-
2714-
if (IsA(rtr, RangeTblRef))
2715-
{
2716-
RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
2717-
2718-
if (rte->rtekind == RTE_VALUES)
2719-
{
2720-
get_values_def(rte->values_lists, context);
2721-
return;
2722-
}
2723-
}
2773+
get_values_def(values_rte->values_lists, context);
2774+
return;
27242775
}
27252776

27262777
/*
@@ -6099,7 +6150,9 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
60996150
break;
61006151
case RTE_VALUES:
61016152
/* Values list RTE */
6153+
appendStringInfoChar(buf, '(');
61026154
get_values_def(rte->values_lists, context);
6155+
appendStringInfoChar(buf, ')');
61036156
break;
61046157
case RTE_CTE:
61056158
appendStringInfoString(buf, quote_identifier(rte->ctename));
@@ -6139,6 +6192,13 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
61396192
quote_identifier(rte->eref->aliasname));
61406193
gavealias = true;
61416194
}
6195+
else if (rte->rtekind == RTE_VALUES)
6196+
{
6197+
/* Alias is syntactically required for VALUES */
6198+
appendStringInfo(buf, " %s",
6199+
quote_identifier(rte->eref->aliasname));
6200+
gavealias = true;
6201+
}
61426202

61436203
if (rte->rtekind == RTE_FUNCTION)
61446204
{

src/test/regress/expected/rules.out

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,3 +1583,54 @@ select * from only t1_2;
15831583
19
15841584
(10 rows)
15851585

1586+
reset constraint_exclusion;
1587+
--
1588+
-- check display of VALUES in view definitions
1589+
--
1590+
create view rule_v1 as values(1,2);
1591+
\d+ rule_v1
1592+
View "public.rule_v1"
1593+
Column | Type | Modifiers | Storage | Description
1594+
---------+---------+-----------+---------+-------------
1595+
column1 | integer | | plain |
1596+
column2 | integer | | plain |
1597+
View definition:
1598+
VALUES (1,2);
1599+
1600+
drop view rule_v1;
1601+
create view rule_v1(x) as values(1,2);
1602+
\d+ rule_v1
1603+
View "public.rule_v1"
1604+
Column | Type | Modifiers | Storage | Description
1605+
---------+---------+-----------+---------+-------------
1606+
x | integer | | plain |
1607+
column2 | integer | | plain |
1608+
View definition:
1609+
SELECT "*VALUES*".column1 AS x, "*VALUES*".column2
1610+
FROM (VALUES (1,2)) "*VALUES*";
1611+
1612+
drop view rule_v1;
1613+
create view rule_v1(x) as select * from (values(1,2)) v;
1614+
\d+ rule_v1
1615+
View "public.rule_v1"
1616+
Column | Type | Modifiers | Storage | Description
1617+
---------+---------+-----------+---------+-------------
1618+
x | integer | | plain |
1619+
column2 | integer | | plain |
1620+
View definition:
1621+
SELECT v.column1 AS x, v.column2
1622+
FROM ( VALUES (1,2)) v;
1623+
1624+
drop view rule_v1;
1625+
create view rule_v1(x) as select * from (values(1,2)) v(q,w);
1626+
\d+ rule_v1
1627+
View "public.rule_v1"
1628+
Column | Type | Modifiers | Storage | Description
1629+
--------+---------+-----------+---------+-------------
1630+
x | integer | | plain |
1631+
w | integer | | plain |
1632+
View definition:
1633+
SELECT v.q AS x, v.w
1634+
FROM ( VALUES (1,2)) v(q, w);
1635+
1636+
drop view rule_v1;

src/test/regress/sql/rules.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,3 +942,21 @@ update t1 set a = 4 where a = 5;
942942
select * from only t1;
943943
select * from only t1_1;
944944
select * from only t1_2;
945+
946+
reset constraint_exclusion;
947+
948+
--
949+
-- check display of VALUES in view definitions
950+
--
951+
create view rule_v1 as values(1,2);
952+
\d+ rule_v1
953+
drop view rule_v1;
954+
create view rule_v1(x) as values(1,2);
955+
\d+ rule_v1
956+
drop view rule_v1;
957+
create view rule_v1(x) as select * from (values(1,2)) v;
958+
\d+ rule_v1
959+
drop view rule_v1;
960+
create view rule_v1(x) as select * from (values(1,2)) v(q,w);
961+
\d+ rule_v1
962+
drop view rule_v1;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy