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