Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * parse_merge.c
4 : * handle merge-statement in parser
5 : *
6 : * Portions Copyright (c) 1996-2024, 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"
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"
30 :
31 : static void setNamespaceForMergeWhen(ParseState *pstate,
32 : MergeWhenClause *mergeWhenClause,
33 : Index targetRTI,
34 : Index sourceRTI);
35 : static 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 : */
51 : static void
52 2928 : setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause,
53 : Index targetRTI, Index sourceRTI)
54 : {
55 : RangeTblEntry *targetRelRTE,
56 : *sourceRelRTE;
57 :
58 2928 : targetRelRTE = rt_fetch(targetRTI, pstate->p_rtable);
59 2928 : sourceRelRTE = rt_fetch(sourceRTI, pstate->p_rtable);
60 :
61 2928 : 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. */
68 1814 : setNamespaceVisibilityForRTE(pstate->p_namespace,
69 : targetRelRTE, true, true);
70 1814 : setNamespaceVisibilityForRTE(pstate->p_namespace,
71 : sourceRelRTE, true, true);
72 : }
73 1114 : 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);
82 144 : setNamespaceVisibilityForRTE(pstate->p_namespace,
83 : targetRelRTE, true, true);
84 144 : setNamespaceVisibilityForRTE(pstate->p_namespace,
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);
95 970 : setNamespaceVisibilityForRTE(pstate->p_namespace,
96 : targetRelRTE, false, false);
97 970 : setNamespaceVisibilityForRTE(pstate->p_namespace,
98 : sourceRelRTE, true, true);
99 : }
100 2928 : }
101 :
102 : /*
103 : * transformMergeStmt -
104 : * transforms a MERGE statement
105 : */
106 : Query *
107 1914 : transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
108 : {
109 1914 : Query *qry = makeNode(Query);
110 : ListCell *l;
111 1914 : 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 1914 : qry->commandType = CMD_MERGE;
121 1914 : qry->hasRecursive = false;
122 :
123 : /* process the WITH clause independently of all else */
124 1914 : if (stmt->withClause)
125 : {
126 52 : if (stmt->withClause->recursive)
127 6 : ereport(ERROR,
128 : (errcode(ERRCODE_SYNTAX_ERROR),
129 : errmsg("WITH RECURSIVE is not supported for MERGE statement")));
130 :
131 46 : qry->cteList = transformWithClause(pstate, stmt->withClause);
132 46 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
133 : }
134 :
135 : /*
136 : * Check WHEN clauses for permissions and sanity
137 : */
138 1908 : is_terminal[MERGE_WHEN_MATCHED] = false;
139 1908 : is_terminal[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] = false;
140 1908 : is_terminal[MERGE_WHEN_NOT_MATCHED_BY_TARGET] = false;
141 4872 : foreach(l, stmt->mergeWhenClauses)
142 : {
143 2970 : 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 2970 : switch (mergeWhenClause->commandType)
153 : {
154 968 : case CMD_INSERT:
155 968 : targetPerms |= ACL_INSERT;
156 968 : break;
157 1454 : case CMD_UPDATE:
158 1454 : targetPerms |= ACL_UPDATE;
159 1454 : break;
160 470 : case CMD_DELETE:
161 470 : targetPerms |= ACL_DELETE;
162 470 : break;
163 78 : case CMD_NOTHING:
164 78 : targetPerms |= ACL_SELECT;
165 78 : break;
166 0 : default:
167 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
168 : }
169 :
170 : /*
171 : * Check for unreachable WHEN clauses
172 : */
173 2970 : if (is_terminal[mergeWhenClause->matchKind])
174 6 : ereport(ERROR,
175 : (errcode(ERRCODE_SYNTAX_ERROR),
176 : errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
177 2964 : if (mergeWhenClause->condition == NULL)
178 2180 : 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 3804 : qry->resultRelation = setTargetTable(pstate, stmt->relation,
193 1902 : stmt->relation->inh,
194 : false, targetPerms);
195 1902 : qry->mergeTargetRelation = qry->resultRelation;
196 :
197 : /* The target relation must be a table or a view */
198 1902 : if (pstate->p_target_relation->rd_rel->relkind != RELKIND_RELATION &&
199 786 : pstate->p_target_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
200 654 : pstate->p_target_relation->rd_rel->relkind != RELKIND_VIEW)
201 6 : ereport(ERROR,
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)));
206 :
207 : /* Now transform the source relation to produce the source RTE. */
208 1896 : transformFromClause(pstate,
209 1896 : list_make1(stmt->sourceRelation));
210 1890 : sourceRTI = list_length(pstate->p_rtable);
211 1890 : 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 1890 : if (strcmp(pstate->p_target_nsitem->p_names->aliasname,
219 1890 : nsitem->p_names->aliasname) == 0)
220 6 : ereport(ERROR,
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."));
225 :
226 : /*
227 : * There's no need for a targetlist here; it'll be set up by
228 : * preprocess_targetlist later.
229 : */
230 1884 : qry->targetList = NIL;
231 1884 : qry->rtable = pstate->p_rtable;
232 1884 : 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 1884 : addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true);
239 1884 : qry->mergeJoinCondition = transformExpr(pstate, stmt->joinCondition,
240 : EXPR_KIND_JOIN_ON);
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 1884 : qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
248 :
249 : /* Transform the RETURNING list, if any */
250 1884 : qry->returningList = transformReturningList(pstate, stmt->returningList,
251 : EXPR_KIND_MERGE_RETURNING);
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 1884 : mergeActionList = NIL;
268 4776 : foreach(l, stmt->mergeWhenClauses)
269 : {
270 2928 : MergeWhenClause *mergeWhenClause = lfirst_node(MergeWhenClause, l);
271 : MergeAction *action;
272 :
273 2928 : action = makeNode(MergeAction);
274 2928 : action->commandType = mergeWhenClause->commandType;
275 2928 : 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 2928 : setNamespaceForMergeWhen(pstate, mergeWhenClause,
282 2928 : 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 2928 : 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 2910 : switch (action->commandType)
299 : {
300 944 : case CMD_INSERT:
301 : {
302 944 : List *exprList = NIL;
303 : ListCell *lc;
304 : RTEPermissionInfo *perminfo;
305 : ListCell *icols;
306 : ListCell *attnos;
307 : List *icolumns;
308 : List *attrnos;
309 :
310 944 : pstate->p_is_insert = true;
311 :
312 944 : icolumns = checkInsertTargets(pstate,
313 : mergeWhenClause->targetList,
314 : &attrnos);
315 : Assert(list_length(icolumns) == list_length(attrnos));
316 :
317 944 : action->override = mergeWhenClause->override;
318 :
319 : /*
320 : * Handle INSERT much like in transformInsertStmt
321 : */
322 944 : 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 24 : 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 920 : exprList = transformExpressionList(pstate,
347 : mergeWhenClause->values,
348 : EXPR_KIND_VALUES_SINGLE,
349 : true);
350 :
351 : /* Prepare row for assignment to target table */
352 908 : 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 932 : perminfo = pstate->p_target_nsitem->p_perminfo;
364 2912 : forthree(lc, exprList, icols, icolumns, attnos, attrnos)
365 : {
366 1980 : Expr *expr = (Expr *) lfirst(lc);
367 1980 : ResTarget *col = lfirst_node(ResTarget, icols);
368 1980 : AttrNumber attr_num = (AttrNumber) lfirst_int(attnos);
369 : TargetEntry *tle;
370 :
371 1980 : tle = makeTargetEntry(expr,
372 : attr_num,
373 : col->name,
374 : false);
375 1980 : action->targetList = lappend(action->targetList, tle);
376 :
377 1980 : perminfo->insertedCols =
378 1980 : bms_add_member(perminfo->insertedCols,
379 : attr_num - FirstLowInvalidHeapAttributeNumber);
380 : }
381 : }
382 932 : break;
383 1442 : case CMD_UPDATE:
384 : {
385 1442 : pstate->p_is_insert = false;
386 1436 : action->targetList =
387 1442 : transformUpdateTargetList(pstate,
388 : mergeWhenClause->targetList);
389 : }
390 1436 : break;
391 452 : case CMD_DELETE:
392 452 : break;
393 :
394 72 : case CMD_NOTHING:
395 72 : action->targetList = NIL;
396 72 : break;
397 0 : default:
398 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
399 : }
400 :
401 2892 : mergeActionList = lappend(mergeActionList, action);
402 : }
403 :
404 1848 : qry->mergeActionList = mergeActionList;
405 :
406 1848 : qry->hasTargetSRFs = false;
407 1848 : qry->hasSubLinks = pstate->p_hasSubLinks;
408 :
409 1848 : assign_query_collations(pstate, qry);
410 :
411 1848 : return qry;
412 : }
413 :
414 : static void
415 5856 : setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
416 : bool rel_visible,
417 : bool cols_visible)
418 : {
419 : ListCell *lc;
420 :
421 8976 : foreach(lc, namespace)
422 : {
423 8976 : ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
424 :
425 8976 : if (nsitem->p_rte == rte)
426 : {
427 5856 : nsitem->p_rel_visible = rel_visible;
428 5856 : nsitem->p_cols_visible = cols_visible;
429 5856 : break;
430 : }
431 : }
432 5856 : }
|