1 /*-------------------------------------------------------------------------
4 * handle merge-statement in parser
6 * Portions Copyright (c) 1996-2024, 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 "miscadmin.h"
20 #include "nodes/makefuncs.h"
21 #include "parser/analyze.h"
22 #include "parser/parse_collate.h"
23 #include "parser/parsetree.h"
24 #include "parser/parser.h"
25 #include "parser/parse_clause.h"
26 #include "parser/parse_cte.h"
27 #include "parser/parse_expr.h"
28 #include "parser/parse_merge.h"
29 #include "parser/parse_relation.h"
30 #include "parser/parse_target.h"
31 #include "utils/rel.h"
32 #include "utils/relcache.h"
34 static void setNamespaceForMergeWhen(ParseState
*pstate
,
35 MergeWhenClause
*mergeWhenClause
,
38 static void setNamespaceVisibilityForRTE(List
*namespace, RangeTblEntry
*rte
,
43 * Make appropriate changes to the namespace visibility while transforming
44 * individual action's quals and targetlist expressions. In particular, for
45 * INSERT actions we must only see the source relation (since INSERT action is
46 * invoked for NOT MATCHED tuples and hence there is no target tuple to deal
47 * with). On the other hand, UPDATE and DELETE actions can see both source and
50 * Also, since the internal join node can hide the source and target
51 * relations, we must explicitly make the respective relation as visible so
52 * that columns can be referenced unqualified from these relations.
55 setNamespaceForMergeWhen(ParseState
*pstate
, MergeWhenClause
*mergeWhenClause
,
56 Index targetRTI
, Index sourceRTI
)
58 RangeTblEntry
*targetRelRTE
,
61 targetRelRTE
= rt_fetch(targetRTI
, pstate
->p_rtable
);
62 sourceRelRTE
= rt_fetch(sourceRTI
, pstate
->p_rtable
);
64 if (mergeWhenClause
->matched
)
66 Assert(mergeWhenClause
->commandType
== CMD_UPDATE
||
67 mergeWhenClause
->commandType
== CMD_DELETE
||
68 mergeWhenClause
->commandType
== CMD_NOTHING
);
70 /* MATCHED actions can see both target and source relations. */
71 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
72 targetRelRTE
, true, true);
73 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
74 sourceRelRTE
, true, true);
79 * NOT MATCHED actions can't see target relation, but they can see
82 Assert(mergeWhenClause
->commandType
== CMD_INSERT
||
83 mergeWhenClause
->commandType
== CMD_NOTHING
);
84 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
85 targetRelRTE
, false, false);
86 setNamespaceVisibilityForRTE(pstate
->p_namespace
,
87 sourceRelRTE
, true, true);
92 * transformMergeStmt -
93 * transforms a MERGE statement
96 transformMergeStmt(ParseState
*pstate
, MergeStmt
*stmt
)
98 Query
*qry
= makeNode(Query
);
100 AclMode targetPerms
= ACL_NO_RIGHTS
;
103 List
*mergeActionList
;
105 ParseNamespaceItem
*nsitem
;
107 /* There can't be any outer WITH to worry about */
108 Assert(pstate
->p_ctenamespace
== NIL
);
110 qry
->commandType
= CMD_MERGE
;
111 qry
->hasRecursive
= false;
113 /* process the WITH clause independently of all else */
114 if (stmt
->withClause
)
116 if (stmt
->withClause
->recursive
)
118 (errcode(ERRCODE_SYNTAX_ERROR
),
119 errmsg("WITH RECURSIVE is not supported for MERGE statement")));
121 qry
->cteList
= transformWithClause(pstate
, stmt
->withClause
);
122 qry
->hasModifyingCTE
= pstate
->p_hasModifyingCTE
;
126 * Check WHEN clauses for permissions and sanity
128 is_terminal
[0] = false;
129 is_terminal
[1] = false;
130 foreach(l
, stmt
->mergeWhenClauses
)
132 MergeWhenClause
*mergeWhenClause
= (MergeWhenClause
*) lfirst(l
);
133 int when_type
= (mergeWhenClause
->matched
? 0 : 1);
136 * Collect permissions to check, according to action types. We require
137 * SELECT privileges for DO NOTHING because it'd be irregular to have
138 * a target relation with zero privileges checked, in case DO NOTHING
139 * is the only action. There's no damage from that: any meaningful
140 * MERGE command requires at least some access to the table anyway.
142 switch (mergeWhenClause
->commandType
)
145 targetPerms
|= ACL_INSERT
;
148 targetPerms
|= ACL_UPDATE
;
151 targetPerms
|= ACL_DELETE
;
154 targetPerms
|= ACL_SELECT
;
157 elog(ERROR
, "unknown action in MERGE WHEN clause");
161 * Check for unreachable WHEN clauses
163 if (is_terminal
[when_type
])
165 (errcode(ERRCODE_SYNTAX_ERROR
),
166 errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
167 if (mergeWhenClause
->condition
== NULL
)
168 is_terminal
[when_type
] = true;
172 * Set up the MERGE target table. The target table is added to the
173 * namespace below and to joinlist in transform_MERGE_to_join, so don't do
176 qry
->resultRelation
= setTargetTable(pstate
, stmt
->relation
,
181 * MERGE is unsupported in various cases
183 if (pstate
->p_target_relation
->rd_rel
->relkind
!= RELKIND_RELATION
&&
184 pstate
->p_target_relation
->rd_rel
->relkind
!= RELKIND_PARTITIONED_TABLE
)
186 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
187 errmsg("cannot execute MERGE on relation \"%s\"",
188 RelationGetRelationName(pstate
->p_target_relation
)),
189 errdetail_relkind_not_supported(pstate
->p_target_relation
->rd_rel
->relkind
)));
190 if (pstate
->p_target_relation
->rd_rules
!= NULL
&&
191 pstate
->p_target_relation
->rd_rules
->numLocks
> 0)
193 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
194 errmsg("cannot execute MERGE on relation \"%s\"",
195 RelationGetRelationName(pstate
->p_target_relation
)),
196 errdetail("MERGE is not supported for relations with rules.")));
198 /* Now transform the source relation to produce the source RTE. */
199 transformFromClause(pstate
,
200 list_make1(stmt
->sourceRelation
));
201 sourceRTI
= list_length(pstate
->p_rtable
);
202 nsitem
= GetNSItemByRangeTablePosn(pstate
, sourceRTI
, 0);
205 * Check that the target table doesn't conflict with the source table.
206 * This would typically be a checkNameSpaceConflicts call, but we want a
207 * more specific error message.
209 if (strcmp(pstate
->p_target_nsitem
->p_names
->aliasname
,
210 nsitem
->p_names
->aliasname
) == 0)
212 errcode(ERRCODE_DUPLICATE_ALIAS
),
213 errmsg("name \"%s\" specified more than once",
214 pstate
->p_target_nsitem
->p_names
->aliasname
),
215 errdetail("The name is used both as MERGE target table and data source."));
218 * There's no need for a targetlist here; it'll be set up by
219 * preprocess_targetlist later.
221 qry
->targetList
= NIL
;
222 qry
->rtable
= pstate
->p_rtable
;
223 qry
->rteperminfos
= pstate
->p_rteperminfos
;
226 * Transform the join condition. This includes references to the target
227 * side, so add that to the namespace.
229 addNSItemToQuery(pstate
, pstate
->p_target_nsitem
, false, true, true);
230 joinExpr
= transformExpr(pstate
, stmt
->joinCondition
,
234 * Create the temporary query's jointree using the joinlist we built using
235 * just the source relation; the target relation is not included. The
236 * quals we use are the join conditions to the merge target. The join
237 * will be constructed fully by transform_MERGE_to_join.
239 qry
->jointree
= makeFromExpr(pstate
->p_joinlist
, joinExpr
);
242 * We now have a good query shape, so now look at the WHEN conditions and
243 * action targetlists.
245 * Overall, the MERGE Query's targetlist is NIL.
247 * Each individual action has its own targetlist that needs separate
248 * transformation. These transforms don't do anything to the overall
249 * targetlist, since that is only used for resjunk columns.
251 * We can reference any column in Target or Source, which is OK because
252 * both of those already have RTEs. There is nothing like the EXCLUDED
253 * pseudo-relation for INSERT ON CONFLICT.
255 mergeActionList
= NIL
;
256 foreach(l
, stmt
->mergeWhenClauses
)
258 MergeWhenClause
*mergeWhenClause
= lfirst_node(MergeWhenClause
, l
);
261 action
= makeNode(MergeAction
);
262 action
->commandType
= mergeWhenClause
->commandType
;
263 action
->matched
= mergeWhenClause
->matched
;
265 /* Use an outer join if any INSERT actions exist in the command. */
266 if (action
->commandType
== CMD_INSERT
)
267 qry
->mergeUseOuterJoin
= true;
270 * Set namespace for the specific action. This must be done before
271 * analyzing the WHEN quals and the action targetlist.
273 setNamespaceForMergeWhen(pstate
, mergeWhenClause
,
278 * Transform the WHEN condition.
280 * Note that these quals are NOT added to the join quals; instead they
281 * are evaluated separately during execution to decide which of the
282 * WHEN MATCHED or WHEN NOT MATCHED actions to execute.
284 action
->qual
= transformWhereClause(pstate
, mergeWhenClause
->condition
,
285 EXPR_KIND_MERGE_WHEN
, "WHEN");
288 * Transform target lists for each INSERT and UPDATE action stmt
290 switch (action
->commandType
)
294 List
*exprList
= NIL
;
296 RTEPermissionInfo
*perminfo
;
302 pstate
->p_is_insert
= true;
304 icolumns
= checkInsertTargets(pstate
,
305 mergeWhenClause
->targetList
,
307 Assert(list_length(icolumns
) == list_length(attrnos
));
309 action
->override
= mergeWhenClause
->override
;
312 * Handle INSERT much like in transformInsertStmt
314 if (mergeWhenClause
->values
== NIL
)
317 * We have INSERT ... DEFAULT VALUES. We can handle
318 * this case by emitting an empty targetlist --- all
319 * columns will be defaulted when the planner expands
327 * Process INSERT ... VALUES with a single VALUES
328 * sublist. We treat this case separately for
329 * efficiency. The sublist is just computed directly
330 * as the Query's targetlist, with no VALUES RTE. So
331 * it works just like a SELECT without any FROM.
335 * Do basic expression transformation (same as a ROW()
336 * expr, but allow SetToDefault at top level)
338 exprList
= transformExpressionList(pstate
,
339 mergeWhenClause
->values
,
340 EXPR_KIND_VALUES_SINGLE
,
343 /* Prepare row for assignment to target table */
344 exprList
= transformInsertRow(pstate
, exprList
,
345 mergeWhenClause
->targetList
,
351 * Generate action's target list using the computed list
352 * of expressions. Also, mark all the target columns as
353 * needing insert permissions.
355 perminfo
= pstate
->p_target_nsitem
->p_perminfo
;
356 forthree(lc
, exprList
, icols
, icolumns
, attnos
, attrnos
)
358 Expr
*expr
= (Expr
*) lfirst(lc
);
359 ResTarget
*col
= lfirst_node(ResTarget
, icols
);
360 AttrNumber attr_num
= (AttrNumber
) lfirst_int(attnos
);
363 tle
= makeTargetEntry(expr
,
367 action
->targetList
= lappend(action
->targetList
, tle
);
369 perminfo
->insertedCols
=
370 bms_add_member(perminfo
->insertedCols
,
371 attr_num
- FirstLowInvalidHeapAttributeNumber
);
377 pstate
->p_is_insert
= false;
379 transformUpdateTargetList(pstate
,
380 mergeWhenClause
->targetList
);
387 action
->targetList
= NIL
;
390 elog(ERROR
, "unknown action in MERGE WHEN clause");
393 mergeActionList
= lappend(mergeActionList
, action
);
396 qry
->mergeActionList
= mergeActionList
;
398 /* RETURNING could potentially be added in the future, but not in SQL std */
399 qry
->returningList
= NULL
;
401 qry
->hasTargetSRFs
= false;
402 qry
->hasSubLinks
= pstate
->p_hasSubLinks
;
404 assign_query_collations(pstate
, qry
);
410 setNamespaceVisibilityForRTE(List
*namespace, RangeTblEntry
*rte
,
416 foreach(lc
, namespace)
418 ParseNamespaceItem
*nsitem
= (ParseNamespaceItem
*) lfirst(lc
);
420 if (nsitem
->p_rte
== rte
)
422 nsitem
->p_rel_visible
= rel_visible
;
423 nsitem
->p_cols_visible
= cols_visible
;