[DEPARSE] An alias named 'set' on an UPDATE, DELETE or MERGE target does not read back

From: Henson Choi <assam258(at)gmail(dot)com>
To: pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: [DEPARSE] An alias named 'set' on an UPDATE, DELETE or MERGE target does not read back
Date: 2026-10-13 06:04:52
Message-ID: CAAAe_zDqXin5NeB5atRCbZqUdcMOeyXRqhan+tVp0F30OCURnw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi hackers,

When the target of an UPDATE, DELETE or MERGE in a rule or in a
BEGIN ATOMIC function body has the alias "set", ruleutils.c prints
the alias bare, and the text no longer parses. The two attached
patches add tests and fix it.

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

The example below shows the output before and after the patch
(only the body of the rule is shown):

create table t (a int, b int);
create rule r as on insert to t
do also update t "set" set b = 1 where "set".a = 2;
select pg_get_ruledef(oid) from pg_rewrite where rulename = 'r';

before: ON INSERT TO public.t DO UPDATE t set SET b = 1
WHERE (set.a = 2);
ERROR: syntax error at or near "b"
after: ON INSERT TO public.t DO UPDATE t AS set SET b = 1
WHERE (set.a = 2);

DELETE FROM t "set" and MERGE INTO t "set" are printed the same
way and fail at "set". pg_dump prints rules and function bodies
with the same code, so a dump of such a database does not restore.

The target of these three statements is parsed by
relation_expr_opt_alias. On seeing "set" after the relation name,
the parser has to decide with one token of lookahead whether it is
an alias or the SET keyword of UPDATE, and gram.y resolves that in
favor of the keyword (see the comment above
relation_expr_opt_alias). DELETE and MERGE use the same
production, so they are caught too, although no SET clause follows
there. As SET is an unreserved keyword, quote_identifier() leaves
"set" bare.

The fix
-------

0002 gives get_rte_alias() a dml_target argument and prints AS
before an alias named "set" on the target of UPDATE, DELETE and
MERGE. INSERT already prints AS always. An alias named "set" in
FROM is not ambiguous, so it is printed as before.

Quoting the alias only at that position, as in
UPDATE t "set" SET ..., would work as well. Which of the two
would people prefer?

Tests
-----

0001 adds the tests with the output before the fix, and 0002
updates it, so that the diff shows what changes. The rules and
the function are printed, dropped and created again from the
printed text.

statement where "set" is object
--------- ---------------------- -------------------------
UPDATE target alias rule
DELETE target alias rule
MERGE target alias BEGIN ATOMIC function
SELECT FROM alias (unchanged) view

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

I think this should be back-patched to 15. I applied both
patches to REL_15_STABLE through REL_19_STABLE.

git am applies them cleanly on each branch, and make check passes,
also with the test patch alone.

This came out of an AI-assisted audit of ruleutils.c. I
will post the other problems it found as separate,
independent reports as I work through them.

Best regards,
Henson

Attachment Content-Type Size
v1-0001-Add-tests-for-a-relation-alias-named-SET-in-DML-t.patch application/octet-stream 7.6 KB
v1-0002-Write-AS-before-an-alias-named-SET-of-a-DML-targe.patch application/octet-stream 10.5 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Mahendra Singh Thalor 2026-10-13 06:12:52 Re: Non-text mode for pg_dumpall
Previous Message kedar anavardekar 2026-10-13 05:38:19 Re: Fix WITHOUT OVERLAPS multirange with location replication