BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE

Started by PG Bug reporting form6 days ago2 messagesbugs
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

appliessuccessCI history

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253798
psql -h localhost -U postgres

Built from patchset v2 (message #2), September 21, 2026 at 03:17 AM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t253798_2 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t253798_2 && git checkout t253798_2

Patchset v2 (message #2) is on t253798_2

Jump to latest
#1PG Bug reporting form
noreply@postgresql.org

The following bug has been logged on the website:

Bug reference: 19689
Logged by: Qifan Liu
Email address: imchifan@163.com
PostgreSQL version: 18.6
Operating system: Linux x86-64
Description:

Description
-----------
A set-returning function can be placed in the VALUES expression of a MERGE
WHEN NOT MATCHED INSERT action. PREPARE accepts the statement, but EXECUTE
fails with "set-valued function called in context that cannot accept a set"
and inserts no rows. This violates the analysis/execution contract: an
expression that the MERGE action cannot execute should be rejected while the
statement is analyzed, rather than being accepted into a prepared plan that
deterministically fails only when executed.

Impact: Applications can successfully prepare an unusable MERGE statement
and encounter a localized execution failure later. The statement inserts no
rows. This was reproduced consistently on all tested versions.

Steps to reproduce
------------------
```sql
CREATE TABLE merge_target (id integer);
CREATE TABLE merge_source (id integer);
INSERT INTO merge_source VALUES (1);

PREPARE merge_srf AS
MERGE INTO merge_target AS t
USING merge_source AS s
ON false
WHEN NOT MATCHED THEN
INSERT VALUES (generate_series(1, 2));

EXECUTE merge_srf;

SELECT count(*) AS rows_after_execution
FROM merge_target;
```

Actual result
-------------
```text
ERROR: set-valued function called in context that cannot accept a set

rows_after_execution
----------------------
0
(1 row)
```

Expected result
---------------
PREPARE should reject the set-returning function because a MERGE INSERT
action cannot execute that expression in this context. It should not create
a prepared statement that is accepted successfully and then
deterministically fails during EXECUTE.

Additional information
----------------------
The issue was reproduced on PostgreSQL 20devel, PostgreSQL 18.6, and
PostgreSQL 17.11. Inference: the MERGE INSERT action's specialized
expression handling does not propagate or enforce the restriction on
set-returning functions during analysis, leaving the executor to detect the
unsupported context.

#2Tender Wang
tndrwang@gmail.com
In reply to: PG Bug reporting form (#1)
Re: BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE

PG Bug reporting form <noreply@postgresql.org> 于2026年9月15日周二 17:36写道:

Additional information
----------------------
The issue was reproduced on PostgreSQL 20devel, PostgreSQL 18.6, and
PostgreSQL 17.11. Inference: the MERGE INSERT action's specialized
expression handling does not propagate or enforce the restriction on
set-returning functions during analysis, leaving the executor to detect the
unsupported context.

I looked into this issue.

MERGE INSERT values are transformed using EXPR_KIND_VALUES_SINGLE,
which permits set-returning functions and sets
pstate->p_hasTargetSRFs. However, transformMergeStmt() later
unconditionally sets qry->hasTargetSRFs to false, and a MERGE action
target list is not expanded using a ProjectSet plan node.

Consequently, the SRF is accepted during PREPARE, but later fails
during executor expression initialization with:

ERROR: set-valued function called in context that cannot accept a set

The attached patch is one possible fix. It checks
pstate->p_hasTargetSRFs immediately after transforming the MERGE
INSERT values and reports an error during parse analysis. The check
is placed specifically in the CMD_INSERT case, so it does not affect
the other MERGE actions or set-returning functions used as the MERGE
data source.

I have not included a regression test in this version, since I would
first like to confirm whether rejecting SRFs in MERGE INSERT actions
is the intended behavior. If so, I can add a test in the next
version.

Thoughts?

--
Thanks,
Tender Wang

Attachments:

t253798_2
0001-Reject-set-returning-functions-in-MERGE-INSERT-actio.patchtext/plain; charset=US-ASCII; name=0001-Reject-set-returning-functions-in-MERGE-INSERT-actio.patchDownload+6-1