Columns assigned by MERGE can be dropped from SQL-standard function bodies

From: Henson Choi <assam258(at)gmail(dot)com>
To: pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Columns assigned by MERGE can be dropped from SQL-standard function bodies
Date: 2026-10-30 14:01:26
Message-ID: CAAAe_zBHzEFR9bFSiJYfve7=QoWG_cL8+TWxx8vz47Jy0kUSHQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi hackers,

A SQL-standard function or procedure body that uses MERGE does not
depend on the columns assigned by the MERGE actions, so those columns
can be dropped or have their type altered. The same goes for a rule
whose action has a MERGE in WITH. The function then fails at
execution time, and its dumped definition cannot be restored. The
two attached patches add tests and fix it.

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

create table t (k int, a int, b int);
create table s (k int, v int);
create function f() returns void
begin atomic
merge into t using s on t.k = s.k
when matched then update set a = s.v
when not matched then insert (k, b) values (s.k, s.v);
end;
alter table t drop column a; -- succeeds
alter table t drop column b; -- succeeds
select pg_get_functiondef('f'::regproc);

...
THEN UPDATE SET "........pg.dropped.2........" = s.v
WHEN NOT MATCHED
THEN INSERT (k, "........pg.dropped.3........")
...

ALTER COLUMN TYPE succeeds as well, and calling the function then
fails with "table row type and query-specified row type do not
match".

find_expr_references_walker() records a dependency on each target
column of an INSERT or UPDATE query (since c6be1f43ab0). But MERGE
keeps the target lists of its INSERT and UPDATE actions in
mergeActionList, not in the query's targetList, so they were never
looked at. This goes back to 15, where MERGE was added
(7103ebb7aae).

The fix
-------

0002 records the dependencies for the target lists of the INSERT and
UPDATE actions as well. The existing loop is moved into a small
helper used for both cases. With it, DROP COLUMN and ALTER COLUMN
TYPE on an assigned column fail, as they do for INSERT and UPDATE.
Columns that the MERGE does not assign can still be dropped.
Existing functions and rules get the dependencies only when they are
created again.

Tests
-----

0001 adds the tests with the output before the fix, and 0002 updates
it. create_function_sql covers a function body, and rules covers a
rule with a MERGE in WITH. Each tries ALTER COLUMN TYPE and DROP
COLUMN on the assigned columns and on one that is not assigned, and
then prints the definition.

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

I think this should be back-patched to 15. The patches apply as they
are to REL_19_STABLE. On 17 and 18 the code applies as it is. In
the tests, the function test has to be moved, since the neighbouring
test it follows does not exist there, and the DETAIL of the ALTER
COLUMN TYPE errors has no trailing period there. A version for 16 is
attached separately as .txt files, and it also applies to 15 as it
is. It differs as follows:

- foreach and lfirst_node are used instead of foreach_node.
- add_object_address() is passed OCLASS_CLASS instead of
RelationRelationId. The latter also compiles there without a
warning but is wrong, so this needs care when back-patching.
- The rule test is dropped, since MERGE in WITH is allowed only
from 17.

On master and on 15 to 19, the tests show the bug with 0001 alone,
and check-world passes with both patches (on 17 and 18 with the test
adjusted as above).

This also came out of the AI-assisted audit of ruleutils.c, the same
one as my earlier report on parameters in SQL-standard function
bodies.

Best regards,
Henson

Attachment Content-Type Size
REL_16-v1-0001-Add-tests-for-column-dependencies-of-MERGE-action.txt text/plain 4.1 KB
v1-0001-Add-tests-for-column-dependencies-of-MERGE-action.patch application/octet-stream 7.7 KB
v1-0002-Record-dependencies-on-the-target-columns-of-MERG.patch application/octet-stream 9.8 KB
REL_16-v1-0002-Record-dependencies-on-the-target-columns-of-MERG.txt text/plain 6.6 KB

Browse pgsql-hackers by date

  From Date Subject
Next Message Bertrand Drouvot 2026-10-30 14:29:35 Re: Introduce XID age based replication slot invalidation
Previous Message Greg Burd 2026-10-30 13:51:41 Re: Tepid: selective index updates for heap relations