Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * parse_merge.c
4 : * handle merge-statement in parser
5 : *
6 : * Portions Copyright (c) 1996-2023, 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 "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"
33 :
34 : static void setNamespaceForMergeWhen(ParseState *pstate,
35 : MergeWhenClause *mergeWhenClause,
36 : Index targetRTI,
37 : Index sourceRTI);
38 : static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
39 : bool rel_visible,
40 : bool cols_visible);
41 :
42 : /*
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
48 : * target relations.
49 : *
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.
53 : */
54 : static void
377 alvherre 55 CBC 729 : setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause,
56 : Index targetRTI, Index sourceRTI)
57 : {
58 : RangeTblEntry *targetRelRTE,
59 : *sourceRelRTE;
60 :
61 729 : targetRelRTE = rt_fetch(targetRTI, pstate->p_rtable);
62 729 : sourceRelRTE = rt_fetch(sourceRTI, pstate->p_rtable);
63 :
64 729 : if (mergeWhenClause->matched)
65 : {
66 436 : Assert(mergeWhenClause->commandType == CMD_UPDATE ||
67 : mergeWhenClause->commandType == CMD_DELETE ||
68 : mergeWhenClause->commandType == CMD_NOTHING);
69 :
70 : /* MATCHED actions can see both target and source relations. */
71 436 : setNamespaceVisibilityForRTE(pstate->p_namespace,
72 : targetRelRTE, true, true);
73 436 : setNamespaceVisibilityForRTE(pstate->p_namespace,
74 : sourceRelRTE, true, true);
75 : }
76 : else
77 : {
78 : /*
79 : * NOT MATCHED actions can't see target relation, but they can see
80 : * source relation.
81 : */
82 293 : Assert(mergeWhenClause->commandType == CMD_INSERT ||
83 : mergeWhenClause->commandType == CMD_NOTHING);
84 293 : setNamespaceVisibilityForRTE(pstate->p_namespace,
85 : targetRelRTE, false, false);
86 293 : setNamespaceVisibilityForRTE(pstate->p_namespace,
87 : sourceRelRTE, true, true);
88 : }
89 729 : }
90 :
91 : /*
92 : * transformMergeStmt -
93 : * transforms a MERGE statement
94 : */
95 : Query *
96 504 : transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
97 : {
98 504 : Query *qry = makeNode(Query);
99 : ListCell *l;
100 504 : AclMode targetPerms = ACL_NO_RIGHTS;
101 : bool is_terminal[2];
102 : Index sourceRTI;
103 : List *mergeActionList;
104 : Node *joinExpr;
105 : ParseNamespaceItem *nsitem;
106 :
107 : /* There can't be any outer WITH to worry about */
108 504 : Assert(pstate->p_ctenamespace == NIL);
109 :
110 504 : qry->commandType = CMD_MERGE;
111 504 : qry->hasRecursive = false;
112 :
113 : /* process the WITH clause independently of all else */
114 504 : if (stmt->withClause)
115 : {
116 24 : if (stmt->withClause->recursive)
117 3 : ereport(ERROR,
118 : (errcode(ERRCODE_SYNTAX_ERROR),
119 : errmsg("WITH RECURSIVE is not supported for MERGE statement")));
120 :
121 21 : qry->cteList = transformWithClause(pstate, stmt->withClause);
122 21 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
123 : }
124 :
125 : /*
126 : * Check WHEN clauses for permissions and sanity
127 : */
128 501 : is_terminal[0] = false;
129 501 : is_terminal[1] = false;
130 1260 : foreach(l, stmt->mergeWhenClauses)
131 : {
132 762 : MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l);
133 762 : int when_type = (mergeWhenClause->matched ? 0 : 1);
134 :
135 : /*
136 : * Collect action types so we can check target permissions
137 : */
138 762 : switch (mergeWhenClause->commandType)
139 : {
140 304 : case CMD_INSERT:
141 304 : targetPerms |= ACL_INSERT;
142 304 : break;
143 334 : case CMD_UPDATE:
144 334 : targetPerms |= ACL_UPDATE;
145 334 : break;
146 109 : case CMD_DELETE:
147 109 : targetPerms |= ACL_DELETE;
148 109 : break;
149 15 : case CMD_NOTHING:
150 15 : break;
377 alvherre 151 UBC 0 : default:
152 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
153 : }
154 :
155 : /*
156 : * Check for unreachable WHEN clauses
157 : */
89 dean.a.rasheed 158 CBC 762 : if (is_terminal[when_type])
377 alvherre 159 3 : ereport(ERROR,
160 : (errcode(ERRCODE_SYNTAX_ERROR),
161 : errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
89 dean.a.rasheed 162 759 : if (mergeWhenClause->condition == NULL)
163 556 : is_terminal[when_type] = true;
164 : }
165 :
166 : /*
167 : * Set up the MERGE target table. The target table is added to the
168 : * namespace below and to joinlist in transform_MERGE_to_join, so don't
169 : * do it here.
170 : */
377 alvherre 171 996 : qry->resultRelation = setTargetTable(pstate, stmt->relation,
172 498 : stmt->relation->inh,
173 : false, targetPerms);
174 :
175 : /*
176 : * MERGE is unsupported in various cases
177 : */
178 498 : if (pstate->p_target_relation->rd_rel->relkind != RELKIND_RELATION &&
179 56 : pstate->p_target_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
180 6 : ereport(ERROR,
181 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
182 : errmsg("cannot execute MERGE on relation \"%s\"",
183 : RelationGetRelationName(pstate->p_target_relation)),
184 : errdetail_relkind_not_supported(pstate->p_target_relation->rd_rel->relkind)));
135 dean.a.rasheed 185 492 : if (pstate->p_target_relation->rd_rules != NULL &&
186 3 : pstate->p_target_relation->rd_rules->numLocks > 0)
377 alvherre 187 3 : ereport(ERROR,
188 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
189 : errmsg("cannot execute MERGE on relation \"%s\"",
190 : RelationGetRelationName(pstate->p_target_relation)),
191 : errdetail("MERGE is not supported for relations with rules.")));
192 :
193 : /* Now transform the source relation to produce the source RTE. */
194 489 : transformFromClause(pstate,
195 489 : list_make1(stmt->sourceRelation));
196 486 : sourceRTI = list_length(pstate->p_rtable);
197 486 : nsitem = GetNSItemByRangeTablePosn(pstate, sourceRTI, 0);
198 :
199 : /*
200 : * Check that the target table doesn't conflict with the source table.
201 : * This would typically be a checkNameSpaceConflicts call, but we want a
202 : * more specific error message.
203 : */
204 486 : if (strcmp(pstate->p_target_nsitem->p_names->aliasname,
205 486 : nsitem->p_names->aliasname) == 0)
206 3 : ereport(ERROR,
207 : errcode(ERRCODE_DUPLICATE_ALIAS),
208 : errmsg("name \"%s\" specified more than once",
209 : pstate->p_target_nsitem->p_names->aliasname),
210 : errdetail("The name is used both as MERGE target table and data source."));
211 :
212 : /*
213 : * There's no need for a targetlist here; it'll be set up by
214 : * preprocess_targetlist later.
215 : */
362 216 483 : qry->targetList = NIL;
377 217 483 : qry->rtable = pstate->p_rtable;
124 alvherre 218 GNC 483 : qry->rteperminfos = pstate->p_rteperminfos;
377 alvherre 219 ECB :
220 : /*
221 : * Transform the join condition. This includes references to the target
222 : * side, so add that to the namespace.
223 : */
377 alvherre 224 GIC 483 : addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true);
377 alvherre 225 CBC 483 : joinExpr = transformExpr(pstate, stmt->joinCondition,
377 alvherre 226 ECB : EXPR_KIND_JOIN_ON);
227 :
228 : /*
229 : * Create the temporary query's jointree using the joinlist we built using
230 : * just the source relation; the target relation is not included. The
231 : * quals we use are the join conditions to the merge target. The join
232 : * will be constructed fully by transform_MERGE_to_join.
233 : */
377 alvherre 234 GIC 483 : qry->jointree = makeFromExpr(pstate->p_joinlist, joinExpr);
377 alvherre 235 ECB :
236 : /*
237 : * We now have a good query shape, so now look at the WHEN conditions and
238 : * action targetlists.
239 : *
240 : * Overall, the MERGE Query's targetlist is NIL.
241 : *
242 : * Each individual action has its own targetlist that needs separate
243 : * transformation. These transforms don't do anything to the overall
244 : * targetlist, since that is only used for resjunk columns.
245 : *
246 : * We can reference any column in Target or Source, which is OK because
247 : * both of those already have RTEs. There is nothing like the EXCLUDED
248 : * pseudo-relation for INSERT ON CONFLICT.
249 : */
377 alvherre 250 GIC 483 : mergeActionList = NIL;
377 alvherre 251 CBC 1197 : foreach(l, stmt->mergeWhenClauses)
377 alvherre 252 ECB : {
377 alvherre 253 GIC 729 : MergeWhenClause *mergeWhenClause = lfirst_node(MergeWhenClause, l);
377 alvherre 254 ECB : MergeAction *action;
255 :
377 alvherre 256 GIC 729 : action = makeNode(MergeAction);
377 alvherre 257 CBC 729 : action->commandType = mergeWhenClause->commandType;
258 729 : action->matched = mergeWhenClause->matched;
377 alvherre 259 ECB :
260 : /* Use an outer join if any INSERT actions exist in the command. */
377 alvherre 261 GIC 729 : if (action->commandType == CMD_INSERT)
377 alvherre 262 CBC 289 : qry->mergeUseOuterJoin = true;
377 alvherre 263 ECB :
264 : /*
265 : * Set namespace for the specific action. This must be done before
266 : * analyzing the WHEN quals and the action targetlist.
267 : */
377 alvherre 268 GIC 729 : setNamespaceForMergeWhen(pstate, mergeWhenClause,
377 alvherre 269 CBC 729 : qry->resultRelation,
377 alvherre 270 ECB : sourceRTI);
271 :
272 : /*
273 : * Transform the WHEN condition.
274 : *
275 : * Note that these quals are NOT added to the join quals; instead they
276 : * are evaluated separately during execution to decide which of the
277 : * WHEN MATCHED or WHEN NOT MATCHED actions to execute.
278 : */
377 alvherre 279 GIC 729 : action->qual = transformWhereClause(pstate, mergeWhenClause->condition,
377 alvherre 280 ECB : EXPR_KIND_MERGE_WHEN, "WHEN");
281 :
282 : /*
283 : * Transform target lists for each INSERT and UPDATE action stmt
284 : */
377 alvherre 285 GIC 723 : switch (action->commandType)
377 alvherre 286 ECB : {
377 alvherre 287 GIC 286 : case CMD_INSERT:
377 alvherre 288 ECB : {
377 alvherre 289 GIC 286 : List *exprList = NIL;
377 alvherre 290 ECB : ListCell *lc;
291 : RTEPermissionInfo *perminfo;
292 : ListCell *icols;
293 : ListCell *attnos;
294 : List *icolumns;
295 : List *attrnos;
296 :
377 alvherre 297 GIC 286 : pstate->p_is_insert = true;
377 alvherre 298 ECB :
377 alvherre 299 GIC 286 : icolumns = checkInsertTargets(pstate,
377 alvherre 300 ECB : mergeWhenClause->targetList,
301 : &attrnos);
377 alvherre 302 GIC 286 : Assert(list_length(icolumns) == list_length(attrnos));
377 alvherre 303 ECB :
377 alvherre 304 GIC 286 : action->override = mergeWhenClause->override;
377 alvherre 305 ECB :
306 : /*
307 : * Handle INSERT much like in transformInsertStmt
308 : */
377 alvherre 309 GIC 286 : if (mergeWhenClause->values == NIL)
377 alvherre 310 ECB : {
311 : /*
312 : * We have INSERT ... DEFAULT VALUES. We can handle
313 : * this case by emitting an empty targetlist --- all
314 : * columns will be defaulted when the planner expands
315 : * the targetlist.
316 : */
377 alvherre 317 GIC 9 : exprList = NIL;
377 alvherre 318 ECB : }
319 : else
320 : {
321 : /*
322 : * Process INSERT ... VALUES with a single VALUES
323 : * sublist. We treat this case separately for
324 : * efficiency. The sublist is just computed directly
325 : * as the Query's targetlist, with no VALUES RTE. So
326 : * it works just like a SELECT without any FROM.
327 : */
328 :
329 : /*
330 : * Do basic expression transformation (same as a ROW()
331 : * expr, but allow SetToDefault at top level)
332 : */
377 alvherre 333 GIC 277 : exprList = transformExpressionList(pstate,
377 alvherre 334 ECB : mergeWhenClause->values,
335 : EXPR_KIND_VALUES_SINGLE,
336 : true);
337 :
338 : /* Prepare row for assignment to target table */
377 alvherre 339 GIC 271 : exprList = transformInsertRow(pstate, exprList,
377 alvherre 340 ECB : mergeWhenClause->targetList,
341 : icolumns, attrnos,
342 : false);
343 : }
344 :
345 : /*
346 : * Generate action's target list using the computed list
347 : * of expressions. Also, mark all the target columns as
348 : * needing insert permissions.
349 : */
124 alvherre 350 GNC 280 : perminfo = pstate->p_target_nsitem->p_perminfo;
377 alvherre 351 CBC 888 : forthree(lc, exprList, icols, icolumns, attnos, attrnos)
377 alvherre 352 ECB : {
377 alvherre 353 GIC 608 : Expr *expr = (Expr *) lfirst(lc);
377 alvherre 354 CBC 608 : ResTarget *col = lfirst_node(ResTarget, icols);
355 608 : AttrNumber attr_num = (AttrNumber) lfirst_int(attnos);
377 alvherre 356 ECB : TargetEntry *tle;
357 :
377 alvherre 358 GIC 608 : tle = makeTargetEntry(expr,
377 alvherre 359 ECB : attr_num,
360 : col->name,
361 : false);
377 alvherre 362 GIC 608 : action->targetList = lappend(action->targetList, tle);
377 alvherre 363 ECB :
124 alvherre 364 GNC 608 : perminfo->insertedCols =
365 608 : bms_add_member(perminfo->insertedCols,
377 alvherre 366 ECB : attr_num - FirstLowInvalidHeapAttributeNumber);
367 : }
368 : }
377 alvherre 369 GIC 280 : break;
377 alvherre 370 CBC 325 : case CMD_UPDATE:
377 alvherre 371 ECB : {
377 alvherre 372 GIC 325 : pstate->p_is_insert = false;
377 alvherre 373 CBC 322 : action->targetList =
374 325 : transformUpdateTargetList(pstate,
377 alvherre 375 ECB : mergeWhenClause->targetList);
376 : }
377 alvherre 377 GIC 322 : break;
377 alvherre 378 CBC 100 : case CMD_DELETE:
379 100 : break;
377 alvherre 380 ECB :
377 alvherre 381 GIC 12 : case CMD_NOTHING:
377 alvherre 382 CBC 12 : action->targetList = NIL;
383 12 : break;
377 alvherre 384 LBC 0 : default:
377 alvherre 385 UBC 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
377 alvherre 386 EUB : }
387 :
377 alvherre 388 GIC 714 : mergeActionList = lappend(mergeActionList, action);
377 alvherre 389 ECB : }
390 :
377 alvherre 391 GIC 468 : qry->mergeActionList = mergeActionList;
377 alvherre 392 ECB :
393 : /* RETURNING could potentially be added in the future, but not in SQL std */
377 alvherre 394 GIC 468 : qry->returningList = NULL;
377 alvherre 395 ECB :
377 alvherre 396 GIC 468 : qry->hasTargetSRFs = false;
377 alvherre 397 CBC 468 : qry->hasSubLinks = pstate->p_hasSubLinks;
377 alvherre 398 ECB :
377 alvherre 399 GIC 468 : assign_query_collations(pstate, qry);
377 alvherre 400 ECB :
377 alvherre 401 GIC 468 : return qry;
377 alvherre 402 ECB : }
403 :
404 : static void
377 alvherre 405 GIC 1458 : setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
377 alvherre 406 ECB : bool rel_visible,
407 : bool cols_visible)
408 : {
409 : ListCell *lc;
410 :
377 alvherre 411 GIC 2283 : foreach(lc, namespace)
377 alvherre 412 ECB : {
377 alvherre 413 GIC 2283 : ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
377 alvherre 414 ECB :
377 alvherre 415 GIC 2283 : if (nsitem->p_rte == rte)
377 alvherre 416 ECB : {
377 alvherre 417 GIC 1458 : nsitem->p_rel_visible = rel_visible;
377 alvherre 418 CBC 1458 : nsitem->p_cols_visible = cols_visible;
419 1458 : break;
377 alvherre 420 ECB : }
421 : }
377 alvherre 422 GIC 1458 : }
|