Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeModifyTable.c
4 : * routines to handle ModifyTable nodes.
5 : *
6 : * Portions Copyright (c) 1996-2026, 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 : /*
16 : * INTERFACE ROUTINES
17 : * ExecInitModifyTable - initialize the ModifyTable node
18 : * ExecModifyTable - retrieve the next tuple from the node
19 : * ExecEndModifyTable - shut down the ModifyTable node
20 : * ExecReScanModifyTable - rescan the ModifyTable node
21 : *
22 : * NOTES
23 : * The ModifyTable node receives input from its outerPlan, which is
24 : * the data to insert for INSERT cases, the changed columns' new
25 : * values plus row-locating info for UPDATE and MERGE cases, or just the
26 : * row-locating info for DELETE cases.
27 : *
28 : * The relation to modify can be an ordinary table, a foreign table, or a
29 : * view. If it's a view, either it has sufficient INSTEAD OF triggers or
30 : * this node executes only MERGE ... DO NOTHING. If the original MERGE
31 : * targeted a view not in one of those two categories, earlier processing
32 : * already pointed the ModifyTable result relation to an underlying
33 : * relation of that other view. This node does process
34 : * ri_WithCheckOptions, which may have expressions from those other,
35 : * automatically updatable views.
36 : *
37 : * MERGE runs a join between the source relation and the target table.
38 : * If any WHEN NOT MATCHED [BY TARGET] clauses are present, then the join
39 : * is an outer join that might output tuples without a matching target
40 : * tuple. In this case, any unmatched target tuples will have NULL
41 : * row-locating info, and only INSERT can be run. But for matched target
42 : * tuples, the row-locating info is used to determine the tuple to UPDATE
43 : * or DELETE. When all clauses are WHEN MATCHED or WHEN NOT MATCHED BY
44 : * SOURCE, all tuples produced by the join will include a matching target
45 : * tuple, so all tuples contain row-locating info.
46 : *
47 : * If the query specifies RETURNING, then the ModifyTable returns a
48 : * RETURNING tuple after completing each row insert, update, or delete.
49 : * It must be called again to continue the operation. Without RETURNING,
50 : * we just loop within the node until all the work is done, then
51 : * return NULL. This avoids useless call/return overhead.
52 : */
53 :
54 : #include "postgres.h"
55 :
56 : #include "access/htup_details.h"
57 : #include "access/tableam.h"
58 : #include "access/tupconvert.h"
59 : #include "access/xact.h"
60 : #include "commands/trigger.h"
61 : #include "executor/execPartition.h"
62 : #include "executor/executor.h"
63 : #include "executor/instrument.h"
64 : #include "executor/nodeModifyTable.h"
65 : #include "foreign/fdwapi.h"
66 : #include "miscadmin.h"
67 : #include "nodes/nodeFuncs.h"
68 : #include "optimizer/optimizer.h"
69 : #include "pgstat.h"
70 : #include "rewrite/rewriteHandler.h"
71 : #include "rewrite/rewriteManip.h"
72 : #include "storage/lmgr.h"
73 : #include "utils/builtins.h"
74 : #include "utils/datum.h"
75 : #include "utils/injection_point.h"
76 : #include "utils/rangetypes.h"
77 : #include "utils/rel.h"
78 : #include "utils/snapmgr.h"
79 :
80 :
81 : typedef struct MTTargetRelLookup
82 : {
83 : Oid relationOid; /* hash key, must be first */
84 : int relationIndex; /* rel's index in resultRelInfo[] array */
85 : } MTTargetRelLookup;
86 :
87 : /*
88 : * Context struct for a ModifyTable operation, containing basic execution
89 : * state and some output variables populated by ExecUpdateAct() and
90 : * ExecDeleteAct() to report the result of their actions to callers.
91 : */
92 : typedef struct ModifyTableContext
93 : {
94 : /* Operation state */
95 : ModifyTableState *mtstate;
96 : EPQState *epqstate;
97 : EState *estate;
98 :
99 : /*
100 : * Slot containing tuple obtained from ModifyTable's subplan. Used to
101 : * access "junk" columns that are not going to be stored.
102 : */
103 : TupleTableSlot *planSlot;
104 :
105 : /*
106 : * Information about the changes that were made concurrently to a tuple
107 : * being updated or deleted
108 : */
109 : TM_FailureData tmfd;
110 :
111 : /*
112 : * The tuple deleted when doing a cross-partition UPDATE with a RETURNING
113 : * clause that refers to OLD columns (converted to the root's tuple
114 : * descriptor).
115 : */
116 : TupleTableSlot *cpDeletedSlot;
117 :
118 : /*
119 : * The tuple projected by the INSERT's RETURNING clause, when doing a
120 : * cross-partition UPDATE
121 : */
122 : TupleTableSlot *cpUpdateReturningSlot;
123 : } ModifyTableContext;
124 :
125 : /*
126 : * Context struct containing output data specific to UPDATE operations.
127 : */
128 : typedef struct UpdateContext
129 : {
130 : bool crossPartUpdate; /* was it a cross-partition update? */
131 : TU_UpdateIndexes updateIndexes; /* Which index updates are required? */
132 :
133 : /*
134 : * Lock mode to acquire on the latest tuple version before performing
135 : * EvalPlanQual on it
136 : */
137 : LockTupleMode lockmode;
138 : } UpdateContext;
139 :
140 :
141 : static void ExecBatchInsert(ModifyTableState *mtstate,
142 : ResultRelInfo *resultRelInfo,
143 : TupleTableSlot **slots,
144 : TupleTableSlot **planSlots,
145 : int numSlots,
146 : EState *estate,
147 : bool canSetTag);
148 : static void ExecPendingInserts(EState *estate);
149 : static void ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
150 : ResultRelInfo *sourcePartInfo,
151 : ResultRelInfo *destPartInfo,
152 : ItemPointer tupleid,
153 : TupleTableSlot *oldslot,
154 : TupleTableSlot *newslot);
155 : static bool ExecOnConflictLockRow(ModifyTableContext *context,
156 : TupleTableSlot *existing,
157 : ItemPointer conflictTid,
158 : Relation relation,
159 : LockTupleMode lockmode,
160 : bool isUpdate);
161 : static bool ExecOnConflictUpdate(ModifyTableContext *context,
162 : ResultRelInfo *resultRelInfo,
163 : ItemPointer conflictTid,
164 : TupleTableSlot *excludedSlot,
165 : bool canSetTag,
166 : TupleTableSlot **returning);
167 : static bool ExecOnConflictSelect(ModifyTableContext *context,
168 : ResultRelInfo *resultRelInfo,
169 : ItemPointer conflictTid,
170 : TupleTableSlot *excludedSlot,
171 : bool canSetTag,
172 : TupleTableSlot **returning);
173 : static void ExecForPortionOfLeftovers(ModifyTableContext *context,
174 : EState *estate,
175 : ResultRelInfo *resultRelInfo,
176 : ItemPointer tupleid);
177 : static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
178 : EState *estate,
179 : PartitionTupleRouting *proute,
180 : ResultRelInfo *targetRelInfo,
181 : TupleTableSlot *slot,
182 : ResultRelInfo **partRelInfo);
183 :
184 : static TupleTableSlot *ExecMerge(ModifyTableContext *context,
185 : ResultRelInfo *resultRelInfo,
186 : ItemPointer tupleid,
187 : HeapTuple oldtuple,
188 : bool canSetTag);
189 : static void ExecInitMerge(ModifyTableState *mtstate, EState *estate);
190 : static TupleTableSlot *ExecMergeMatched(ModifyTableContext *context,
191 : ResultRelInfo *resultRelInfo,
192 : ItemPointer tupleid,
193 : HeapTuple oldtuple,
194 : bool canSetTag,
195 : bool *matched);
196 : static TupleTableSlot *ExecMergeNotMatched(ModifyTableContext *context,
197 : ResultRelInfo *resultRelInfo,
198 : bool canSetTag);
199 : static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate);
200 : static void fireBSTriggers(ModifyTableState *node);
201 : static void fireASTriggers(ModifyTableState *node);
202 :
203 :
204 : /*
205 : * Verify that the tuples to be produced by INSERT match the
206 : * target relation's rowtype
207 : *
208 : * We do this to guard against stale plans. If plan invalidation is
209 : * functioning properly then we should never get a failure here, but better
210 : * safe than sorry. Note that this is called after we have obtained lock
211 : * on the target rel, so the rowtype can't change underneath us.
212 : *
213 : * The plan output is represented by its targetlist, because that makes
214 : * handling the dropped-column case easier.
215 : *
216 : * We used to use this for UPDATE as well, but now the equivalent checks
217 : * are done in ExecBuildUpdateProjection.
218 : */
219 : static void
220 56794 : ExecCheckPlanOutput(Relation resultRel, List *targetList)
221 : {
222 56794 : TupleDesc resultDesc = RelationGetDescr(resultRel);
223 56794 : int attno = 0;
224 : ListCell *lc;
225 :
226 178434 : foreach(lc, targetList)
227 : {
228 121640 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
229 : Form_pg_attribute attr;
230 :
231 : Assert(!tle->resjunk); /* caller removed junk items already */
232 :
233 121640 : if (attno >= resultDesc->natts)
234 0 : ereport(ERROR,
235 : (errcode(ERRCODE_DATATYPE_MISMATCH),
236 : errmsg("table row type and query-specified row type do not match"),
237 : errdetail("Query has too many columns.")));
238 121640 : attr = TupleDescAttr(resultDesc, attno);
239 121640 : attno++;
240 :
241 : /*
242 : * Special cases here should match planner's expand_insert_targetlist.
243 : */
244 121640 : if (attr->attisdropped)
245 : {
246 : /*
247 : * For a dropped column, we can't check atttypid (it's likely 0).
248 : * In any case the planner has most likely inserted an INT4 null.
249 : * What we insist on is just *some* NULL constant.
250 : */
251 447 : if (!IsA(tle->expr, Const) ||
252 447 : !((Const *) tle->expr)->constisnull)
253 0 : ereport(ERROR,
254 : (errcode(ERRCODE_DATATYPE_MISMATCH),
255 : errmsg("table row type and query-specified row type do not match"),
256 : errdetail("Query provides a value for a dropped column at ordinal position %d.",
257 : attno)));
258 : }
259 121193 : else if (attr->attgenerated)
260 : {
261 : /*
262 : * For a generated column, the planner will have inserted a null
263 : * of the column's base type (to avoid possibly failing on domain
264 : * not-null constraints). It doesn't seem worth insisting on that
265 : * exact type though, since a null value is type-independent. As
266 : * above, just insist on *some* NULL constant.
267 : */
268 864 : if (!IsA(tle->expr, Const) ||
269 864 : !((Const *) tle->expr)->constisnull)
270 0 : ereport(ERROR,
271 : (errcode(ERRCODE_DATATYPE_MISMATCH),
272 : errmsg("table row type and query-specified row type do not match"),
273 : errdetail("Query provides a value for a generated column at ordinal position %d.",
274 : attno)));
275 : }
276 : else
277 : {
278 : /* Normal case: demand type match */
279 120329 : if (exprType((Node *) tle->expr) != attr->atttypid)
280 0 : ereport(ERROR,
281 : (errcode(ERRCODE_DATATYPE_MISMATCH),
282 : errmsg("table row type and query-specified row type do not match"),
283 : errdetail("Table has type %s at ordinal position %d, but query expects %s.",
284 : format_type_be(attr->atttypid),
285 : attno,
286 : format_type_be(exprType((Node *) tle->expr)))));
287 : }
288 : }
289 56794 : if (attno != resultDesc->natts)
290 0 : ereport(ERROR,
291 : (errcode(ERRCODE_DATATYPE_MISMATCH),
292 : errmsg("table row type and query-specified row type do not match"),
293 : errdetail("Query has too few columns.")));
294 56794 : }
295 :
296 : /*
297 : * ExecProcessReturning --- evaluate a RETURNING list
298 : *
299 : * context: context for the ModifyTable operation
300 : * resultRelInfo: current result rel
301 : * isDelete: true if the operation/merge action is a DELETE
302 : * oldSlot: slot holding old tuple deleted or updated
303 : * newSlot: slot holding new tuple inserted or updated
304 : * planSlot: slot holding tuple returned by top subplan node
305 : *
306 : * Note: If oldSlot and newSlot are NULL, the FDW should have already provided
307 : * econtext's scan tuple and its old & new tuples are not needed (FDW direct-
308 : * modify is disabled if the RETURNING list refers to any OLD/NEW values).
309 : *
310 : * Note: For the SELECT path of INSERT ... ON CONFLICT DO SELECT, oldSlot and
311 : * newSlot are both the existing tuple, since it's not changed.
312 : *
313 : * Returns a slot holding the result tuple
314 : */
315 : static TupleTableSlot *
316 5920 : ExecProcessReturning(ModifyTableContext *context,
317 : ResultRelInfo *resultRelInfo,
318 : bool isDelete,
319 : TupleTableSlot *oldSlot,
320 : TupleTableSlot *newSlot,
321 : TupleTableSlot *planSlot)
322 : {
323 5920 : EState *estate = context->estate;
324 5920 : ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
325 5920 : ExprContext *econtext = projectReturning->pi_exprContext;
326 :
327 : /* Make tuple and any needed join variables available to ExecProject */
328 5920 : if (isDelete)
329 : {
330 : /* return old tuple by default */
331 877 : if (oldSlot)
332 758 : econtext->ecxt_scantuple = oldSlot;
333 : }
334 : else
335 : {
336 : /* return new tuple by default */
337 5043 : if (newSlot)
338 4815 : econtext->ecxt_scantuple = newSlot;
339 : }
340 5920 : econtext->ecxt_outertuple = planSlot;
341 :
342 : /* Make old/new tuples available to ExecProject, if required */
343 5920 : if (oldSlot)
344 2580 : econtext->ecxt_oldtuple = oldSlot;
345 3340 : else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD)
346 140 : econtext->ecxt_oldtuple = ExecGetAllNullSlot(estate, resultRelInfo);
347 : else
348 3200 : econtext->ecxt_oldtuple = NULL; /* No references to OLD columns */
349 :
350 5920 : if (newSlot)
351 4815 : econtext->ecxt_newtuple = newSlot;
352 1105 : else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW)
353 96 : econtext->ecxt_newtuple = ExecGetAllNullSlot(estate, resultRelInfo);
354 : else
355 1009 : econtext->ecxt_newtuple = NULL; /* No references to NEW columns */
356 :
357 : /*
358 : * Tell ExecProject whether or not the OLD/NEW rows actually exist. This
359 : * information is required to evaluate ReturningExpr nodes and also in
360 : * ExecEvalSysVar() and ExecEvalWholeRowVar().
361 : */
362 5920 : if (oldSlot == NULL)
363 3340 : projectReturning->pi_state.flags |= EEO_FLAG_OLD_IS_NULL;
364 : else
365 2580 : projectReturning->pi_state.flags &= ~EEO_FLAG_OLD_IS_NULL;
366 :
367 5920 : if (newSlot == NULL)
368 1105 : projectReturning->pi_state.flags |= EEO_FLAG_NEW_IS_NULL;
369 : else
370 4815 : projectReturning->pi_state.flags &= ~EEO_FLAG_NEW_IS_NULL;
371 :
372 : /* Compute the RETURNING expressions */
373 5920 : return ExecProject(projectReturning);
374 : }
375 :
376 : /*
377 : * ExecCheckTupleVisible -- verify tuple is visible
378 : *
379 : * It would not be consistent with guarantees of the higher isolation levels to
380 : * proceed with avoiding insertion (taking speculative insertion's alternative
381 : * path) on the basis of another tuple that is not visible to MVCC snapshot.
382 : * Check for the need to raise a serialization failure, and do so as necessary.
383 : */
384 : static void
385 2956 : ExecCheckTupleVisible(EState *estate,
386 : Relation rel,
387 : TupleTableSlot *slot)
388 : {
389 2956 : if (!IsolationUsesXactSnapshot())
390 2914 : return;
391 :
392 42 : if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
393 : {
394 : Datum xminDatum;
395 : TransactionId xmin;
396 : bool isnull;
397 :
398 30 : xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
399 : Assert(!isnull);
400 30 : xmin = DatumGetTransactionId(xminDatum);
401 :
402 : /*
403 : * We should not raise a serialization failure if the conflict is
404 : * against a tuple inserted by our own transaction, even if it's not
405 : * visible to our snapshot. (This would happen, for example, if
406 : * conflicting keys are proposed for insertion in a single command.)
407 : */
408 30 : if (!TransactionIdIsCurrentTransactionId(xmin))
409 10 : ereport(ERROR,
410 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
411 : errmsg("could not serialize access due to concurrent update")));
412 : }
413 : }
414 :
415 : /*
416 : * ExecCheckTIDVisible -- convenience variant of ExecCheckTupleVisible()
417 : */
418 : static void
419 139 : ExecCheckTIDVisible(EState *estate,
420 : ResultRelInfo *relinfo,
421 : ItemPointer tid,
422 : TupleTableSlot *tempSlot)
423 : {
424 139 : Relation rel = relinfo->ri_RelationDesc;
425 :
426 : /* Redundantly check isolation level */
427 139 : if (!IsolationUsesXactSnapshot())
428 105 : return;
429 :
430 34 : if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
431 0 : elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
432 34 : ExecCheckTupleVisible(estate, rel, tempSlot);
433 24 : ExecClearTuple(tempSlot);
434 : }
435 :
436 : /*
437 : * Initialize generated columns handling for a tuple
438 : *
439 : * This fills the resultRelInfo's ri_GeneratedExprsI/ri_NumGeneratedNeededI or
440 : * ri_GeneratedExprsU/ri_NumGeneratedNeededU fields, depending on cmdtype.
441 : * This is used only for stored generated columns.
442 : *
443 : * If cmdType == CMD_UPDATE, the ri_extraUpdatedCols field is filled too.
444 : * This is used by both stored and virtual generated columns.
445 : *
446 : * Note: usually, a given query would need only one of ri_GeneratedExprsI and
447 : * ri_GeneratedExprsU per result rel; but MERGE can need both, and so can
448 : * cross-partition UPDATEs, since a partition might be the target of both
449 : * UPDATE and INSERT actions.
450 : */
451 : void
452 31911 : ExecInitGenerated(ResultRelInfo *resultRelInfo,
453 : EState *estate,
454 : CmdType cmdtype)
455 : {
456 31911 : Relation rel = resultRelInfo->ri_RelationDesc;
457 31911 : TupleDesc tupdesc = RelationGetDescr(rel);
458 31911 : int natts = tupdesc->natts;
459 : ExprState **ri_GeneratedExprs;
460 : int ri_NumGeneratedNeeded;
461 : Bitmapset *updatedCols;
462 : MemoryContext oldContext;
463 :
464 : /* Nothing to do if no generated columns */
465 31911 : if (!(tupdesc->constr && (tupdesc->constr->has_generated_stored || tupdesc->constr->has_generated_virtual)))
466 31120 : return;
467 :
468 : /*
469 : * In an UPDATE, we can skip computing any generated columns that do not
470 : * depend on any UPDATE target column. But if there is a BEFORE ROW
471 : * UPDATE trigger, we cannot skip because the trigger might change more
472 : * columns.
473 : */
474 791 : if (cmdtype == CMD_UPDATE &&
475 179 : !(rel->trigdesc && rel->trigdesc->trig_update_before_row))
476 151 : updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
477 : else
478 640 : updatedCols = NULL;
479 :
480 : /*
481 : * Make sure these data structures are built in the per-query memory
482 : * context so they'll survive throughout the query.
483 : */
484 791 : oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
485 :
486 791 : ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
487 791 : ri_NumGeneratedNeeded = 0;
488 :
489 3150 : for (int i = 0; i < natts; i++)
490 : {
491 2363 : char attgenerated = TupleDescAttr(tupdesc, i)->attgenerated;
492 :
493 2363 : if (attgenerated)
494 : {
495 : Expr *expr;
496 :
497 : /* Fetch the GENERATED AS expression tree */
498 838 : expr = (Expr *) build_column_default(rel, i + 1);
499 838 : if (expr == NULL)
500 0 : elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
501 : i + 1, RelationGetRelationName(rel));
502 :
503 : /*
504 : * If it's an update with a known set of update target columns,
505 : * see if we can skip the computation.
506 : */
507 838 : if (updatedCols)
508 : {
509 159 : Bitmapset *attrs_used = NULL;
510 :
511 159 : pull_varattnos((Node *) expr, 1, &attrs_used);
512 :
513 159 : if (!bms_overlap(updatedCols, attrs_used))
514 15 : continue; /* need not update this column */
515 : }
516 :
517 : /* No luck, so prepare the expression for execution */
518 823 : if (attgenerated == ATTRIBUTE_GENERATED_STORED)
519 : {
520 755 : ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
521 751 : ri_NumGeneratedNeeded++;
522 : }
523 :
524 : /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
525 819 : if (cmdtype == CMD_UPDATE)
526 176 : resultRelInfo->ri_extraUpdatedCols =
527 176 : bms_add_member(resultRelInfo->ri_extraUpdatedCols,
528 : i + 1 - FirstLowInvalidHeapAttributeNumber);
529 : }
530 : }
531 :
532 787 : if (ri_NumGeneratedNeeded == 0)
533 : {
534 : /* didn't need it after all */
535 47 : pfree(ri_GeneratedExprs);
536 47 : ri_GeneratedExprs = NULL;
537 : }
538 :
539 : /* Save in appropriate set of fields */
540 787 : if (cmdtype == CMD_UPDATE)
541 : {
542 : /* Don't call twice */
543 : Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
544 :
545 179 : resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
546 179 : resultRelInfo->ri_NumGeneratedNeededU = ri_NumGeneratedNeeded;
547 :
548 179 : resultRelInfo->ri_extraUpdatedCols_valid = true;
549 : }
550 : else
551 : {
552 : /* Don't call twice */
553 : Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
554 :
555 608 : resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
556 608 : resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
557 : }
558 :
559 787 : MemoryContextSwitchTo(oldContext);
560 : }
561 :
562 : /*
563 : * Compute stored generated columns for a tuple
564 : */
565 : void
566 1081 : ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
567 : EState *estate, TupleTableSlot *slot,
568 : CmdType cmdtype)
569 : {
570 1081 : Relation rel = resultRelInfo->ri_RelationDesc;
571 1081 : TupleDesc tupdesc = RelationGetDescr(rel);
572 1081 : int natts = tupdesc->natts;
573 1081 : ExprContext *econtext = GetPerTupleExprContext(estate);
574 : ExprState **ri_GeneratedExprs;
575 : MemoryContext oldContext;
576 : Datum *values;
577 : bool *nulls;
578 :
579 : /* We should not be called unless this is true */
580 : Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
581 :
582 : /*
583 : * Initialize the expressions if we didn't already, and check whether we
584 : * can exit early because nothing needs to be computed.
585 : */
586 1081 : if (cmdtype == CMD_UPDATE)
587 : {
588 168 : if (resultRelInfo->ri_GeneratedExprsU == NULL)
589 127 : ExecInitGenerated(resultRelInfo, estate, cmdtype);
590 168 : if (resultRelInfo->ri_NumGeneratedNeededU == 0)
591 11 : return;
592 157 : ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
593 : }
594 : else
595 : {
596 913 : if (resultRelInfo->ri_GeneratedExprsI == NULL)
597 612 : ExecInitGenerated(resultRelInfo, estate, cmdtype);
598 : /* Early exit is impossible given the prior Assert */
599 : Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
600 909 : ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
601 : }
602 :
603 1066 : oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
604 :
605 1066 : values = palloc_array(Datum, natts);
606 1066 : nulls = palloc_array(bool, natts);
607 :
608 1066 : slot_getallattrs(slot);
609 1066 : memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
610 :
611 4265 : for (int i = 0; i < natts; i++)
612 : {
613 3215 : CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
614 :
615 3215 : if (ri_GeneratedExprs[i])
616 : {
617 : Datum val;
618 : bool isnull;
619 :
620 : Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
621 :
622 1079 : econtext->ecxt_scantuple = slot;
623 :
624 1079 : val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
625 :
626 : /*
627 : * We must make a copy of val as we have no guarantees about where
628 : * memory for a pass-by-reference Datum is located.
629 : */
630 1063 : if (!isnull)
631 1007 : val = datumCopy(val, attr->attbyval, attr->attlen);
632 :
633 1063 : values[i] = val;
634 1063 : nulls[i] = isnull;
635 : }
636 : else
637 : {
638 2136 : if (!nulls[i])
639 2042 : values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
640 : }
641 : }
642 :
643 1050 : ExecClearTuple(slot);
644 1050 : memcpy(slot->tts_values, values, sizeof(*values) * natts);
645 1050 : memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
646 1050 : ExecStoreVirtualTuple(slot);
647 1050 : ExecMaterializeSlot(slot);
648 :
649 1050 : MemoryContextSwitchTo(oldContext);
650 : }
651 :
652 : /*
653 : * ExecInitInsertProjection
654 : * Do one-time initialization of projection data for INSERT tuples.
655 : *
656 : * INSERT queries may need a projection to filter out junk attrs in the tlist.
657 : *
658 : * This is also a convenient place to verify that the
659 : * output of an INSERT matches the target table.
660 : */
661 : static void
662 56090 : ExecInitInsertProjection(ModifyTableState *mtstate,
663 : ResultRelInfo *resultRelInfo)
664 : {
665 56090 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
666 56090 : Plan *subplan = outerPlan(node);
667 56090 : EState *estate = mtstate->ps.state;
668 56090 : List *insertTargetList = NIL;
669 56090 : bool need_projection = false;
670 : ListCell *l;
671 :
672 : /* Extract non-junk columns of the subplan's result tlist. */
673 175897 : foreach(l, subplan->targetlist)
674 : {
675 119807 : TargetEntry *tle = (TargetEntry *) lfirst(l);
676 :
677 119807 : if (!tle->resjunk)
678 119807 : insertTargetList = lappend(insertTargetList, tle);
679 : else
680 0 : need_projection = true;
681 : }
682 :
683 : /*
684 : * The junk-free list must produce a tuple suitable for the result
685 : * relation.
686 : */
687 56090 : ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
688 :
689 : /* We'll need a slot matching the table's format. */
690 56090 : resultRelInfo->ri_newTupleSlot =
691 56090 : table_slot_create(resultRelInfo->ri_RelationDesc,
692 : &estate->es_tupleTable);
693 :
694 : /* Build ProjectionInfo if needed (it probably isn't). */
695 56090 : if (need_projection)
696 : {
697 0 : TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
698 :
699 : /* need an expression context to do the projection */
700 0 : if (mtstate->ps.ps_ExprContext == NULL)
701 0 : ExecAssignExprContext(estate, &mtstate->ps);
702 :
703 0 : resultRelInfo->ri_projectNew =
704 0 : ExecBuildProjectionInfo(insertTargetList,
705 : mtstate->ps.ps_ExprContext,
706 : resultRelInfo->ri_newTupleSlot,
707 : &mtstate->ps,
708 : relDesc);
709 : }
710 :
711 56090 : resultRelInfo->ri_projectNewInfoValid = true;
712 56090 : }
713 :
714 : /*
715 : * ExecInitUpdateProjection
716 : * Do one-time initialization of projection data for UPDATE tuples.
717 : *
718 : * UPDATE always needs a projection, because (1) there's always some junk
719 : * attrs, and (2) we may need to merge values of not-updated columns from
720 : * the old tuple into the final tuple. In UPDATE, the tuple arriving from
721 : * the subplan contains only new values for the changed columns, plus row
722 : * identity info in the junk attrs.
723 : *
724 : * This is "one-time" for any given result rel, but we might touch more than
725 : * one result rel in the course of an inherited UPDATE, and each one needs
726 : * its own projection due to possible column order variation.
727 : *
728 : * This is also a convenient place to verify that the output of an UPDATE
729 : * matches the target table (ExecBuildUpdateProjection does that).
730 : */
731 : static void
732 8719 : ExecInitUpdateProjection(ModifyTableState *mtstate,
733 : ResultRelInfo *resultRelInfo)
734 : {
735 8719 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
736 8719 : Plan *subplan = outerPlan(node);
737 8719 : EState *estate = mtstate->ps.state;
738 8719 : TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
739 : int whichrel;
740 : List *updateColnos;
741 :
742 : /*
743 : * Usually, mt_lastResultIndex matches the target rel. If it happens not
744 : * to, we can get the index the hard way with an integer division.
745 : */
746 8719 : whichrel = mtstate->mt_lastResultIndex;
747 8719 : if (resultRelInfo != mtstate->resultRelInfo + whichrel)
748 : {
749 0 : whichrel = resultRelInfo - mtstate->resultRelInfo;
750 : Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
751 : }
752 :
753 8719 : updateColnos = (List *) list_nth(mtstate->mt_updateColnosLists, whichrel);
754 :
755 : /*
756 : * For UPDATE, we use the old tuple to fill up missing values in the tuple
757 : * produced by the subplan to get the new tuple. We need two slots, both
758 : * matching the table's desired format.
759 : */
760 8719 : resultRelInfo->ri_oldTupleSlot =
761 8719 : table_slot_create(resultRelInfo->ri_RelationDesc,
762 : &estate->es_tupleTable);
763 8719 : resultRelInfo->ri_newTupleSlot =
764 8719 : table_slot_create(resultRelInfo->ri_RelationDesc,
765 : &estate->es_tupleTable);
766 :
767 : /* need an expression context to do the projection */
768 8719 : if (mtstate->ps.ps_ExprContext == NULL)
769 7430 : ExecAssignExprContext(estate, &mtstate->ps);
770 :
771 8719 : resultRelInfo->ri_projectNew =
772 8719 : ExecBuildUpdateProjection(subplan->targetlist,
773 : false, /* subplan did the evaluation */
774 : updateColnos,
775 : relDesc,
776 : mtstate->ps.ps_ExprContext,
777 : resultRelInfo->ri_newTupleSlot,
778 : &mtstate->ps);
779 :
780 8719 : resultRelInfo->ri_projectNewInfoValid = true;
781 8719 : }
782 :
783 : /*
784 : * ExecGetInsertNewTuple
785 : * This prepares a "new" tuple ready to be inserted into given result
786 : * relation, by removing any junk columns of the plan's output tuple
787 : * and (if necessary) coercing the tuple to the right tuple format.
788 : */
789 : static TupleTableSlot *
790 7777075 : ExecGetInsertNewTuple(ResultRelInfo *relinfo,
791 : TupleTableSlot *planSlot)
792 : {
793 7777075 : ProjectionInfo *newProj = relinfo->ri_projectNew;
794 : ExprContext *econtext;
795 :
796 : /*
797 : * If there's no projection to be done, just make sure the slot is of the
798 : * right type for the target rel. If the planSlot is the right type we
799 : * can use it as-is, else copy the data into ri_newTupleSlot.
800 : */
801 7777075 : if (newProj == NULL)
802 : {
803 7777075 : if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
804 : {
805 7264017 : ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
806 7264017 : return relinfo->ri_newTupleSlot;
807 : }
808 : else
809 513058 : return planSlot;
810 : }
811 :
812 : /*
813 : * Else project; since the projection output slot is ri_newTupleSlot, this
814 : * will also fix any slot-type problem.
815 : *
816 : * Note: currently, this is dead code, because INSERT cases don't receive
817 : * any junk columns so there's never a projection to be done.
818 : */
819 0 : econtext = newProj->pi_exprContext;
820 0 : econtext->ecxt_outertuple = planSlot;
821 0 : return ExecProject(newProj);
822 : }
823 :
824 : /*
825 : * ExecGetUpdateNewTuple
826 : * This prepares a "new" tuple by combining an UPDATE subplan's output
827 : * tuple (which contains values of changed columns) with unchanged
828 : * columns taken from the old tuple.
829 : *
830 : * The subplan tuple might also contain junk columns, which are ignored.
831 : * Note that the projection also ensures we have a slot of the right type.
832 : */
833 : TupleTableSlot *
834 2220526 : ExecGetUpdateNewTuple(ResultRelInfo *relinfo,
835 : TupleTableSlot *planSlot,
836 : TupleTableSlot *oldSlot)
837 : {
838 2220526 : ProjectionInfo *newProj = relinfo->ri_projectNew;
839 : ExprContext *econtext;
840 :
841 : /* Use a few extra Asserts to protect against outside callers */
842 : Assert(relinfo->ri_projectNewInfoValid);
843 : Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
844 : Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
845 :
846 2220526 : econtext = newProj->pi_exprContext;
847 2220526 : econtext->ecxt_outertuple = planSlot;
848 2220526 : econtext->ecxt_scantuple = oldSlot;
849 2220526 : return ExecProject(newProj);
850 : }
851 :
852 : /* ----------------------------------------------------------------
853 : * ExecInsert
854 : *
855 : * For INSERT, we have to insert the tuple into the target relation
856 : * (or partition thereof) and insert appropriate tuples into the index
857 : * relations.
858 : *
859 : * slot contains the new tuple value to be stored.
860 : *
861 : * Returns RETURNING result if any, otherwise NULL.
862 : * *inserted_tuple is the tuple that's effectively inserted;
863 : * *insert_destrel is the relation where it was inserted.
864 : * These are only set on success.
865 : *
866 : * This may change the currently active tuple conversion map in
867 : * mtstate->mt_transition_capture, so the callers must take care to
868 : * save the previous value to avoid losing track of it.
869 : * ----------------------------------------------------------------
870 : */
871 : static TupleTableSlot *
872 7780025 : ExecInsert(ModifyTableContext *context,
873 : ResultRelInfo *resultRelInfo,
874 : TupleTableSlot *slot,
875 : bool canSetTag,
876 : TupleTableSlot **inserted_tuple,
877 : ResultRelInfo **insert_destrel)
878 : {
879 7780025 : ModifyTableState *mtstate = context->mtstate;
880 7780025 : EState *estate = context->estate;
881 : Relation resultRelationDesc;
882 7780025 : List *recheckIndexes = NIL;
883 7780025 : TupleTableSlot *planSlot = context->planSlot;
884 7780025 : TupleTableSlot *result = NULL;
885 : TransitionCaptureState *ar_insert_trig_tcs;
886 7780025 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
887 7780025 : OnConflictAction onconflict = node->onConflictAction;
888 7780025 : PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
889 : MemoryContext oldContext;
890 :
891 : /*
892 : * If the input result relation is a partitioned table, find the leaf
893 : * partition to insert the tuple into.
894 : */
895 7780025 : if (proute)
896 : {
897 : ResultRelInfo *partRelInfo;
898 :
899 481010 : slot = ExecPrepareTupleRouting(mtstate, estate, proute,
900 : resultRelInfo, slot,
901 : &partRelInfo);
902 480866 : resultRelInfo = partRelInfo;
903 : }
904 :
905 7779881 : ExecMaterializeSlot(slot);
906 :
907 7779881 : resultRelationDesc = resultRelInfo->ri_RelationDesc;
908 :
909 : /*
910 : * Open the table's indexes, if we have not done so already, so that we
911 : * can add new index entries for the inserted tuple.
912 : */
913 7779881 : if (resultRelationDesc->rd_rel->relhasindex &&
914 2408901 : resultRelInfo->ri_IndexRelationDescs == NULL)
915 20808 : ExecOpenIndices(resultRelInfo, onconflict != ONCONFLICT_NONE);
916 :
917 : /*
918 : * BEFORE ROW INSERT Triggers.
919 : *
920 : * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
921 : * INSERT ... ON CONFLICT statement. We cannot check for constraint
922 : * violations before firing these triggers, because they can change the
923 : * values to insert. Also, they can run arbitrary user-defined code with
924 : * side-effects that we can't cancel by just not inserting the tuple.
925 : */
926 7779881 : if (resultRelInfo->ri_TrigDesc &&
927 452177 : resultRelInfo->ri_TrigDesc->trig_insert_before_row)
928 : {
929 : /* Flush any pending inserts, so rows are visible to the triggers */
930 1418 : if (estate->es_insert_pending_result_relations != NIL)
931 3 : ExecPendingInserts(estate);
932 :
933 1418 : if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
934 131 : return NULL; /* "do nothing" */
935 : }
936 :
937 : /* INSTEAD OF ROW INSERT Triggers */
938 7779688 : if (resultRelInfo->ri_TrigDesc &&
939 451984 : resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
940 : {
941 111 : if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
942 4 : return NULL; /* "do nothing" */
943 : }
944 7779577 : else if (resultRelInfo->ri_FdwRoutine)
945 : {
946 : /*
947 : * GENERATED expressions might reference the tableoid column, so
948 : * (re-)initialize tts_tableOid before evaluating them.
949 : */
950 1010 : slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
951 :
952 : /*
953 : * Compute stored generated columns
954 : */
955 1010 : if (resultRelationDesc->rd_att->constr &&
956 179 : resultRelationDesc->rd_att->constr->has_generated_stored)
957 4 : ExecComputeStoredGenerated(resultRelInfo, estate, slot,
958 : CMD_INSERT);
959 :
960 : /*
961 : * If the FDW supports batching, and batching is requested, accumulate
962 : * rows and insert them in batches. Otherwise use the per-row inserts.
963 : */
964 1010 : if (resultRelInfo->ri_BatchSize > 1)
965 : {
966 145 : bool flushed = false;
967 :
968 : /*
969 : * When we've reached the desired batch size, perform the
970 : * insertion.
971 : */
972 145 : if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize)
973 : {
974 10 : ExecBatchInsert(mtstate, resultRelInfo,
975 : resultRelInfo->ri_Slots,
976 : resultRelInfo->ri_PlanSlots,
977 : resultRelInfo->ri_NumSlots,
978 : estate, canSetTag);
979 10 : flushed = true;
980 : }
981 :
982 145 : oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
983 :
984 145 : if (resultRelInfo->ri_Slots == NULL)
985 : {
986 15 : resultRelInfo->ri_Slots = palloc_array(TupleTableSlot *, resultRelInfo->ri_BatchSize);
987 15 : resultRelInfo->ri_PlanSlots = palloc_array(TupleTableSlot *, resultRelInfo->ri_BatchSize);
988 : }
989 :
990 : /*
991 : * Initialize the batch slots. We don't know how many slots will
992 : * be needed, so we initialize them as the batch grows, and we
993 : * keep them across batches. To mitigate an inefficiency in how
994 : * resource owner handles objects with many references (as with
995 : * many slots all referencing the same tuple descriptor) we copy
996 : * the appropriate tuple descriptor for each slot.
997 : */
998 145 : if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
999 : {
1000 72 : TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
1001 : TupleDesc plan_tdesc =
1002 72 : CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
1003 :
1004 144 : resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
1005 72 : MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
1006 :
1007 144 : resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
1008 72 : MakeSingleTupleTableSlot(plan_tdesc, planSlot->tts_ops);
1009 :
1010 : /* remember how many batch slots we initialized */
1011 72 : resultRelInfo->ri_NumSlotsInitialized++;
1012 : }
1013 :
1014 145 : ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
1015 : slot);
1016 :
1017 145 : ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
1018 : planSlot);
1019 :
1020 : /*
1021 : * If these are the first tuples stored in the buffers, add the
1022 : * target rel and the mtstate to the
1023 : * es_insert_pending_result_relations and
1024 : * es_insert_pending_modifytables lists respectively, except in
1025 : * the case where flushing was done above, in which case they
1026 : * would already have been added to the lists, so no need to do
1027 : * this.
1028 : */
1029 145 : if (resultRelInfo->ri_NumSlots == 0 && !flushed)
1030 : {
1031 : Assert(!list_member_ptr(estate->es_insert_pending_result_relations,
1032 : resultRelInfo));
1033 19 : estate->es_insert_pending_result_relations =
1034 19 : lappend(estate->es_insert_pending_result_relations,
1035 : resultRelInfo);
1036 19 : estate->es_insert_pending_modifytables =
1037 19 : lappend(estate->es_insert_pending_modifytables, mtstate);
1038 : }
1039 : Assert(list_member_ptr(estate->es_insert_pending_result_relations,
1040 : resultRelInfo));
1041 :
1042 145 : resultRelInfo->ri_NumSlots++;
1043 :
1044 145 : MemoryContextSwitchTo(oldContext);
1045 :
1046 145 : return NULL;
1047 : }
1048 :
1049 : /*
1050 : * insert into foreign table: let the FDW do it
1051 : */
1052 865 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
1053 : resultRelInfo,
1054 : slot,
1055 : planSlot);
1056 :
1057 862 : if (slot == NULL) /* "do nothing" */
1058 2 : return NULL;
1059 :
1060 : /*
1061 : * AFTER ROW Triggers or RETURNING expressions might reference the
1062 : * tableoid column, so (re-)initialize tts_tableOid before evaluating
1063 : * them. (This covers the case where the FDW replaced the slot.)
1064 : */
1065 860 : slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1066 : }
1067 : else
1068 : {
1069 : WCOKind wco_kind;
1070 :
1071 : /*
1072 : * Constraints and GENERATED expressions might reference the tableoid
1073 : * column, so (re-)initialize tts_tableOid before evaluating them.
1074 : */
1075 7778567 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1076 :
1077 : /*
1078 : * Compute stored generated columns
1079 : */
1080 7778567 : if (resultRelationDesc->rd_att->constr &&
1081 2564828 : resultRelationDesc->rd_att->constr->has_generated_stored)
1082 884 : ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1083 : CMD_INSERT);
1084 :
1085 : /*
1086 : * Check any RLS WITH CHECK policies.
1087 : *
1088 : * Normally we should check INSERT policies. But if the insert is the
1089 : * result of a partition key update that moved the tuple to a new
1090 : * partition, we should instead check UPDATE policies, because we are
1091 : * executing policies defined on the target table, and not those
1092 : * defined on the child partitions.
1093 : *
1094 : * If we're running MERGE, we refer to the action that we're executing
1095 : * to know if we're doing an INSERT or UPDATE to a partition table.
1096 : */
1097 7778547 : if (mtstate->operation == CMD_UPDATE)
1098 549 : wco_kind = WCO_RLS_UPDATE_CHECK;
1099 7777998 : else if (mtstate->operation == CMD_MERGE)
1100 1181 : wco_kind = (mtstate->mt_merge_action->mas_action->commandType == CMD_UPDATE) ?
1101 1181 : WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK;
1102 : else
1103 7776817 : wco_kind = WCO_RLS_INSERT_CHECK;
1104 :
1105 : /*
1106 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind
1107 : * we are looking for at this point.
1108 : */
1109 7778547 : if (resultRelInfo->ri_WithCheckOptions != NIL)
1110 474 : ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
1111 :
1112 : /*
1113 : * Check the constraints of the tuple.
1114 : */
1115 7778419 : if (resultRelationDesc->rd_att->constr)
1116 2564736 : ExecConstraints(resultRelInfo, slot, estate);
1117 :
1118 : /*
1119 : * Also check the tuple against the partition constraint, if there is
1120 : * one; except that if we got here via tuple-routing, we don't need to
1121 : * if there's no BR trigger defined on the partition.
1122 : */
1123 7777902 : if (resultRelationDesc->rd_rel->relispartition &&
1124 483362 : (resultRelInfo->ri_RootResultRelInfo == NULL ||
1125 480506 : (resultRelInfo->ri_TrigDesc &&
1126 1113 : resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
1127 2994 : ExecPartitionCheck(resultRelInfo, slot, estate, true);
1128 :
1129 7777790 : if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
1130 2215 : {
1131 : /* Perform a speculative insertion. */
1132 : uint32 specToken;
1133 : ItemPointerData conflictTid;
1134 : ItemPointerData invalidItemPtr;
1135 : bool specConflict;
1136 : List *arbiterIndexes;
1137 :
1138 5312 : ItemPointerSetInvalid(&invalidItemPtr);
1139 5312 : arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
1140 :
1141 : /*
1142 : * Do a non-conclusive check for conflicts first.
1143 : *
1144 : * We're not holding any locks yet, so this doesn't guarantee that
1145 : * the later insert won't conflict. But it avoids leaving behind
1146 : * a lot of canceled speculative insertions, if you run a lot of
1147 : * INSERT ON CONFLICT statements that do conflict.
1148 : *
1149 : * We loop back here if we find a conflict below, either during
1150 : * the pre-check, or when we re-check after inserting the tuple
1151 : * speculatively. Better allow interrupts in case some bug makes
1152 : * this an infinite loop.
1153 : */
1154 14 : vlock:
1155 5326 : CHECK_FOR_INTERRUPTS();
1156 5326 : specConflict = false;
1157 5326 : if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
1158 : &conflictTid, &invalidItemPtr,
1159 : arbiterIndexes))
1160 : {
1161 : /* committed conflict tuple found */
1162 3092 : if (onconflict == ONCONFLICT_UPDATE)
1163 : {
1164 : /*
1165 : * In case of ON CONFLICT DO UPDATE, execute the UPDATE
1166 : * part. Be prepared to retry if the UPDATE fails because
1167 : * of another concurrent UPDATE/DELETE to the conflict
1168 : * tuple.
1169 : */
1170 2761 : TupleTableSlot *returning = NULL;
1171 :
1172 2761 : if (ExecOnConflictUpdate(context, resultRelInfo,
1173 : &conflictTid, slot, canSetTag,
1174 : &returning))
1175 : {
1176 2706 : InstrCountTuples2(&mtstate->ps, 1);
1177 2706 : return returning;
1178 : }
1179 : else
1180 3 : goto vlock;
1181 : }
1182 331 : else if (onconflict == ONCONFLICT_SELECT)
1183 : {
1184 : /*
1185 : * In case of ON CONFLICT DO SELECT, optionally lock the
1186 : * conflicting tuple, fetch it and project RETURNING on
1187 : * it. Be prepared to retry if locking fails because of a
1188 : * concurrent UPDATE/DELETE to the conflict tuple.
1189 : */
1190 192 : TupleTableSlot *returning = NULL;
1191 :
1192 192 : if (ExecOnConflictSelect(context, resultRelInfo,
1193 : &conflictTid, slot, canSetTag,
1194 : &returning))
1195 : {
1196 176 : InstrCountTuples2(&mtstate->ps, 1);
1197 176 : return returning;
1198 : }
1199 : else
1200 0 : goto vlock;
1201 : }
1202 : else
1203 : {
1204 : /*
1205 : * In case of ON CONFLICT DO NOTHING, do nothing. However,
1206 : * verify that the tuple is visible to the executor's MVCC
1207 : * snapshot at higher isolation levels.
1208 : *
1209 : * Using ExecGetReturningSlot() to store the tuple for the
1210 : * recheck isn't that pretty, but we can't trivially use
1211 : * the input slot, because it might not be of a compatible
1212 : * type. As there's no conflicting usage of
1213 : * ExecGetReturningSlot() in the DO NOTHING case...
1214 : */
1215 : Assert(onconflict == ONCONFLICT_NOTHING);
1216 139 : ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
1217 : ExecGetReturningSlot(estate, resultRelInfo));
1218 129 : InstrCountTuples2(&mtstate->ps, 1);
1219 129 : return NULL;
1220 : }
1221 : }
1222 :
1223 : /*
1224 : * Before we start insertion proper, acquire our "speculative
1225 : * insertion lock". Others can use that to wait for us to decide
1226 : * if we're going to go ahead with the insertion, instead of
1227 : * waiting for the whole transaction to complete.
1228 : */
1229 2230 : INJECTION_POINT("exec-insert-before-insert-speculative", NULL);
1230 2230 : specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
1231 :
1232 : /* insert the tuple, with the speculative token */
1233 2230 : table_tuple_insert_speculative(resultRelationDesc, slot,
1234 : estate->es_output_cid,
1235 : 0,
1236 : NULL,
1237 : specToken);
1238 :
1239 : /* insert index entries for tuple */
1240 2230 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1241 : estate, EIIT_NO_DUPE_ERROR,
1242 : slot, arbiterIndexes,
1243 : &specConflict);
1244 :
1245 : /* adjust the tuple's state accordingly */
1246 2226 : table_tuple_complete_speculative(resultRelationDesc, slot,
1247 2226 : specToken, !specConflict);
1248 :
1249 : /*
1250 : * Wake up anyone waiting for our decision. They will re-check
1251 : * the tuple, see that it's no longer speculative, and wait on our
1252 : * XID as if this was a regularly inserted tuple all along. Or if
1253 : * we killed the tuple, they will see it's dead, and proceed as if
1254 : * the tuple never existed.
1255 : */
1256 2226 : SpeculativeInsertionLockRelease(GetCurrentTransactionId());
1257 :
1258 : /*
1259 : * If there was a conflict, start from the beginning. We'll do
1260 : * the pre-check again, which will now find the conflicting tuple
1261 : * (unless it aborts before we get there).
1262 : */
1263 2226 : if (specConflict)
1264 : {
1265 11 : list_free(recheckIndexes);
1266 11 : goto vlock;
1267 : }
1268 :
1269 : /* Since there was no insertion conflict, we're done */
1270 : }
1271 : else
1272 : {
1273 : /* insert the tuple normally */
1274 7772478 : table_tuple_insert(resultRelationDesc, slot,
1275 : estate->es_output_cid,
1276 : 0, NULL);
1277 :
1278 : /* insert index entries for tuple */
1279 7772458 : if (resultRelInfo->ri_NumIndices > 0)
1280 2403208 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo, estate,
1281 : 0, slot, NIL,
1282 : NULL);
1283 : }
1284 : }
1285 :
1286 7775261 : if (canSetTag)
1287 7773393 : (estate->es_processed)++;
1288 :
1289 : /*
1290 : * If this insert is the result of a partition key update that moved the
1291 : * tuple to a new partition, put this row into the transition NEW TABLE,
1292 : * if there is one. We need to do this separately for DELETE and INSERT
1293 : * because they happen on different tables.
1294 : */
1295 7775261 : ar_insert_trig_tcs = mtstate->mt_transition_capture;
1296 7775261 : if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
1297 36 : && mtstate->mt_transition_capture->tcs_update_new_table)
1298 : {
1299 32 : ExecARUpdateTriggers(estate, resultRelInfo,
1300 : NULL, NULL,
1301 : NULL,
1302 : NULL,
1303 : slot,
1304 : NULL,
1305 32 : mtstate->mt_transition_capture,
1306 : false);
1307 :
1308 : /*
1309 : * We've already captured the NEW TABLE row, so make sure any AR
1310 : * INSERT trigger fired below doesn't capture it again.
1311 : */
1312 32 : ar_insert_trig_tcs = NULL;
1313 : }
1314 :
1315 : /* AFTER ROW INSERT Triggers */
1316 7775261 : ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
1317 : ar_insert_trig_tcs);
1318 :
1319 7775260 : list_free(recheckIndexes);
1320 :
1321 : /*
1322 : * Check any WITH CHECK OPTION constraints from parent views. We are
1323 : * required to do this after testing all constraints and uniqueness
1324 : * violations per the SQL spec, so we do it after actually inserting the
1325 : * record into the heap and all indexes.
1326 : *
1327 : * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
1328 : * tuple will never be seen, if it violates the WITH CHECK OPTION.
1329 : *
1330 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1331 : * are looking for at this point.
1332 : */
1333 7775260 : if (resultRelInfo->ri_WithCheckOptions != NIL)
1334 294 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1335 :
1336 : /* Process RETURNING if present */
1337 7775164 : if (resultRelInfo->ri_projectReturning)
1338 : {
1339 3023 : TupleTableSlot *oldSlot = NULL;
1340 :
1341 : /*
1342 : * If this is part of a cross-partition UPDATE, and the RETURNING list
1343 : * refers to any OLD columns, ExecDelete() will have saved the tuple
1344 : * deleted from the original partition, which we must use here to
1345 : * compute the OLD column values. Otherwise, all OLD column values
1346 : * will be NULL.
1347 : */
1348 3023 : if (context->cpDeletedSlot)
1349 : {
1350 : TupleConversionMap *tupconv_map;
1351 :
1352 : /*
1353 : * Convert the OLD tuple to the new partition's format/slot, if
1354 : * needed. Note that ExecDelete() already converted it to the
1355 : * root's partition's format/slot.
1356 : */
1357 30 : oldSlot = context->cpDeletedSlot;
1358 30 : tupconv_map = ExecGetRootToChildMap(resultRelInfo, estate);
1359 30 : if (tupconv_map != NULL)
1360 : {
1361 10 : oldSlot = execute_attr_map_slot(tupconv_map->attrMap,
1362 : oldSlot,
1363 : ExecGetReturningSlot(estate,
1364 : resultRelInfo));
1365 :
1366 10 : oldSlot->tts_tableOid = context->cpDeletedSlot->tts_tableOid;
1367 10 : ItemPointerCopy(&context->cpDeletedSlot->tts_tid, &oldSlot->tts_tid);
1368 : }
1369 : }
1370 :
1371 3023 : result = ExecProcessReturning(context, resultRelInfo, false,
1372 : oldSlot, slot, planSlot);
1373 :
1374 : /*
1375 : * For a cross-partition UPDATE, release the old tuple, first making
1376 : * sure that the result slot has a local copy of any pass-by-reference
1377 : * values.
1378 : */
1379 3015 : if (context->cpDeletedSlot)
1380 : {
1381 30 : ExecMaterializeSlot(result);
1382 30 : ExecClearTuple(oldSlot);
1383 30 : if (context->cpDeletedSlot != oldSlot)
1384 10 : ExecClearTuple(context->cpDeletedSlot);
1385 30 : context->cpDeletedSlot = NULL;
1386 : }
1387 : }
1388 :
1389 7775156 : if (inserted_tuple)
1390 565 : *inserted_tuple = slot;
1391 7775156 : if (insert_destrel)
1392 565 : *insert_destrel = resultRelInfo;
1393 :
1394 7775156 : return result;
1395 : }
1396 :
1397 : /* ----------------------------------------------------------------
1398 : * ExecForPortionOfLeftovers
1399 : *
1400 : * Insert tuples for the untouched portion of a row in a FOR
1401 : * PORTION OF UPDATE/DELETE
1402 : * ----------------------------------------------------------------
1403 : */
1404 : static void
1405 815 : ExecForPortionOfLeftovers(ModifyTableContext *context,
1406 : EState *estate,
1407 : ResultRelInfo *resultRelInfo,
1408 : ItemPointer tupleid)
1409 : {
1410 815 : ModifyTableState *mtstate = context->mtstate;
1411 815 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
1412 815 : ForPortionOfExpr *forPortionOf = (ForPortionOfExpr *) node->forPortionOf;
1413 : AttrNumber rangeAttno;
1414 : Datum oldRange;
1415 : TypeCacheEntry *typcache;
1416 : ForPortionOfState *fpoState;
1417 : TupleTableSlot *oldtupleSlot;
1418 : TupleTableSlot *leftoverSlot;
1419 815 : TupleConversionMap *map = NULL;
1420 815 : HeapTuple oldtuple = NULL;
1421 : CmdType oldOperation;
1422 : TransitionCaptureState *oldTcs;
1423 : FmgrInfo flinfo;
1424 : PgStat_FunctionCallUsage fcusage;
1425 : ReturnSetInfo rsi;
1426 815 : bool didInit = false;
1427 815 : bool shouldFree = false;
1428 :
1429 815 : LOCAL_FCINFO(fcinfo, 2);
1430 :
1431 815 : if (!resultRelInfo->ri_forPortionOf)
1432 : {
1433 : /*
1434 : * If we don't have a ForPortionOfState yet, we must be a partition
1435 : * child being hit for the first time. Make a copy from the root, with
1436 : * our own TupleTableSlot. We do this lazily so that we don't pay the
1437 : * price of unused partitions.
1438 : */
1439 56 : ForPortionOfState *leafState = makeNode(ForPortionOfState);
1440 :
1441 56 : if (!mtstate->rootResultRelInfo)
1442 0 : elog(ERROR, "no root relation but ri_forPortionOf is uninitialized");
1443 :
1444 56 : fpoState = mtstate->rootResultRelInfo->ri_forPortionOf;
1445 : Assert(fpoState);
1446 :
1447 56 : leafState->fp_rangeName = fpoState->fp_rangeName;
1448 56 : leafState->fp_rangeType = fpoState->fp_rangeType;
1449 56 : leafState->fp_rangeAttno = fpoState->fp_rangeAttno;
1450 56 : leafState->fp_targetRange = fpoState->fp_targetRange;
1451 56 : leafState->fp_Leftover = fpoState->fp_Leftover;
1452 : /* Each partition needs a slot matching its tuple descriptor */
1453 56 : leafState->fp_Existing =
1454 56 : table_slot_create(resultRelInfo->ri_RelationDesc,
1455 56 : &mtstate->ps.state->es_tupleTable);
1456 :
1457 56 : resultRelInfo->ri_forPortionOf = leafState;
1458 : }
1459 815 : fpoState = resultRelInfo->ri_forPortionOf;
1460 815 : oldtupleSlot = fpoState->fp_Existing;
1461 815 : leftoverSlot = fpoState->fp_Leftover;
1462 :
1463 : /*
1464 : * Get the old pre-UPDATE/DELETE tuple. We will use its range to compute
1465 : * untouched parts of history, and if necessary we will insert copies with
1466 : * truncated start/end times.
1467 : *
1468 : * We have already locked the tuple in ExecUpdate/ExecDelete, and it has
1469 : * passed EvalPlanQual. This ensures that concurrent updates in READ
1470 : * COMMITTED can't insert conflicting temporal leftovers.
1471 : *
1472 : * It does *not* protect against concurrent update/deletes overlooking
1473 : * each others' leftovers though. See our isolation tests for details
1474 : * about that and a viable workaround.
1475 : */
1476 815 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc, tupleid, SnapshotAny, oldtupleSlot))
1477 0 : elog(ERROR, "failed to fetch tuple for FOR PORTION OF");
1478 :
1479 : /*
1480 : * Get the old range of the record being updated/deleted. Must read with
1481 : * the attno of the leaf partition being updated.
1482 : */
1483 :
1484 815 : rangeAttno = forPortionOf->rangeVar->varattno;
1485 815 : if (resultRelInfo->ri_RootResultRelInfo)
1486 64 : map = ExecGetChildToRootMap(resultRelInfo);
1487 815 : if (map != NULL)
1488 16 : rangeAttno = map->attrMap->attnums[rangeAttno - 1];
1489 815 : slot_getallattrs(oldtupleSlot);
1490 :
1491 815 : if (oldtupleSlot->tts_isnull[rangeAttno - 1])
1492 0 : elog(ERROR, "found a NULL range in a temporal table");
1493 815 : oldRange = oldtupleSlot->tts_values[rangeAttno - 1];
1494 :
1495 : /*
1496 : * Get the range's type cache entry. This is worth caching for the whole
1497 : * UPDATE/DELETE as range functions do.
1498 : */
1499 :
1500 815 : typcache = fpoState->fp_leftoverstypcache;
1501 815 : if (typcache == NULL)
1502 : {
1503 676 : typcache = lookup_type_cache(forPortionOf->rangeType, 0);
1504 676 : fpoState->fp_leftoverstypcache = typcache;
1505 : }
1506 :
1507 : /*
1508 : * Get the ranges to the left/right of the targeted range. We call a SETOF
1509 : * support function and insert as many temporal leftovers as it gives us.
1510 : * Although rangetypes have 0/1/2 leftovers, multiranges have 0/1, and
1511 : * other types may have more.
1512 : */
1513 :
1514 815 : fmgr_info(forPortionOf->withoutPortionProc, &flinfo);
1515 815 : rsi.type = T_ReturnSetInfo;
1516 815 : rsi.econtext = mtstate->ps.ps_ExprContext;
1517 815 : rsi.expectedDesc = NULL;
1518 815 : rsi.allowedModes = (int) (SFRM_ValuePerCall);
1519 815 : rsi.returnMode = SFRM_ValuePerCall;
1520 : /* isDone is filled below */
1521 815 : rsi.setResult = NULL;
1522 815 : rsi.setDesc = NULL;
1523 :
1524 815 : InitFunctionCallInfoData(*fcinfo, &flinfo, 2, InvalidOid, NULL, (Node *) &rsi);
1525 815 : fcinfo->args[0].value = oldRange;
1526 815 : fcinfo->args[0].isnull = false;
1527 815 : fcinfo->args[1].value = fpoState->fp_targetRange;
1528 815 : fcinfo->args[1].isnull = false;
1529 :
1530 : /*
1531 : * If there are partitions, we must insert into the root table, so we get
1532 : * tuple routing. We already set up leftoverSlot with the root tuple
1533 : * descriptor.
1534 : */
1535 815 : if (resultRelInfo->ri_RootResultRelInfo)
1536 64 : resultRelInfo = resultRelInfo->ri_RootResultRelInfo;
1537 :
1538 : /*
1539 : * Insert a leftover for each value returned by the without_portion helper
1540 : * function
1541 : */
1542 : while (true)
1543 1048 : {
1544 : Datum leftover;
1545 :
1546 : /* Call the function one time */
1547 1863 : pgstat_init_function_usage(fcinfo, &fcusage);
1548 :
1549 1863 : fcinfo->isnull = false;
1550 1863 : rsi.isDone = ExprSingleResult;
1551 1863 : leftover = FunctionCallInvoke(fcinfo);
1552 :
1553 1863 : pgstat_end_function_usage(&fcusage,
1554 1863 : rsi.isDone != ExprMultipleResult);
1555 :
1556 1863 : if (rsi.returnMode != SFRM_ValuePerCall)
1557 0 : elog(ERROR, "without_portion function violated function call protocol");
1558 :
1559 : /* Are we done? */
1560 1863 : if (rsi.isDone == ExprEndResult)
1561 783 : break;
1562 :
1563 1080 : if (fcinfo->isnull)
1564 0 : elog(ERROR, "got a null from without_portion function");
1565 :
1566 : /*
1567 : * Does the new Datum violate domain checks? Row-level CHECK
1568 : * constraints are validated by ExecInsert, so we don't need to do
1569 : * anything here for those.
1570 : */
1571 1080 : if (forPortionOf->isDomain)
1572 80 : domain_check(leftover, false, forPortionOf->rangeVar->vartype, NULL, NULL);
1573 :
1574 1064 : if (!didInit)
1575 : {
1576 : /*
1577 : * Make a copy of the pre-UPDATE row. Then we'll overwrite the
1578 : * range column below. Convert oldtuple to the base table's format
1579 : * if necessary. We need to insert temporal leftovers through the
1580 : * root partition so they get routed correctly.
1581 : */
1582 699 : if (map != NULL)
1583 : {
1584 16 : leftoverSlot = execute_attr_map_slot(map->attrMap,
1585 : oldtupleSlot,
1586 : leftoverSlot);
1587 : }
1588 : else
1589 : {
1590 683 : oldtuple = ExecFetchSlotHeapTuple(oldtupleSlot, false, &shouldFree);
1591 683 : ExecForceStoreHeapTuple(oldtuple, leftoverSlot, false);
1592 : }
1593 :
1594 : /*
1595 : * Save some mtstate things so we can restore them below. XXX:
1596 : * Should we create our own ModifyTableState instead?
1597 : */
1598 699 : oldOperation = mtstate->operation;
1599 699 : mtstate->operation = CMD_INSERT;
1600 699 : oldTcs = mtstate->mt_transition_capture;
1601 :
1602 699 : didInit = true;
1603 : }
1604 :
1605 1064 : leftoverSlot->tts_values[forPortionOf->rangeVar->varattno - 1] = leftover;
1606 1064 : leftoverSlot->tts_isnull[forPortionOf->rangeVar->varattno - 1] = false;
1607 1064 : ExecMaterializeSlot(leftoverSlot);
1608 :
1609 : /*
1610 : * The standard says that each temporal leftover should execute its
1611 : * own INSERT statement, firing all statement and row triggers, but
1612 : * skipping insert permission checks. Therefore we give each insert
1613 : * its own transition table. If we just push & pop a new trigger level
1614 : * for each insert, we get exactly what we need.
1615 : *
1616 : * We have to make sure that the inserts don't add to the ROW_COUNT
1617 : * diagnostic or the command tag, so we pass false for canSetTag.
1618 : */
1619 1064 : AfterTriggerBeginQuery();
1620 1064 : ExecSetupTransitionCaptureState(mtstate, estate);
1621 1064 : fireBSTriggers(mtstate);
1622 1064 : ExecInsert(context, resultRelInfo, leftoverSlot, false, NULL, NULL);
1623 1048 : fireASTriggers(mtstate);
1624 1048 : AfterTriggerEndQuery(estate);
1625 : }
1626 :
1627 783 : if (didInit)
1628 : {
1629 683 : mtstate->operation = oldOperation;
1630 683 : mtstate->mt_transition_capture = oldTcs;
1631 :
1632 683 : if (shouldFree)
1633 0 : heap_freetuple(oldtuple);
1634 : }
1635 783 : }
1636 :
1637 : /* ----------------------------------------------------------------
1638 : * ExecBatchInsert
1639 : *
1640 : * Insert multiple tuples in an efficient way.
1641 : * Currently, this handles inserting into a foreign table without
1642 : * RETURNING clause.
1643 : * ----------------------------------------------------------------
1644 : */
1645 : static void
1646 29 : ExecBatchInsert(ModifyTableState *mtstate,
1647 : ResultRelInfo *resultRelInfo,
1648 : TupleTableSlot **slots,
1649 : TupleTableSlot **planSlots,
1650 : int numSlots,
1651 : EState *estate,
1652 : bool canSetTag)
1653 : {
1654 : int i;
1655 29 : int numInserted = numSlots;
1656 29 : TupleTableSlot *slot = NULL;
1657 : TupleTableSlot **rslots;
1658 :
1659 : /*
1660 : * insert into foreign table: let the FDW do it
1661 : */
1662 29 : rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1663 : resultRelInfo,
1664 : slots,
1665 : planSlots,
1666 : &numInserted);
1667 :
1668 173 : for (i = 0; i < numInserted; i++)
1669 : {
1670 145 : slot = rslots[i];
1671 :
1672 : /*
1673 : * AFTER ROW Triggers might reference the tableoid column, so
1674 : * (re-)initialize tts_tableOid before evaluating them.
1675 : */
1676 145 : slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1677 :
1678 : /* AFTER ROW INSERT Triggers */
1679 145 : ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1680 145 : mtstate->mt_transition_capture);
1681 :
1682 : /*
1683 : * Check any WITH CHECK OPTION constraints from parent views. See the
1684 : * comment in ExecInsert.
1685 : */
1686 144 : if (resultRelInfo->ri_WithCheckOptions != NIL)
1687 0 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1688 : }
1689 :
1690 28 : if (canSetTag && numInserted > 0)
1691 28 : estate->es_processed += numInserted;
1692 :
1693 : /* Clean up all the slots, ready for the next batch */
1694 172 : for (i = 0; i < numSlots; i++)
1695 : {
1696 144 : ExecClearTuple(slots[i]);
1697 144 : ExecClearTuple(planSlots[i]);
1698 : }
1699 28 : resultRelInfo->ri_NumSlots = 0;
1700 28 : }
1701 :
1702 : /*
1703 : * ExecPendingInserts -- flushes all pending inserts to the foreign tables
1704 : */
1705 : static void
1706 18 : ExecPendingInserts(EState *estate)
1707 : {
1708 : ListCell *l1,
1709 : *l2;
1710 :
1711 36 : forboth(l1, estate->es_insert_pending_result_relations,
1712 : l2, estate->es_insert_pending_modifytables)
1713 : {
1714 19 : ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l1);
1715 19 : ModifyTableState *mtstate = (ModifyTableState *) lfirst(l2);
1716 :
1717 : Assert(mtstate);
1718 19 : ExecBatchInsert(mtstate, resultRelInfo,
1719 : resultRelInfo->ri_Slots,
1720 : resultRelInfo->ri_PlanSlots,
1721 : resultRelInfo->ri_NumSlots,
1722 19 : estate, mtstate->canSetTag);
1723 : }
1724 :
1725 17 : list_free(estate->es_insert_pending_result_relations);
1726 17 : list_free(estate->es_insert_pending_modifytables);
1727 17 : estate->es_insert_pending_result_relations = NIL;
1728 17 : estate->es_insert_pending_modifytables = NIL;
1729 17 : }
1730 :
1731 : /*
1732 : * ExecDeletePrologue -- subroutine for ExecDelete
1733 : *
1734 : * Prepare executor state for DELETE. Actually, the only thing we have to do
1735 : * here is execute BEFORE ROW triggers. We return false if one of them makes
1736 : * the delete a no-op; otherwise, return true.
1737 : */
1738 : static bool
1739 1031661 : ExecDeletePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1740 : ItemPointer tupleid, HeapTuple oldtuple,
1741 : TupleTableSlot **epqreturnslot, TM_Result *result)
1742 : {
1743 1031661 : if (result)
1744 1066 : *result = TM_Ok;
1745 :
1746 : /* BEFORE ROW DELETE triggers */
1747 1031661 : if (resultRelInfo->ri_TrigDesc &&
1748 4712 : resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1749 : {
1750 : /* Flush any pending inserts, so rows are visible to the triggers */
1751 217 : if (context->estate->es_insert_pending_result_relations != NIL)
1752 1 : ExecPendingInserts(context->estate);
1753 :
1754 207 : return ExecBRDeleteTriggers(context->estate, context->epqstate,
1755 : resultRelInfo, tupleid, oldtuple,
1756 : epqreturnslot, result, &context->tmfd,
1757 217 : context->mtstate->operation == CMD_MERGE);
1758 : }
1759 :
1760 1031444 : return true;
1761 : }
1762 :
1763 : /*
1764 : * ExecDeleteAct -- subroutine for ExecDelete
1765 : *
1766 : * Actually delete the tuple from a plain table.
1767 : *
1768 : * Caller is in charge of doing EvalPlanQual as necessary
1769 : */
1770 : static TM_Result
1771 1031555 : ExecDeleteAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1772 : ItemPointer tupleid, bool changingPart)
1773 : {
1774 1031555 : EState *estate = context->estate;
1775 1031555 : uint32 options = 0;
1776 :
1777 1031555 : if (changingPart)
1778 696 : options |= TABLE_DELETE_CHANGING_PARTITION;
1779 :
1780 1031555 : return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1781 : estate->es_output_cid,
1782 : options,
1783 : estate->es_snapshot,
1784 : estate->es_crosscheck_snapshot,
1785 : true /* wait for commit */ ,
1786 : &context->tmfd);
1787 : }
1788 :
1789 : /*
1790 : * ExecDeleteEpilogue -- subroutine for ExecDelete
1791 : *
1792 : * Closing steps of tuple deletion; this invokes AFTER FOR EACH ROW triggers,
1793 : * including the UPDATE triggers if the deletion is being done as part of a
1794 : * cross-partition tuple move. It also inserts temporal leftovers from a
1795 : * DELETE FOR PORTION OF.
1796 : */
1797 : static void
1798 1031494 : ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1799 : ItemPointer tupleid, HeapTuple oldtuple, bool changingPart)
1800 : {
1801 1031494 : ModifyTableState *mtstate = context->mtstate;
1802 1031494 : EState *estate = context->estate;
1803 : TransitionCaptureState *ar_delete_trig_tcs;
1804 :
1805 : /*
1806 : * If this delete is the result of a partition key update that moved the
1807 : * tuple to a new partition, put this row into the transition OLD TABLE,
1808 : * if there is one. We need to do this separately for DELETE and INSERT
1809 : * because they happen on different tables.
1810 : */
1811 1031494 : ar_delete_trig_tcs = mtstate->mt_transition_capture;
1812 1031494 : if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1813 36 : mtstate->mt_transition_capture->tcs_update_old_table)
1814 : {
1815 32 : ExecARUpdateTriggers(estate, resultRelInfo,
1816 : NULL, NULL,
1817 : tupleid, oldtuple,
1818 32 : NULL, NULL, mtstate->mt_transition_capture,
1819 : false);
1820 :
1821 : /*
1822 : * We've already captured the OLD TABLE row, so make sure any AR
1823 : * DELETE trigger fired below doesn't capture it again.
1824 : */
1825 32 : ar_delete_trig_tcs = NULL;
1826 : }
1827 :
1828 : /* Compute temporal leftovers in FOR PORTION OF */
1829 1031494 : if (((ModifyTable *) context->mtstate->ps.plan)->forPortionOf)
1830 369 : ExecForPortionOfLeftovers(context, estate, resultRelInfo, tupleid);
1831 :
1832 : /* AFTER ROW DELETE Triggers */
1833 1031478 : ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1834 : ar_delete_trig_tcs, changingPart);
1835 1031476 : }
1836 :
1837 : /* ----------------------------------------------------------------
1838 : * ExecDelete
1839 : *
1840 : * DELETE is like UPDATE, except that we delete the tuple and no
1841 : * index modifications are needed.
1842 : *
1843 : * When deleting from a table, tupleid identifies the tuple to delete and
1844 : * oldtuple is NULL. When deleting through a view INSTEAD OF trigger,
1845 : * oldtuple is passed to the triggers and identifies what to delete, and
1846 : * tupleid is invalid. When deleting from a foreign table, tupleid is
1847 : * invalid; the FDW has to figure out which row to delete using data from
1848 : * the planSlot. oldtuple is passed to foreign table triggers; it is
1849 : * NULL when the foreign table has no relevant triggers. We use
1850 : * tupleDeleted to indicate whether the tuple is actually deleted,
1851 : * callers can use it to decide whether to continue the operation. When
1852 : * this DELETE is a part of an UPDATE of partition-key, then the slot
1853 : * returned by EvalPlanQual() is passed back using output parameter
1854 : * epqreturnslot.
1855 : *
1856 : * Returns RETURNING result if any, otherwise NULL.
1857 : * ----------------------------------------------------------------
1858 : */
1859 : static TupleTableSlot *
1860 1031313 : ExecDelete(ModifyTableContext *context,
1861 : ResultRelInfo *resultRelInfo,
1862 : ItemPointer tupleid,
1863 : HeapTuple oldtuple,
1864 : bool processReturning,
1865 : bool changingPart,
1866 : bool canSetTag,
1867 : TM_Result *tmresult,
1868 : bool *tupleDeleted,
1869 : TupleTableSlot **epqreturnslot)
1870 : {
1871 1031313 : EState *estate = context->estate;
1872 1031313 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1873 1031313 : TupleTableSlot *slot = NULL;
1874 : TM_Result result;
1875 : bool saveOld;
1876 :
1877 1031313 : if (tupleDeleted)
1878 718 : *tupleDeleted = false;
1879 :
1880 : /*
1881 : * Prepare for the delete. This includes BEFORE ROW triggers, so we're
1882 : * done if it says we are.
1883 : */
1884 1031313 : if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple,
1885 : epqreturnslot, tmresult))
1886 33 : return NULL;
1887 :
1888 : /* INSTEAD OF ROW DELETE Triggers */
1889 1031270 : if (resultRelInfo->ri_TrigDesc &&
1890 4625 : resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
1891 31 : {
1892 : bool dodelete;
1893 :
1894 : Assert(oldtuple != NULL);
1895 35 : dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
1896 :
1897 35 : if (!dodelete) /* "do nothing" */
1898 4 : return NULL;
1899 : }
1900 1031235 : else if (resultRelInfo->ri_FdwRoutine)
1901 : {
1902 : /*
1903 : * delete from foreign table: let the FDW do it
1904 : *
1905 : * We offer the returning slot as a place to store RETURNING data,
1906 : * although the FDW can return some other slot if it wants.
1907 : */
1908 23 : slot = ExecGetReturningSlot(estate, resultRelInfo);
1909 23 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
1910 : resultRelInfo,
1911 : slot,
1912 : context->planSlot);
1913 :
1914 23 : if (slot == NULL) /* "do nothing" */
1915 0 : return NULL;
1916 :
1917 : /*
1918 : * RETURNING expressions might reference the tableoid column, so
1919 : * (re)initialize tts_tableOid before evaluating them.
1920 : */
1921 23 : if (TTS_EMPTY(slot))
1922 5 : ExecStoreAllNullTuple(slot);
1923 :
1924 23 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1925 : }
1926 : else
1927 : {
1928 : /*
1929 : * delete the tuple
1930 : *
1931 : * Note: if context->estate->es_crosscheck_snapshot isn't
1932 : * InvalidSnapshot, we check that the row to be deleted is visible to
1933 : * that snapshot, and throw a can't-serialize error if not. This is a
1934 : * special-case behavior needed for referential integrity updates in
1935 : * transaction-snapshot mode transactions.
1936 : */
1937 1031212 : ldelete:
1938 1031218 : result = ExecDeleteAct(context, resultRelInfo, tupleid, changingPart);
1939 :
1940 1031200 : if (tmresult)
1941 696 : *tmresult = result;
1942 :
1943 1031200 : switch (result)
1944 : {
1945 28 : case TM_SelfModified:
1946 :
1947 : /*
1948 : * The target tuple was already updated or deleted by the
1949 : * current command, or by a later command in the current
1950 : * transaction. The former case is possible in a join DELETE
1951 : * where multiple tuples join to the same target tuple. This
1952 : * is somewhat questionable, but Postgres has always allowed
1953 : * it: we just ignore additional deletion attempts.
1954 : *
1955 : * The latter case arises if the tuple is modified by a
1956 : * command in a BEFORE trigger, or perhaps by a command in a
1957 : * volatile function used in the query. In such situations we
1958 : * should not ignore the deletion, but it is equally unsafe to
1959 : * proceed. We don't want to discard the original DELETE
1960 : * while keeping the triggered actions based on its deletion;
1961 : * and it would be no better to allow the original DELETE
1962 : * while discarding updates that it triggered. The row update
1963 : * carries some information that might be important according
1964 : * to business rules; so throwing an error is the only safe
1965 : * course.
1966 : *
1967 : * If a trigger actually intends this type of interaction, it
1968 : * can re-execute the DELETE and then return NULL to cancel
1969 : * the outer delete.
1970 : */
1971 28 : if (context->tmfd.cmax != estate->es_output_cid)
1972 4 : ereport(ERROR,
1973 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1974 : errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1975 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1976 :
1977 : /* Else, already deleted by self; nothing to do */
1978 24 : return NULL;
1979 :
1980 1031110 : case TM_Ok:
1981 1031110 : break;
1982 :
1983 47 : case TM_Updated:
1984 : {
1985 : TupleTableSlot *inputslot;
1986 : TupleTableSlot *epqslot;
1987 :
1988 47 : if (IsolationUsesXactSnapshot())
1989 10 : ereport(ERROR,
1990 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1991 : errmsg("could not serialize access due to concurrent update")));
1992 :
1993 : /*
1994 : * Already know that we're going to need to do EPQ, so
1995 : * fetch tuple directly into the right slot.
1996 : */
1997 37 : EvalPlanQualBegin(context->epqstate);
1998 37 : inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
1999 : resultRelInfo->ri_RangeTableIndex);
2000 :
2001 37 : result = table_tuple_lock(resultRelationDesc, tupleid,
2002 : estate->es_snapshot,
2003 : inputslot, estate->es_output_cid,
2004 : LockTupleExclusive, LockWaitBlock,
2005 : TUPLE_LOCK_FLAG_FIND_LAST_VERSION,
2006 : &context->tmfd);
2007 :
2008 33 : switch (result)
2009 : {
2010 30 : case TM_Ok:
2011 : Assert(context->tmfd.traversed);
2012 30 : epqslot = EvalPlanQual(context->epqstate,
2013 : resultRelationDesc,
2014 : resultRelInfo->ri_RangeTableIndex,
2015 : inputslot);
2016 30 : if (TupIsNull(epqslot))
2017 : /* Tuple not passing quals anymore, exiting... */
2018 16 : return NULL;
2019 :
2020 : /*
2021 : * If requested, skip delete and pass back the
2022 : * updated row.
2023 : */
2024 14 : if (epqreturnslot)
2025 : {
2026 8 : *epqreturnslot = epqslot;
2027 8 : return NULL;
2028 : }
2029 : else
2030 6 : goto ldelete;
2031 :
2032 2 : case TM_SelfModified:
2033 :
2034 : /*
2035 : * This can be reached when following an update
2036 : * chain from a tuple updated by another session,
2037 : * reaching a tuple that was already updated in
2038 : * this transaction. If previously updated by this
2039 : * command, ignore the delete, otherwise error
2040 : * out.
2041 : *
2042 : * See also TM_SelfModified response to
2043 : * table_tuple_delete() above.
2044 : */
2045 2 : if (context->tmfd.cmax != estate->es_output_cid)
2046 1 : ereport(ERROR,
2047 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2048 : errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
2049 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2050 1 : return NULL;
2051 :
2052 1 : case TM_Deleted:
2053 : /* tuple already deleted; nothing to do */
2054 1 : return NULL;
2055 :
2056 0 : default:
2057 :
2058 : /*
2059 : * TM_Invisible should be impossible because we're
2060 : * waiting for updated row versions, and would
2061 : * already have errored out if the first version
2062 : * is invisible.
2063 : *
2064 : * TM_Updated should be impossible, because we're
2065 : * locking the latest version via
2066 : * TUPLE_LOCK_FLAG_FIND_LAST_VERSION.
2067 : */
2068 0 : elog(ERROR, "unexpected table_tuple_lock status: %u",
2069 : result);
2070 : return NULL;
2071 : }
2072 :
2073 : Assert(false);
2074 : break;
2075 : }
2076 :
2077 15 : case TM_Deleted:
2078 15 : if (IsolationUsesXactSnapshot())
2079 9 : ereport(ERROR,
2080 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2081 : errmsg("could not serialize access due to concurrent delete")));
2082 : /* tuple already deleted; nothing to do */
2083 6 : return NULL;
2084 :
2085 0 : default:
2086 0 : elog(ERROR, "unrecognized table_tuple_delete status: %u",
2087 : result);
2088 : return NULL;
2089 : }
2090 :
2091 : /*
2092 : * Note: Normally one would think that we have to delete index tuples
2093 : * associated with the heap tuple now...
2094 : *
2095 : * ... but in POSTGRES, we have no need to do this because VACUUM will
2096 : * take care of it later. We can't delete index tuples immediately
2097 : * anyway, since the tuple is still visible to other transactions.
2098 : */
2099 : }
2100 :
2101 1031164 : if (canSetTag)
2102 1030331 : (estate->es_processed)++;
2103 :
2104 : /* Tell caller that the delete actually happened. */
2105 1031164 : if (tupleDeleted)
2106 666 : *tupleDeleted = true;
2107 :
2108 1031164 : ExecDeleteEpilogue(context, resultRelInfo, tupleid, oldtuple, changingPart);
2109 :
2110 : /*
2111 : * Process RETURNING if present and if requested.
2112 : *
2113 : * If this is part of a cross-partition UPDATE, and the RETURNING list
2114 : * refers to any OLD column values, save the old tuple here for later
2115 : * processing of the RETURNING list by ExecInsert().
2116 : */
2117 1031241 : saveOld = changingPart && resultRelInfo->ri_projectReturning &&
2118 95 : resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD;
2119 :
2120 1031146 : if (resultRelInfo->ri_projectReturning && (processReturning || saveOld))
2121 : {
2122 : /*
2123 : * We have to put the target tuple into a slot, which means first we
2124 : * gotta fetch it. We can use the trigger tuple slot.
2125 : */
2126 : TupleTableSlot *rslot;
2127 :
2128 624 : if (resultRelInfo->ri_FdwRoutine)
2129 : {
2130 : /* FDW must have provided a slot containing the deleted row */
2131 : Assert(!TupIsNull(slot));
2132 : }
2133 : else
2134 : {
2135 617 : slot = ExecGetReturningSlot(estate, resultRelInfo);
2136 617 : if (oldtuple != NULL)
2137 : {
2138 16 : ExecForceStoreHeapTuple(oldtuple, slot, false);
2139 : }
2140 : else
2141 : {
2142 601 : if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
2143 : SnapshotAny, slot))
2144 0 : elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
2145 : }
2146 : }
2147 :
2148 : /*
2149 : * If required, save the old tuple for later processing of the
2150 : * RETURNING list by ExecInsert().
2151 : */
2152 624 : if (saveOld)
2153 : {
2154 : TupleConversionMap *tupconv_map;
2155 :
2156 : /*
2157 : * Convert the tuple into the root partition's format/slot, if
2158 : * needed. ExecInsert() will then convert it to the new
2159 : * partition's format/slot, if necessary.
2160 : */
2161 30 : tupconv_map = ExecGetChildToRootMap(resultRelInfo);
2162 30 : if (tupconv_map != NULL)
2163 : {
2164 12 : ResultRelInfo *rootRelInfo = context->mtstate->rootResultRelInfo;
2165 12 : TupleTableSlot *oldSlot = slot;
2166 :
2167 12 : slot = execute_attr_map_slot(tupconv_map->attrMap,
2168 : slot,
2169 : ExecGetReturningSlot(estate,
2170 : rootRelInfo));
2171 :
2172 12 : slot->tts_tableOid = oldSlot->tts_tableOid;
2173 12 : ItemPointerCopy(&oldSlot->tts_tid, &slot->tts_tid);
2174 : }
2175 :
2176 30 : context->cpDeletedSlot = slot;
2177 :
2178 30 : return NULL;
2179 : }
2180 :
2181 594 : rslot = ExecProcessReturning(context, resultRelInfo, true,
2182 : slot, NULL, context->planSlot);
2183 :
2184 : /*
2185 : * Before releasing the target tuple again, make sure rslot has a
2186 : * local copy of any pass-by-reference values.
2187 : */
2188 594 : ExecMaterializeSlot(rslot);
2189 :
2190 594 : ExecClearTuple(slot);
2191 :
2192 594 : return rslot;
2193 : }
2194 :
2195 1030522 : return NULL;
2196 : }
2197 :
2198 : /*
2199 : * ExecCrossPartitionUpdate --- Move an updated tuple to another partition.
2200 : *
2201 : * This works by first deleting the old tuple from the current partition,
2202 : * followed by inserting the new tuple into the root parent table, that is,
2203 : * mtstate->rootResultRelInfo. It will be re-routed from there to the
2204 : * correct partition.
2205 : *
2206 : * Returns true if the tuple has been successfully moved, or if it's found
2207 : * that the tuple was concurrently deleted so there's nothing more to do
2208 : * for the caller.
2209 : *
2210 : * False is returned if the tuple we're trying to move is found to have been
2211 : * concurrently updated. In that case, the caller must check if the updated
2212 : * tuple that's returned in *retry_slot still needs to be re-routed, and call
2213 : * this function again or perform a regular update accordingly. For MERGE,
2214 : * the updated tuple is not returned in *retry_slot; it has its own retry
2215 : * logic.
2216 : */
2217 : static bool
2218 750 : ExecCrossPartitionUpdate(ModifyTableContext *context,
2219 : ResultRelInfo *resultRelInfo,
2220 : ItemPointer tupleid, HeapTuple oldtuple,
2221 : TupleTableSlot *slot,
2222 : bool canSetTag,
2223 : UpdateContext *updateCxt,
2224 : TM_Result *tmresult,
2225 : TupleTableSlot **retry_slot,
2226 : TupleTableSlot **inserted_tuple,
2227 : ResultRelInfo **insert_destrel)
2228 : {
2229 750 : ModifyTableState *mtstate = context->mtstate;
2230 750 : EState *estate = mtstate->ps.state;
2231 : TupleConversionMap *tupconv_map;
2232 : bool tuple_deleted;
2233 750 : TupleTableSlot *epqslot = NULL;
2234 :
2235 750 : context->cpDeletedSlot = NULL;
2236 750 : context->cpUpdateReturningSlot = NULL;
2237 750 : *retry_slot = NULL;
2238 :
2239 : /*
2240 : * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
2241 : * to migrate to a different partition. Maybe this can be implemented
2242 : * some day, but it seems a fringe feature with little redeeming value.
2243 : */
2244 750 : if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
2245 0 : ereport(ERROR,
2246 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2247 : errmsg("invalid ON UPDATE specification"),
2248 : errdetail("The result tuple would appear in a different partition than the original tuple.")));
2249 :
2250 : /*
2251 : * When an UPDATE is run directly on a leaf partition, simply fail with a
2252 : * partition constraint violation error.
2253 : */
2254 750 : if (resultRelInfo == mtstate->rootResultRelInfo)
2255 32 : ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
2256 :
2257 : /*
2258 : * Initialize tuple routing info if not already done. Note whatever we do
2259 : * here must be done in ExecInitModifyTable for FOR PORTION OF as well.
2260 : */
2261 718 : if (mtstate->mt_partition_tuple_routing == NULL)
2262 : {
2263 439 : Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
2264 : MemoryContext oldcxt;
2265 :
2266 : /* Things built here have to last for the query duration. */
2267 439 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
2268 :
2269 439 : mtstate->mt_partition_tuple_routing =
2270 439 : ExecSetupPartitionTupleRouting(estate, rootRel);
2271 :
2272 : /*
2273 : * Before a partition's tuple can be re-routed, it must first be
2274 : * converted to the root's format, so we'll need a slot for storing
2275 : * such tuples.
2276 : */
2277 : Assert(mtstate->mt_root_tuple_slot == NULL);
2278 439 : mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
2279 :
2280 439 : MemoryContextSwitchTo(oldcxt);
2281 : }
2282 :
2283 : /*
2284 : * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
2285 : * We want to return rows from INSERT.
2286 : */
2287 718 : ExecDelete(context, resultRelInfo,
2288 : tupleid, oldtuple,
2289 : false, /* processReturning */
2290 : true, /* changingPart */
2291 : false, /* canSetTag */
2292 : tmresult, &tuple_deleted, &epqslot);
2293 :
2294 : /*
2295 : * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
2296 : * it was already deleted by self, or it was concurrently deleted by
2297 : * another transaction), then we should skip the insert as well;
2298 : * otherwise, an UPDATE could cause an increase in the total number of
2299 : * rows across all partitions, which is clearly wrong.
2300 : *
2301 : * For a normal UPDATE, the case where the tuple has been the subject of a
2302 : * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
2303 : * machinery, but for an UPDATE that we've translated into a DELETE from
2304 : * this partition and an INSERT into some other partition, that's not
2305 : * available, because CTID chains can't span relation boundaries. We
2306 : * mimic the semantics to a limited extent by skipping the INSERT if the
2307 : * DELETE fails to find a tuple. This ensures that two concurrent
2308 : * attempts to UPDATE the same tuple at the same time can't turn one tuple
2309 : * into two, and that an UPDATE of a just-deleted tuple can't resurrect
2310 : * it.
2311 : */
2312 715 : if (!tuple_deleted)
2313 : {
2314 : /*
2315 : * epqslot will be typically NULL. But when ExecDelete() finds that
2316 : * another transaction has concurrently updated the same row, it
2317 : * re-fetches the row, skips the delete, and epqslot is set to the
2318 : * re-fetched tuple slot. In that case, we need to do all the checks
2319 : * again. For MERGE, we leave everything to the caller (it must do
2320 : * additional rechecking, and might end up executing a different
2321 : * action entirely).
2322 : */
2323 49 : if (mtstate->operation == CMD_MERGE)
2324 23 : return *tmresult == TM_Ok;
2325 26 : else if (TupIsNull(epqslot))
2326 23 : return true;
2327 : else
2328 : {
2329 : /* Fetch the most recent version of old tuple. */
2330 : TupleTableSlot *oldSlot;
2331 :
2332 : /* ... but first, make sure ri_oldTupleSlot is initialized. */
2333 3 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2334 0 : ExecInitUpdateProjection(mtstate, resultRelInfo);
2335 3 : oldSlot = resultRelInfo->ri_oldTupleSlot;
2336 3 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
2337 : tupleid,
2338 : SnapshotAny,
2339 : oldSlot))
2340 0 : elog(ERROR, "failed to fetch tuple being updated");
2341 : /* and project the new tuple to retry the UPDATE with */
2342 3 : *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot,
2343 : oldSlot);
2344 3 : return false;
2345 : }
2346 : }
2347 :
2348 : /*
2349 : * resultRelInfo is one of the per-relation resultRelInfos. So we should
2350 : * convert the tuple into root's tuple descriptor if needed, since
2351 : * ExecInsert() starts the search from root.
2352 : */
2353 666 : tupconv_map = ExecGetChildToRootMap(resultRelInfo);
2354 666 : if (tupconv_map != NULL)
2355 217 : slot = execute_attr_map_slot(tupconv_map->attrMap,
2356 : slot,
2357 : mtstate->mt_root_tuple_slot);
2358 :
2359 : /* Tuple routing starts from the root table. */
2360 583 : context->cpUpdateReturningSlot =
2361 666 : ExecInsert(context, mtstate->rootResultRelInfo, slot, canSetTag,
2362 : inserted_tuple, insert_destrel);
2363 :
2364 : /*
2365 : * Reset the transition state that may possibly have been written by
2366 : * INSERT.
2367 : */
2368 583 : if (mtstate->mt_transition_capture)
2369 36 : mtstate->mt_transition_capture->tcs_original_insert_tuple = NULL;
2370 :
2371 : /* We're done moving. */
2372 583 : return true;
2373 : }
2374 :
2375 : /*
2376 : * ExecUpdatePrologue -- subroutine for ExecUpdate
2377 : *
2378 : * Prepare executor state for UPDATE. This includes running BEFORE ROW
2379 : * triggers. We return false if one of them makes the update a no-op;
2380 : * otherwise, return true.
2381 : */
2382 : static bool
2383 2224583 : ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2384 : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
2385 : TM_Result *result)
2386 : {
2387 2224583 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2388 :
2389 2224583 : if (result)
2390 1413 : *result = TM_Ok;
2391 :
2392 2224583 : ExecMaterializeSlot(slot);
2393 :
2394 : /*
2395 : * Open the table's indexes, if we have not done so already, so that we
2396 : * can add new index entries for the updated tuple.
2397 : */
2398 2224583 : if (resultRelationDesc->rd_rel->relhasindex &&
2399 156492 : resultRelInfo->ri_IndexRelationDescs == NULL)
2400 5776 : ExecOpenIndices(resultRelInfo, false);
2401 :
2402 : /* BEFORE ROW UPDATE triggers */
2403 2224583 : if (resultRelInfo->ri_TrigDesc &&
2404 3995 : resultRelInfo->ri_TrigDesc->trig_update_before_row)
2405 : {
2406 : /* Flush any pending inserts, so rows are visible to the triggers */
2407 1569 : if (context->estate->es_insert_pending_result_relations != NIL)
2408 1 : ExecPendingInserts(context->estate);
2409 :
2410 1557 : return ExecBRUpdateTriggers(context->estate, context->epqstate,
2411 : resultRelInfo, tupleid, oldtuple, slot,
2412 : result, &context->tmfd,
2413 1569 : context->mtstate->operation == CMD_MERGE);
2414 : }
2415 :
2416 2223014 : return true;
2417 : }
2418 :
2419 : /*
2420 : * ExecUpdatePrepareSlot -- subroutine for ExecUpdateAct
2421 : *
2422 : * Apply the final modifications to the tuple slot before the update.
2423 : * (This is split out because we also need it in the foreign-table code path.)
2424 : */
2425 : static void
2426 2224398 : ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo,
2427 : TupleTableSlot *slot,
2428 : EState *estate)
2429 : {
2430 2224398 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2431 :
2432 : /*
2433 : * Constraints and GENERATED expressions might reference the tableoid
2434 : * column, so (re-)initialize tts_tableOid before evaluating them.
2435 : */
2436 2224398 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2437 :
2438 : /*
2439 : * Compute stored generated columns
2440 : */
2441 2224398 : if (resultRelationDesc->rd_att->constr &&
2442 133524 : resultRelationDesc->rd_att->constr->has_generated_stored)
2443 166 : ExecComputeStoredGenerated(resultRelInfo, estate, slot,
2444 : CMD_UPDATE);
2445 2224398 : }
2446 :
2447 : /*
2448 : * ExecUpdateAct -- subroutine for ExecUpdate
2449 : *
2450 : * Actually update the tuple, when operating on a plain table. If the
2451 : * table is a partition, and the command was called referencing an ancestor
2452 : * partitioned table, this routine migrates the resulting tuple to another
2453 : * partition.
2454 : *
2455 : * The caller is in charge of keeping indexes current as necessary. The
2456 : * caller is also in charge of doing EvalPlanQual if the tuple is found to
2457 : * be concurrently updated. However, in case of a cross-partition update,
2458 : * this routine does it.
2459 : */
2460 : static TM_Result
2461 2224300 : ExecUpdateAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2462 : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
2463 : bool canSetTag, UpdateContext *updateCxt)
2464 : {
2465 2224300 : EState *estate = context->estate;
2466 2224300 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2467 : bool partition_constraint_failed;
2468 : TM_Result result;
2469 :
2470 2224300 : updateCxt->crossPartUpdate = false;
2471 :
2472 : /*
2473 : * If we move the tuple to a new partition, we loop back here to recompute
2474 : * GENERATED values (which are allowed to be different across partitions)
2475 : * and recheck any RLS policies and constraints. We do not fire any
2476 : * BEFORE triggers of the new partition, however.
2477 : */
2478 2224303 : lreplace:
2479 : /* Fill in GENERATEd columns */
2480 2224303 : ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2481 :
2482 : /* ensure slot is independent, consider e.g. EPQ */
2483 2224303 : ExecMaterializeSlot(slot);
2484 :
2485 : /*
2486 : * If partition constraint fails, this row might get moved to another
2487 : * partition, in which case we should check the RLS CHECK policy just
2488 : * before inserting into the new partition, rather than doing it here.
2489 : * This is because a trigger on that partition might again change the row.
2490 : * So skip the WCO checks if the partition constraint fails.
2491 : */
2492 2224303 : partition_constraint_failed =
2493 2226140 : resultRelationDesc->rd_rel->relispartition &&
2494 1837 : !ExecPartitionCheck(resultRelInfo, slot, estate, false);
2495 :
2496 : /* Check any RLS UPDATE WITH CHECK policies */
2497 2224303 : if (!partition_constraint_failed &&
2498 2223553 : resultRelInfo->ri_WithCheckOptions != NIL)
2499 : {
2500 : /*
2501 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind
2502 : * we are looking for at this point.
2503 : */
2504 356 : ExecWithCheckOptions(WCO_RLS_UPDATE_CHECK,
2505 : resultRelInfo, slot, estate);
2506 : }
2507 :
2508 : /*
2509 : * If a partition check failed, try to move the row into the right
2510 : * partition.
2511 : */
2512 2224267 : if (partition_constraint_failed)
2513 : {
2514 : TupleTableSlot *inserted_tuple,
2515 : *retry_slot;
2516 750 : ResultRelInfo *insert_destrel = NULL;
2517 :
2518 : /*
2519 : * ExecCrossPartitionUpdate will first DELETE the row from the
2520 : * partition it's currently in and then insert it back into the root
2521 : * table, which will re-route it to the correct partition. However,
2522 : * if the tuple has been concurrently updated, a retry is needed.
2523 : */
2524 750 : if (ExecCrossPartitionUpdate(context, resultRelInfo,
2525 : tupleid, oldtuple, slot,
2526 : canSetTag, updateCxt,
2527 : &result,
2528 : &retry_slot,
2529 : &inserted_tuple,
2530 : &insert_destrel))
2531 : {
2532 : /* success! */
2533 622 : updateCxt->crossPartUpdate = true;
2534 :
2535 : /*
2536 : * If the partitioned table being updated is referenced in foreign
2537 : * keys, queue up trigger events to check that none of them were
2538 : * violated. No special treatment is needed in
2539 : * non-cross-partition update situations, because the leaf
2540 : * partition's AR update triggers will take care of that. During
2541 : * cross-partition updates implemented as delete on the source
2542 : * partition followed by insert on the destination partition,
2543 : * AR-UPDATE triggers of the root table (that is, the table
2544 : * mentioned in the query) must be fired.
2545 : *
2546 : * NULL insert_destrel means that the move failed to occur, that
2547 : * is, the update failed, so no need to anything in that case.
2548 : */
2549 622 : if (insert_destrel &&
2550 565 : resultRelInfo->ri_TrigDesc &&
2551 242 : resultRelInfo->ri_TrigDesc->trig_update_after_row)
2552 202 : ExecCrossPartitionUpdateForeignKey(context,
2553 : resultRelInfo,
2554 : insert_destrel,
2555 : tupleid, slot,
2556 : inserted_tuple);
2557 :
2558 625 : return TM_Ok;
2559 : }
2560 :
2561 : /*
2562 : * No luck, a retry is needed. If running MERGE, we do not do so
2563 : * here; instead let it handle that on its own rules.
2564 : */
2565 10 : if (context->mtstate->operation == CMD_MERGE)
2566 7 : return result;
2567 :
2568 : /*
2569 : * ExecCrossPartitionUpdate installed an updated version of the new
2570 : * tuple in the retry slot; start over.
2571 : */
2572 3 : slot = retry_slot;
2573 3 : goto lreplace;
2574 : }
2575 :
2576 : /*
2577 : * Check the constraints of the tuple. We've already checked the
2578 : * partition constraint above; however, we must still ensure the tuple
2579 : * passes all other constraints, so we will call ExecConstraints() and
2580 : * have it validate all remaining checks.
2581 : */
2582 2223517 : if (resultRelationDesc->rd_att->constr)
2583 133123 : ExecConstraints(resultRelInfo, slot, estate);
2584 :
2585 : /*
2586 : * replace the heap tuple
2587 : *
2588 : * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
2589 : * the row to be updated is visible to that snapshot, and throw a
2590 : * can't-serialize error if not. This is a special-case behavior needed
2591 : * for referential integrity updates in transaction-snapshot mode
2592 : * transactions.
2593 : */
2594 2223461 : result = table_tuple_update(resultRelationDesc, tupleid, slot,
2595 : estate->es_output_cid,
2596 : 0,
2597 : estate->es_snapshot,
2598 : estate->es_crosscheck_snapshot,
2599 : true /* wait for commit */ ,
2600 : &context->tmfd, &updateCxt->lockmode,
2601 : &updateCxt->updateIndexes);
2602 :
2603 2223449 : return result;
2604 : }
2605 :
2606 : /*
2607 : * ExecUpdateEpilogue -- subroutine for ExecUpdate
2608 : *
2609 : * Closing steps of updating a tuple. Must be called if ExecUpdateAct
2610 : * returns indicating that the tuple was updated. It also inserts temporal
2611 : * leftovers from an UPDATE FOR PORTION OF.
2612 : */
2613 : static void
2614 2223449 : ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt,
2615 : ResultRelInfo *resultRelInfo, ItemPointer tupleid,
2616 : HeapTuple oldtuple, TupleTableSlot *slot)
2617 : {
2618 2223449 : ModifyTableState *mtstate = context->mtstate;
2619 2223449 : List *recheckIndexes = NIL;
2620 :
2621 : /* insert index entries for tuple if necessary */
2622 2223449 : if (resultRelInfo->ri_NumIndices > 0 && (updateCxt->updateIndexes != TU_None))
2623 : {
2624 125967 : uint32 flags = EIIT_IS_UPDATE;
2625 :
2626 125967 : if (updateCxt->updateIndexes == TU_Summarizing)
2627 2188 : flags |= EIIT_ONLY_SUMMARIZING;
2628 125967 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo, context->estate,
2629 : flags, slot, NIL,
2630 : NULL);
2631 : }
2632 :
2633 : /* Compute temporal leftovers in FOR PORTION OF */
2634 2223388 : if (((ModifyTable *) context->mtstate->ps.plan)->forPortionOf)
2635 446 : ExecForPortionOfLeftovers(context, context->estate, resultRelInfo, tupleid);
2636 :
2637 : /* AFTER ROW UPDATE Triggers */
2638 2223372 : ExecARUpdateTriggers(context->estate, resultRelInfo,
2639 : NULL, NULL,
2640 : tupleid, oldtuple, slot,
2641 : recheckIndexes,
2642 2223372 : mtstate->operation == CMD_INSERT ?
2643 : mtstate->mt_oc_transition_capture :
2644 : mtstate->mt_transition_capture,
2645 : false);
2646 :
2647 2223370 : list_free(recheckIndexes);
2648 :
2649 : /*
2650 : * Check any WITH CHECK OPTION constraints from parent views. We are
2651 : * required to do this after testing all constraints and uniqueness
2652 : * violations per the SQL spec, so we do it after actually updating the
2653 : * record in the heap and all indexes.
2654 : *
2655 : * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
2656 : * are looking for at this point.
2657 : */
2658 2223370 : if (resultRelInfo->ri_WithCheckOptions != NIL)
2659 337 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo,
2660 : slot, context->estate);
2661 2223316 : }
2662 :
2663 : /*
2664 : * Queues up an update event using the target root partitioned table's
2665 : * trigger to check that a cross-partition update hasn't broken any foreign
2666 : * keys pointing into it.
2667 : */
2668 : static void
2669 202 : ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
2670 : ResultRelInfo *sourcePartInfo,
2671 : ResultRelInfo *destPartInfo,
2672 : ItemPointer tupleid,
2673 : TupleTableSlot *oldslot,
2674 : TupleTableSlot *newslot)
2675 : {
2676 : ListCell *lc;
2677 : ResultRelInfo *rootRelInfo;
2678 : List *ancestorRels;
2679 :
2680 202 : rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2681 202 : ancestorRels = ExecGetAncestorResultRels(context->estate, sourcePartInfo);
2682 :
2683 : /*
2684 : * For any foreign keys that point directly into a non-root ancestors of
2685 : * the source partition, we can in theory fire an update event to enforce
2686 : * those constraints using their triggers, if we could tell that both the
2687 : * source and the destination partitions are under the same ancestor. But
2688 : * for now, we simply report an error that those cannot be enforced.
2689 : */
2690 440 : foreach(lc, ancestorRels)
2691 : {
2692 242 : ResultRelInfo *rInfo = lfirst(lc);
2693 242 : TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2694 242 : bool has_noncloned_fkey = false;
2695 :
2696 : /* Root ancestor's triggers will be processed. */
2697 242 : if (rInfo == rootRelInfo)
2698 198 : continue;
2699 :
2700 44 : if (trigdesc && trigdesc->trig_update_after_row)
2701 : {
2702 152 : for (int i = 0; i < trigdesc->numtriggers; i++)
2703 : {
2704 112 : Trigger *trig = &trigdesc->triggers[i];
2705 :
2706 116 : if (!trig->tgisclone &&
2707 4 : RI_FKey_trigger_type(trig->tgfoid) == RI_TRIGGER_PK)
2708 : {
2709 4 : has_noncloned_fkey = true;
2710 4 : break;
2711 : }
2712 : }
2713 : }
2714 :
2715 44 : if (has_noncloned_fkey)
2716 4 : ereport(ERROR,
2717 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2718 : errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2719 : errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2720 : RelationGetRelationName(rInfo->ri_RelationDesc),
2721 : RelationGetRelationName(rootRelInfo->ri_RelationDesc)),
2722 : errhint("Consider defining the foreign key on table \"%s\".",
2723 : RelationGetRelationName(rootRelInfo->ri_RelationDesc))));
2724 : }
2725 :
2726 : /* Perform the root table's triggers. */
2727 198 : ExecARUpdateTriggers(context->estate,
2728 : rootRelInfo, sourcePartInfo, destPartInfo,
2729 : tupleid, NULL, newslot, NIL, NULL, true);
2730 198 : }
2731 :
2732 : /* ----------------------------------------------------------------
2733 : * ExecUpdate
2734 : *
2735 : * note: we can't run UPDATE queries with transactions
2736 : * off because UPDATEs are actually INSERTs and our
2737 : * scan will mistakenly loop forever, updating the tuple
2738 : * it just inserted.. This should be fixed but until it
2739 : * is, we don't want to get stuck in an infinite loop
2740 : * which corrupts your database..
2741 : *
2742 : * When updating a table, tupleid identifies the tuple to update and
2743 : * oldtuple is NULL. When updating through a view INSTEAD OF trigger,
2744 : * oldtuple is passed to the triggers and identifies what to update, and
2745 : * tupleid is invalid. When updating a foreign table, tupleid is
2746 : * invalid; the FDW has to figure out which row to update using data from
2747 : * the planSlot. oldtuple is passed to foreign table triggers; it is
2748 : * NULL when the foreign table has no relevant triggers.
2749 : *
2750 : * oldSlot contains the old tuple value.
2751 : * slot contains the new tuple value to be stored.
2752 : * planSlot is the output of the ModifyTable's subplan; we use it
2753 : * to access values from other input tables (for RETURNING),
2754 : * row-ID junk columns, etc.
2755 : *
2756 : * Returns RETURNING result if any, otherwise NULL. On exit, if tupleid
2757 : * had identified the tuple to update, it will identify the tuple
2758 : * actually updated after EvalPlanQual.
2759 : * ----------------------------------------------------------------
2760 : */
2761 : static TupleTableSlot *
2762 2223170 : ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2763 : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *oldSlot,
2764 : TupleTableSlot *slot, bool canSetTag)
2765 : {
2766 2223170 : EState *estate = context->estate;
2767 2223170 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2768 2223170 : UpdateContext updateCxt = {0};
2769 : TM_Result result;
2770 :
2771 : /*
2772 : * abort the operation if not running transactions
2773 : */
2774 2223170 : if (IsBootstrapProcessingMode())
2775 0 : elog(ERROR, "cannot UPDATE during bootstrap");
2776 :
2777 : /*
2778 : * Prepare for the update. This includes BEFORE ROW triggers, so we're
2779 : * done if it says we are.
2780 : */
2781 2223170 : if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
2782 85 : return NULL;
2783 :
2784 : /* INSTEAD OF ROW UPDATE Triggers */
2785 2223073 : if (resultRelInfo->ri_TrigDesc &&
2786 3657 : resultRelInfo->ri_TrigDesc->trig_update_instead_row)
2787 : {
2788 83 : if (!ExecIRUpdateTriggers(estate, resultRelInfo,
2789 : oldtuple, slot))
2790 12 : return NULL; /* "do nothing" */
2791 : }
2792 2222990 : else if (resultRelInfo->ri_FdwRoutine)
2793 : {
2794 : /* Fill in GENERATEd columns */
2795 95 : ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2796 :
2797 : /*
2798 : * update in foreign table: let the FDW do it
2799 : */
2800 95 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
2801 : resultRelInfo,
2802 : slot,
2803 : context->planSlot);
2804 :
2805 95 : if (slot == NULL) /* "do nothing" */
2806 1 : return NULL;
2807 :
2808 : /*
2809 : * AFTER ROW Triggers or RETURNING expressions might reference the
2810 : * tableoid column, so (re-)initialize tts_tableOid before evaluating
2811 : * them. (This covers the case where the FDW replaced the slot.)
2812 : */
2813 94 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2814 : }
2815 : else
2816 : {
2817 : ItemPointerData lockedtid;
2818 :
2819 : /*
2820 : * If we generate a new candidate tuple after EvalPlanQual testing, we
2821 : * must loop back here to try again. (We don't need to redo triggers,
2822 : * however. If there are any BEFORE triggers then trigger.c will have
2823 : * done table_tuple_lock to lock the correct tuple, so there's no need
2824 : * to do them again.)
2825 : */
2826 2222895 : redo_act:
2827 2222950 : lockedtid = *tupleid;
2828 2222950 : result = ExecUpdateAct(context, resultRelInfo, tupleid, oldtuple, slot,
2829 : canSetTag, &updateCxt);
2830 :
2831 : /*
2832 : * If ExecUpdateAct reports that a cross-partition update was done,
2833 : * then the RETURNING tuple (if any) has been projected and there's
2834 : * nothing else for us to do.
2835 : */
2836 2222739 : if (updateCxt.crossPartUpdate)
2837 616 : return context->cpUpdateReturningSlot;
2838 :
2839 2222210 : switch (result)
2840 : {
2841 60 : case TM_SelfModified:
2842 :
2843 : /*
2844 : * The target tuple was already updated or deleted by the
2845 : * current command, or by a later command in the current
2846 : * transaction. The former case is possible in a join UPDATE
2847 : * where multiple tuples join to the same target tuple. This
2848 : * is pretty questionable, but Postgres has always allowed it:
2849 : * we just execute the first update action and ignore
2850 : * additional update attempts.
2851 : *
2852 : * The latter case arises if the tuple is modified by a
2853 : * command in a BEFORE trigger, or perhaps by a command in a
2854 : * volatile function used in the query. In such situations we
2855 : * should not ignore the update, but it is equally unsafe to
2856 : * proceed. We don't want to discard the original UPDATE
2857 : * while keeping the triggered actions based on it; and we
2858 : * have no principled way to merge this update with the
2859 : * previous ones. So throwing an error is the only safe
2860 : * course.
2861 : *
2862 : * If a trigger actually intends this type of interaction, it
2863 : * can re-execute the UPDATE (assuming it can figure out how)
2864 : * and then return NULL to cancel the outer update.
2865 : */
2866 60 : if (context->tmfd.cmax != estate->es_output_cid)
2867 4 : ereport(ERROR,
2868 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2869 : errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2870 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2871 :
2872 : /* Else, already updated by self; nothing to do */
2873 56 : return NULL;
2874 :
2875 2222041 : case TM_Ok:
2876 2222041 : break;
2877 :
2878 93 : case TM_Updated:
2879 : {
2880 : TupleTableSlot *inputslot;
2881 : TupleTableSlot *epqslot;
2882 :
2883 93 : if (IsolationUsesXactSnapshot())
2884 11 : ereport(ERROR,
2885 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2886 : errmsg("could not serialize access due to concurrent update")));
2887 :
2888 : /*
2889 : * Already know that we're going to need to do EPQ, so
2890 : * fetch tuple directly into the right slot.
2891 : */
2892 82 : inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
2893 : resultRelInfo->ri_RangeTableIndex);
2894 :
2895 82 : result = table_tuple_lock(resultRelationDesc, tupleid,
2896 : estate->es_snapshot,
2897 : inputslot, estate->es_output_cid,
2898 : updateCxt.lockmode, LockWaitBlock,
2899 : TUPLE_LOCK_FLAG_FIND_LAST_VERSION,
2900 : &context->tmfd);
2901 :
2902 80 : switch (result)
2903 : {
2904 75 : case TM_Ok:
2905 : Assert(context->tmfd.traversed);
2906 :
2907 75 : epqslot = EvalPlanQual(context->epqstate,
2908 : resultRelationDesc,
2909 : resultRelInfo->ri_RangeTableIndex,
2910 : inputslot);
2911 75 : if (TupIsNull(epqslot))
2912 : /* Tuple not passing quals anymore, exiting... */
2913 20 : return NULL;
2914 :
2915 : /* Make sure ri_oldTupleSlot is initialized. */
2916 55 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2917 0 : ExecInitUpdateProjection(context->mtstate,
2918 : resultRelInfo);
2919 :
2920 55 : if (resultRelInfo->ri_needLockTagTuple)
2921 : {
2922 1 : UnlockTuple(resultRelationDesc,
2923 : &lockedtid, InplaceUpdateTupleLock);
2924 1 : LockTuple(resultRelationDesc,
2925 : tupleid, InplaceUpdateTupleLock);
2926 : }
2927 :
2928 : /* Fetch the most recent version of old tuple. */
2929 55 : oldSlot = resultRelInfo->ri_oldTupleSlot;
2930 55 : if (!table_tuple_fetch_row_version(resultRelationDesc,
2931 : tupleid,
2932 : SnapshotAny,
2933 : oldSlot))
2934 0 : elog(ERROR, "failed to fetch tuple being updated");
2935 55 : slot = ExecGetUpdateNewTuple(resultRelInfo,
2936 : epqslot, oldSlot);
2937 55 : goto redo_act;
2938 :
2939 1 : case TM_Deleted:
2940 : /* tuple already deleted; nothing to do */
2941 1 : return NULL;
2942 :
2943 4 : case TM_SelfModified:
2944 :
2945 : /*
2946 : * This can be reached when following an update
2947 : * chain from a tuple updated by another session,
2948 : * reaching a tuple that was already updated in
2949 : * this transaction. If previously modified by
2950 : * this command, ignore the redundant update,
2951 : * otherwise error out.
2952 : *
2953 : * See also TM_SelfModified response to
2954 : * table_tuple_update() above.
2955 : */
2956 4 : if (context->tmfd.cmax != estate->es_output_cid)
2957 1 : ereport(ERROR,
2958 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2959 : errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2960 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2961 3 : return NULL;
2962 :
2963 0 : default:
2964 : /* see table_tuple_lock call in ExecDelete() */
2965 0 : elog(ERROR, "unexpected table_tuple_lock status: %u",
2966 : result);
2967 : return NULL;
2968 : }
2969 : }
2970 :
2971 : break;
2972 :
2973 16 : case TM_Deleted:
2974 16 : if (IsolationUsesXactSnapshot())
2975 9 : ereport(ERROR,
2976 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2977 : errmsg("could not serialize access due to concurrent delete")));
2978 : /* tuple already deleted; nothing to do */
2979 7 : return NULL;
2980 :
2981 0 : default:
2982 0 : elog(ERROR, "unrecognized table_tuple_update status: %u",
2983 : result);
2984 : return NULL;
2985 : }
2986 : }
2987 :
2988 2222198 : if (canSetTag)
2989 2221781 : (estate->es_processed)++;
2990 :
2991 2222198 : ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, tupleid, oldtuple,
2992 : slot);
2993 :
2994 : /* Process RETURNING if present */
2995 2222073 : if (resultRelInfo->ri_projectReturning)
2996 1516 : return ExecProcessReturning(context, resultRelInfo, false,
2997 : oldSlot, slot, context->planSlot);
2998 :
2999 2220557 : return NULL;
3000 : }
3001 :
3002 : /*
3003 : * ExecOnConflictLockRow --- lock the row for ON CONFLICT DO SELECT/UPDATE
3004 : *
3005 : * Try to lock tuple for update as part of speculative insertion for ON
3006 : * CONFLICT DO UPDATE or ON CONFLICT DO SELECT FOR UPDATE/SHARE.
3007 : *
3008 : * Returns true if the row is successfully locked, or false if the caller must
3009 : * retry the INSERT from scratch.
3010 : */
3011 : static bool
3012 2831 : ExecOnConflictLockRow(ModifyTableContext *context,
3013 : TupleTableSlot *existing,
3014 : ItemPointer conflictTid,
3015 : Relation relation,
3016 : LockTupleMode lockmode,
3017 : bool isUpdate)
3018 : {
3019 : TM_FailureData tmfd;
3020 : TM_Result test;
3021 : Datum xminDatum;
3022 : TransactionId xmin;
3023 : bool isnull;
3024 :
3025 : /*
3026 : * Lock tuple with lockmode. Don't follow updates when tuple cannot be
3027 : * locked without doing so. A row locking conflict here means our
3028 : * previous conclusion that the tuple is conclusively committed is not
3029 : * true anymore.
3030 : */
3031 2831 : test = table_tuple_lock(relation, conflictTid,
3032 2831 : context->estate->es_snapshot,
3033 2831 : existing, context->estate->es_output_cid,
3034 : lockmode, LockWaitBlock, 0,
3035 : &tmfd);
3036 2831 : switch (test)
3037 : {
3038 2800 : case TM_Ok:
3039 : /* success! */
3040 2800 : break;
3041 :
3042 28 : case TM_Invisible:
3043 :
3044 : /*
3045 : * This can occur when a just inserted tuple is updated again in
3046 : * the same command. E.g. because multiple rows with the same
3047 : * conflicting key values are inserted.
3048 : *
3049 : * This is somewhat similar to the ExecUpdate() TM_SelfModified
3050 : * case. We do not want to proceed because it would lead to the
3051 : * same row being updated a second time in some unspecified order,
3052 : * and in contrast to plain UPDATEs there's no historical behavior
3053 : * to break.
3054 : *
3055 : * It is the user's responsibility to prevent this situation from
3056 : * occurring. These problems are why the SQL standard similarly
3057 : * specifies that for SQL MERGE, an exception must be raised in
3058 : * the event of an attempt to update the same row twice.
3059 : */
3060 28 : xminDatum = slot_getsysattr(existing,
3061 : MinTransactionIdAttributeNumber,
3062 : &isnull);
3063 : Assert(!isnull);
3064 28 : xmin = DatumGetTransactionId(xminDatum);
3065 :
3066 28 : if (TransactionIdIsCurrentTransactionId(xmin))
3067 28 : ereport(ERROR,
3068 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
3069 : /* translator: %s is a SQL command name */
3070 : errmsg("%s command cannot affect row a second time",
3071 : isUpdate ? "ON CONFLICT DO UPDATE" : "ON CONFLICT DO SELECT"),
3072 : errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
3073 :
3074 : /* This shouldn't happen */
3075 0 : elog(ERROR, "attempted to lock invisible tuple");
3076 : break;
3077 :
3078 0 : case TM_SelfModified:
3079 :
3080 : /*
3081 : * This state should never be reached. As a dirty snapshot is used
3082 : * to find conflicting tuples, speculative insertion wouldn't have
3083 : * seen this row to conflict with.
3084 : */
3085 0 : elog(ERROR, "unexpected self-updated tuple");
3086 : break;
3087 :
3088 2 : case TM_Updated:
3089 2 : if (IsolationUsesXactSnapshot())
3090 0 : ereport(ERROR,
3091 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3092 : errmsg("could not serialize access due to concurrent update")));
3093 :
3094 : /*
3095 : * Tell caller to try again from the very start.
3096 : *
3097 : * It does not make sense to use the usual EvalPlanQual() style
3098 : * loop here, as the new version of the row might not conflict
3099 : * anymore, or the conflicting tuple has actually been deleted.
3100 : */
3101 2 : ExecClearTuple(existing);
3102 2 : return false;
3103 :
3104 1 : case TM_Deleted:
3105 1 : if (IsolationUsesXactSnapshot())
3106 0 : ereport(ERROR,
3107 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3108 : errmsg("could not serialize access due to concurrent delete")));
3109 :
3110 : /* see TM_Updated case */
3111 1 : ExecClearTuple(existing);
3112 1 : return false;
3113 :
3114 0 : default:
3115 0 : elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
3116 : }
3117 :
3118 : /* Success, the tuple is locked. */
3119 2800 : return true;
3120 : }
3121 :
3122 : /*
3123 : * ExecOnConflictUpdate --- execute UPDATE of INSERT ON CONFLICT DO UPDATE
3124 : *
3125 : * Try to lock tuple for update as part of speculative insertion. If
3126 : * a qual originating from ON CONFLICT DO UPDATE is satisfied, update
3127 : * (but still lock row, even though it may not satisfy estate's
3128 : * snapshot).
3129 : *
3130 : * Returns true if we're done (with or without an update), or false if
3131 : * the caller must retry the INSERT from scratch.
3132 : */
3133 : static bool
3134 2761 : ExecOnConflictUpdate(ModifyTableContext *context,
3135 : ResultRelInfo *resultRelInfo,
3136 : ItemPointer conflictTid,
3137 : TupleTableSlot *excludedSlot,
3138 : bool canSetTag,
3139 : TupleTableSlot **returning)
3140 : {
3141 2761 : ModifyTableState *mtstate = context->mtstate;
3142 2761 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
3143 2761 : Relation relation = resultRelInfo->ri_RelationDesc;
3144 2761 : ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
3145 2761 : TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
3146 : LockTupleMode lockmode;
3147 :
3148 : /*
3149 : * Parse analysis should have blocked ON CONFLICT for all system
3150 : * relations, which includes these. There's no fundamental obstacle to
3151 : * supporting this; we'd just need to handle LOCKTAG_TUPLE like the other
3152 : * ExecUpdate() caller.
3153 : */
3154 : Assert(!resultRelInfo->ri_needLockTagTuple);
3155 :
3156 : /* Determine lock mode to use */
3157 2761 : lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
3158 :
3159 : /* Lock tuple for update */
3160 2761 : if (!ExecOnConflictLockRow(context, existing, conflictTid,
3161 : resultRelInfo->ri_RelationDesc, lockmode, true))
3162 3 : return false;
3163 :
3164 : /*
3165 : * Verify that the tuple is visible to our MVCC snapshot if the current
3166 : * isolation level mandates that.
3167 : *
3168 : * It's not sufficient to rely on the check within ExecUpdate() as e.g.
3169 : * CONFLICT ... WHERE clause may prevent us from reaching that.
3170 : *
3171 : * This means we only ever continue when a new command in the current
3172 : * transaction could see the row, even though in READ COMMITTED mode the
3173 : * tuple will not be visible according to the current statement's
3174 : * snapshot. This is in line with the way UPDATE deals with newer tuple
3175 : * versions.
3176 : */
3177 2742 : ExecCheckTupleVisible(context->estate, relation, existing);
3178 :
3179 : /*
3180 : * Make tuple and any needed join variables available to ExecQual and
3181 : * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
3182 : * the target's existing tuple is installed in the scantuple. EXCLUDED
3183 : * has been made to reference INNER_VAR in setrefs.c, but there is no
3184 : * other redirection.
3185 : */
3186 2742 : econtext->ecxt_scantuple = existing;
3187 2742 : econtext->ecxt_innertuple = excludedSlot;
3188 2742 : econtext->ecxt_outertuple = NULL;
3189 :
3190 2742 : if (!ExecQual(onConflictSetWhere, econtext))
3191 : {
3192 21 : ExecClearTuple(existing); /* see return below */
3193 21 : InstrCountFiltered1(&mtstate->ps, 1);
3194 21 : return true; /* done with the tuple */
3195 : }
3196 :
3197 2721 : if (resultRelInfo->ri_WithCheckOptions != NIL)
3198 : {
3199 : /*
3200 : * Check target's existing tuple against UPDATE-applicable USING
3201 : * security barrier quals (if any), enforced here as RLS checks/WCOs.
3202 : *
3203 : * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
3204 : * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK.
3205 : * Since SELECT permission on the target table is always required for
3206 : * INSERT ... ON CONFLICT DO UPDATE, the rewriter also adds SELECT RLS
3207 : * checks/WCOs for SELECT security quals, using WCOs of the same kind,
3208 : * and this check enforces them too.
3209 : *
3210 : * The rewriter will also have associated UPDATE-applicable straight
3211 : * RLS checks/WCOs for the benefit of the ExecUpdate() call that
3212 : * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
3213 : * kinds, so there is no danger of spurious over-enforcement in the
3214 : * INSERT or UPDATE path.
3215 : */
3216 48 : ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo,
3217 : existing,
3218 : mtstate->ps.state);
3219 : }
3220 :
3221 : /* Project the new tuple version */
3222 2705 : ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
3223 :
3224 : /*
3225 : * Note that it is possible that the target tuple has been modified in
3226 : * this session, after the above table_tuple_lock. We choose to not error
3227 : * out in that case, in line with ExecUpdate's treatment of similar cases.
3228 : * This can happen if an UPDATE is triggered from within ExecQual(),
3229 : * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
3230 : * wCTE in the ON CONFLICT's SET.
3231 : */
3232 :
3233 : /* Execute UPDATE with projection */
3234 5390 : *returning = ExecUpdate(context, resultRelInfo,
3235 : conflictTid, NULL, existing,
3236 2705 : resultRelInfo->ri_onConflict->oc_ProjSlot,
3237 : canSetTag);
3238 :
3239 : /*
3240 : * Clear out existing tuple, as there might not be another conflict among
3241 : * the next input rows. Don't want to hold resources till the end of the
3242 : * query. First though, make sure that the returning slot, if any, has a
3243 : * local copy of any OLD pass-by-reference values, if it refers to any OLD
3244 : * columns.
3245 : */
3246 2685 : if (*returning != NULL &&
3247 174 : resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD)
3248 8 : ExecMaterializeSlot(*returning);
3249 :
3250 2685 : ExecClearTuple(existing);
3251 :
3252 2685 : return true;
3253 : }
3254 :
3255 : /*
3256 : * ExecOnConflictSelect --- execute SELECT of INSERT ON CONFLICT DO SELECT
3257 : *
3258 : * If SELECT FOR UPDATE/SHARE is specified, try to lock tuple as part of
3259 : * speculative insertion. If a qual originating from ON CONFLICT DO SELECT is
3260 : * satisfied, select (but still lock row, even though it may not satisfy
3261 : * estate's snapshot).
3262 : *
3263 : * Returns true if we're done (with or without a select), or false if the
3264 : * caller must retry the INSERT from scratch.
3265 : */
3266 : static bool
3267 192 : ExecOnConflictSelect(ModifyTableContext *context,
3268 : ResultRelInfo *resultRelInfo,
3269 : ItemPointer conflictTid,
3270 : TupleTableSlot *excludedSlot,
3271 : bool canSetTag,
3272 : TupleTableSlot **returning)
3273 : {
3274 192 : ModifyTableState *mtstate = context->mtstate;
3275 192 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
3276 192 : Relation relation = resultRelInfo->ri_RelationDesc;
3277 192 : ExprState *onConflictSelectWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
3278 192 : TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
3279 192 : LockClauseStrength lockStrength = resultRelInfo->ri_onConflict->oc_LockStrength;
3280 :
3281 : /*
3282 : * Parse analysis should have blocked ON CONFLICT for all system
3283 : * relations, which includes these. There's no fundamental obstacle to
3284 : * supporting this; we'd just need to handle LOCKTAG_TUPLE appropriately.
3285 : */
3286 : Assert(!resultRelInfo->ri_needLockTagTuple);
3287 :
3288 : /* Fetch/lock existing tuple, according to the requested lock strength */
3289 192 : if (lockStrength == LCS_NONE)
3290 : {
3291 122 : if (!table_tuple_fetch_row_version(relation,
3292 : conflictTid,
3293 : SnapshotAny,
3294 : existing))
3295 0 : elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
3296 : }
3297 : else
3298 : {
3299 : LockTupleMode lockmode;
3300 :
3301 70 : switch (lockStrength)
3302 : {
3303 1 : case LCS_FORKEYSHARE:
3304 1 : lockmode = LockTupleKeyShare;
3305 1 : break;
3306 1 : case LCS_FORSHARE:
3307 1 : lockmode = LockTupleShare;
3308 1 : break;
3309 1 : case LCS_FORNOKEYUPDATE:
3310 1 : lockmode = LockTupleNoKeyExclusive;
3311 1 : break;
3312 67 : case LCS_FORUPDATE:
3313 67 : lockmode = LockTupleExclusive;
3314 67 : break;
3315 0 : default:
3316 0 : elog(ERROR, "Unexpected lock strength %d", (int) lockStrength);
3317 : }
3318 :
3319 70 : if (!ExecOnConflictLockRow(context, existing, conflictTid,
3320 : resultRelInfo->ri_RelationDesc, lockmode, false))
3321 0 : return false;
3322 : }
3323 :
3324 : /*
3325 : * Verify that the tuple is visible to our MVCC snapshot if the current
3326 : * isolation level mandates that. See comments in ExecOnConflictUpdate().
3327 : */
3328 180 : ExecCheckTupleVisible(context->estate, relation, existing);
3329 :
3330 : /*
3331 : * Make tuple and any needed join variables available to ExecQual. The
3332 : * EXCLUDED tuple is installed in ecxt_innertuple, while the target's
3333 : * existing tuple is installed in the scantuple. EXCLUDED has been made
3334 : * to reference INNER_VAR in setrefs.c, but there is no other redirection.
3335 : */
3336 180 : econtext->ecxt_scantuple = existing;
3337 180 : econtext->ecxt_innertuple = excludedSlot;
3338 180 : econtext->ecxt_outertuple = NULL;
3339 :
3340 180 : if (!ExecQual(onConflictSelectWhere, econtext))
3341 : {
3342 24 : ExecClearTuple(existing); /* see return below */
3343 24 : InstrCountFiltered1(&mtstate->ps, 1);
3344 24 : return true; /* done with the tuple */
3345 : }
3346 :
3347 156 : if (resultRelInfo->ri_WithCheckOptions != NIL)
3348 : {
3349 : /*
3350 : * Check target's existing tuple against SELECT-applicable USING
3351 : * security barrier quals (if any), enforced here as RLS checks/WCOs.
3352 : *
3353 : * The rewriter creates WCOs from the USING quals of SELECT policies,
3354 : * and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK. If FOR
3355 : * UPDATE/SHARE was specified, UPDATE permissions are required on the
3356 : * target table, and the rewriter also adds WCOs built from the USING
3357 : * quals of UPDATE policies, using WCOs of the same kind, and this
3358 : * check enforces them too.
3359 : */
3360 24 : ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo,
3361 : existing,
3362 : mtstate->ps.state);
3363 : }
3364 :
3365 : /* RETURNING is required for DO SELECT */
3366 : Assert(resultRelInfo->ri_projectReturning);
3367 :
3368 152 : *returning = ExecProcessReturning(context, resultRelInfo, false,
3369 : existing, existing, context->planSlot);
3370 :
3371 152 : if (canSetTag)
3372 152 : context->estate->es_processed++;
3373 :
3374 : /*
3375 : * Before releasing the existing tuple, make sure that the returning slot
3376 : * has a local copy of any pass-by-reference values.
3377 : */
3378 152 : ExecMaterializeSlot(*returning);
3379 :
3380 : /*
3381 : * Clear out existing tuple, as there might not be another conflict among
3382 : * the next input rows. Don't want to hold resources till the end of the
3383 : * query.
3384 : */
3385 152 : ExecClearTuple(existing);
3386 :
3387 152 : return true;
3388 : }
3389 :
3390 : /*
3391 : * Perform MERGE.
3392 : */
3393 : static TupleTableSlot *
3394 10352 : ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
3395 : ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
3396 : {
3397 10352 : TupleTableSlot *rslot = NULL;
3398 : bool matched;
3399 :
3400 : /*-----
3401 : * If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
3402 : * valid, depending on whether the result relation is a table or a view.
3403 : * We execute the first action for which the additional WHEN MATCHED AND
3404 : * quals pass. If an action without quals is found, that action is
3405 : * executed.
3406 : *
3407 : * Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
3408 : * is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
3409 : * in sequence until one passes. This is almost identical to the WHEN
3410 : * MATCHED case, and both cases are handled by ExecMergeMatched().
3411 : *
3412 : * Finally, in the WHEN NOT MATCHED [BY TARGET] case, both tupleid and
3413 : * oldtuple are invalid, and we look at the given WHEN NOT MATCHED [BY
3414 : * TARGET] actions in sequence until one passes.
3415 : *
3416 : * Things get interesting in case of concurrent update/delete of the
3417 : * target tuple. Such concurrent update/delete is detected while we are
3418 : * executing a WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action.
3419 : *
3420 : * A concurrent update can:
3421 : *
3422 : * 1. modify the target tuple so that the results from checking any
3423 : * additional quals attached to WHEN MATCHED or WHEN NOT MATCHED BY
3424 : * SOURCE actions potentially change, but the result from the join
3425 : * quals does not change.
3426 : *
3427 : * In this case, we are still dealing with the same kind of match
3428 : * (MATCHED or NOT MATCHED BY SOURCE). We recheck the same list of
3429 : * actions from the start and choose the first one that satisfies the
3430 : * new target tuple.
3431 : *
3432 : * 2. modify the target tuple in the WHEN MATCHED case so that the join
3433 : * quals no longer pass and hence the source and target tuples no
3434 : * longer match.
3435 : *
3436 : * In this case, we are now dealing with a NOT MATCHED case, and we
3437 : * process both WHEN NOT MATCHED BY SOURCE and WHEN NOT MATCHED [BY
3438 : * TARGET] actions. First ExecMergeMatched() processes the list of
3439 : * WHEN NOT MATCHED BY SOURCE actions in sequence until one passes,
3440 : * then ExecMergeNotMatched() processes any WHEN NOT MATCHED [BY
3441 : * TARGET] actions in sequence until one passes. Thus we may execute
3442 : * two actions; one of each kind.
3443 : *
3444 : * Thus we support concurrent updates that turn MATCHED candidate rows
3445 : * into NOT MATCHED rows. However, we do not attempt to support cases
3446 : * that would turn NOT MATCHED rows into MATCHED rows, or which would
3447 : * cause a target row to match a different source row.
3448 : *
3449 : * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED
3450 : * [BY TARGET].
3451 : *
3452 : * ExecMergeMatched() takes care of following the update chain and
3453 : * re-finding the qualifying WHEN MATCHED or WHEN NOT MATCHED BY SOURCE
3454 : * action, as long as the target tuple still exists. If the target tuple
3455 : * gets deleted or a concurrent update causes the join quals to fail, it
3456 : * returns a matched status of false and we call ExecMergeNotMatched().
3457 : * Given that ExecMergeMatched() always makes progress by following the
3458 : * update chain and we never switch from ExecMergeNotMatched() to
3459 : * ExecMergeMatched(), there is no risk of a livelock.
3460 : */
3461 10352 : matched = tupleid != NULL || oldtuple != NULL;
3462 10352 : if (matched)
3463 8561 : rslot = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
3464 : canSetTag, &matched);
3465 :
3466 : /*
3467 : * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
3468 : * join, or a previously MATCHED tuple for which ExecMergeMatched() set
3469 : * "matched" to false, indicating that it no longer matches).
3470 : */
3471 10290 : if (!matched)
3472 : {
3473 : /*
3474 : * If a concurrent update turned a MATCHED case into a NOT MATCHED
3475 : * case, and we have both WHEN NOT MATCHED BY SOURCE and WHEN NOT
3476 : * MATCHED [BY TARGET] actions, and there is a RETURNING clause,
3477 : * ExecMergeMatched() may have already executed a WHEN NOT MATCHED BY
3478 : * SOURCE action, and computed the row to return. If so, we cannot
3479 : * execute a WHEN NOT MATCHED [BY TARGET] action now, so mark it as
3480 : * pending (to be processed on the next call to ExecModifyTable()).
3481 : * Otherwise, just process the action now.
3482 : */
3483 1800 : if (rslot == NULL)
3484 1798 : rslot = ExecMergeNotMatched(context, resultRelInfo, canSetTag);
3485 : else
3486 2 : context->mtstate->mt_merge_pending_not_matched = context->planSlot;
3487 : }
3488 :
3489 10251 : return rslot;
3490 : }
3491 :
3492 : /*
3493 : * Check and execute the first qualifying MATCHED or NOT MATCHED BY SOURCE
3494 : * action, depending on whether the join quals are satisfied. If the target
3495 : * relation is a table, the current target tuple is identified by tupleid.
3496 : * Otherwise, if the target relation is a view, oldtuple is the current target
3497 : * tuple from the view.
3498 : *
3499 : * We start from the first WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action
3500 : * and check if the WHEN quals pass, if any. If the WHEN quals for the first
3501 : * action do not pass, we check the second, then the third and so on. If we
3502 : * reach the end without finding a qualifying action, we return NULL.
3503 : * Otherwise, we execute the qualifying action and return its RETURNING
3504 : * result, if any, or NULL.
3505 : *
3506 : * On entry, "*matched" is assumed to be true. If a concurrent update or
3507 : * delete is detected that causes the join quals to no longer pass, we set it
3508 : * to false, indicating that the caller should process any NOT MATCHED [BY
3509 : * TARGET] actions.
3510 : *
3511 : * After a concurrent update, we restart from the first action to look for a
3512 : * new qualifying action to execute. If the join quals originally passed, and
3513 : * the concurrent update caused them to no longer pass, then we switch from
3514 : * the MATCHED to the NOT MATCHED BY SOURCE list of actions before restarting
3515 : * (and setting "*matched" to false). As a result we may execute a WHEN NOT
3516 : * MATCHED BY SOURCE action, and set "*matched" to false, causing the caller
3517 : * to also execute a WHEN NOT MATCHED [BY TARGET] action.
3518 : */
3519 : static TupleTableSlot *
3520 8561 : ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
3521 : ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag,
3522 : bool *matched)
3523 : {
3524 8561 : ModifyTableState *mtstate = context->mtstate;
3525 8561 : List **mergeActions = resultRelInfo->ri_MergeActions;
3526 : ItemPointerData lockedtid;
3527 : List *actionStates;
3528 8561 : TupleTableSlot *newslot = NULL;
3529 8561 : TupleTableSlot *rslot = NULL;
3530 8561 : EState *estate = context->estate;
3531 8561 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
3532 : bool isNull;
3533 8561 : EPQState *epqstate = &mtstate->mt_epqstate;
3534 : ListCell *l;
3535 :
3536 : /* Expect matched to be true on entry */
3537 : Assert(*matched);
3538 :
3539 : /*
3540 : * If there are no WHEN MATCHED or WHEN NOT MATCHED BY SOURCE actions, we
3541 : * are done.
3542 : */
3543 8561 : if (mergeActions[MERGE_WHEN_MATCHED] == NIL &&
3544 780 : mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] == NIL)
3545 332 : return NULL;
3546 :
3547 : /*
3548 : * Make tuple and any needed join variables available to ExecQual and
3549 : * ExecProject. The target's existing tuple is installed in the scantuple.
3550 : * This target relation's slot is required only in the case of a MATCHED
3551 : * or NOT MATCHED BY SOURCE tuple and UPDATE/DELETE actions.
3552 : */
3553 8229 : econtext->ecxt_scantuple = resultRelInfo->ri_oldTupleSlot;
3554 8229 : econtext->ecxt_innertuple = context->planSlot;
3555 8229 : econtext->ecxt_outertuple = NULL;
3556 :
3557 : /*
3558 : * This routine is only invoked for matched target rows, so we should
3559 : * either have the tupleid of the target row, or an old tuple from the
3560 : * target wholerow junk attr.
3561 : */
3562 : Assert(tupleid != NULL || oldtuple != NULL);
3563 8229 : ItemPointerSetInvalid(&lockedtid);
3564 8229 : if (oldtuple != NULL)
3565 : {
3566 : Assert(!resultRelInfo->ri_needLockTagTuple);
3567 64 : ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
3568 : false);
3569 : }
3570 : else
3571 : {
3572 8165 : if (resultRelInfo->ri_needLockTagTuple)
3573 : {
3574 : /*
3575 : * This locks even for CMD_DELETE, for CMD_NOTHING, and for tuples
3576 : * that don't match mas_whenqual. MERGE on system catalogs is a
3577 : * minor use case, so don't bother optimizing those.
3578 : */
3579 5548 : LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
3580 : InplaceUpdateTupleLock);
3581 5548 : lockedtid = *tupleid;
3582 : }
3583 8165 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
3584 : tupleid,
3585 : SnapshotAny,
3586 : resultRelInfo->ri_oldTupleSlot))
3587 0 : elog(ERROR, "failed to fetch the target tuple");
3588 : }
3589 :
3590 : /*
3591 : * Test the join condition. If it's satisfied, perform a MATCHED action.
3592 : * Otherwise, perform a NOT MATCHED BY SOURCE action.
3593 : *
3594 : * Note that this join condition will be NULL if there are no NOT MATCHED
3595 : * BY SOURCE actions --- see transform_MERGE_to_join(). In that case, we
3596 : * need only consider MATCHED actions here.
3597 : */
3598 8229 : if (ExecQual(resultRelInfo->ri_MergeJoinCondition, econtext))
3599 8107 : actionStates = mergeActions[MERGE_WHEN_MATCHED];
3600 : else
3601 122 : actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
3602 :
3603 8229 : lmerge_matched:
3604 :
3605 14784 : foreach(l, actionStates)
3606 : {
3607 8312 : MergeActionState *relaction = (MergeActionState *) lfirst(l);
3608 8312 : CmdType commandType = relaction->mas_action->commandType;
3609 : TM_Result result;
3610 8312 : UpdateContext updateCxt = {0};
3611 :
3612 : /*
3613 : * Test condition, if any.
3614 : *
3615 : * In the absence of any condition, we perform the action
3616 : * unconditionally (no need to check separately since ExecQual() will
3617 : * return true if there are no conditions to evaluate).
3618 : */
3619 8312 : if (!ExecQual(relaction->mas_whenqual, econtext))
3620 6515 : continue;
3621 :
3622 : /*
3623 : * Check if the existing target tuple meets the USING checks of
3624 : * UPDATE/DELETE RLS policies. If those checks fail, we throw an
3625 : * error.
3626 : *
3627 : * The WITH CHECK quals for UPDATE RLS policies are applied in
3628 : * ExecUpdateAct() and hence we need not do anything special to handle
3629 : * them.
3630 : *
3631 : * NOTE: We must do this after WHEN quals are evaluated, so that we
3632 : * check policies only when they matter.
3633 : */
3634 1797 : if (resultRelInfo->ri_WithCheckOptions && commandType != CMD_NOTHING)
3635 : {
3636 76 : ExecWithCheckOptions(commandType == CMD_UPDATE ?
3637 : WCO_RLS_MERGE_UPDATE_CHECK : WCO_RLS_MERGE_DELETE_CHECK,
3638 : resultRelInfo,
3639 : resultRelInfo->ri_oldTupleSlot,
3640 76 : context->mtstate->ps.state);
3641 : }
3642 :
3643 : /* Perform stated action */
3644 1781 : switch (commandType)
3645 : {
3646 1413 : case CMD_UPDATE:
3647 :
3648 : /*
3649 : * Project the output tuple, and use that to update the table.
3650 : * We don't need to filter out junk attributes, because the
3651 : * UPDATE action's targetlist doesn't have any.
3652 : */
3653 1413 : newslot = ExecProject(relaction->mas_proj);
3654 :
3655 1413 : mtstate->mt_merge_action = relaction;
3656 1413 : if (!ExecUpdatePrologue(context, resultRelInfo,
3657 : tupleid, NULL, newslot, &result))
3658 : {
3659 11 : if (result == TM_Ok)
3660 102 : goto out; /* "do nothing" */
3661 :
3662 7 : break; /* concurrent update/delete */
3663 : }
3664 :
3665 : /* INSTEAD OF ROW UPDATE Triggers */
3666 1402 : if (resultRelInfo->ri_TrigDesc &&
3667 230 : resultRelInfo->ri_TrigDesc->trig_update_instead_row)
3668 : {
3669 52 : if (!ExecIRUpdateTriggers(estate, resultRelInfo,
3670 : oldtuple, newslot))
3671 0 : goto out; /* "do nothing" */
3672 : }
3673 : else
3674 : {
3675 : /* checked ri_needLockTagTuple above */
3676 : Assert(oldtuple == NULL);
3677 :
3678 1350 : result = ExecUpdateAct(context, resultRelInfo, tupleid,
3679 : NULL, newslot, canSetTag,
3680 : &updateCxt);
3681 :
3682 : /*
3683 : * As in ExecUpdate(), if ExecUpdateAct() reports that a
3684 : * cross-partition update was done, then there's nothing
3685 : * else for us to do --- the UPDATE has been turned into a
3686 : * DELETE and an INSERT, and we must not perform any of
3687 : * the usual post-update tasks. Also, the RETURNING tuple
3688 : * (if any) has been projected, so we can just return
3689 : * that.
3690 : */
3691 1335 : if (updateCxt.crossPartUpdate)
3692 : {
3693 89 : mtstate->mt_merge_updated += 1;
3694 89 : rslot = context->cpUpdateReturningSlot;
3695 89 : goto out;
3696 : }
3697 : }
3698 :
3699 1298 : if (result == TM_Ok)
3700 : {
3701 1251 : ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
3702 : tupleid, NULL, newslot);
3703 1243 : mtstate->mt_merge_updated += 1;
3704 : }
3705 1290 : break;
3706 :
3707 348 : case CMD_DELETE:
3708 348 : mtstate->mt_merge_action = relaction;
3709 348 : if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
3710 : NULL, NULL, &result))
3711 : {
3712 7 : if (result == TM_Ok)
3713 4 : goto out; /* "do nothing" */
3714 :
3715 3 : break; /* concurrent update/delete */
3716 : }
3717 :
3718 : /* INSTEAD OF ROW DELETE Triggers */
3719 341 : if (resultRelInfo->ri_TrigDesc &&
3720 37 : resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
3721 : {
3722 4 : if (!ExecIRDeleteTriggers(estate, resultRelInfo,
3723 : oldtuple))
3724 0 : goto out; /* "do nothing" */
3725 : }
3726 : else
3727 : {
3728 : /* checked ri_needLockTagTuple above */
3729 : Assert(oldtuple == NULL);
3730 :
3731 337 : result = ExecDeleteAct(context, resultRelInfo, tupleid,
3732 : false);
3733 : }
3734 :
3735 341 : if (result == TM_Ok)
3736 : {
3737 330 : ExecDeleteEpilogue(context, resultRelInfo, tupleid, NULL,
3738 : false);
3739 330 : mtstate->mt_merge_deleted += 1;
3740 : }
3741 341 : break;
3742 :
3743 20 : case CMD_NOTHING:
3744 : /* Doing nothing is always OK */
3745 20 : result = TM_Ok;
3746 20 : break;
3747 :
3748 0 : default:
3749 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
3750 : }
3751 :
3752 1661 : switch (result)
3753 : {
3754 1593 : case TM_Ok:
3755 : /* all good; perform final actions */
3756 1593 : if (canSetTag && commandType != CMD_NOTHING)
3757 1559 : (estate->es_processed)++;
3758 :
3759 1593 : break;
3760 :
3761 21 : case TM_SelfModified:
3762 :
3763 : /*
3764 : * The target tuple was already updated or deleted by the
3765 : * current command, or by a later command in the current
3766 : * transaction. The former case is explicitly disallowed by
3767 : * the SQL standard for MERGE, which insists that the MERGE
3768 : * join condition should not join a target row to more than
3769 : * one source row.
3770 : *
3771 : * The latter case arises if the tuple is modified by a
3772 : * command in a BEFORE trigger, or perhaps by a command in a
3773 : * volatile function used in the query. In such situations we
3774 : * should not ignore the MERGE action, but it is equally
3775 : * unsafe to proceed. We don't want to discard the original
3776 : * MERGE action while keeping the triggered actions based on
3777 : * it; and it would be no better to allow the original MERGE
3778 : * action while discarding the updates that it triggered. So
3779 : * throwing an error is the only safe course.
3780 : */
3781 21 : if (context->tmfd.cmax != estate->es_output_cid)
3782 8 : ereport(ERROR,
3783 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3784 : errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3785 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3786 :
3787 13 : if (TransactionIdIsCurrentTransactionId(context->tmfd.xmax))
3788 13 : ereport(ERROR,
3789 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
3790 : /* translator: %s is a SQL command name */
3791 : errmsg("%s command cannot affect row a second time",
3792 : "MERGE"),
3793 : errhint("Ensure that not more than one source row matches any one target row.")));
3794 :
3795 : /* This shouldn't happen */
3796 0 : elog(ERROR, "attempted to update or delete invisible tuple");
3797 : break;
3798 :
3799 5 : case TM_Deleted:
3800 5 : if (IsolationUsesXactSnapshot())
3801 0 : ereport(ERROR,
3802 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3803 : errmsg("could not serialize access due to concurrent delete")));
3804 :
3805 : /*
3806 : * If the tuple was already deleted, set matched to false to
3807 : * let caller handle it under NOT MATCHED [BY TARGET] clauses.
3808 : */
3809 5 : *matched = false;
3810 5 : goto out;
3811 :
3812 42 : case TM_Updated:
3813 : {
3814 : bool was_matched;
3815 : Relation resultRelationDesc;
3816 : TupleTableSlot *epqslot,
3817 : *inputslot;
3818 : LockTupleMode lockmode;
3819 :
3820 42 : if (IsolationUsesXactSnapshot())
3821 1 : ereport(ERROR,
3822 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3823 : errmsg("could not serialize access due to concurrent update")));
3824 :
3825 : /*
3826 : * The target tuple was concurrently updated by some other
3827 : * transaction. If we are currently processing a MATCHED
3828 : * action, use EvalPlanQual() with the new version of the
3829 : * tuple and recheck the join qual, to detect a change
3830 : * from the MATCHED to the NOT MATCHED cases. If we are
3831 : * already processing a NOT MATCHED BY SOURCE action, we
3832 : * skip this (cannot switch from NOT MATCHED BY SOURCE to
3833 : * MATCHED).
3834 : */
3835 41 : was_matched = relaction->mas_action->matchKind == MERGE_WHEN_MATCHED;
3836 41 : resultRelationDesc = resultRelInfo->ri_RelationDesc;
3837 41 : lockmode = ExecUpdateLockMode(estate, resultRelInfo);
3838 :
3839 41 : if (was_matched)
3840 41 : inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
3841 : resultRelInfo->ri_RangeTableIndex);
3842 : else
3843 0 : inputslot = resultRelInfo->ri_oldTupleSlot;
3844 :
3845 41 : result = table_tuple_lock(resultRelationDesc, tupleid,
3846 : estate->es_snapshot,
3847 : inputslot, estate->es_output_cid,
3848 : lockmode, LockWaitBlock,
3849 : TUPLE_LOCK_FLAG_FIND_LAST_VERSION,
3850 : &context->tmfd);
3851 41 : switch (result)
3852 : {
3853 40 : case TM_Ok:
3854 :
3855 : /*
3856 : * If the tuple was updated and migrated to
3857 : * another partition concurrently, the current
3858 : * MERGE implementation can't follow. There's
3859 : * probably a better way to handle this case, but
3860 : * it'd require recognizing the relation to which
3861 : * the tuple moved, and setting our current
3862 : * resultRelInfo to that.
3863 : */
3864 40 : if (ItemPointerIndicatesMovedPartitions(tupleid))
3865 0 : ereport(ERROR,
3866 : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3867 : errmsg("tuple to be merged was already moved to another partition due to concurrent update")));
3868 :
3869 : /*
3870 : * If this was a MATCHED case, use EvalPlanQual()
3871 : * to recheck the join condition.
3872 : */
3873 40 : if (was_matched)
3874 : {
3875 40 : epqslot = EvalPlanQual(epqstate,
3876 : resultRelationDesc,
3877 : resultRelInfo->ri_RangeTableIndex,
3878 : inputslot);
3879 :
3880 : /*
3881 : * If the subplan didn't return a tuple, then
3882 : * we must be dealing with an inner join for
3883 : * which the join condition no longer matches.
3884 : * This can only happen if there are no NOT
3885 : * MATCHED actions, and so there is nothing
3886 : * more to do.
3887 : */
3888 40 : if (TupIsNull(epqslot))
3889 0 : goto out;
3890 :
3891 : /*
3892 : * If we got a NULL ctid from the subplan, the
3893 : * join quals no longer pass and we switch to
3894 : * the NOT MATCHED BY SOURCE case.
3895 : */
3896 40 : (void) ExecGetJunkAttribute(epqslot,
3897 40 : resultRelInfo->ri_RowIdAttNo,
3898 : &isNull);
3899 40 : if (isNull)
3900 2 : *matched = false;
3901 :
3902 : /*
3903 : * Otherwise, recheck the join quals to see if
3904 : * we need to switch to the NOT MATCHED BY
3905 : * SOURCE case.
3906 : */
3907 40 : if (resultRelInfo->ri_needLockTagTuple)
3908 : {
3909 1 : if (ItemPointerIsValid(&lockedtid))
3910 1 : UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3911 : InplaceUpdateTupleLock);
3912 1 : LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
3913 : InplaceUpdateTupleLock);
3914 1 : lockedtid = *tupleid;
3915 : }
3916 :
3917 40 : if (!table_tuple_fetch_row_version(resultRelationDesc,
3918 : tupleid,
3919 : SnapshotAny,
3920 : resultRelInfo->ri_oldTupleSlot))
3921 0 : elog(ERROR, "failed to fetch the target tuple");
3922 :
3923 40 : if (*matched)
3924 38 : *matched = ExecQual(resultRelInfo->ri_MergeJoinCondition,
3925 : econtext);
3926 :
3927 : /* Switch lists, if necessary */
3928 40 : if (!*matched)
3929 : {
3930 4 : actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
3931 :
3932 : /*
3933 : * If we have both NOT MATCHED BY SOURCE
3934 : * and NOT MATCHED BY TARGET actions (a
3935 : * full join between the source and target
3936 : * relations), the single previously
3937 : * matched tuple from the outer plan node
3938 : * is treated as two not matched tuples,
3939 : * in the same way as if they had not
3940 : * matched to start with. Therefore, we
3941 : * must adjust the outer plan node's tuple
3942 : * count, if we're instrumenting the
3943 : * query, to get the correct "skipped" row
3944 : * count --- see show_modifytable_info().
3945 : */
3946 4 : if (outerPlanState(mtstate)->instrument &&
3947 1 : mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] &&
3948 1 : mergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET])
3949 1 : InstrUpdateTupleCount(outerPlanState(mtstate)->instrument, 1.0);
3950 : }
3951 : }
3952 :
3953 : /*
3954 : * Loop back and process the MATCHED or NOT
3955 : * MATCHED BY SOURCE actions from the start.
3956 : */
3957 40 : goto lmerge_matched;
3958 :
3959 0 : case TM_Deleted:
3960 :
3961 : /*
3962 : * tuple already deleted; tell caller to run NOT
3963 : * MATCHED [BY TARGET] actions
3964 : */
3965 0 : *matched = false;
3966 0 : goto out;
3967 :
3968 1 : case TM_SelfModified:
3969 :
3970 : /*
3971 : * This can be reached when following an update
3972 : * chain from a tuple updated by another session,
3973 : * reaching a tuple that was already updated or
3974 : * deleted by the current command, or by a later
3975 : * command in the current transaction. As above,
3976 : * this should always be treated as an error.
3977 : */
3978 1 : if (context->tmfd.cmax != estate->es_output_cid)
3979 0 : ereport(ERROR,
3980 : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3981 : errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3982 : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3983 :
3984 1 : if (TransactionIdIsCurrentTransactionId(context->tmfd.xmax))
3985 1 : ereport(ERROR,
3986 : (errcode(ERRCODE_CARDINALITY_VIOLATION),
3987 : /* translator: %s is a SQL command name */
3988 : errmsg("%s command cannot affect row a second time",
3989 : "MERGE"),
3990 : errhint("Ensure that not more than one source row matches any one target row.")));
3991 :
3992 : /* This shouldn't happen */
3993 0 : elog(ERROR, "attempted to update or delete invisible tuple");
3994 : goto out;
3995 :
3996 0 : default:
3997 : /* see table_tuple_lock call in ExecDelete() */
3998 0 : elog(ERROR, "unexpected table_tuple_lock status: %u",
3999 : result);
4000 : goto out;
4001 : }
4002 : }
4003 :
4004 0 : case TM_Invisible:
4005 : case TM_WouldBlock:
4006 : case TM_BeingModified:
4007 : /* these should not occur */
4008 0 : elog(ERROR, "unexpected tuple operation result: %d", result);
4009 : break;
4010 : }
4011 :
4012 : /* Process RETURNING if present */
4013 1593 : if (resultRelInfo->ri_projectReturning)
4014 : {
4015 288 : switch (commandType)
4016 : {
4017 124 : case CMD_UPDATE:
4018 124 : rslot = ExecProcessReturning(context,
4019 : resultRelInfo,
4020 : false,
4021 : resultRelInfo->ri_oldTupleSlot,
4022 : newslot,
4023 : context->planSlot);
4024 124 : break;
4025 :
4026 164 : case CMD_DELETE:
4027 164 : rslot = ExecProcessReturning(context,
4028 : resultRelInfo,
4029 : true,
4030 : resultRelInfo->ri_oldTupleSlot,
4031 : NULL,
4032 : context->planSlot);
4033 164 : break;
4034 :
4035 0 : case CMD_NOTHING:
4036 0 : break;
4037 :
4038 0 : default:
4039 0 : elog(ERROR, "unrecognized commandType: %d",
4040 : (int) commandType);
4041 : }
4042 : }
4043 :
4044 : /*
4045 : * We've activated one of the WHEN clauses, so we don't search
4046 : * further. This is required behaviour, not an optimization.
4047 : */
4048 1593 : break;
4049 : }
4050 :
4051 : /*
4052 : * Successfully executed an action or no qualifying action was found.
4053 : */
4054 8167 : out:
4055 8167 : if (ItemPointerIsValid(&lockedtid))
4056 5548 : UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
4057 : InplaceUpdateTupleLock);
4058 8167 : return rslot;
4059 : }
4060 :
4061 : /*
4062 : * Execute the first qualifying NOT MATCHED [BY TARGET] action.
4063 : */
4064 : static TupleTableSlot *
4065 1800 : ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
4066 : bool canSetTag)
4067 : {
4068 1800 : ModifyTableState *mtstate = context->mtstate;
4069 1800 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
4070 : List *actionStates;
4071 1800 : TupleTableSlot *rslot = NULL;
4072 : ListCell *l;
4073 :
4074 : /*
4075 : * For INSERT actions, the root relation's merge action is OK since the
4076 : * INSERT's targetlist and the WHEN conditions can only refer to the
4077 : * source relation and hence it does not matter which result relation we
4078 : * work with.
4079 : *
4080 : * XXX does this mean that we can avoid creating copies of actionStates on
4081 : * partitioned tables, for not-matched actions?
4082 : */
4083 1800 : actionStates = resultRelInfo->ri_MergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET];
4084 :
4085 : /*
4086 : * Make source tuple available to ExecQual and ExecProject. We don't need
4087 : * the target tuple, since the WHEN quals and targetlist can't refer to
4088 : * the target columns.
4089 : */
4090 1800 : econtext->ecxt_scantuple = NULL;
4091 1800 : econtext->ecxt_innertuple = context->planSlot;
4092 1800 : econtext->ecxt_outertuple = NULL;
4093 :
4094 2380 : foreach(l, actionStates)
4095 : {
4096 1800 : MergeActionState *action = (MergeActionState *) lfirst(l);
4097 1800 : CmdType commandType = action->mas_action->commandType;
4098 : TupleTableSlot *newslot;
4099 :
4100 : /*
4101 : * Test condition, if any.
4102 : *
4103 : * In the absence of any condition, we perform the action
4104 : * unconditionally (no need to check separately since ExecQual() will
4105 : * return true if there are no conditions to evaluate).
4106 : */
4107 1800 : if (!ExecQual(action->mas_whenqual, econtext))
4108 580 : continue;
4109 :
4110 : /* Perform stated action */
4111 1220 : switch (commandType)
4112 : {
4113 1220 : case CMD_INSERT:
4114 :
4115 : /*
4116 : * Project the tuple. In case of a partitioned table, the
4117 : * projection was already built to use the root's descriptor,
4118 : * so we don't need to map the tuple here.
4119 : */
4120 1220 : newslot = ExecProject(action->mas_proj);
4121 1220 : mtstate->mt_merge_action = action;
4122 :
4123 1220 : rslot = ExecInsert(context, mtstate->rootResultRelInfo,
4124 : newslot, canSetTag, NULL, NULL);
4125 1181 : mtstate->mt_merge_inserted += 1;
4126 1181 : break;
4127 0 : case CMD_NOTHING:
4128 : /* Do nothing */
4129 0 : break;
4130 0 : default:
4131 0 : elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
4132 : }
4133 :
4134 : /*
4135 : * We've activated one of the WHEN clauses, so we don't search
4136 : * further. This is required behaviour, not an optimization.
4137 : */
4138 1181 : break;
4139 : }
4140 :
4141 1761 : return rslot;
4142 : }
4143 :
4144 : /*
4145 : * Initialize state for execution of MERGE.
4146 : */
4147 : void
4148 1056 : ExecInitMerge(ModifyTableState *mtstate, EState *estate)
4149 : {
4150 1056 : List *mergeActionLists = mtstate->mt_mergeActionLists;
4151 1056 : List *mergeJoinConditions = mtstate->mt_mergeJoinConditions;
4152 1056 : ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
4153 : ResultRelInfo *resultRelInfo;
4154 : ExprContext *econtext;
4155 : ListCell *lc;
4156 : int i;
4157 :
4158 1056 : if (mergeActionLists == NIL)
4159 0 : return;
4160 :
4161 1056 : mtstate->mt_merge_subcommands = 0;
4162 :
4163 1056 : if (mtstate->ps.ps_ExprContext == NULL)
4164 864 : ExecAssignExprContext(estate, &mtstate->ps);
4165 1056 : econtext = mtstate->ps.ps_ExprContext;
4166 :
4167 : /*
4168 : * Create a MergeActionState for each action on the mergeActionList and
4169 : * add it to either a list of matched actions or not-matched actions.
4170 : *
4171 : * Similar logic appears in ExecInitPartitionInfo(), so if changing
4172 : * anything here, do so there too.
4173 : */
4174 1056 : i = 0;
4175 2268 : foreach(lc, mergeActionLists)
4176 : {
4177 1212 : List *mergeActionList = lfirst(lc);
4178 : Node *joinCondition;
4179 : TupleDesc relationDesc;
4180 : ListCell *l;
4181 :
4182 1212 : joinCondition = (Node *) list_nth(mergeJoinConditions, i);
4183 1212 : resultRelInfo = mtstate->resultRelInfo + i;
4184 1212 : i++;
4185 1212 : relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
4186 :
4187 : /* initialize slots for MERGE fetches from this rel */
4188 1212 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4189 1212 : ExecInitMergeTupleSlots(mtstate, resultRelInfo);
4190 :
4191 : /* initialize state for join condition checking */
4192 1212 : resultRelInfo->ri_MergeJoinCondition =
4193 1212 : ExecInitQual((List *) joinCondition, &mtstate->ps);
4194 :
4195 3321 : foreach(l, mergeActionList)
4196 : {
4197 2109 : MergeAction *action = (MergeAction *) lfirst(l);
4198 : MergeActionState *action_state;
4199 : TupleTableSlot *tgtslot;
4200 : TupleDesc tgtdesc;
4201 :
4202 : /*
4203 : * Build action merge state for this rel. (For partitions,
4204 : * equivalent code exists in ExecInitPartitionInfo.)
4205 : */
4206 2109 : action_state = makeNode(MergeActionState);
4207 2109 : action_state->mas_action = action;
4208 2109 : action_state->mas_whenqual = ExecInitQual((List *) action->qual,
4209 : &mtstate->ps);
4210 :
4211 : /*
4212 : * We create three lists - one for each MergeMatchKind - and stick
4213 : * the MergeActionState into the appropriate list.
4214 : */
4215 4218 : resultRelInfo->ri_MergeActions[action->matchKind] =
4216 2109 : lappend(resultRelInfo->ri_MergeActions[action->matchKind],
4217 : action_state);
4218 :
4219 2109 : switch (action->commandType)
4220 : {
4221 704 : case CMD_INSERT:
4222 : /* INSERT actions always use rootRelInfo */
4223 704 : ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
4224 : action->targetList);
4225 :
4226 : /*
4227 : * If the MERGE targets a partitioned table, any INSERT
4228 : * actions must be routed through it, not the child
4229 : * relations. Initialize the routing struct and the root
4230 : * table's "new" tuple slot for that, if not already done.
4231 : * The projection we prepare, for all relations, uses the
4232 : * root relation descriptor, and targets the plan's root
4233 : * slot. (This is consistent with the fact that we
4234 : * checked the plan output to match the root relation,
4235 : * above.)
4236 : */
4237 704 : if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
4238 : RELKIND_PARTITIONED_TABLE)
4239 : {
4240 216 : if (mtstate->mt_partition_tuple_routing == NULL)
4241 : {
4242 : /*
4243 : * Initialize planstate for routing if not already
4244 : * done.
4245 : *
4246 : * Note that the slot is managed as a standalone
4247 : * slot belonging to ModifyTableState, so we pass
4248 : * NULL for the 2nd argument.
4249 : */
4250 100 : mtstate->mt_root_tuple_slot =
4251 100 : table_slot_create(rootRelInfo->ri_RelationDesc,
4252 : NULL);
4253 100 : mtstate->mt_partition_tuple_routing =
4254 100 : ExecSetupPartitionTupleRouting(estate,
4255 : rootRelInfo->ri_RelationDesc);
4256 : }
4257 216 : tgtslot = mtstate->mt_root_tuple_slot;
4258 216 : tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
4259 : }
4260 : else
4261 : {
4262 : /*
4263 : * If the MERGE targets an inherited table, we insert
4264 : * into the root table, so we must initialize its
4265 : * "new" tuple slot, if not already done, and use its
4266 : * relation descriptor for the projection.
4267 : *
4268 : * For non-inherited tables, rootRelInfo and
4269 : * resultRelInfo are the same, and the "new" tuple
4270 : * slot will already have been initialized.
4271 : */
4272 488 : if (rootRelInfo->ri_newTupleSlot == NULL)
4273 24 : rootRelInfo->ri_newTupleSlot =
4274 24 : table_slot_create(rootRelInfo->ri_RelationDesc,
4275 : &estate->es_tupleTable);
4276 :
4277 488 : tgtslot = rootRelInfo->ri_newTupleSlot;
4278 488 : tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
4279 : }
4280 :
4281 704 : action_state->mas_proj =
4282 704 : ExecBuildProjectionInfo(action->targetList, econtext,
4283 : tgtslot,
4284 : &mtstate->ps,
4285 : tgtdesc);
4286 :
4287 704 : mtstate->mt_merge_subcommands |= MERGE_INSERT;
4288 704 : break;
4289 1040 : case CMD_UPDATE:
4290 1040 : action_state->mas_proj =
4291 1040 : ExecBuildUpdateProjection(action->targetList,
4292 : true,
4293 : action->updateColnos,
4294 : relationDesc,
4295 : econtext,
4296 : resultRelInfo->ri_newTupleSlot,
4297 : &mtstate->ps);
4298 1040 : mtstate->mt_merge_subcommands |= MERGE_UPDATE;
4299 1040 : break;
4300 315 : case CMD_DELETE:
4301 315 : mtstate->mt_merge_subcommands |= MERGE_DELETE;
4302 315 : break;
4303 50 : case CMD_NOTHING:
4304 50 : break;
4305 0 : default:
4306 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
4307 : break;
4308 : }
4309 : }
4310 : }
4311 :
4312 : /*
4313 : * If the MERGE targets an inherited table, any INSERT actions will use
4314 : * rootRelInfo, and rootRelInfo will not be in the resultRelInfo array.
4315 : * Therefore we must initialize its WITH CHECK OPTION constraints and
4316 : * RETURNING projection, as ExecInitModifyTable did for the resultRelInfo
4317 : * entries.
4318 : *
4319 : * Note that the planner does not build a withCheckOptionList or
4320 : * returningList for the root relation, but as in ExecInitPartitionInfo,
4321 : * we can use the first resultRelInfo entry as a reference to calculate
4322 : * the attno's for the root table.
4323 : */
4324 1056 : if (rootRelInfo != mtstate->resultRelInfo &&
4325 159 : rootRelInfo->ri_RelationDesc->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
4326 32 : (mtstate->mt_merge_subcommands & MERGE_INSERT) != 0)
4327 : {
4328 24 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
4329 24 : Relation rootRelation = rootRelInfo->ri_RelationDesc;
4330 24 : Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
4331 24 : int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
4332 24 : AttrMap *part_attmap = NULL;
4333 : bool found_whole_row;
4334 :
4335 24 : if (node->withCheckOptionLists != NIL)
4336 : {
4337 : List *wcoList;
4338 12 : List *wcoExprs = NIL;
4339 :
4340 : /* There should be as many WCO lists as result rels */
4341 : Assert(list_length(node->withCheckOptionLists) ==
4342 : list_length(node->resultRelations));
4343 :
4344 : /*
4345 : * Use the first WCO list as a reference. In the most common case,
4346 : * this will be for the same relation as rootRelInfo, and so there
4347 : * will be no need to adjust its attno's.
4348 : */
4349 12 : wcoList = linitial(node->withCheckOptionLists);
4350 12 : if (rootRelation != firstResultRel)
4351 : {
4352 : /* Convert any Vars in it to contain the root's attno's */
4353 : part_attmap =
4354 12 : build_attrmap_by_name(RelationGetDescr(rootRelation),
4355 : RelationGetDescr(firstResultRel),
4356 : false);
4357 :
4358 : wcoList = (List *)
4359 12 : map_variable_attnos((Node *) wcoList,
4360 : firstVarno, 0,
4361 : part_attmap,
4362 12 : RelationGetForm(rootRelation)->reltype,
4363 : &found_whole_row);
4364 : }
4365 :
4366 60 : foreach(lc, wcoList)
4367 : {
4368 48 : WithCheckOption *wco = lfirst_node(WithCheckOption, lc);
4369 48 : ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
4370 : &mtstate->ps);
4371 :
4372 48 : wcoExprs = lappend(wcoExprs, wcoExpr);
4373 : }
4374 :
4375 12 : rootRelInfo->ri_WithCheckOptions = wcoList;
4376 12 : rootRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4377 : }
4378 :
4379 24 : if (node->returningLists != NIL)
4380 : {
4381 : List *returningList;
4382 :
4383 : /* There should be as many returning lists as result rels */
4384 : Assert(list_length(node->returningLists) ==
4385 : list_length(node->resultRelations));
4386 :
4387 : /*
4388 : * Use the first returning list as a reference. In the most common
4389 : * case, this will be for the same relation as rootRelInfo, and so
4390 : * there will be no need to adjust its attno's.
4391 : */
4392 4 : returningList = linitial(node->returningLists);
4393 4 : if (rootRelation != firstResultRel)
4394 : {
4395 : /* Convert any Vars in it to contain the root's attno's */
4396 4 : if (part_attmap == NULL)
4397 : part_attmap =
4398 0 : build_attrmap_by_name(RelationGetDescr(rootRelation),
4399 : RelationGetDescr(firstResultRel),
4400 : false);
4401 :
4402 : returningList = (List *)
4403 4 : map_variable_attnos((Node *) returningList,
4404 : firstVarno, 0,
4405 : part_attmap,
4406 4 : RelationGetForm(rootRelation)->reltype,
4407 : &found_whole_row);
4408 : }
4409 4 : rootRelInfo->ri_returningList = returningList;
4410 :
4411 : /* Initialize the RETURNING projection */
4412 4 : rootRelInfo->ri_projectReturning =
4413 4 : ExecBuildProjectionInfo(returningList, econtext,
4414 : mtstate->ps.ps_ResultTupleSlot,
4415 : &mtstate->ps,
4416 : RelationGetDescr(rootRelation));
4417 : }
4418 : }
4419 : }
4420 :
4421 : /*
4422 : * Initializes the tuple slots in a ResultRelInfo for any MERGE action.
4423 : *
4424 : * We mark 'projectNewInfoValid' even though the projections themselves
4425 : * are not initialized here.
4426 : */
4427 : void
4428 1227 : ExecInitMergeTupleSlots(ModifyTableState *mtstate,
4429 : ResultRelInfo *resultRelInfo)
4430 : {
4431 1227 : EState *estate = mtstate->ps.state;
4432 :
4433 : Assert(!resultRelInfo->ri_projectNewInfoValid);
4434 :
4435 1227 : resultRelInfo->ri_oldTupleSlot =
4436 1227 : table_slot_create(resultRelInfo->ri_RelationDesc,
4437 : &estate->es_tupleTable);
4438 1227 : resultRelInfo->ri_newTupleSlot =
4439 1227 : table_slot_create(resultRelInfo->ri_RelationDesc,
4440 : &estate->es_tupleTable);
4441 1227 : resultRelInfo->ri_projectNewInfoValid = true;
4442 1227 : }
4443 :
4444 : /*
4445 : * Process BEFORE EACH STATEMENT triggers
4446 : */
4447 : static void
4448 76178 : fireBSTriggers(ModifyTableState *node)
4449 : {
4450 76178 : ModifyTable *plan = (ModifyTable *) node->ps.plan;
4451 76178 : ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4452 :
4453 76178 : switch (node->operation)
4454 : {
4455 57909 : case CMD_INSERT:
4456 57909 : ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4457 57901 : if (plan->onConflictAction == ONCONFLICT_UPDATE)
4458 616 : ExecBSUpdateTriggers(node->ps.state,
4459 : resultRelInfo);
4460 57901 : break;
4461 8978 : case CMD_UPDATE:
4462 8978 : ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4463 8978 : break;
4464 8334 : case CMD_DELETE:
4465 8334 : ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4466 8334 : break;
4467 957 : case CMD_MERGE:
4468 957 : if (node->mt_merge_subcommands & MERGE_INSERT)
4469 523 : ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4470 957 : if (node->mt_merge_subcommands & MERGE_UPDATE)
4471 629 : ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4472 957 : if (node->mt_merge_subcommands & MERGE_DELETE)
4473 259 : ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4474 957 : break;
4475 0 : default:
4476 0 : elog(ERROR, "unknown operation");
4477 : break;
4478 : }
4479 76170 : }
4480 :
4481 : /*
4482 : * Process AFTER EACH STATEMENT triggers
4483 : */
4484 : static void
4485 73875 : fireASTriggers(ModifyTableState *node)
4486 : {
4487 73875 : ModifyTable *plan = (ModifyTable *) node->ps.plan;
4488 73875 : ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4489 :
4490 73875 : switch (node->operation)
4491 : {
4492 56314 : case CMD_INSERT:
4493 56314 : if (plan->onConflictAction == ONCONFLICT_UPDATE)
4494 544 : ExecASUpdateTriggers(node->ps.state,
4495 : resultRelInfo,
4496 544 : node->mt_oc_transition_capture);
4497 56314 : ExecASInsertTriggers(node->ps.state, resultRelInfo,
4498 56314 : node->mt_transition_capture);
4499 56314 : break;
4500 8472 : case CMD_UPDATE:
4501 8472 : ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4502 8472 : node->mt_transition_capture);
4503 8472 : break;
4504 8234 : case CMD_DELETE:
4505 8234 : ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4506 8234 : node->mt_transition_capture);
4507 8234 : break;
4508 855 : case CMD_MERGE:
4509 855 : if (node->mt_merge_subcommands & MERGE_DELETE)
4510 234 : ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4511 234 : node->mt_transition_capture);
4512 855 : if (node->mt_merge_subcommands & MERGE_UPDATE)
4513 564 : ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4514 564 : node->mt_transition_capture);
4515 855 : if (node->mt_merge_subcommands & MERGE_INSERT)
4516 478 : ExecASInsertTriggers(node->ps.state, resultRelInfo,
4517 478 : node->mt_transition_capture);
4518 855 : break;
4519 0 : default:
4520 0 : elog(ERROR, "unknown operation");
4521 : break;
4522 : }
4523 73875 : }
4524 :
4525 : /*
4526 : * Set up the state needed for collecting transition tuples for AFTER
4527 : * triggers.
4528 : */
4529 : static void
4530 76432 : ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
4531 : {
4532 76432 : ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
4533 76432 : ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
4534 :
4535 : /* Check for transition tables on the directly targeted relation. */
4536 76432 : mtstate->mt_transition_capture =
4537 76432 : MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
4538 76432 : RelationGetRelid(targetRelInfo->ri_RelationDesc),
4539 : mtstate->operation);
4540 76432 : if (plan->operation == CMD_INSERT &&
4541 56850 : plan->onConflictAction == ONCONFLICT_UPDATE)
4542 620 : mtstate->mt_oc_transition_capture =
4543 620 : MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
4544 620 : RelationGetRelid(targetRelInfo->ri_RelationDesc),
4545 : CMD_UPDATE);
4546 76432 : }
4547 :
4548 : /*
4549 : * ExecPrepareTupleRouting --- prepare for routing one tuple
4550 : *
4551 : * Determine the partition in which the tuple in slot is to be inserted,
4552 : * and return its ResultRelInfo in *partRelInfo. The return value is
4553 : * a slot holding the tuple of the partition rowtype.
4554 : *
4555 : * This also sets the transition table information in mtstate based on the
4556 : * selected partition.
4557 : */
4558 : static TupleTableSlot *
4559 481010 : ExecPrepareTupleRouting(ModifyTableState *mtstate,
4560 : EState *estate,
4561 : PartitionTupleRouting *proute,
4562 : ResultRelInfo *targetRelInfo,
4563 : TupleTableSlot *slot,
4564 : ResultRelInfo **partRelInfo)
4565 : {
4566 : ResultRelInfo *partrel;
4567 : TupleConversionMap *map;
4568 :
4569 : /*
4570 : * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
4571 : * not find a valid partition for the tuple in 'slot' then an error is
4572 : * raised. An error may also be raised if the found partition is not a
4573 : * valid target for INSERTs. This is required since a partitioned table
4574 : * UPDATE to another partition becomes a DELETE+INSERT.
4575 : */
4576 481010 : partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
4577 :
4578 : /*
4579 : * If we're capturing transition tuples, we might need to convert from the
4580 : * partition rowtype to root partitioned table's rowtype. But if there
4581 : * are no BEFORE triggers on the partition that could change the tuple, we
4582 : * can just remember the original unconverted tuple to avoid a needless
4583 : * round trip conversion.
4584 : */
4585 480866 : if (mtstate->mt_transition_capture != NULL)
4586 : {
4587 : bool has_before_insert_row_trig;
4588 :
4589 130 : has_before_insert_row_trig = (partrel->ri_TrigDesc &&
4590 28 : partrel->ri_TrigDesc->trig_insert_before_row);
4591 :
4592 102 : mtstate->mt_transition_capture->tcs_original_insert_tuple =
4593 102 : !has_before_insert_row_trig ? slot : NULL;
4594 : }
4595 :
4596 : /*
4597 : * Convert the tuple, if necessary.
4598 : */
4599 480866 : map = ExecGetRootToChildMap(partrel, estate);
4600 480866 : if (map != NULL)
4601 : {
4602 45768 : TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
4603 :
4604 45768 : slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
4605 : }
4606 :
4607 480866 : *partRelInfo = partrel;
4608 480866 : return slot;
4609 : }
4610 :
4611 : /* ----------------------------------------------------------------
4612 : * ExecModifyTable
4613 : *
4614 : * Perform table modifications as required, and return RETURNING results
4615 : * if needed.
4616 : * ----------------------------------------------------------------
4617 : */
4618 : static TupleTableSlot *
4619 81563 : ExecModifyTable(PlanState *pstate)
4620 : {
4621 81563 : ModifyTableState *node = castNode(ModifyTableState, pstate);
4622 : ModifyTableContext context;
4623 81563 : EState *estate = node->ps.state;
4624 81563 : CmdType operation = node->operation;
4625 : ResultRelInfo *resultRelInfo;
4626 : PlanState *subplanstate;
4627 : TupleTableSlot *slot;
4628 : TupleTableSlot *oldSlot;
4629 : ItemPointerData tuple_ctid;
4630 : HeapTupleData oldtupdata;
4631 : HeapTuple oldtuple;
4632 : ItemPointer tupleid;
4633 : bool tuplock;
4634 :
4635 81563 : CHECK_FOR_INTERRUPTS();
4636 :
4637 : /*
4638 : * This should NOT get called during EvalPlanQual; we should have passed a
4639 : * subplan tree to EvalPlanQual, instead. Use a runtime test not just
4640 : * Assert because this condition is easy to miss in testing. (Note:
4641 : * although ModifyTable should not get executed within an EvalPlanQual
4642 : * operation, we do have to allow it to be initialized and shut down in
4643 : * case it is within a CTE subplan. Hence this test must be here, not in
4644 : * ExecInitModifyTable.)
4645 : */
4646 81563 : if (estate->es_epq_active != NULL)
4647 0 : elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
4648 :
4649 : /*
4650 : * If we've already completed processing, don't try to do more. We need
4651 : * this test because ExecPostprocessPlan might call us an extra time, and
4652 : * our subplan's nodes aren't necessarily robust against being called
4653 : * extra times.
4654 : */
4655 81563 : if (node->mt_done)
4656 584 : return NULL;
4657 :
4658 : /*
4659 : * On first call, fire BEFORE STATEMENT triggers before proceeding.
4660 : */
4661 80979 : if (node->fireBSTriggers)
4662 : {
4663 75114 : fireBSTriggers(node);
4664 75106 : node->fireBSTriggers = false;
4665 : }
4666 :
4667 : /* Preload local variables */
4668 80971 : resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
4669 80971 : subplanstate = outerPlanState(node);
4670 :
4671 : /* Set global context */
4672 80971 : context.mtstate = node;
4673 80971 : context.epqstate = &node->mt_epqstate;
4674 80971 : context.estate = estate;
4675 :
4676 : /*
4677 : * Fetch rows from subplan, and execute the required table modification
4678 : * for each row.
4679 : */
4680 : for (;;)
4681 : {
4682 : /*
4683 : * Reset the per-output-tuple exprcontext. This is needed because
4684 : * triggers expect to use that context as workspace. It's a bit ugly
4685 : * to do this below the top level of the plan, however. We might need
4686 : * to rethink this later.
4687 : */
4688 11111962 : ResetPerTupleExprContext(estate);
4689 :
4690 : /*
4691 : * Reset per-tuple memory context used for processing on conflict and
4692 : * returning clauses, to free any expression evaluation storage
4693 : * allocated in the previous cycle.
4694 : */
4695 11111962 : if (pstate->ps_ExprContext)
4696 2244934 : ResetExprContext(pstate->ps_ExprContext);
4697 :
4698 : /*
4699 : * If there is a pending MERGE ... WHEN NOT MATCHED [BY TARGET] action
4700 : * to execute, do so now --- see the comments in ExecMerge().
4701 : */
4702 11111962 : if (node->mt_merge_pending_not_matched != NULL)
4703 : {
4704 2 : context.planSlot = node->mt_merge_pending_not_matched;
4705 2 : context.cpDeletedSlot = NULL;
4706 :
4707 2 : slot = ExecMergeNotMatched(&context, node->resultRelInfo,
4708 2 : node->canSetTag);
4709 :
4710 : /* Clear the pending action */
4711 2 : node->mt_merge_pending_not_matched = NULL;
4712 :
4713 : /*
4714 : * If we got a RETURNING result, return it to the caller. We'll
4715 : * continue the work on next call.
4716 : */
4717 2 : if (slot)
4718 2 : return slot;
4719 :
4720 0 : continue; /* continue with the next tuple */
4721 : }
4722 :
4723 : /* Fetch the next row from subplan */
4724 11111960 : context.planSlot = ExecProcNode(subplanstate);
4725 11111662 : context.cpDeletedSlot = NULL;
4726 :
4727 : /* No more tuples to process? */
4728 11111662 : if (TupIsNull(context.planSlot))
4729 : break;
4730 :
4731 : /*
4732 : * When there are multiple result relations, each tuple contains a
4733 : * junk column that gives the OID of the rel from which it came.
4734 : * Extract it and select the correct result relation.
4735 : */
4736 11038834 : if (AttributeNumberIsValid(node->mt_resultOidAttno))
4737 : {
4738 : Datum datum;
4739 : bool isNull;
4740 : Oid resultoid;
4741 :
4742 3417 : datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
4743 : &isNull);
4744 3417 : if (isNull)
4745 : {
4746 : /*
4747 : * For commands other than MERGE, any tuples having InvalidOid
4748 : * for tableoid are errors. For MERGE, we may need to handle
4749 : * them as WHEN NOT MATCHED clauses if any, so do that.
4750 : *
4751 : * Note that we use the node's toplevel resultRelInfo, not any
4752 : * specific partition's.
4753 : */
4754 338 : if (operation == CMD_MERGE)
4755 : {
4756 338 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4757 :
4758 338 : slot = ExecMerge(&context, node->resultRelInfo,
4759 338 : NULL, NULL, node->canSetTag);
4760 :
4761 : /*
4762 : * If we got a RETURNING result, return it to the caller.
4763 : * We'll continue the work on next call.
4764 : */
4765 330 : if (slot)
4766 25 : return slot;
4767 :
4768 305 : continue; /* continue with the next tuple */
4769 : }
4770 :
4771 0 : elog(ERROR, "tableoid is NULL");
4772 : }
4773 3079 : resultoid = DatumGetObjectId(datum);
4774 :
4775 : /* If it's not the same as last time, we need to locate the rel */
4776 3079 : if (resultoid != node->mt_lastResultOid)
4777 2127 : resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
4778 : false, true);
4779 : }
4780 :
4781 : /*
4782 : * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
4783 : * here is compute the RETURNING expressions.
4784 : */
4785 11038496 : if (resultRelInfo->ri_usesFdwDirectModify)
4786 : {
4787 : Assert(resultRelInfo->ri_projectReturning);
4788 :
4789 : /*
4790 : * A scan slot containing the data that was actually inserted,
4791 : * updated or deleted has already been made available to
4792 : * ExecProcessReturning by IterateDirectModify, so no need to
4793 : * provide it here. The individual old and new slots are not
4794 : * needed, since direct-modify is disabled if the RETURNING list
4795 : * refers to OLD/NEW values.
4796 : */
4797 : Assert((resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD) == 0 &&
4798 : (resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW) == 0);
4799 :
4800 347 : slot = ExecProcessReturning(&context, resultRelInfo,
4801 : operation == CMD_DELETE,
4802 : NULL, NULL, context.planSlot);
4803 :
4804 347 : return slot;
4805 : }
4806 :
4807 11038149 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4808 11038149 : slot = context.planSlot;
4809 :
4810 11038149 : tupleid = NULL;
4811 11038149 : oldtuple = NULL;
4812 :
4813 : /*
4814 : * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
4815 : * to be updated/deleted/merged. For a heap relation, that's a TID;
4816 : * otherwise we may have a wholerow junk attr that carries the old
4817 : * tuple in toto. Keep this in step with the part of
4818 : * ExecInitModifyTable that sets up ri_RowIdAttNo.
4819 : */
4820 11038149 : if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4821 : operation == CMD_MERGE)
4822 : {
4823 : char relkind;
4824 : Datum datum;
4825 : bool isNull;
4826 :
4827 3261074 : relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4828 3261074 : if (relkind == RELKIND_RELATION ||
4829 338 : relkind == RELKIND_MATVIEW ||
4830 : relkind == RELKIND_PARTITIONED_TABLE)
4831 : {
4832 : /*
4833 : * ri_RowIdAttNo refers to a ctid attribute. See the comment
4834 : * in ExecInitModifyTable().
4835 : */
4836 : Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo) ||
4837 : relkind == RELKIND_PARTITIONED_TABLE);
4838 3260740 : datum = ExecGetJunkAttribute(slot,
4839 3260740 : resultRelInfo->ri_RowIdAttNo,
4840 : &isNull);
4841 :
4842 : /*
4843 : * For commands other than MERGE, any tuples having a null row
4844 : * identifier are errors. For MERGE, we may need to handle
4845 : * them as WHEN NOT MATCHED clauses if any, so do that.
4846 : *
4847 : * Note that we use the node's toplevel resultRelInfo, not any
4848 : * specific partition's.
4849 : */
4850 3260740 : if (isNull)
4851 : {
4852 1421 : if (operation == CMD_MERGE)
4853 : {
4854 1421 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4855 :
4856 1421 : slot = ExecMerge(&context, node->resultRelInfo,
4857 1421 : NULL, NULL, node->canSetTag);
4858 :
4859 : /*
4860 : * If we got a RETURNING result, return it to the
4861 : * caller. We'll continue the work on next call.
4862 : */
4863 1394 : if (slot)
4864 88 : return slot;
4865 :
4866 1334 : continue; /* continue with the next tuple */
4867 : }
4868 :
4869 0 : elog(ERROR, "ctid is NULL");
4870 : }
4871 :
4872 3259319 : tupleid = (ItemPointer) DatumGetPointer(datum);
4873 3259319 : tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
4874 3259319 : tupleid = &tuple_ctid;
4875 : }
4876 :
4877 : /*
4878 : * Use the wholerow attribute, when available, to reconstruct the
4879 : * old relation tuple. The old tuple serves one or both of two
4880 : * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
4881 : * provides values for any unchanged columns for the NEW tuple of
4882 : * an UPDATE, because the subplan does not produce all the columns
4883 : * of the target table.
4884 : *
4885 : * Note that the wholerow attribute does not carry system columns,
4886 : * so foreign table triggers miss seeing those, except that we
4887 : * know enough here to set t_tableOid. Quite separately from
4888 : * this, the FDW may fetch its own junk attrs to identify the row.
4889 : *
4890 : * Other relevant relkinds, currently limited to views, always
4891 : * have a wholerow attribute.
4892 : */
4893 334 : else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4894 : {
4895 319 : datum = ExecGetJunkAttribute(slot,
4896 319 : resultRelInfo->ri_RowIdAttNo,
4897 : &isNull);
4898 :
4899 : /*
4900 : * For commands other than MERGE, any tuples having a null row
4901 : * identifier are errors. For MERGE, we may need to handle
4902 : * them as WHEN NOT MATCHED clauses if any, so do that.
4903 : *
4904 : * Note that we use the node's toplevel resultRelInfo, not any
4905 : * specific partition's.
4906 : */
4907 319 : if (isNull)
4908 : {
4909 32 : if (operation == CMD_MERGE)
4910 : {
4911 32 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4912 :
4913 32 : slot = ExecMerge(&context, node->resultRelInfo,
4914 32 : NULL, NULL, node->canSetTag);
4915 :
4916 : /*
4917 : * If we got a RETURNING result, return it to the
4918 : * caller. We'll continue the work on next call.
4919 : */
4920 28 : if (slot)
4921 8 : return slot;
4922 :
4923 20 : continue; /* continue with the next tuple */
4924 : }
4925 :
4926 0 : elog(ERROR, "wholerow is NULL");
4927 : }
4928 :
4929 287 : oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
4930 287 : oldtupdata.t_len =
4931 287 : HeapTupleHeaderGetDatumLength(oldtupdata.t_data);
4932 287 : ItemPointerSetInvalid(&(oldtupdata.t_self));
4933 : /* Historically, view triggers see invalid t_tableOid. */
4934 287 : oldtupdata.t_tableOid =
4935 287 : (relkind == RELKIND_VIEW) ? InvalidOid :
4936 105 : RelationGetRelid(resultRelInfo->ri_RelationDesc);
4937 :
4938 287 : oldtuple = &oldtupdata;
4939 : }
4940 : else
4941 : {
4942 : /* Only foreign tables are allowed to omit a row-ID attr */
4943 : Assert(relkind == RELKIND_FOREIGN_TABLE);
4944 : }
4945 : }
4946 :
4947 11036696 : switch (operation)
4948 : {
4949 7777075 : case CMD_INSERT:
4950 : /* Initialize projection info if first time for this table */
4951 7777075 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4952 56090 : ExecInitInsertProjection(node, resultRelInfo);
4953 7777075 : slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
4954 7777075 : slot = ExecInsert(&context, resultRelInfo, slot,
4955 7777075 : node->canSetTag, NULL, NULL);
4956 7775637 : break;
4957 :
4958 2220465 : case CMD_UPDATE:
4959 2220465 : tuplock = false;
4960 :
4961 : /* Initialize projection info if first time for this table */
4962 2220465 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4963 8719 : ExecInitUpdateProjection(node, resultRelInfo);
4964 :
4965 : /*
4966 : * Make the new tuple by combining plan's output tuple with
4967 : * the old tuple being updated.
4968 : */
4969 2220465 : oldSlot = resultRelInfo->ri_oldTupleSlot;
4970 2220465 : if (oldtuple != NULL)
4971 : {
4972 : Assert(!resultRelInfo->ri_needLockTagTuple);
4973 : /* Use the wholerow junk attr as the old tuple. */
4974 179 : ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
4975 : }
4976 : else
4977 : {
4978 : /* Fetch the most recent version of old tuple. */
4979 2220286 : Relation relation = resultRelInfo->ri_RelationDesc;
4980 :
4981 2220286 : if (resultRelInfo->ri_needLockTagTuple)
4982 : {
4983 15891 : LockTuple(relation, tupleid, InplaceUpdateTupleLock);
4984 15891 : tuplock = true;
4985 : }
4986 2220286 : if (!table_tuple_fetch_row_version(relation, tupleid,
4987 : SnapshotAny,
4988 : oldSlot))
4989 0 : elog(ERROR, "failed to fetch tuple being updated");
4990 : }
4991 2220465 : slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
4992 : oldSlot);
4993 :
4994 : /* Now apply the update. */
4995 2220465 : slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
4996 2220465 : oldSlot, slot, node->canSetTag);
4997 2220102 : if (tuplock)
4998 15891 : UnlockTuple(resultRelInfo->ri_RelationDesc, tupleid,
4999 : InplaceUpdateTupleLock);
5000 2220102 : break;
5001 :
5002 1030595 : case CMD_DELETE:
5003 1030595 : slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
5004 1030595 : true, false, node->canSetTag, NULL, NULL, NULL);
5005 1030524 : break;
5006 :
5007 8561 : case CMD_MERGE:
5008 8561 : slot = ExecMerge(&context, resultRelInfo, tupleid, oldtuple,
5009 8561 : node->canSetTag);
5010 8499 : break;
5011 :
5012 0 : default:
5013 0 : elog(ERROR, "unknown operation");
5014 : break;
5015 : }
5016 :
5017 : /*
5018 : * If we got a RETURNING result, return it to caller. We'll continue
5019 : * the work on next call.
5020 : */
5021 11034762 : if (slot)
5022 5410 : return slot;
5023 : }
5024 :
5025 : /*
5026 : * Insert remaining tuples for batch insert.
5027 : */
5028 72828 : if (estate->es_insert_pending_result_relations != NIL)
5029 13 : ExecPendingInserts(estate);
5030 :
5031 : /*
5032 : * We're done, but fire AFTER STATEMENT triggers before exiting.
5033 : */
5034 72827 : fireASTriggers(node);
5035 :
5036 72827 : node->mt_done = true;
5037 :
5038 72827 : return NULL;
5039 : }
5040 :
5041 : /*
5042 : * ExecLookupResultRelByOid
5043 : * If the table with given OID is among the result relations to be
5044 : * updated by the given ModifyTable node, return its ResultRelInfo.
5045 : *
5046 : * If not found, return NULL if missing_ok, else raise error.
5047 : *
5048 : * If update_cache is true, then upon successful lookup, update the node's
5049 : * one-element cache. ONLY ExecModifyTable may pass true for this.
5050 : */
5051 : ResultRelInfo *
5052 8315 : ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid,
5053 : bool missing_ok, bool update_cache)
5054 : {
5055 8315 : if (node->mt_resultOidHash)
5056 : {
5057 : /* Use the pre-built hash table to locate the rel */
5058 : MTTargetRelLookup *mtlookup;
5059 :
5060 : mtlookup = (MTTargetRelLookup *)
5061 0 : hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
5062 0 : if (mtlookup)
5063 : {
5064 0 : if (update_cache)
5065 : {
5066 0 : node->mt_lastResultOid = resultoid;
5067 0 : node->mt_lastResultIndex = mtlookup->relationIndex;
5068 : }
5069 0 : return node->resultRelInfo + mtlookup->relationIndex;
5070 : }
5071 : }
5072 : else
5073 : {
5074 : /* With few target rels, just search the ResultRelInfo array */
5075 15785 : for (int ndx = 0; ndx < node->mt_nrels; ndx++)
5076 : {
5077 10001 : ResultRelInfo *rInfo = node->resultRelInfo + ndx;
5078 :
5079 10001 : if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
5080 : {
5081 2531 : if (update_cache)
5082 : {
5083 2127 : node->mt_lastResultOid = resultoid;
5084 2127 : node->mt_lastResultIndex = ndx;
5085 : }
5086 2531 : return rInfo;
5087 : }
5088 : }
5089 : }
5090 :
5091 5784 : if (!missing_ok)
5092 0 : elog(ERROR, "incorrect result relation OID %u", resultoid);
5093 5784 : return NULL;
5094 : }
5095 :
5096 : /* ----------------------------------------------------------------
5097 : * ExecInitModifyTable
5098 : * ----------------------------------------------------------------
5099 : */
5100 : ModifyTableState *
5101 76031 : ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
5102 : {
5103 : ModifyTableState *mtstate;
5104 76031 : Plan *subplan = outerPlan(node);
5105 76031 : CmdType operation = node->operation;
5106 76031 : int total_nrels = list_length(node->resultRelations);
5107 : int nrels;
5108 76031 : List *resultRelations = NIL;
5109 76031 : List *withCheckOptionLists = NIL;
5110 76031 : List *returningLists = NIL;
5111 76031 : List *updateColnosLists = NIL;
5112 76031 : List *mergeActionLists = NIL;
5113 76031 : List *mergeJoinConditions = NIL;
5114 : ResultRelInfo *resultRelInfo;
5115 : List *arowmarks;
5116 : ListCell *l;
5117 : int i;
5118 : Relation rel;
5119 :
5120 : /* check for unsupported flags */
5121 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
5122 :
5123 : /*
5124 : * Only consider unpruned relations for initializing their ResultRelInfo
5125 : * struct and other fields such as withCheckOptions, etc.
5126 : *
5127 : * Note: We must avoid pruning every result relation. This is important
5128 : * for MERGE, since even if every result relation is pruned from the
5129 : * subplan, there might still be NOT MATCHED rows, for which there may be
5130 : * INSERT actions to perform. To allow these actions to be found, at
5131 : * least one result relation must be kept. Also, when inserting into a
5132 : * partitioned table, ExecInitPartitionInfo() needs a ResultRelInfo struct
5133 : * as a reference for building the ResultRelInfo of the target partition.
5134 : * In either case, it doesn't matter which result relation is kept, so we
5135 : * just keep the first one, if all others have been pruned. See also,
5136 : * ExecDoInitialPruning(), which ensures that this first result relation
5137 : * has been locked.
5138 : */
5139 76031 : i = 0;
5140 153725 : foreach(l, node->resultRelations)
5141 : {
5142 77694 : Index rti = lfirst_int(l);
5143 : bool keep_rel;
5144 :
5145 77694 : keep_rel = bms_is_member(rti, estate->es_unpruned_relids);
5146 77694 : if (!keep_rel && i == total_nrels - 1 && resultRelations == NIL)
5147 : {
5148 : /* all result relations pruned; keep the first one */
5149 32 : keep_rel = true;
5150 32 : rti = linitial_int(node->resultRelations);
5151 32 : i = 0;
5152 : }
5153 :
5154 77694 : if (keep_rel)
5155 : {
5156 77637 : resultRelations = lappend_int(resultRelations, rti);
5157 77637 : if (node->withCheckOptionLists)
5158 : {
5159 1052 : List *withCheckOptions = list_nth_node(List,
5160 : node->withCheckOptionLists,
5161 : i);
5162 :
5163 1052 : withCheckOptionLists = lappend(withCheckOptionLists, withCheckOptions);
5164 : }
5165 77637 : if (node->returningLists)
5166 : {
5167 4080 : List *returningList = list_nth_node(List,
5168 : node->returningLists,
5169 : i);
5170 :
5171 4080 : returningLists = lappend(returningLists, returningList);
5172 : }
5173 77637 : if (node->updateColnosLists)
5174 : {
5175 10570 : List *updateColnosList = list_nth(node->updateColnosLists, i);
5176 :
5177 10570 : updateColnosLists = lappend(updateColnosLists, updateColnosList);
5178 : }
5179 77637 : if (node->mergeActionLists)
5180 : {
5181 1220 : List *mergeActionList = list_nth(node->mergeActionLists, i);
5182 :
5183 1220 : mergeActionLists = lappend(mergeActionLists, mergeActionList);
5184 : }
5185 77637 : if (node->mergeJoinConditions)
5186 : {
5187 1220 : List *mergeJoinCondition = list_nth(node->mergeJoinConditions, i);
5188 :
5189 1220 : mergeJoinConditions = lappend(mergeJoinConditions, mergeJoinCondition);
5190 : }
5191 : }
5192 77694 : i++;
5193 : }
5194 76031 : nrels = list_length(resultRelations);
5195 : Assert(nrels > 0);
5196 :
5197 : /*
5198 : * create state structure
5199 : */
5200 76031 : mtstate = makeNode(ModifyTableState);
5201 76031 : mtstate->ps.plan = (Plan *) node;
5202 76031 : mtstate->ps.state = estate;
5203 76031 : mtstate->ps.ExecProcNode = ExecModifyTable;
5204 :
5205 76031 : mtstate->operation = operation;
5206 76031 : mtstate->canSetTag = node->canSetTag;
5207 76031 : mtstate->mt_done = false;
5208 :
5209 76031 : mtstate->mt_nrels = nrels;
5210 76031 : mtstate->resultRelInfo = palloc_array(ResultRelInfo, nrels);
5211 :
5212 76031 : mtstate->mt_merge_pending_not_matched = NULL;
5213 76031 : mtstate->mt_merge_inserted = 0;
5214 76031 : mtstate->mt_merge_updated = 0;
5215 76031 : mtstate->mt_merge_deleted = 0;
5216 76031 : mtstate->mt_updateColnosLists = updateColnosLists;
5217 76031 : mtstate->mt_mergeActionLists = mergeActionLists;
5218 76031 : mtstate->mt_mergeJoinConditions = mergeJoinConditions;
5219 :
5220 : /*----------
5221 : * Resolve the target relation. This is the same as:
5222 : *
5223 : * - the relation for which we will fire FOR STATEMENT triggers,
5224 : * - the relation into whose tuple format all captured transition tuples
5225 : * must be converted, and
5226 : * - the root partitioned table used for tuple routing.
5227 : *
5228 : * If it's a partitioned or inherited table, the root partition or
5229 : * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
5230 : * given explicitly in node->rootRelation. Otherwise, the target relation
5231 : * is the sole relation in the node->resultRelations list and, since it can
5232 : * never be pruned, also in the resultRelations list constructed above.
5233 : *----------
5234 : */
5235 76031 : if (node->rootRelation > 0)
5236 : {
5237 : Assert(bms_is_member(node->rootRelation, estate->es_unpruned_relids));
5238 1919 : mtstate->rootResultRelInfo = makeNode(ResultRelInfo);
5239 1919 : ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
5240 : node->rootRelation);
5241 : }
5242 : else
5243 : {
5244 : Assert(list_length(node->resultRelations) == 1);
5245 : Assert(list_length(resultRelations) == 1);
5246 74112 : mtstate->rootResultRelInfo = mtstate->resultRelInfo;
5247 74112 : ExecInitResultRelation(estate, mtstate->resultRelInfo,
5248 74112 : linitial_int(resultRelations));
5249 : }
5250 :
5251 : /* set up epqstate with dummy subplan data for the moment */
5252 76031 : EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
5253 : node->epqParam, resultRelations);
5254 76031 : mtstate->fireBSTriggers = true;
5255 :
5256 : /*
5257 : * Build state for collecting transition tuples. This requires having a
5258 : * valid trigger query context, so skip it in explain-only mode.
5259 : */
5260 76031 : if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
5261 75368 : ExecSetupTransitionCaptureState(mtstate, estate);
5262 :
5263 : /*
5264 : * Open all the result relations and initialize the ResultRelInfo structs.
5265 : * (But root relation was initialized above, if it's part of the array.)
5266 : * We must do this before initializing the subplan, because direct-modify
5267 : * FDWs expect their ResultRelInfos to be available.
5268 : */
5269 76031 : resultRelInfo = mtstate->resultRelInfo;
5270 76031 : i = 0;
5271 153440 : foreach(l, resultRelations)
5272 : {
5273 77633 : Index resultRelation = lfirst_int(l);
5274 77633 : List *mergeActions = NIL;
5275 :
5276 77633 : if (mergeActionLists)
5277 1220 : mergeActions = list_nth(mergeActionLists, i);
5278 :
5279 77633 : if (resultRelInfo != mtstate->rootResultRelInfo)
5280 : {
5281 3521 : ExecInitResultRelation(estate, resultRelInfo, resultRelation);
5282 :
5283 : /*
5284 : * For child result relations, store the root result relation
5285 : * pointer. We do so for the convenience of places that want to
5286 : * look at the query's original target relation but don't have the
5287 : * mtstate handy.
5288 : */
5289 3521 : resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
5290 : }
5291 :
5292 : /* Initialize the usesFdwDirectModify flag */
5293 77633 : resultRelInfo->ri_usesFdwDirectModify =
5294 77633 : bms_is_member(i, node->fdwDirectModifyPlans);
5295 :
5296 : /*
5297 : * Verify result relation is a valid target for the current operation
5298 : */
5299 77633 : CheckValidResultRel(resultRelInfo, operation, node->onConflictAction,
5300 : mergeActions);
5301 :
5302 77409 : resultRelInfo++;
5303 77409 : i++;
5304 : }
5305 :
5306 : /*
5307 : * Now we may initialize the subplan.
5308 : */
5309 75807 : outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
5310 :
5311 : /*
5312 : * Do additional per-result-relation initialization.
5313 : */
5314 153194 : for (i = 0; i < nrels; i++)
5315 : {
5316 77387 : resultRelInfo = &mtstate->resultRelInfo[i];
5317 :
5318 : /* Let FDWs init themselves for foreign-table result rels */
5319 77387 : if (!resultRelInfo->ri_usesFdwDirectModify &&
5320 77283 : resultRelInfo->ri_FdwRoutine != NULL &&
5321 170 : resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
5322 : {
5323 170 : List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
5324 :
5325 170 : resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
5326 : resultRelInfo,
5327 : fdw_private,
5328 : i,
5329 : eflags);
5330 : }
5331 :
5332 : /*
5333 : * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
5334 : * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
5335 : * tables, the FDW might have created additional junk attr(s), but
5336 : * those are no concern of ours.
5337 : */
5338 77387 : if (operation == CMD_UPDATE || operation == CMD_DELETE ||
5339 : operation == CMD_MERGE)
5340 : {
5341 : char relkind;
5342 :
5343 20356 : relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
5344 20356 : if (relkind == RELKIND_RELATION ||
5345 406 : relkind == RELKIND_MATVIEW ||
5346 : relkind == RELKIND_PARTITIONED_TABLE)
5347 : {
5348 19980 : resultRelInfo->ri_RowIdAttNo =
5349 19980 : ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
5350 :
5351 : /*
5352 : * For heap relations, a ctid junk attribute must be present.
5353 : * Partitioned tables should only appear here when all leaf
5354 : * partitions were pruned, in which case no rows can be
5355 : * produced and ctid is not needed.
5356 : */
5357 19980 : if (relkind == RELKIND_PARTITIONED_TABLE)
5358 : Assert(nrels == 1);
5359 19950 : else if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
5360 0 : elog(ERROR, "could not find junk ctid column");
5361 : }
5362 376 : else if (relkind == RELKIND_FOREIGN_TABLE)
5363 : {
5364 : /*
5365 : * We don't support MERGE with foreign tables for now. (It's
5366 : * problematic because the implementation uses CTID.)
5367 : */
5368 : Assert(operation != CMD_MERGE);
5369 :
5370 : /*
5371 : * When there is a row-level trigger, there should be a
5372 : * wholerow attribute. We also require it to be present in
5373 : * UPDATE and MERGE, so we can get the values of unchanged
5374 : * columns.
5375 : */
5376 186 : resultRelInfo->ri_RowIdAttNo =
5377 186 : ExecFindJunkAttributeInTlist(subplan->targetlist,
5378 : "wholerow");
5379 186 : if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
5380 105 : !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
5381 0 : elog(ERROR, "could not find junk wholerow column");
5382 : }
5383 : else
5384 : {
5385 : /* Other valid target relkinds must provide wholerow */
5386 190 : resultRelInfo->ri_RowIdAttNo =
5387 190 : ExecFindJunkAttributeInTlist(subplan->targetlist,
5388 : "wholerow");
5389 190 : if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
5390 0 : elog(ERROR, "could not find junk wholerow column");
5391 : }
5392 : }
5393 : }
5394 :
5395 : /*
5396 : * If this is an inherited update/delete/merge, there will be a junk
5397 : * attribute named "tableoid" present in the subplan's targetlist. It
5398 : * will be used to identify the result relation for a given tuple to be
5399 : * updated/deleted/merged.
5400 : */
5401 75807 : mtstate->mt_resultOidAttno =
5402 75807 : ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
5403 : Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || total_nrels == 1);
5404 75807 : mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
5405 75807 : mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
5406 :
5407 : /* Get the root target relation */
5408 75807 : rel = mtstate->rootResultRelInfo->ri_RelationDesc;
5409 :
5410 : /*
5411 : * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
5412 : * or MERGE might need this too, but only if it actually moves tuples
5413 : * between partitions; in that case setup is done by
5414 : * ExecCrossPartitionUpdate.
5415 : */
5416 75807 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5417 : operation == CMD_INSERT)
5418 3934 : mtstate->mt_partition_tuple_routing =
5419 3934 : ExecSetupPartitionTupleRouting(estate, rel);
5420 :
5421 : /*
5422 : * Initialize any WITH CHECK OPTION constraints if needed.
5423 : */
5424 75807 : resultRelInfo = mtstate->resultRelInfo;
5425 76859 : foreach(l, withCheckOptionLists)
5426 : {
5427 1052 : List *wcoList = (List *) lfirst(l);
5428 1052 : List *wcoExprs = NIL;
5429 : ListCell *ll;
5430 :
5431 3115 : foreach(ll, wcoList)
5432 : {
5433 2063 : WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
5434 2063 : ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
5435 : &mtstate->ps);
5436 :
5437 2063 : wcoExprs = lappend(wcoExprs, wcoExpr);
5438 : }
5439 :
5440 1052 : resultRelInfo->ri_WithCheckOptions = wcoList;
5441 1052 : resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
5442 1052 : resultRelInfo++;
5443 : }
5444 :
5445 : /*
5446 : * Initialize RETURNING projections if needed.
5447 : */
5448 75807 : if (returningLists)
5449 : {
5450 : TupleTableSlot *slot;
5451 : ExprContext *econtext;
5452 :
5453 : /*
5454 : * Initialize result tuple slot and assign its rowtype using the plan
5455 : * node's declared targetlist, which the planner set up to be the same
5456 : * as the first (before runtime pruning) RETURNING list. We assume
5457 : * all the result rels will produce compatible output.
5458 : */
5459 3865 : ExecInitResultTupleSlotTL(&mtstate->ps, &TTSOpsVirtual);
5460 3865 : slot = mtstate->ps.ps_ResultTupleSlot;
5461 :
5462 : /* Need an econtext too */
5463 3865 : if (mtstate->ps.ps_ExprContext == NULL)
5464 3865 : ExecAssignExprContext(estate, &mtstate->ps);
5465 3865 : econtext = mtstate->ps.ps_ExprContext;
5466 :
5467 : /*
5468 : * Build a projection for each result rel.
5469 : */
5470 3865 : resultRelInfo = mtstate->resultRelInfo;
5471 7945 : foreach(l, returningLists)
5472 : {
5473 4080 : List *rlist = (List *) lfirst(l);
5474 :
5475 4080 : resultRelInfo->ri_returningList = rlist;
5476 4080 : resultRelInfo->ri_projectReturning =
5477 4080 : ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
5478 4080 : resultRelInfo->ri_RelationDesc->rd_att);
5479 4080 : resultRelInfo++;
5480 : }
5481 : }
5482 : else
5483 : {
5484 : /*
5485 : * We still must construct a dummy result tuple type, because InitPlan
5486 : * expects one (maybe should change that?).
5487 : */
5488 71942 : ExecInitResultTypeTL(&mtstate->ps);
5489 :
5490 71942 : mtstate->ps.ps_ExprContext = NULL;
5491 : }
5492 :
5493 : /* Set the list of arbiter indexes if needed for ON CONFLICT */
5494 75807 : resultRelInfo = mtstate->resultRelInfo;
5495 75807 : if (node->onConflictAction != ONCONFLICT_NONE)
5496 : {
5497 : /* insert may only have one relation, inheritance is not expanded */
5498 : Assert(total_nrels == 1);
5499 1192 : resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
5500 : }
5501 :
5502 : /*
5503 : * For ON CONFLICT DO SELECT/UPDATE, initialize the ON CONFLICT action
5504 : * state.
5505 : */
5506 75807 : if (node->onConflictAction == ONCONFLICT_UPDATE ||
5507 75139 : node->onConflictAction == ONCONFLICT_SELECT)
5508 : {
5509 888 : OnConflictActionState *onconfl = makeNode(OnConflictActionState);
5510 :
5511 : /* already exists if created by RETURNING processing above */
5512 888 : if (mtstate->ps.ps_ExprContext == NULL)
5513 452 : ExecAssignExprContext(estate, &mtstate->ps);
5514 :
5515 : /* action state for DO SELECT/UPDATE */
5516 888 : resultRelInfo->ri_onConflict = onconfl;
5517 :
5518 : /* lock strength for DO SELECT [FOR UPDATE/SHARE] */
5519 888 : onconfl->oc_LockStrength = node->onConflictLockStrength;
5520 :
5521 : /* initialize slot for the existing tuple */
5522 888 : onconfl->oc_Existing =
5523 888 : table_slot_create(resultRelInfo->ri_RelationDesc,
5524 888 : &mtstate->ps.state->es_tupleTable);
5525 :
5526 : /*
5527 : * For ON CONFLICT DO UPDATE, initialize target list and projection.
5528 : */
5529 888 : if (node->onConflictAction == ONCONFLICT_UPDATE)
5530 : {
5531 : ExprContext *econtext;
5532 : TupleDesc relationDesc;
5533 :
5534 668 : econtext = mtstate->ps.ps_ExprContext;
5535 668 : relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
5536 :
5537 : /*
5538 : * Create the tuple slot for the UPDATE SET projection. We want a
5539 : * slot of the table's type here, because the slot will be used to
5540 : * insert into the table, and for RETURNING processing - which may
5541 : * access system attributes.
5542 : */
5543 668 : onconfl->oc_ProjSlot =
5544 668 : table_slot_create(resultRelInfo->ri_RelationDesc,
5545 668 : &mtstate->ps.state->es_tupleTable);
5546 :
5547 : /* build UPDATE SET projection state */
5548 668 : onconfl->oc_ProjInfo =
5549 668 : ExecBuildUpdateProjection(node->onConflictSet,
5550 : true,
5551 : node->onConflictCols,
5552 : relationDesc,
5553 : econtext,
5554 : onconfl->oc_ProjSlot,
5555 : &mtstate->ps);
5556 : }
5557 :
5558 : /* initialize state to evaluate the WHERE clause, if any */
5559 888 : if (node->onConflictWhere)
5560 : {
5561 : ExprState *qualexpr;
5562 :
5563 207 : qualexpr = ExecInitQual((List *) node->onConflictWhere,
5564 : &mtstate->ps);
5565 207 : onconfl->oc_WhereClause = qualexpr;
5566 : }
5567 : }
5568 :
5569 : /*
5570 : * If needed, initialize the target range for FOR PORTION OF.
5571 : */
5572 75807 : if (node->forPortionOf)
5573 : {
5574 : ResultRelInfo *rootRelInfo;
5575 : TupleDesc tupDesc;
5576 : ForPortionOfExpr *forPortionOf;
5577 : Datum targetRange;
5578 : bool isNull;
5579 : ExprContext *econtext;
5580 : ExprState *exprState;
5581 : ForPortionOfState *fpoState;
5582 :
5583 822 : rootRelInfo = mtstate->resultRelInfo;
5584 822 : if (rootRelInfo->ri_RootResultRelInfo)
5585 56 : rootRelInfo = rootRelInfo->ri_RootResultRelInfo;
5586 :
5587 822 : tupDesc = rootRelInfo->ri_RelationDesc->rd_att;
5588 822 : forPortionOf = (ForPortionOfExpr *) node->forPortionOf;
5589 :
5590 : /* Eval the FOR PORTION OF target */
5591 822 : if (mtstate->ps.ps_ExprContext == NULL)
5592 802 : ExecAssignExprContext(estate, &mtstate->ps);
5593 822 : econtext = mtstate->ps.ps_ExprContext;
5594 :
5595 822 : exprState = ExecPrepareExpr((Expr *) forPortionOf->targetRange, estate);
5596 822 : targetRange = ExecEvalExpr(exprState, econtext, &isNull);
5597 :
5598 : /*
5599 : * FOR PORTION OF ... TO ... FROM should never give us a NULL target,
5600 : * but FOR PORTION OF (...) could.
5601 : */
5602 822 : if (isNull)
5603 16 : ereport(ERROR,
5604 : (errmsg("FOR PORTION OF target was null")),
5605 : executor_errposition(estate, forPortionOf->targetLocation));
5606 :
5607 : /* Create state for FOR PORTION OF operation */
5608 :
5609 806 : fpoState = makeNode(ForPortionOfState);
5610 806 : fpoState->fp_rangeName = forPortionOf->range_name;
5611 806 : fpoState->fp_rangeType = forPortionOf->rangeType;
5612 806 : fpoState->fp_rangeAttno = forPortionOf->rangeVar->varattno;
5613 806 : fpoState->fp_targetRange = targetRange;
5614 :
5615 : /* Initialize slot for the existing tuple */
5616 :
5617 806 : fpoState->fp_Existing =
5618 806 : table_slot_create(rootRelInfo->ri_RelationDesc,
5619 806 : &mtstate->ps.state->es_tupleTable);
5620 :
5621 : /* Create the tuple slot for INSERTing the temporal leftovers */
5622 :
5623 806 : fpoState->fp_Leftover =
5624 806 : ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc, &TTSOpsVirtual);
5625 :
5626 806 : rootRelInfo->ri_forPortionOf = fpoState;
5627 :
5628 : /*
5629 : * Make sure the root relation has the FOR PORTION OF clause too. Each
5630 : * partition needs its own TupleTableSlot, since they can have
5631 : * different descriptors, so they'll use the root fpoState to
5632 : * initialize one if necessary.
5633 : */
5634 806 : if (node->rootRelation > 0)
5635 56 : mtstate->rootResultRelInfo->ri_forPortionOf = fpoState;
5636 :
5637 806 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5638 56 : mtstate->mt_partition_tuple_routing == NULL)
5639 : {
5640 : /*
5641 : * We will need tuple routing to insert temporal leftovers. Since
5642 : * we are initializing things before ExecCrossPartitionUpdate
5643 : * runs, we must do everything it needs as well.
5644 : */
5645 56 : Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
5646 : MemoryContext oldcxt;
5647 :
5648 : /* Things built here have to last for the query duration. */
5649 56 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
5650 :
5651 56 : mtstate->mt_partition_tuple_routing =
5652 56 : ExecSetupPartitionTupleRouting(estate, rootRel);
5653 :
5654 : /*
5655 : * Before a partition's tuple can be re-routed, it must first be
5656 : * converted to the root's format, so we'll need a slot for
5657 : * storing such tuples.
5658 : */
5659 : Assert(mtstate->mt_root_tuple_slot == NULL);
5660 56 : mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
5661 :
5662 56 : MemoryContextSwitchTo(oldcxt);
5663 : }
5664 :
5665 : /*
5666 : * Don't free the ExprContext here because the result must last for
5667 : * the whole query.
5668 : */
5669 : }
5670 :
5671 : /*
5672 : * If we have any secondary relations in an UPDATE or DELETE, they need to
5673 : * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
5674 : * EvalPlanQual mechanism needs to be told about them. This also goes for
5675 : * the source relations in a MERGE. Locate the relevant ExecRowMarks.
5676 : */
5677 75791 : arowmarks = NIL;
5678 77678 : foreach(l, node->rowMarks)
5679 : {
5680 1887 : PlanRowMark *rc = lfirst_node(PlanRowMark, l);
5681 1887 : RangeTblEntry *rte = exec_rt_fetch(rc->rti, estate);
5682 : ExecRowMark *erm;
5683 : ExecAuxRowMark *aerm;
5684 :
5685 : /* ignore "parent" rowmarks; they are irrelevant at runtime */
5686 1887 : if (rc->isParent)
5687 94 : continue;
5688 :
5689 : /*
5690 : * Also ignore rowmarks belonging to child tables that have been
5691 : * pruned in ExecDoInitialPruning().
5692 : */
5693 1793 : if (rte->rtekind == RTE_RELATION &&
5694 1418 : !bms_is_member(rc->rti, estate->es_unpruned_relids))
5695 0 : continue;
5696 :
5697 : /* Find ExecRowMark and build ExecAuxRowMark */
5698 1793 : erm = ExecFindRowMark(estate, rc->rti, false);
5699 1793 : aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
5700 1793 : arowmarks = lappend(arowmarks, aerm);
5701 : }
5702 :
5703 : /* For a MERGE command, initialize its state */
5704 75791 : if (mtstate->operation == CMD_MERGE)
5705 1056 : ExecInitMerge(mtstate, estate);
5706 :
5707 75791 : EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
5708 :
5709 : /*
5710 : * If there are a lot of result relations, use a hash table to speed the
5711 : * lookups. If there are not a lot, a simple linear search is faster.
5712 : *
5713 : * It's not clear where the threshold is, but try 64 for starters. In a
5714 : * debugging build, use a small threshold so that we get some test
5715 : * coverage of both code paths.
5716 : */
5717 : #ifdef USE_ASSERT_CHECKING
5718 : #define MT_NRELS_HASH 4
5719 : #else
5720 : #define MT_NRELS_HASH 64
5721 : #endif
5722 75791 : if (nrels >= MT_NRELS_HASH)
5723 : {
5724 : HASHCTL hash_ctl;
5725 :
5726 0 : hash_ctl.keysize = sizeof(Oid);
5727 0 : hash_ctl.entrysize = sizeof(MTTargetRelLookup);
5728 0 : hash_ctl.hcxt = CurrentMemoryContext;
5729 0 : mtstate->mt_resultOidHash =
5730 0 : hash_create("ModifyTable target hash",
5731 : nrels, &hash_ctl,
5732 : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
5733 0 : for (i = 0; i < nrels; i++)
5734 : {
5735 : Oid hashkey;
5736 : MTTargetRelLookup *mtlookup;
5737 : bool found;
5738 :
5739 0 : resultRelInfo = &mtstate->resultRelInfo[i];
5740 0 : hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
5741 : mtlookup = (MTTargetRelLookup *)
5742 0 : hash_search(mtstate->mt_resultOidHash, &hashkey,
5743 : HASH_ENTER, &found);
5744 : Assert(!found);
5745 0 : mtlookup->relationIndex = i;
5746 : }
5747 : }
5748 : else
5749 75791 : mtstate->mt_resultOidHash = NULL;
5750 :
5751 : /*
5752 : * Determine if the FDW supports batch insert and determine the batch size
5753 : * (a FDW may support batching, but it may be disabled for the
5754 : * server/table).
5755 : *
5756 : * We only do this for INSERT, so that for UPDATE/DELETE the batch size
5757 : * remains set to 0.
5758 : */
5759 75791 : if (operation == CMD_INSERT)
5760 : {
5761 : /* insert may only have one relation, inheritance is not expanded */
5762 : Assert(total_nrels == 1);
5763 57031 : resultRelInfo = mtstate->resultRelInfo;
5764 57031 : if (!resultRelInfo->ri_usesFdwDirectModify &&
5765 57031 : resultRelInfo->ri_FdwRoutine != NULL &&
5766 88 : resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
5767 88 : resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
5768 : {
5769 88 : resultRelInfo->ri_BatchSize =
5770 88 : resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
5771 88 : Assert(resultRelInfo->ri_BatchSize >= 1);
5772 : }
5773 : else
5774 56943 : resultRelInfo->ri_BatchSize = 1;
5775 : }
5776 :
5777 : /*
5778 : * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
5779 : * to estate->es_auxmodifytables so that it will be run to completion by
5780 : * ExecPostprocessPlan. (It'd actually work fine to add the primary
5781 : * ModifyTable node too, but there's no need.) Note the use of lcons not
5782 : * lappend: we need later-initialized ModifyTable nodes to be shut down
5783 : * before earlier ones. This ensures that we don't throw away RETURNING
5784 : * rows that need to be seen by a later CTE subplan.
5785 : */
5786 75791 : if (!mtstate->canSetTag)
5787 699 : estate->es_auxmodifytables = lcons(mtstate,
5788 : estate->es_auxmodifytables);
5789 :
5790 75791 : return mtstate;
5791 : }
5792 :
5793 : /* ----------------------------------------------------------------
5794 : * ExecEndModifyTable
5795 : *
5796 : * Shuts down the plan.
5797 : *
5798 : * Returns nothing of interest.
5799 : * ----------------------------------------------------------------
5800 : */
5801 : void
5802 72708 : ExecEndModifyTable(ModifyTableState *node)
5803 : {
5804 : int i;
5805 :
5806 : /*
5807 : * Allow any FDWs to shut down
5808 : */
5809 146796 : for (i = 0; i < node->mt_nrels; i++)
5810 : {
5811 : int j;
5812 74088 : ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
5813 :
5814 74088 : if (!resultRelInfo->ri_usesFdwDirectModify &&
5815 73992 : resultRelInfo->ri_FdwRoutine != NULL &&
5816 156 : resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
5817 156 : resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
5818 : resultRelInfo);
5819 :
5820 : /*
5821 : * Cleanup the initialized batch slots. This only matters for FDWs
5822 : * with batching, but the other cases will have ri_NumSlotsInitialized
5823 : * == 0.
5824 : */
5825 74116 : for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
5826 : {
5827 28 : ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
5828 28 : ExecDropSingleTupleTableSlot(resultRelInfo->ri_PlanSlots[j]);
5829 : }
5830 : }
5831 :
5832 : /*
5833 : * Close all the partitioned tables, leaf partitions, and their indices
5834 : * and release the slot used for tuple routing, if set.
5835 : */
5836 72708 : if (node->mt_partition_tuple_routing)
5837 : {
5838 4002 : ExecCleanupTupleRouting(node, node->mt_partition_tuple_routing);
5839 :
5840 4002 : if (node->mt_root_tuple_slot)
5841 481 : ExecDropSingleTupleTableSlot(node->mt_root_tuple_slot);
5842 : }
5843 :
5844 : /*
5845 : * Terminate EPQ execution if active
5846 : */
5847 72708 : EvalPlanQualEnd(&node->mt_epqstate);
5848 :
5849 : /*
5850 : * shut down subplan
5851 : */
5852 72708 : ExecEndNode(outerPlanState(node));
5853 72708 : }
5854 :
5855 : void
5856 0 : ExecReScanModifyTable(ModifyTableState *node)
5857 : {
5858 : /*
5859 : * Currently, we don't need to support rescan on ModifyTable nodes. The
5860 : * semantics of that would be a bit debatable anyway.
5861 : */
5862 0 : elog(ERROR, "ExecReScanModifyTable is not implemented");
5863 : }
|