Fix ruleutils.c's dumping of whole-row Vars in more contexts.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Jan 2022 22:49:26 +0000 (17:49 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 13 Jan 2022 22:49:26 +0000 (17:49 -0500)
Commit 7745bc352 intended to ensure that whole-row Vars would be
printed with "::type" decoration in all contexts where plain
"var.*" notation would result in star-expansion, notably in
ROW() and VALUES() constructs.  However, it missed the case of
INSERT with a single-row VALUES, as reported by Timur Khanjanov.

Nosing around ruleutils.c, I found a second oversight: the
code for RowCompareExpr generates ROW() notation without benefit
of an actual RowExpr, and naturally it wasn't in sync :-(.
(The code for FieldStore also does this, but we don't expect that
to generate strictly parsable SQL anyway, so I left it alone.)

Back-patch to all supported branches.

Discussion: https://postgr.es/m/efaba6f9-4190-56be-8ff2-7a1674f9194f@intrans.baku.az

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

index 5564725196983e0503c5cda2eb3e0a617c5d8246..36d7b53c142ae24d941b6a5eed2c5ed32d1cc04a 100644 (file)
@@ -432,6 +432,8 @@ 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_rule_list_toplevel(List *lst, deparse_context *context,
+                                  bool showimplicit);
 static void get_rule_expr_funccall(Node *node, deparse_context *context,
                                   bool showimplicit);
 static bool looks_like_function(Node *node);
@@ -6229,7 +6231,7 @@ get_insert_query_def(Query *query, deparse_context *context)
        /* Add the single-VALUES expression list */
        appendContextKeyword(context, "VALUES (",
                             -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
-       get_rule_expr((Node *) strippedexprs, context, false);
+       get_rule_list_toplevel(strippedexprs, context, false);
        appendStringInfoChar(buf, ')');
    }
    else
@@ -8539,23 +8541,15 @@ get_rule_expr(Node *node, deparse_context *context,
        case T_RowCompareExpr:
            {
                RowCompareExpr *rcexpr = (RowCompareExpr *) node;
-               ListCell   *arg;
-               char       *sep;
 
                /*
                 * SQL99 allows "ROW" to be omitted when there is more than
-                * one column, but for simplicity we always print it.
+                * one column, but for simplicity we always print it.  Within
+                * a ROW expression, whole-row Vars need special treatment, so
+                * use get_rule_list_toplevel.
                 */
                appendStringInfoString(buf, "(ROW(");
-               sep = "";
-               foreach(arg, rcexpr->largs)
-               {
-                   Node       *e = (Node *) lfirst(arg);
-
-                   appendStringInfoString(buf, sep);
-                   get_rule_expr(e, context, true);
-                   sep = ", ";
-               }
+               get_rule_list_toplevel(rcexpr->largs, context, true);
 
                /*
                 * We assume that the name of the first-column operator will
@@ -8568,15 +8562,7 @@ get_rule_expr(Node *node, deparse_context *context,
                                 generate_operator_name(linitial_oid(rcexpr->opnos),
                                                        exprType(linitial(rcexpr->largs)),
                                                        exprType(linitial(rcexpr->rargs))));
-               sep = "";
-               foreach(arg, rcexpr->rargs)
-               {
-                   Node       *e = (Node *) lfirst(arg);
-
-                   appendStringInfoString(buf, sep);
-                   get_rule_expr(e, context, true);
-                   sep = ", ";
-               }
+               get_rule_list_toplevel(rcexpr->rargs, context, true);
                appendStringInfoString(buf, "))");
            }
            break;
@@ -9121,6 +9107,32 @@ get_rule_expr_toplevel(Node *node, deparse_context *context,
        get_rule_expr(node, context, showimplicit);
 }
 
+/*
+ * get_rule_list_toplevel      - Parse back a list of toplevel expressions
+ *
+ * Apply get_rule_expr_toplevel() to each element of a List.
+ *
+ * This adds commas between the expressions, but caller is responsible
+ * for printing surrounding decoration.
+ */
+static void
+get_rule_list_toplevel(List *lst, deparse_context *context,
+                      bool showimplicit)
+{
+   const char *sep;
+   ListCell   *lc;
+
+   sep = "";
+   foreach(lc, lst)
+   {
+       Node       *e = (Node *) lfirst(lc);
+
+       appendStringInfoString(context->buf, sep);
+       get_rule_expr_toplevel(e, context, showimplicit);
+       sep = ", ";
+   }
+}
+
 /*
  * get_rule_expr_funccall      - Parse back a function-call expression
  *
index f10a3a7a127136f8a638cc70b263db206731620b..f21e785e58f23820d2025a7dd4722725bdf81cea 100644 (file)
@@ -1636,6 +1636,22 @@ select * from int8_tbl i where i.* in (values(i.*::int8_tbl));
  4567890123456789 | -4567890123456789
 (5 rows)
 
+create table tt15v_log(o tt15v, n tt15v, incr bool);
+create rule updlog as on update to tt15v do also
+  insert into tt15v_log values(old, new, row(old,old) < row(new,new));
+\d+ tt15v
+                             View "testviewschm2.tt15v"
+ Column |      Type       | Collation | Nullable | Default | Storage  | Description 
+--------+-----------------+-----------+----------+---------+----------+-------------
+ row    | nestedcomposite |           |          |         | extended | 
+View definition:
+ SELECT ROW(i.*::int8_tbl)::nestedcomposite AS "row"
+   FROM int8_tbl i;
+Rules:
+ updlog AS
+    ON UPDATE TO tt15v DO  INSERT INTO tt15v_log (o, n, incr)
+  VALUES (old.*::tt15v, new.*::tt15v, (ROW(old.*::tt15v, old.*::tt15v) < ROW(new.*::tt15v, new.*::tt15v)))
+
 -- check unique-ification of overlength names
 create view tt18v as
   select * from int8_tbl xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy
@@ -1904,7 +1920,7 @@ drop cascades to view aliased_view_2
 drop cascades to view aliased_view_3
 drop cascades to view aliased_view_4
 DROP SCHEMA testviewschm2 CASCADE;
-NOTICE:  drop cascades to 67 other objects
+NOTICE:  drop cascades to 68 other objects
 DETAIL:  drop cascades to table t1
 drop cascades to view temporal1
 drop cascades to view temporal2
@@ -1963,6 +1979,7 @@ drop cascades to type nestedcomposite
 drop cascades to view tt15v
 drop cascades to view tt16v
 drop cascades to view tt17v
+drop cascades to table tt15v_log
 drop cascades to view tt18v
 drop cascades to view tt19v
 drop cascades to view tt20v
index e7af0bf2fa46807f12ca33adbbb7058e51b35c16..e06d42c59c0665c77ebeea4e087906be5a6fa5b7 100644 (file)
@@ -554,6 +554,11 @@ select * from tt17v;
 select pg_get_viewdef('tt17v', true);
 select * from int8_tbl i where i.* in (values(i.*::int8_tbl));
 
+create table tt15v_log(o tt15v, n tt15v, incr bool);
+create rule updlog as on update to tt15v do also
+  insert into tt15v_log values(old, new, row(old,old) < row(new,new));
+\d+ tt15v
+
 -- check unique-ification of overlength names
 
 create view tt18v as