]> granicus.if.org Git - postgresql/commitdiff
Fix ruleutils.c's dumping of whole-row Vars in ROW() and VALUES() contexts.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 15 Nov 2015 19:41:09 +0000 (14:41 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 15 Nov 2015 19:41:09 +0000 (14:41 -0500)
Normally ruleutils prints a whole-row Var as "foo.*".  We already knew that
that doesn't work at top level of a SELECT list, because the parser would
treat the "*" as a directive to expand the reference into separate columns,
not a whole-row Var.  However, Joshua Yanovski points out in bug #13776
that the same thing happens at top level of a ROW() construct; and some
nosing around in the parser shows that the same is true in VALUES().
Hence, apply the same workaround already devised for the SELECT-list case,
namely to add a forced cast to the appropriate rowtype in these cases.
(The alternative of just printing "foo" was rejected because it is
difficult to avoid ambiguity against plain columns named "foo".)

Back-patch to all supported branches.

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

index 09c95600f7b34c682897d664f13cc2a99be14305..bf98620460de4c700658b7be2c7ddd6b064cb1d4 100644 (file)
@@ -217,6 +217,8 @@ static void appendContextKeyword(deparse_context *context, const char *str,
                                         int indentBefore, int indentAfter, int indentPlus);
 static void get_rule_expr(Node *node, deparse_context *context,
                          bool showimplicit);
+static void get_rule_expr_toplevel(Node *node, deparse_context *context,
+                                          bool showimplicit);
 static void get_oper_expr(OpExpr *expr, deparse_context *context);
 static void get_func_expr(FuncExpr *expr, deparse_context *context,
                          bool showimplicit);
@@ -2706,10 +2708,10 @@ get_values_def(List *values_lists, deparse_context *context)
 
                        /*
                         * Strip any top-level nodes representing indirection assignments,
-                        * then print the result.
+                        * then print the result.  Whole-row Vars need special treatment.
                         */
-                       get_rule_expr(processIndirection(col, context, false),
-                                                 context, false);
+                       get_rule_expr_toplevel(processIndirection(col, context, false),
+                                                                  context, false);
                }
                appendStringInfoChar(buf, ')');
        }
@@ -3073,7 +3075,8 @@ get_target_list(List *targetList, deparse_context *context,
                 * the top level of a SELECT list it's not right (the parser will
                 * expand that notation into multiple columns, yielding behavior
                 * different from a whole-row Var).  We need to call get_variable
-                * directly so that we can tell it to do the right thing.
+                * directly so that we can tell it to do the right thing, and so that
+                * we can get the attribute name which is the default AS label.
                 */
                if (tle->expr && IsA(tle->expr, Var))
                {
@@ -5456,7 +5459,8 @@ get_rule_expr(Node *node, deparse_context *context,
                                                !tupdesc->attrs[i]->attisdropped)
                                        {
                                                appendStringInfoString(buf, sep);
-                                               get_rule_expr(e, context, true);
+                                               /* Whole-row Vars need special treatment here */
+                                               get_rule_expr_toplevel(e, context, true);
                                                sep = ", ";
                                        }
                                        i++;
@@ -5836,6 +5840,27 @@ get_rule_expr(Node *node, deparse_context *context,
        }
 }
 
+/*
+ * get_rule_expr_toplevel              - Parse back a toplevel expression
+ *
+ * Same as get_rule_expr(), except that if the expr is just a Var, we pass
+ * istoplevel = true not false to get_variable().  This causes whole-row Vars
+ * to get printed with decoration that will prevent expansion of "*".
+ * We need to use this in contexts such as ROW() and VALUES(), where the
+ * parser would expand "foo.*" appearing at top level.  (In principle we'd
+ * use this in get_target_list() too, but that has additional worries about
+ * whether to print AS, so it needs to invoke get_variable() directly anyway.)
+ */
+static void
+get_rule_expr_toplevel(Node *node, deparse_context *context,
+                                          bool showimplicit)
+{
+       if (node && IsA(node, Var))
+               (void) get_variable((Var *) node, 0, true, context);
+       else
+               get_rule_expr(node, context, showimplicit);
+}
+
 
 /*
  * get_oper_expr                       - Parse back an OpExpr node
index f9490a3a5506268a8e462f165a2c0530d725c4c1..56a1566aeed617e38233015eec31e18e0a0086db 100644 (file)
@@ -239,46 +239,67 @@ And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%')
      1
 (1 row)
 
+-- check display of whole-row variables in some corner cases
+create type nestedcomposite as (x int8_tbl);
+create view tt15v as select row(i)::nestedcomposite from int8_tbl i;
+select * from tt15v;
+                   row                    
+------------------------------------------
+ ("(123,456)")
+ ("(123,4567890123456789)")
+ ("(4567890123456789,123)")
+ ("(4567890123456789,4567890123456789)")
+ ("(4567890123456789,-4567890123456789)")
+(5 rows)
+
+select pg_get_viewdef('tt15v', true);
+                    pg_get_viewdef                    
+------------------------------------------------------
+  SELECT ROW(i.*::int8_tbl)::nestedcomposite AS "row"+
+    FROM int8_tbl i;
+(1 row)
+
+select row(i.*::int8_tbl)::nestedcomposite from int8_tbl i;
+                   row                    
+------------------------------------------
+ ("(123,456)")
+ ("(123,4567890123456789)")
+ ("(4567890123456789,123)")
+ ("(4567890123456789,4567890123456789)")
+ ("(4567890123456789,-4567890123456789)")
+(5 rows)
+
+create view tt17v as select * from int8_tbl i where i in (values(i));
+select * from tt17v;
+        q1        |        q2         
+------------------+-------------------
+              123 |               456
+              123 |  4567890123456789
+ 4567890123456789 |               123
+ 4567890123456789 |  4567890123456789
+ 4567890123456789 | -4567890123456789
+(5 rows)
+
+select pg_get_viewdef('tt17v', true);
+               pg_get_viewdef                
+---------------------------------------------
+  SELECT i.q1, i.q2                         +
+    FROM int8_tbl i                         +
+   WHERE (i.* IN ( VALUES (i.*::int8_tbl)));
+(1 row)
+
+select * from int8_tbl i where i.* in (values(i.*::int8_tbl));
+        q1        |        q2         
+------------------+-------------------
+              123 |               456
+              123 |  4567890123456789
+ 4567890123456789 |               123
+ 4567890123456789 |  4567890123456789
+ 4567890123456789 | -4567890123456789
+(5 rows)
+
+-- clean up all the random objects we made above
+set client_min_messages = warning;
 DROP SCHEMA temp_view_test CASCADE;
-NOTICE:  drop cascades to 22 other objects
-DETAIL:  drop cascades to table temp_view_test.base_table
-drop cascades to view v7_temp
-drop cascades to view v10_temp
-drop cascades to view v11_temp
-drop cascades to view v12_temp
-drop cascades to view v2_temp
-drop cascades to view v4_temp
-drop cascades to view v6_temp
-drop cascades to view v8_temp
-drop cascades to view v9_temp
-drop cascades to table temp_view_test.base_table2
-drop cascades to view v5_temp
-drop cascades to view temp_view_test.v1
-drop cascades to view temp_view_test.v2
-drop cascades to view temp_view_test.v3
-drop cascades to view temp_view_test.v4
-drop cascades to view temp_view_test.v5
-drop cascades to view temp_view_test.v6
-drop cascades to view temp_view_test.v7
-drop cascades to view temp_view_test.v8
-drop cascades to sequence temp_view_test.seq1
-drop cascades to view temp_view_test.v9
 DROP SCHEMA testviewschm2 CASCADE;
-NOTICE:  drop cascades to 16 other objects
-DETAIL:  drop cascades to table t1
-drop cascades to view temporal1
-drop cascades to view temporal2
-drop cascades to view temporal3
-drop cascades to view temporal4
-drop cascades to table t2
-drop cascades to view nontemp1
-drop cascades to view nontemp2
-drop cascades to view nontemp3
-drop cascades to view nontemp4
-drop cascades to table tbl1
-drop cascades to table tbl2
-drop cascades to table tbl3
-drop cascades to table tbl4
-drop cascades to view mytempview
-drop cascades to view pubview
 SET search_path to public;
index 86cfc5162c6959146f788db6b1bd1af636a4c418..6ea6b98708a5a2a9f22fe88aaf83c2db77644447 100644 (file)
@@ -191,6 +191,21 @@ AND NOT EXISTS (SELECT g FROM tbl4 LEFT JOIN tmptbl ON tbl4.h = tmptbl.j);
 SELECT count(*) FROM pg_class where relname LIKE 'mytempview'
 And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%');
 
+-- check display of whole-row variables in some corner cases
+
+create type nestedcomposite as (x int8_tbl);
+create view tt15v as select row(i)::nestedcomposite from int8_tbl i;
+select * from tt15v;
+select pg_get_viewdef('tt15v', true);
+select row(i.*::int8_tbl)::nestedcomposite from int8_tbl i;
+
+create view tt17v as select * from int8_tbl i where i in (values(i));
+select * from tt17v;
+select pg_get_viewdef('tt17v', true);
+select * from int8_tbl i where i.* in (values(i.*::int8_tbl));
+
+-- clean up all the random objects we made above
+set client_min_messages = warning;
 DROP SCHEMA temp_view_test CASCADE;
 DROP SCHEMA testviewschm2 CASCADE;