Fix under-parenthesized display of AT TIME ZONE constructs.
authorTom Lane <[email protected]>
Thu, 1 Dec 2022 16:38:06 +0000 (11:38 -0500)
committerTom Lane <[email protected]>
Thu, 1 Dec 2022 16:38:14 +0000 (11:38 -0500)
In commit 40c24bfef, I forgot to use get_rule_expr_paren() for the
arguments of AT TIME ZONE, resulting in possibly not printing parens
for expressions that need it.  But get_rule_expr_paren() wouldn't have
gotten it right anyway, because isSimpleNode() hadn't been taught that
COERCE_SQL_SYNTAX parent nodes don't guarantee sufficient parentheses.
Improve all that.  Also use this methodology for F_IS_NORMALIZED, so
that we don't print useless parens for that.

In passing, remove a comment that was obsoleted later.

Per report from Duncan Sands.  Back-patch to v14 where this code
came in.  (Before that, we didn't try to print AT TIME ZONE that way,
so there was no bug just ugliness.)

Discussion: https://postgr.es/m/f41566aa-a057-6628-4b7c-b48770ecb84a@deepbluecap.com

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

index f3ea36a231c5943f628401a600e1a102930b7e2b..a20a1b069bf5a150b9c49e53c16337480d17ebaa 100644 (file)
@@ -8254,11 +8254,12 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                        {
                                case T_FuncExpr:
                                        {
-                                               /* special handling for casts */
+                                               /* special handling for casts and COERCE_SQL_SYNTAX */
                                                CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
 
                                                if (type == COERCE_EXPLICIT_CAST ||
-                                                       type == COERCE_IMPLICIT_CAST)
+                                                       type == COERCE_IMPLICIT_CAST ||
+                                                       type == COERCE_SQL_SYNTAX)
                                                        return false;
                                                return true;    /* own parentheses */
                                        }
@@ -8306,11 +8307,12 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                                        return false;
                                case T_FuncExpr:
                                        {
-                                               /* special handling for casts */
+                                               /* special handling for casts and COERCE_SQL_SYNTAX */
                                                CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
 
                                                if (type == COERCE_EXPLICIT_CAST ||
-                                                       type == COERCE_IMPLICIT_CAST)
+                                                       type == COERCE_IMPLICIT_CAST ||
+                                                       type == COERCE_SQL_SYNTAX)
                                                        return false;
                                                return true;    /* own parentheses */
                                        }
@@ -10046,9 +10048,11 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
                case F_TIMEZONE_TEXT_TIMETZ:
                        /* AT TIME ZONE ... note reversed argument order */
                        appendStringInfoChar(buf, '(');
-                       get_rule_expr((Node *) lsecond(expr->args), context, false);
+                       get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
+                                                               (Node *) expr);
                        appendStringInfoString(buf, " AT TIME ZONE ");
-                       get_rule_expr((Node *) linitial(expr->args), context, false);
+                       get_rule_expr_paren((Node *) linitial(expr->args), context, false,
+                                                               (Node *) expr);
                        appendStringInfoChar(buf, ')');
                        return true;
 
@@ -10100,9 +10104,10 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
 
                case F_IS_NORMALIZED:
                        /* IS xxx NORMALIZED */
-                       appendStringInfoString(buf, "((");
-                       get_rule_expr((Node *) linitial(expr->args), context, false);
-                       appendStringInfoString(buf, ") IS");
+                       appendStringInfoString(buf, "(");
+                       get_rule_expr_paren((Node *) linitial(expr->args), context, false,
+                                                               (Node *) expr);
+                       appendStringInfoString(buf, " IS");
                        if (list_length(expr->args) == 2)
                        {
                                Const      *con = (Const *) lsecond(expr->args);
@@ -10123,11 +10128,6 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
                        appendStringInfoChar(buf, ')');
                        return true;
 
-                       /*
-                        * XXX EXTRACT, a/k/a date_part(), is intentionally not covered
-                        * yet.  Add it after we change the return type to numeric.
-                        */
-
                case F_NORMALIZE:
                        /* NORMALIZE() */
                        appendStringInfoString(buf, "NORMALIZE(");
index f9bbad00df210ac3f37e2f702713777445b2befd..17ca29ddbf746acda97f026e0cd6a3b395d9d624 100644 (file)
@@ -1922,6 +1922,7 @@ select pg_get_viewdef('tt20v', true);
 -- reverse-listing of various special function syntaxes required by SQL
 create view tt201v as
 select
+  ('2022-12-01'::date + '1 day'::interval) at time zone 'UTC' as atz,
   extract(day from now()) as extr,
   (now(), '1 day'::interval) overlaps
     (current_timestamp(2), '1 day'::interval) as o,
@@ -1976,10 +1977,11 @@ select
 select pg_get_viewdef('tt201v', true);
                                         pg_get_viewdef                                         
 -----------------------------------------------------------------------------------------------
-  SELECT EXTRACT(day FROM now()) AS extr,                                                     +
+  SELECT (('12-01-2022'::date + '@ 1 day'::interval) AT TIME ZONE 'UTC'::text) AS atz,        +
+     EXTRACT(day FROM now()) AS extr,                                                         +
      ((now(), '@ 1 day'::interval) OVERLAPS (CURRENT_TIMESTAMP(2), '@ 1 day'::interval)) AS o,+
-     (('foo'::text) IS NORMALIZED) AS isn,                                                    +
-     (('foo'::text) IS NFKC NORMALIZED) AS isnn,                                              +
+     ('foo'::text IS NORMALIZED) AS isn,                                                      +
+     ('foo'::text IS NFKC NORMALIZED) AS isnn,                                                +
      NORMALIZE('foo'::text) AS n,                                                             +
      NORMALIZE('foo'::text, NFKD) AS nfkd,                                                    +
      OVERLAY('foo'::text PLACING 'bar'::text FROM 2) AS ovl,                                  +
index bd189b2209219eff0482152b8c44b1161e90ec0e..8838a40f7abbe6c9ed0d8be02ad48cd2cc800882 100644 (file)
@@ -703,6 +703,7 @@ select pg_get_viewdef('tt20v', true);
 
 create view tt201v as
 select
+  ('2022-12-01'::date + '1 day'::interval) at time zone 'UTC' as atz,
   extract(day from now()) as extr,
   (now(), '1 day'::interval) overlaps
     (current_timestamp(2), '1 day'::interval) as o,