agora inbox for pgsql-bugs@postgresql.org  
help / color / mirror / Atom feed
BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE
2+ messages / 2 participants
[nested] [flat]

* BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE
@ 2026-09-15 06:46 PG Bug reporting form <noreply@postgresql.org>
  2026-09-16 02:56 ` Re: BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE Tender Wang <tndrwang@gmail.com>
  0 siblings, 1 reply; 2+ messages in thread

From: PG Bug reporting form @ 2026-09-15 06:46 UTC (permalink / raw)
  To: pgsql-bugs@lists.postgresql.org; +Cc: imchifan@163.com

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.








^ permalink  raw  reply  [nested|flat] 2+ messages in thread

* Re: BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE
  2026-09-15 06:46 BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE PG Bug reporting form <noreply@postgresql.org>
@ 2026-09-16 02:56 ` Tender Wang <tndrwang@gmail.com>
  0 siblings, 0 replies; 2+ messages in thread

From: Tender Wang @ 2026-09-16 02:56 UTC (permalink / raw)
  To: imchifan@163.com; pgsql-bugs@lists.postgresql.org

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
From 06ea984af3096589ee324f91a7d9d3d6e66af981 Mon Sep 17 00:00:00 2001
From: Tender Wang <tndrwang@gmail.com>
Date: Wed, 16 Sep 2026 10:49:33 +0800
Subject: [PATCH] Reject set-returning functions in MERGE INSERT actions

MERGE INSERT values are transformed using EXPR_KIND_VALUES_SINGLE,
which allows set-returning functions and sets p_hasTargetSRFs.  However,
a MERGE INSERT action is supposed to insert one row for each candidate
change row, and its target list cannot be expanded using a ProjectSet
plan node.

As a result, an SRF in a MERGE INSERT action was accepted during parse
analysis, but failed during executor initialization with "set-valued
function called in context that cannot accept a set".  In particular,
this allowed PREPARE to succeed for a statement that would always fail
when executed.

Reject such functions while transforming the MERGE INSERT action, so
the error is reported during parse analysis.
---
 src/backend/parser/parse_merge.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c
index 0a70d48fd4c..daa89e199a5 100644
--- a/src/backend/parser/parse_merge.c
+++ b/src/backend/parser/parse_merge.c
@@ -17,6 +17,7 @@
 
 #include "access/sysattr.h"
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_collate.h"
@@ -345,6 +346,11 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 														   mergeWhenClause->values,
 														   EXPR_KIND_VALUES_SINGLE,
 														   true);
+						if (pstate->p_hasTargetSRFs)
+							ereport(ERROR,
+								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+								 errmsg("set-returning functions are not allowed in MERGE INSERT actions"),
+								 parser_errposition(pstate, exprLocation(pstate->p_last_srf))));
 
 						/* Prepare row for assignment to target table */
 						exprList = transformInsertRow(pstate, exprList,
-- 
2.43.0



Attachments:

  [text/plain] 0001-Reject-set-returning-functions-in-MERGE-INSERT-actio.patch (1.9K, ../../CAHewXN=t2OW_gehDsso17CLgqRfMfnhzDsby7pXWpzSy_-e9Tg@mail.gmail.com/2-0001-Reject-set-returning-functions-in-MERGE-INSERT-actio.patch)
  download | inline diff:
From 06ea984af3096589ee324f91a7d9d3d6e66af981 Mon Sep 17 00:00:00 2001
From: Tender Wang <tndrwang@gmail.com>
Date: Wed, 16 Sep 2026 10:49:33 +0800
Subject: [PATCH] Reject set-returning functions in MERGE INSERT actions

MERGE INSERT values are transformed using EXPR_KIND_VALUES_SINGLE,
which allows set-returning functions and sets p_hasTargetSRFs.  However,
a MERGE INSERT action is supposed to insert one row for each candidate
change row, and its target list cannot be expanded using a ProjectSet
plan node.

As a result, an SRF in a MERGE INSERT action was accepted during parse
analysis, but failed during executor initialization with "set-valued
function called in context that cannot accept a set".  In particular,
this allowed PREPARE to succeed for a statement that would always fail
when executed.

Reject such functions while transforming the MERGE INSERT action, so
the error is reported during parse analysis.
---
 src/backend/parser/parse_merge.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c
index 0a70d48fd4c..daa89e199a5 100644
--- a/src/backend/parser/parse_merge.c
+++ b/src/backend/parser/parse_merge.c
@@ -17,6 +17,7 @@
 
 #include "access/sysattr.h"
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_collate.h"
@@ -345,6 +346,11 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
 														   mergeWhenClause->values,
 														   EXPR_KIND_VALUES_SINGLE,
 														   true);
+						if (pstate->p_hasTargetSRFs)
+							ereport(ERROR,
+								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+								 errmsg("set-returning functions are not allowed in MERGE INSERT actions"),
+								 parser_errposition(pstate, exprLocation(pstate->p_last_srf))));
 
 						/* Prepare row for assignment to target table */
 						exprList = transformInsertRow(pstate, exprList,
-- 
2.43.0



^ permalink  raw  reply  [nested|flat] 2+ messages in thread


end of thread, other threads:[~2026-09-16 02:56 UTC | newest]

Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 06:46 BUG #19689: MERGE INSERT accepts a set-returning function during PREPARE but fails at EXECUTE PG Bug reporting form <noreply@postgresql.org>
2026-09-16 02:56 ` Tender Wang <tndrwang@gmail.com>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox