Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeModifyTable.c
4 : * routines to handle ModifyTable nodes.
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/executor/nodeModifyTable.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : /* INTERFACE ROUTINES
16 : * ExecInitModifyTable - initialize the ModifyTable node
17 : * ExecModifyTable - retrieve the next tuple from the node
18 : * ExecEndModifyTable - shut down the ModifyTable node
19 : * ExecReScanModifyTable - rescan the ModifyTable node
20 : *
21 : * NOTES
22 : * The ModifyTable node receives input from its outerPlan, which is
23 : * the data to insert for INSERT cases, the changed columns' new
24 : * values plus row-locating info for UPDATE and MERGE cases, or just the
25 : * row-locating info for DELETE cases.
26 : *
27 : * MERGE runs a join between the source relation and the target
28 : * table; if any WHEN NOT MATCHED clauses are present, then the
29 : * join is an outer join. In this case, any unmatched tuples will
30 : * have NULL row-locating info, and only INSERT can be run. But for
31 : * matched tuples, then row-locating info is used to determine the
32 : * tuple to UPDATE or DELETE. When all clauses are WHEN MATCHED,
33 : * then an inner join is used, so all tuples contain row-locating info.
34 : *
35 : * If the query specifies RETURNING, then the ModifyTable returns a
36 : * RETURNING tuple after completing each row insert, update, or delete.
37 : * It must be called again to continue the operation. Without RETURNING,
38 : * we just loop within the node until all the work is done, then
39 : * return NULL. This avoids useless call/return overhead.
40 : */
41 :
42 : #include "postgres.h"
43 :
44 : #include "access/htup_details.h"
45 : #include "access/tableam.h"
46 : #include "access/xact.h"
47 : #include "commands/trigger.h"
48 : #include "executor/execPartition.h"
49 : #include "executor/executor.h"
50 : #include "executor/nodeModifyTable.h"
51 : #include "foreign/fdwapi.h"
52 : #include "miscadmin.h"
53 : #include "nodes/nodeFuncs.h"
54 : #include "optimizer/optimizer.h"
55 : #include "rewrite/rewriteHandler.h"
56 : #include "storage/lmgr.h"
57 : #include "utils/builtins.h"
58 : #include "utils/datum.h"
59 : #include "utils/rel.h"
60 : #include "utils/snapmgr.h"
61 :
62 :
63 : typedef struct MTTargetRelLookup
64 : {
65 : Oid relationOid; /* hash key, must be first */
66 : int relationIndex; /* rel's index in resultRelInfo[] array */
67 : } MTTargetRelLookup;
68 :
69 : /*
70 : * Context struct for a ModifyTable operation, containing basic execution
71 : * state and some output variables populated by ExecUpdateAct() and
72 : * ExecDeleteAct() to report the result of their actions to callers.
73 : */
74 : typedef struct ModifyTableContext
75 : {
76 : /* Operation state */
77 : ModifyTableState *mtstate;
78 : EPQState *epqstate;
79 : EState *estate;
80 :
81 : /*
82 : * Slot containing tuple obtained from ModifyTable's subplan. Used to
83 : * access "junk" columns that are not going to be stored.
84 : */
85 : TupleTableSlot *planSlot;
86 :
87 : /*
88 : * Information about the changes that were made concurrently to a tuple
89 : * being updated or deleted
90 : */
91 : TM_FailureData tmfd;
92 :
93 : /*
94 : * The tuple projected by the INSERT's RETURNING clause, when doing a
95 : * cross-partition UPDATE
96 : */
97 : TupleTableSlot *cpUpdateReturningSlot;
98 : } ModifyTableContext;
99 :
100 : /*
101 : * Context struct containing output data specific to UPDATE operations.
102 : */
103 : typedef struct UpdateContext
104 : {
105 : bool crossPartUpdate; /* was it a cross-partition update? */
106 : TU_UpdateIndexes updateIndexes; /* Which index updates are required? */
107 :
108 : /*
109 : * Lock mode to acquire on the latest tuple version before performing
110 : * EvalPlanQual on it
111 : */
112 : LockTupleMode lockmode;
113 : } UpdateContext;
114 :
115 :
116 : static void ExecBatchInsert(ModifyTableState *mtstate,
117 : ResultRelInfo *resultRelInfo,
118 : TupleTableSlot **slots,
119 : TupleTableSlot **planSlots,
120 : int numSlots,
121 : EState *estate,
122 : bool canSetTag);
123 : static void ExecPendingInserts(EState *estate);
124 : static void ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
125 : ResultRelInfo *sourcePartInfo,
126 : ResultRelInfo *destPartInfo,
127 : ItemPointer tupleid,
128 : TupleTableSlot *oldslot,
129 : TupleTableSlot *newslot);
130 : static bool ExecOnConflictUpdate(ModifyTableContext *context,
131 : ResultRelInfo *resultRelInfo,
132 : ItemPointer conflictTid,
133 : TupleTableSlot *excludedSlot,
134 : bool canSetTag,
135 : TupleTableSlot **returning);
136 : static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
137 : EState *estate,
138 : PartitionTupleRouting *proute,
139 : ResultRelInfo *targetRelInfo,
140 : TupleTableSlot *slot,
141 : ResultRelInfo **partRelInfo);
142 :
143 : static TupleTableSlot *ExecMerge(ModifyTableContext *context,
144 : ResultRelInfo *resultRelInfo,
145 : ItemPointer tupleid,
146 : HeapTuple oldtuple,
147 : bool canSetTag);
148 : static void ExecInitMerge(ModifyTableState *mtstate, EState *estate);
149 : static TupleTableSlot *ExecMergeMatched(ModifyTableContext *context,
150 : ResultRelInfo *resultRelInfo,
151 : ItemPointer tupleid,
152 : HeapTuple oldtuple,
153 : bool canSetTag,
154 : bool *matched);
155 : static TupleTableSlot *ExecMergeNotMatched(ModifyTableContext *context,
156 : ResultRelInfo *resultRelInfo,
157 : bool canSetTag);
158 :
159 :
160 : /*
161 : * Verify that the tuples to be produced by INSERT match the
162 : * target relation's rowtype
163 : *
164 : * We do this to guard against stale plans. If plan invalidation is
165 : * functioning properly then we should never get a failure here, but better
166 : * safe than sorry. Note that this is called after we have obtained lock
167 : * on the target rel, so the rowtype can't change underneath us.
168 : *
169 : * The plan output is represented by its targetlist, because that makes
170 : * handling the dropped-column case easier.
171 : *
172 : * We used to use this for UPDATE as well, but now the equivalent checks
173 : * are done in ExecBuildUpdateProjection.
174 : */
175 : static void
176 89040 : ExecCheckPlanOutput(Relation resultRel, List *targetList)
177 : {
178 89040 : TupleDesc resultDesc = RelationGetDescr(resultRel);
179 89040 : int attno = 0;
180 : ListCell *lc;
181 :
182 272220 : foreach(lc, targetList)
183 : {
184 183180 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
185 : Form_pg_attribute attr;
186 :
187 : Assert(!tle->resjunk); /* caller removed junk items already */
188 :
189 183180 : if (attno >= resultDesc->natts)
190 0 : ereport(ERROR,
191 : (errcode(ERRCODE_DATATYPE_MISMATCH),
192 : errmsg("table row type and query-specified row type do not match"),
193 : errdetail("Query has too many columns.")));
194 183180 : attr = TupleDescAttr(resultDesc, attno);
195 183180 : attno++;
196 :
197 183180 : if (!attr->attisdropped)
198 : {
199 : /* Normal case: demand type match */
200 182570 : if (exprType((Node *) tle->expr) != attr->atttypid)
201 0 : ereport(ERROR,
202 : (errcode(ERRCODE_DATATYPE_MISMATCH),
203 : errmsg("table row type and query-specified row type do not match"),
204 : errdetail("Table has type %s at ordinal position %d, but query expects %s.",
205 : format_type_be(attr->atttypid),
206 : attno,
207 : format_type_be(exprType((Node *) tle->expr)))));
208 : }
209 : else
210 : {
211 : /*
212 : * For a dropped column, we can't check atttypid (it's likely 0).
213 : * In any case the planner has most likely inserted an INT4 null.
214 : * What we insist on is just *some* NULL constant.
215 : */
216 610 : if (!IsA(tle->expr, Const) ||
217 610 : !((Const *) tle->expr)->constisnull)
218 0 : ereport(ERROR,
219 : (errcode(ERRCODE_DATATYPE_MISMATCH),
220 : errmsg("table row type and query-specified row type do not match"),
221 : errdetail("Query provides a value for a dropped column at ordinal position %d.",
222 : attno)));
223 : }
224 : }
225 89040 : if (attno != resultDesc->natts)
226 0 : ereport(ERROR,
227 : (errcode(ERRCODE_DATATYPE_MISMATCH),
228 : errmsg("table row type and query-specified row type do not match"),
229 : errdetail("Query has too few columns.")));
230 89040 : }
231 :
232 : /*
233 : * ExecProcessReturning --- evaluate a RETURNING list
234 : *
235 : * resultRelInfo: current result rel
236 : * tupleSlot: slot holding tuple actually inserted/updated/deleted
237 : * planSlot: slot holding tuple returned by top subplan node
238 : *
239 : * Note: If tupleSlot is NULL, the FDW should have already provided econtext's
240 : * scan tuple.
241 : *
242 : * Returns a slot holding the result tuple
243 : */
244 : static TupleTableSlot *
245 7292 : ExecProcessReturning(ResultRelInfo *resultRelInfo,
246 : TupleTableSlot *tupleSlot,
247 : TupleTableSlot *planSlot)
248 : {
249 7292 : ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
250 7292 : ExprContext *econtext = projectReturning->pi_exprContext;
251 :
252 : /* Make tuple and any needed join variables available to ExecProject */
253 7292 : if (tupleSlot)
254 6598 : econtext->ecxt_scantuple = tupleSlot;
255 7292 : econtext->ecxt_outertuple = planSlot;
256 :
257 : /*
258 : * RETURNING expressions might reference the tableoid column, so
259 : * reinitialize tts_tableOid before evaluating them.
260 : */
261 7292 : econtext->ecxt_scantuple->tts_tableOid =
262 7292 : RelationGetRelid(resultRelInfo->ri_RelationDesc);
263 :
264 : /* Compute the RETURNING expressions */
265 7292 : return ExecProject(projectReturning);
266 : }
267 :
268 : /*
269 : * ExecCheckTupleVisible -- verify tuple is visible
270 : *
271 : * It would not be consistent with guarantees of the higher isolation levels to
272 : * proceed with avoiding insertion (taking speculative insertion's alternative
273 : * path) on the basis of another tuple that is not visible to MVCC snapshot.
274 : * Check for the need to raise a serialization failure, and do so as necessary.
275 : */
276 : static void
277 5240 : ExecCheckTupleVisible(EState *estate,
278 : Relation rel,
279 : TupleTableSlot *slot)
280 : {
281 5240 : if (!IsolationUsesXactSnapshot())
282 5176 : return;
283 :
284 64 : if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
285 : {
286 : Datum xminDatum;
287 : TransactionId xmin;
288 : bool isnull;
289 :
290 40 : xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
291 : Assert(!isnull);
292 40 : xmin = DatumGetTransactionId(xminDatum);
293 :
294 : /*
295 : * We should not raise a serialization failure if the conflict is
296 : * against a tuple inserted by our own transaction, even if it's not
297 : * visible to our snapshot. (This would happen, for example, if
298 : * conflicting keys are proposed for insertion in a single command.)
299 : */
300 40 : if (!TransactionIdIsCurrentTransactionId(xmin))
301 20 : ereport(ERROR,
302 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
303 : errmsg("could not serialize access due to concurrent update")));
304 : }
305 : }
306 :
307 : /*
308 : * ExecCheckTIDVisible -- convenience variant of ExecCheckTupleVisible()
309 : */
310 : static void
311 158 : ExecCheckTIDVisible(EState *estate,
312 : ResultRelInfo *relinfo,
313 : ItemPointer tid,
314 : TupleTableSlot *tempSlot)
315 : {
316 158 : Relation rel = relinfo->ri_RelationDesc;
317 :
318 : /* Redundantly check isolation level */
319 158 : if (!IsolationUsesXactSnapshot())
320 94 : return;
321 :
322 64 : if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
323 0 : elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
324 64 : ExecCheckTupleVisible(estate, rel, tempSlot);
325 44 : ExecClearTuple(tempSlot);
326 : }
327 :
328 : /*
329 : * Initialize to compute stored generated columns for a tuple
330 : *
331 : * This fills the resultRelInfo's ri_GeneratedExprsI/ri_NumGeneratedNeededI
332 : * or ri_GeneratedExprsU/ri_NumGeneratedNeededU fields, depending on cmdtype.
333 : * If cmdType == CMD_UPDATE, the ri_extraUpdatedCols field is filled too.
334 : *
335 : * Note: usually, a given query would need only one of ri_GeneratedExprsI and
336 : * ri_GeneratedExprsU per result rel; but MERGE can need both, and so can
337 : * cross-partition UPDATEs, since a partition might be the target of both
338 : * UPDATE and INSERT actions.
339 : */
340 : void
341 58312 : ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
342 : EState *estate,
343 : CmdType cmdtype)
344 : {
345 58312 : Relation rel = resultRelInfo->ri_RelationDesc;
346 58312 : TupleDesc tupdesc = RelationGetDescr(rel);
347 58312 : int natts = tupdesc->natts;
348 : ExprState **ri_GeneratedExprs;
349 : int ri_NumGeneratedNeeded;
350 : Bitmapset *updatedCols;
351 : MemoryContext oldContext;
352 :
353 : /* Nothing to do if no generated columns */
354 58312 : if (!(tupdesc->constr && tupdesc->constr->has_generated_stored))
355 57382 : return;
356 :
357 : /*
358 : * In an UPDATE, we can skip computing any generated columns that do not
359 : * depend on any UPDATE target column. But if there is a BEFORE ROW
360 : * UPDATE trigger, we cannot skip because the trigger might change more
361 : * columns.
362 : */
363 930 : if (cmdtype == CMD_UPDATE &&
364 230 : !(rel->trigdesc && rel->trigdesc->trig_update_before_row))
365 204 : updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
366 : else
367 726 : updatedCols = NULL;
368 :
369 : /*
370 : * Make sure these data structures are built in the per-query memory
371 : * context so they'll survive throughout the query.
372 : */
373 930 : oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
374 :
375 930 : ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
376 930 : ri_NumGeneratedNeeded = 0;
377 :
378 3586 : for (int i = 0; i < natts; i++)
379 : {
380 2656 : if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
381 : {
382 : Expr *expr;
383 :
384 : /* Fetch the GENERATED AS expression tree */
385 950 : expr = (Expr *) build_column_default(rel, i + 1);
386 950 : if (expr == NULL)
387 0 : elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
388 : i + 1, RelationGetRelationName(rel));
389 :
390 : /*
391 : * If it's an update with a known set of update target columns,
392 : * see if we can skip the computation.
393 : */
394 950 : if (updatedCols)
395 : {
396 210 : Bitmapset *attrs_used = NULL;
397 :
398 210 : pull_varattnos((Node *) expr, 1, &attrs_used);
399 :
400 210 : if (!bms_overlap(updatedCols, attrs_used))
401 24 : continue; /* need not update this column */
402 : }
403 :
404 : /* No luck, so prepare the expression for execution */
405 926 : ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
406 926 : ri_NumGeneratedNeeded++;
407 :
408 : /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
409 926 : if (cmdtype == CMD_UPDATE)
410 212 : resultRelInfo->ri_extraUpdatedCols =
411 212 : bms_add_member(resultRelInfo->ri_extraUpdatedCols,
412 : i + 1 - FirstLowInvalidHeapAttributeNumber);
413 : }
414 : }
415 :
416 : /* Save in appropriate set of fields */
417 930 : if (cmdtype == CMD_UPDATE)
418 : {
419 : /* Don't call twice */
420 : Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
421 :
422 230 : resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
423 230 : resultRelInfo->ri_NumGeneratedNeededU = ri_NumGeneratedNeeded;
424 : }
425 : else
426 : {
427 : /* Don't call twice */
428 : Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
429 :
430 700 : resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
431 700 : resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
432 : }
433 :
434 930 : MemoryContextSwitchTo(oldContext);
435 : }
436 :
437 : /*
438 : * Compute stored generated columns for a tuple
439 : */
440 : void
441 1224 : ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
442 : EState *estate, TupleTableSlot *slot,
443 : CmdType cmdtype)
444 : {
445 1224 : Relation rel = resultRelInfo->ri_RelationDesc;
446 1224 : TupleDesc tupdesc = RelationGetDescr(rel);
447 1224 : int natts = tupdesc->natts;
448 1224 : ExprContext *econtext = GetPerTupleExprContext(estate);
449 : ExprState **ri_GeneratedExprs;
450 : MemoryContext oldContext;
451 : Datum *values;
452 : bool *nulls;
453 :
454 : /* We should not be called unless this is true */
455 : Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
456 :
457 : /*
458 : * Initialize the expressions if we didn't already, and check whether we
459 : * can exit early because nothing needs to be computed.
460 : */
461 1224 : if (cmdtype == CMD_UPDATE)
462 : {
463 266 : if (resultRelInfo->ri_GeneratedExprsU == NULL)
464 204 : ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
465 266 : if (resultRelInfo->ri_NumGeneratedNeededU == 0)
466 18 : return;
467 248 : ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
468 : }
469 : else
470 : {
471 958 : if (resultRelInfo->ri_GeneratedExprsI == NULL)
472 700 : ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
473 : /* Early exit is impossible given the prior Assert */
474 : Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
475 958 : ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
476 : }
477 :
478 1206 : oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
479 :
480 1206 : values = palloc(sizeof(*values) * natts);
481 1206 : nulls = palloc(sizeof(*nulls) * natts);
482 :
483 1206 : slot_getallattrs(slot);
484 1206 : memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
485 :
486 4534 : for (int i = 0; i < natts; i++)
487 : {
488 3340 : Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
489 :
490 3340 : if (ri_GeneratedExprs[i])
491 : {
492 : Datum val;
493 : bool isnull;
494 :
495 : Assert(attr->attgenerated == ATTRIBUTE_GENERATED_STORED);
496 :
497 1220 : econtext->ecxt_scantuple = slot;
498 :
499 1220 : val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
500 :
501 : /*
502 : * We must make a copy of val as we have no guarantees about where
503 : * memory for a pass-by-reference Datum is located.
504 : */
505 1208 : if (!isnull)
506 1166 : val = datumCopy(val, attr->attbyval, attr->attlen);
507 :
508 1208 : values[i] = val;
509 1208 : nulls[i] = isnull;
510 : }
511 : else
512 : {
513 2120 : if (!nulls[i])
514 2078 : values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
515 : }
516 : }
517 :
518 1194 : ExecClearTuple(slot);
519 1194 : memcpy(slot->tts_values, values, sizeof(*values) * natts);
520 1194 : memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
521 1194 : ExecStoreVirtualTuple(slot);
522 1194 : ExecMaterializeSlot(slot);
523 :
524 1194 : MemoryContextSwitchTo(oldContext);
525 : }
526 :
527 : /*
528 : * ExecInitInsertProjection
529 : * Do one-time initialization of projection data for INSERT tuples.
530 : *
531 : * INSERT queries may need a projection to filter out junk attrs in the tlist.
532 : *
533 : * This is also a convenient place to verify that the
534 : * output of an INSERT matches the target table.
535 : */
536 : static void
537 88154 : ExecInitInsertProjection(ModifyTableState *mtstate,
538 : ResultRelInfo *resultRelInfo)
539 : {
540 88154 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
541 88154 : Plan *subplan = outerPlan(node);
542 88154 : EState *estate = mtstate->ps.state;
543 88154 : List *insertTargetList = NIL;
544 88154 : bool need_projection = false;
545 : ListCell *l;
546 :
547 : /* Extract non-junk columns of the subplan's result tlist. */
548 269074 : foreach(l, subplan->targetlist)
549 : {
550 180920 : TargetEntry *tle = (TargetEntry *) lfirst(l);
551 :
552 180920 : if (!tle->resjunk)
553 180920 : insertTargetList = lappend(insertTargetList, tle);
554 : else
555 0 : need_projection = true;
556 : }
557 :
558 : /*
559 : * The junk-free list must produce a tuple suitable for the result
560 : * relation.
561 : */
562 88154 : ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
563 :
564 : /* We'll need a slot matching the table's format. */
565 88154 : resultRelInfo->ri_newTupleSlot =
566 88154 : table_slot_create(resultRelInfo->ri_RelationDesc,
567 : &estate->es_tupleTable);
568 :
569 : /*
570 : * In the ON CONFLICT UPDATE case, we will also need a slot for the old
571 : * tuple to calculate the updated tuple on its base.
572 : */
573 88154 : if (node->onConflictAction == ONCONFLICT_UPDATE)
574 828 : resultRelInfo->ri_oldTupleSlot =
575 828 : table_slot_create(resultRelInfo->ri_RelationDesc,
576 : &estate->es_tupleTable);
577 :
578 : /* Build ProjectionInfo if needed (it probably isn't). */
579 88154 : if (need_projection)
580 : {
581 0 : TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
582 :
583 : /* need an expression context to do the projection */
584 0 : if (mtstate->ps.ps_ExprContext == NULL)
585 0 : ExecAssignExprContext(estate, &mtstate->ps);
586 :
587 0 : resultRelInfo->ri_projectNew =
588 0 : ExecBuildProjectionInfo(insertTargetList,
589 : mtstate->ps.ps_ExprContext,
590 : resultRelInfo->ri_newTupleSlot,
591 : &mtstate->ps,
592 : relDesc);
593 : }
594 :
595 88154 : resultRelInfo->ri_projectNewInfoValid = true;
596 88154 : }
597 :
598 : /*
599 : * ExecInitUpdateProjection
600 : * Do one-time initialization of projection data for UPDATE tuples.
601 : *
602 : * UPDATE always needs a projection, because (1) there's always some junk
603 : * attrs, and (2) we may need to merge values of not-updated columns from
604 : * the old tuple into the final tuple. In UPDATE, the tuple arriving from
605 : * the subplan contains only new values for the changed columns, plus row
606 : * identity info in the junk attrs.
607 : *
608 : * This is "one-time" for any given result rel, but we might touch more than
609 : * one result rel in the course of an inherited UPDATE, and each one needs
610 : * its own projection due to possible column order variation.
611 : *
612 : * This is also a convenient place to verify that the output of an UPDATE
613 : * matches the target table (ExecBuildUpdateProjection does that).
614 : */
615 : static void
616 12708 : ExecInitUpdateProjection(ModifyTableState *mtstate,
617 : ResultRelInfo *resultRelInfo)
618 : {
619 12708 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
620 12708 : Plan *subplan = outerPlan(node);
621 12708 : EState *estate = mtstate->ps.state;
622 12708 : TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
623 : int whichrel;
624 : List *updateColnos;
625 :
626 : /*
627 : * Usually, mt_lastResultIndex matches the target rel. If it happens not
628 : * to, we can get the index the hard way with an integer division.
629 : */
630 12708 : whichrel = mtstate->mt_lastResultIndex;
631 12708 : if (resultRelInfo != mtstate->resultRelInfo + whichrel)
632 : {
633 0 : whichrel = resultRelInfo - mtstate->resultRelInfo;
634 : Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
635 : }
636 :
637 12708 : updateColnos = (List *) list_nth(node->updateColnosLists, whichrel);
638 :
639 : /*
640 : * For UPDATE, we use the old tuple to fill up missing values in the tuple
641 : * produced by the subplan to get the new tuple. We need two slots, both
642 : * matching the table's desired format.
643 : */
644 12708 : resultRelInfo->ri_oldTupleSlot =
645 12708 : table_slot_create(resultRelInfo->ri_RelationDesc,
646 : &estate->es_tupleTable);
647 12708 : resultRelInfo->ri_newTupleSlot =
648 12708 : table_slot_create(resultRelInfo->ri_RelationDesc,
649 : &estate->es_tupleTable);
650 :
651 : /* need an expression context to do the projection */
652 12708 : if (mtstate->ps.ps_ExprContext == NULL)
653 11518 : ExecAssignExprContext(estate, &mtstate->ps);
654 :
655 12708 : resultRelInfo->ri_projectNew =
656 12708 : ExecBuildUpdateProjection(subplan->targetlist,
657 : false, /* subplan did the evaluation */
658 : updateColnos,
659 : relDesc,
660 : mtstate->ps.ps_ExprContext,
661 : resultRelInfo->ri_newTupleSlot,
662 : &mtstate->ps);
663 :
664 12708 : resultRelInfo->ri_projectNewInfoValid = true;
665 12708 : }
666 :
667 : /*
668 : * ExecGetInsertNewTuple
669 : * This prepares a "new" tuple ready to be inserted into given result
670 : * relation, by removing any junk columns of the plan's output tuple
671 : * and (if necessary) coercing the tuple to the right tuple format.
672 : */
673 : static TupleTableSlot *
674 11189526 : ExecGetInsertNewTuple(ResultRelInfo *relinfo,
675 : TupleTableSlot *planSlot)
676 : {
677 11189526 : ProjectionInfo *newProj = relinfo->ri_projectNew;
678 : ExprContext *econtext;
679 :
680 : /*
681 : * If there's no projection to be done, just make sure the slot is of the
682 : * right type for the target rel. If the planSlot is the right type we
683 : * can use it as-is, else copy the data into ri_newTupleSlot.
684 : */
685 11189526 : if (newProj == NULL)
686 : {
687 11189526 : if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
688 : {
689 10419062 : ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
690 10419062 : return relinfo->ri_newTupleSlot;
691 : }
692 : else
693 770464 : return planSlot;
694 : }
695 :
696 : /*
697 : * Else project; since the projection output slot is ri_newTupleSlot, this
698 : * will also fix any slot-type problem.
699 : *
700 : * Note: currently, this is dead code, because INSERT cases don't receive
701 : * any junk columns so there's never a projection to be done.
702 : */
703 0 : econtext = newProj->pi_exprContext;
704 0 : econtext->ecxt_outertuple = planSlot;
705 0 : return ExecProject(newProj);
706 : }
707 :
708 : /*
709 : * ExecGetUpdateNewTuple
710 : * This prepares a "new" tuple by combining an UPDATE subplan's output
711 : * tuple (which contains values of changed columns) with unchanged
712 : * columns taken from the old tuple.
713 : *
714 : * The subplan tuple might also contain junk columns, which are ignored.
715 : * Note that the projection also ensures we have a slot of the right type.
716 : */
717 : TupleTableSlot *
718 307104 : ExecGetUpdateNewTuple(ResultRelInfo *relinfo,
719 : TupleTableSlot *planSlot,
720 : TupleTableSlot *oldSlot)
721 : {
722 307104 : ProjectionInfo *newProj = relinfo->ri_projectNew;
723 : ExprContext *econtext;
724 :
725 : /* Use a few extra Asserts to protect against outside callers */
726 : Assert(relinfo->ri_projectNewInfoValid);
727 : Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
728 : Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
729 :
730 307104 : econtext = newProj->pi_exprContext;
731 307104 : econtext->ecxt_outertuple = planSlot;
732 307104 : econtext->ecxt_scantuple = oldSlot;
733 307104 : return ExecProject(newProj);
734 : }
735 :
736 : /* ----------------------------------------------------------------
737 : * ExecInsert
738 : *
739 : * For INSERT, we have to insert the tuple into the target relation
740 : * (or partition thereof) and insert appropriate tuples into the index
741 : * relations.
742 : *
743 : * slot contains the new tuple value to be stored.
744 : *
745 : * Returns RETURNING result if any, otherwise NULL.
746 : * *inserted_tuple is the tuple that's effectively inserted;
747 : * *insert_destrel is the relation where it was inserted.
748 : * These are only set on success.
749 : *
750 : * This may change the currently active tuple conversion map in
751 : * mtstate->mt_transition_capture, so the callers must take care to
752 : * save the previous value to avoid losing track of it.
753 : * ----------------------------------------------------------------
754 : */
755 : static TupleTableSlot *
756 11192142 : ExecInsert(ModifyTableContext *context,
757 : ResultRelInfo *resultRelInfo,
758 : TupleTableSlot *slot,
759 : bool canSetTag,
760 : TupleTableSlot **inserted_tuple,
761 : ResultRelInfo **insert_destrel)
762 : {
763 11192142 : ModifyTableState *mtstate = context->mtstate;
764 11192142 : EState *estate = context->estate;
765 : Relation resultRelationDesc;
766 11192142 : List *recheckIndexes = NIL;
767 11192142 : TupleTableSlot *planSlot = context->planSlot;
768 11192142 : TupleTableSlot *result = NULL;
769 : TransitionCaptureState *ar_insert_trig_tcs;
770 11192142 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
771 11192142 : OnConflictAction onconflict = node->onConflictAction;
772 11192142 : PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
773 : MemoryContext oldContext;
774 :
775 : /*
776 : * If the input result relation is a partitioned table, find the leaf
777 : * partition to insert the tuple into.
778 : */
779 11192142 : if (proute)
780 : {
781 : ResultRelInfo *partRelInfo;
782 :
783 721592 : slot = ExecPrepareTupleRouting(mtstate, estate, proute,
784 : resultRelInfo, slot,
785 : &partRelInfo);
786 721388 : resultRelInfo = partRelInfo;
787 : }
788 :
789 11191938 : ExecMaterializeSlot(slot);
790 :
791 11191938 : resultRelationDesc = resultRelInfo->ri_RelationDesc;
792 :
793 : /*
794 : * Open the table's indexes, if we have not done so already, so that we
795 : * can add new index entries for the inserted tuple.
796 : */
797 11191938 : if (resultRelationDesc->rd_rel->relhasindex &&
798 2810656 : resultRelInfo->ri_IndexRelationDescs == NULL)
799 29280 : ExecOpenIndices(resultRelInfo, onconflict != ONCONFLICT_NONE);
800 :
801 : /*
802 : * BEFORE ROW INSERT Triggers.
803 : *
804 : * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
805 : * INSERT ... ON CONFLICT statement. We cannot check for constraint
806 : * violations before firing these triggers, because they can change the
807 : * values to insert. Also, they can run arbitrary user-defined code with
808 : * side-effects that we can't cancel by just not inserting the tuple.
809 : */
810 11191938 : if (resultRelInfo->ri_TrigDesc &&
811 75048 : resultRelInfo->ri_TrigDesc->trig_insert_before_row)
812 : {
813 : /* Flush any pending inserts, so rows are visible to the triggers */
814 2114 : if (estate->es_insert_pending_result_relations != NIL)
815 6 : ExecPendingInserts(estate);
816 :
817 2114 : if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
818 200 : return NULL; /* "do nothing" */
819 : }
820 :
821 : /* INSTEAD OF ROW INSERT Triggers */
822 11191622 : if (resultRelInfo->ri_TrigDesc &&
823 74732 : resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
824 : {
825 168 : if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
826 6 : return NULL; /* "do nothing" */
827 : }
828 11191454 : else if (resultRelInfo->ri_FdwRoutine)
829 : {
830 : /*
831 : * GENERATED expressions might reference the tableoid column, so
832 : * (re-)initialize tts_tableOid before evaluating them.
833 : */
834 2014 : slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
835 :
836 : /*
837 : * Compute stored generated columns
838 : */
839 2014 : if (resultRelationDesc->rd_att->constr &&
840 366 : resultRelationDesc->rd_att->constr->has_generated_stored)
841 8 : ExecComputeStoredGenerated(resultRelInfo, estate, slot,
842 : CMD_INSERT);
843 :
844 : /*
845 : * If the FDW supports batching, and batching is requested, accumulate
846 : * rows and insert them in batches. Otherwise use the per-row inserts.
847 : */
848 2014 : if (resultRelInfo->ri_BatchSize > 1)
849 : {
850 288 : bool flushed = false;
851 :
852 : /*
853 : * When we've reached the desired batch size, perform the
854 : * insertion.
855 : */
856 288 : if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize)
857 : {
858 20 : ExecBatchInsert(mtstate, resultRelInfo,
859 : resultRelInfo->ri_Slots,
860 : resultRelInfo->ri_PlanSlots,
861 : resultRelInfo->ri_NumSlots,
862 : estate, canSetTag);
863 20 : flushed = true;
864 : }
865 :
866 288 : oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
867 :
868 288 : if (resultRelInfo->ri_Slots == NULL)
869 : {
870 56 : resultRelInfo->ri_Slots = palloc(sizeof(TupleTableSlot *) *
871 28 : resultRelInfo->ri_BatchSize);
872 28 : resultRelInfo->ri_PlanSlots = palloc(sizeof(TupleTableSlot *) *
873 28 : resultRelInfo->ri_BatchSize);
874 : }
875 :
876 : /*
877 : * Initialize the batch slots. We don't know how many slots will
878 : * be needed, so we initialize them as the batch grows, and we
879 : * keep them across batches. To mitigate an inefficiency in how
880 : * resource owner handles objects with many references (as with
881 : * many slots all referencing the same tuple descriptor) we copy
882 : * the appropriate tuple descriptor for each slot.
883 : */
884 288 : if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
885 : {
886 142 : TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
887 : TupleDesc plan_tdesc =
888 142 : CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
889 :
890 284 : resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
891 142 : MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
892 :
893 284 : resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
894 142 : MakeSingleTupleTableSlot(plan_tdesc, planSlot->tts_ops);
895 :
896 : /* remember how many batch slots we initialized */
897 142 : resultRelInfo->ri_NumSlotsInitialized++;
898 : }
899 :
900 288 : ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
901 : slot);
902 :
903 288 : ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
904 : planSlot);
905 :
906 : /*
907 : * If these are the first tuples stored in the buffers, add the
908 : * target rel and the mtstate to the
909 : * es_insert_pending_result_relations and
910 : * es_insert_pending_modifytables lists respectively, except in
911 : * the case where flushing was done above, in which case they
912 : * would already have been added to the lists, so no need to do
913 : * this.
914 : */
915 288 : if (resultRelInfo->ri_NumSlots == 0 && !flushed)
916 : {
917 : Assert(!list_member_ptr(estate->es_insert_pending_result_relations,
918 : resultRelInfo));
919 36 : estate->es_insert_pending_result_relations =
920 36 : lappend(estate->es_insert_pending_result_relations,
921 : resultRelInfo);
922 36 : estate->es_insert_pending_modifytables =
923 36 : lappend(estate->es_insert_pending_modifytables, mtstate);
924 : }
925 : Assert(list_member_ptr(estate->es_insert_pending_result_relations,
926 : resultRelInfo));
927 :
928 288 : resultRelInfo->ri_NumSlots++;
929 :
930 288 : MemoryContextSwitchTo(oldContext);
931 :
932 288 : return NULL;
933 : }
934 :
935 : /*
936 : * insert into foreign table: let the FDW do it
937 : */
938 1726 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
939 : resultRelInfo,
940 : slot,
941 : planSlot);
942 :
943 1720 : if (slot == NULL) /* "do nothing" */
944 4 : return NULL;
945 :
946 : /*
947 : * AFTER ROW Triggers or RETURNING expressions might reference the
948 : * tableoid column, so (re-)initialize tts_tableOid before evaluating
949 : * them. (This covers the case where the FDW replaced the slot.)
950 : */
951 1716 : slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
952 : }
953 : else
954 : {
955 : WCOKind wco_kind;
956 :
957 : /*
958 : * Constraints and GENERATED expressions might reference the tableoid
959 : * column, so (re-)initialize tts_tableOid before evaluating them.
960 : */
961 11189440 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
962 :
963 : /*
964 : * Compute stored generated columns
965 : */
966 11189440 : if (resultRelationDesc->rd_att->constr &&
967 3036212 : resultRelationDesc->rd_att->constr->has_generated_stored)
968 906 : ExecComputeStoredGenerated(resultRelInfo, estate, slot,
969 : CMD_INSERT);
970 :
971 : /*
972 : * Check any RLS WITH CHECK policies.
973 : *
974 : * Normally we should check INSERT policies. But if the insert is the
975 : * result of a partition key update that moved the tuple to a new
976 : * partition, we should instead check UPDATE policies, because we are
977 : * executing policies defined on the target table, and not those
978 : * defined on the child partitions.
979 : *
980 : * If we're running MERGE, we refer to the action that we're executing
981 : * to know if we're doing an INSERT or UPDATE to a partition table.
982 : */
983 11189428 : if (mtstate->operation == CMD_UPDATE)
984 710 : wco_kind = WCO_RLS_UPDATE_CHECK;
985 11188718 : else if (mtstate->operation == CMD_MERGE)
986 1666 : wco_kind = (mtstate->mt_merge_action->mas_action->commandType == CMD_UPDATE) ?
987 1666 : WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK;
988 : else
989 11187052 : wco_kind = WCO_RLS_INSERT_CHECK;
990 :
991 : /*
992 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind
993 : * we are looking for at this point.
994 : */
995 11189428 : if (resultRelInfo->ri_WithCheckOptions != NIL)
996 552 : ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
997 :
998 : /*
999 : * Check the constraints of the tuple.
1000 : */
1001 11189254 : if (resultRelationDesc->rd_att->constr)
1002 3036116 : ExecConstraints(resultRelInfo, slot, estate);
1003 :
1004 : /*
1005 : * Also check the tuple against the partition constraint, if there is
1006 : * one; except that if we got here via tuple-routing, we don't need to
1007 : * if there's no BR trigger defined on the partition.
1008 : */
1009 11188614 : if (resultRelationDesc->rd_rel->relispartition &&
1010 725616 : (resultRelInfo->ri_RootResultRelInfo == NULL ||
1011 720810 : (resultRelInfo->ri_TrigDesc &&
1012 1454 : resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
1013 5002 : ExecPartitionCheck(resultRelInfo, slot, estate, true);
1014 :
1015 11188446 : if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
1016 4010 : {
1017 : /* Perform a speculative insertion. */
1018 : uint32 specToken;
1019 : ItemPointerData conflictTid;
1020 : bool specConflict;
1021 : List *arbiterIndexes;
1022 :
1023 9380 : arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
1024 :
1025 : /*
1026 : * Do a non-conclusive check for conflicts first.
1027 : *
1028 : * We're not holding any locks yet, so this doesn't guarantee that
1029 : * the later insert won't conflict. But it avoids leaving behind
1030 : * a lot of canceled speculative insertions, if you run a lot of
1031 : * INSERT ON CONFLICT statements that do conflict.
1032 : *
1033 : * We loop back here if we find a conflict below, either during
1034 : * the pre-check, or when we re-check after inserting the tuple
1035 : * speculatively. Better allow interrupts in case some bug makes
1036 : * this an infinite loop.
1037 : */
1038 9390 : vlock:
1039 9390 : CHECK_FOR_INTERRUPTS();
1040 9390 : specConflict = false;
1041 9390 : if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
1042 : &conflictTid, arbiterIndexes))
1043 : {
1044 : /* committed conflict tuple found */
1045 5358 : if (onconflict == ONCONFLICT_UPDATE)
1046 : {
1047 : /*
1048 : * In case of ON CONFLICT DO UPDATE, execute the UPDATE
1049 : * part. Be prepared to retry if the UPDATE fails because
1050 : * of another concurrent UPDATE/DELETE to the conflict
1051 : * tuple.
1052 : */
1053 5200 : TupleTableSlot *returning = NULL;
1054 :
1055 5200 : if (ExecOnConflictUpdate(context, resultRelInfo,
1056 : &conflictTid, slot, canSetTag,
1057 : &returning))
1058 : {
1059 5122 : InstrCountTuples2(&mtstate->ps, 1);
1060 5122 : return returning;
1061 : }
1062 : else
1063 0 : goto vlock;
1064 : }
1065 : else
1066 : {
1067 : /*
1068 : * In case of ON CONFLICT DO NOTHING, do nothing. However,
1069 : * verify that the tuple is visible to the executor's MVCC
1070 : * snapshot at higher isolation levels.
1071 : *
1072 : * Using ExecGetReturningSlot() to store the tuple for the
1073 : * recheck isn't that pretty, but we can't trivially use
1074 : * the input slot, because it might not be of a compatible
1075 : * type. As there's no conflicting usage of
1076 : * ExecGetReturningSlot() in the DO NOTHING case...
1077 : */
1078 : Assert(onconflict == ONCONFLICT_NOTHING);
1079 158 : ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
1080 : ExecGetReturningSlot(estate, resultRelInfo));
1081 138 : InstrCountTuples2(&mtstate->ps, 1);
1082 138 : return NULL;
1083 : }
1084 : }
1085 :
1086 : /*
1087 : * Before we start insertion proper, acquire our "speculative
1088 : * insertion lock". Others can use that to wait for us to decide
1089 : * if we're going to go ahead with the insertion, instead of
1090 : * waiting for the whole transaction to complete.
1091 : */
1092 4026 : specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
1093 :
1094 : /* insert the tuple, with the speculative token */
1095 4026 : table_tuple_insert_speculative(resultRelationDesc, slot,
1096 : estate->es_output_cid,
1097 : 0,
1098 : NULL,
1099 : specToken);
1100 :
1101 : /* insert index entries for tuple */
1102 4026 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1103 : slot, estate, false, true,
1104 : &specConflict,
1105 : arbiterIndexes,
1106 : false);
1107 :
1108 : /* adjust the tuple's state accordingly */
1109 4020 : table_tuple_complete_speculative(resultRelationDesc, slot,
1110 4020 : specToken, !specConflict);
1111 :
1112 : /*
1113 : * Wake up anyone waiting for our decision. They will re-check
1114 : * the tuple, see that it's no longer speculative, and wait on our
1115 : * XID as if this was a regularly inserted tuple all along. Or if
1116 : * we killed the tuple, they will see it's dead, and proceed as if
1117 : * the tuple never existed.
1118 : */
1119 4020 : SpeculativeInsertionLockRelease(GetCurrentTransactionId());
1120 :
1121 : /*
1122 : * If there was a conflict, start from the beginning. We'll do
1123 : * the pre-check again, which will now find the conflicting tuple
1124 : * (unless it aborts before we get there).
1125 : */
1126 4020 : if (specConflict)
1127 : {
1128 10 : list_free(recheckIndexes);
1129 10 : goto vlock;
1130 : }
1131 :
1132 : /* Since there was no insertion conflict, we're done */
1133 : }
1134 : else
1135 : {
1136 : /* insert the tuple normally */
1137 11179066 : slot = table_tuple_insert(resultRelationDesc, slot,
1138 : estate->es_output_cid,
1139 : 0, NULL);
1140 :
1141 : /* insert index entries for tuple */
1142 11179042 : if (resultRelInfo->ri_NumIndices > 0)
1143 2800612 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1144 : slot, estate, false,
1145 : false, NULL, NIL,
1146 : false);
1147 : }
1148 : }
1149 :
1150 11184392 : if (canSetTag)
1151 11183220 : (estate->es_processed)++;
1152 :
1153 : /*
1154 : * If this insert is the result of a partition key update that moved the
1155 : * tuple to a new partition, put this row into the transition NEW TABLE,
1156 : * if there is one. We need to do this separately for DELETE and INSERT
1157 : * because they happen on different tables.
1158 : */
1159 11184392 : ar_insert_trig_tcs = mtstate->mt_transition_capture;
1160 11184392 : if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
1161 42 : && mtstate->mt_transition_capture->tcs_update_new_table)
1162 : {
1163 42 : ExecARUpdateTriggers(estate, resultRelInfo,
1164 : NULL, NULL,
1165 : NULL,
1166 : resultRelInfo->ri_oldTupleSlot,
1167 : slot,
1168 : NULL,
1169 42 : mtstate->mt_transition_capture,
1170 : false);
1171 :
1172 : /*
1173 : * We've already captured the NEW TABLE row, so make sure any AR
1174 : * INSERT trigger fired below doesn't capture it again.
1175 : */
1176 42 : ar_insert_trig_tcs = NULL;
1177 : }
1178 :
1179 : /* AFTER ROW INSERT Triggers */
1180 11184392 : ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
1181 : ar_insert_trig_tcs);
1182 :
1183 11184392 : list_free(recheckIndexes);
1184 :
1185 : /*
1186 : * Check any WITH CHECK OPTION constraints from parent views. We are
1187 : * required to do this after testing all constraints and uniqueness
1188 : * violations per the SQL spec, so we do it after actually inserting the
1189 : * record into the heap and all indexes.
1190 : *
1191 : * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
1192 : * tuple will never be seen, if it violates the WITH CHECK OPTION.
1193 : *
1194 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1195 : * are looking for at this point.
1196 : */
1197 11184392 : if (resultRelInfo->ri_WithCheckOptions != NIL)
1198 364 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1199 :
1200 : /* Process RETURNING if present */
1201 11184246 : if (resultRelInfo->ri_projectReturning)
1202 3420 : result = ExecProcessReturning(resultRelInfo, slot, planSlot);
1203 :
1204 11184234 : if (inserted_tuple)
1205 718 : *inserted_tuple = slot;
1206 11184234 : if (insert_destrel)
1207 718 : *insert_destrel = resultRelInfo;
1208 :
1209 11184234 : return result;
1210 : }
1211 :
1212 : /* ----------------------------------------------------------------
1213 : * ExecBatchInsert
1214 : *
1215 : * Insert multiple tuples in an efficient way.
1216 : * Currently, this handles inserting into a foreign table without
1217 : * RETURNING clause.
1218 : * ----------------------------------------------------------------
1219 : */
1220 : static void
1221 56 : ExecBatchInsert(ModifyTableState *mtstate,
1222 : ResultRelInfo *resultRelInfo,
1223 : TupleTableSlot **slots,
1224 : TupleTableSlot **planSlots,
1225 : int numSlots,
1226 : EState *estate,
1227 : bool canSetTag)
1228 : {
1229 : int i;
1230 56 : int numInserted = numSlots;
1231 56 : TupleTableSlot *slot = NULL;
1232 : TupleTableSlot **rslots;
1233 :
1234 : /*
1235 : * insert into foreign table: let the FDW do it
1236 : */
1237 56 : rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1238 : resultRelInfo,
1239 : slots,
1240 : planSlots,
1241 : &numInserted);
1242 :
1243 344 : for (i = 0; i < numInserted; i++)
1244 : {
1245 288 : slot = rslots[i];
1246 :
1247 : /*
1248 : * AFTER ROW Triggers might reference the tableoid column, so
1249 : * (re-)initialize tts_tableOid before evaluating them.
1250 : */
1251 288 : slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1252 :
1253 : /* AFTER ROW INSERT Triggers */
1254 288 : ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1255 288 : mtstate->mt_transition_capture);
1256 :
1257 : /*
1258 : * Check any WITH CHECK OPTION constraints from parent views. See the
1259 : * comment in ExecInsert.
1260 : */
1261 288 : if (resultRelInfo->ri_WithCheckOptions != NIL)
1262 0 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1263 : }
1264 :
1265 56 : if (canSetTag && numInserted > 0)
1266 56 : estate->es_processed += numInserted;
1267 :
1268 : /* Clean up all the slots, ready for the next batch */
1269 344 : for (i = 0; i < numSlots; i++)
1270 : {
1271 288 : ExecClearTuple(slots[i]);
1272 288 : ExecClearTuple(planSlots[i]);
1273 : }
1274 56 : resultRelInfo->ri_NumSlots = 0;
1275 56 : }
1276 :
1277 : /*
1278 : * ExecPendingInserts -- flushes all pending inserts to the foreign tables
1279 : */
1280 : static void
1281 34 : ExecPendingInserts(EState *estate)
1282 : {
1283 : ListCell *l1,
1284 : *l2;
1285 :
1286 70 : forboth(l1, estate->es_insert_pending_result_relations,
1287 : l2, estate->es_insert_pending_modifytables)
1288 : {
1289 36 : ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l1);
1290 36 : ModifyTableState *mtstate = (ModifyTableState *) lfirst(l2);
1291 :
1292 : Assert(mtstate);
1293 36 : ExecBatchInsert(mtstate, resultRelInfo,
1294 : resultRelInfo->ri_Slots,
1295 : resultRelInfo->ri_PlanSlots,
1296 : resultRelInfo->ri_NumSlots,
1297 36 : estate, mtstate->canSetTag);
1298 : }
1299 :
1300 34 : list_free(estate->es_insert_pending_result_relations);
1301 34 : list_free(estate->es_insert_pending_modifytables);
1302 34 : estate->es_insert_pending_result_relations = NIL;
1303 34 : estate->es_insert_pending_modifytables = NIL;
1304 34 : }
1305 :
1306 : /*
1307 : * ExecDeletePrologue -- subroutine for ExecDelete
1308 : *
1309 : * Prepare executor state for DELETE. Actually, the only thing we have to do
1310 : * here is execute BEFORE ROW triggers. We return false if one of them makes
1311 : * the delete a no-op; otherwise, return true.
1312 : */
1313 : static bool
1314 1526090 : ExecDeletePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1315 : ItemPointer tupleid, HeapTuple oldtuple,
1316 : TupleTableSlot **epqreturnslot, TM_Result *result)
1317 : {
1318 1526090 : if (result)
1319 1286 : *result = TM_Ok;
1320 :
1321 : /* BEFORE ROW DELETE triggers */
1322 1526090 : if (resultRelInfo->ri_TrigDesc &&
1323 7090 : resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1324 : {
1325 : /* Flush any pending inserts, so rows are visible to the triggers */
1326 382 : if (context->estate->es_insert_pending_result_relations != NIL)
1327 2 : ExecPendingInserts(context->estate);
1328 :
1329 382 : return ExecBRDeleteTriggers(context->estate, context->epqstate,
1330 : resultRelInfo, tupleid, oldtuple,
1331 : epqreturnslot, result, &context->tmfd);
1332 : }
1333 :
1334 1525708 : return true;
1335 : }
1336 :
1337 : /*
1338 : * ExecDeleteAct -- subroutine for ExecDelete
1339 : *
1340 : * Actually delete the tuple from a plain table.
1341 : *
1342 : * Caller is in charge of doing EvalPlanQual as necessary
1343 : */
1344 : static TM_Result
1345 1525902 : ExecDeleteAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1346 : ItemPointer tupleid, bool changingPart, int options,
1347 : TupleTableSlot *oldSlot)
1348 : {
1349 1525902 : EState *estate = context->estate;
1350 :
1351 1525902 : return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1352 : estate->es_output_cid,
1353 : estate->es_snapshot,
1354 : estate->es_crosscheck_snapshot,
1355 : options,
1356 : &context->tmfd,
1357 : changingPart,
1358 : oldSlot);
1359 : }
1360 :
1361 : /*
1362 : * ExecDeleteEpilogue -- subroutine for ExecDelete
1363 : *
1364 : * Closing steps of tuple deletion; this invokes AFTER FOR EACH ROW triggers,
1365 : * including the UPDATE triggers if the deletion is being done as part of a
1366 : * cross-partition tuple move.
1367 : *
1368 : * The old tuple is already fetched into ‘slot’ for regular tables. For FDW,
1369 : * the old tuple is given as 'oldtuple' and is to be stored in 'slot' when
1370 : * needed.
1371 : */
1372 : static void
1373 1525842 : ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1374 : ItemPointer tupleid, HeapTuple oldtuple,
1375 : TupleTableSlot *slot, bool changingPart)
1376 : {
1377 1525842 : ModifyTableState *mtstate = context->mtstate;
1378 1525842 : EState *estate = context->estate;
1379 : TransitionCaptureState *ar_delete_trig_tcs;
1380 :
1381 : /*
1382 : * If this delete is the result of a partition key update that moved the
1383 : * tuple to a new partition, put this row into the transition OLD TABLE,
1384 : * if there is one. We need to do this separately for DELETE and INSERT
1385 : * because they happen on different tables.
1386 : */
1387 1525842 : ar_delete_trig_tcs = mtstate->mt_transition_capture;
1388 1525842 : if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1389 42 : mtstate->mt_transition_capture->tcs_update_old_table)
1390 : {
1391 42 : ExecARUpdateTriggers(estate, resultRelInfo,
1392 : NULL, NULL,
1393 : oldtuple,
1394 42 : slot, NULL, NULL, mtstate->mt_transition_capture,
1395 : false);
1396 :
1397 : /*
1398 : * We've already captured the OLD TABLE row, so make sure any AR
1399 : * DELETE trigger fired below doesn't capture it again.
1400 : */
1401 42 : ar_delete_trig_tcs = NULL;
1402 : }
1403 :
1404 : /* AFTER ROW DELETE Triggers */
1405 1525842 : ExecARDeleteTriggers(estate, resultRelInfo, oldtuple, slot,
1406 : ar_delete_trig_tcs, changingPart);
1407 1525842 : }
1408 :
1409 : /*
1410 : * Initializes the tuple slot in a ResultRelInfo for DELETE action.
1411 : *
1412 : * We mark 'projectNewInfoValid' even though the projections themselves
1413 : * are not initialized here.
1414 : */
1415 : static void
1416 11756 : ExecInitDeleteTupleSlot(ModifyTableState *mtstate,
1417 : ResultRelInfo *resultRelInfo)
1418 : {
1419 11756 : EState *estate = mtstate->ps.state;
1420 :
1421 : Assert(!resultRelInfo->ri_projectNewInfoValid);
1422 :
1423 11756 : resultRelInfo->ri_oldTupleSlot =
1424 11756 : table_slot_create(resultRelInfo->ri_RelationDesc,
1425 : &estate->es_tupleTable);
1426 11756 : resultRelInfo->ri_projectNewInfoValid = true;
1427 11756 : }
1428 :
1429 : /* ----------------------------------------------------------------
1430 : * ExecDelete
1431 : *
1432 : * DELETE is like UPDATE, except that we delete the tuple and no
1433 : * index modifications are needed.
1434 : *
1435 : * When deleting from a table, tupleid identifies the tuple to
1436 : * delete and oldtuple is NULL. When deleting from a view,
1437 : * oldtuple is passed to the INSTEAD OF triggers and identifies
1438 : * what to delete, and tupleid is invalid. When deleting from a
1439 : * foreign table, tupleid is invalid; the FDW has to figure out
1440 : * which row to delete using data from the planSlot. oldtuple is
1441 : * passed to foreign table triggers; it is NULL when the foreign
1442 : * table has no relevant triggers. We use tupleDeleted to indicate
1443 : * whether the tuple is actually deleted, callers can use it to
1444 : * decide whether to continue the operation. When this DELETE is a
1445 : * part of an UPDATE of partition-key, then the slot returned by
1446 : * EvalPlanQual() is passed back using output parameter epqreturnslot.
1447 : *
1448 : * Returns RETURNING result if any, otherwise NULL. The deleted tuple
1449 : * to be stored into oldslot independently that.
1450 : * ----------------------------------------------------------------
1451 : */
1452 : static TupleTableSlot *
1453 1525756 : ExecDelete(ModifyTableContext *context,
1454 : ResultRelInfo *resultRelInfo,
1455 : ItemPointer tupleid,
1456 : HeapTuple oldtuple,
1457 : TupleTableSlot *oldslot,
1458 : bool processReturning,
1459 : bool changingPart,
1460 : bool canSetTag,
1461 : TM_Result *tmresult,
1462 : bool *tupleDeleted,
1463 : TupleTableSlot **epqreturnslot)
1464 : {
1465 1525756 : EState *estate = context->estate;
1466 1525756 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1467 1525756 : TupleTableSlot *slot = NULL;
1468 : TM_Result result;
1469 :
1470 1525756 : if (tupleDeleted)
1471 952 : *tupleDeleted = false;
1472 :
1473 : /*
1474 : * Prepare for the delete. This includes BEFORE ROW triggers, so we're
1475 : * done if it says we are.
1476 : */
1477 1525756 : if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple,
1478 : epqreturnslot, tmresult))
1479 52 : return NULL;
1480 :
1481 : /* INSTEAD OF ROW DELETE Triggers */
1482 1525670 : if (resultRelInfo->ri_TrigDesc &&
1483 6954 : resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
1484 48 : {
1485 : bool dodelete;
1486 :
1487 : Assert(oldtuple != NULL);
1488 54 : dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
1489 :
1490 54 : if (!dodelete) /* "do nothing" */
1491 6 : return NULL;
1492 : }
1493 1525616 : else if (resultRelInfo->ri_FdwRoutine)
1494 : {
1495 : /*
1496 : * delete from foreign table: let the FDW do it
1497 : *
1498 : * We offer the returning slot as a place to store RETURNING data,
1499 : * although the FDW can return some other slot if it wants.
1500 : */
1501 34 : slot = ExecGetReturningSlot(estate, resultRelInfo);
1502 34 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
1503 : resultRelInfo,
1504 : slot,
1505 : context->planSlot);
1506 :
1507 34 : if (slot == NULL) /* "do nothing" */
1508 0 : return NULL;
1509 :
1510 : /*
1511 : * RETURNING expressions might reference the tableoid column, so
1512 : * (re)initialize tts_tableOid before evaluating them.
1513 : */
1514 34 : if (TTS_EMPTY(slot))
1515 6 : ExecStoreAllNullTuple(slot);
1516 :
1517 34 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1518 : }
1519 : else
1520 : {
1521 1525582 : int options = TABLE_MODIFY_WAIT | TABLE_MODIFY_FETCH_OLD_TUPLE;
1522 :
1523 : /*
1524 : * Specify that we need to lock and fetch the last tuple version for
1525 : * EPQ on appropriate transaction isolation levels.
1526 : */
1527 1525582 : if (!IsolationUsesXactSnapshot())
1528 1523826 : options |= TABLE_MODIFY_LOCK_UPDATED;
1529 :
1530 : /*
1531 : * delete the tuple
1532 : *
1533 : * Note: if context->estate->es_crosscheck_snapshot isn't
1534 : * InvalidSnapshot, we check that the row to be deleted is visible to
1535 : * that snapshot, and throw a can't-serialize error if not. This is a
1536 : * special-case behavior needed for referential integrity updates in
1537 : * transaction-snapshot mode transactions.
1538 : */
1539 1525582 : ldelete:
1540 1525586 : result = ExecDeleteAct(context, resultRelInfo, tupleid, changingPart,
1541 : options, oldslot);
1542 :
1543 1525546 : if (tmresult)
1544 916 : *tmresult = result;
1545 :
1546 1525546 : switch (result)
1547 : {
1548 34 : case TM_SelfModified:
1549 :
1550 : /*
1551 : * The target tuple was already updated or deleted by the
1552 : * current command, or by a later command in the current
1553 : * transaction. The former case is possible in a join DELETE
1554 : * where multiple tuples join to the same target tuple. This
1555 : * is somewhat questionable, but Postgres has always allowed
1556 : * it: we just ignore additional deletion attempts.
1557 : *
1558 : * The latter case arises if the tuple is modified by a
1559 : * command in a BEFORE trigger, or perhaps by a command in a
1560 : * volatile function used in the query. In such situations we
1561 : * should not ignore the deletion, but it is equally unsafe to
1562 : * proceed. We don't want to discard the original DELETE
1563 : * while keeping the triggered actions based on its deletion;
1564 : * and it would be no better to allow the original DELETE
1565 : * while discarding updates that it triggered. The row update
1566 : * carries some information that might be important according
1567 : * to business rules; so throwing an error is the only safe
1568 : * course.
1569 : *
1570 : * If a trigger actually intends this type of interaction, it
1571 : * can re-execute the DELETE and then return NULL to cancel
1572 : * the outer delete.
1573 : */
1574 34 : if (context->tmfd.cmax != estate->es_output_cid)
1575 8 : ereport(ERROR,
1576 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1577 : errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1578 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1579 :
1580 : /* Else, already deleted by self; nothing to do */
1581 26 : return NULL;
1582 :
1583 1525456 : case TM_Ok:
1584 1525456 : break;
1585 :
1586 48 : case TM_Updated:
1587 : {
1588 : TupleTableSlot *epqslot;
1589 :
1590 48 : if (IsolationUsesXactSnapshot())
1591 2 : ereport(ERROR,
1592 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1593 : errmsg("could not serialize access due to concurrent update")));
1594 :
1595 : /*
1596 : * We need to do EPQ. The latest tuple is already found
1597 : * and locked as a result of TABLE_MODIFY_LOCK_UPDATED.
1598 : */
1599 : Assert(context->tmfd.traversed);
1600 46 : epqslot = EvalPlanQual(context->epqstate,
1601 : resultRelationDesc,
1602 : resultRelInfo->ri_RangeTableIndex,
1603 : oldslot);
1604 46 : if (TupIsNull(epqslot))
1605 : /* Tuple not passing quals anymore, exiting... */
1606 30 : return NULL;
1607 :
1608 : /*
1609 : * If requested, skip delete and pass back the updated
1610 : * row.
1611 : */
1612 16 : if (epqreturnslot)
1613 : {
1614 12 : *epqreturnslot = epqslot;
1615 12 : return NULL;
1616 : }
1617 : else
1618 4 : goto ldelete;
1619 : }
1620 :
1621 8 : case TM_Deleted:
1622 8 : if (IsolationUsesXactSnapshot())
1623 0 : ereport(ERROR,
1624 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1625 : errmsg("could not serialize access due to concurrent delete")));
1626 : /* tuple already deleted; nothing to do */
1627 8 : return NULL;
1628 :
1629 0 : default:
1630 0 : elog(ERROR, "unrecognized table_tuple_delete status: %u",
1631 : result);
1632 : return NULL;
1633 : }
1634 :
1635 : /*
1636 : * Note: Normally one would think that we have to delete index tuples
1637 : * associated with the heap tuple now...
1638 : *
1639 : * ... but in POSTGRES, we have no need to do this because VACUUM will
1640 : * take care of it later. We can't delete index tuples immediately
1641 : * anyway, since the tuple is still visible to other transactions.
1642 : */
1643 : }
1644 :
1645 1525538 : if (canSetTag)
1646 1524436 : (estate->es_processed)++;
1647 :
1648 : /* Tell caller that the delete actually happened. */
1649 1525538 : if (tupleDeleted)
1650 874 : *tupleDeleted = true;
1651 :
1652 1525538 : ExecDeleteEpilogue(context, resultRelInfo, tupleid, oldtuple,
1653 : oldslot, changingPart);
1654 :
1655 : /* Process RETURNING if present and if requested */
1656 1525538 : if (processReturning && resultRelInfo->ri_projectReturning)
1657 : {
1658 : /*
1659 : * We have to put the target tuple into a slot, which means first we
1660 : * gotta fetch it. We can use the trigger tuple slot.
1661 : */
1662 : TupleTableSlot *rslot;
1663 :
1664 874 : if (resultRelInfo->ri_FdwRoutine)
1665 : {
1666 : /* FDW must have provided a slot containing the deleted row */
1667 : Assert(!TupIsNull(slot));
1668 : }
1669 : else
1670 : {
1671 : /* Copy old tuple to the returning slot */
1672 868 : slot = ExecGetReturningSlot(estate, resultRelInfo);
1673 868 : if (oldtuple != NULL)
1674 24 : ExecForceStoreHeapTuple(oldtuple, slot, false);
1675 : else
1676 844 : ExecCopySlot(slot, oldslot);
1677 : Assert(!TupIsNull(slot));
1678 : }
1679 :
1680 874 : rslot = ExecProcessReturning(resultRelInfo, slot, context->planSlot);
1681 :
1682 : /*
1683 : * Before releasing the target tuple again, make sure rslot has a
1684 : * local copy of any pass-by-reference values.
1685 : */
1686 874 : ExecMaterializeSlot(rslot);
1687 :
1688 874 : ExecClearTuple(slot);
1689 :
1690 874 : return rslot;
1691 : }
1692 :
1693 1524664 : return NULL;
1694 : }
1695 :
1696 : /*
1697 : * ExecCrossPartitionUpdate --- Move an updated tuple to another partition.
1698 : *
1699 : * This works by first deleting the old tuple from the current partition,
1700 : * followed by inserting the new tuple into the root parent table, that is,
1701 : * mtstate->rootResultRelInfo. It will be re-routed from there to the
1702 : * correct partition.
1703 : *
1704 : * Returns true if the tuple has been successfully moved, or if it's found
1705 : * that the tuple was concurrently deleted so there's nothing more to do
1706 : * for the caller.
1707 : *
1708 : * False is returned if the tuple we're trying to move is found to have been
1709 : * concurrently updated. In that case, the caller must check if the updated
1710 : * tuple that's returned in *retry_slot still needs to be re-routed, and call
1711 : * this function again or perform a regular update accordingly. For MERGE,
1712 : * the updated tuple is not returned in *retry_slot; it has its own retry
1713 : * logic.
1714 : */
1715 : static bool
1716 994 : ExecCrossPartitionUpdate(ModifyTableContext *context,
1717 : ResultRelInfo *resultRelInfo,
1718 : ItemPointer tupleid, HeapTuple oldtuple,
1719 : TupleTableSlot *slot,
1720 : bool canSetTag,
1721 : UpdateContext *updateCxt,
1722 : TM_Result *tmresult,
1723 : TupleTableSlot **retry_slot,
1724 : TupleTableSlot **inserted_tuple,
1725 : ResultRelInfo **insert_destrel)
1726 : {
1727 994 : ModifyTableState *mtstate = context->mtstate;
1728 994 : EState *estate = mtstate->ps.state;
1729 : TupleConversionMap *tupconv_map;
1730 : bool tuple_deleted;
1731 994 : TupleTableSlot *epqslot = NULL;
1732 :
1733 994 : context->cpUpdateReturningSlot = NULL;
1734 994 : *retry_slot = NULL;
1735 :
1736 : /*
1737 : * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1738 : * to migrate to a different partition. Maybe this can be implemented
1739 : * some day, but it seems a fringe feature with little redeeming value.
1740 : */
1741 994 : if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1742 0 : ereport(ERROR,
1743 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1744 : errmsg("invalid ON UPDATE specification"),
1745 : errdetail("The result tuple would appear in a different partition than the original tuple.")));
1746 :
1747 : /*
1748 : * When an UPDATE is run directly on a leaf partition, simply fail with a
1749 : * partition constraint violation error.
1750 : */
1751 994 : if (resultRelInfo == mtstate->rootResultRelInfo)
1752 42 : ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1753 :
1754 : /* Initialize tuple routing info if not already done. */
1755 952 : if (mtstate->mt_partition_tuple_routing == NULL)
1756 : {
1757 602 : Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
1758 : MemoryContext oldcxt;
1759 :
1760 : /* Things built here have to last for the query duration. */
1761 602 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
1762 :
1763 602 : mtstate->mt_partition_tuple_routing =
1764 602 : ExecSetupPartitionTupleRouting(estate, rootRel);
1765 :
1766 : /*
1767 : * Before a partition's tuple can be re-routed, it must first be
1768 : * converted to the root's format, so we'll need a slot for storing
1769 : * such tuples.
1770 : */
1771 : Assert(mtstate->mt_root_tuple_slot == NULL);
1772 602 : mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
1773 :
1774 602 : MemoryContextSwitchTo(oldcxt);
1775 : }
1776 :
1777 : /*
1778 : * Make sure ri_oldTupleSlot is initialized. The old tuple is to be saved
1779 : * there by ExecDelete() to save effort on further re-fetching.
1780 : */
1781 952 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
1782 0 : ExecInitUpdateProjection(mtstate, resultRelInfo);
1783 :
1784 : /*
1785 : * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
1786 : * We want to return rows from INSERT.
1787 : */
1788 952 : ExecDelete(context, resultRelInfo,
1789 : tupleid, oldtuple, resultRelInfo->ri_oldTupleSlot,
1790 : false, /* processReturning */
1791 : true, /* changingPart */
1792 : false, /* canSetTag */
1793 : tmresult, &tuple_deleted, &epqslot);
1794 :
1795 : /*
1796 : * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
1797 : * it was already deleted by self, or it was concurrently deleted by
1798 : * another transaction), then we should skip the insert as well;
1799 : * otherwise, an UPDATE could cause an increase in the total number of
1800 : * rows across all partitions, which is clearly wrong.
1801 : *
1802 : * For a normal UPDATE, the case where the tuple has been the subject of a
1803 : * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
1804 : * machinery, but for an UPDATE that we've translated into a DELETE from
1805 : * this partition and an INSERT into some other partition, that's not
1806 : * available, because CTID chains can't span relation boundaries. We
1807 : * mimic the semantics to a limited extent by skipping the INSERT if the
1808 : * DELETE fails to find a tuple. This ensures that two concurrent
1809 : * attempts to UPDATE the same tuple at the same time can't turn one tuple
1810 : * into two, and that an UPDATE of a just-deleted tuple can't resurrect
1811 : * it.
1812 : */
1813 950 : if (!tuple_deleted)
1814 : {
1815 : /*
1816 : * epqslot will be typically NULL. But when ExecDelete() finds that
1817 : * another transaction has concurrently updated the same row, it
1818 : * re-fetches the row, skips the delete, and epqslot is set to the
1819 : * re-fetched tuple slot. In that case, we need to do all the checks
1820 : * again. For MERGE, we leave everything to the caller (it must do
1821 : * additional rechecking, and might end up executing a different
1822 : * action entirely).
1823 : */
1824 76 : if (mtstate->operation == CMD_MERGE)
1825 34 : return *tmresult == TM_Ok;
1826 42 : else if (TupIsNull(epqslot))
1827 36 : return true;
1828 : else
1829 : {
1830 : /*
1831 : * ExecDelete already fetches the most recent version of old tuple
1832 : * to resultRelInfo->ri_oldTupleSlot. So, just project the new
1833 : * tuple to retry the UPDATE with.
1834 : */
1835 6 : *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot,
1836 : resultRelInfo->ri_oldTupleSlot);
1837 6 : return false;
1838 : }
1839 : }
1840 :
1841 : /*
1842 : * resultRelInfo is one of the per-relation resultRelInfos. So we should
1843 : * convert the tuple into root's tuple descriptor if needed, since
1844 : * ExecInsert() starts the search from root.
1845 : */
1846 874 : tupconv_map = ExecGetChildToRootMap(resultRelInfo);
1847 874 : if (tupconv_map != NULL)
1848 290 : slot = execute_attr_map_slot(tupconv_map->attrMap,
1849 : slot,
1850 : mtstate->mt_root_tuple_slot);
1851 :
1852 : /* Tuple routing starts from the root table. */
1853 746 : context->cpUpdateReturningSlot =
1854 874 : ExecInsert(context, mtstate->rootResultRelInfo, slot, canSetTag,
1855 : inserted_tuple, insert_destrel);
1856 :
1857 : /*
1858 : * Reset the transition state that may possibly have been written by
1859 : * INSERT.
1860 : */
1861 746 : if (mtstate->mt_transition_capture)
1862 42 : mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL;
1863 :
1864 : /* We're done moving. */
1865 746 : return true;
1866 : }
1867 :
1868 : /*
1869 : * ExecUpdatePrologue -- subroutine for ExecUpdate
1870 : *
1871 : * Prepare executor state for UPDATE. This includes running BEFORE ROW
1872 : * triggers. We return false if one of them makes the update a no-op;
1873 : * otherwise, return true.
1874 : */
1875 : static bool
1876 314052 : ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1877 : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
1878 : TM_Result *result)
1879 : {
1880 314052 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1881 :
1882 314052 : if (result)
1883 1934 : *result = TM_Ok;
1884 :
1885 314052 : ExecMaterializeSlot(slot);
1886 :
1887 : /*
1888 : * Open the table's indexes, if we have not done so already, so that we
1889 : * can add new index entries for the updated tuple.
1890 : */
1891 314052 : if (resultRelationDesc->rd_rel->relhasindex &&
1892 223728 : resultRelInfo->ri_IndexRelationDescs == NULL)
1893 8440 : ExecOpenIndices(resultRelInfo, false);
1894 :
1895 : /* BEFORE ROW UPDATE triggers */
1896 314052 : if (resultRelInfo->ri_TrigDesc &&
1897 6110 : resultRelInfo->ri_TrigDesc->trig_update_before_row)
1898 : {
1899 : /* Flush any pending inserts, so rows are visible to the triggers */
1900 2560 : if (context->estate->es_insert_pending_result_relations != NIL)
1901 2 : ExecPendingInserts(context->estate);
1902 :
1903 2560 : return ExecBRUpdateTriggers(context->estate, context->epqstate,
1904 : resultRelInfo, tupleid, oldtuple, slot,
1905 : result, &context->tmfd);
1906 : }
1907 :
1908 311492 : return true;
1909 : }
1910 :
1911 : /*
1912 : * ExecUpdatePrepareSlot -- subroutine for ExecUpdateAct
1913 : *
1914 : * Apply the final modifications to the tuple slot before the update.
1915 : * (This is split out because we also need it in the foreign-table code path.)
1916 : */
1917 : static void
1918 313786 : ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo,
1919 : TupleTableSlot *slot,
1920 : EState *estate)
1921 : {
1922 313786 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1923 :
1924 : /*
1925 : * Constraints and GENERATED expressions might reference the tableoid
1926 : * column, so (re-)initialize tts_tableOid before evaluating them.
1927 : */
1928 313786 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1929 :
1930 : /*
1931 : * Compute stored generated columns
1932 : */
1933 313786 : if (resultRelationDesc->rd_att->constr &&
1934 186880 : resultRelationDesc->rd_att->constr->has_generated_stored)
1935 260 : ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1936 : CMD_UPDATE);
1937 313786 : }
1938 :
1939 : /*
1940 : * ExecUpdateAct -- subroutine for ExecUpdate
1941 : *
1942 : * Actually update the tuple, when operating on a plain table. If the
1943 : * table is a partition, and the command was called referencing an ancestor
1944 : * partitioned table, this routine migrates the resulting tuple to another
1945 : * partition.
1946 : *
1947 : * The caller is in charge of keeping indexes current as necessary. The
1948 : * caller is also in charge of doing EvalPlanQual if the tuple is found to
1949 : * be concurrently updated. However, in case of a cross-partition update,
1950 : * this routine does it.
1951 : */
1952 : static TM_Result
1953 313638 : ExecUpdateAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1954 : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
1955 : bool canSetTag, int options, TupleTableSlot *oldSlot,
1956 : UpdateContext *updateCxt)
1957 : {
1958 313638 : EState *estate = context->estate;
1959 313638 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1960 : bool partition_constraint_failed;
1961 : TM_Result result;
1962 :
1963 313638 : updateCxt->crossPartUpdate = false;
1964 :
1965 : /*
1966 : * If we move the tuple to a new partition, we loop back here to recompute
1967 : * GENERATED values (which are allowed to be different across partitions)
1968 : * and recheck any RLS policies and constraints. We do not fire any
1969 : * BEFORE triggers of the new partition, however.
1970 : */
1971 313644 : lreplace:
1972 : /* Fill in GENERATEd columns */
1973 313644 : ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
1974 :
1975 : /* ensure slot is independent, consider e.g. EPQ */
1976 313644 : ExecMaterializeSlot(slot);
1977 :
1978 : /*
1979 : * If partition constraint fails, this row might get moved to another
1980 : * partition, in which case we should check the RLS CHECK policy just
1981 : * before inserting into the new partition, rather than doing it here.
1982 : * This is because a trigger on that partition might again change the row.
1983 : * So skip the WCO checks if the partition constraint fails.
1984 : */
1985 313644 : partition_constraint_failed =
1986 316128 : resultRelationDesc->rd_rel->relispartition &&
1987 2484 : !ExecPartitionCheck(resultRelInfo, slot, estate, false);
1988 :
1989 : /* Check any RLS UPDATE WITH CHECK policies */
1990 313644 : if (!partition_constraint_failed &&
1991 312650 : resultRelInfo->ri_WithCheckOptions != NIL)
1992 : {
1993 : /*
1994 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind
1995 : * we are looking for at this point.
1996 : */
1997 474 : ExecWithCheckOptions(WCO_RLS_UPDATE_CHECK,
1998 : resultRelInfo, slot, estate);
1999 : }
2000 :
2001 : /*
2002 : * If a partition check failed, try to move the row into the right
2003 : * partition.
2004 : */
2005 313590 : if (partition_constraint_failed)
2006 : {
2007 : TupleTableSlot *inserted_tuple,
2008 : *retry_slot;
2009 994 : ResultRelInfo *insert_destrel = NULL;
2010 :
2011 : /*
2012 : * ExecCrossPartitionUpdate will first DELETE the row from the
2013 : * partition it's currently in and then insert it back into the root
2014 : * table, which will re-route it to the correct partition. However,
2015 : * if the tuple has been concurrently updated, a retry is needed.
2016 : */
2017 994 : if (ExecCrossPartitionUpdate(context, resultRelInfo,
2018 : tupleid, oldtuple, slot,
2019 : canSetTag, updateCxt,
2020 : &result,
2021 : &retry_slot,
2022 : &inserted_tuple,
2023 : &insert_destrel))
2024 : {
2025 : /* success! */
2026 806 : updateCxt->crossPartUpdate = true;
2027 :
2028 : /*
2029 : * If the partitioned table being updated is referenced in foreign
2030 : * keys, queue up trigger events to check that none of them were
2031 : * violated. No special treatment is needed in
2032 : * non-cross-partition update situations, because the leaf
2033 : * partition's AR update triggers will take care of that. During
2034 : * cross-partition updates implemented as delete on the source
2035 : * partition followed by insert on the destination partition,
2036 : * AR-UPDATE triggers of the root table (that is, the table
2037 : * mentioned in the query) must be fired.
2038 : *
2039 : * NULL insert_destrel means that the move failed to occur, that
2040 : * is, the update failed, so no need to anything in that case.
2041 : */
2042 806 : if (insert_destrel &&
2043 718 : resultRelInfo->ri_TrigDesc &&
2044 338 : resultRelInfo->ri_TrigDesc->trig_update_after_row)
2045 276 : ExecCrossPartitionUpdateForeignKey(context,
2046 : resultRelInfo,
2047 : insert_destrel,
2048 : tupleid,
2049 : resultRelInfo->ri_oldTupleSlot,
2050 : inserted_tuple);
2051 :
2052 810 : return TM_Ok;
2053 : }
2054 :
2055 : /*
2056 : * No luck, a retry is needed. If running MERGE, we do not do so
2057 : * here; instead let it handle that on its own rules.
2058 : */
2059 16 : if (context->mtstate->operation == CMD_MERGE)
2060 10 : return result;
2061 :
2062 : /*
2063 : * ExecCrossPartitionUpdate installed an updated version of the new
2064 : * tuple in the retry slot; start over.
2065 : */
2066 6 : slot = retry_slot;
2067 6 : goto lreplace;
2068 : }
2069 :
2070 : /*
2071 : * Check the constraints of the tuple. We've already checked the
2072 : * partition constraint above; however, we must still ensure the tuple
2073 : * passes all other constraints, so we will call ExecConstraints() and
2074 : * have it validate all remaining checks.
2075 : */
2076 312596 : if (resultRelationDesc->rd_att->constr)
2077 186332 : ExecConstraints(resultRelInfo, slot, estate);
2078 :
2079 : /*
2080 : * replace the heap tuple
2081 : *
2082 : * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
2083 : * the row to be updated is visible to that snapshot, and throw a
2084 : * can't-serialize error if not. This is a special-case behavior needed
2085 : * for referential integrity updates in transaction-snapshot mode
2086 : * transactions.
2087 : */
2088 312546 : result = table_tuple_update(resultRelationDesc, tupleid, slot,
2089 : estate->es_output_cid,
2090 : estate->es_snapshot,
2091 : estate->es_crosscheck_snapshot,
2092 : options /* wait for commit */ ,
2093 : &context->tmfd, &updateCxt->lockmode,
2094 : &updateCxt->updateIndexes,
2095 : oldSlot);
2096 :
2097 312518 : return result;
2098 : }
2099 :
2100 : /*
2101 : * ExecUpdateEpilogue -- subroutine for ExecUpdate
2102 : *
2103 : * Closing steps of updating a tuple. Must be called if ExecUpdateAct
2104 : * returns indicating that the tuple was updated.
2105 : */
2106 : static void
2107 312514 : ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt,
2108 : ResultRelInfo *resultRelInfo, ItemPointer tupleid,
2109 : HeapTuple oldtuple, TupleTableSlot *slot,
2110 : TupleTableSlot *oldslot)
2111 : {
2112 312514 : ModifyTableState *mtstate = context->mtstate;
2113 312514 : List *recheckIndexes = NIL;
2114 :
2115 : /* insert index entries for tuple if necessary */
2116 312514 : if (resultRelInfo->ri_NumIndices > 0 && (updateCxt->updateIndexes != TU_None))
2117 168976 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
2118 : slot, context->estate,
2119 : true, false,
2120 : NULL, NIL,
2121 168976 : (updateCxt->updateIndexes == TU_Summarizing));
2122 :
2123 : /* AFTER ROW UPDATE Triggers */
2124 312490 : ExecARUpdateTriggers(context->estate, resultRelInfo,
2125 : NULL, NULL,
2126 : oldtuple, oldslot, slot,
2127 : recheckIndexes,
2128 312490 : mtstate->operation == CMD_INSERT ?
2129 : mtstate->mt_oc_transition_capture :
2130 : mtstate->mt_transition_capture,
2131 : false);
2132 :
2133 312490 : list_free(recheckIndexes);
2134 :
2135 : /*
2136 : * Check any WITH CHECK OPTION constraints from parent views. We are
2137 : * required to do this after testing all constraints and uniqueness
2138 : * violations per the SQL spec, so we do it after actually updating the
2139 : * record in the heap and all indexes.
2140 : *
2141 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
2142 : * are looking for at this point.
2143 : */
2144 312490 : if (resultRelInfo->ri_WithCheckOptions != NIL)
2145 448 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo,
2146 : slot, context->estate);
2147 312414 : }
2148 :
2149 : /*
2150 : * Queues up an update event using the target root partitioned table's
2151 : * trigger to check that a cross-partition update hasn't broken any foreign
2152 : * keys pointing into it.
2153 : */
2154 : static void
2155 276 : ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
2156 : ResultRelInfo *sourcePartInfo,
2157 : ResultRelInfo *destPartInfo,
2158 : ItemPointer tupleid,
2159 : TupleTableSlot *oldslot,
2160 : TupleTableSlot *newslot)
2161 : {
2162 : ListCell *lc;
2163 : ResultRelInfo *rootRelInfo;
2164 : List *ancestorRels;
2165 :
2166 276 : rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2167 276 : ancestorRels = ExecGetAncestorResultRels(context->estate, sourcePartInfo);
2168 :
2169 : /*
2170 : * For any foreign keys that point directly into a non-root ancestors of
2171 : * the source partition, we can in theory fire an update event to enforce
2172 : * those constraints using their triggers, if we could tell that both the
2173 : * source and the destination partitions are under the same ancestor. But
2174 : * for now, we simply report an error that those cannot be enforced.
2175 : */
2176 606 : foreach(lc, ancestorRels)
2177 : {
2178 336 : ResultRelInfo *rInfo = lfirst(lc);
2179 336 : TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2180 336 : bool has_noncloned_fkey = false;
2181 :
2182 : /* Root ancestor's triggers will be processed. */
2183 336 : if (rInfo == rootRelInfo)
2184 270 : continue;
2185 :
2186 66 : if (trigdesc && trigdesc->trig_update_after_row)
2187 : {
2188 228 : for (int i = 0; i < trigdesc->numtriggers; i++)
2189 : {
2190 168 : Trigger *trig = &trigdesc->triggers[i];
2191 :
2192 174 : if (!trig->tgisclone &&
2193 6 : RI_FKey_trigger_type(trig->tgfoid) == RI_TRIGGER_PK)
2194 : {
2195 6 : has_noncloned_fkey = true;
2196 6 : break;
2197 : }
2198 : }
2199 : }
2200 :
2201 66 : if (has_noncloned_fkey)
2202 6 : ereport(ERROR,
2203 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2204 : errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2205 : errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2206 : RelationGetRelationName(rInfo->ri_RelationDesc),
2207 : RelationGetRelationName(rootRelInfo->ri_RelationDesc)),
2208 : errhint("Consider defining the foreign key on table \"%s\".",
2209 : RelationGetRelationName(rootRelInfo->ri_RelationDesc))));
2210 : }
2211 :
2212 : /* Perform the root table's triggers. */
2213 270 : ExecARUpdateTriggers(context->estate,
2214 : rootRelInfo, sourcePartInfo, destPartInfo,
2215 : NULL, oldslot, newslot, NIL, NULL, true);
2216 270 : }
2217 :
2218 : /* ----------------------------------------------------------------
2219 : * ExecUpdate
2220 : *
2221 : * note: we can't run UPDATE queries with transactions
2222 : * off because UPDATEs are actually INSERTs and our
2223 : * scan will mistakenly loop forever, updating the tuple
2224 : * it just inserted.. This should be fixed but until it
2225 : * is, we don't want to get stuck in an infinite loop
2226 : * which corrupts your database..
2227 : *
2228 : * When updating a table, tupleid identifies the tuple to
2229 : * update and oldtuple is NULL. When updating a view, oldtuple
2230 : * is passed to the INSTEAD OF triggers and identifies what to
2231 : * update, and tupleid is invalid. When updating a foreign table,
2232 : * tupleid is invalid; the FDW has to figure out which row to
2233 : * update using data from the planSlot. oldtuple is passed to
2234 : * foreign table triggers; it is NULL when the foreign table has
2235 : * no relevant triggers.
2236 : *
2237 : * slot contains the new tuple value to be stored.
2238 : * oldslot is the slot to store the old tuple.
2239 : * planSlot is the output of the ModifyTable's subplan; we use it
2240 : * to access values from other input tables (for RETURNING),
2241 : * row-ID junk columns, etc.
2242 : *
2243 : * Returns RETURNING result if any, otherwise NULL.
2244 : * ----------------------------------------------------------------
2245 : */
2246 : static TupleTableSlot *
2247 312118 : ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2248 : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
2249 : TupleTableSlot *oldslot, bool canSetTag, bool locked)
2250 : {
2251 312118 : EState *estate = context->estate;
2252 312118 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2253 312118 : UpdateContext updateCxt = {0};
2254 : TM_Result result;
2255 :
2256 : /*
2257 : * abort the operation if not running transactions
2258 : */
2259 312118 : if (IsBootstrapProcessingMode())
2260 0 : elog(ERROR, "cannot UPDATE during bootstrap");
2261 :
2262 : /*
2263 : * Prepare for the update. This includes BEFORE ROW triggers, so we're
2264 : * done if it says we are.
2265 : */
2266 312118 : if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
2267 138 : return NULL;
2268 :
2269 : /* INSTEAD OF ROW UPDATE Triggers */
2270 311944 : if (resultRelInfo->ri_TrigDesc &&
2271 5626 : resultRelInfo->ri_TrigDesc->trig_update_instead_row)
2272 : {
2273 114 : if (!ExecIRUpdateTriggers(estate, resultRelInfo,
2274 : oldtuple, slot))
2275 18 : return NULL; /* "do nothing" */
2276 : }
2277 311830 : else if (resultRelInfo->ri_FdwRoutine)
2278 : {
2279 : /* Fill in GENERATEd columns */
2280 142 : ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2281 :
2282 : /*
2283 : * update in foreign table: let the FDW do it
2284 : */
2285 142 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
2286 : resultRelInfo,
2287 : slot,
2288 : context->planSlot);
2289 :
2290 142 : if (slot == NULL) /* "do nothing" */
2291 2 : return NULL;
2292 :
2293 : /*
2294 : * AFTER ROW Triggers or RETURNING expressions might reference the
2295 : * tableoid column, so (re-)initialize tts_tableOid before evaluating
2296 : * them. (This covers the case where the FDW replaced the slot.)
2297 : */
2298 140 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2299 : }
2300 : else
2301 : {
2302 311688 : int options = TABLE_MODIFY_WAIT | TABLE_MODIFY_FETCH_OLD_TUPLE;
2303 :
2304 : /*
2305 : * Specify that we need to lock and fetch the last tuple version for
2306 : * EPQ on appropriate transaction isolation levels if the tuple isn't
2307 : * locked already.
2308 : */
2309 311688 : if (!locked && !IsolationUsesXactSnapshot())
2310 303722 : options |= TABLE_MODIFY_LOCK_UPDATED;
2311 :
2312 : /*
2313 : * If we generate a new candidate tuple after EvalPlanQual testing, we
2314 : * must loop back here to try again. (We don't need to redo triggers,
2315 : * however. If there are any BEFORE triggers then trigger.c will have
2316 : * done table_tuple_lock to lock the correct tuple, so there's no need
2317 : * to do them again.)
2318 : */
2319 311688 : redo_act:
2320 311782 : result = ExecUpdateAct(context, resultRelInfo, tupleid, oldtuple, slot,
2321 : canSetTag, options, oldslot, &updateCxt);
2322 :
2323 : /*
2324 : * If ExecUpdateAct reports that a cross-partition update was done,
2325 : * then the RETURNING tuple (if any) has been projected and there's
2326 : * nothing else for us to do.
2327 : */
2328 311492 : if (updateCxt.crossPartUpdate)
2329 684 : return context->cpUpdateReturningSlot;
2330 :
2331 310808 : switch (result)
2332 : {
2333 92 : case TM_SelfModified:
2334 :
2335 : /*
2336 : * The target tuple was already updated or deleted by the
2337 : * current command, or by a later command in the current
2338 : * transaction. The former case is possible in a join UPDATE
2339 : * where multiple tuples join to the same target tuple. This
2340 : * is pretty questionable, but Postgres has always allowed it:
2341 : * we just execute the first update action and ignore
2342 : * additional update attempts.
2343 : *
2344 : * The latter case arises if the tuple is modified by a
2345 : * command in a BEFORE trigger, or perhaps by a command in a
2346 : * volatile function used in the query. In such situations we
2347 : * should not ignore the update, but it is equally unsafe to
2348 : * proceed. We don't want to discard the original UPDATE
2349 : * while keeping the triggered actions based on it; and we
2350 : * have no principled way to merge this update with the
2351 : * previous ones. So throwing an error is the only safe
2352 : * course.
2353 : *
2354 : * If a trigger actually intends this type of interaction, it
2355 : * can re-execute the UPDATE (assuming it can figure out how)
2356 : * and then return NULL to cancel the outer update.
2357 : */
2358 92 : if (context->tmfd.cmax != estate->es_output_cid)
2359 8 : ereport(ERROR,
2360 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2361 : errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2362 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2363 :
2364 : /* Else, already updated by self; nothing to do */
2365 84 : return NULL;
2366 :
2367 310574 : case TM_Ok:
2368 310574 : break;
2369 :
2370 132 : case TM_Updated:
2371 : {
2372 : TupleTableSlot *epqslot;
2373 :
2374 132 : if (IsolationUsesXactSnapshot())
2375 4 : ereport(ERROR,
2376 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2377 : errmsg("could not serialize access due to concurrent update")));
2378 :
2379 : /* Shouldn't get there if the tuple was previously locked */
2380 : Assert(!locked);
2381 :
2382 : /*
2383 : * We need to do EPQ. The latest tuple is already found
2384 : * and locked as a result of TABLE_MODIFY_LOCK_UPDATED.
2385 : */
2386 : Assert(context->tmfd.traversed);
2387 128 : epqslot = EvalPlanQual(context->epqstate,
2388 : resultRelationDesc,
2389 : resultRelInfo->ri_RangeTableIndex,
2390 : oldslot);
2391 128 : if (TupIsNull(epqslot))
2392 : /* Tuple not passing quals anymore, exiting... */
2393 34 : return NULL;
2394 94 : slot = ExecGetUpdateNewTuple(resultRelInfo,
2395 : epqslot,
2396 : oldslot);
2397 94 : goto redo_act;
2398 : }
2399 :
2400 : break;
2401 :
2402 10 : case TM_Deleted:
2403 10 : if (IsolationUsesXactSnapshot())
2404 0 : ereport(ERROR,
2405 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2406 : errmsg("could not serialize access due to concurrent delete")));
2407 : /* tuple already deleted; nothing to do */
2408 10 : return NULL;
2409 :
2410 0 : default:
2411 0 : elog(ERROR, "unrecognized table_tuple_update status: %u",
2412 : result);
2413 : return NULL;
2414 : }
2415 : }
2416 :
2417 310804 : if (canSetTag)
2418 310212 : (estate->es_processed)++;
2419 :
2420 310804 : ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, tupleid, oldtuple,
2421 : slot, oldslot);
2422 :
2423 : /* Process RETURNING if present */
2424 310716 : if (resultRelInfo->ri_projectReturning)
2425 2154 : return ExecProcessReturning(resultRelInfo, slot, context->planSlot);
2426 :
2427 308562 : return NULL;
2428 : }
2429 :
2430 : /*
2431 : * ExecOnConflictUpdate --- execute UPDATE of INSERT ON CONFLICT DO UPDATE
2432 : *
2433 : * Try to lock tuple for update as part of speculative insertion. If
2434 : * a qual originating from ON CONFLICT DO UPDATE is satisfied, update
2435 : * (but still lock row, even though it may not satisfy estate's
2436 : * snapshot).
2437 : *
2438 : * Returns true if we're done (with or without an update), or false if
2439 : * the caller must retry the INSERT from scratch.
2440 : */
2441 : static bool
2442 5200 : ExecOnConflictUpdate(ModifyTableContext *context,
2443 : ResultRelInfo *resultRelInfo,
2444 : ItemPointer conflictTid,
2445 : TupleTableSlot *excludedSlot,
2446 : bool canSetTag,
2447 : TupleTableSlot **returning)
2448 : {
2449 5200 : ModifyTableState *mtstate = context->mtstate;
2450 5200 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
2451 5200 : Relation relation = resultRelInfo->ri_RelationDesc;
2452 5200 : ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
2453 5200 : TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
2454 : TM_FailureData tmfd;
2455 : LockTupleMode lockmode;
2456 : TM_Result test;
2457 : Datum xminDatum;
2458 : TransactionId xmin;
2459 : bool isnull;
2460 :
2461 : /* Determine lock mode to use */
2462 5200 : lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
2463 :
2464 : /*
2465 : * Lock tuple for update. Don't follow updates when tuple cannot be
2466 : * locked without doing so. A row locking conflict here means our
2467 : * previous conclusion that the tuple is conclusively committed is not
2468 : * true anymore.
2469 : */
2470 5200 : test = table_tuple_lock(relation, conflictTid,
2471 5200 : context->estate->es_snapshot,
2472 5200 : existing, context->estate->es_output_cid,
2473 : lockmode, LockWaitBlock, 0,
2474 : &tmfd);
2475 5200 : switch (test)
2476 : {
2477 5176 : case TM_Ok:
2478 : /* success! */
2479 5176 : break;
2480 :
2481 24 : case TM_Invisible:
2482 :
2483 : /*
2484 : * This can occur when a just inserted tuple is updated again in
2485 : * the same command. E.g. because multiple rows with the same
2486 : * conflicting key values are inserted.
2487 : *
2488 : * This is somewhat similar to the ExecUpdate() TM_SelfModified
2489 : * case. We do not want to proceed because it would lead to the
2490 : * same row being updated a second time in some unspecified order,
2491 : * and in contrast to plain UPDATEs there's no historical behavior
2492 : * to break.
2493 : *
2494 : * It is the user's responsibility to prevent this situation from
2495 : * occurring. These problems are why the SQL standard similarly
2496 : * specifies that for SQL MERGE, an exception must be raised in
2497 : * the event of an attempt to update the same row twice.
2498 : */
2499 24 : xminDatum = slot_getsysattr(existing,
2500 : MinTransactionIdAttributeNumber,
2501 : &isnull);
2502 : Assert(!isnull);
2503 24 : xmin = DatumGetTransactionId(xminDatum);
2504 :
2505 24 : if (TransactionIdIsCurrentTransactionId(xmin))
2506 24 : ereport(ERROR,
2507 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
2508 : /* translator: %s is a SQL command name */
2509 : errmsg("%s command cannot affect row a second time",
2510 : "ON CONFLICT DO UPDATE"),
2511 : errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
2512 :
2513 : /* This shouldn't happen */
2514 0 : elog(ERROR, "attempted to lock invisible tuple");
2515 : break;
2516 :
2517 0 : case TM_SelfModified:
2518 :
2519 : /*
2520 : * This state should never be reached. As a dirty snapshot is used
2521 : * to find conflicting tuples, speculative insertion wouldn't have
2522 : * seen this row to conflict with.
2523 : */
2524 0 : elog(ERROR, "unexpected self-updated tuple");
2525 : break;
2526 :
2527 0 : case TM_Updated:
2528 0 : if (IsolationUsesXactSnapshot())
2529 0 : ereport(ERROR,
2530 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2531 : errmsg("could not serialize access due to concurrent update")));
2532 :
2533 : /*
2534 : * As long as we don't support an UPDATE of INSERT ON CONFLICT for
2535 : * a partitioned table we shouldn't reach to a case where tuple to
2536 : * be lock is moved to another partition due to concurrent update
2537 : * of the partition key.
2538 : */
2539 : Assert(!ItemPointerIndicatesMovedPartitions(&tmfd.ctid));
2540 :
2541 : /*
2542 : * Tell caller to try again from the very start.
2543 : *
2544 : * It does not make sense to use the usual EvalPlanQual() style
2545 : * loop here, as the new version of the row might not conflict
2546 : * anymore, or the conflicting tuple has actually been deleted.
2547 : */
2548 0 : ExecClearTuple(existing);
2549 0 : return false;
2550 :
2551 0 : case TM_Deleted:
2552 0 : if (IsolationUsesXactSnapshot())
2553 0 : ereport(ERROR,
2554 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2555 : errmsg("could not serialize access due to concurrent delete")));
2556 :
2557 : /* see TM_Updated case */
2558 : Assert(!ItemPointerIndicatesMovedPartitions(&tmfd.ctid));
2559 0 : ExecClearTuple(existing);
2560 0 : return false;
2561 :
2562 0 : default:
2563 0 : elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
2564 : }
2565 :
2566 : /* Success, the tuple is locked. */
2567 :
2568 : /*
2569 : * Verify that the tuple is visible to our MVCC snapshot if the current
2570 : * isolation level mandates that.
2571 : *
2572 : * It's not sufficient to rely on the check within ExecUpdate() as e.g.
2573 : * CONFLICT ... WHERE clause may prevent us from reaching that.
2574 : *
2575 : * This means we only ever continue when a new command in the current
2576 : * transaction could see the row, even though in READ COMMITTED mode the
2577 : * tuple will not be visible according to the current statement's
2578 : * snapshot. This is in line with the way UPDATE deals with newer tuple
2579 : * versions.
2580 : */
2581 5176 : ExecCheckTupleVisible(context->estate, relation, existing);
2582 :
2583 : /*
2584 : * Make tuple and any needed join variables available to ExecQual and
2585 : * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
2586 : * the target's existing tuple is installed in the scantuple. EXCLUDED
2587 : * has been made to reference INNER_VAR in setrefs.c, but there is no
2588 : * other redirection.
2589 : */
2590 5176 : econtext->ecxt_scantuple = existing;
2591 5176 : econtext->ecxt_innertuple = excludedSlot;
2592 5176 : econtext->ecxt_outertuple = NULL;
2593 :
2594 5176 : if (!ExecQual(onConflictSetWhere, econtext))
2595 : {
2596 32 : ExecClearTuple(existing); /* see return below */
2597 32 : InstrCountFiltered1(&mtstate->ps, 1);
2598 32 : return true; /* done with the tuple */
2599 : }
2600 :
2601 5144 : if (resultRelInfo->ri_WithCheckOptions != NIL)
2602 : {
2603 : /*
2604 : * Check target's existing tuple against UPDATE-applicable USING
2605 : * security barrier quals (if any), enforced here as RLS checks/WCOs.
2606 : *
2607 : * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
2608 : * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
2609 : * but that's almost the extent of its special handling for ON
2610 : * CONFLICT DO UPDATE.
2611 : *
2612 : * The rewriter will also have associated UPDATE applicable straight
2613 : * RLS checks/WCOs for the benefit of the ExecUpdate() call that
2614 : * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
2615 : * kinds, so there is no danger of spurious over-enforcement in the
2616 : * INSERT or UPDATE path.
2617 : */
2618 60 : ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo,
2619 : existing,
2620 : mtstate->ps.state);
2621 : }
2622 :
2623 : /* Project the new tuple version */
2624 5120 : ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
2625 :
2626 : /*
2627 : * Note that it is possible that the target tuple has been modified in
2628 : * this session, after the above table_tuple_lock. We choose to not error
2629 : * out in that case, in line with ExecUpdate's treatment of similar cases.
2630 : * This can happen if an UPDATE is triggered from within ExecQual(),
2631 : * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
2632 : * wCTE in the ON CONFLICT's SET.
2633 : */
2634 :
2635 : /* Execute UPDATE with projection */
2636 10210 : *returning = ExecUpdate(context, resultRelInfo,
2637 : conflictTid, NULL,
2638 5120 : resultRelInfo->ri_onConflict->oc_ProjSlot,
2639 : existing,
2640 : canSetTag, true);
2641 :
2642 : /*
2643 : * Clear out existing tuple, as there might not be another conflict among
2644 : * the next input rows. Don't want to hold resources till the end of the
2645 : * query.
2646 : */
2647 5090 : ExecClearTuple(existing);
2648 5090 : return true;
2649 : }
2650 :
2651 : /*
2652 : * Perform MERGE.
2653 : */
2654 : static TupleTableSlot *
2655 5834 : ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2656 : ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
2657 : {
2658 5834 : TupleTableSlot *rslot = NULL;
2659 : bool matched;
2660 :
2661 : /*-----
2662 : * If we are dealing with a WHEN MATCHED case (tupleid or oldtuple is
2663 : * valid, depending on whether the result relation is a table or a view),
2664 : * we execute the first action for which the additional WHEN MATCHED AND
2665 : * quals pass. If an action without quals is found, that action is
2666 : * executed.
2667 : *
2668 : * Similarly, if we are dealing with WHEN NOT MATCHED case, we look at
2669 : * the given WHEN NOT MATCHED actions in sequence until one passes.
2670 : *
2671 : * Things get interesting in case of concurrent update/delete of the
2672 : * target tuple. Such concurrent update/delete is detected while we are
2673 : * executing a WHEN MATCHED action.
2674 : *
2675 : * A concurrent update can:
2676 : *
2677 : * 1. modify the target tuple so that it no longer satisfies the
2678 : * additional quals attached to the current WHEN MATCHED action
2679 : *
2680 : * In this case, we are still dealing with a WHEN MATCHED case.
2681 : * We recheck the list of WHEN MATCHED actions from the start and
2682 : * choose the first one that satisfies the new target tuple.
2683 : *
2684 : * 2. modify the target tuple so that the join quals no longer pass and
2685 : * hence the source tuple no longer has a match.
2686 : *
2687 : * In this case, the source tuple no longer matches the target tuple,
2688 : * so we now instead find a qualifying WHEN NOT MATCHED action to
2689 : * execute.
2690 : *
2691 : * XXX Hmmm, what if the updated tuple would now match one that was
2692 : * considered NOT MATCHED so far?
2693 : *
2694 : * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED.
2695 : *
2696 : * ExecMergeMatched takes care of following the update chain and
2697 : * re-finding the qualifying WHEN MATCHED action, as long as the updated
2698 : * target tuple still satisfies the join quals, i.e., it remains a WHEN
2699 : * MATCHED case. If the tuple gets deleted or the join quals fail, it
2700 : * returns and we try ExecMergeNotMatched. Given that ExecMergeMatched
2701 : * always make progress by following the update chain and we never switch
2702 : * from ExecMergeNotMatched to ExecMergeMatched, there is no risk of a
2703 : * livelock.
2704 : */
2705 5834 : matched = tupleid != NULL || oldtuple != NULL;
2706 5834 : if (matched)
2707 3526 : rslot = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
2708 : canSetTag, &matched);
2709 :
2710 : /*
2711 : * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
2712 : * join, or a previously MATCHED tuple for which ExecMergeMatched() set
2713 : * "matched" to false, indicating that it no longer matches).
2714 : */
2715 5744 : if (!matched)
2716 2324 : rslot = ExecMergeNotMatched(context, resultRelInfo, canSetTag);
2717 :
2718 5690 : return rslot;
2719 : }
2720 :
2721 : /*
2722 : * Check and execute the first qualifying MATCHED action. If the target
2723 : * relation is a table, the current target tuple is identified by tupleid.
2724 : * Otherwise, if the target relation is a view, oldtuple is the current target
2725 : * tuple from the view.
2726 : *
2727 : * We start from the first WHEN MATCHED action and check if the WHEN quals
2728 : * pass, if any. If the WHEN quals for the first action do not pass, we
2729 : * check the second, then the third and so on. If we reach to the end, no
2730 : * action is taken and "matched" is set to true, indicating that no further
2731 : * action is required for this tuple.
2732 : *
2733 : * If we do find a qualifying action, then we attempt to execute the action.
2734 : *
2735 : * If the tuple is concurrently updated, EvalPlanQual is run with the updated
2736 : * tuple to recheck the join quals. Note that the additional quals associated
2737 : * with individual actions are evaluated by this routine via ExecQual, while
2738 : * EvalPlanQual checks for the join quals. If EvalPlanQual tells us that the
2739 : * updated tuple still passes the join quals, then we restart from the first
2740 : * action to look for a qualifying action. Otherwise, "matched" is set to
2741 : * false -- meaning that a NOT MATCHED action must now be executed for the
2742 : * current source tuple.
2743 : */
2744 : static TupleTableSlot *
2745 3526 : ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2746 : ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag,
2747 : bool *matched)
2748 : {
2749 3526 : ModifyTableState *mtstate = context->mtstate;
2750 3526 : TupleTableSlot *newslot = NULL;
2751 3526 : TupleTableSlot *rslot = NULL;
2752 3526 : EState *estate = context->estate;
2753 3526 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
2754 : bool isNull;
2755 3526 : EPQState *epqstate = &mtstate->mt_epqstate;
2756 : ListCell *l;
2757 :
2758 : /*
2759 : * If there are no WHEN MATCHED actions, we are done.
2760 : */
2761 3526 : if (resultRelInfo->ri_matchedMergeAction == NIL)
2762 : {
2763 528 : *matched = true;
2764 528 : return NULL;
2765 : }
2766 :
2767 : /*
2768 : * Make tuple and any needed join variables available to ExecQual and
2769 : * ExecProject. The target's existing tuple is installed in the scantuple.
2770 : * Again, this target relation's slot is required only in the case of a
2771 : * MATCHED tuple and UPDATE/DELETE actions.
2772 : */
2773 2998 : econtext->ecxt_scantuple = resultRelInfo->ri_oldTupleSlot;
2774 2998 : econtext->ecxt_innertuple = context->planSlot;
2775 2998 : econtext->ecxt_outertuple = NULL;
2776 :
2777 : /*
2778 : * This routine is only invoked for matched rows, so we should either have
2779 : * the tupleid of the target row, or an old tuple from the target wholerow
2780 : * junk attr.
2781 : */
2782 : Assert(tupleid != NULL || oldtuple != NULL);
2783 2998 : if (oldtuple != NULL)
2784 66 : ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
2785 : false);
2786 :
2787 2998 : lmerge_matched:
2788 :
2789 : /*
2790 : * If passed a tupleid, use it to fetch the old target row.
2791 : *
2792 : * We use SnapshotAny for this because we might get called again after
2793 : * EvalPlanQual returns us a new tuple, which may not be visible to our
2794 : * MVCC snapshot.
2795 : */
2796 3054 : if (tupleid != NULL)
2797 : {
2798 2988 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
2799 : tupleid,
2800 : SnapshotAny,
2801 : resultRelInfo->ri_oldTupleSlot))
2802 0 : elog(ERROR, "failed to fetch the target tuple");
2803 : }
2804 :
2805 4502 : foreach(l, resultRelInfo->ri_matchedMergeAction)
2806 : {
2807 3758 : MergeActionState *relaction = (MergeActionState *) lfirst(l);
2808 3758 : CmdType commandType = relaction->mas_action->commandType;
2809 : TM_Result result;
2810 3758 : UpdateContext updateCxt = {0};
2811 :
2812 : /*
2813 : * Test condition, if any.
2814 : *
2815 : * In the absence of any condition, we perform the action
2816 : * unconditionally (no need to check separately since ExecQual() will
2817 : * return true if there are no conditions to evaluate).
2818 : */
2819 3758 : if (!ExecQual(relaction->mas_whenqual, econtext))
2820 1448 : continue;
2821 :
2822 : /*
2823 : * Check if the existing target tuple meets the USING checks of
2824 : * UPDATE/DELETE RLS policies. If those checks fail, we throw an
2825 : * error.
2826 : *
2827 : * The WITH CHECK quals for UPDATE RLS policies are applied in
2828 : * ExecUpdateAct() and hence we need not do anything special to handle
2829 : * them.
2830 : *
2831 : * NOTE: We must do this after WHEN quals are evaluated, so that we
2832 : * check policies only when they matter.
2833 : */
2834 2310 : if (resultRelInfo->ri_WithCheckOptions && commandType != CMD_NOTHING)
2835 : {
2836 90 : ExecWithCheckOptions(commandType == CMD_UPDATE ?
2837 : WCO_RLS_MERGE_UPDATE_CHECK : WCO_RLS_MERGE_DELETE_CHECK,
2838 : resultRelInfo,
2839 : resultRelInfo->ri_oldTupleSlot,
2840 90 : context->mtstate->ps.state);
2841 : }
2842 :
2843 : /* Perform stated action */
2844 2286 : switch (commandType)
2845 : {
2846 1934 : case CMD_UPDATE:
2847 :
2848 : /*
2849 : * Project the output tuple, and use that to update the table.
2850 : * We don't need to filter out junk attributes, because the
2851 : * UPDATE action's targetlist doesn't have any.
2852 : */
2853 1934 : newslot = ExecProject(relaction->mas_proj);
2854 :
2855 1934 : mtstate->mt_merge_action = relaction;
2856 1934 : if (!ExecUpdatePrologue(context, resultRelInfo,
2857 : tupleid, NULL, newslot, &result))
2858 : {
2859 18 : if (result == TM_Ok)
2860 : {
2861 6 : *matched = true;
2862 144 : return NULL; /* "do nothing" */
2863 : }
2864 12 : break; /* concurrent update/delete */
2865 : }
2866 :
2867 : /* INSTEAD OF ROW UPDATE Triggers */
2868 1916 : if (resultRelInfo->ri_TrigDesc &&
2869 292 : resultRelInfo->ri_TrigDesc->trig_update_instead_row)
2870 : {
2871 60 : if (!ExecIRUpdateTriggers(estate, resultRelInfo,
2872 : oldtuple, newslot))
2873 : {
2874 0 : *matched = true;
2875 0 : return NULL; /* "do nothing" */
2876 : }
2877 : }
2878 : else
2879 : {
2880 1856 : result = ExecUpdateAct(context, resultRelInfo, tupleid,
2881 : NULL, newslot, canSetTag,
2882 : TABLE_MODIFY_WAIT, NULL,
2883 : &updateCxt);
2884 :
2885 : /*
2886 : * As in ExecUpdate(), if ExecUpdateAct() reports that a
2887 : * cross-partition update was done, then there's nothing
2888 : * else for us to do --- the UPDATE has been turned into a
2889 : * DELETE and an INSERT, and we must not perform any of
2890 : * the usual post-update tasks. Also, the RETURNING tuple
2891 : * (if any) has been projected, so we can just return
2892 : * that.
2893 : */
2894 1836 : if (updateCxt.crossPartUpdate)
2895 : {
2896 116 : mtstate->mt_merge_updated += 1;
2897 116 : *matched = true;
2898 116 : return context->cpUpdateReturningSlot;
2899 : }
2900 : }
2901 :
2902 1780 : if (result == TM_Ok)
2903 : {
2904 1710 : ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
2905 : tupleid, NULL, newslot,
2906 : resultRelInfo->ri_oldTupleSlot);
2907 1698 : mtstate->mt_merge_updated += 1;
2908 : }
2909 1768 : break;
2910 :
2911 334 : case CMD_DELETE:
2912 334 : mtstate->mt_merge_action = relaction;
2913 334 : if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
2914 : NULL, NULL, &result))
2915 : {
2916 12 : if (result == TM_Ok)
2917 : {
2918 6 : *matched = true;
2919 6 : return NULL; /* "do nothing" */
2920 : }
2921 6 : break; /* concurrent update/delete */
2922 : }
2923 :
2924 : /* INSTEAD OF ROW DELETE Triggers */
2925 322 : if (resultRelInfo->ri_TrigDesc &&
2926 38 : resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
2927 : {
2928 6 : if (!ExecIRDeleteTriggers(estate, resultRelInfo,
2929 : oldtuple))
2930 : {
2931 0 : *matched = true;
2932 0 : return NULL; /* "do nothing" */
2933 : }
2934 : }
2935 : else
2936 316 : result = ExecDeleteAct(context, resultRelInfo, tupleid,
2937 : false, TABLE_MODIFY_WAIT, NULL);
2938 :
2939 322 : if (result == TM_Ok)
2940 : {
2941 304 : ExecDeleteEpilogue(context, resultRelInfo, tupleid, NULL,
2942 : resultRelInfo->ri_oldTupleSlot, false);
2943 304 : mtstate->mt_merge_deleted += 1;
2944 : }
2945 322 : break;
2946 :
2947 18 : case CMD_NOTHING:
2948 : /* Doing nothing is always OK */
2949 18 : result = TM_Ok;
2950 18 : break;
2951 :
2952 0 : default:
2953 0 : elog(ERROR, "unknown action in MERGE WHEN MATCHED clause");
2954 : }
2955 :
2956 2126 : switch (result)
2957 : {
2958 2020 : case TM_Ok:
2959 : /* all good; perform final actions */
2960 2020 : if (canSetTag && commandType != CMD_NOTHING)
2961 1984 : (estate->es_processed)++;
2962 :
2963 2020 : break;
2964 :
2965 32 : case TM_SelfModified:
2966 :
2967 : /*
2968 : * The target tuple was already updated or deleted by the
2969 : * current command, or by a later command in the current
2970 : * transaction. The former case is explicitly disallowed by
2971 : * the SQL standard for MERGE, which insists that the MERGE
2972 : * join condition should not join a target row to more than
2973 : * one source row.
2974 : *
2975 : * The latter case arises if the tuple is modified by a
2976 : * command in a BEFORE trigger, or perhaps by a command in a
2977 : * volatile function used in the query. In such situations we
2978 : * should not ignore the MERGE action, but it is equally
2979 : * unsafe to proceed. We don't want to discard the original
2980 : * MERGE action while keeping the triggered actions based on
2981 : * it; and it would be no better to allow the original MERGE
2982 : * action while discarding the updates that it triggered. So
2983 : * throwing an error is the only safe course.
2984 : */
2985 32 : if (context->tmfd.cmax != estate->es_output_cid)
2986 12 : ereport(ERROR,
2987 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2988 : errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
2989 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2990 :
2991 20 : if (TransactionIdIsCurrentTransactionId(context->tmfd.xmax))
2992 20 : ereport(ERROR,
2993 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
2994 : /* translator: %s is a SQL command name */
2995 : errmsg("%s command cannot affect row a second time",
2996 : "MERGE"),
2997 : errhint("Ensure that not more than one source row matches any one target row.")));
2998 :
2999 : /* This shouldn't happen */
3000 0 : elog(ERROR, "attempted to update or delete invisible tuple");
3001 : break;
3002 :
3003 10 : case TM_Deleted:
3004 10 : if (IsolationUsesXactSnapshot())
3005 0 : ereport(ERROR,
3006 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3007 : errmsg("could not serialize access due to concurrent delete")));
3008 :
3009 : /*
3010 : * If the tuple was already deleted, return to let caller
3011 : * handle it under NOT MATCHED clauses.
3012 : */
3013 10 : *matched = false;
3014 10 : return NULL;
3015 :
3016 64 : case TM_Updated:
3017 : {
3018 : Relation resultRelationDesc;
3019 : TupleTableSlot *epqslot,
3020 : *inputslot;
3021 : LockTupleMode lockmode;
3022 :
3023 : /*
3024 : * The target tuple was concurrently updated by some other
3025 : * transaction. Run EvalPlanQual() with the new version of
3026 : * the tuple. If it does not return a tuple, then we
3027 : * switch to the NOT MATCHED list of actions. If it does
3028 : * return a tuple and the join qual is still satisfied,
3029 : * then we just need to recheck the MATCHED actions,
3030 : * starting from the top, and execute the first qualifying
3031 : * action.
3032 : */
3033 64 : resultRelationDesc = resultRelInfo->ri_RelationDesc;
3034 64 : lockmode = ExecUpdateLockMode(estate, resultRelInfo);
3035 :
3036 64 : inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
3037 : resultRelInfo->ri_RangeTableIndex);
3038 :
3039 64 : result = table_tuple_lock(resultRelationDesc, tupleid,
3040 : estate->es_snapshot,
3041 : inputslot, estate->es_output_cid,
3042 : lockmode, LockWaitBlock,
3043 : TUPLE_LOCK_FLAG_FIND_LAST_VERSION,
3044 : &context->tmfd);
3045 64 : switch (result)
3046 : {
3047 62 : case TM_Ok:
3048 62 : epqslot = EvalPlanQual(epqstate,
3049 : resultRelationDesc,
3050 : resultRelInfo->ri_RangeTableIndex,
3051 : inputslot);
3052 :
3053 : /*
3054 : * If we got no tuple, or the tuple we get has a
3055 : * NULL ctid, go back to caller: this one is not a
3056 : * MATCHED tuple anymore, so they can retry with
3057 : * NOT MATCHED actions.
3058 : */
3059 62 : if (TupIsNull(epqslot))
3060 : {
3061 0 : *matched = false;
3062 0 : return NULL;
3063 : }
3064 :
3065 62 : (void) ExecGetJunkAttribute(epqslot,
3066 62 : resultRelInfo->ri_RowIdAttNo,
3067 : &isNull);
3068 62 : if (isNull)
3069 : {
3070 6 : *matched = false;
3071 6 : return NULL;
3072 : }
3073 :
3074 : /*
3075 : * When a tuple was updated and migrated to
3076 : * another partition concurrently, the current
3077 : * MERGE implementation can't follow. There's
3078 : * probably a better way to handle this case, but
3079 : * it'd require recognizing the relation to which
3080 : * the tuple moved, and setting our current
3081 : * resultRelInfo to that.
3082 : */
3083 56 : if (ItemPointerIndicatesMovedPartitions(&context->tmfd.ctid))
3084 0 : ereport(ERROR,
3085 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3086 : errmsg("tuple to be deleted was already moved to another partition due to concurrent update")));
3087 :
3088 : /*
3089 : * A non-NULL ctid means that we are still dealing
3090 : * with MATCHED case. Restart the loop so that we
3091 : * apply all the MATCHED rules again, to ensure
3092 : * that the first qualifying WHEN MATCHED action
3093 : * is executed.
3094 : *
3095 : * Update tupleid to that of the new tuple, for
3096 : * the refetch we do at the top.
3097 : */
3098 56 : ItemPointerCopy(&context->tmfd.ctid, tupleid);
3099 56 : goto lmerge_matched;
3100 :
3101 0 : case TM_Deleted:
3102 :
3103 : /*
3104 : * tuple already deleted; tell caller to run NOT
3105 : * MATCHED actions
3106 : */
3107 0 : *matched = false;
3108 0 : return NULL;
3109 :
3110 2 : case TM_SelfModified:
3111 :
3112 : /*
3113 : * This can be reached when following an update
3114 : * chain from a tuple updated by another session,
3115 : * reaching a tuple that was already updated or
3116 : * deleted by the current command, or by a later
3117 : * command in the current transaction. As above,
3118 : * this should always be treated as an error.
3119 : */
3120 2 : if (context->tmfd.cmax != estate->es_output_cid)
3121 0 : ereport(ERROR,
3122 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3123 : errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3124 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3125 :
3126 2 : if (TransactionIdIsCurrentTransactionId(context->tmfd.xmax))
3127 2 : ereport(ERROR,
3128 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
3129 : /* translator: %s is a SQL command name */
3130 : errmsg("%s command cannot affect row a second time",
3131 : "MERGE"),
3132 : errhint("Ensure that not more than one source row matches any one target row.")));
3133 :
3134 : /* This shouldn't happen */
3135 0 : elog(ERROR, "attempted to update or delete invisible tuple");
3136 : return NULL;
3137 :
3138 0 : default:
3139 : /* see table_tuple_lock call in ExecDelete() */
3140 0 : elog(ERROR, "unexpected table_tuple_lock status: %u",
3141 : result);
3142 : return NULL;
3143 : }
3144 : }
3145 :
3146 0 : case TM_Invisible:
3147 : case TM_WouldBlock:
3148 : case TM_BeingModified:
3149 : /* these should not occur */
3150 0 : elog(ERROR, "unexpected tuple operation result: %d", result);
3151 : break;
3152 : }
3153 :
3154 : /* Process RETURNING if present */
3155 2020 : if (resultRelInfo->ri_projectReturning)
3156 : {
3157 150 : switch (commandType)
3158 : {
3159 96 : case CMD_UPDATE:
3160 96 : rslot = ExecProcessReturning(resultRelInfo, newslot,
3161 : context->planSlot);
3162 96 : break;
3163 :
3164 54 : case CMD_DELETE:
3165 54 : rslot = ExecProcessReturning(resultRelInfo,
3166 : resultRelInfo->ri_oldTupleSlot,
3167 : context->planSlot);
3168 54 : break;
3169 :
3170 0 : case CMD_NOTHING:
3171 0 : break;
3172 :
3173 0 : default:
3174 0 : elog(ERROR, "unrecognized commandType: %d",
3175 : (int) commandType);
3176 : }
3177 : }
3178 :
3179 : /*
3180 : * We've activated one of the WHEN clauses, so we don't search
3181 : * further. This is required behaviour, not an optimization.
3182 : */
3183 2020 : break;
3184 : }
3185 :
3186 : /*
3187 : * Successfully executed an action or no qualifying action was found.
3188 : */
3189 2764 : *matched = true;
3190 :
3191 2764 : return rslot;
3192 : }
3193 :
3194 : /*
3195 : * Execute the first qualifying NOT MATCHED action.
3196 : */
3197 : static TupleTableSlot *
3198 2324 : ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
3199 : bool canSetTag)
3200 : {
3201 2324 : ModifyTableState *mtstate = context->mtstate;
3202 2324 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
3203 2324 : List *actionStates = NIL;
3204 2324 : TupleTableSlot *rslot = NULL;
3205 : ListCell *l;
3206 :
3207 : /*
3208 : * For INSERT actions, the root relation's merge action is OK since the
3209 : * INSERT's targetlist and the WHEN conditions can only refer to the
3210 : * source relation and hence it does not matter which result relation we
3211 : * work with.
3212 : *
3213 : * XXX does this mean that we can avoid creating copies of actionStates on
3214 : * partitioned tables, for not-matched actions?
3215 : */
3216 2324 : actionStates = resultRelInfo->ri_notMatchedMergeAction;
3217 :
3218 : /*
3219 : * Make source tuple available to ExecQual and ExecProject. We don't need
3220 : * the target tuple, since the WHEN quals and targetlist can't refer to
3221 : * the target columns.
3222 : */
3223 2324 : econtext->ecxt_scantuple = NULL;
3224 2324 : econtext->ecxt_innertuple = context->planSlot;
3225 2324 : econtext->ecxt_outertuple = NULL;
3226 :
3227 2906 : foreach(l, actionStates)
3228 : {
3229 2324 : MergeActionState *action = (MergeActionState *) lfirst(l);
3230 2324 : CmdType commandType = action->mas_action->commandType;
3231 : TupleTableSlot *newslot;
3232 :
3233 : /*
3234 : * Test condition, if any.
3235 : *
3236 : * In the absence of any condition, we perform the action
3237 : * unconditionally (no need to check separately since ExecQual() will
3238 : * return true if there are no conditions to evaluate).
3239 : */
3240 2324 : if (!ExecQual(action->mas_whenqual, econtext))
3241 582 : continue;
3242 :
3243 : /* Perform stated action */
3244 1742 : switch (commandType)
3245 : {
3246 1742 : case CMD_INSERT:
3247 :
3248 : /*
3249 : * Project the tuple. In case of a partitioned table, the
3250 : * projection was already built to use the root's descriptor,
3251 : * so we don't need to map the tuple here.
3252 : */
3253 1742 : newslot = ExecProject(action->mas_proj);
3254 1742 : mtstate->mt_merge_action = action;
3255 :
3256 1742 : rslot = ExecInsert(context, mtstate->rootResultRelInfo,
3257 : newslot, canSetTag, NULL, NULL);
3258 1688 : mtstate->mt_merge_inserted += 1;
3259 1688 : break;
3260 0 : case CMD_NOTHING:
3261 : /* Do nothing */
3262 0 : break;
3263 0 : default:
3264 0 : elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
3265 : }
3266 :
3267 : /*
3268 : * We've activated one of the WHEN clauses, so we don't search
3269 : * further. This is required behaviour, not an optimization.
3270 : */
3271 1688 : break;
3272 : }
3273 :
3274 2270 : return rslot;
3275 : }
3276 :
3277 : /*
3278 : * Initialize state for execution of MERGE.
3279 : */
3280 : void
3281 1324 : ExecInitMerge(ModifyTableState *mtstate, EState *estate)
3282 : {
3283 1324 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
3284 1324 : ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
3285 : ResultRelInfo *resultRelInfo;
3286 : ExprContext *econtext;
3287 : ListCell *lc;
3288 : int i;
3289 :
3290 1324 : if (node->mergeActionLists == NIL)
3291 0 : return;
3292 :
3293 1324 : mtstate->mt_merge_subcommands = 0;
3294 :
3295 1324 : if (mtstate->ps.ps_ExprContext == NULL)
3296 1216 : ExecAssignExprContext(estate, &mtstate->ps);
3297 1324 : econtext = mtstate->ps.ps_ExprContext;
3298 :
3299 : /*
3300 : * Create a MergeActionState for each action on the mergeActionList and
3301 : * add it to either a list of matched actions or not-matched actions.
3302 : *
3303 : * Similar logic appears in ExecInitPartitionInfo(), so if changing
3304 : * anything here, do so there too.
3305 : */
3306 1324 : i = 0;
3307 2838 : foreach(lc, node->mergeActionLists)
3308 : {
3309 1514 : List *mergeActionList = lfirst(lc);
3310 : TupleDesc relationDesc;
3311 : ListCell *l;
3312 :
3313 1514 : resultRelInfo = mtstate->resultRelInfo + i;
3314 1514 : i++;
3315 1514 : relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3316 :
3317 : /* initialize slots for MERGE fetches from this rel */
3318 1514 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3319 1514 : ExecInitMergeTupleSlots(mtstate, resultRelInfo);
3320 :
3321 3952 : foreach(l, mergeActionList)
3322 : {
3323 2438 : MergeAction *action = (MergeAction *) lfirst(l);
3324 : MergeActionState *action_state;
3325 : TupleTableSlot *tgtslot;
3326 : TupleDesc tgtdesc;
3327 : List **list;
3328 :
3329 : /*
3330 : * Build action merge state for this rel. (For partitions,
3331 : * equivalent code exists in ExecInitPartitionInfo.)
3332 : */
3333 2438 : action_state = makeNode(MergeActionState);
3334 2438 : action_state->mas_action = action;
3335 2438 : action_state->mas_whenqual = ExecInitQual((List *) action->qual,
3336 : &mtstate->ps);
3337 :
3338 : /*
3339 : * We create two lists - one for WHEN MATCHED actions and one for
3340 : * WHEN NOT MATCHED actions - and stick the MergeActionState into
3341 : * the appropriate list.
3342 : */
3343 2438 : if (action_state->mas_action->matched)
3344 1538 : list = &resultRelInfo->ri_matchedMergeAction;
3345 : else
3346 900 : list = &resultRelInfo->ri_notMatchedMergeAction;
3347 2438 : *list = lappend(*list, action_state);
3348 :
3349 2438 : switch (action->commandType)
3350 : {
3351 886 : case CMD_INSERT:
3352 886 : ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
3353 : action->targetList);
3354 :
3355 : /*
3356 : * If the MERGE targets a partitioned table, any INSERT
3357 : * actions must be routed through it, not the child
3358 : * relations. Initialize the routing struct and the root
3359 : * table's "new" tuple slot for that, if not already done.
3360 : * The projection we prepare, for all relations, uses the
3361 : * root relation descriptor, and targets the plan's root
3362 : * slot. (This is consistent with the fact that we
3363 : * checked the plan output to match the root relation,
3364 : * above.)
3365 : */
3366 886 : if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
3367 : RELKIND_PARTITIONED_TABLE)
3368 : {
3369 256 : if (mtstate->mt_partition_tuple_routing == NULL)
3370 : {
3371 : /*
3372 : * Initialize planstate for routing if not already
3373 : * done.
3374 : *
3375 : * Note that the slot is managed as a standalone
3376 : * slot belonging to ModifyTableState, so we pass
3377 : * NULL for the 2nd argument.
3378 : */
3379 124 : mtstate->mt_root_tuple_slot =
3380 124 : table_slot_create(rootRelInfo->ri_RelationDesc,
3381 : NULL);
3382 124 : mtstate->mt_partition_tuple_routing =
3383 124 : ExecSetupPartitionTupleRouting(estate,
3384 : rootRelInfo->ri_RelationDesc);
3385 : }
3386 256 : tgtslot = mtstate->mt_root_tuple_slot;
3387 256 : tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3388 : }
3389 : else
3390 : {
3391 : /* not partitioned? use the stock relation and slot */
3392 630 : tgtslot = resultRelInfo->ri_newTupleSlot;
3393 630 : tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3394 : }
3395 :
3396 886 : action_state->mas_proj =
3397 886 : ExecBuildProjectionInfo(action->targetList, econtext,
3398 : tgtslot,
3399 : &mtstate->ps,
3400 : tgtdesc);
3401 :
3402 886 : mtstate->mt_merge_subcommands |= MERGE_INSERT;
3403 886 : break;
3404 1174 : case CMD_UPDATE:
3405 1174 : action_state->mas_proj =
3406 1174 : ExecBuildUpdateProjection(action->targetList,
3407 : true,
3408 : action->updateColnos,
3409 : relationDesc,
3410 : econtext,
3411 : resultRelInfo->ri_newTupleSlot,
3412 : &mtstate->ps);
3413 1174 : mtstate->mt_merge_subcommands |= MERGE_UPDATE;
3414 1174 : break;
3415 338 : case CMD_DELETE:
3416 338 : mtstate->mt_merge_subcommands |= MERGE_DELETE;
3417 338 : break;
3418 40 : case CMD_NOTHING:
3419 40 : break;
3420 0 : default:
3421 0 : elog(ERROR, "unknown operation");
3422 : break;
3423 : }
3424 : }
3425 : }
3426 : }
3427 :
3428 : /*
3429 : * Initializes the tuple slots in a ResultRelInfo for any MERGE action.
3430 : *
3431 : * We mark 'projectNewInfoValid' even though the projections themselves
3432 : * are not initialized here.
3433 : */
3434 : void
3435 1564 : ExecInitMergeTupleSlots(ModifyTableState *mtstate,
3436 : ResultRelInfo *resultRelInfo)
3437 : {
3438 1564 : EState *estate = mtstate->ps.state;
3439 :
3440 : Assert(!resultRelInfo->ri_projectNewInfoValid);
3441 :
3442 1564 : resultRelInfo->ri_oldTupleSlot =
3443 1564 : table_slot_create(resultRelInfo->ri_RelationDesc,
3444 : &estate->es_tupleTable);
3445 1564 : resultRelInfo->ri_newTupleSlot =
3446 1564 : table_slot_create(resultRelInfo->ri_RelationDesc,
3447 : &estate->es_tupleTable);
3448 1564 : resultRelInfo->ri_projectNewInfoValid = true;
3449 1564 : }
3450 :
3451 : /*
3452 : * Process BEFORE EACH STATEMENT triggers
3453 : */
3454 : static void
3455 115464 : fireBSTriggers(ModifyTableState *node)
3456 : {
3457 115464 : ModifyTable *plan = (ModifyTable *) node->ps.plan;
3458 115464 : ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3459 :
3460 115464 : switch (node->operation)
3461 : {
3462 89238 : case CMD_INSERT:
3463 89238 : ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3464 89226 : if (plan->onConflictAction == ONCONFLICT_UPDATE)
3465 828 : ExecBSUpdateTriggers(node->ps.state,
3466 : resultRelInfo);
3467 89226 : break;
3468 12968 : case CMD_UPDATE:
3469 12968 : ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
3470 12968 : break;
3471 12036 : case CMD_DELETE:
3472 12036 : ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
3473 12036 : break;
3474 1222 : case CMD_MERGE:
3475 1222 : if (node->mt_merge_subcommands & MERGE_INSERT)
3476 682 : ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3477 1222 : if (node->mt_merge_subcommands & MERGE_UPDATE)
3478 876 : ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
3479 1222 : if (node->mt_merge_subcommands & MERGE_DELETE)
3480 290 : ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
3481 1222 : break;
3482 0 : default:
3483 0 : elog(ERROR, "unknown operation");
3484 : break;
3485 : }
3486 115452 : }
3487 :
3488 : /*
3489 : * Process AFTER EACH STATEMENT triggers
3490 : */
3491 : static void
3492 112444 : fireASTriggers(ModifyTableState *node)
3493 : {
3494 112444 : ModifyTable *plan = (ModifyTable *) node->ps.plan;
3495 112444 : ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3496 :
3497 112444 : switch (node->operation)
3498 : {
3499 87078 : case CMD_INSERT:
3500 87078 : if (plan->onConflictAction == ONCONFLICT_UPDATE)
3501 726 : ExecASUpdateTriggers(node->ps.state,
3502 : resultRelInfo,
3503 726 : node->mt_oc_transition_capture);
3504 87078 : ExecASInsertTriggers(node->ps.state, resultRelInfo,
3505 87078 : node->mt_transition_capture);
3506 87078 : break;
3507 12376 : case CMD_UPDATE:
3508 12376 : ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3509 12376 : node->mt_transition_capture);
3510 12376 : break;
3511 11912 : case CMD_DELETE:
3512 11912 : ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3513 11912 : node->mt_transition_capture);
3514 11912 : break;
3515 1078 : case CMD_MERGE:
3516 1078 : if (node->mt_merge_subcommands & MERGE_DELETE)
3517 254 : ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3518 254 : node->mt_transition_capture);
3519 1078 : if (node->mt_merge_subcommands & MERGE_UPDATE)
3520 780 : ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3521 780 : node->mt_transition_capture);
3522 1078 : if (node->mt_merge_subcommands & MERGE_INSERT)
3523 620 : ExecASInsertTriggers(node->ps.state, resultRelInfo,
3524 620 : node->mt_transition_capture);
3525 1078 : break;
3526 0 : default:
3527 0 : elog(ERROR, "unknown operation");
3528 : break;
3529 : }
3530 112444 : }
3531 :
3532 : /*
3533 : * Set up the state needed for collecting transition tuples for AFTER
3534 : * triggers.
3535 : */
3536 : static void
3537 115758 : ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
3538 : {
3539 115758 : ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
3540 115758 : ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
3541 :
3542 : /* Check for transition tables on the directly targeted relation. */
3543 115758 : mtstate->mt_transition_capture =
3544 115758 : MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3545 115758 : RelationGetRelid(targetRelInfo->ri_RelationDesc),
3546 : mtstate->operation);
3547 115758 : if (plan->operation == CMD_INSERT &&
3548 89240 : plan->onConflictAction == ONCONFLICT_UPDATE)
3549 828 : mtstate->mt_oc_transition_capture =
3550 828 : MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3551 828 : RelationGetRelid(targetRelInfo->ri_RelationDesc),
3552 : CMD_UPDATE);
3553 115758 : }
3554 :
3555 : /*
3556 : * ExecPrepareTupleRouting --- prepare for routing one tuple
3557 : *
3558 : * Determine the partition in which the tuple in slot is to be inserted,
3559 : * and return its ResultRelInfo in *partRelInfo. The return value is
3560 : * a slot holding the tuple of the partition rowtype.
3561 : *
3562 : * This also sets the transition table information in mtstate based on the
3563 : * selected partition.
3564 : */
3565 : static TupleTableSlot *
3566 721592 : ExecPrepareTupleRouting(ModifyTableState *mtstate,
3567 : EState *estate,
3568 : PartitionTupleRouting *proute,
3569 : ResultRelInfo *targetRelInfo,
3570 : TupleTableSlot *slot,
3571 : ResultRelInfo **partRelInfo)
3572 : {
3573 : ResultRelInfo *partrel;
3574 : TupleConversionMap *map;
3575 :
3576 : /*
3577 : * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
3578 : * not find a valid partition for the tuple in 'slot' then an error is
3579 : * raised. An error may also be raised if the found partition is not a
3580 : * valid target for INSERTs. This is required since a partitioned table
3581 : * UPDATE to another partition becomes a DELETE+INSERT.
3582 : */
3583 721592 : partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
3584 :
3585 : /*
3586 : * If we're capturing transition tuples, we might need to convert from the
3587 : * partition rowtype to root partitioned table's rowtype. But if there
3588 : * are no BEFORE triggers on the partition that could change the tuple, we
3589 : * can just remember the original unconverted tuple to avoid a needless
3590 : * round trip conversion.
3591 : */
3592 721388 : if (mtstate->mt_transition_capture != NULL)
3593 : {
3594 : bool has_before_insert_row_trig;
3595 :
3596 168 : has_before_insert_row_trig = (partrel->ri_TrigDesc &&
3597 42 : partrel->ri_TrigDesc->trig_insert_before_row);
3598 :
3599 126 : mtstate->mt_transition_capture->tcs_original_insert_tuple =
3600 126 : !has_before_insert_row_trig ? slot : NULL;
3601 : }
3602 :
3603 : /*
3604 : * Convert the tuple, if necessary.
3605 : */
3606 721388 : map = ExecGetRootToChildMap(partrel, estate);
3607 721388 : if (map != NULL)
3608 : {
3609 68392 : TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
3610 :
3611 68392 : slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
3612 : }
3613 :
3614 721388 : *partRelInfo = partrel;
3615 721388 : return slot;
3616 : }
3617 :
3618 : /* ----------------------------------------------------------------
3619 : * ExecModifyTable
3620 : *
3621 : * Perform table modifications as required, and return RETURNING results
3622 : * if needed.
3623 : * ----------------------------------------------------------------
3624 : */
3625 : static TupleTableSlot *
3626 123514 : ExecModifyTable(PlanState *pstate)
3627 : {
3628 123514 : ModifyTableState *node = castNode(ModifyTableState, pstate);
3629 : ModifyTableContext context;
3630 123514 : EState *estate = node->ps.state;
3631 123514 : CmdType operation = node->operation;
3632 : ResultRelInfo *resultRelInfo;
3633 : PlanState *subplanstate;
3634 : TupleTableSlot *slot;
3635 : TupleTableSlot *oldSlot;
3636 : ItemPointerData tuple_ctid;
3637 : HeapTupleData oldtupdata;
3638 : HeapTuple oldtuple;
3639 : ItemPointer tupleid;
3640 :
3641 123514 : CHECK_FOR_INTERRUPTS();
3642 :
3643 : /*
3644 : * This should NOT get called during EvalPlanQual; we should have passed a
3645 : * subplan tree to EvalPlanQual, instead. Use a runtime test not just
3646 : * Assert because this condition is easy to miss in testing. (Note:
3647 : * although ModifyTable should not get executed within an EvalPlanQual
3648 : * operation, we do have to allow it to be initialized and shut down in
3649 : * case it is within a CTE subplan. Hence this test must be here, not in
3650 : * ExecInitModifyTable.)
3651 : */
3652 123514 : if (estate->es_epq_active != NULL)
3653 0 : elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
3654 :
3655 : /*
3656 : * If we've already completed processing, don't try to do more. We need
3657 : * this test because ExecPostprocessPlan might call us an extra time, and
3658 : * our subplan's nodes aren't necessarily robust against being called
3659 : * extra times.
3660 : */
3661 123514 : if (node->mt_done)
3662 782 : return NULL;
3663 :
3664 : /*
3665 : * On first call, fire BEFORE STATEMENT triggers before proceeding.
3666 : */
3667 122732 : if (node->fireBSTriggers)
3668 : {
3669 115464 : fireBSTriggers(node);
3670 115452 : node->fireBSTriggers = false;
3671 : }
3672 :
3673 : /* Preload local variables */
3674 122720 : resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
3675 122720 : subplanstate = outerPlanState(node);
3676 :
3677 : /* Set global context */
3678 122720 : context.mtstate = node;
3679 122720 : context.epqstate = &node->mt_epqstate;
3680 122720 : context.estate = estate;
3681 :
3682 : /*
3683 : * Fetch rows from subplan, and execute the required table modification
3684 : * for each row.
3685 : */
3686 : for (;;)
3687 : {
3688 : /*
3689 : * Reset the per-output-tuple exprcontext. This is needed because
3690 : * triggers expect to use that context as workspace. It's a bit ugly
3691 : * to do this below the top level of the plan, however. We might need
3692 : * to rethink this later.
3693 : */
3694 13140700 : ResetPerTupleExprContext(estate);
3695 :
3696 : /*
3697 : * Reset per-tuple memory context used for processing on conflict and
3698 : * returning clauses, to free any expression evaluation storage
3699 : * allocated in the previous cycle.
3700 : */
3701 13140700 : if (pstate->ps_ExprContext)
3702 331710 : ResetExprContext(pstate->ps_ExprContext);
3703 :
3704 13140700 : context.planSlot = ExecProcNode(subplanstate);
3705 :
3706 : /* No more tuples to process? */
3707 13140300 : if (TupIsNull(context.planSlot))
3708 : break;
3709 :
3710 : /*
3711 : * When there are multiple result relations, each tuple contains a
3712 : * junk column that gives the OID of the rel from which it came.
3713 : * Extract it and select the correct result relation.
3714 : */
3715 13027856 : if (AttributeNumberIsValid(node->mt_resultOidAttno))
3716 : {
3717 : Datum datum;
3718 : bool isNull;
3719 : Oid resultoid;
3720 :
3721 4708 : datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
3722 : &isNull);
3723 4708 : if (isNull)
3724 : {
3725 : /*
3726 : * For commands other than MERGE, any tuples having InvalidOid
3727 : * for tableoid are errors. For MERGE, we may need to handle
3728 : * them as WHEN NOT MATCHED clauses if any, so do that.
3729 : *
3730 : * Note that we use the node's toplevel resultRelInfo, not any
3731 : * specific partition's.
3732 : */
3733 466 : if (operation == CMD_MERGE)
3734 : {
3735 466 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3736 :
3737 466 : slot = ExecMerge(&context, node->resultRelInfo,
3738 466 : NULL, NULL, node->canSetTag);
3739 :
3740 : /*
3741 : * If we got a RETURNING result, return it to the caller.
3742 : * We'll continue the work on next call.
3743 : */
3744 460 : if (slot)
3745 18 : return slot;
3746 :
3747 442 : continue; /* continue with the next tuple */
3748 : }
3749 :
3750 0 : elog(ERROR, "tableoid is NULL");
3751 : }
3752 4242 : resultoid = DatumGetObjectId(datum);
3753 :
3754 : /* If it's not the same as last time, we need to locate the rel */
3755 4242 : if (resultoid != node->mt_lastResultOid)
3756 2914 : resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
3757 : false, true);
3758 : }
3759 :
3760 : /*
3761 : * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
3762 : * here is compute the RETURNING expressions.
3763 : */
3764 13027390 : if (resultRelInfo->ri_usesFdwDirectModify)
3765 : {
3766 : Assert(resultRelInfo->ri_projectReturning);
3767 :
3768 : /*
3769 : * A scan slot containing the data that was actually inserted,
3770 : * updated or deleted has already been made available to
3771 : * ExecProcessReturning by IterateDirectModify, so no need to
3772 : * provide it here.
3773 : */
3774 694 : slot = ExecProcessReturning(resultRelInfo, NULL, context.planSlot);
3775 :
3776 694 : return slot;
3777 : }
3778 :
3779 13026696 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3780 13026696 : slot = context.planSlot;
3781 :
3782 13026696 : tupleid = NULL;
3783 13026696 : oldtuple = NULL;
3784 :
3785 : /*
3786 : * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
3787 : * to be updated/deleted/merged. For a heap relation, that's a TID;
3788 : * otherwise we may have a wholerow junk attr that carries the old
3789 : * tuple in toto. Keep this in step with the part of
3790 : * ExecInitModifyTable that sets up ri_RowIdAttNo.
3791 : */
3792 13026696 : if (operation == CMD_UPDATE || operation == CMD_DELETE ||
3793 : operation == CMD_MERGE)
3794 : {
3795 : char relkind;
3796 : Datum datum;
3797 : bool isNull;
3798 :
3799 1837170 : relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
3800 1837170 : if (relkind == RELKIND_RELATION ||
3801 462 : relkind == RELKIND_MATVIEW ||
3802 : relkind == RELKIND_PARTITIONED_TABLE)
3803 : {
3804 : /* ri_RowIdAttNo refers to a ctid attribute */
3805 : Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo));
3806 1836714 : datum = ExecGetJunkAttribute(slot,
3807 1836714 : resultRelInfo->ri_RowIdAttNo,
3808 : &isNull);
3809 :
3810 : /*
3811 : * For commands other than MERGE, any tuples having a null row
3812 : * identifier are errors. For MERGE, we may need to handle
3813 : * them as WHEN NOT MATCHED clauses if any, so do that.
3814 : *
3815 : * Note that we use the node's toplevel resultRelInfo, not any
3816 : * specific partition's.
3817 : */
3818 1836714 : if (isNull)
3819 : {
3820 1800 : if (operation == CMD_MERGE)
3821 : {
3822 1800 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3823 :
3824 1800 : slot = ExecMerge(&context, node->resultRelInfo,
3825 1800 : NULL, NULL, node->canSetTag);
3826 :
3827 : /*
3828 : * If we got a RETURNING result, return it to the
3829 : * caller. We'll continue the work on next call.
3830 : */
3831 1758 : if (slot)
3832 72 : return slot;
3833 :
3834 1722 : continue; /* continue with the next tuple */
3835 : }
3836 :
3837 0 : elog(ERROR, "ctid is NULL");
3838 : }
3839 :
3840 1834914 : tupleid = (ItemPointer) DatumGetPointer(datum);
3841 1834914 : tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
3842 1834914 : tupleid = &tuple_ctid;
3843 : }
3844 :
3845 : /*
3846 : * Use the wholerow attribute, when available, to reconstruct the
3847 : * old relation tuple. The old tuple serves one or both of two
3848 : * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
3849 : * provides values for any unchanged columns for the NEW tuple of
3850 : * an UPDATE, because the subplan does not produce all the columns
3851 : * of the target table.
3852 : *
3853 : * Note that the wholerow attribute does not carry system columns,
3854 : * so foreign table triggers miss seeing those, except that we
3855 : * know enough here to set t_tableOid. Quite separately from
3856 : * this, the FDW may fetch its own junk attrs to identify the row.
3857 : *
3858 : * Other relevant relkinds, currently limited to views, always
3859 : * have a wholerow attribute.
3860 : */
3861 456 : else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
3862 : {
3863 438 : datum = ExecGetJunkAttribute(slot,
3864 438 : resultRelInfo->ri_RowIdAttNo,
3865 : &isNull);
3866 :
3867 : /*
3868 : * For commands other than MERGE, any tuples having a null row
3869 : * identifier are errors. For MERGE, we may need to handle
3870 : * them as WHEN NOT MATCHED clauses if any, so do that.
3871 : *
3872 : * Note that we use the node's toplevel resultRelInfo, not any
3873 : * specific partition's.
3874 : */
3875 438 : if (isNull)
3876 : {
3877 42 : if (operation == CMD_MERGE)
3878 : {
3879 42 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3880 :
3881 42 : slot = ExecMerge(&context, node->resultRelInfo,
3882 42 : NULL, NULL, node->canSetTag);
3883 :
3884 : /*
3885 : * If we got a RETURNING result, return it to the
3886 : * caller. We'll continue the work on next call.
3887 : */
3888 36 : if (slot)
3889 6 : return slot;
3890 :
3891 30 : continue; /* continue with the next tuple */
3892 : }
3893 :
3894 0 : elog(ERROR, "wholerow is NULL");
3895 : }
3896 :
3897 396 : oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
3898 396 : oldtupdata.t_len =
3899 396 : HeapTupleHeaderGetDatumLength(oldtupdata.t_data);
3900 396 : ItemPointerSetInvalid(&(oldtupdata.t_self));
3901 : /* Historically, view triggers see invalid t_tableOid. */
3902 396 : oldtupdata.t_tableOid =
3903 396 : (relkind == RELKIND_VIEW) ? InvalidOid :
3904 162 : RelationGetRelid(resultRelInfo->ri_RelationDesc);
3905 :
3906 396 : oldtuple = &oldtupdata;
3907 : }
3908 : else
3909 : {
3910 : /* Only foreign tables are allowed to omit a row-ID attr */
3911 : Assert(relkind == RELKIND_FOREIGN_TABLE);
3912 : }
3913 : }
3914 :
3915 13024854 : switch (operation)
3916 : {
3917 11189526 : case CMD_INSERT:
3918 : /* Initialize projection info if first time for this table */
3919 11189526 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3920 88154 : ExecInitInsertProjection(node, resultRelInfo);
3921 11189526 : slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
3922 11189526 : slot = ExecInsert(&context, resultRelInfo, slot,
3923 11189526 : node->canSetTag, NULL, NULL);
3924 11187558 : break;
3925 :
3926 306998 : case CMD_UPDATE:
3927 : /* Initialize projection info if first time for this table */
3928 306998 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3929 12708 : ExecInitUpdateProjection(node, resultRelInfo);
3930 :
3931 : /*
3932 : * Make the new tuple by combining plan's output tuple with
3933 : * the old tuple being updated.
3934 : */
3935 306998 : oldSlot = resultRelInfo->ri_oldTupleSlot;
3936 306998 : if (oldtuple != NULL)
3937 : {
3938 : /* Use the wholerow junk attr as the old tuple. */
3939 258 : ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
3940 : }
3941 : else
3942 : {
3943 : /* Fetch the most recent version of old tuple. */
3944 306740 : Relation relation = resultRelInfo->ri_RelationDesc;
3945 :
3946 306740 : if (!table_tuple_fetch_row_version(relation, tupleid,
3947 : SnapshotAny,
3948 : oldSlot))
3949 0 : elog(ERROR, "failed to fetch tuple being updated");
3950 : }
3951 306998 : slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
3952 : oldSlot);
3953 :
3954 : /* Now apply the update. */
3955 306998 : slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
3956 : slot, resultRelInfo->ri_oldTupleSlot,
3957 306998 : node->canSetTag, false);
3958 306596 : break;
3959 :
3960 1524804 : case CMD_DELETE:
3961 : /* Initialize slot for DELETE to fetch the old tuple */
3962 1524804 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3963 11756 : ExecInitDeleteTupleSlot(node, resultRelInfo);
3964 :
3965 1524804 : slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
3966 : resultRelInfo->ri_oldTupleSlot, true, false,
3967 1524804 : node->canSetTag, NULL, NULL, NULL);
3968 1524722 : break;
3969 :
3970 3526 : case CMD_MERGE:
3971 3526 : slot = ExecMerge(&context, resultRelInfo, tupleid, oldtuple,
3972 3526 : node->canSetTag);
3973 3436 : break;
3974 :
3975 0 : default:
3976 0 : elog(ERROR, "unknown operation");
3977 : break;
3978 : }
3979 :
3980 : /*
3981 : * If we got a RETURNING result, return it to caller. We'll continue
3982 : * the work on next call.
3983 : */
3984 13022312 : if (slot)
3985 6496 : return slot;
3986 : }
3987 :
3988 : /*
3989 : * Insert remaining tuples for batch insert.
3990 : */
3991 112444 : if (estate->es_insert_pending_result_relations != NIL)
3992 24 : ExecPendingInserts(estate);
3993 :
3994 : /*
3995 : * We're done, but fire AFTER STATEMENT triggers before exiting.
3996 : */
3997 112444 : fireASTriggers(node);
3998 :
3999 112444 : node->mt_done = true;
4000 :
4001 112444 : return NULL;
4002 : }
4003 :
4004 : /*
4005 : * ExecLookupResultRelByOid
4006 : * If the table with given OID is among the result relations to be
4007 : * updated by the given ModifyTable node, return its ResultRelInfo.
4008 : *
4009 : * If not found, return NULL if missing_ok, else raise error.
4010 : *
4011 : * If update_cache is true, then upon successful lookup, update the node's
4012 : * one-element cache. ONLY ExecModifyTable may pass true for this.
4013 : */
4014 : ResultRelInfo *
4015 11318 : ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid,
4016 : bool missing_ok, bool update_cache)
4017 : {
4018 11318 : if (node->mt_resultOidHash)
4019 : {
4020 : /* Use the pre-built hash table to locate the rel */
4021 : MTTargetRelLookup *mtlookup;
4022 :
4023 : mtlookup = (MTTargetRelLookup *)
4024 0 : hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
4025 0 : if (mtlookup)
4026 : {
4027 0 : if (update_cache)
4028 : {
4029 0 : node->mt_lastResultOid = resultoid;
4030 0 : node->mt_lastResultIndex = mtlookup->relationIndex;
4031 : }
4032 0 : return node->resultRelInfo + mtlookup->relationIndex;
4033 : }
4034 : }
4035 : else
4036 : {
4037 : /* With few target rels, just search the ResultRelInfo array */
4038 21640 : for (int ndx = 0; ndx < node->mt_nrels; ndx++)
4039 : {
4040 13682 : ResultRelInfo *rInfo = node->resultRelInfo + ndx;
4041 :
4042 13682 : if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
4043 : {
4044 3360 : if (update_cache)
4045 : {
4046 2914 : node->mt_lastResultOid = resultoid;
4047 2914 : node->mt_lastResultIndex = ndx;
4048 : }
4049 3360 : return rInfo;
4050 : }
4051 : }
4052 : }
4053 :
4054 7958 : if (!missing_ok)
4055 0 : elog(ERROR, "incorrect result relation OID %u", resultoid);
4056 7958 : return NULL;
4057 : }
4058 :
4059 : /* ----------------------------------------------------------------
4060 : * ExecInitModifyTable
4061 : * ----------------------------------------------------------------
4062 : */
4063 : ModifyTableState *
4064 116582 : ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
4065 : {
4066 : ModifyTableState *mtstate;
4067 116582 : Plan *subplan = outerPlan(node);
4068 116582 : CmdType operation = node->operation;
4069 116582 : int nrels = list_length(node->resultRelations);
4070 : ResultRelInfo *resultRelInfo;
4071 : List *arowmarks;
4072 : ListCell *l;
4073 : int i;
4074 : Relation rel;
4075 :
4076 : /* check for unsupported flags */
4077 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
4078 :
4079 : /*
4080 : * create state structure
4081 : */
4082 116582 : mtstate = makeNode(ModifyTableState);
4083 116582 : mtstate->ps.plan = (Plan *) node;
4084 116582 : mtstate->ps.state = estate;
4085 116582 : mtstate->ps.ExecProcNode = ExecModifyTable;
4086 :
4087 116582 : mtstate->operation = operation;
4088 116582 : mtstate->canSetTag = node->canSetTag;
4089 116582 : mtstate->mt_done = false;
4090 :
4091 116582 : mtstate->mt_nrels = nrels;
4092 116582 : mtstate->resultRelInfo = (ResultRelInfo *)
4093 116582 : palloc(nrels * sizeof(ResultRelInfo));
4094 :
4095 116582 : mtstate->mt_merge_inserted = 0;
4096 116582 : mtstate->mt_merge_updated = 0;
4097 116582 : mtstate->mt_merge_deleted = 0;
4098 :
4099 : /*----------
4100 : * Resolve the target relation. This is the same as:
4101 : *
4102 : * - the relation for which we will fire FOR STATEMENT triggers,
4103 : * - the relation into whose tuple format all captured transition tuples
4104 : * must be converted, and
4105 : * - the root partitioned table used for tuple routing.
4106 : *
4107 : * If it's a partitioned or inherited table, the root partition or
4108 : * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
4109 : * given explicitly in node->rootRelation. Otherwise, the target relation
4110 : * is the sole relation in the node->resultRelations list.
4111 : *----------
4112 : */
4113 116582 : if (node->rootRelation > 0)
4114 : {
4115 2502 : mtstate->rootResultRelInfo = makeNode(ResultRelInfo);
4116 2502 : ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
4117 : node->rootRelation);
4118 : }
4119 : else
4120 : {
4121 : Assert(list_length(node->resultRelations) == 1);
4122 114080 : mtstate->rootResultRelInfo = mtstate->resultRelInfo;
4123 114080 : ExecInitResultRelation(estate, mtstate->resultRelInfo,
4124 114080 : linitial_int(node->resultRelations));
4125 : }
4126 :
4127 : /* set up epqstate with dummy subplan data for the moment */
4128 116582 : EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
4129 : node->epqParam, node->resultRelations);
4130 116582 : mtstate->fireBSTriggers = true;
4131 :
4132 : /*
4133 : * Build state for collecting transition tuples. This requires having a
4134 : * valid trigger query context, so skip it in explain-only mode.
4135 : */
4136 116582 : if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
4137 115758 : ExecSetupTransitionCaptureState(mtstate, estate);
4138 :
4139 : /*
4140 : * Open all the result relations and initialize the ResultRelInfo structs.
4141 : * (But root relation was initialized above, if it's part of the array.)
4142 : * We must do this before initializing the subplan, because direct-modify
4143 : * FDWs expect their ResultRelInfos to be available.
4144 : */
4145 116582 : resultRelInfo = mtstate->resultRelInfo;
4146 116582 : i = 0;
4147 235162 : foreach(l, node->resultRelations)
4148 : {
4149 118854 : Index resultRelation = lfirst_int(l);
4150 118854 : List *mergeActions = NIL;
4151 :
4152 118854 : if (node->mergeActionLists)
4153 1514 : mergeActions = list_nth(node->mergeActionLists, i);
4154 :
4155 118854 : if (resultRelInfo != mtstate->rootResultRelInfo)
4156 : {
4157 4774 : ExecInitResultRelation(estate, resultRelInfo, resultRelation);
4158 :
4159 : /*
4160 : * For child result relations, store the root result relation
4161 : * pointer. We do so for the convenience of places that want to
4162 : * look at the query's original target relation but don't have the
4163 : * mtstate handy.
4164 : */
4165 4774 : resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
4166 : }
4167 :
4168 : /* Initialize the usesFdwDirectModify flag */
4169 118854 : resultRelInfo->ri_usesFdwDirectModify =
4170 118854 : bms_is_member(i, node->fdwDirectModifyPlans);
4171 :
4172 : /*
4173 : * Verify result relation is a valid target for the current operation
4174 : */
4175 118854 : CheckValidResultRel(resultRelInfo, operation, mergeActions);
4176 :
4177 118580 : resultRelInfo++;
4178 118580 : i++;
4179 : }
4180 :
4181 : /*
4182 : * Now we may initialize the subplan.
4183 : */
4184 116308 : outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
4185 :
4186 : /*
4187 : * Do additional per-result-relation initialization.
4188 : */
4189 234854 : for (i = 0; i < nrels; i++)
4190 : {
4191 118546 : resultRelInfo = &mtstate->resultRelInfo[i];
4192 :
4193 : /* Let FDWs init themselves for foreign-table result rels */
4194 118546 : if (!resultRelInfo->ri_usesFdwDirectModify &&
4195 118338 : resultRelInfo->ri_FdwRoutine != NULL &&
4196 306 : resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
4197 : {
4198 306 : List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
4199 :
4200 306 : resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
4201 : resultRelInfo,
4202 : fdw_private,
4203 : i,
4204 : eflags);
4205 : }
4206 :
4207 : /*
4208 : * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
4209 : * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
4210 : * tables, the FDW might have created additional junk attr(s), but
4211 : * those are no concern of ours.
4212 : */
4213 118546 : if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4214 : operation == CMD_MERGE)
4215 : {
4216 : char relkind;
4217 :
4218 29082 : relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4219 29082 : if (relkind == RELKIND_RELATION ||
4220 628 : relkind == RELKIND_MATVIEW ||
4221 : relkind == RELKIND_PARTITIONED_TABLE)
4222 : {
4223 28490 : resultRelInfo->ri_RowIdAttNo =
4224 28490 : ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
4225 28490 : if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4226 0 : elog(ERROR, "could not find junk ctid column");
4227 : }
4228 592 : else if (relkind == RELKIND_FOREIGN_TABLE)
4229 : {
4230 : /*
4231 : * We don't support MERGE with foreign tables for now. (It's
4232 : * problematic because the implementation uses CTID.)
4233 : */
4234 : Assert(operation != CMD_MERGE);
4235 :
4236 : /*
4237 : * When there is a row-level trigger, there should be a
4238 : * wholerow attribute. We also require it to be present in
4239 : * UPDATE and MERGE, so we can get the values of unchanged
4240 : * columns.
4241 : */
4242 340 : resultRelInfo->ri_RowIdAttNo =
4243 340 : ExecFindJunkAttributeInTlist(subplan->targetlist,
4244 : "wholerow");
4245 340 : if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
4246 190 : !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4247 0 : elog(ERROR, "could not find junk wholerow column");
4248 : }
4249 : else
4250 : {
4251 : /* Other valid target relkinds must provide wholerow */
4252 252 : resultRelInfo->ri_RowIdAttNo =
4253 252 : ExecFindJunkAttributeInTlist(subplan->targetlist,
4254 : "wholerow");
4255 252 : if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4256 0 : elog(ERROR, "could not find junk wholerow column");
4257 : }
4258 : }
4259 : }
4260 :
4261 : /*
4262 : * If this is an inherited update/delete/merge, there will be a junk
4263 : * attribute named "tableoid" present in the subplan's targetlist. It
4264 : * will be used to identify the result relation for a given tuple to be
4265 : * updated/deleted/merged.
4266 : */
4267 116308 : mtstate->mt_resultOidAttno =
4268 116308 : ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
4269 : Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || nrels == 1);
4270 116308 : mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
4271 116308 : mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
4272 :
4273 : /* Get the root target relation */
4274 116308 : rel = mtstate->rootResultRelInfo->ri_RelationDesc;
4275 :
4276 : /*
4277 : * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
4278 : * or MERGE might need this too, but only if it actually moves tuples
4279 : * between partitions; in that case setup is done by
4280 : * ExecCrossPartitionUpdate.
4281 : */
4282 116308 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
4283 : operation == CMD_INSERT)
4284 5682 : mtstate->mt_partition_tuple_routing =
4285 5682 : ExecSetupPartitionTupleRouting(estate, rel);
4286 :
4287 : /*
4288 : * Initialize any WITH CHECK OPTION constraints if needed.
4289 : */
4290 116308 : resultRelInfo = mtstate->resultRelInfo;
4291 117630 : foreach(l, node->withCheckOptionLists)
4292 : {
4293 1322 : List *wcoList = (List *) lfirst(l);
4294 1322 : List *wcoExprs = NIL;
4295 : ListCell *ll;
4296 :
4297 3608 : foreach(ll, wcoList)
4298 : {
4299 2286 : WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
4300 2286 : ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
4301 : &mtstate->ps);
4302 :
4303 2286 : wcoExprs = lappend(wcoExprs, wcoExpr);
4304 : }
4305 :
4306 1322 : resultRelInfo->ri_WithCheckOptions = wcoList;
4307 1322 : resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4308 1322 : resultRelInfo++;
4309 : }
4310 :
4311 : /*
4312 : * Initialize RETURNING projections if needed.
4313 : */
4314 116308 : if (node->returningLists)
4315 : {
4316 : TupleTableSlot *slot;
4317 : ExprContext *econtext;
4318 :
4319 : /*
4320 : * Initialize result tuple slot and assign its rowtype using the first
4321 : * RETURNING list. We assume the rest will look the same.
4322 : */
4323 4118 : mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
4324 :
4325 : /* Set up a slot for the output of the RETURNING projection(s) */
4326 4118 : ExecInitResultTupleSlotTL(&mtstate->ps, &TTSOpsVirtual);
4327 4118 : slot = mtstate->ps.ps_ResultTupleSlot;
4328 :
4329 : /* Need an econtext too */
4330 4118 : if (mtstate->ps.ps_ExprContext == NULL)
4331 4118 : ExecAssignExprContext(estate, &mtstate->ps);
4332 4118 : econtext = mtstate->ps.ps_ExprContext;
4333 :
4334 : /*
4335 : * Build a projection for each result rel.
4336 : */
4337 4118 : resultRelInfo = mtstate->resultRelInfo;
4338 8552 : foreach(l, node->returningLists)
4339 : {
4340 4434 : List *rlist = (List *) lfirst(l);
4341 :
4342 4434 : resultRelInfo->ri_returningList = rlist;
4343 4434 : resultRelInfo->ri_projectReturning =
4344 4434 : ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
4345 4434 : resultRelInfo->ri_RelationDesc->rd_att);
4346 4434 : resultRelInfo++;
4347 : }
4348 : }
4349 : else
4350 : {
4351 : /*
4352 : * We still must construct a dummy result tuple type, because InitPlan
4353 : * expects one (maybe should change that?).
4354 : */
4355 112190 : mtstate->ps.plan->targetlist = NIL;
4356 112190 : ExecInitResultTypeTL(&mtstate->ps);
4357 :
4358 112190 : mtstate->ps.ps_ExprContext = NULL;
4359 : }
4360 :
4361 : /* Set the list of arbiter indexes if needed for ON CONFLICT */
4362 116308 : resultRelInfo = mtstate->resultRelInfo;
4363 116308 : if (node->onConflictAction != ONCONFLICT_NONE)
4364 : {
4365 : /* insert may only have one relation, inheritance is not expanded */
4366 : Assert(nrels == 1);
4367 1188 : resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
4368 : }
4369 :
4370 : /*
4371 : * If needed, Initialize target list, projection and qual for ON CONFLICT
4372 : * DO UPDATE.
4373 : */
4374 116308 : if (node->onConflictAction == ONCONFLICT_UPDATE)
4375 : {
4376 900 : OnConflictSetState *onconfl = makeNode(OnConflictSetState);
4377 : ExprContext *econtext;
4378 : TupleDesc relationDesc;
4379 :
4380 : /* already exists if created by RETURNING processing above */
4381 900 : if (mtstate->ps.ps_ExprContext == NULL)
4382 632 : ExecAssignExprContext(estate, &mtstate->ps);
4383 :
4384 900 : econtext = mtstate->ps.ps_ExprContext;
4385 900 : relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
4386 :
4387 : /* create state for DO UPDATE SET operation */
4388 900 : resultRelInfo->ri_onConflict = onconfl;
4389 :
4390 : /* initialize slot for the existing tuple */
4391 900 : onconfl->oc_Existing =
4392 900 : table_slot_create(resultRelInfo->ri_RelationDesc,
4393 900 : &mtstate->ps.state->es_tupleTable);
4394 :
4395 : /*
4396 : * Create the tuple slot for the UPDATE SET projection. We want a slot
4397 : * of the table's type here, because the slot will be used to insert
4398 : * into the table, and for RETURNING processing - which may access
4399 : * system attributes.
4400 : */
4401 900 : onconfl->oc_ProjSlot =
4402 900 : table_slot_create(resultRelInfo->ri_RelationDesc,
4403 900 : &mtstate->ps.state->es_tupleTable);
4404 :
4405 : /* build UPDATE SET projection state */
4406 900 : onconfl->oc_ProjInfo =
4407 900 : ExecBuildUpdateProjection(node->onConflictSet,
4408 : true,
4409 : node->onConflictCols,
4410 : relationDesc,
4411 : econtext,
4412 : onconfl->oc_ProjSlot,
4413 : &mtstate->ps);
4414 :
4415 : /* initialize state to evaluate the WHERE clause, if any */
4416 900 : if (node->onConflictWhere)
4417 : {
4418 : ExprState *qualexpr;
4419 :
4420 176 : qualexpr = ExecInitQual((List *) node->onConflictWhere,
4421 : &mtstate->ps);
4422 176 : onconfl->oc_WhereClause = qualexpr;
4423 : }
4424 : }
4425 :
4426 : /*
4427 : * If we have any secondary relations in an UPDATE or DELETE, they need to
4428 : * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
4429 : * EvalPlanQual mechanism needs to be told about them. This also goes for
4430 : * the source relations in a MERGE. Locate the relevant ExecRowMarks.
4431 : */
4432 116308 : arowmarks = NIL;
4433 118742 : foreach(l, node->rowMarks)
4434 : {
4435 2434 : PlanRowMark *rc = lfirst_node(PlanRowMark, l);
4436 : ExecRowMark *erm;
4437 : ExecAuxRowMark *aerm;
4438 :
4439 : /* ignore "parent" rowmarks; they are irrelevant at runtime */
4440 2434 : if (rc->isParent)
4441 100 : continue;
4442 :
4443 : /* Find ExecRowMark and build ExecAuxRowMark */
4444 2334 : erm = ExecFindRowMark(estate, rc->rti, false);
4445 2334 : aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
4446 2334 : arowmarks = lappend(arowmarks, aerm);
4447 : }
4448 :
4449 : /* For a MERGE command, initialize its state */
4450 116308 : if (mtstate->operation == CMD_MERGE)
4451 1324 : ExecInitMerge(mtstate, estate);
4452 :
4453 116308 : EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
4454 :
4455 : /*
4456 : * If there are a lot of result relations, use a hash table to speed the
4457 : * lookups. If there are not a lot, a simple linear search is faster.
4458 : *
4459 : * It's not clear where the threshold is, but try 64 for starters. In a
4460 : * debugging build, use a small threshold so that we get some test
4461 : * coverage of both code paths.
4462 : */
4463 : #ifdef USE_ASSERT_CHECKING
4464 : #define MT_NRELS_HASH 4
4465 : #else
4466 : #define MT_NRELS_HASH 64
4467 : #endif
4468 116308 : if (nrels >= MT_NRELS_HASH)
4469 : {
4470 : HASHCTL hash_ctl;
4471 :
4472 0 : hash_ctl.keysize = sizeof(Oid);
4473 0 : hash_ctl.entrysize = sizeof(MTTargetRelLookup);
4474 0 : hash_ctl.hcxt = CurrentMemoryContext;
4475 0 : mtstate->mt_resultOidHash =
4476 0 : hash_create("ModifyTable target hash",
4477 : nrels, &hash_ctl,
4478 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
4479 0 : for (i = 0; i < nrels; i++)
4480 : {
4481 : Oid hashkey;
4482 : MTTargetRelLookup *mtlookup;
4483 : bool found;
4484 :
4485 0 : resultRelInfo = &mtstate->resultRelInfo[i];
4486 0 : hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
4487 : mtlookup = (MTTargetRelLookup *)
4488 0 : hash_search(mtstate->mt_resultOidHash, &hashkey,
4489 : HASH_ENTER, &found);
4490 : Assert(!found);
4491 0 : mtlookup->relationIndex = i;
4492 : }
4493 : }
4494 : else
4495 116308 : mtstate->mt_resultOidHash = NULL;
4496 :
4497 : /*
4498 : * Determine if the FDW supports batch insert and determine the batch size
4499 : * (a FDW may support batching, but it may be disabled for the
4500 : * server/table).
4501 : *
4502 : * We only do this for INSERT, so that for UPDATE/DELETE the batch size
4503 : * remains set to 0.
4504 : */
4505 116308 : if (operation == CMD_INSERT)
4506 : {
4507 : /* insert may only have one relation, inheritance is not expanded */
4508 : Assert(nrels == 1);
4509 89464 : resultRelInfo = mtstate->resultRelInfo;
4510 89464 : if (!resultRelInfo->ri_usesFdwDirectModify &&
4511 89464 : resultRelInfo->ri_FdwRoutine != NULL &&
4512 174 : resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
4513 174 : resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
4514 : {
4515 174 : resultRelInfo->ri_BatchSize =
4516 174 : resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
4517 174 : Assert(resultRelInfo->ri_BatchSize >= 1);
4518 : }
4519 : else
4520 89290 : resultRelInfo->ri_BatchSize = 1;
4521 : }
4522 :
4523 : /*
4524 : * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
4525 : * to estate->es_auxmodifytables so that it will be run to completion by
4526 : * ExecPostprocessPlan. (It'd actually work fine to add the primary
4527 : * ModifyTable node too, but there's no need.) Note the use of lcons not
4528 : * lappend: we need later-initialized ModifyTable nodes to be shut down
4529 : * before earlier ones. This ensures that we don't throw away RETURNING
4530 : * rows that need to be seen by a later CTE subplan.
4531 : */
4532 116308 : if (!mtstate->canSetTag)
4533 914 : estate->es_auxmodifytables = lcons(mtstate,
4534 : estate->es_auxmodifytables);
4535 :
4536 116308 : return mtstate;
4537 : }
4538 :
4539 : /* ----------------------------------------------------------------
4540 : * ExecEndModifyTable
4541 : *
4542 : * Shuts down the plan.
4543 : *
4544 : * Returns nothing of interest.
4545 : * ----------------------------------------------------------------
4546 : */
4547 : void
4548 112254 : ExecEndModifyTable(ModifyTableState *node)
4549 : {
4550 : int i;
4551 :
4552 : /*
4553 : * Allow any FDWs to shut down
4554 : */
4555 226484 : for (i = 0; i < node->mt_nrels; i++)
4556 : {
4557 : int j;
4558 114230 : ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
4559 :
4560 114230 : if (!resultRelInfo->ri_usesFdwDirectModify &&
4561 114038 : resultRelInfo->ri_FdwRoutine != NULL &&
4562 286 : resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
4563 286 : resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
4564 : resultRelInfo);
4565 :
4566 : /*
4567 : * Cleanup the initialized batch slots. This only matters for FDWs
4568 : * with batching, but the other cases will have ri_NumSlotsInitialized
4569 : * == 0.
4570 : */
4571 114286 : for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
4572 : {
4573 56 : ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
4574 56 : ExecDropSingleTupleTableSlot(resultRelInfo->ri_PlanSlots[j]);
4575 : }
4576 : }
4577 :
4578 : /*
4579 : * Close all the partitioned tables, leaf partitions, and their indices
4580 : * and release the slot used for tuple routing, if set.
4581 : */
4582 112254 : if (node->mt_partition_tuple_routing)
4583 : {
4584 5700 : ExecCleanupTupleRouting(node, node->mt_partition_tuple_routing);
4585 :
4586 5700 : if (node->mt_root_tuple_slot)
4587 554 : ExecDropSingleTupleTableSlot(node->mt_root_tuple_slot);
4588 : }
4589 :
4590 : /*
4591 : * Terminate EPQ execution if active
4592 : */
4593 112254 : EvalPlanQualEnd(&node->mt_epqstate);
4594 :
4595 : /*
4596 : * shut down subplan
4597 : */
4598 112254 : ExecEndNode(outerPlanState(node));
4599 112254 : }
4600 :
4601 : void
4602 0 : ExecReScanModifyTable(ModifyTableState *node)
4603 : {
4604 : /*
4605 : * Currently, we don't need to support rescan on ModifyTable nodes. The
4606 : * semantics of that would be a bit debatable anyway.
4607 : */
4608 0 : elog(ERROR, "ExecReScanModifyTable is not implemented");
4609 : }
|