PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
parse_merge.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * parse_merge.c
4 * handle merge-statement in parser
5 *
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/parser/parse_merge.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "postgres.h"
17
18#include "access/sysattr.h"
19#include "nodes/makefuncs.h"
20#include "parser/analyze.h"
21#include "parser/parse_clause.h"
23#include "parser/parse_cte.h"
24#include "parser/parse_expr.h"
25#include "parser/parse_merge.h"
27#include "parser/parse_target.h"
28#include "parser/parsetree.h"
29#include "utils/rel.h"
30
31static void setNamespaceForMergeWhen(ParseState *pstate,
32 MergeWhenClause *mergeWhenClause,
33 Index targetRTI,
34 Index sourceRTI);
35static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
36 bool rel_visible,
37 bool cols_visible);
38
39/*
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.
46 *
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.
50 */
51static void
53 Index targetRTI, Index sourceRTI)
54{
55 RangeTblEntry *targetRelRTE,
56 *sourceRelRTE;
57
58 targetRelRTE = rt_fetch(targetRTI, pstate->p_rtable);
59 sourceRelRTE = rt_fetch(sourceRTI, pstate->p_rtable);
60
61 if (mergeWhenClause->matchKind == MERGE_WHEN_MATCHED)
62 {
63 Assert(mergeWhenClause->commandType == CMD_UPDATE ||
64 mergeWhenClause->commandType == CMD_DELETE ||
65 mergeWhenClause->commandType == CMD_NOTHING);
66
67 /* MATCHED actions can see both target and source relations. */
69 targetRelRTE, true, true);
71 sourceRelRTE, true, true);
72 }
73 else if (mergeWhenClause->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
74 {
75 /*
76 * NOT MATCHED BY SOURCE actions can see the target relation, but they
77 * can't see the source relation.
78 */
79 Assert(mergeWhenClause->commandType == CMD_UPDATE ||
80 mergeWhenClause->commandType == CMD_DELETE ||
81 mergeWhenClause->commandType == CMD_NOTHING);
83 targetRelRTE, true, true);
85 sourceRelRTE, false, false);
86 }
87 else /* MERGE_WHEN_NOT_MATCHED_BY_TARGET */
88 {
89 /*
90 * NOT MATCHED [BY TARGET] actions can't see target relation, but they
91 * can see source relation.
92 */
93 Assert(mergeWhenClause->commandType == CMD_INSERT ||
94 mergeWhenClause->commandType == CMD_NOTHING);
96 targetRelRTE, false, false);
98 sourceRelRTE, true, true);
99 }
100}
101
102/*
103 * transformMergeStmt -
104 * transforms a MERGE statement
105 */
106Query *
108{
109 Query *qry = makeNode(Query);
110 ListCell *l;
111 AclMode targetPerms = ACL_NO_RIGHTS;
112 bool is_terminal[NUM_MERGE_MATCH_KINDS];
113 Index sourceRTI;
114 List *mergeActionList;
115 ParseNamespaceItem *nsitem;
116
117 /* There can't be any outer WITH to worry about */
118 Assert(pstate->p_ctenamespace == NIL);
119
120 qry->commandType = CMD_MERGE;
121 qry->hasRecursive = false;
122
123 /* process the WITH clause independently of all else */
124 if (stmt->withClause)
125 {
126 if (stmt->withClause->recursive)
128 (errcode(ERRCODE_SYNTAX_ERROR),
129 errmsg("WITH RECURSIVE is not supported for MERGE statement")));
130
131 qry->cteList = transformWithClause(pstate, stmt->withClause);
132 qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
133 }
134
135 /*
136 * Check WHEN clauses for permissions and sanity
137 */
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)
142 {
143 MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l);
144
145 /*
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.
151 */
152 switch (mergeWhenClause->commandType)
153 {
154 case CMD_INSERT:
155 targetPerms |= ACL_INSERT;
156 break;
157 case CMD_UPDATE:
158 targetPerms |= ACL_UPDATE;
159 break;
160 case CMD_DELETE:
161 targetPerms |= ACL_DELETE;
162 break;
163 case CMD_NOTHING:
164 targetPerms |= ACL_SELECT;
165 break;
166 default:
167 elog(ERROR, "unknown action in MERGE WHEN clause");
168 }
169
170 /*
171 * Check for unreachable WHEN clauses
172 */
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;
179 }
180
181 /*
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
184 * it here.
185 *
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.
191 */
192 qry->resultRelation = setTargetTable(pstate, stmt->relation,
193 stmt->relation->inh,
194 false, targetPerms);
195 qry->mergeTargetRelation = qry->resultRelation;
196
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\"",
206
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);
212
213 /*
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.
217 */
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",
224 errdetail("The name is used both as MERGE target table and data source."));
225
226 /*
227 * There's no need for a targetlist here; it'll be set up by
228 * preprocess_targetlist later.
229 */
230 qry->targetList = NIL;
231 qry->rtable = pstate->p_rtable;
232 qry->rteperminfos = pstate->p_rteperminfos;
233
234 /*
235 * Transform the join condition. This includes references to the target
236 * side, so add that to the namespace.
237 */
238 addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true);
239 qry->mergeJoinCondition = transformExpr(pstate, stmt->joinCondition,
241
242 /*
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.
246 */
247 qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
248
249 /* Transform the RETURNING list, if any */
250 transformReturningClause(pstate, qry, stmt->returningClause,
252
253 /*
254 * We now have a good query shape, so now look at the WHEN conditions and
255 * action targetlists.
256 *
257 * Overall, the MERGE Query's targetlist is NIL.
258 *
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.
262 *
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.
266 */
267 mergeActionList = NIL;
268 foreach(l, stmt->mergeWhenClauses)
269 {
270 MergeWhenClause *mergeWhenClause = lfirst_node(MergeWhenClause, l);
272
274 action->commandType = mergeWhenClause->commandType;
275 action->matchKind = mergeWhenClause->matchKind;
276
277 /*
278 * Set namespace for the specific action. This must be done before
279 * analyzing the WHEN quals and the action targetlist.
280 */
281 setNamespaceForMergeWhen(pstate, mergeWhenClause,
282 qry->resultRelation,
283 sourceRTI);
284
285 /*
286 * Transform the WHEN condition.
287 *
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.
291 */
292 action->qual = transformWhereClause(pstate, mergeWhenClause->condition,
293 EXPR_KIND_MERGE_WHEN, "WHEN");
294
295 /*
296 * Transform target lists for each INSERT and UPDATE action stmt
297 */
298 switch (action->commandType)
299 {
300 case CMD_INSERT:
301 {
302 List *exprList = NIL;
303 ListCell *lc;
304 RTEPermissionInfo *perminfo;
305 ListCell *icols;
306 ListCell *attnos;
307 List *icolumns;
308 List *attrnos;
309
310 pstate->p_is_insert = true;
311
312 icolumns = checkInsertTargets(pstate,
313 mergeWhenClause->targetList,
314 &attrnos);
315 Assert(list_length(icolumns) == list_length(attrnos));
316
317 action->override = mergeWhenClause->override;
318
319 /*
320 * Handle INSERT much like in transformInsertStmt
321 */
322 if (mergeWhenClause->values == NIL)
323 {
324 /*
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
328 * the targetlist.
329 */
330 exprList = NIL;
331 }
332 else
333 {
334 /*
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.
340 */
341
342 /*
343 * Do basic expression transformation (same as a ROW()
344 * expr, but allow SetToDefault at top level)
345 */
346 exprList = transformExpressionList(pstate,
347 mergeWhenClause->values,
349 true);
350
351 /* Prepare row for assignment to target table */
352 exprList = transformInsertRow(pstate, exprList,
353 mergeWhenClause->targetList,
354 icolumns, attrnos,
355 false);
356 }
357
358 /*
359 * Generate action's target list using the computed list
360 * of expressions. Also, mark all the target columns as
361 * needing insert permissions.
362 */
363 perminfo = pstate->p_target_nsitem->p_perminfo;
364 forthree(lc, exprList, icols, icolumns, attnos, attrnos)
365 {
366 Expr *expr = (Expr *) lfirst(lc);
367 ResTarget *col = lfirst_node(ResTarget, icols);
368 AttrNumber attr_num = (AttrNumber) lfirst_int(attnos);
369 TargetEntry *tle;
370
371 tle = makeTargetEntry(expr,
372 attr_num,
373 col->name,
374 false);
375 action->targetList = lappend(action->targetList, tle);
376
377 perminfo->insertedCols =
380 }
381 }
382 break;
383 case CMD_UPDATE:
384 {
385 pstate->p_is_insert = false;
386 action->targetList =
388 mergeWhenClause->targetList);
389 }
390 break;
391 case CMD_DELETE:
392 break;
393
394 case CMD_NOTHING:
395 action->targetList = NIL;
396 break;
397 default:
398 elog(ERROR, "unknown action in MERGE WHEN clause");
399 }
400
401 mergeActionList = lappend(mergeActionList, action);
402 }
403
404 qry->mergeActionList = mergeActionList;
405
406 qry->hasTargetSRFs = false;
407 qry->hasSubLinks = pstate->p_hasSubLinks;
408
409 assign_query_collations(pstate, qry);
410
411 return qry;
412}
413
414static void
416 bool rel_visible,
417 bool cols_visible)
418{
419 ListCell *lc;
420
421 foreach(lc, namespace)
422 {
424
425 if (nsitem->p_rte == rte)
426 {
427 nsitem->p_rel_visible = rel_visible;
428 nsitem->p_cols_visible = cols_visible;
429 break;
430 }
431 }
432}
int16 AttrNumber
Definition: attnum.h:21
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
unsigned int Index
Definition: c.h:585
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
Assert(PointerIsAligned(start, uint64))
#define stmt
Definition: indent_codes.h:59
List * lappend(List *list, void *datum)
Definition: list.c:339
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:336
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:289
@ CMD_MERGE
Definition: nodes.h:275
@ CMD_INSERT
Definition: nodes.h:273
@ CMD_DELETE
Definition: nodes.h:274
@ CMD_UPDATE
Definition: nodes.h:272
@ CMD_NOTHING
Definition: nodes.h:278
#define makeNode(_type_)
Definition: nodes.h:161
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void transformFromClause(ParseState *pstate, List *frmList)
Definition: parse_clause.c:112
int setTargetTable(ParseState *pstate, RangeVar *relation, bool inh, bool alsoSource, AclMode requiredPerms)
Definition: parse_clause.c:178
void assign_query_collations(ParseState *pstate, Query *query)
List * transformWithClause(ParseState *pstate, WithClause *withClause)
Definition: parse_cte.c:110
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
static void setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause, Index targetRTI, Index sourceRTI)
Definition: parse_merge.c:52
Query * transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
Definition: parse_merge.c:107
static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte, bool rel_visible, bool cols_visible)
Definition: parse_merge.c:415
@ EXPR_KIND_MERGE_WHEN
Definition: parse_node.h:58
@ EXPR_KIND_MERGE_RETURNING
Definition: parse_node.h:65
@ EXPR_KIND_JOIN_ON
Definition: parse_node.h:42
@ EXPR_KIND_VALUES_SINGLE
Definition: parse_node.h:67
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
ParseNamespaceItem * GetNSItemByRangeTablePosn(ParseState *pstate, int varno, int sublevels_up)
List * transformExpressionList(ParseState *pstate, List *exprlist, ParseExprKind exprKind, bool allowDefault)
Definition: parse_target.c:220
List * checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
#define ACL_DELETE
Definition: parsenodes.h:79
uint64 AclMode
Definition: parsenodes.h:74
#define ACL_INSERT
Definition: parsenodes.h:76
#define ACL_NO_RIGHTS
Definition: parsenodes.h:92
#define ACL_UPDATE
Definition: parsenodes.h:78
#define ACL_SELECT
Definition: parsenodes.h:77
List * transformInsertRow(ParseState *pstate, List *exprlist, List *stmtcols, List *icolumns, List *attrnos, bool strip_indirection)
Definition: analyze.c:1137
void transformReturningClause(ParseState *pstate, Query *qry, ReturningClause *returningClause, ParseExprKind exprKind)
Definition: analyze.c:2730
List * transformUpdateTargetList(ParseState *pstate, List *origTlist)
Definition: analyze.c:2615
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define lfirst_int(lc)
Definition: pg_list.h:173
#define list_make1(x1)
Definition: pg_list.h:212
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:563
#define NUM_MERGE_MATCH_KINDS
Definition: primnodes.h:2006
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition: primnodes.h:2003
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition: primnodes.h:2002
@ MERGE_WHEN_MATCHED
Definition: primnodes.h:2001
#define RelationGetRelationName(relation)
Definition: rel.h:550
char * aliasname
Definition: primnodes.h:51
Definition: pg_list.h:54
OverridingKind override
Definition: parsenodes.h:1736
CmdType commandType
Definition: parsenodes.h:1735
MergeMatchKind matchKind
Definition: parsenodes.h:1734
RangeTblEntry * p_rte
Definition: parse_node.h:311
RTEPermissionInfo * p_perminfo
Definition: parse_node.h:313
List * p_ctenamespace
Definition: parse_node.h:222
ParseNamespaceItem * p_target_nsitem
Definition: parse_node.h:226
List * p_namespace
Definition: parse_node.h:219
bool p_is_insert
Definition: parse_node.h:228
bool p_hasModifyingCTE
Definition: parse_node.h:246
List * p_rteperminfos
Definition: parse_node.h:213
Relation p_target_relation
Definition: parse_node.h:225
bool p_hasSubLinks
Definition: parse_node.h:245
List * p_joinlist
Definition: parse_node.h:217
List * p_rtable
Definition: parse_node.h:212
Node * mergeJoinCondition
Definition: parsenodes.h:191
FromExpr * jointree
Definition: parsenodes.h:177
List * cteList
Definition: parsenodes.h:168
List * rtable
Definition: parsenodes.h:170
CmdType commandType
Definition: parsenodes.h:121
List * mergeActionList
Definition: parsenodes.h:180
List * targetList
Definition: parsenodes.h:193
Bitmapset * insertedCols
Definition: parsenodes.h:1308
Form_pg_class rd_rel
Definition: rel.h:111
char * name
Definition: parsenodes.h:528
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27