1 /*-------------------------------------------------------------------------
4 * handle merge-statement in parser
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/parser/parse_merge.c
13 *-------------------------------------------------------------------------
18 #include "access/sysattr.h"
19 #include "nodes/makefuncs.h"
20 #include "parser/analyze.h"
21 #include "parser/parse_clause.h"
22 #include "parser/parse_collate.h"
23 #include "parser/parse_cte.h"
24 #include "parser/parse_expr.h"
25 #include "parser/parse_merge.h"
26 #include "parser/parse_relation.h"
27 #include "parser/parse_target.h"
28 #include "parser/parsetree.h"
29 #include "utils/rel.h"
31 static void setNamespaceForMergeWhen(ParseState
*pstate
,
32 MergeWhenClause
*mergeWhenClause
,
35 static void setNamespaceVisibilityForRTE(List
*namespace, RangeTblEntry
*rte
,
40 * Make appropriate changes to the namespace visibility while transforming
41 * individual action's quals and targetlist expressions. In particular, for
42 * INSERT actions we must only see the source relation (since INSERT action is
43 * invoked for NOT MATCHED [BY TARGET] tuples and hence there is no target
44 * tuple to deal with). On the other hand, UPDATE and DELETE actions can see
45 * both source and target relations, unless invoked for NOT MATCHED BY SOURCE.
47 * Also, since the internal join node can hide the source and target
48 * relations, we must explicitly make the respective relation as visible so
49 * that columns can be referenced unqualified from these relations.
52 setNamespaceForMergeWhen(ParseState
*pstate
, MergeWhenClause
*mergeWhenClause
,
53 Index targetRTI
, Index sourceRTI
)
55 RangeTblEntry
*targetRelRTE
,
58 targetRelRTE
= rt_fetch(targetRTI
, pstate
->p_rtable
);
59 sourceRelRTE
= rt_fetch(sourceRTI
, pstate
->p_rtable
);
61 if (mergeWhenClause
->matchKind
== MERGE_WHEN_MATCHED
)
63 Assert(mergeWhenClause
->commandType
== CMD_UPDATE
||
64 mergeWhenClause
->commandType
== CMD_DELETE
||
65 mergeWhenClause
->commandType
== CMD_NOTHING
);
67 /* MATCHED actions can see both target and source relations. */
68 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
69 targetRelRTE
, true, true);
70 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
71 sourceRelRTE
, true, true);
73 else if (mergeWhenClause
->matchKind
== MERGE_WHEN_NOT_MATCHED_BY_SOURCE
)
76 * NOT MATCHED BY SOURCE actions can see the target relation, but they
77 * can't see the source relation.
79 Assert(mergeWhenClause
->commandType
== CMD_UPDATE
||
80 mergeWhenClause
->commandType
== CMD_DELETE
||
81 mergeWhenClause
->commandType
== CMD_NOTHING
);
82 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
83 targetRelRTE
, true, true);
84 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
85 sourceRelRTE
, false, false);
87 else /* MERGE_WHEN_NOT_MATCHED_BY_TARGET */
90 * NOT MATCHED [BY TARGET] actions can't see target relation, but they
91 * can see source relation.
93 Assert(mergeWhenClause
->commandType
== CMD_INSERT
||
94 mergeWhenClause
->commandType
== CMD_NOTHING
);
95 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
96 targetRelRTE
, false, false);
97 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
98 sourceRelRTE
, true, true);
103 * transformMergeStmt -
104 * transforms a MERGE statement
107 transformMergeStmt(ParseState
*pstate
, MergeStmt
*stmt
)
109 Query
*qry
= makeNode(Query
);
111 AclMode targetPerms
= ACL_NO_RIGHTS
;
112 bool is_terminal
[NUM_MERGE_MATCH_KINDS
];
114 List
*mergeActionList
;
115 ParseNamespaceItem
*nsitem
;
117 /* There can't be any outer WITH to worry about */
118 Assert(pstate
->p_ctenamespace
== NIL
);
120 qry
->commandType
= CMD_MERGE
;
121 qry
->hasRecursive
= false;
123 /* process the WITH clause independently of all else */
124 if (stmt
->withClause
)
126 if (stmt
->withClause
->recursive
)
128 (errcode(ERRCODE_SYNTAX_ERROR
),
129 errmsg("WITH RECURSIVE is not supported for MERGE statement")));
131 qry
->cteList
= transformWithClause(pstate
, stmt
->withClause
);
132 qry
->hasModifyingCTE
= pstate
->p_hasModifyingCTE
;
136 * Check WHEN clauses for permissions and sanity
138 is_terminal
[MERGE_WHEN_MATCHED
] = false;
139 is_terminal
[MERGE_WHEN_NOT_MATCHED_BY_SOURCE
] = false;
140 is_terminal
[MERGE_WHEN_NOT_MATCHED_BY_TARGET
] = false;
141 foreach(l
, stmt
->mergeWhenClauses
)
143 MergeWhenClause
*mergeWhenClause
= (MergeWhenClause
*) lfirst(l
);
146 * Collect permissions to check, according to action types. We require
147 * SELECT privileges for DO NOTHING because it'd be irregular to have
148 * a target relation with zero privileges checked, in case DO NOTHING
149 * is the only action. There's no damage from that: any meaningful
150 * MERGE command requires at least some access to the table anyway.
152 switch (mergeWhenClause
->commandType
)
155 targetPerms
|= ACL_INSERT
;
158 targetPerms
|= ACL_UPDATE
;
161 targetPerms
|= ACL_DELETE
;
164 targetPerms
|= ACL_SELECT
;
167 elog(ERROR
, "unknown action in MERGE WHEN clause");
171 * Check for unreachable WHEN clauses
173 if (is_terminal
[mergeWhenClause
->matchKind
])
175 (errcode(ERRCODE_SYNTAX_ERROR
),
176 errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
177 if (mergeWhenClause
->condition
== NULL
)
178 is_terminal
[mergeWhenClause
->matchKind
] = true;
182 * Set up the MERGE target table. The target table is added to the
183 * namespace below and to joinlist in transform_MERGE_to_join, so don't do
186 * Initially mergeTargetRelation is the same as resultRelation, so data is
187 * read from the table being updated. However, that might be changed by
188 * the rewriter, if the target is a trigger-updatable view, to allow
189 * target data to be read from the expanded view query while updating the
190 * original view relation.
192 qry
->resultRelation
= setTargetTable(pstate
, stmt
->relation
,
195 qry
->mergeTargetRelation
= qry
->resultRelation
;
197 /* The target relation must be a table or a view */
198 if (pstate
->p_target_relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
199 pstate
->p_target_relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
&&
200 pstate
->p_target_relation
->rd_rel
->relkind
!= RELKIND_VIEW
)
202 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
203 errmsg("cannot execute MERGE on relation \"%s\"",
204 RelationGetRelationName(pstate
->p_target_relation
)),
205 errdetail_relkind_not_supported(pstate
->p_target_relation
->rd_rel
->relkind
)));
207 /* Now transform the source relation to produce the source RTE. */
208 transformFromClause(pstate
,
209 list_make1(stmt
->sourceRelation
));
210 sourceRTI
= list_length(pstate
->p_rtable
);
211 nsitem
= GetNSItemByRangeTablePosn(pstate
, sourceRTI
, 0);
214 * Check that the target table doesn't conflict with the source table.
215 * This would typically be a checkNameSpaceConflicts call, but we want a
216 * more specific error message.
218 if (strcmp(pstate
->p_target_nsitem
->p_names
->aliasname
,
219 nsitem
->p_names
->aliasname
) == 0)
221 errcode(ERRCODE_DUPLICATE_ALIAS
),
222 errmsg("name \"%s\" specified more than once",
223 pstate
->p_target_nsitem
->p_names
->aliasname
),
224 errdetail("The name is used both as MERGE target table and data source."));
227 * There's no need for a targetlist here; it'll be set up by
228 * preprocess_targetlist later.
230 qry
->targetList
= NIL
;
231 qry
->rtable
= pstate
->p_rtable
;
232 qry
->rteperminfos
= pstate
->p_rteperminfos
;
235 * Transform the join condition. This includes references to the target
236 * side, so add that to the namespace.
238 addNSItemToQuery(pstate
, pstate
->p_target_nsitem
, false, true, true);
239 qry
->mergeJoinCondition
= transformExpr(pstate
, stmt
->joinCondition
,
243 * Create the temporary query's jointree using the joinlist we built using
244 * just the source relation; the target relation is not included. The join
245 * will be constructed fully by transform_MERGE_to_join.
247 qry
->jointree
= makeFromExpr(pstate
->p_joinlist
, NULL
);
249 /* Transform the RETURNING list, if any */
250 transformReturningClause(pstate
, qry
, stmt
->returningClause
,
251 EXPR_KIND_MERGE_RETURNING
);
254 * We now have a good query shape, so now look at the WHEN conditions and
255 * action targetlists.
257 * Overall, the MERGE Query's targetlist is NIL.
259 * Each individual action has its own targetlist that needs separate
260 * transformation. These transforms don't do anything to the overall
261 * targetlist, since that is only used for resjunk columns.
263 * We can reference any column in Target or Source, which is OK because
264 * both of those already have RTEs. There is nothing like the EXCLUDED
265 * pseudo-relation for INSERT ON CONFLICT.
267 mergeActionList
= NIL
;
268 foreach(l
, stmt
->mergeWhenClauses
)
270 MergeWhenClause
*mergeWhenClause
= lfirst_node(MergeWhenClause
, l
);
273 action
= makeNode(MergeAction
);
274 action
->commandType
= mergeWhenClause
->commandType
;
275 action
->matchKind
= mergeWhenClause
->matchKind
;
278 * Set namespace for the specific action. This must be done before
279 * analyzing the WHEN quals and the action targetlist.
281 setNamespaceForMergeWhen(pstate
, mergeWhenClause
,
286 * Transform the WHEN condition.
288 * Note that these quals are NOT added to the join quals; instead they
289 * are evaluated separately during execution to decide which of the
290 * WHEN MATCHED or WHEN NOT MATCHED actions to execute.
292 action
->qual
= transformWhereClause(pstate
, mergeWhenClause
->condition
,
293 EXPR_KIND_MERGE_WHEN
, "WHEN");
296 * Transform target lists for each INSERT and UPDATE action stmt
298 switch (action
->commandType
)
302 List
*exprList
= NIL
;
304 RTEPermissionInfo
*perminfo
;
310 pstate
->p_is_insert
= true;
312 icolumns
= checkInsertTargets(pstate
,
313 mergeWhenClause
->targetList
,
315 Assert(list_length(icolumns
) == list_length(attrnos
));
317 action
->override
= mergeWhenClause
->override
;
320 * Handle INSERT much like in transformInsertStmt
322 if (mergeWhenClause
->values
== NIL
)
325 * We have INSERT ... DEFAULT VALUES. We can handle
326 * this case by emitting an empty targetlist --- all
327 * columns will be defaulted when the planner expands
335 * Process INSERT ... VALUES with a single VALUES
336 * sublist. We treat this case separately for
337 * efficiency. The sublist is just computed directly
338 * as the Query's targetlist, with no VALUES RTE. So
339 * it works just like a SELECT without any FROM.
343 * Do basic expression transformation (same as a ROW()
344 * expr, but allow SetToDefault at top level)
346 exprList
= transformExpressionList(pstate
,
347 mergeWhenClause
->values
,
348 EXPR_KIND_VALUES_SINGLE
,
351 /* Prepare row for assignment to target table */
352 exprList
= transformInsertRow(pstate
, exprList
,
353 mergeWhenClause
->targetList
,
359 * Generate action's target list using the computed list
360 * of expressions. Also, mark all the target columns as
361 * needing insert permissions.
363 perminfo
= pstate
->p_target_nsitem
->p_perminfo
;
364 forthree(lc
, exprList
, icols
, icolumns
, attnos
, attrnos
)
366 Expr
*expr
= (Expr
*) lfirst(lc
);
367 ResTarget
*col
= lfirst_node(ResTarget
, icols
);
368 AttrNumber attr_num
= (AttrNumber
) lfirst_int(attnos
);
371 tle
= makeTargetEntry(expr
,
375 action
->targetList
= lappend(action
->targetList
, tle
);
377 perminfo
->insertedCols
=
378 bms_add_member(perminfo
->insertedCols
,
379 attr_num
- FirstLowInvalidHeapAttributeNumber
);
385 pstate
->p_is_insert
= false;
387 transformUpdateTargetList(pstate
,
388 mergeWhenClause
->targetList
);
395 action
->targetList
= NIL
;
398 elog(ERROR
, "unknown action in MERGE WHEN clause");
401 mergeActionList
= lappend(mergeActionList
, action
);
404 qry
->mergeActionList
= mergeActionList
;
406 qry
->hasTargetSRFs
= false;
407 qry
->hasSubLinks
= pstate
->p_hasSubLinks
;
409 assign_query_collations(pstate
, qry
);
415 setNamespaceVisibilityForRTE(List
*namespace, RangeTblEntry
*rte
,
421 foreach(lc
, namespace)
423 ParseNamespaceItem
*nsitem
= (ParseNamespaceItem
*) lfirst(lc
);
425 if (nsitem
->p_rte
== rte
)
427 nsitem
->p_rel_visible
= rel_visible
;
428 nsitem
->p_cols_visible
= cols_visible
;