Fix under-parenthesized display of AT TIME ZONE constructs.
authorTom Lane
Thu, 1 Dec 2022 16:38:06 +0000 (11:38 -0500)
committerTom Lane
Thu, 1 Dec 2022 16:38:15 +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 c86f825d8b80fb7d126a90ec19514fa84f7114a8..8e428e932865faf0ebf67eba67a91bc559113929 100644 (file)
@@ -8189,11 +8189,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 */
                    }
@@ -8241,11 +8242,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 */
                    }
@@ -10017,9 +10019,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;
 
@@ -10071,9 +10075,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);
@@ -10094,11 +10099,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 a8df9cd1237ab02421690e3a1e40935a62388720..569d5c255c20cf9097c6f6d9af726d6cef719f3d 100644 (file)
@@ -1790,6 +1790,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,
@@ -1812,10 +1813,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 843a6d2e4a713e0e85fb44411fb4f23a8e03d2f1..168b4be591cbab93dc9c6e963b558e0ae9402bad 100644 (file)
@@ -608,6 +608,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,