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