[DEPARSE] AT TIME ZONE and similar as a function in FROM do not read back

From: Henson Choi <assam258(at)gmail(dot)com>
To: pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: [DEPARSE] AT TIME ZONE and similar as a function in FROM do not read back
Date: 2026-10-21 11:01:24
Message-ID: CAAAe_zC68s-DwVX1o7C1HVcRk+Z+RTS6Vw19auZFfz=KaX8tZA@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi hackers,

A function in FROM, or in ROWS FROM(), must be written in
function-call syntax. ruleutils.c knows this and puts anything else
there inside CAST(... AS type), but it takes AT TIME ZONE, AT LOCAL,
OVERLAPS and IS NORMALIZED for function calls. They are printed in
parentheses instead, which the grammar does not accept there, so a
view using one cannot be dumped and restored. The two attached
patches add tests and fix it.

The problem
-----------

create view v as
select * from cast(now() at time zone 'UTC' as timestamp) as t;

before: SELECT t FROM (now() AT TIME ZONE 'UTC'::text) t(t);
ERROR: syntax error at or near "TIME"
after: SELECT t FROM CAST((now() AT TIME ZONE 'UTC'::text)
AS timestamp without time zone) t(t);

In ROWS FROM() the same expression fails with a syntax error at
"(". AT LOCAL (17 and later), (a, b) OVERLAPS (c, d) and
x IS NORMALIZED behave the same way. Pretty and non-pretty output
are both affected, so pg_dump output cannot be restored. The other
SQL-syntax functions, such as EXTRACT, SUBSTRING, TRIM and
COLLATION FOR, print in a form the grammar accepts there.

The fix
-------

Only a function in FROM and in ROWS FROM() must be in function-call
syntax (func_expr_windowless), and both go through
get_rule_expr_funccall(). Index, statistics and partition keys
accept function-call syntax too, but they also accept a
parenthesized expression, so the current output reads back there.
Other places, such as the select list, are fine.

looks_like_function() serves both. The callers for keys only decide
whether to add parentheses, so simply rejecting these four forms
there would give key expressions a second pair:
RANGE (((ts AT TIME ZONE 'UTC'::text))). Printing them always as a
CAST in get_func_sql_syntax() would fix FROM too, but would change
all the output that was fine: view bodies, key expressions, EXPLAIN
and so on.

So 0002 gives looks_like_function() an in_from argument and rejects
these four forms only when it is true. get_rule_expr_funccall()
passes true, and the callers for keys pass false. A function in
FROM then gets the dummy CAST() that is already there for such
cases, and no other output changes.

This puts a list of the function OIDs for these four forms in
looks_like_function(). A function added to them must be added to
the list too, so the comments on both sides point to each other.

Testing
-------

0001 adds the four forms to the existing tt20v view in create_view.
0001 has the output before the fix, and 0002 updates it. I checked
that views and ROWS FROM recreated from the fixed output have the
same definitions as the originals.

Back branches
-------------

Since 14, these functions are printed in SQL syntax, and
looks_like_function() treats every SQL-syntax function as a function
call; before that they were printed as plain function calls. So I
think this should be fixed back to 14. Since the only output that
changes is the function in FROM, which was broken, the same patch can
go into the back branches. AT LOCAL exists only in 17 and later, so
14-16 need its cases and its test column dropped.

This came out of the same AI-assisted audit of ruleutils.c as my
earlier [DEPARSE] reports.

Best regards,
Henson

Attachment Content-Type Size
v1-0001-Add-tests-for-AT-TIME-ZONE-and-similar.patch application/octet-stream 5.1 KB
v1-0002-Fix-deparsing-of-AT-TIME-ZONE-and-similar.patch application/octet-stream 13.0 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Salma Elsayed 2026-10-21 11:18:35 Re: PoC: Simplify recovery after dropping a table by LOGGING the restore LSN
Previous Message Bohyun Lee 2026-10-21 10:27:29 Re: [PATCH] pg_upgrade: add --initdb option to create the new cluster automatically