]> granicus.if.org Git - postgresql/commitdiff
Fix dumping of views that are just VALUES(...) but have column aliases.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 25 Feb 2015 17:01:12 +0000 (12:01 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 25 Feb 2015 17:01:12 +0000 (12:01 -0500)
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 092d7ded29f36b0539046b23b81b9f0bf2d637f1
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.

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

index da8ccc4ad27fcd350f0f0ce173668e1a0702f702..4df1105e02b7f9245f52caf42112370513a70049 100644 (file)
@@ -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)
                {
index 20c42a53871ebbf3c2e5ec584bb7127a39573021..f7ad30b450e6c424e9a147717a407a7bd8b53277 100644 (file)
@@ -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;
index 0c86a1d6e1bf4a75ea0652265cab81f6075e4f57..af46b612edde050fbadd63936668ab8630e45c81 100644 (file)
@@ -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;