Further fix dumping of views that contain just VALUES(...).
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Nov 2019 01:00:19 +0000 (20:00 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Nov 2019 01:00:19 +0000 (20:00 -0500)
It turns out that commit e9f1c01b7 missed a case: we must print a
VALUES clause in long format if get_query_def is given a resultDesc
that would require the query's output column name(s) to be different
from what the bare VALUES clause would produce.

This applies in case an ALTER ... RENAME COLUMN has been done to
a view that formerly could be printed in simple format, as shown
in the added regression test case.  It also explains bug #16119
from Dmitry Telpt, because it turns out that (unlike CREATE VIEW)
CREATE MATERIALIZED VIEW fails to apply any column aliases it's
given to the stored ON SELECT rule.  So to get them to be printed,
we have to account for the resultDesc renaming.  It might be worth
changing the matview code so that it creates the ON SELECT rule
with the correct aliases; but we'd still need these messy checks in
get_simple_values_rte to handle the case of a subsequent column
rename, so any such change would be just neatnik-ism not a bug fix.

Like the previous patch, back-patch to all supported branches.

Discussion: https://postgr.es/m/16119-e64823f30a45a754@postgresql.org

src/backend/utils/adt/ruleutils.c
src/test/regress/expected/rules.out
src/test/regress/sql/rules.sql

index 4c83cae5e922b53d2f059f5878f64a0a7797a1d8..13685a0a0e4348313ea543ac846de6ddb26568b9 100644 (file)
@@ -5370,19 +5370,20 @@ get_select_query_def(Query *query, deparse_context *context,
 }
 
 /*
- * Detect whether query looks like SELECT ... FROM VALUES();
- * if so, return the VALUES RTE.  Otherwise return NULL.
+ * Detect whether query looks like SELECT ... FROM VALUES(),
+ * with no need to rename the output columns of the VALUES RTE.
+ * If so, return the VALUES RTE.  Otherwise return NULL.
  */
 static RangeTblEntry *
-get_simple_values_rte(Query *query)
+get_simple_values_rte(Query *query, TupleDesc resultDesc)
 {
    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.
+    * We want to detect a match 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)
    {
@@ -5405,23 +5406,36 @@ get_simple_values_rte(Query *query)
     * 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.
+    * injecting new column aliases, or we might have some other column
+    * aliases forced by a resultDesc.  We can only simplify if the RTE's
+    * column names match the names that get_target_list() would select.
     */
    if (result)
    {
        ListCell   *lcn;
+       int         colno;
 
        if (list_length(query->targetList) != list_length(result->eref->colnames))
            return NULL;        /* this probably cannot happen */
+       colno = 0;
        forboth(lc, query->targetList, lcn, result->eref->colnames)
        {
            TargetEntry *tle = (TargetEntry *) lfirst(lc);
            char       *cname = strVal(lfirst(lcn));
+           char       *colname;
 
            if (tle->resjunk)
                return NULL;    /* this probably cannot happen */
-           if (tle->resname == NULL || strcmp(tle->resname, cname) != 0)
+
+           /* compute name that get_target_list would use for column */
+           colno++;
+           if (resultDesc && colno <= resultDesc->natts)
+               colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
+           else
+               colname = tle->resname;
+
+           /* does it match the VALUES RTE? */
+           if (colname == NULL || strcmp(colname, cname) != 0)
                return NULL;    /* column name has been changed */
        }
    }
@@ -5449,7 +5463,7 @@ get_basic_select_query(Query *query, deparse_context *context,
     * VALUES part.  This reverses what transformValuesClause() did at parse
     * time.
     */
-   values_rte = get_simple_values_rte(query);
+   values_rte = get_simple_values_rte(query, resultDesc);
    if (values_rte)
    {
        get_values_def(values_rte->values_lists, context);
index 14e72143468e51b7f61401ea44e56f82fd18c882..abe3a43cd27c0160cbc88f656001ef865db9287a 100644 (file)
@@ -3025,6 +3025,18 @@ create view rule_v1 as values(1,2);
 View definition:
  VALUES (1,2);
 
+alter table rule_v1 rename column column2 to q2;
+\d+ rule_v1
+                           View "public.rule_v1"
+ Column  |  Type   | Collation | Nullable | Default | Storage | Description 
+---------+---------+-----------+----------+---------+---------+-------------
+ column1 | integer |           |          |         | plain   | 
+ q2      | integer |           |          |         | plain   | 
+View definition:
+ SELECT "*VALUES*".column1,
+    "*VALUES*".column2 AS q2
+   FROM (VALUES (1,2)) "*VALUES*";
+
 drop view rule_v1;
 create view rule_v1(x) as values(1,2);
 \d+ rule_v1
index a042e595469a6c5520c010defadcaad671adf0c2..b7d7f434b6a5b8d15f5226f57f305bc705ee819e 100644 (file)
@@ -1047,6 +1047,8 @@ DROP TABLE rule_t1;
 --
 create view rule_v1 as values(1,2);
 \d+ rule_v1
+alter table rule_v1 rename column column2 to q2;
+\d+ rule_v1
 drop view rule_v1;
 create view rule_v1(x) as values(1,2);
 \d+ rule_v1