Age Owner Branch data TLA 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
6132 tgl@sss.pgh.pa.us 222 :CBC 54084 : ExecCheckPlanOutput(Relation resultRel, List *targetList)
223 : : {
224 : 54084 : TupleDesc resultDesc = RelationGetDescr(resultRel);
225 : 54084 : int attno = 0;
226 : : ListCell *lc;
227 : :
228 [ + + + + : 168689 : foreach(lc, targetList)
+ + ]
229 : : {
230 : 114605 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
231 : : Form_pg_attribute attr;
232 : :
1942 233 [ - + ]: 114605 : Assert(!tle->resjunk); /* caller removed junk items already */
234 : :
6132 235 [ - + ]: 114605 : if (attno >= resultDesc->natts)
6132 tgl@sss.pgh.pa.us 236 [ # # ]:UBC 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.")));
3261 andres@anarazel.de 240 :CBC 114605 : attr = TupleDescAttr(resultDesc, attno);
241 : 114605 : attno++;
242 : :
243 : : /*
244 : : * Special cases here should match planner's expand_insert_targetlist.
245 : : */
466 tgl@sss.pgh.pa.us 246 [ + + ]: 114605 : 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)
6132 tgl@sss.pgh.pa.us 255 [ # # ]:UBC 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 : : }
466 tgl@sss.pgh.pa.us 261 [ + + ]:CBC 114162 : 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 : : */
6132 270 [ + - ]: 925 : if (!IsA(tle->expr, Const) ||
271 [ - + ]: 925 : !((Const *) tle->expr)->constisnull)
6132 tgl@sss.pgh.pa.us 272 [ # # ]:UBC 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 */
466 tgl@sss.pgh.pa.us 281 [ - + ]:CBC 113237 : if (exprType((Node *) tle->expr) != attr->atttypid)
466 tgl@sss.pgh.pa.us 282 [ # # ]:UBC 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 : : }
6132 tgl@sss.pgh.pa.us 291 [ - + ]:CBC 54084 : if (attno != resultDesc->natts)
6132 tgl@sss.pgh.pa.us 292 [ # # ]:UBC 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.")));
6132 tgl@sss.pgh.pa.us 296 :CBC 54084 : }
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 *
555 dean.a.rasheed@gmail 318 : 5688 : ExecProcessReturning(ModifyTableContext *context,
319 : : ResultRelInfo *resultRelInfo,
320 : : bool isDelete,
321 : : TupleTableSlot *oldSlot,
322 : : TupleTableSlot *newSlot,
323 : : TupleTableSlot *planSlot)
324 : : {
325 : 5688 : EState *estate = context->estate;
3781 rhaas@postgresql.org 326 : 5688 : ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
6132 tgl@sss.pgh.pa.us 327 : 5688 : ExprContext *econtext = projectReturning->pi_exprContext;
328 : :
329 : : /* Make tuple and any needed join variables available to ExecProject */
163 dean.a.rasheed@gmail 330 [ + + ]: 5688 : if (isDelete)
331 : : {
332 : : /* return old tuple by default */
333 [ + + ]: 884 : if (oldSlot)
334 : 765 : econtext->ecxt_scantuple = oldSlot;
335 : : }
336 : : else
337 : : {
338 : : /* return new tuple by default */
339 [ + + ]: 4804 : if (newSlot)
340 : 4575 : econtext->ecxt_scantuple = newSlot;
341 : : }
6132 tgl@sss.pgh.pa.us 342 : 5688 : econtext->ecxt_outertuple = planSlot;
343 : :
344 : : /* Make old/new tuples available to ExecProject, if required */
555 dean.a.rasheed@gmail 345 [ + + ]: 5688 : if (oldSlot)
346 : 2591 : econtext->ecxt_oldtuple = oldSlot;
347 [ + + ]: 3097 : else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD)
348 : 146 : econtext->ecxt_oldtuple = ExecGetAllNullSlot(estate, resultRelInfo);
349 : : else
350 : 2951 : econtext->ecxt_oldtuple = NULL; /* No references to OLD columns */
351 : :
352 [ + + ]: 5688 : if (newSlot)
353 : 4575 : econtext->ecxt_newtuple = newSlot;
354 [ + + ]: 1113 : else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW)
355 : 103 : econtext->ecxt_newtuple = ExecGetAllNullSlot(estate, resultRelInfo);
356 : : else
357 : 1010 : 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 [ + + ]: 5688 : if (oldSlot == NULL)
365 : 3097 : projectReturning->pi_state.flags |= EEO_FLAG_OLD_IS_NULL;
366 : : else
367 : 2591 : projectReturning->pi_state.flags &= ~EEO_FLAG_OLD_IS_NULL;
368 : :
369 [ + + ]: 5688 : if (newSlot == NULL)
370 : 1113 : projectReturning->pi_state.flags |= EEO_FLAG_NEW_IS_NULL;
371 : : else
372 : 4575 : projectReturning->pi_state.flags &= ~EEO_FLAG_NEW_IS_NULL;
373 : :
374 : : /* Compute the RETURNING expressions */
3474 andres@anarazel.de 375 : 5688 : 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
2681 387 : 2956 : ExecCheckTupleVisible(EState *estate,
388 : : Relation rel,
389 : : TupleTableSlot *slot)
390 : : {
4096 391 [ + + ]: 2956 : if (!IsolationUsesXactSnapshot())
392 : 2914 : return;
393 : :
2681 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 [ - + ]: 30 : 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))
3562 tgl@sss.pgh.pa.us 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
4096 andres@anarazel.de 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 : :
2620 432 [ - + ]: 34 : if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
4096 andres@anarazel.de 433 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
2681 andres@anarazel.de 434 :CBC 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
533 peter@eisentraut.org 454 : 32110 : ExecInitGenerated(ResultRelInfo *resultRelInfo,
455 : : EState *estate,
456 : : CmdType cmdtype)
457 : : {
2674 458 : 32110 : Relation rel = resultRelInfo->ri_RelationDesc;
459 : 32110 : TupleDesc tupdesc = RelationGetDescr(rel);
460 : 32110 : 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 */
533 467 [ + + + + : 32110 : if (!(tupdesc->constr && (tupdesc->constr->has_generated_stored || tupdesc->constr->has_generated_virtual)))
+ + ]
1297 tgl@sss.pgh.pa.us 468 : 31193 : 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 [ + + ]: 917 : if (cmdtype == CMD_UPDATE &&
477 [ + + - + ]: 220 : !(rel->trigdesc && rel->trigdesc->trig_update_before_row))
478 : 192 : updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
479 : : else
480 : 725 : 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 : 917 : oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
487 : :
1237 488 : 917 : ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
489 : 917 : ri_NumGeneratedNeeded = 0;
490 : :
1297 491 [ + + ]: 3878 : for (int i = 0; i < natts; i++)
492 : : {
533 peter@eisentraut.org 493 : 2965 : char attgenerated = TupleDescAttr(tupdesc, i)->attgenerated;
494 : :
495 [ + + ]: 2965 : if (attgenerated)
496 : : {
497 : : Expr *expr;
498 : :
499 : : /* Fetch the GENERATED AS expression tree */
1297 tgl@sss.pgh.pa.us 500 : 988 : expr = (Expr *) build_column_default(rel, i + 1);
501 [ - + ]: 988 : if (expr == NULL)
1297 tgl@sss.pgh.pa.us 502 [ # # ]:UBC 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 : : */
1297 tgl@sss.pgh.pa.us 509 [ + + ]:CBC 988 : if (updatedCols)
510 : : {
511 : 208 : Bitmapset *attrs_used = NULL;
512 : :
513 : 208 : pull_varattnos((Node *) expr, 1, &attrs_used);
514 : :
515 [ + + ]: 208 : if (!bms_overlap(updatedCols, attrs_used))
516 : 21 : continue; /* need not update this column */
517 : : }
518 : :
519 : : /* No luck, so prepare the expression for execution */
533 peter@eisentraut.org 520 [ + + ]: 967 : if (attgenerated == ATTRIBUTE_GENERATED_STORED)
521 : : {
522 : 875 : ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
523 : 871 : ri_NumGeneratedNeeded++;
524 : : }
525 : :
526 : : /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
1237 tgl@sss.pgh.pa.us 527 [ + + ]: 963 : if (cmdtype == CMD_UPDATE)
528 : 219 : resultRelInfo->ri_extraUpdatedCols =
529 : 219 : bms_add_member(resultRelInfo->ri_extraUpdatedCols,
530 : : i + 1 - FirstLowInvalidHeapAttributeNumber);
531 : : }
532 : : }
533 : :
533 peter@eisentraut.org 534 [ + + ]: 913 : if (ri_NumGeneratedNeeded == 0)
535 : : {
536 : : /* didn't need it after all */
537 : 53 : pfree(ri_GeneratedExprs);
538 : 53 : ri_GeneratedExprs = NULL;
539 : : }
540 : :
541 : : /* Save in appropriate set of fields */
1237 tgl@sss.pgh.pa.us 542 [ + + ]: 913 : if (cmdtype == CMD_UPDATE)
543 : : {
544 : : /* Don't call twice */
545 [ - + ]: 220 : Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
546 : :
547 : 220 : resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
548 : 220 : resultRelInfo->ri_NumGeneratedNeededU = ri_NumGeneratedNeeded;
549 : :
533 peter@eisentraut.org 550 : 220 : resultRelInfo->ri_extraUpdatedCols_valid = true;
551 : : }
552 : : else
553 : : {
554 : : /* Don't call twice */
1237 tgl@sss.pgh.pa.us 555 [ - + ]: 693 : Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
556 : :
557 : 693 : resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
558 : 693 : resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
559 : : }
560 : :
1297 561 : 913 : MemoryContextSwitchTo(oldContext);
562 : : }
563 : :
564 : : /*
565 : : * Compute stored generated columns for a tuple
566 : : */
567 : : void
568 : 1248 : ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
569 : : EState *estate, TupleTableSlot *slot,
570 : : CmdType cmdtype)
571 : : {
572 : 1248 : Relation rel = resultRelInfo->ri_RelationDesc;
573 : 1248 : TupleDesc tupdesc = RelationGetDescr(rel);
574 : 1248 : int natts = tupdesc->natts;
575 [ + + ]: 1248 : 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 [ + - - + ]: 1248 : 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 : : */
1237 588 [ + + ]: 1248 : if (cmdtype == CMD_UPDATE)
589 : : {
590 [ + + ]: 208 : if (resultRelInfo->ri_GeneratedExprsU == NULL)
533 peter@eisentraut.org 591 : 167 : ExecInitGenerated(resultRelInfo, estate, cmdtype);
1237 tgl@sss.pgh.pa.us 592 [ + + ]: 208 : if (resultRelInfo->ri_NumGeneratedNeededU == 0)
593 : 17 : return;
594 : 191 : ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
595 : : }
596 : : else
597 : : {
598 [ + + ]: 1040 : if (resultRelInfo->ri_GeneratedExprsI == NULL)
533 peter@eisentraut.org 599 : 697 : ExecInitGenerated(resultRelInfo, estate, cmdtype);
600 : : /* Early exit is impossible given the prior Assert */
1237 tgl@sss.pgh.pa.us 601 [ - + ]: 1036 : Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
602 : 1036 : ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
603 : : }
604 : :
2674 peter@eisentraut.org 605 [ + - ]: 1227 : oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
606 : :
227 michael@paquier.xyz 607 : 1227 : values = palloc_array(Datum, natts);
608 : 1227 : nulls = palloc_array(bool, natts);
609 : :
2628 peter@eisentraut.org 610 : 1227 : slot_getallattrs(slot);
611 : 1227 : memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
612 : :
2674 613 [ + + ]: 5209 : for (int i = 0; i < natts; i++)
614 : : {
582 drowley@postgresql.o 615 : 3998 : CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
616 : :
1237 tgl@sss.pgh.pa.us 617 [ + + ]: 3998 : if (ri_GeneratedExprs[i])
618 : : {
619 : : Datum val;
620 : : bool isnull;
621 : :
582 drowley@postgresql.o 622 [ - + ]: 1240 : Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
623 : :
2674 peter@eisentraut.org 624 : 1240 : econtext->ecxt_scantuple = slot;
625 : :
1237 tgl@sss.pgh.pa.us 626 : 1240 : 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 : : */
2289 drowley@postgresql.o 632 [ + + ]: 1224 : if (!isnull)
633 : 1168 : val = datumCopy(val, attr->attbyval, attr->attlen);
634 : :
2674 peter@eisentraut.org 635 : 1224 : values[i] = val;
636 : 1224 : nulls[i] = isnull;
637 : : }
638 : : else
639 : : {
2628 640 [ + + ]: 2758 : if (!nulls[i])
641 : 2440 : values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
642 : : }
643 : : }
644 : :
645 : 1211 : ExecClearTuple(slot);
646 : 1211 : memcpy(slot->tts_values, values, sizeof(*values) * natts);
647 : 1211 : memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
648 : 1211 : ExecStoreVirtualTuple(slot);
649 : 1211 : ExecMaterializeSlot(slot);
650 : :
2674 651 : 1211 : 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
1936 tgl@sss.pgh.pa.us 664 : 53380 : ExecInitInsertProjection(ModifyTableState *mtstate,
665 : : ResultRelInfo *resultRelInfo)
666 : : {
667 : 53380 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
668 : 53380 : Plan *subplan = outerPlan(node);
669 : 53380 : EState *estate = mtstate->ps.state;
670 : 53380 : List *insertTargetList = NIL;
671 : 53380 : bool need_projection = false;
672 : : ListCell *l;
673 : :
674 : : /* Extract non-junk columns of the subplan's result tlist. */
675 [ + + + + : 166152 : foreach(l, subplan->targetlist)
+ + ]
676 : : {
677 : 112772 : TargetEntry *tle = (TargetEntry *) lfirst(l);
678 : :
679 [ + - ]: 112772 : if (!tle->resjunk)
680 : 112772 : insertTargetList = lappend(insertTargetList, tle);
681 : : else
1936 tgl@sss.pgh.pa.us 682 :UBC 0 : need_projection = true;
683 : : }
684 : :
685 : : /*
686 : : * The junk-free list must produce a tuple suitable for the result
687 : : * relation.
688 : : */
1936 tgl@sss.pgh.pa.us 689 :CBC 53380 : ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
690 : :
691 : : /* We'll need a slot matching the table's format. */
692 : 53380 : resultRelInfo->ri_newTupleSlot =
693 : 53380 : table_slot_create(resultRelInfo->ri_RelationDesc,
694 : : &estate->es_tupleTable);
695 : :
696 : : /* Build ProjectionInfo if needed (it probably isn't). */
697 [ - + ]: 53380 : if (need_projection)
698 : : {
1936 tgl@sss.pgh.pa.us 699 :UBC 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 : :
1936 tgl@sss.pgh.pa.us 713 :CBC 53380 : resultRelInfo->ri_projectNewInfoValid = true;
714 : 53380 : }
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 : 8763 : ExecInitUpdateProjection(ModifyTableState *mtstate,
735 : : ResultRelInfo *resultRelInfo)
736 : : {
737 : 8763 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
738 : 8763 : Plan *subplan = outerPlan(node);
739 : 8763 : EState *estate = mtstate->ps.state;
740 : 8763 : 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 : 8763 : whichrel = mtstate->mt_lastResultIndex;
749 [ - + ]: 8763 : if (resultRelInfo != mtstate->resultRelInfo + whichrel)
750 : : {
1936 tgl@sss.pgh.pa.us 751 :UBC 0 : whichrel = resultRelInfo - mtstate->resultRelInfo;
752 [ # # # # ]: 0 : Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
753 : : }
754 : :
533 amitlan@postgresql.o 755 :CBC 8763 : 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 : : */
1936 tgl@sss.pgh.pa.us 762 : 8763 : resultRelInfo->ri_oldTupleSlot =
763 : 8763 : table_slot_create(resultRelInfo->ri_RelationDesc,
764 : : &estate->es_tupleTable);
765 : 8763 : resultRelInfo->ri_newTupleSlot =
766 : 8763 : table_slot_create(resultRelInfo->ri_RelationDesc,
767 : : &estate->es_tupleTable);
768 : :
769 : : /* need an expression context to do the projection */
770 [ + + ]: 8763 : if (mtstate->ps.ps_ExprContext == NULL)
771 : 7428 : ExecAssignExprContext(estate, &mtstate->ps);
772 : :
773 : 8763 : resultRelInfo->ri_projectNew =
774 : 8763 : 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 : 8763 : resultRelInfo->ri_projectNewInfoValid = true;
783 : 8763 : }
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 *
1942 792 : 7934120 : ExecGetInsertNewTuple(ResultRelInfo *relinfo,
793 : : TupleTableSlot *planSlot)
794 : : {
795 : 7934120 : 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 [ + - ]: 7934120 : if (newProj == NULL)
804 : : {
805 [ + + ]: 7934120 : if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
806 : : {
807 : 7421827 : ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
808 : 7421827 : return relinfo->ri_newTupleSlot;
809 : : }
810 : : else
811 : 512293 : 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 : : */
1942 tgl@sss.pgh.pa.us 821 :UBC 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 *
1942 tgl@sss.pgh.pa.us 836 :CBC 2209476 : ExecGetUpdateNewTuple(ResultRelInfo *relinfo,
837 : : TupleTableSlot *planSlot,
838 : : TupleTableSlot *oldSlot)
839 : : {
1230 dean.a.rasheed@gmail 840 : 2209476 : ProjectionInfo *newProj = relinfo->ri_projectNew;
841 : : ExprContext *econtext;
842 : :
843 : : /* Use a few extra Asserts to protect against outside callers */
1936 tgl@sss.pgh.pa.us 844 [ - + ]: 2209476 : Assert(relinfo->ri_projectNewInfoValid);
1942 845 [ + - - + ]: 2209476 : Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
846 [ + - - + ]: 2209476 : Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
847 : :
848 : 2209476 : econtext = newProj->pi_exprContext;
849 : 2209476 : econtext->ecxt_outertuple = planSlot;
850 : 2209476 : econtext->ecxt_scantuple = oldSlot;
851 : 2209476 : 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 *
1591 alvherre@alvh.no-ip. 874 : 7937189 : ExecInsert(ModifyTableContext *context,
875 : : ResultRelInfo *resultRelInfo,
876 : : TupleTableSlot *slot,
877 : : bool canSetTag,
878 : : TupleTableSlot **inserted_tuple,
879 : : ResultRelInfo **insert_destrel)
880 : : {
881 : 7937189 : ModifyTableState *mtstate = context->mtstate;
882 : 7937189 : EState *estate = context->estate;
883 : : Relation resultRelationDesc;
6132 tgl@sss.pgh.pa.us 884 : 7937189 : List *recheckIndexes = NIL;
1591 alvherre@alvh.no-ip. 885 : 7937189 : TupleTableSlot *planSlot = context->planSlot;
3393 rhaas@postgresql.org 886 : 7937189 : TupleTableSlot *result = NULL;
887 : : TransitionCaptureState *ar_insert_trig_tcs;
3050 alvherre@alvh.no-ip. 888 : 7937189 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
889 : 7937189 : OnConflictAction onconflict = node->onConflictAction;
2110 heikki.linnakangas@i 890 : 7937189 : 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 [ + + ]: 7937189 : if (proute)
898 : : {
899 : : ResultRelInfo *partRelInfo;
900 : :
901 : 480264 : slot = ExecPrepareTupleRouting(mtstate, estate, proute,
902 : : resultRelInfo, slot,
903 : : &partRelInfo);
904 : 480120 : resultRelInfo = partRelInfo;
905 : : }
906 : :
907 : 7937045 : ExecMaterializeSlot(slot);
908 : :
6132 tgl@sss.pgh.pa.us 909 : 7937045 : 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 : : */
1936 915 [ + + ]: 7937045 : if (resultRelationDesc->rd_rel->relhasindex &&
916 [ + + ]: 2418718 : resultRelInfo->ri_IndexRelationDescs == NULL)
917 : 21754 : 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 : : */
6132 928 [ + + ]: 7937045 : if (resultRelInfo->ri_TrigDesc &&
5767 929 [ + + ]: 454139 : resultRelInfo->ri_TrigDesc->trig_insert_before_row)
930 : : {
931 : : /* Flush any pending inserts, so rows are visible to the triggers */
1338 efujita@postgresql.o 932 [ + + ]: 1431 : if (estate->es_insert_pending_result_relations != NIL)
933 : 3 : ExecPendingInserts(estate);
934 : :
2706 andres@anarazel.de 935 [ + + ]: 1431 : if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
936 : 131 : return NULL; /* "do nothing" */
937 : : }
938 : :
939 : : /* INSTEAD OF ROW INSERT Triggers */
5767 tgl@sss.pgh.pa.us 940 [ + + ]: 7936852 : if (resultRelInfo->ri_TrigDesc &&
941 [ + + ]: 453946 : resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
942 : : {
2706 andres@anarazel.de 943 [ + + ]: 111 : if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
944 : 4 : return NULL; /* "do nothing" */
945 : : }
4885 tgl@sss.pgh.pa.us 946 [ + + ]: 7936741 : 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 : : */
1891 952 : 1010 : slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
953 : :
954 : : /*
955 : : * Compute stored generated columns
956 : : */
2674 peter@eisentraut.org 957 [ + + ]: 1010 : if (resultRelationDesc->rd_att->constr &&
958 [ + + ]: 179 : resultRelationDesc->rd_att->constr->has_generated_stored)
2110 heikki.linnakangas@i 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 : : */
2012 tomas.vondra@postgre 966 [ + + ]: 1010 : if (resultRelInfo->ri_BatchSize > 1)
967 : : {
1338 efujita@postgresql.o 968 : 145 : bool flushed = false;
969 : :
970 : : /*
971 : : * When we've reached the desired batch size, perform the
972 : : * insertion.
973 : : */
2012 tomas.vondra@postgre 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);
1338 efujita@postgresql.o 981 : 10 : flushed = true;
982 : : }
983 : :
2012 tomas.vondra@postgre 984 : 145 : oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
985 : :
986 [ + + ]: 145 : if (resultRelInfo->ri_Slots == NULL)
987 : : {
227 michael@paquier.xyz 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 : : */
1870 tomas.vondra@postgre 1000 [ + + ]: 145 : if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
1001 : : {
1853 andrew@dunslane.net 1002 : 72 : TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
1003 : : TupleDesc plan_tdesc =
1163 tgl@sss.pgh.pa.us 1004 : 72 : CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
1005 : :
1870 tomas.vondra@postgre 1006 : 144 : resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
1007 : 72 : MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
1008 : :
1009 : 144 : resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
1808 1010 : 72 : MakeSingleTupleTableSlot(plan_tdesc, planSlot->tts_ops);
1011 : :
1012 : : /* remember how many batch slots we initialized */
1870 1013 : 72 : resultRelInfo->ri_NumSlotsInitialized++;
1014 : : }
1015 : :
1865 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 : : */
1338 efujita@postgresql.o 1031 [ + + + + ]: 145 : if (resultRelInfo->ri_NumSlots == 0 && !flushed)
1032 : : {
1033 [ - + ]: 19 : 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);
1325 1038 : 19 : estate->es_insert_pending_modifytables =
1039 : 19 : lappend(estate->es_insert_pending_modifytables, mtstate);
1040 : : }
1338 1041 [ - + ]: 145 : Assert(list_member_ptr(estate->es_insert_pending_result_relations,
1042 : : resultRelInfo));
1043 : :
2012 tomas.vondra@postgre 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 : : */
4885 tgl@sss.pgh.pa.us 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 : : */
2706 andres@anarazel.de 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 : 7935731 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1078 : :
1079 : : /*
1080 : : * Compute stored generated columns
1081 : : */
2674 peter@eisentraut.org 1082 [ + + ]: 7935731 : if (resultRelationDesc->rd_att->constr &&
1083 [ + + ]: 2425397 : resultRelationDesc->rd_att->constr->has_generated_stored)
2110 heikki.linnakangas@i 1084 : 1011 : 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 : : */
1580 alvherre@alvh.no-ip. 1099 [ + + ]: 7935711 : if (mtstate->operation == CMD_UPDATE)
1100 : 549 : wco_kind = WCO_RLS_UPDATE_CHECK;
1101 [ + + ]: 7935162 : else if (mtstate->operation == CMD_MERGE)
860 dean.a.rasheed@gmail 1102 : 1181 : wco_kind = (mtstate->mt_merge_action->mas_action->commandType == CMD_UPDATE) ?
1580 alvherre@alvh.no-ip. 1103 [ + + ]: 1181 : WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK;
1104 : : else
1105 : 7933981 : 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 : : */
4110 sfrost@snowman.net 1111 [ + + ]: 7935711 : if (resultRelInfo->ri_WithCheckOptions != NIL)
3109 rhaas@postgresql.org 1112 : 514 : ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
1113 : :
1114 : : /*
1115 : : * Check the constraints of the tuple.
1116 : : */
2966 alvherre@alvh.no-ip. 1117 [ + + ]: 7935575 : if (resultRelationDesc->rd_att->constr)
1118 : 2425305 : 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 : : */
2138 tgl@sss.pgh.pa.us 1125 [ + + ]: 7935058 : if (resultRelationDesc->rd_rel->relispartition &&
1993 heikki.linnakangas@i 1126 [ + + ]: 481618 : (resultRelInfo->ri_RootResultRelInfo == NULL ||
2966 alvherre@alvh.no-ip. 1127 [ + + ]: 479760 : (resultRelInfo->ri_TrigDesc &&
1128 [ + + ]: 1113 : resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
1129 : 1996 : ExecPartitionCheck(resultRelInfo, slot, estate, true);
1130 : :
4096 andres@anarazel.de 1131 [ + + + - ]: 7934946 : 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 : :
704 akapila@postgresql.o 1140 : 5313 : ItemPointerSetInvalid(&invalidItemPtr);
3043 alvherre@alvh.no-ip. 1141 : 5313 : 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 : : */
4096 andres@anarazel.de 1156 : 14 : vlock:
1451 tgl@sss.pgh.pa.us 1157 [ - + ]: 5327 : CHECK_FOR_INTERRUPTS();
4096 andres@anarazel.de 1158 : 5327 : specConflict = false;
2110 heikki.linnakangas@i 1159 [ + + ]: 5327 : if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
1160 : : &conflictTid, &invalidItemPtr,
1161 : : arbiterIndexes))
1162 : : {
1163 : : /* committed conflict tuple found */
4096 andres@anarazel.de 1164 [ + + ]: 3092 : 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 : 2761 : TupleTableSlot *returning = NULL;
1173 : :
1591 alvherre@alvh.no-ip. 1174 [ + + ]: 2761 : if (ExecOnConflictUpdate(context, resultRelInfo,
1175 : : &conflictTid, slot, canSetTag,
1176 : : &returning))
1177 : : {
3028 1178 [ - + ]: 2706 : InstrCountTuples2(&mtstate->ps, 1);
4096 andres@anarazel.de 1179 : 2706 : return returning;
1180 : : }
1181 : : else
1182 : 3 : goto vlock;
1183 : : }
163 dean.a.rasheed@gmail 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
163 dean.a.rasheed@gmail 1202 :UBC 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 : : */
4096 andres@anarazel.de 1217 [ - + ]:CBC 139 : Assert(onconflict == ONCONFLICT_NOTHING);
2681 1218 : 139 : ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
1219 : : ExecGetReturningSlot(estate, resultRelInfo));
3028 alvherre@alvh.no-ip. 1220 [ - + ]: 129 : InstrCountTuples2(&mtstate->ps, 1);
4096 andres@anarazel.de 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 : : */
243 alvherre@kurilemu.de 1231 : 2231 : INJECTION_POINT("exec-insert-before-insert-speculative", NULL);
4096 andres@anarazel.de 1232 : 2231 : specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
1233 : :
1234 : : /* insert the tuple, with the speculative token */
2620 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 */
2110 heikki.linnakangas@i 1242 : 2231 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1243 : : estate, EIIT_NO_DUPE_ERROR,
1244 : : slot, arbiterIndexes,
1245 : : &specConflict);
1246 : :
1247 : : /* adjust the tuple's state accordingly */
2620 andres@anarazel.de 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 : : */
4096 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 */
835 akorotkov@postgresql 1276 : 7929633 : table_tuple_insert(resultRelationDesc, slot,
1277 : : estate->es_output_cid,
1278 : : 0, NULL);
1279 : :
1280 : : /* insert index entries for tuple */
1281 [ + + ]: 7929619 : if (resultRelInfo->ri_NumIndices > 0)
158 alvherre@kurilemu.de 1282 : 2413024 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo, estate,
1283 : : 0, slot, NIL,
1284 : : NULL);
1285 : : }
1286 : : }
1287 : :
5629 tgl@sss.pgh.pa.us 1288 [ + + ]: 7932416 : if (canSetTag)
1289 : 7930441 : (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 : : */
3109 rhaas@postgresql.org 1297 : 7932416 : ar_insert_trig_tcs = mtstate->mt_transition_capture;
1298 [ + + + + ]: 7932416 : if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
1299 [ + + ]: 36 : && mtstate->mt_transition_capture->tcs_update_new_table)
1300 : : {
1588 alvherre@alvh.no-ip. 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 : : */
3109 rhaas@postgresql.org 1314 : 32 : ar_insert_trig_tcs = NULL;
1315 : : }
1316 : :
1317 : : /* AFTER ROW INSERT Triggers */
2706 andres@anarazel.de 1318 : 7932416 : ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
1319 : : ar_insert_trig_tcs);
1320 : :
6019 tgl@sss.pgh.pa.us 1321 : 7932415 : 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 : : */
4755 sfrost@snowman.net 1335 [ + + ]: 7932415 : if (resultRelInfo->ri_WithCheckOptions != NIL)
4110 1336 : 326 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1337 : :
1338 : : /* Process RETURNING if present */
6132 tgl@sss.pgh.pa.us 1339 [ + + ]: 7932319 : if (resultRelInfo->ri_projectReturning)
1340 : : {
555 dean.a.rasheed@gmail 1341 : 2779 : 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 [ + + ]: 2779 : 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 : :
163 1373 : 2779 : 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 : : */
555 1381 [ + + ]: 2771 : 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 : :
1588 alvherre@alvh.no-ip. 1391 [ + + ]: 7932311 : if (inserted_tuple)
1392 : 565 : *inserted_tuple = slot;
1393 [ + + ]: 7932311 : if (insert_destrel)
1394 : 565 : *insert_destrel = resultRelInfo;
1395 : :
3474 rhaas@postgresql.org 1396 : 7932311 : 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
115 peter@eisentraut.org 1407 : 885 : ExecForPortionOfLeftovers(ModifyTableContext *context,
1408 : : EState *estate,
1409 : : ResultRelInfo *resultRelInfo,
1410 : : ItemPointer tupleid)
1411 : : {
1412 : 885 : ModifyTableState *mtstate = context->mtstate;
1413 : 885 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
1414 : 885 : ForPortionOfExpr *forPortionOf = (ForPortionOfExpr *) node->forPortionOf;
1415 : : Datum oldRange;
1416 : : TypeCacheEntry *typcache;
1417 : : ForPortionOfState *fpoState;
1418 : : TupleTableSlot *oldtupleSlot;
1419 : : TupleTableSlot *leftoverSlot;
1420 : 885 : TupleConversionMap *map = NULL;
1421 : 885 : HeapTuple oldtuple = NULL;
1422 : : CmdType oldOperation;
1423 : : TransitionCaptureState *oldTcs;
1424 : : FmgrInfo flinfo;
1425 : : PgStat_FunctionCallUsage fcusage;
1426 : : ReturnSetInfo rsi;
1427 : 885 : bool didInit = false;
1428 : 885 : bool shouldFree = false;
47 1429 : 885 : ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
1430 : 885 : bool partitionRouting =
1431 [ + - ]: 1770 : rootRelInfo &&
1432 [ + + ]: 885 : rootRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
1433 : :
115 1434 : 885 : LOCAL_FCINFO(fcinfo, 2);
1435 : :
1436 : 885 : fpoState = resultRelInfo->ri_forPortionOf;
1437 : 885 : oldtupleSlot = fpoState->fp_Existing;
1438 : 885 : 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 [ - + ]: 885 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc, tupleid, SnapshotAny, oldtupleSlot))
115 peter@eisentraut.org 1454 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch tuple for FOR PORTION OF");
1455 : :
115 peter@eisentraut.org 1456 :CBC 885 : slot_getallattrs(oldtupleSlot);
1457 : :
1458 : : /* Get the old range of the record being updated/deleted. */
47 1459 [ - + ]: 885 : if (oldtupleSlot->tts_isnull[fpoState->fp_rangeAttno - 1])
115 peter@eisentraut.org 1460 [ # # ]:UBC 0 : elog(ERROR, "found a NULL range in a temporal table");
47 peter@eisentraut.org 1461 :CBC 885 : 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 : :
115 1468 : 885 : typcache = fpoState->fp_leftoverstypcache;
1469 [ + + ]: 885 : if (typcache == NULL)
1470 : : {
1471 : 746 : typcache = lookup_type_cache(forPortionOf->rangeType, 0);
1472 : 746 : 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 : 885 : fmgr_info(forPortionOf->withoutPortionProc, &flinfo);
1483 : 885 : rsi.type = T_ReturnSetInfo;
1484 : 885 : rsi.econtext = mtstate->ps.ps_ExprContext;
1485 : 885 : rsi.expectedDesc = NULL;
1486 : 885 : rsi.allowedModes = (int) (SFRM_ValuePerCall);
1487 : 885 : rsi.returnMode = SFRM_ValuePerCall;
1488 : : /* isDone is filled below */
1489 : 885 : rsi.setResult = NULL;
1490 : 885 : rsi.setDesc = NULL;
1491 : :
1492 : 885 : InitFunctionCallInfoData(*fcinfo, &flinfo, 2, InvalidOid, NULL, (Node *) &rsi);
1493 : 885 : fcinfo->args[0].value = oldRange;
1494 : 885 : fcinfo->args[0].isnull = false;
1495 : 885 : fcinfo->args[1].value = fpoState->fp_targetRange;
1496 : 885 : 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 : : */
47 1507 [ + + ]: 885 : if (partitionRouting)
1508 : : {
1509 : 66 : map = ExecGetChildToRootMap(resultRelInfo);
115 1510 : 66 : 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 : 1155 : {
1519 : : Datum leftover;
1520 : :
1521 : : /* Call the function one time */
96 tgl@sss.pgh.pa.us 1522 : 2040 : pgstat_init_function_usage(fcinfo, &fcusage);
1523 : :
1524 : 2040 : fcinfo->isnull = false;
1525 : 2040 : rsi.isDone = ExprSingleResult;
1526 : 2040 : leftover = FunctionCallInvoke(fcinfo);
1527 : :
1528 : 2040 : pgstat_end_function_usage(&fcusage,
1529 : 2040 : rsi.isDone != ExprMultipleResult);
1530 : :
1531 [ - + ]: 2040 : if (rsi.returnMode != SFRM_ValuePerCall)
96 tgl@sss.pgh.pa.us 1532 [ # # ]:UBC 0 : elog(ERROR, "without_portion function violated function call protocol");
1533 : :
1534 : : /* Are we done? */
115 peter@eisentraut.org 1535 [ + + ]:CBC 2040 : if (rsi.isDone == ExprEndResult)
1536 : 841 : break;
1537 : :
1538 [ - + ]: 1199 : if (fcinfo->isnull)
96 tgl@sss.pgh.pa.us 1539 [ # # ]:UBC 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 : : */
115 peter@eisentraut.org 1546 [ + + ]:CBC 1199 : if (forPortionOf->isDomain)
1547 : 80 : domain_check(leftover, false, forPortionOf->rangeVar->vartype, NULL, NULL);
1548 : :
1549 [ + + ]: 1183 : 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 [ + + ]: 765 : if (map != NULL)
1558 : : {
1559 : 16 : leftoverSlot = execute_attr_map_slot(map->attrMap,
1560 : : oldtupleSlot,
1561 : : leftoverSlot);
1562 : : }
1563 : : else
1564 : : {
1565 : 749 : oldtuple = ExecFetchSlotHeapTuple(oldtupleSlot, false, &shouldFree);
1566 : 749 : 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 : 765 : oldOperation = mtstate->operation;
1574 : 765 : mtstate->operation = CMD_INSERT;
1575 : 765 : oldTcs = mtstate->mt_transition_capture;
1576 : :
1577 : 765 : 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 : : */
51 1586 [ + + ]: 418 : if (map != NULL)
1587 : 16 : execute_attr_map_slot(map->attrMap, oldtupleSlot, leftoverSlot);
1588 : : else
1589 : 402 : ExecForceStoreHeapTuple(oldtuple, leftoverSlot, false);
1590 : : }
1591 : :
47 1592 : 1183 : leftoverSlot->tts_values[resultRelInfo->ri_forPortionOf->fp_rangeAttno - 1] = leftover;
1593 : 1183 : leftoverSlot->tts_isnull[resultRelInfo->ri_forPortionOf->fp_rangeAttno - 1] = false;
115 1594 : 1183 : 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 : 1183 : AfterTriggerBeginQuery();
1607 : 1183 : ExecSetupTransitionCaptureState(mtstate, estate);
1608 : 1183 : fireBSTriggers(mtstate);
1609 : 1183 : ExecInsert(context, resultRelInfo, leftoverSlot, false, NULL, NULL);
1610 : 1155 : fireASTriggers(mtstate);
1611 : 1155 : AfterTriggerEndQuery(estate);
1612 : : }
1613 : :
1614 [ + + ]: 841 : if (didInit)
1615 : : {
1616 : 737 : mtstate->operation = oldOperation;
1617 : 737 : mtstate->mt_transition_capture = oldTcs;
1618 : :
1619 [ - + ]: 737 : if (shouldFree)
115 peter@eisentraut.org 1620 :UBC 0 : heap_freetuple(oldtuple);
1621 : : }
115 peter@eisentraut.org 1622 :CBC 841 : }
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
2012 tomas.vondra@postgre 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)
2012 tomas.vondra@postgre 1674 :UBC 0 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1675 : : }
1676 : :
2012 tomas.vondra@postgre 1677 [ + - + - ]:CBC 28 : if (canSetTag && numInserted > 0)
1678 : 28 : estate->es_processed += numInserted;
1679 : :
1680 : : /* Clean up all the slots, ready for the next batch */
1187 michael@paquier.xyz 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;
2012 tomas.vondra@postgre 1687 : 28 : }
1688 : :
1689 : : /*
1690 : : * ExecPendingInserts -- flushes all pending inserts to the foreign tables
1691 : : */
1692 : : static void
1338 efujita@postgresql.o 1693 : 18 : ExecPendingInserts(EState *estate)
1694 : : {
1695 : : ListCell *l1,
1696 : : *l2;
1697 : :
1325 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 : :
1338 1704 [ - + ]: 19 : 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);
1325 1713 : 17 : list_free(estate->es_insert_pending_modifytables);
1338 1714 : 17 : estate->es_insert_pending_result_relations = NIL;
1325 1715 : 17 : estate->es_insert_pending_modifytables = NIL;
1338 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
1591 alvherre@alvh.no-ip. 1726 : 985185 : ExecDeletePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1727 : : ItemPointer tupleid, HeapTuple oldtuple,
1728 : : TupleTableSlot **epqreturnslot, TM_Result *result)
1729 : : {
1230 dean.a.rasheed@gmail 1730 [ + + ]: 985185 : if (result)
1731 : 1070 : *result = TM_Ok;
1732 : :
1733 : : /* BEFORE ROW DELETE triggers */
1591 alvherre@alvh.no-ip. 1734 [ + + ]: 985185 : if (resultRelInfo->ri_TrigDesc &&
1735 [ + + ]: 4707 : resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1736 : : {
1737 : : /* Flush any pending inserts, so rows are visible to the triggers */
1338 efujita@postgresql.o 1738 [ + + ]: 218 : if (context->estate->es_insert_pending_result_relations != NIL)
1739 : 1 : ExecPendingInserts(context->estate);
1740 : :
1591 alvherre@alvh.no-ip. 1741 : 208 : return ExecBRDeleteTriggers(context->estate, context->epqstate,
1742 : : resultRelInfo, tupleid, oldtuple,
1743 : : epqreturnslot, result, &context->tmfd,
372 dean.a.rasheed@gmail 1744 : 218 : context->mtstate->operation == CMD_MERGE);
1745 : : }
1746 : :
1591 alvherre@alvh.no-ip. 1747 : 984967 : 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 : 985079 : ExecDeleteAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1759 : : ItemPointer tupleid, bool changingPart)
1760 : : {
1761 : 985079 : EState *estate = context->estate;
115 alvherre@kurilemu.de 1762 : 985079 : uint32 options = 0;
1763 : :
1764 [ + + ]: 985079 : if (changingPart)
1765 : 697 : options |= TABLE_DELETE_CHANGING_PARTITION;
1766 : :
1591 alvherre@alvh.no-ip. 1767 : 985079 : 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 : 985017 : ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
1786 : : ItemPointer tupleid, HeapTuple oldtuple, bool changingPart)
1787 : : {
1788 : 985017 : ModifyTableState *mtstate = context->mtstate;
1789 : 985017 : 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 : 985017 : ar_delete_trig_tcs = mtstate->mt_transition_capture;
1799 [ + + + + ]: 985017 : if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1800 [ + + ]: 36 : mtstate->mt_transition_capture->tcs_update_old_table)
1801 : : {
1588 1802 : 32 : ExecARUpdateTriggers(estate, resultRelInfo,
1803 : : NULL, NULL,
1804 : : tupleid, oldtuple,
835 akorotkov@postgresql 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 : : */
1591 alvherre@alvh.no-ip. 1812 : 32 : ar_delete_trig_tcs = NULL;
1813 : : }
1814 : :
1815 : : /* Compute temporal leftovers in FOR PORTION OF */
115 peter@eisentraut.org 1816 [ + + ]: 985017 : if (((ModifyTable *) context->mtstate->ps.plan)->forPortionOf)
1817 : 394 : ExecForPortionOfLeftovers(context, estate, resultRelInfo, tupleid);
1818 : :
1819 : : /* AFTER ROW DELETE Triggers */
835 akorotkov@postgresql 1820 : 984997 : ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1821 : : ar_delete_trig_tcs, changingPart);
1591 alvherre@alvh.no-ip. 1822 : 984995 : }
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 : 984834 : 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 : 984834 : EState *estate = context->estate;
2110 heikki.linnakangas@i 1859 : 984834 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
4885 tgl@sss.pgh.pa.us 1860 : 984834 : TupleTableSlot *slot = NULL;
1861 : : TM_Result result;
1862 : : bool saveOld;
1863 : :
3109 rhaas@postgresql.org 1864 [ + + ]: 984834 : if (tupleDeleted)
1865 : 719 : *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 : : */
1591 alvherre@alvh.no-ip. 1871 [ + + ]: 984834 : if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple,
1872 : : epqreturnslot, tmresult))
1873 : 33 : return NULL;
1874 : :
1875 : : /* INSTEAD OF ROW DELETE Triggers */
5767 tgl@sss.pgh.pa.us 1876 [ + + ]: 984791 : if (resultRelInfo->ri_TrigDesc &&
1877 [ + + ]: 4619 : resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
6132 1878 : 31 : {
1879 : : bool dodelete;
1880 : :
5767 1881 [ - + ]: 35 : Assert(oldtuple != NULL);
4507 noah@leadboat.com 1882 : 35 : dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
1883 : :
5767 tgl@sss.pgh.pa.us 1884 [ + + ]: 35 : if (!dodelete) /* "do nothing" */
6132 1885 : 4 : return NULL;
1886 : : }
4885 1887 [ + + ]: 984756 : 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 : : */
2706 andres@anarazel.de 1895 : 23 : slot = ExecGetReturningSlot(estate, resultRelInfo);
4885 tgl@sss.pgh.pa.us 1896 : 23 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
1897 : : resultRelInfo,
1898 : : slot,
1899 : : context->planSlot);
1900 : :
1901 [ - + ]: 23 : if (slot == NULL) /* "do nothing" */
4885 tgl@sss.pgh.pa.us 1902 :UBC 0 : return NULL;
1903 : :
1904 : : /*
1905 : : * RETURNING expressions might reference the tableoid column, so
1906 : : * (re)initialize tts_tableOid before evaluating them.
1907 : : */
2840 andres@anarazel.de 1908 [ + + ]:CBC 23 : if (TTS_EMPTY(slot))
3824 rhaas@postgresql.org 1909 : 5 : ExecStoreAllNullTuple(slot);
1910 : :
2706 andres@anarazel.de 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 : : */
1384 john.naylor@postgres 1924 : 984733 : ldelete:
835 akorotkov@postgresql 1925 : 984739 : result = ExecDeleteAct(context, resultRelInfo, tupleid, changingPart);
1926 : :
947 dean.a.rasheed@gmail 1927 [ + + ]: 984721 : if (tmresult)
1928 : 697 : *tmresult = result;
1929 : :
5767 tgl@sss.pgh.pa.us 1930 [ + + + + : 984721 : switch (result)
- ]
1931 : : {
2681 andres@anarazel.de 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 : : */
1591 alvherre@alvh.no-ip. 1958 [ + + ]: 28 : if (context->tmfd.cmax != estate->es_output_cid)
5020 kgrittn@postgresql.o 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 */
5767 tgl@sss.pgh.pa.us 1965 : 24 : return NULL;
1966 : :
2681 andres@anarazel.de 1967 : 984630 : case TM_Ok:
5767 tgl@sss.pgh.pa.us 1968 : 984630 : break;
1969 : :
2681 andres@anarazel.de 1970 : 48 : case TM_Updated:
1971 : : {
1972 : : TupleTableSlot *inputslot;
1973 : : TupleTableSlot *epqslot;
1974 : :
1975 [ + + ]: 48 : 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 : : */
835 akorotkov@postgresql 1984 : 38 : EvalPlanQualBegin(context->epqstate);
1985 : 38 : inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
1986 : : resultRelInfo->ri_RangeTableIndex);
1987 : :
1988 : 38 : 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 [ + + + - ]: 34 : switch (result)
1996 : : {
1997 : 31 : case TM_Ok:
1998 [ - + ]: 31 : Assert(context->tmfd.traversed);
1999 : 31 : epqslot = EvalPlanQual(context->epqstate,
2000 : : resultRelationDesc,
2001 : : resultRelInfo->ri_RangeTableIndex,
2002 : : inputslot);
2003 [ + - + + ]: 31 : 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 [ + + ]: 15 : if (epqreturnslot)
2012 : : {
2013 : 9 : *epqreturnslot = epqslot;
2014 : 9 : 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 : :
835 akorotkov@postgresql 2043 :UBC 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 : :
2681 andres@anarazel.de 2064 :CBC 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 */
5767 tgl@sss.pgh.pa.us 2070 : 6 : return NULL;
2071 : :
5767 tgl@sss.pgh.pa.us 2072 :UBC 0 : default:
2620 andres@anarazel.de 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 : :
5629 tgl@sss.pgh.pa.us 2088 [ + + ]:CBC 984684 : if (canSetTag)
2089 : 983851 : (estate->es_processed)++;
2090 : :
2091 : : /* Tell caller that the delete actually happened. */
3109 rhaas@postgresql.org 2092 [ + + ]: 984684 : if (tupleDeleted)
2093 : 666 : *tupleDeleted = true;
2094 : :
835 akorotkov@postgresql 2095 : 984684 : 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 : : */
555 dean.a.rasheed@gmail 2104 [ + + + + ]: 984757 : saveOld = changingPart && resultRelInfo->ri_projectReturning &&
2105 [ + + ]: 95 : resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD;
2106 : :
2107 [ + + + + : 984662 : 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 : :
4885 tgl@sss.pgh.pa.us 2115 [ + + ]: 628 : if (resultRelInfo->ri_FdwRoutine)
2116 : : {
2117 : : /* FDW must have provided a slot containing the deleted row */
2118 [ + - - + ]: 7 : Assert(!TupIsNull(slot));
2119 : : }
2120 : : else
2121 : : {
2706 andres@anarazel.de 2122 : 621 : slot = ExecGetReturningSlot(estate, resultRelInfo);
4885 tgl@sss.pgh.pa.us 2123 [ + + ]: 621 : if (oldtuple != NULL)
2124 : : {
2654 andres@anarazel.de 2125 : 16 : ExecForceStoreHeapTuple(oldtuple, slot, false);
2126 : : }
2127 : : else
2128 : : {
835 akorotkov@postgresql 2129 [ - + ]: 605 : if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
2130 : : SnapshotAny, slot))
835 akorotkov@postgresql 2131 [ # # ]:UBC 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 : : */
555 dean.a.rasheed@gmail 2139 [ + + ]:CBC 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 : :
163 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 : : */
4885 tgl@sss.pgh.pa.us 2175 : 598 : ExecMaterializeSlot(rslot);
2176 : :
6132 2177 : 598 : ExecClearTuple(slot);
2178 : :
2179 : 598 : return rslot;
2180 : : }
2181 : :
2182 : 984034 : 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
1591 alvherre@alvh.no-ip. 2205 : 751 : 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 : 751 : ModifyTableState *mtstate = context->mtstate;
2109 heikki.linnakangas@i 2217 : 751 : EState *estate = mtstate->ps.state;
2218 : : TupleConversionMap *tupconv_map;
2219 : : bool tuple_deleted;
2220 : 751 : TupleTableSlot *epqslot = NULL;
2221 : :
555 dean.a.rasheed@gmail 2222 : 751 : context->cpDeletedSlot = NULL;
1591 alvherre@alvh.no-ip. 2223 : 751 : context->cpUpdateReturningSlot = NULL;
1230 dean.a.rasheed@gmail 2224 : 751 : *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 : : */
2109 heikki.linnakangas@i 2231 [ - + ]: 751 : if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
2109 heikki.linnakangas@i 2232 [ # # ]:UBC 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 : : */
1936 tgl@sss.pgh.pa.us 2241 [ + + ]:CBC 751 : if (resultRelInfo == mtstate->rootResultRelInfo)
2109 heikki.linnakangas@i 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 : : */
1936 tgl@sss.pgh.pa.us 2248 [ + + ]: 719 : if (mtstate->mt_partition_tuple_routing == NULL)
2249 : : {
2250 : 440 : Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
2251 : : MemoryContext oldcxt;
2252 : :
2253 : : /* Things built here have to last for the query duration. */
2254 : 440 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
2255 : :
2256 : 440 : mtstate->mt_partition_tuple_routing =
2257 : 440 : 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 [ - + ]: 440 : Assert(mtstate->mt_root_tuple_slot == NULL);
2265 : 440 : mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
2266 : :
2267 : 440 : 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 : : */
1591 alvherre@alvh.no-ip. 2274 : 719 : 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 : : */
2109 heikki.linnakangas@i 2299 [ + + ]: 716 : 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 : : */
860 dean.a.rasheed@gmail 2310 [ + + ]: 50 : if (mtstate->operation == CMD_MERGE)
947 2311 : 24 : return *tmresult == TM_Ok;
1230 2312 [ + + - + ]: 26 : else if (TupIsNull(epqslot))
2109 heikki.linnakangas@i 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. */
835 akorotkov@postgresql 2320 [ - + ]: 3 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
835 akorotkov@postgresql 2321 :UBC 0 : ExecInitUpdateProjection(mtstate, resultRelInfo);
835 akorotkov@postgresql 2322 :CBC 3 : oldSlot = resultRelInfo->ri_oldTupleSlot;
2323 [ - + ]: 3 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
2324 : : tupleid,
2325 : : SnapshotAny,
2326 : : oldSlot))
835 akorotkov@postgresql 2327 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch tuple being updated");
2328 : : /* and project the new tuple to retry the UPDATE with */
1230 dean.a.rasheed@gmail 2329 :CBC 3 : *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot,
2330 : : oldSlot);
2109 heikki.linnakangas@i 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 : : */
1936 tgl@sss.pgh.pa.us 2340 : 666 : tupconv_map = ExecGetChildToRootMap(resultRelInfo);
2109 heikki.linnakangas@i 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. */
1591 alvherre@alvh.no-ip. 2347 : 583 : context->cpUpdateReturningSlot =
1588 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 : : */
2109 heikki.linnakangas@i 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
1591 alvherre@alvh.no-ip. 2370 : 2213538 : ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2371 : : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
2372 : : TM_Result *result)
2373 : : {
2374 : 2213538 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2375 : :
1230 dean.a.rasheed@gmail 2376 [ + + ]: 2213538 : if (result)
2377 : 1416 : *result = TM_Ok;
2378 : :
1591 alvherre@alvh.no-ip. 2379 : 2213538 : 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 [ + + ]: 2213538 : if (resultRelationDesc->rd_rel->relhasindex &&
2386 [ + + ]: 146418 : resultRelInfo->ri_IndexRelationDescs == NULL)
2387 : 5785 : ExecOpenIndices(resultRelInfo, false);
2388 : :
2389 : : /* BEFORE ROW UPDATE triggers */
2390 [ + + ]: 2213538 : if (resultRelInfo->ri_TrigDesc &&
2391 [ + + ]: 4010 : resultRelInfo->ri_TrigDesc->trig_update_before_row)
2392 : : {
2393 : : /* Flush any pending inserts, so rows are visible to the triggers */
1338 efujita@postgresql.o 2394 [ + + ]: 1584 : if (context->estate->es_insert_pending_result_relations != NIL)
2395 : 1 : ExecPendingInserts(context->estate);
2396 : :
1591 alvherre@alvh.no-ip. 2397 : 1572 : return ExecBRUpdateTriggers(context->estate, context->epqstate,
2398 : : resultRelInfo, tupleid, oldtuple, slot,
2399 : : result, &context->tmfd,
372 dean.a.rasheed@gmail 2400 : 1584 : context->mtstate->operation == CMD_MERGE);
2401 : : }
2402 : :
1591 alvherre@alvh.no-ip. 2403 : 2211954 : 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 : 2213350 : ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo,
2414 : : TupleTableSlot *slot,
2415 : : EState *estate)
2416 : : {
2417 : 2213350 : 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 : 2213350 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2424 : :
2425 : : /*
2426 : : * Compute stored generated columns
2427 : : */
2428 [ + + ]: 2213350 : if (resultRelationDesc->rd_att->constr &&
2429 [ + + ]: 123469 : resultRelationDesc->rd_att->constr->has_generated_stored)
2430 : 206 : ExecComputeStoredGenerated(resultRelInfo, estate, slot,
2431 : : CMD_UPDATE);
2432 : 2213350 : }
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 : 2213251 : ExecUpdateAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2449 : : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
2450 : : bool canSetTag, UpdateContext *updateCxt)
2451 : : {
2452 : 2213251 : EState *estate = context->estate;
2453 : 2213251 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2454 : : bool partition_constraint_failed;
2455 : : TM_Result result;
2456 : :
2457 : 2213251 : 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 : : */
1384 john.naylor@postgres 2465 : 2213254 : lreplace:
2466 : : /* Fill in GENERATEd columns */
1237 tgl@sss.pgh.pa.us 2467 : 2213254 : ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2468 : :
2469 : : /* ensure slot is independent, consider e.g. EPQ */
1591 alvherre@alvh.no-ip. 2470 : 2213254 : 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 : 2213254 : partition_constraint_failed =
2480 [ + + ]: 2215095 : resultRelationDesc->rd_rel->relispartition &&
2481 [ + + ]: 1841 : !ExecPartitionCheck(resultRelInfo, slot, estate, false);
2482 : :
2483 : : /* Check any RLS UPDATE WITH CHECK policies */
2484 [ + + ]: 2213254 : if (!partition_constraint_failed &&
2485 [ + + ]: 2212503 : 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 : 368 : 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 [ + + ]: 2213218 : if (partition_constraint_failed)
2500 : : {
2501 : : TupleTableSlot *inserted_tuple,
2502 : : *retry_slot;
1588 2503 : 751 : 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 : : */
1591 2511 [ + + ]: 751 : 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 : : */
1588 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 : :
1591 2545 : 626 : 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 : : */
860 dean.a.rasheed@gmail 2552 [ + + ]: 11 : if (context->mtstate->operation == CMD_MERGE)
947 2553 : 8 : return result;
2554 : :
2555 : : /*
2556 : : * ExecCrossPartitionUpdate installed an updated version of the new
2557 : : * tuple in the retry slot; start over.
2558 : : */
1230 2559 : 3 : slot = retry_slot;
1591 alvherre@alvh.no-ip. 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 [ + + ]: 2212467 : if (resultRelationDesc->rd_att->constr)
2570 : 123063 : 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 : 2212411 : 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 : 2212399 : 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 : 2212401 : ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt,
2602 : : ResultRelInfo *resultRelInfo, ItemPointer tupleid,
2603 : : HeapTuple oldtuple, TupleTableSlot *slot)
2604 : : {
2605 : 2212401 : ModifyTableState *mtstate = context->mtstate;
1230 dean.a.rasheed@gmail 2606 : 2212401 : List *recheckIndexes = NIL;
2607 : :
2608 : : /* insert index entries for tuple if necessary */
1223 tomas.vondra@postgre 2609 [ + + + + ]: 2212401 : if (resultRelInfo->ri_NumIndices > 0 && (updateCxt->updateIndexes != TU_None))
2610 : : {
117 nathan@postgresql.or 2611 : 115875 : uint32 flags = EIIT_IS_UPDATE;
2612 : :
158 alvherre@kurilemu.de 2613 [ + + ]: 115875 : if (updateCxt->updateIndexes == TU_Summarizing)
2614 : 2188 : flags |= EIIT_ONLY_SUMMARIZING;
2615 : 115875 : recheckIndexes = ExecInsertIndexTuples(resultRelInfo, context->estate,
2616 : : flags, slot, NIL,
2617 : : NULL);
2618 : : }
2619 : :
2620 : : /* Compute temporal leftovers in FOR PORTION OF */
115 peter@eisentraut.org 2621 [ + + ]: 2212341 : if (((ModifyTable *) context->mtstate->ps.plan)->forPortionOf)
2622 : 491 : ExecForPortionOfLeftovers(context, context->estate, resultRelInfo, tupleid);
2623 : :
2624 : : /* AFTER ROW UPDATE Triggers */
1591 alvherre@alvh.no-ip. 2625 : 2212317 : ExecARUpdateTriggers(context->estate, resultRelInfo,
2626 : : NULL, NULL,
2627 : : tupleid, oldtuple, slot,
2628 : : recheckIndexes,
2629 [ + + ]: 2212317 : mtstate->operation == CMD_INSERT ?
2630 : : mtstate->mt_oc_transition_capture :
2631 : : mtstate->mt_transition_capture,
2632 : : false);
2633 : :
1230 dean.a.rasheed@gmail 2634 : 2212315 : 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 : : */
1591 alvherre@alvh.no-ip. 2645 [ + + ]: 2212315 : if (resultRelInfo->ri_WithCheckOptions != NIL)
2646 : 345 : ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo,
2647 : : slot, context->estate);
2648 : 2212261 : }
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
1588 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 *
1591 2749 : 2212122 : ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
2750 : : ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *oldSlot,
2751 : : TupleTableSlot *slot, bool canSetTag)
2752 : : {
2753 : 2212122 : EState *estate = context->estate;
2110 heikki.linnakangas@i 2754 : 2212122 : Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1591 alvherre@alvh.no-ip. 2755 : 2212122 : UpdateContext updateCxt = {0};
2756 : : TM_Result result;
2757 : :
2758 : : /*
2759 : : * abort the operation if not running transactions
2760 : : */
6132 tgl@sss.pgh.pa.us 2761 [ - + ]: 2212122 : if (IsBootstrapProcessingMode())
6132 tgl@sss.pgh.pa.us 2762 [ # # ]:UBC 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 : : */
17 dean.a.rasheed@gmail 2768 :CBC 2212122 : context->tmfd.traversed = false;
1230 2769 [ + + ]: 2212122 : if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
1591 alvherre@alvh.no-ip. 2770 : 85 : return NULL;
2771 : :
2772 : : /*
2773 : : * If the target tuple was concurrently updated, the trigger code will
2774 : : * have done EPQ and updated tupleid, following the update chain. In this
2775 : : * case, we must fetch the most recent version of old tuple for the
2776 : : * benefit of RETURNING. Technically, we could get away with not doing
2777 : : * this, if there is no RETURNING clause, or it doesn't refer to OLD, but
2778 : : * it seems preferable to always ensure that the contents of oldSlot are
2779 : : * correct.
2780 : : */
17 dean.a.rasheed@gmail 2781 [ + + ]: 2212025 : if (context->tmfd.traversed)
2782 : : {
2783 [ - + ]: 3 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
2784 : : tupleid,
2785 : : SnapshotAny,
2786 : : oldSlot))
17 dean.a.rasheed@gmail 2787 [ # # ]:UBC 0 : elog(ERROR, "failed to re-fetch tuple updated during trigger execution");
2788 : : }
2789 : :
2790 : : /* INSTEAD OF ROW UPDATE Triggers */
5767 tgl@sss.pgh.pa.us 2791 [ + + ]:CBC 2212025 : if (resultRelInfo->ri_TrigDesc &&
2792 [ + + ]: 3671 : resultRelInfo->ri_TrigDesc->trig_update_instead_row)
2793 : : {
2706 andres@anarazel.de 2794 [ + + ]: 83 : if (!ExecIRUpdateTriggers(estate, resultRelInfo,
2795 : : oldtuple, slot))
2621 tgl@sss.pgh.pa.us 2796 : 12 : return NULL; /* "do nothing" */
2797 : : }
4885 2798 [ + + ]: 2211942 : else if (resultRelInfo->ri_FdwRoutine)
2799 : : {
2800 : : /* Fill in GENERATEd columns */
1591 alvherre@alvh.no-ip. 2801 : 96 : ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2802 : :
2803 : : /*
2804 : : * update in foreign table: let the FDW do it
2805 : : */
4885 tgl@sss.pgh.pa.us 2806 : 96 : slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
2807 : : resultRelInfo,
2808 : : slot,
2809 : : context->planSlot);
2810 : :
2811 [ + + ]: 96 : if (slot == NULL) /* "do nothing" */
2812 : 1 : return NULL;
2813 : :
2814 : : /*
2815 : : * AFTER ROW Triggers or RETURNING expressions might reference the
2816 : : * tableoid column, so (re-)initialize tts_tableOid before evaluating
2817 : : * them. (This covers the case where the FDW replaced the slot.)
2818 : : */
2706 andres@anarazel.de 2819 : 95 : slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2820 : : }
2821 : : else
2822 : : {
2823 : : ItemPointerData lockedtid;
2824 : :
2825 : : /*
2826 : : * If we generate a new candidate tuple after EvalPlanQual testing, we
2827 : : * must loop back here to try again. (We don't need to redo triggers,
2828 : : * however. If there are any BEFORE triggers then trigger.c will have
2829 : : * done table_tuple_lock to lock the correct tuple, so there's no need
2830 : : * to do them again.)
2831 : : */
1591 alvherre@alvh.no-ip. 2832 : 2211846 : redo_act:
669 noah@leadboat.com 2833 : 2211899 : lockedtid = *tupleid;
1591 alvherre@alvh.no-ip. 2834 : 2211899 : result = ExecUpdateAct(context, resultRelInfo, tupleid, oldtuple, slot,
2835 : : canSetTag, &updateCxt);
2836 : :
2837 : : /*
2838 : : * If ExecUpdateAct reports that a cross-partition update was done,
2839 : : * then the RETURNING tuple (if any) has been projected and there's
2840 : : * nothing else for us to do.
2841 : : */
2842 [ + + ]: 2211688 : if (updateCxt.crossPartUpdate)
2843 : 616 : return context->cpUpdateReturningSlot;
2844 : :
5767 tgl@sss.pgh.pa.us 2845 [ + + + + : 2211159 : switch (result)
- ]
2846 : : {
2681 andres@anarazel.de 2847 : 60 : case TM_SelfModified:
2848 : :
2849 : : /*
2850 : : * The target tuple was already updated or deleted by the
2851 : : * current command, or by a later command in the current
2852 : : * transaction. The former case is possible in a join UPDATE
2853 : : * where multiple tuples join to the same target tuple. This
2854 : : * is pretty questionable, but Postgres has always allowed it:
2855 : : * we just execute the first update action and ignore
2856 : : * additional update attempts.
2857 : : *
2858 : : * The latter case arises if the tuple is modified by a
2859 : : * command in a BEFORE trigger, or perhaps by a command in a
2860 : : * volatile function used in the query. In such situations we
2861 : : * should not ignore the update, but it is equally unsafe to
2862 : : * proceed. We don't want to discard the original UPDATE
2863 : : * while keeping the triggered actions based on it; and we
2864 : : * have no principled way to merge this update with the
2865 : : * previous ones. So throwing an error is the only safe
2866 : : * course.
2867 : : *
2868 : : * If a trigger actually intends this type of interaction, it
2869 : : * can re-execute the UPDATE (assuming it can figure out how)
2870 : : * and then return NULL to cancel the outer update.
2871 : : */
1591 alvherre@alvh.no-ip. 2872 [ + + ]: 60 : if (context->tmfd.cmax != estate->es_output_cid)
5020 kgrittn@postgresql.o 2873 [ + - ]: 4 : ereport(ERROR,
2874 : : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2875 : : errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2876 : : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2877 : :
2878 : : /* Else, already updated by self; nothing to do */
5767 tgl@sss.pgh.pa.us 2879 : 56 : return NULL;
2880 : :
2681 andres@anarazel.de 2881 : 2210992 : case TM_Ok:
5767 tgl@sss.pgh.pa.us 2882 : 2210992 : break;
2883 : :
2681 andres@anarazel.de 2884 : 91 : case TM_Updated:
2885 : : {
2886 : : TupleTableSlot *inputslot;
2887 : : TupleTableSlot *epqslot;
2888 : :
2889 [ + + ]: 91 : if (IsolationUsesXactSnapshot())
2890 [ + - ]: 11 : ereport(ERROR,
2891 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2892 : : errmsg("could not serialize access due to concurrent update")));
2893 : :
2894 : : /*
2895 : : * Already know that we're going to need to do EPQ, so
2896 : : * fetch tuple directly into the right slot.
2897 : : */
835 akorotkov@postgresql 2898 : 80 : inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
2899 : : resultRelInfo->ri_RangeTableIndex);
2900 : :
2901 : 80 : result = table_tuple_lock(resultRelationDesc, tupleid,
2902 : : estate->es_snapshot,
2903 : : inputslot, estate->es_output_cid,
2904 : : updateCxt.lockmode, LockWaitBlock,
2905 : : TUPLE_LOCK_FLAG_FIND_LAST_VERSION,
2906 : : &context->tmfd);
2907 : :
2908 [ + + + - ]: 78 : switch (result)
2909 : : {
2910 : 73 : case TM_Ok:
2911 [ - + ]: 73 : Assert(context->tmfd.traversed);
2912 : :
2913 : 73 : epqslot = EvalPlanQual(context->epqstate,
2914 : : resultRelationDesc,
2915 : : resultRelInfo->ri_RangeTableIndex,
2916 : : inputslot);
2917 [ + + + + ]: 73 : if (TupIsNull(epqslot))
2918 : : /* Tuple not passing quals anymore, exiting... */
2919 : 20 : return NULL;
2920 : :
2921 : : /* Make sure ri_oldTupleSlot is initialized. */
2922 [ - + ]: 53 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
835 akorotkov@postgresql 2923 :UBC 0 : ExecInitUpdateProjection(context->mtstate,
2924 : : resultRelInfo);
2925 : :
669 noah@leadboat.com 2926 [ + + ]:CBC 53 : if (resultRelInfo->ri_needLockTagTuple)
2927 : : {
2928 : 1 : UnlockTuple(resultRelationDesc,
2929 : : &lockedtid, InplaceUpdateTupleLock);
2930 : 1 : LockTuple(resultRelationDesc,
2931 : : tupleid, InplaceUpdateTupleLock);
2932 : : }
2933 : :
2934 : : /* Fetch the most recent version of old tuple. */
835 akorotkov@postgresql 2935 : 53 : oldSlot = resultRelInfo->ri_oldTupleSlot;
2936 [ - + ]: 53 : if (!table_tuple_fetch_row_version(resultRelationDesc,
2937 : : tupleid,
2938 : : SnapshotAny,
2939 : : oldSlot))
835 akorotkov@postgresql 2940 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch tuple being updated");
835 akorotkov@postgresql 2941 :CBC 53 : slot = ExecGetUpdateNewTuple(resultRelInfo,
2942 : : epqslot, oldSlot);
2943 : 53 : goto redo_act;
2944 : :
2945 : 1 : case TM_Deleted:
2946 : : /* tuple already deleted; nothing to do */
2947 : 1 : return NULL;
2948 : :
2949 : 4 : case TM_SelfModified:
2950 : :
2951 : : /*
2952 : : * This can be reached when following an update
2953 : : * chain from a tuple updated by another session,
2954 : : * reaching a tuple that was already updated in
2955 : : * this transaction. If previously modified by
2956 : : * this command, ignore the redundant update,
2957 : : * otherwise error out.
2958 : : *
2959 : : * See also TM_SelfModified response to
2960 : : * table_tuple_update() above.
2961 : : */
2962 [ + + ]: 4 : if (context->tmfd.cmax != estate->es_output_cid)
2963 [ + - ]: 1 : ereport(ERROR,
2964 : : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2965 : : errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2966 : : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2967 : 3 : return NULL;
2968 : :
835 akorotkov@postgresql 2969 :UBC 0 : default:
2970 : : /* see table_tuple_lock call in ExecDelete() */
2971 [ # # ]: 0 : elog(ERROR, "unexpected table_tuple_lock status: %u",
2972 : : result);
2973 : : return NULL;
2974 : : }
2975 : : }
2976 : :
2977 : : break;
2978 : :
2681 andres@anarazel.de 2979 :CBC 16 : case TM_Deleted:
2980 [ + + ]: 16 : if (IsolationUsesXactSnapshot())
2981 [ + - ]: 9 : ereport(ERROR,
2982 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2983 : : errmsg("could not serialize access due to concurrent delete")));
2984 : : /* tuple already deleted; nothing to do */
5767 tgl@sss.pgh.pa.us 2985 : 7 : return NULL;
2986 : :
5767 tgl@sss.pgh.pa.us 2987 :UBC 0 : default:
2620 andres@anarazel.de 2988 [ # # ]: 0 : elog(ERROR, "unrecognized table_tuple_update status: %u",
2989 : : result);
2990 : : return NULL;
2991 : : }
2992 : : }
2993 : :
5629 tgl@sss.pgh.pa.us 2994 [ + + ]:CBC 2211150 : if (canSetTag)
2995 : 2210733 : (estate->es_processed)++;
2996 : :
1591 alvherre@alvh.no-ip. 2997 : 2211150 : ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, tupleid, oldtuple,
2998 : : slot);
2999 : :
3000 : : /* Process RETURNING if present */
6132 tgl@sss.pgh.pa.us 3001 [ + + ]: 2211018 : if (resultRelInfo->ri_projectReturning)
163 dean.a.rasheed@gmail 3002 : 1515 : return ExecProcessReturning(context, resultRelInfo, false,
3003 : : oldSlot, slot, context->planSlot);
3004 : :
6132 tgl@sss.pgh.pa.us 3005 : 2209503 : return NULL;
3006 : : }
3007 : :
3008 : : /*
3009 : : * ExecOnConflictLockRow --- lock the row for ON CONFLICT DO SELECT/UPDATE
3010 : : *
3011 : : * Try to lock tuple for update as part of speculative insertion for ON
3012 : : * CONFLICT DO UPDATE or ON CONFLICT DO SELECT FOR UPDATE/SHARE.
3013 : : *
3014 : : * Returns true if the row is successfully locked, or false if the caller must
3015 : : * retry the INSERT from scratch.
3016 : : */
3017 : : static bool
163 dean.a.rasheed@gmail 3018 : 2831 : ExecOnConflictLockRow(ModifyTableContext *context,
3019 : : TupleTableSlot *existing,
3020 : : ItemPointer conflictTid,
3021 : : Relation relation,
3022 : : LockTupleMode lockmode,
3023 : : bool isUpdate)
3024 : : {
3025 : : TM_FailureData tmfd;
3026 : : TM_Result test;
3027 : : Datum xminDatum;
3028 : : TransactionId xmin;
3029 : : bool isnull;
3030 : :
3031 : : /*
3032 : : * Lock tuple with lockmode. Don't follow updates when tuple cannot be
3033 : : * locked without doing so. A row locking conflict here means our
3034 : : * previous conclusion that the tuple is conclusively committed is not
3035 : : * true anymore.
3036 : : */
2620 andres@anarazel.de 3037 : 2831 : test = table_tuple_lock(relation, conflictTid,
1591 alvherre@alvh.no-ip. 3038 : 2831 : context->estate->es_snapshot,
3039 : 2831 : existing, context->estate->es_output_cid,
3040 : : lockmode, LockWaitBlock, 0,
3041 : : &tmfd);
4096 andres@anarazel.de 3042 [ + + - + : 2831 : switch (test)
+ - ]
3043 : : {
2681 3044 : 2800 : case TM_Ok:
3045 : : /* success! */
4096 3046 : 2800 : break;
3047 : :
2681 3048 : 28 : case TM_Invisible:
3049 : :
3050 : : /*
3051 : : * This can occur when a just inserted tuple is updated again in
3052 : : * the same command. E.g. because multiple rows with the same
3053 : : * conflicting key values are inserted.
3054 : : *
3055 : : * This is somewhat similar to the ExecUpdate() TM_SelfModified
3056 : : * case. We do not want to proceed because it would lead to the
3057 : : * same row being updated a second time in some unspecified order,
3058 : : * and in contrast to plain UPDATEs there's no historical behavior
3059 : : * to break.
3060 : : *
3061 : : * It is the user's responsibility to prevent this situation from
3062 : : * occurring. These problems are why the SQL standard similarly
3063 : : * specifies that for SQL MERGE, an exception must be raised in
3064 : : * the event of an attempt to update the same row twice.
3065 : : */
3066 : 28 : xminDatum = slot_getsysattr(existing,
3067 : : MinTransactionIdAttributeNumber,
3068 : : &isnull);
3069 [ - + ]: 28 : Assert(!isnull);
3070 : 28 : xmin = DatumGetTransactionId(xminDatum);
3071 : :
3072 [ + - ]: 28 : if (TransactionIdIsCurrentTransactionId(xmin))
4096 3073 [ + - + + ]: 28 : ereport(ERROR,
3074 : : (errcode(ERRCODE_CARDINALITY_VIOLATION),
3075 : : /* translator: %s is a SQL command name */
3076 : : errmsg("%s command cannot affect row a second time",
3077 : : isUpdate ? "ON CONFLICT DO UPDATE" : "ON CONFLICT DO SELECT"),
3078 : : errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
3079 : :
3080 : : /* This shouldn't happen */
4096 andres@anarazel.de 3081 [ # # ]:UBC 0 : elog(ERROR, "attempted to lock invisible tuple");
3082 : : break;
3083 : :
2681 3084 : 0 : case TM_SelfModified:
3085 : :
3086 : : /*
3087 : : * This state should never be reached. As a dirty snapshot is used
3088 : : * to find conflicting tuples, speculative insertion wouldn't have
3089 : : * seen this row to conflict with.
3090 : : */
4096 3091 [ # # ]: 0 : elog(ERROR, "unexpected self-updated tuple");
3092 : : break;
3093 : :
2681 andres@anarazel.de 3094 :CBC 2 : case TM_Updated:
4096 3095 [ - + ]: 2 : if (IsolationUsesXactSnapshot())
4096 andres@anarazel.de 3096 [ # # ]:UBC 0 : ereport(ERROR,
3097 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3098 : : errmsg("could not serialize access due to concurrent update")));
3099 : :
3100 : : /*
3101 : : * Tell caller to try again from the very start.
3102 : : *
3103 : : * It does not make sense to use the usual EvalPlanQual() style
3104 : : * loop here, as the new version of the row might not conflict
3105 : : * anymore, or the conflicting tuple has actually been deleted.
3106 : : */
2681 andres@anarazel.de 3107 :CBC 2 : ExecClearTuple(existing);
3108 : 2 : return false;
3109 : :
3110 : 1 : case TM_Deleted:
3111 [ - + ]: 1 : if (IsolationUsesXactSnapshot())
2681 andres@anarazel.de 3112 [ # # ]:UBC 0 : ereport(ERROR,
3113 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3114 : : errmsg("could not serialize access due to concurrent delete")));
3115 : :
3116 : : /* see TM_Updated case */
2681 andres@anarazel.de 3117 :CBC 1 : ExecClearTuple(existing);
4096 3118 : 1 : return false;
3119 : :
4096 andres@anarazel.de 3120 :UBC 0 : default:
2620 3121 [ # # ]: 0 : elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
3122 : : }
3123 : :
3124 : : /* Success, the tuple is locked. */
163 dean.a.rasheed@gmail 3125 :CBC 2800 : return true;
3126 : : }
3127 : :
3128 : : /*
3129 : : * ExecOnConflictUpdate --- execute UPDATE of INSERT ON CONFLICT DO UPDATE
3130 : : *
3131 : : * Try to lock tuple for update as part of speculative insertion. If
3132 : : * a qual originating from ON CONFLICT DO UPDATE is satisfied, update
3133 : : * (but still lock row, even though it may not satisfy estate's
3134 : : * snapshot).
3135 : : *
3136 : : * Returns true if we're done (with or without an update), or false if
3137 : : * the caller must retry the INSERT from scratch.
3138 : : */
3139 : : static bool
3140 : 2761 : ExecOnConflictUpdate(ModifyTableContext *context,
3141 : : ResultRelInfo *resultRelInfo,
3142 : : ItemPointer conflictTid,
3143 : : TupleTableSlot *excludedSlot,
3144 : : bool canSetTag,
3145 : : TupleTableSlot **returning)
3146 : : {
3147 : 2761 : ModifyTableState *mtstate = context->mtstate;
3148 : 2761 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
3149 : 2761 : Relation relation = resultRelInfo->ri_RelationDesc;
3150 : 2761 : ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
3151 : 2761 : TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
3152 : : LockTupleMode lockmode;
3153 : :
3154 : : /*
3155 : : * Parse analysis should have blocked ON CONFLICT for all system
3156 : : * relations, which includes these. There's no fundamental obstacle to
3157 : : * supporting this; we'd just need to handle LOCKTAG_TUPLE like the other
3158 : : * ExecUpdate() caller.
3159 : : */
3160 [ - + ]: 2761 : Assert(!resultRelInfo->ri_needLockTagTuple);
3161 : :
3162 : : /* Determine lock mode to use */
3163 : 2761 : lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
3164 : :
3165 : : /* Lock tuple for update */
3166 [ + + ]: 2761 : if (!ExecOnConflictLockRow(context, existing, conflictTid,
3167 : : resultRelInfo->ri_RelationDesc, lockmode, true))
3168 : 3 : return false;
3169 : :
3170 : : /*
3171 : : * Verify that the tuple is visible to our MVCC snapshot if the current
3172 : : * isolation level mandates that.
3173 : : *
3174 : : * It's not sufficient to rely on the check within ExecUpdate() as e.g.
3175 : : * CONFLICT ... WHERE clause may prevent us from reaching that.
3176 : : *
3177 : : * This means we only ever continue when a new command in the current
3178 : : * transaction could see the row, even though in READ COMMITTED mode the
3179 : : * tuple will not be visible according to the current statement's
3180 : : * snapshot. This is in line with the way UPDATE deals with newer tuple
3181 : : * versions.
3182 : : */
1591 alvherre@alvh.no-ip. 3183 : 2742 : ExecCheckTupleVisible(context->estate, relation, existing);
3184 : :
3185 : : /*
3186 : : * Make tuple and any needed join variables available to ExecQual and
3187 : : * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
3188 : : * the target's existing tuple is installed in the scantuple. EXCLUDED
3189 : : * has been made to reference INNER_VAR in setrefs.c, but there is no
3190 : : * other redirection.
3191 : : */
2698 andres@anarazel.de 3192 : 2742 : econtext->ecxt_scantuple = existing;
4096 3193 : 2742 : econtext->ecxt_innertuple = excludedSlot;
3194 : 2742 : econtext->ecxt_outertuple = NULL;
3195 : :
3420 3196 [ + + ]: 2742 : if (!ExecQual(onConflictSetWhere, econtext))
3197 : : {
2698 3198 : 21 : ExecClearTuple(existing); /* see return below */
4096 3199 [ - + ]: 21 : InstrCountFiltered1(&mtstate->ps, 1);
3200 : 21 : return true; /* done with the tuple */
3201 : : }
3202 : :
3203 [ + + ]: 2721 : if (resultRelInfo->ri_WithCheckOptions != NIL)
3204 : : {
3205 : : /*
3206 : : * Check target's existing tuple against UPDATE-applicable USING
3207 : : * security barrier quals (if any), enforced here as RLS checks/WCOs.
3208 : : *
3209 : : * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
3210 : : * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK.
3211 : : * Since SELECT permission on the target table is always required for
3212 : : * INSERT ... ON CONFLICT DO UPDATE, the rewriter also adds SELECT RLS
3213 : : * checks/WCOs for SELECT security quals, using WCOs of the same kind,
3214 : : * and this check enforces them too.
3215 : : *
3216 : : * The rewriter will also have associated UPDATE-applicable straight
3217 : : * RLS checks/WCOs for the benefit of the ExecUpdate() call that
3218 : : * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
3219 : : * kinds, so there is no danger of spurious over-enforcement in the
3220 : : * INSERT or UPDATE path.
3221 : : */
3222 : 48 : ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo,
3223 : : existing,
3224 : : mtstate->ps.state);
3225 : : }
3226 : :
3227 : : /* Project the new tuple version */
3043 alvherre@alvh.no-ip. 3228 : 2705 : ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
3229 : :
3230 : : /*
3231 : : * Note that it is possible that the target tuple has been modified in
3232 : : * this session, after the above table_tuple_lock. We choose to not error
3233 : : * out in that case, in line with ExecUpdate's treatment of similar cases.
3234 : : * This can happen if an UPDATE is triggered from within ExecQual(),
3235 : : * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
3236 : : * wCTE in the ON CONFLICT's SET.
3237 : : */
3238 : :
3239 : : /* Execute UPDATE with projection */
1591 3240 : 5390 : *returning = ExecUpdate(context, resultRelInfo,
3241 : : conflictTid, NULL, existing,
2698 andres@anarazel.de 3242 : 2705 : resultRelInfo->ri_onConflict->oc_ProjSlot,
3243 : : canSetTag);
3244 : :
3245 : : /*
3246 : : * Clear out existing tuple, as there might not be another conflict among
3247 : : * the next input rows. Don't want to hold resources till the end of the
3248 : : * query. First though, make sure that the returning slot, if any, has a
3249 : : * local copy of any OLD pass-by-reference values, if it refers to any OLD
3250 : : * columns.
3251 : : */
555 dean.a.rasheed@gmail 3252 [ + + ]: 2685 : if (*returning != NULL &&
3253 [ + + ]: 174 : resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD)
3254 : 12 : ExecMaterializeSlot(*returning);
3255 : :
2698 andres@anarazel.de 3256 : 2685 : ExecClearTuple(existing);
3257 : :
4096 3258 : 2685 : return true;
3259 : : }
3260 : :
3261 : : /*
3262 : : * ExecOnConflictSelect --- execute SELECT of INSERT ON CONFLICT DO SELECT
3263 : : *
3264 : : * If SELECT FOR UPDATE/SHARE is specified, try to lock tuple as part of
3265 : : * speculative insertion. If a qual originating from ON CONFLICT DO SELECT is
3266 : : * satisfied, select (but still lock row, even though it may not satisfy
3267 : : * estate's snapshot).
3268 : : *
3269 : : * Returns true if we're done (with or without a select), or false if the
3270 : : * caller must retry the INSERT from scratch.
3271 : : */
3272 : : static bool
163 dean.a.rasheed@gmail 3273 : 192 : ExecOnConflictSelect(ModifyTableContext *context,
3274 : : ResultRelInfo *resultRelInfo,
3275 : : ItemPointer conflictTid,
3276 : : TupleTableSlot *excludedSlot,
3277 : : bool canSetTag,
3278 : : TupleTableSlot **returning)
3279 : : {
3280 : 192 : ModifyTableState *mtstate = context->mtstate;
3281 : 192 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
3282 : 192 : Relation relation = resultRelInfo->ri_RelationDesc;
3283 : 192 : ExprState *onConflictSelectWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
3284 : 192 : TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
3285 : 192 : LockClauseStrength lockStrength = resultRelInfo->ri_onConflict->oc_LockStrength;
3286 : :
3287 : : /*
3288 : : * Parse analysis should have blocked ON CONFLICT for all system
3289 : : * relations, which includes these. There's no fundamental obstacle to
3290 : : * supporting this; we'd just need to handle LOCKTAG_TUPLE appropriately.
3291 : : */
3292 [ - + ]: 192 : Assert(!resultRelInfo->ri_needLockTagTuple);
3293 : :
3294 : : /* Fetch/lock existing tuple, according to the requested lock strength */
3295 [ + + ]: 192 : if (lockStrength == LCS_NONE)
3296 : : {
3297 [ - + ]: 122 : if (!table_tuple_fetch_row_version(relation,
3298 : : conflictTid,
3299 : : SnapshotAny,
3300 : : existing))
163 dean.a.rasheed@gmail 3301 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
3302 : : }
3303 : : else
3304 : : {
3305 : : LockTupleMode lockmode;
3306 : :
163 dean.a.rasheed@gmail 3307 [ + + + + :CBC 70 : switch (lockStrength)
- ]
3308 : : {
3309 : 1 : case LCS_FORKEYSHARE:
3310 : 1 : lockmode = LockTupleKeyShare;
3311 : 1 : break;
3312 : 1 : case LCS_FORSHARE:
3313 : 1 : lockmode = LockTupleShare;
3314 : 1 : break;
3315 : 1 : case LCS_FORNOKEYUPDATE:
3316 : 1 : lockmode = LockTupleNoKeyExclusive;
3317 : 1 : break;
3318 : 67 : case LCS_FORUPDATE:
3319 : 67 : lockmode = LockTupleExclusive;
3320 : 67 : break;
163 dean.a.rasheed@gmail 3321 :UBC 0 : default:
3322 [ # # ]: 0 : elog(ERROR, "Unexpected lock strength %d", (int) lockStrength);
3323 : : }
3324 : :
163 dean.a.rasheed@gmail 3325 [ - + ]:CBC 70 : if (!ExecOnConflictLockRow(context, existing, conflictTid,
3326 : : resultRelInfo->ri_RelationDesc, lockmode, false))
163 dean.a.rasheed@gmail 3327 :UBC 0 : return false;
3328 : : }
3329 : :
3330 : : /*
3331 : : * Verify that the tuple is visible to our MVCC snapshot if the current
3332 : : * isolation level mandates that. See comments in ExecOnConflictUpdate().
3333 : : */
163 dean.a.rasheed@gmail 3334 :CBC 180 : ExecCheckTupleVisible(context->estate, relation, existing);
3335 : :
3336 : : /*
3337 : : * Make tuple and any needed join variables available to ExecQual. The
3338 : : * EXCLUDED tuple is installed in ecxt_innertuple, while the target's
3339 : : * existing tuple is installed in the scantuple. EXCLUDED has been made
3340 : : * to reference INNER_VAR in setrefs.c, but there is no other redirection.
3341 : : */
3342 : 180 : econtext->ecxt_scantuple = existing;
3343 : 180 : econtext->ecxt_innertuple = excludedSlot;
3344 : 180 : econtext->ecxt_outertuple = NULL;
3345 : :
3346 [ + + ]: 180 : if (!ExecQual(onConflictSelectWhere, econtext))
3347 : : {
3348 : 24 : ExecClearTuple(existing); /* see return below */
3349 [ - + ]: 24 : InstrCountFiltered1(&mtstate->ps, 1);
3350 : 24 : return true; /* done with the tuple */
3351 : : }
3352 : :
3353 [ + + ]: 156 : if (resultRelInfo->ri_WithCheckOptions != NIL)
3354 : : {
3355 : : /*
3356 : : * Check target's existing tuple against SELECT-applicable USING
3357 : : * security barrier quals (if any), enforced here as RLS checks/WCOs.
3358 : : *
3359 : : * The rewriter creates WCOs from the USING quals of SELECT policies,
3360 : : * and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK. If FOR
3361 : : * UPDATE/SHARE was specified, UPDATE permissions are required on the
3362 : : * target table, and the rewriter also adds WCOs built from the USING
3363 : : * quals of UPDATE policies, using WCOs of the same kind, and this
3364 : : * check enforces them too.
3365 : : */
3366 : 24 : ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo,
3367 : : existing,
3368 : : mtstate->ps.state);
3369 : : }
3370 : :
3371 : : /* RETURNING is required for DO SELECT */
3372 [ - + ]: 152 : Assert(resultRelInfo->ri_projectReturning);
3373 : :
3374 : 152 : *returning = ExecProcessReturning(context, resultRelInfo, false,
3375 : : existing, existing, context->planSlot);
3376 : :
3377 [ + - ]: 152 : if (canSetTag)
3378 : 152 : context->estate->es_processed++;
3379 : :
3380 : : /*
3381 : : * Before releasing the existing tuple, make sure that the returning slot
3382 : : * has a local copy of any pass-by-reference values.
3383 : : */
3384 : 152 : ExecMaterializeSlot(*returning);
3385 : :
3386 : : /*
3387 : : * Clear out existing tuple, as there might not be another conflict among
3388 : : * the next input rows. Don't want to hold resources till the end of the
3389 : : * query.
3390 : : */
3391 : 152 : ExecClearTuple(existing);
3392 : :
3393 : 152 : return true;
3394 : : }
3395 : :
3396 : : /*
3397 : : * Perform MERGE.
3398 : : */
3399 : : static TupleTableSlot *
1580 alvherre@alvh.no-ip. 3400 : 10553 : ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
3401 : : ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
3402 : : {
860 dean.a.rasheed@gmail 3403 : 10553 : TupleTableSlot *rslot = NULL;
3404 : : bool matched;
3405 : :
3406 : : /*-----
3407 : : * If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
3408 : : * valid, depending on whether the result relation is a table or a view.
3409 : : * We execute the first action for which the additional WHEN MATCHED AND
3410 : : * quals pass. If an action without quals is found, that action is
3411 : : * executed.
3412 : : *
3413 : : * Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
3414 : : * is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
3415 : : * in sequence until one passes. This is almost identical to the WHEN
3416 : : * MATCHED case, and both cases are handled by ExecMergeMatched().
3417 : : *
3418 : : * Finally, in the WHEN NOT MATCHED [BY TARGET] case, both tupleid and
3419 : : * oldtuple are invalid, and we look at the given WHEN NOT MATCHED [BY
3420 : : * TARGET] actions in sequence until one passes.
3421 : : *
3422 : : * Things get interesting in case of concurrent update/delete of the
3423 : : * target tuple. Such concurrent update/delete is detected while we are
3424 : : * executing a WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action.
3425 : : *
3426 : : * A concurrent update can:
3427 : : *
3428 : : * 1. modify the target tuple so that the results from checking any
3429 : : * additional quals attached to WHEN MATCHED or WHEN NOT MATCHED BY
3430 : : * SOURCE actions potentially change, but the result from the join
3431 : : * quals does not change.
3432 : : *
3433 : : * In this case, we are still dealing with the same kind of match
3434 : : * (MATCHED or NOT MATCHED BY SOURCE). We recheck the same list of
3435 : : * actions from the start and choose the first one that satisfies the
3436 : : * new target tuple.
3437 : : *
3438 : : * 2. modify the target tuple in the WHEN MATCHED case so that the join
3439 : : * quals no longer pass and hence the source and target tuples no
3440 : : * longer match.
3441 : : *
3442 : : * In this case, we are now dealing with a NOT MATCHED case, and we
3443 : : * process both WHEN NOT MATCHED BY SOURCE and WHEN NOT MATCHED [BY
3444 : : * TARGET] actions. First ExecMergeMatched() processes the list of
3445 : : * WHEN NOT MATCHED BY SOURCE actions in sequence until one passes,
3446 : : * then ExecMergeNotMatched() processes any WHEN NOT MATCHED [BY
3447 : : * TARGET] actions in sequence until one passes. Thus we may execute
3448 : : * two actions; one of each kind.
3449 : : *
3450 : : * Thus we support concurrent updates that turn MATCHED candidate rows
3451 : : * into NOT MATCHED rows. However, we do not attempt to support cases
3452 : : * that would turn NOT MATCHED rows into MATCHED rows, or which would
3453 : : * cause a target row to match a different source row.
3454 : : *
3455 : : * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED
3456 : : * [BY TARGET].
3457 : : *
3458 : : * ExecMergeMatched() takes care of following the update chain and
3459 : : * re-finding the qualifying WHEN MATCHED or WHEN NOT MATCHED BY SOURCE
3460 : : * action, as long as the target tuple still exists. If the target tuple
3461 : : * gets deleted or a concurrent update causes the join quals to fail, it
3462 : : * returns a matched status of false and we call ExecMergeNotMatched().
3463 : : * Given that ExecMergeMatched() always makes progress by following the
3464 : : * update chain and we never switch from ExecMergeNotMatched() to
3465 : : * ExecMergeMatched(), there is no risk of a livelock.
3466 : : */
877 3467 [ + + + + ]: 10553 : matched = tupleid != NULL || oldtuple != NULL;
1580 alvherre@alvh.no-ip. 3468 [ + + ]: 10553 : if (matched)
860 dean.a.rasheed@gmail 3469 : 8762 : rslot = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
3470 : : canSetTag, &matched);
3471 : :
3472 : : /*
3473 : : * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
3474 : : * join, or a previously MATCHED tuple for which ExecMergeMatched() set
3475 : : * "matched" to false, indicating that it no longer matches).
3476 : : */
1580 alvherre@alvh.no-ip. 3477 [ + + ]: 10491 : if (!matched)
3478 : : {
3479 : : /*
3480 : : * If a concurrent update turned a MATCHED case into a NOT MATCHED
3481 : : * case, and we have both WHEN NOT MATCHED BY SOURCE and WHEN NOT
3482 : : * MATCHED [BY TARGET] actions, and there is a RETURNING clause,
3483 : : * ExecMergeMatched() may have already executed a WHEN NOT MATCHED BY
3484 : : * SOURCE action, and computed the row to return. If so, we cannot
3485 : : * execute a WHEN NOT MATCHED [BY TARGET] action now, so mark it as
3486 : : * pending (to be processed on the next call to ExecModifyTable()).
3487 : : * Otherwise, just process the action now.
3488 : : */
847 dean.a.rasheed@gmail 3489 [ + + ]: 1800 : if (rslot == NULL)
3490 : 1798 : rslot = ExecMergeNotMatched(context, resultRelInfo, canSetTag);
3491 : : else
3492 : 2 : context->mtstate->mt_merge_pending_not_matched = context->planSlot;
3493 : : }
3494 : :
860 3495 : 10452 : return rslot;
3496 : : }
3497 : :
3498 : : /*
3499 : : * Check and execute the first qualifying MATCHED or NOT MATCHED BY SOURCE
3500 : : * action, depending on whether the join quals are satisfied. If the target
3501 : : * relation is a table, the current target tuple is identified by tupleid.
3502 : : * Otherwise, if the target relation is a view, oldtuple is the current target
3503 : : * tuple from the view.
3504 : : *
3505 : : * We start from the first WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action
3506 : : * and check if the WHEN quals pass, if any. If the WHEN quals for the first
3507 : : * action do not pass, we check the second, then the third and so on. If we
3508 : : * reach the end without finding a qualifying action, we return NULL.
3509 : : * Otherwise, we execute the qualifying action and return its RETURNING
3510 : : * result, if any, or NULL.
3511 : : *
3512 : : * On entry, "*matched" is assumed to be true. If a concurrent update or
3513 : : * delete is detected that causes the join quals to no longer pass, we set it
3514 : : * to false, indicating that the caller should process any NOT MATCHED [BY
3515 : : * TARGET] actions.
3516 : : *
3517 : : * After a concurrent update, we restart from the first action to look for a
3518 : : * new qualifying action to execute. If the join quals originally passed, and
3519 : : * the concurrent update caused them to no longer pass, then we switch from
3520 : : * the MATCHED to the NOT MATCHED BY SOURCE list of actions before restarting
3521 : : * (and setting "*matched" to false). As a result we may execute a WHEN NOT
3522 : : * MATCHED BY SOURCE action, and set "*matched" to false, causing the caller
3523 : : * to also execute a WHEN NOT MATCHED [BY TARGET] action.
3524 : : */
3525 : : static TupleTableSlot *
1580 alvherre@alvh.no-ip. 3526 : 8762 : ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
3527 : : ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag,
3528 : : bool *matched)
3529 : : {
3530 : 8762 : ModifyTableState *mtstate = context->mtstate;
847 dean.a.rasheed@gmail 3531 : 8762 : List **mergeActions = resultRelInfo->ri_MergeActions;
3532 : : ItemPointerData lockedtid;
3533 : : List *actionStates;
860 3534 : 8762 : TupleTableSlot *newslot = NULL;
3535 : 8762 : TupleTableSlot *rslot = NULL;
1580 alvherre@alvh.no-ip. 3536 : 8762 : EState *estate = context->estate;
3537 : 8762 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
3538 : : bool isNull;
3539 : 8762 : EPQState *epqstate = &mtstate->mt_epqstate;
3540 : : ListCell *l;
3541 : :
3542 : : /* Expect matched to be true on entry */
847 dean.a.rasheed@gmail 3543 [ - + ]: 8762 : Assert(*matched);
3544 : :
3545 : : /*
3546 : : * If there are no WHEN MATCHED or WHEN NOT MATCHED BY SOURCE actions, we
3547 : : * are done.
3548 : : */
3549 [ + + ]: 8762 : if (mergeActions[MERGE_WHEN_MATCHED] == NIL &&
3550 [ + + ]: 780 : mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] == NIL)
860 3551 : 332 : return NULL;
3552 : :
3553 : : /*
3554 : : * Make tuple and any needed join variables available to ExecQual and
3555 : : * ExecProject. The target's existing tuple is installed in the scantuple.
3556 : : * This target relation's slot is required only in the case of a MATCHED
3557 : : * or NOT MATCHED BY SOURCE tuple and UPDATE/DELETE actions.
3558 : : */
1580 alvherre@alvh.no-ip. 3559 : 8430 : econtext->ecxt_scantuple = resultRelInfo->ri_oldTupleSlot;
3560 : 8430 : econtext->ecxt_innertuple = context->planSlot;
3561 : 8430 : econtext->ecxt_outertuple = NULL;
3562 : :
3563 : : /*
3564 : : * This routine is only invoked for matched target rows, so we should
3565 : : * either have the tupleid of the target row, or an old tuple from the
3566 : : * target wholerow junk attr.
3567 : : */
877 dean.a.rasheed@gmail 3568 [ + + - + ]: 8430 : Assert(tupleid != NULL || oldtuple != NULL);
669 noah@leadboat.com 3569 : 8430 : ItemPointerSetInvalid(&lockedtid);
877 dean.a.rasheed@gmail 3570 [ + + ]: 8430 : if (oldtuple != NULL)
3571 : : {
669 noah@leadboat.com 3572 [ - + ]: 64 : Assert(!resultRelInfo->ri_needLockTagTuple);
877 dean.a.rasheed@gmail 3573 : 64 : ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
3574 : : false);
3575 : : }
3576 : : else
3577 : : {
669 noah@leadboat.com 3578 [ + + ]: 8366 : if (resultRelInfo->ri_needLockTagTuple)
3579 : : {
3580 : : /*
3581 : : * This locks even for CMD_DELETE, for CMD_NOTHING, and for tuples
3582 : : * that don't match mas_whenqual. MERGE on system catalogs is a
3583 : : * minor use case, so don't bother optimizing those.
3584 : : */
3585 : 5746 : LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
3586 : : InplaceUpdateTupleLock);
3587 : 5746 : lockedtid = *tupleid;
3588 : : }
3589 [ - + ]: 8366 : if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
3590 : : tupleid,
3591 : : SnapshotAny,
3592 : : resultRelInfo->ri_oldTupleSlot))
669 noah@leadboat.com 3593 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch the target tuple");
3594 : : }
3595 : :
3596 : : /*
3597 : : * Test the join condition. If it's satisfied, perform a MATCHED action.
3598 : : * Otherwise, perform a NOT MATCHED BY SOURCE action.
3599 : : *
3600 : : * Note that this join condition will be NULL if there are no NOT MATCHED
3601 : : * BY SOURCE actions --- see transform_MERGE_to_join(). In that case, we
3602 : : * need only consider MATCHED actions here.
3603 : : */
847 dean.a.rasheed@gmail 3604 [ + + ]:CBC 8430 : if (ExecQual(resultRelInfo->ri_MergeJoinCondition, econtext))
3605 : 8308 : actionStates = mergeActions[MERGE_WHEN_MATCHED];
3606 : : else
3607 : 122 : actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
3608 : :
3609 : 8430 : lmerge_matched:
3610 : :
3611 [ + + + + : 15208 : foreach(l, actionStates)
+ + ]
3612 : : {
1580 alvherre@alvh.no-ip. 3613 : 8538 : MergeActionState *relaction = (MergeActionState *) lfirst(l);
3614 : 8538 : CmdType commandType = relaction->mas_action->commandType;
3615 : : TM_Result result;
3616 : 8538 : UpdateContext updateCxt = {0};
3617 : :
3618 : : /*
3619 : : * Test condition, if any.
3620 : : *
3621 : : * In the absence of any condition, we perform the action
3622 : : * unconditionally (no need to check separately since ExecQual() will
3623 : : * return true if there are no conditions to evaluate).
3624 : : */
3625 [ + + ]: 8538 : if (!ExecQual(relaction->mas_whenqual, econtext))
3626 : 6735 : continue;
3627 : :
3628 : : /*
3629 : : * Check if the existing target tuple meets the USING checks of
3630 : : * UPDATE/DELETE RLS policies. If those checks fail, we throw an
3631 : : * error.
3632 : : *
3633 : : * The WITH CHECK quals for UPDATE RLS policies are applied in
3634 : : * ExecUpdateAct() and hence we need not do anything special to handle
3635 : : * them.
3636 : : *
3637 : : * NOTE: We must do this after WHEN quals are evaluated, so that we
3638 : : * check policies only when they matter.
3639 : : */
1083 dean.a.rasheed@gmail 3640 [ + + + + ]: 1803 : if (resultRelInfo->ri_WithCheckOptions && commandType != CMD_NOTHING)
3641 : : {
1580 alvherre@alvh.no-ip. 3642 : 76 : ExecWithCheckOptions(commandType == CMD_UPDATE ?
3643 : : WCO_RLS_MERGE_UPDATE_CHECK : WCO_RLS_MERGE_DELETE_CHECK,
3644 : : resultRelInfo,
3645 : : resultRelInfo->ri_oldTupleSlot,
3646 [ + + ]: 76 : context->mtstate->ps.state);
3647 : : }
3648 : :
3649 : : /* Perform stated action */
3650 [ + + + - ]: 1787 : switch (commandType)
3651 : : {
3652 : 1416 : case CMD_UPDATE:
3653 : :
3654 : : /*
3655 : : * Project the output tuple, and use that to update the table.
3656 : : * We don't need to filter out junk attributes, because the
3657 : : * UPDATE action's targetlist doesn't have any.
3658 : : */
3659 : 1416 : newslot = ExecProject(relaction->mas_proj);
3660 : :
860 dean.a.rasheed@gmail 3661 : 1416 : mtstate->mt_merge_action = relaction;
1580 alvherre@alvh.no-ip. 3662 [ + + ]: 1416 : if (!ExecUpdatePrologue(context, resultRelInfo,
3663 : : tupleid, NULL, newslot, &result))
3664 : : {
1230 dean.a.rasheed@gmail 3665 [ + + ]: 12 : if (result == TM_Ok)
669 noah@leadboat.com 3666 : 102 : goto out; /* "do nothing" */
3667 : :
1230 dean.a.rasheed@gmail 3668 : 8 : break; /* concurrent update/delete */
3669 : : }
3670 : :
3671 : : /* INSTEAD OF ROW UPDATE Triggers */
877 3672 [ + + ]: 1404 : if (resultRelInfo->ri_TrigDesc &&
3673 [ + + ]: 230 : resultRelInfo->ri_TrigDesc->trig_update_instead_row)
3674 : : {
3675 [ - + ]: 52 : if (!ExecIRUpdateTriggers(estate, resultRelInfo,
3676 : : oldtuple, newslot))
669 noah@leadboat.com 3677 :UBC 0 : goto out; /* "do nothing" */
3678 : : }
3679 : : else
3680 : : {
3681 : : /* checked ri_needLockTagTuple above */
742 noah@leadboat.com 3682 [ - + ]:CBC 1352 : Assert(oldtuple == NULL);
3683 : :
877 dean.a.rasheed@gmail 3684 : 1352 : result = ExecUpdateAct(context, resultRelInfo, tupleid,
3685 : : NULL, newslot, canSetTag,
3686 : : &updateCxt);
3687 : :
3688 : : /*
3689 : : * As in ExecUpdate(), if ExecUpdateAct() reports that a
3690 : : * cross-partition update was done, then there's nothing
3691 : : * else for us to do --- the UPDATE has been turned into a
3692 : : * DELETE and an INSERT, and we must not perform any of
3693 : : * the usual post-update tasks. Also, the RETURNING tuple
3694 : : * (if any) has been projected, so we can just return
3695 : : * that.
3696 : : */
3697 [ + + ]: 1337 : if (updateCxt.crossPartUpdate)
3698 : : {
3699 : 89 : mtstate->mt_merge_updated += 1;
669 noah@leadboat.com 3700 : 89 : rslot = context->cpUpdateReturningSlot;
3701 : 89 : goto out;
3702 : : }
3703 : : }
3704 : :
877 dean.a.rasheed@gmail 3705 [ + + ]: 1300 : if (result == TM_Ok)
3706 : : {
1580 alvherre@alvh.no-ip. 3707 : 1251 : ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
3708 : : tupleid, NULL, newslot);
3709 : 1243 : mtstate->mt_merge_updated += 1;
3710 : : }
3711 : 1292 : break;
3712 : :
3713 : 351 : case CMD_DELETE:
860 dean.a.rasheed@gmail 3714 : 351 : mtstate->mt_merge_action = relaction;
1580 alvherre@alvh.no-ip. 3715 [ + + ]: 351 : if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
3716 : : NULL, NULL, &result))
3717 : : {
1230 dean.a.rasheed@gmail 3718 [ + + ]: 7 : if (result == TM_Ok)
669 noah@leadboat.com 3719 : 4 : goto out; /* "do nothing" */
3720 : :
1230 dean.a.rasheed@gmail 3721 : 3 : break; /* concurrent update/delete */
3722 : : }
3723 : :
3724 : : /* INSTEAD OF ROW DELETE Triggers */
877 3725 [ + + ]: 344 : if (resultRelInfo->ri_TrigDesc &&
3726 [ + + ]: 38 : resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
3727 : : {
3728 [ - + ]: 4 : if (!ExecIRDeleteTriggers(estate, resultRelInfo,
3729 : : oldtuple))
669 noah@leadboat.com 3730 :UBC 0 : goto out; /* "do nothing" */
3731 : : }
3732 : : else
3733 : : {
3734 : : /* checked ri_needLockTagTuple above */
742 noah@leadboat.com 3735 [ - + ]:CBC 340 : Assert(oldtuple == NULL);
3736 : :
877 dean.a.rasheed@gmail 3737 : 340 : result = ExecDeleteAct(context, resultRelInfo, tupleid,
3738 : : false);
3739 : : }
3740 : :
1580 alvherre@alvh.no-ip. 3741 [ + + ]: 344 : if (result == TM_Ok)
3742 : : {
3743 : 333 : ExecDeleteEpilogue(context, resultRelInfo, tupleid, NULL,
3744 : : false);
3745 : 333 : mtstate->mt_merge_deleted += 1;
3746 : : }
3747 : 344 : break;
3748 : :
3749 : 20 : case CMD_NOTHING:
3750 : : /* Doing nothing is always OK */
3751 : 20 : result = TM_Ok;
3752 : 20 : break;
3753 : :
1580 alvherre@alvh.no-ip. 3754 :UBC 0 : default:
847 dean.a.rasheed@gmail 3755 [ # # ]: 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
3756 : : }
3757 : :
1580 alvherre@alvh.no-ip. 3758 [ + + + + :CBC 1667 : switch (result)
- - ]
3759 : : {
3760 : 1596 : case TM_Ok:
3761 : : /* all good; perform final actions */
1346 3762 [ + + + + ]: 1596 : if (canSetTag && commandType != CMD_NOTHING)
1580 3763 : 1561 : (estate->es_processed)++;
3764 : :
3765 : 1596 : break;
3766 : :
3767 : 21 : case TM_SelfModified:
3768 : :
3769 : : /*
3770 : : * The target tuple was already updated or deleted by the
3771 : : * current command, or by a later command in the current
3772 : : * transaction. The former case is explicitly disallowed by
3773 : : * the SQL standard for MERGE, which insists that the MERGE
3774 : : * join condition should not join a target row to more than
3775 : : * one source row.
3776 : : *
3777 : : * The latter case arises if the tuple is modified by a
3778 : : * command in a BEFORE trigger, or perhaps by a command in a
3779 : : * volatile function used in the query. In such situations we
3780 : : * should not ignore the MERGE action, but it is equally
3781 : : * unsafe to proceed. We don't want to discard the original
3782 : : * MERGE action while keeping the triggered actions based on
3783 : : * it; and it would be no better to allow the original MERGE
3784 : : * action while discarding the updates that it triggered. So
3785 : : * throwing an error is the only safe course.
3786 : : */
870 dean.a.rasheed@gmail 3787 [ + + ]: 21 : if (context->tmfd.cmax != estate->es_output_cid)
3788 [ + - ]: 8 : ereport(ERROR,
3789 : : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3790 : : errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3791 : : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3792 : :
1580 alvherre@alvh.no-ip. 3793 [ + - ]: 13 : if (TransactionIdIsCurrentTransactionId(context->tmfd.xmax))
3794 [ + - ]: 13 : ereport(ERROR,
3795 : : (errcode(ERRCODE_CARDINALITY_VIOLATION),
3796 : : /* translator: %s is a SQL command name */
3797 : : errmsg("%s command cannot affect row a second time",
3798 : : "MERGE"),
3799 : : errhint("Ensure that not more than one source row matches any one target row.")));
3800 : :
3801 : : /* This shouldn't happen */
1580 alvherre@alvh.no-ip. 3802 [ # # ]:UBC 0 : elog(ERROR, "attempted to update or delete invisible tuple");
3803 : : break;
3804 : :
1580 alvherre@alvh.no-ip. 3805 :CBC 5 : case TM_Deleted:
3806 [ - + ]: 5 : if (IsolationUsesXactSnapshot())
1580 alvherre@alvh.no-ip. 3807 [ # # ]:UBC 0 : ereport(ERROR,
3808 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3809 : : errmsg("could not serialize access due to concurrent delete")));
3810 : :
3811 : : /*
3812 : : * If the tuple was already deleted, set matched to false to
3813 : : * let caller handle it under NOT MATCHED [BY TARGET] clauses.
3814 : : */
860 dean.a.rasheed@gmail 3815 :CBC 5 : *matched = false;
669 noah@leadboat.com 3816 : 5 : goto out;
3817 : :
1580 alvherre@alvh.no-ip. 3818 : 45 : case TM_Updated:
3819 : : {
3820 : : bool was_matched;
3821 : : Relation resultRelationDesc;
3822 : : TupleTableSlot *epqslot,
3823 : : *inputslot;
3824 : : LockTupleMode lockmode;
3825 : :
142 akorotkov@postgresql 3826 [ + + ]: 45 : if (IsolationUsesXactSnapshot())
3827 [ + - ]: 1 : ereport(ERROR,
3828 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3829 : : errmsg("could not serialize access due to concurrent update")));
3830 : :
3831 : : /*
3832 : : * The target tuple was concurrently updated by some other
3833 : : * transaction. If we are currently processing a MATCHED
3834 : : * action, use EvalPlanQual() with the new version of the
3835 : : * tuple and recheck the join qual, to detect a change
3836 : : * from the MATCHED to the NOT MATCHED cases. If we are
3837 : : * already processing a NOT MATCHED BY SOURCE action, we
3838 : : * skip this (cannot switch from NOT MATCHED BY SOURCE to
3839 : : * MATCHED).
3840 : : */
847 dean.a.rasheed@gmail 3841 : 44 : was_matched = relaction->mas_action->matchKind == MERGE_WHEN_MATCHED;
1580 alvherre@alvh.no-ip. 3842 : 44 : resultRelationDesc = resultRelInfo->ri_RelationDesc;
3843 : 44 : lockmode = ExecUpdateLockMode(estate, resultRelInfo);
3844 : :
847 dean.a.rasheed@gmail 3845 [ + - ]: 44 : if (was_matched)
3846 : 44 : inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
3847 : : resultRelInfo->ri_RangeTableIndex);
3848 : : else
847 dean.a.rasheed@gmail 3849 :UBC 0 : inputslot = resultRelInfo->ri_oldTupleSlot;
3850 : :
1580 alvherre@alvh.no-ip. 3851 :CBC 44 : result = table_tuple_lock(resultRelationDesc, tupleid,
3852 : : estate->es_snapshot,
3853 : : inputslot, estate->es_output_cid,
3854 : : lockmode, LockWaitBlock,
3855 : : TUPLE_LOCK_FLAG_FIND_LAST_VERSION,
3856 : : &context->tmfd);
3857 [ + - + - ]: 44 : switch (result)
3858 : : {
3859 : 43 : case TM_Ok:
3860 : :
3861 : : /*
3862 : : * If the tuple was updated and migrated to
3863 : : * another partition concurrently, the current
3864 : : * MERGE implementation can't follow. There's
3865 : : * probably a better way to handle this case, but
3866 : : * it'd require recognizing the relation to which
3867 : : * the tuple moved, and setting our current
3868 : : * resultRelInfo to that.
3869 : : */
323 dean.a.rasheed@gmail 3870 [ - + ]: 43 : if (ItemPointerIndicatesMovedPartitions(tupleid))
1580 alvherre@alvh.no-ip. 3871 [ # # ]:UBC 0 : ereport(ERROR,
3872 : : (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
3873 : : errmsg("tuple to be merged was already moved to another partition due to concurrent update")));
3874 : :
3875 : : /*
3876 : : * If this was a MATCHED case, use EvalPlanQual()
3877 : : * to recheck the join condition.
3878 : : */
847 dean.a.rasheed@gmail 3879 [ + - ]:CBC 43 : if (was_matched)
3880 : : {
3881 : 43 : epqslot = EvalPlanQual(epqstate,
3882 : : resultRelationDesc,
3883 : : resultRelInfo->ri_RangeTableIndex,
3884 : : inputslot);
3885 : :
3886 : : /*
3887 : : * If the subplan didn't return a tuple, then
3888 : : * we must be dealing with an inner join for
3889 : : * which the join condition no longer matches.
3890 : : * This can only happen if there are no NOT
3891 : : * MATCHED actions, and so there is nothing
3892 : : * more to do.
3893 : : */
3894 [ + - - + ]: 43 : if (TupIsNull(epqslot))
669 noah@leadboat.com 3895 :UBC 0 : goto out;
3896 : :
3897 : : /*
3898 : : * If we got a NULL ctid from the subplan, the
3899 : : * join quals no longer pass and we switch to
3900 : : * the NOT MATCHED BY SOURCE case.
3901 : : */
847 dean.a.rasheed@gmail 3902 :CBC 43 : (void) ExecGetJunkAttribute(epqslot,
3903 : 43 : resultRelInfo->ri_RowIdAttNo,
3904 : : &isNull);
3905 [ + + ]: 43 : if (isNull)
3906 : 2 : *matched = false;
3907 : :
3908 : : /*
3909 : : * Otherwise, recheck the join quals to see if
3910 : : * we need to switch to the NOT MATCHED BY
3911 : : * SOURCE case.
3912 : : */
669 noah@leadboat.com 3913 [ + + ]: 43 : if (resultRelInfo->ri_needLockTagTuple)
3914 : : {
3915 [ + - ]: 1 : if (ItemPointerIsValid(&lockedtid))
3916 : 1 : UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3917 : : InplaceUpdateTupleLock);
323 dean.a.rasheed@gmail 3918 : 1 : LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
3919 : : InplaceUpdateTupleLock);
3920 : 1 : lockedtid = *tupleid;
3921 : : }
3922 : :
847 3923 [ - + ]: 43 : if (!table_tuple_fetch_row_version(resultRelationDesc,
3924 : : tupleid,
3925 : : SnapshotAny,
3926 : : resultRelInfo->ri_oldTupleSlot))
847 dean.a.rasheed@gmail 3927 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch the target tuple");
3928 : :
847 dean.a.rasheed@gmail 3929 [ + + ]:CBC 43 : if (*matched)
3930 : 41 : *matched = ExecQual(resultRelInfo->ri_MergeJoinCondition,
3931 : : econtext);
3932 : :
3933 : : /* Switch lists, if necessary */
3934 [ + + ]: 43 : if (!*matched)
3935 : : {
3936 : 4 : actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
3937 : :
3938 : : /*
3939 : : * If we have both NOT MATCHED BY SOURCE
3940 : : * and NOT MATCHED BY TARGET actions (a
3941 : : * full join between the source and target
3942 : : * relations), the single previously
3943 : : * matched tuple from the outer plan node
3944 : : * is treated as two not matched tuples,
3945 : : * in the same way as if they had not
3946 : : * matched to start with. Therefore, we
3947 : : * must adjust the outer plan node's tuple
3948 : : * count, if we're instrumenting the
3949 : : * query, to get the correct "skipped" row
3950 : : * count --- see show_modifytable_info().
3951 : : */
251 3952 [ + + ]: 4 : if (outerPlanState(mtstate)->instrument &&
3953 [ + - ]: 1 : mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] &&
3954 [ + - ]: 1 : mergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET])
3955 : 1 : InstrUpdateTupleCount(outerPlanState(mtstate)->instrument, 1.0);
3956 : : }
3957 : : }
3958 : :
3959 : : /*
3960 : : * Loop back and process the MATCHED or NOT
3961 : : * MATCHED BY SOURCE actions from the start.
3962 : : */
1580 alvherre@alvh.no-ip. 3963 : 43 : goto lmerge_matched;
3964 : :
1580 alvherre@alvh.no-ip. 3965 :UBC 0 : case TM_Deleted:
3966 : :
3967 : : /*
3968 : : * tuple already deleted; tell caller to run NOT
3969 : : * MATCHED [BY TARGET] actions
3970 : : */
860 dean.a.rasheed@gmail 3971 : 0 : *matched = false;
669 noah@leadboat.com 3972 : 0 : goto out;
3973 : :
1580 alvherre@alvh.no-ip. 3974 :CBC 1 : case TM_SelfModified:
3975 : :
3976 : : /*
3977 : : * This can be reached when following an update
3978 : : * chain from a tuple updated by another session,
3979 : : * reaching a tuple that was already updated or
3980 : : * deleted by the current command, or by a later
3981 : : * command in the current transaction. As above,
3982 : : * this should always be treated as an error.
3983 : : */
3984 [ - + ]: 1 : if (context->tmfd.cmax != estate->es_output_cid)
1580 alvherre@alvh.no-ip. 3985 [ # # ]:UBC 0 : ereport(ERROR,
3986 : : (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3987 : : errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3988 : : errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3989 : :
870 dean.a.rasheed@gmail 3990 [ + - ]:CBC 1 : if (TransactionIdIsCurrentTransactionId(context->tmfd.xmax))
3991 [ + - ]: 1 : ereport(ERROR,
3992 : : (errcode(ERRCODE_CARDINALITY_VIOLATION),
3993 : : /* translator: %s is a SQL command name */
3994 : : errmsg("%s command cannot affect row a second time",
3995 : : "MERGE"),
3996 : : errhint("Ensure that not more than one source row matches any one target row.")));
3997 : :
3998 : : /* This shouldn't happen */
870 dean.a.rasheed@gmail 3999 [ # # ]:UBC 0 : elog(ERROR, "attempted to update or delete invisible tuple");
4000 : : goto out;
4001 : :
1580 alvherre@alvh.no-ip. 4002 : 0 : default:
4003 : : /* see table_tuple_lock call in ExecDelete() */
4004 [ # # ]: 0 : elog(ERROR, "unexpected table_tuple_lock status: %u",
4005 : : result);
4006 : : goto out;
4007 : : }
4008 : : }
4009 : :
4010 : 0 : case TM_Invisible:
4011 : : case TM_WouldBlock:
4012 : : case TM_BeingModified:
4013 : : /* these should not occur */
4014 [ # # ]: 0 : elog(ERROR, "unexpected tuple operation result: %d", result);
4015 : : break;
4016 : : }
4017 : :
4018 : : /* Process RETURNING if present */
860 dean.a.rasheed@gmail 4019 [ + + ]:CBC 1596 : if (resultRelInfo->ri_projectReturning)
4020 : : {
4021 [ + + - - ]: 296 : switch (commandType)
4022 : : {
4023 : 129 : case CMD_UPDATE:
555 4024 : 129 : rslot = ExecProcessReturning(context,
4025 : : resultRelInfo,
4026 : : false,
4027 : : resultRelInfo->ri_oldTupleSlot,
4028 : : newslot,
4029 : : context->planSlot);
860 4030 : 129 : break;
4031 : :
4032 : 167 : case CMD_DELETE:
555 4033 : 167 : rslot = ExecProcessReturning(context,
4034 : : resultRelInfo,
4035 : : true,
4036 : : resultRelInfo->ri_oldTupleSlot,
4037 : : NULL,
4038 : : context->planSlot);
860 4039 : 167 : break;
4040 : :
860 dean.a.rasheed@gmail 4041 :UBC 0 : case CMD_NOTHING:
4042 : 0 : break;
4043 : :
4044 : 0 : default:
4045 [ # # ]: 0 : elog(ERROR, "unrecognized commandType: %d",
4046 : : (int) commandType);
4047 : : }
4048 : : }
4049 : :
4050 : : /*
4051 : : * We've activated one of the WHEN clauses, so we don't search
4052 : : * further. This is required behaviour, not an optimization.
4053 : : */
1580 alvherre@alvh.no-ip. 4054 :CBC 1596 : break;
4055 : : }
4056 : :
4057 : : /*
4058 : : * Successfully executed an action or no qualifying action was found.
4059 : : */
669 noah@leadboat.com 4060 : 8368 : out:
4061 [ + + ]: 8368 : if (ItemPointerIsValid(&lockedtid))
4062 : 5746 : UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
4063 : : InplaceUpdateTupleLock);
860 dean.a.rasheed@gmail 4064 : 8368 : return rslot;
4065 : : }
4066 : :
4067 : : /*
4068 : : * Execute the first qualifying NOT MATCHED [BY TARGET] action.
4069 : : */
4070 : : static TupleTableSlot *
1580 alvherre@alvh.no-ip. 4071 : 1800 : ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo,
4072 : : bool canSetTag)
4073 : : {
4074 : 1800 : ModifyTableState *mtstate = context->mtstate;
4075 : 1800 : ExprContext *econtext = mtstate->ps.ps_ExprContext;
4076 : : List *actionStates;
860 dean.a.rasheed@gmail 4077 : 1800 : TupleTableSlot *rslot = NULL;
4078 : : ListCell *l;
4079 : :
4080 : : /*
4081 : : * For INSERT actions, the root relation's merge action is OK since the
4082 : : * INSERT's targetlist and the WHEN conditions can only refer to the
4083 : : * source relation and hence it does not matter which result relation we
4084 : : * work with.
4085 : : *
4086 : : * XXX does this mean that we can avoid creating copies of actionStates on
4087 : : * partitioned tables, for not-matched actions?
4088 : : */
847 4089 : 1800 : actionStates = resultRelInfo->ri_MergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET];
4090 : :
4091 : : /*
4092 : : * Make source tuple available to ExecQual and ExecProject. We don't need
4093 : : * the target tuple, since the WHEN quals and targetlist can't refer to
4094 : : * the target columns.
4095 : : */
1580 alvherre@alvh.no-ip. 4096 : 1800 : econtext->ecxt_scantuple = NULL;
4097 : 1800 : econtext->ecxt_innertuple = context->planSlot;
4098 : 1800 : econtext->ecxt_outertuple = NULL;
4099 : :
4100 [ + - + + : 2380 : foreach(l, actionStates)
+ + ]
4101 : : {
4102 : 1800 : MergeActionState *action = (MergeActionState *) lfirst(l);
4103 : 1800 : CmdType commandType = action->mas_action->commandType;
4104 : : TupleTableSlot *newslot;
4105 : :
4106 : : /*
4107 : : * Test condition, if any.
4108 : : *
4109 : : * In the absence of any condition, we perform the action
4110 : : * unconditionally (no need to check separately since ExecQual() will
4111 : : * return true if there are no conditions to evaluate).
4112 : : */
4113 [ + + ]: 1800 : if (!ExecQual(action->mas_whenqual, econtext))
4114 : 580 : continue;
4115 : :
4116 : : /* Perform stated action */
4117 [ + - - ]: 1220 : switch (commandType)
4118 : : {
4119 : 1220 : case CMD_INSERT:
4120 : :
4121 : : /*
4122 : : * Project the tuple. In case of a partitioned table, the
4123 : : * projection was already built to use the root's descriptor,
4124 : : * so we don't need to map the tuple here.
4125 : : */
4126 : 1220 : newslot = ExecProject(action->mas_proj);
860 dean.a.rasheed@gmail 4127 : 1220 : mtstate->mt_merge_action = action;
4128 : :
4129 : 1220 : rslot = ExecInsert(context, mtstate->rootResultRelInfo,
4130 : : newslot, canSetTag, NULL, NULL);
1580 alvherre@alvh.no-ip. 4131 : 1181 : mtstate->mt_merge_inserted += 1;
4132 : 1181 : break;
1580 alvherre@alvh.no-ip. 4133 :UBC 0 : case CMD_NOTHING:
4134 : : /* Do nothing */
4135 : 0 : break;
4136 : 0 : default:
4137 [ # # ]: 0 : elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
4138 : : }
4139 : :
4140 : : /*
4141 : : * We've activated one of the WHEN clauses, so we don't search
4142 : : * further. This is required behaviour, not an optimization.
4143 : : */
1580 alvherre@alvh.no-ip. 4144 :CBC 1181 : break;
4145 : : }
4146 : :
860 dean.a.rasheed@gmail 4147 : 1761 : return rslot;
4148 : : }
4149 : :
4150 : : /*
4151 : : * Initialize state for execution of MERGE.
4152 : : */
4153 : : void
1580 alvherre@alvh.no-ip. 4154 : 1060 : ExecInitMerge(ModifyTableState *mtstate, EState *estate)
4155 : : {
523 amitlan@postgresql.o 4156 : 1060 : List *mergeActionLists = mtstate->mt_mergeActionLists;
4157 : 1060 : List *mergeJoinConditions = mtstate->mt_mergeJoinConditions;
1580 alvherre@alvh.no-ip. 4158 : 1060 : ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
4159 : : ResultRelInfo *resultRelInfo;
4160 : : ExprContext *econtext;
4161 : : ListCell *lc;
4162 : : int i;
4163 : :
523 amitlan@postgresql.o 4164 [ - + ]: 1060 : if (mergeActionLists == NIL)
1580 alvherre@alvh.no-ip. 4165 :UBC 0 : return;
4166 : :
1580 alvherre@alvh.no-ip. 4167 :CBC 1060 : mtstate->mt_merge_subcommands = 0;
4168 : :
4169 [ + + ]: 1060 : if (mtstate->ps.ps_ExprContext == NULL)
4170 : 857 : ExecAssignExprContext(estate, &mtstate->ps);
4171 : 1060 : econtext = mtstate->ps.ps_ExprContext;
4172 : :
4173 : : /*
4174 : : * Create a MergeActionState for each action on the mergeActionList and
4175 : : * add it to either a list of matched actions or not-matched actions.
4176 : : *
4177 : : * Similar logic appears in ExecInitPartitionInfo(), so if changing
4178 : : * anything here, do so there too.
4179 : : */
4180 : 1060 : i = 0;
523 amitlan@postgresql.o 4181 [ + - + + : 2277 : foreach(lc, mergeActionLists)
+ + ]
4182 : : {
1580 alvherre@alvh.no-ip. 4183 : 1217 : List *mergeActionList = lfirst(lc);
4184 : : Node *joinCondition;
4185 : : TupleDesc relationDesc;
4186 : : ListCell *l;
4187 : :
523 amitlan@postgresql.o 4188 : 1217 : joinCondition = (Node *) list_nth(mergeJoinConditions, i);
1580 alvherre@alvh.no-ip. 4189 : 1217 : resultRelInfo = mtstate->resultRelInfo + i;
4190 : 1217 : i++;
4191 : 1217 : relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
4192 : :
4193 : : /* initialize slots for MERGE fetches from this rel */
4194 [ + - ]: 1217 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4195 : 1217 : ExecInitMergeTupleSlots(mtstate, resultRelInfo);
4196 : :
4197 : : /* initialize state for join condition checking */
847 dean.a.rasheed@gmail 4198 : 1217 : resultRelInfo->ri_MergeJoinCondition =
4199 : 1217 : ExecInitQual((List *) joinCondition, &mtstate->ps);
4200 : :
1580 alvherre@alvh.no-ip. 4201 [ + - + + : 3361 : foreach(l, mergeActionList)
+ + ]
4202 : : {
4203 : 2144 : MergeAction *action = (MergeAction *) lfirst(l);
4204 : : MergeActionState *action_state;
4205 : : TupleTableSlot *tgtslot;
4206 : : TupleDesc tgtdesc;
4207 : :
4208 : : /*
4209 : : * Build action merge state for this rel. (For partitions,
4210 : : * equivalent code exists in ExecInitPartitionInfo.)
4211 : : */
4212 : 2144 : action_state = makeNode(MergeActionState);
4213 : 2144 : action_state->mas_action = action;
4214 : 2144 : action_state->mas_whenqual = ExecInitQual((List *) action->qual,
4215 : : &mtstate->ps);
4216 : :
4217 : : /*
4218 : : * We create three lists - one for each MergeMatchKind - and stick
4219 : : * the MergeActionState into the appropriate list.
4220 : : */
847 dean.a.rasheed@gmail 4221 : 4288 : resultRelInfo->ri_MergeActions[action->matchKind] =
4222 : 2144 : lappend(resultRelInfo->ri_MergeActions[action->matchKind],
4223 : : action_state);
4224 : :
1580 alvherre@alvh.no-ip. 4225 [ + + + + : 2144 : switch (action->commandType)
- ]
4226 : : {
4227 : 704 : case CMD_INSERT:
4228 : : /* INSERT actions always use rootRelInfo */
4229 : 704 : ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
4230 : : action->targetList);
4231 : :
4232 : : /*
4233 : : * If the MERGE targets a partitioned table, any INSERT
4234 : : * actions must be routed through it, not the child
4235 : : * relations. Initialize the routing struct and the root
4236 : : * table's "new" tuple slot for that, if not already done.
4237 : : * The projection we prepare, for all relations, uses the
4238 : : * root relation descriptor, and targets the plan's root
4239 : : * slot. (This is consistent with the fact that we
4240 : : * checked the plan output to match the root relation,
4241 : : * above.)
4242 : : */
4243 [ + + ]: 704 : if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
4244 : : RELKIND_PARTITIONED_TABLE)
4245 : : {
4246 [ + + ]: 216 : if (mtstate->mt_partition_tuple_routing == NULL)
4247 : : {
4248 : : /*
4249 : : * Initialize planstate for routing if not already
4250 : : * done.
4251 : : *
4252 : : * Note that the slot is managed as a standalone
4253 : : * slot belonging to ModifyTableState, so we pass
4254 : : * NULL for the 2nd argument.
4255 : : */
4256 : 100 : mtstate->mt_root_tuple_slot =
4257 : 100 : table_slot_create(rootRelInfo->ri_RelationDesc,
4258 : : NULL);
4259 : 100 : mtstate->mt_partition_tuple_routing =
4260 : 100 : ExecSetupPartitionTupleRouting(estate,
4261 : : rootRelInfo->ri_RelationDesc);
4262 : : }
4263 : 216 : tgtslot = mtstate->mt_root_tuple_slot;
4264 : 216 : tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
4265 : : }
4266 : : else
4267 : : {
4268 : : /*
4269 : : * If the MERGE targets an inherited table, we insert
4270 : : * into the root table, so we must initialize its
4271 : : * "new" tuple slot, if not already done, and use its
4272 : : * relation descriptor for the projection.
4273 : : *
4274 : : * For non-inherited tables, rootRelInfo and
4275 : : * resultRelInfo are the same, and the "new" tuple
4276 : : * slot will already have been initialized.
4277 : : */
420 dean.a.rasheed@gmail 4278 [ + + ]: 488 : if (rootRelInfo->ri_newTupleSlot == NULL)
4279 : 24 : rootRelInfo->ri_newTupleSlot =
4280 : 24 : table_slot_create(rootRelInfo->ri_RelationDesc,
4281 : : &estate->es_tupleTable);
4282 : :
4283 : 488 : tgtslot = rootRelInfo->ri_newTupleSlot;
4284 : 488 : tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
4285 : : }
4286 : :
1580 alvherre@alvh.no-ip. 4287 : 704 : action_state->mas_proj =
4288 : 704 : ExecBuildProjectionInfo(action->targetList, econtext,
4289 : : tgtslot,
4290 : : &mtstate->ps,
4291 : : tgtdesc);
4292 : :
4293 : 704 : mtstate->mt_merge_subcommands |= MERGE_INSERT;
4294 : 704 : break;
4295 : 1055 : case CMD_UPDATE:
4296 : 1055 : action_state->mas_proj =
4297 : 1055 : ExecBuildUpdateProjection(action->targetList,
4298 : : true,
4299 : : action->updateColnos,
4300 : : relationDesc,
4301 : : econtext,
4302 : : resultRelInfo->ri_newTupleSlot,
4303 : : &mtstate->ps);
4304 : 1055 : mtstate->mt_merge_subcommands |= MERGE_UPDATE;
4305 : 1055 : break;
4306 : 335 : case CMD_DELETE:
4307 : 335 : mtstate->mt_merge_subcommands |= MERGE_DELETE;
4308 : 335 : break;
4309 : 50 : case CMD_NOTHING:
4310 : 50 : break;
1580 alvherre@alvh.no-ip. 4311 :UBC 0 : default:
483 dean.a.rasheed@gmail 4312 [ # # ]: 0 : elog(ERROR, "unknown action in MERGE WHEN clause");
4313 : : break;
4314 : : }
4315 : : }
4316 : : }
4317 : :
4318 : : /*
4319 : : * If the MERGE targets an inherited table, any INSERT actions will use
4320 : : * rootRelInfo, and rootRelInfo will not be in the resultRelInfo array.
4321 : : * Therefore we must initialize its WITH CHECK OPTION constraints and
4322 : : * RETURNING projection, as ExecInitModifyTable did for the resultRelInfo
4323 : : * entries.
4324 : : *
4325 : : * Note that the planner does not build a withCheckOptionList or
4326 : : * returningList for the root relation, but as in ExecInitPartitionInfo,
4327 : : * we can use the first resultRelInfo entry as a reference to calculate
4328 : : * the attno's for the root table.
4329 : : */
420 dean.a.rasheed@gmail 4330 [ + + ]:CBC 1060 : if (rootRelInfo != mtstate->resultRelInfo &&
4331 [ + + ]: 160 : rootRelInfo->ri_RelationDesc->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
4332 [ + + ]: 32 : (mtstate->mt_merge_subcommands & MERGE_INSERT) != 0)
4333 : : {
4334 : 24 : ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
4335 : 24 : Relation rootRelation = rootRelInfo->ri_RelationDesc;
4336 : 24 : Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
4337 : 24 : int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
4338 : 24 : AttrMap *part_attmap = NULL;
4339 : : bool found_whole_row;
4340 : :
4341 [ + + ]: 24 : if (node->withCheckOptionLists != NIL)
4342 : : {
4343 : : List *wcoList;
4344 : 12 : List *wcoExprs = NIL;
4345 : :
4346 : : /* There should be as many WCO lists as result rels */
4347 [ - + ]: 12 : Assert(list_length(node->withCheckOptionLists) ==
4348 : : list_length(node->resultRelations));
4349 : :
4350 : : /*
4351 : : * Use the first WCO list as a reference. In the most common case,
4352 : : * this will be for the same relation as rootRelInfo, and so there
4353 : : * will be no need to adjust its attno's.
4354 : : */
4355 : 12 : wcoList = linitial(node->withCheckOptionLists);
4356 [ + - ]: 12 : if (rootRelation != firstResultRel)
4357 : : {
4358 : : /* Convert any Vars in it to contain the root's attno's */
4359 : : part_attmap =
4360 : 12 : build_attrmap_by_name(RelationGetDescr(rootRelation),
4361 : : RelationGetDescr(firstResultRel),
4362 : : false);
4363 : :
4364 : : wcoList = (List *)
4365 : 12 : map_variable_attnos((Node *) wcoList,
4366 : : firstVarno, 0,
4367 : : part_attmap,
4368 : 12 : RelationGetForm(rootRelation)->reltype,
4369 : : &found_whole_row);
4370 : : }
4371 : :
4372 [ + - + + : 60 : foreach(lc, wcoList)
+ + ]
4373 : : {
4374 : 48 : WithCheckOption *wco = lfirst_node(WithCheckOption, lc);
4375 : 48 : ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
4376 : : &mtstate->ps);
4377 : :
4378 : 48 : wcoExprs = lappend(wcoExprs, wcoExpr);
4379 : : }
4380 : :
4381 : 12 : rootRelInfo->ri_WithCheckOptions = wcoList;
4382 : 12 : rootRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4383 : : }
4384 : :
4385 [ + + ]: 24 : if (node->returningLists != NIL)
4386 : : {
4387 : : List *returningList;
4388 : :
4389 : : /* There should be as many returning lists as result rels */
4390 [ - + ]: 4 : Assert(list_length(node->returningLists) ==
4391 : : list_length(node->resultRelations));
4392 : :
4393 : : /*
4394 : : * Use the first returning list as a reference. In the most common
4395 : : * case, this will be for the same relation as rootRelInfo, and so
4396 : : * there will be no need to adjust its attno's.
4397 : : */
4398 : 4 : returningList = linitial(node->returningLists);
4399 [ + - ]: 4 : if (rootRelation != firstResultRel)
4400 : : {
4401 : : /* Convert any Vars in it to contain the root's attno's */
4402 [ - + ]: 4 : if (part_attmap == NULL)
4403 : : part_attmap =
420 dean.a.rasheed@gmail 4404 :UBC 0 : build_attrmap_by_name(RelationGetDescr(rootRelation),
4405 : : RelationGetDescr(firstResultRel),
4406 : : false);
4407 : :
4408 : : returningList = (List *)
420 dean.a.rasheed@gmail 4409 :CBC 4 : map_variable_attnos((Node *) returningList,
4410 : : firstVarno, 0,
4411 : : part_attmap,
4412 : 4 : RelationGetForm(rootRelation)->reltype,
4413 : : &found_whole_row);
4414 : : }
4415 : 4 : rootRelInfo->ri_returningList = returningList;
4416 : :
4417 : : /* Initialize the RETURNING projection */
4418 : 4 : rootRelInfo->ri_projectReturning =
4419 : 4 : ExecBuildProjectionInfo(returningList, econtext,
4420 : : mtstate->ps.ps_ResultTupleSlot,
4421 : : &mtstate->ps,
4422 : : RelationGetDescr(rootRelation));
4423 : : }
4424 : : }
4425 : : }
4426 : :
4427 : : /*
4428 : : * Initializes the tuple slots in a ResultRelInfo for any MERGE action.
4429 : : *
4430 : : * We mark 'projectNewInfoValid' even though the projections themselves
4431 : : * are not initialized here.
4432 : : */
4433 : : void
1580 alvherre@alvh.no-ip. 4434 : 1232 : ExecInitMergeTupleSlots(ModifyTableState *mtstate,
4435 : : ResultRelInfo *resultRelInfo)
4436 : : {
4437 : 1232 : EState *estate = mtstate->ps.state;
4438 : :
4439 [ - + ]: 1232 : Assert(!resultRelInfo->ri_projectNewInfoValid);
4440 : :
4441 : 1232 : resultRelInfo->ri_oldTupleSlot =
4442 : 1232 : table_slot_create(resultRelInfo->ri_RelationDesc,
4443 : : &estate->es_tupleTable);
4444 : 1232 : resultRelInfo->ri_newTupleSlot =
4445 : 1232 : table_slot_create(resultRelInfo->ri_RelationDesc,
4446 : : &estate->es_tupleTable);
4447 : 1232 : resultRelInfo->ri_projectNewInfoValid = true;
4448 : 1232 : }
4449 : :
4450 : : /*
4451 : : * Process BEFORE EACH STATEMENT triggers
4452 : : */
4453 : : static void
6132 tgl@sss.pgh.pa.us 4454 : 73656 : fireBSTriggers(ModifyTableState *node)
4455 : : {
3050 alvherre@alvh.no-ip. 4456 : 73656 : ModifyTable *plan = (ModifyTable *) node->ps.plan;
2105 heikki.linnakangas@i 4457 : 73656 : ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4458 : :
6132 tgl@sss.pgh.pa.us 4459 [ + + + + : 73656 : switch (node->operation)
- ]
4460 : : {
4461 : 55312 : case CMD_INSERT:
3372 rhaas@postgresql.org 4462 : 55312 : ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3050 alvherre@alvh.no-ip. 4463 [ + + ]: 55304 : if (plan->onConflictAction == ONCONFLICT_UPDATE)
4096 andres@anarazel.de 4464 : 617 : ExecBSUpdateTriggers(node->ps.state,
4465 : : resultRelInfo);
6132 tgl@sss.pgh.pa.us 4466 : 55304 : break;
4467 : 9023 : case CMD_UPDATE:
3372 rhaas@postgresql.org 4468 : 9023 : ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
6132 tgl@sss.pgh.pa.us 4469 : 9023 : break;
4470 : 8361 : case CMD_DELETE:
3372 rhaas@postgresql.org 4471 : 8361 : ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
6132 tgl@sss.pgh.pa.us 4472 : 8361 : break;
1580 alvherre@alvh.no-ip. 4473 : 960 : case CMD_MERGE:
4474 [ + + ]: 960 : if (node->mt_merge_subcommands & MERGE_INSERT)
4475 : 523 : ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4476 [ + + ]: 960 : if (node->mt_merge_subcommands & MERGE_UPDATE)
4477 : 632 : ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4478 [ + + ]: 960 : if (node->mt_merge_subcommands & MERGE_DELETE)
4479 : 271 : ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4480 : 960 : break;
6132 tgl@sss.pgh.pa.us 4481 :UBC 0 : default:
4482 [ # # ]: 0 : elog(ERROR, "unknown operation");
4483 : : break;
4484 : : }
6132 tgl@sss.pgh.pa.us 4485 :CBC 73648 : }
4486 : :
4487 : : /*
4488 : : * Process AFTER EACH STATEMENT triggers
4489 : : */
4490 : : static void
3314 rhodiumtoad@postgres 4491 : 71333 : fireASTriggers(ModifyTableState *node)
4492 : : {
3050 alvherre@alvh.no-ip. 4493 : 71333 : ModifyTable *plan = (ModifyTable *) node->ps.plan;
2105 heikki.linnakangas@i 4494 : 71333 : ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4495 : :
6132 tgl@sss.pgh.pa.us 4496 [ + + + + : 71333 : switch (node->operation)
- ]
4497 : : {
4498 : 53708 : case CMD_INSERT:
3050 alvherre@alvh.no-ip. 4499 [ + + ]: 53708 : if (plan->onConflictAction == ONCONFLICT_UPDATE)
4096 andres@anarazel.de 4500 : 545 : ExecASUpdateTriggers(node->ps.state,
4501 : : resultRelInfo,
3234 tgl@sss.pgh.pa.us 4502 : 545 : node->mt_oc_transition_capture);
3314 rhodiumtoad@postgres 4503 : 53708 : ExecASInsertTriggers(node->ps.state, resultRelInfo,
4504 : 53708 : node->mt_transition_capture);
6132 tgl@sss.pgh.pa.us 4505 : 53708 : break;
4506 : 8510 : case CMD_UPDATE:
3314 rhodiumtoad@postgres 4507 : 8510 : ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4508 : 8510 : node->mt_transition_capture);
6132 tgl@sss.pgh.pa.us 4509 : 8510 : break;
4510 : 8257 : case CMD_DELETE:
3314 rhodiumtoad@postgres 4511 : 8257 : ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4512 : 8257 : node->mt_transition_capture);
6132 tgl@sss.pgh.pa.us 4513 : 8257 : break;
1580 alvherre@alvh.no-ip. 4514 : 858 : case CMD_MERGE:
4515 [ + + ]: 858 : if (node->mt_merge_subcommands & MERGE_DELETE)
4516 : 244 : ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4517 : 244 : node->mt_transition_capture);
4518 [ + + ]: 858 : if (node->mt_merge_subcommands & MERGE_UPDATE)
4519 : 567 : ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4520 : 567 : node->mt_transition_capture);
4521 [ + + ]: 858 : if (node->mt_merge_subcommands & MERGE_INSERT)
4522 : 478 : ExecASInsertTriggers(node->ps.state, resultRelInfo,
4523 : 478 : node->mt_transition_capture);
4524 : 858 : break;
6132 tgl@sss.pgh.pa.us 4525 :UBC 0 : default:
4526 [ # # ]: 0 : elog(ERROR, "unknown operation");
4527 : : break;
4528 : : }
6132 tgl@sss.pgh.pa.us 4529 :CBC 71333 : }
4530 : :
4531 : : /*
4532 : : * Set up the state needed for collecting transition tuples for AFTER
4533 : : * triggers.
4534 : : */
4535 : : static void
3314 rhodiumtoad@postgres 4536 : 73923 : ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
4537 : : {
3050 alvherre@alvh.no-ip. 4538 : 73923 : ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
2105 heikki.linnakangas@i 4539 : 73923 : ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
4540 : :
4541 : : /* Check for transition tables on the directly targeted relation. */
3314 rhodiumtoad@postgres 4542 : 73923 : mtstate->mt_transition_capture =
3234 tgl@sss.pgh.pa.us 4543 : 73923 : MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
4544 : 73923 : RelationGetRelid(targetRelInfo->ri_RelationDesc),
4545 : : mtstate->operation);
3050 alvherre@alvh.no-ip. 4546 [ + + ]: 73923 : if (plan->operation == CMD_INSERT &&
4547 [ + + ]: 54138 : plan->onConflictAction == ONCONFLICT_UPDATE)
3234 tgl@sss.pgh.pa.us 4548 : 621 : mtstate->mt_oc_transition_capture =
4549 : 621 : MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
4550 : 621 : RelationGetRelid(targetRelInfo->ri_RelationDesc),
4551 : : CMD_UPDATE);
3109 rhaas@postgresql.org 4552 : 73923 : }
4553 : :
4554 : : /*
4555 : : * ExecPrepareTupleRouting --- prepare for routing one tuple
4556 : : *
4557 : : * Determine the partition in which the tuple in slot is to be inserted,
4558 : : * and return its ResultRelInfo in *partRelInfo. The return value is
4559 : : * a slot holding the tuple of the partition rowtype.
4560 : : *
4561 : : * This also sets the transition table information in mtstate based on the
4562 : : * selected partition.
4563 : : */
4564 : : static TupleTableSlot *
3050 alvherre@alvh.no-ip. 4565 : 480264 : ExecPrepareTupleRouting(ModifyTableState *mtstate,
4566 : : EState *estate,
4567 : : PartitionTupleRouting *proute,
4568 : : ResultRelInfo *targetRelInfo,
4569 : : TupleTableSlot *slot,
4570 : : ResultRelInfo **partRelInfo)
4571 : : {
4572 : : ResultRelInfo *partrel;
4573 : : TupleConversionMap *map;
4574 : :
4575 : : /*
4576 : : * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
4577 : : * not find a valid partition for the tuple in 'slot' then an error is
4578 : : * raised. An error may also be raised if the found partition is not a
4579 : : * valid target for INSERTs. This is required since a partitioned table
4580 : : * UPDATE to another partition becomes a DELETE+INSERT.
4581 : : */
2808 4582 : 480264 : partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
4583 : :
4584 : : /*
4585 : : * If we're capturing transition tuples, we might need to convert from the
4586 : : * partition rowtype to root partitioned table's rowtype. But if there
4587 : : * are no BEFORE triggers on the partition that could change the tuple, we
4588 : : * can just remember the original unconverted tuple to avoid a needless
4589 : : * round trip conversion.
4590 : : */
3050 4591 [ + + ]: 480120 : if (mtstate->mt_transition_capture != NULL)
4592 : : {
4593 : : bool has_before_insert_row_trig;
4594 : :
2105 heikki.linnakangas@i 4595 [ + + ]: 130 : has_before_insert_row_trig = (partrel->ri_TrigDesc &&
4596 [ + + ]: 28 : partrel->ri_TrigDesc->trig_insert_before_row);
4597 : :
4598 : 102 : mtstate->mt_transition_capture->tcs_original_insert_tuple =
4599 [ + + ]: 102 : !has_before_insert_row_trig ? slot : NULL;
4600 : : }
4601 : :
4602 : : /*
4603 : : * Convert the tuple, if necessary.
4604 : : */
1331 alvherre@alvh.no-ip. 4605 : 480120 : map = ExecGetRootToChildMap(partrel, estate);
2853 andres@anarazel.de 4606 [ + + ]: 480120 : if (map != NULL)
4607 : : {
2105 heikki.linnakangas@i 4608 : 45768 : TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
4609 : :
2853 andres@anarazel.de 4610 : 45768 : slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
4611 : : }
4612 : :
2110 heikki.linnakangas@i 4613 : 480120 : *partRelInfo = partrel;
3050 alvherre@alvh.no-ip. 4614 : 480120 : return slot;
4615 : : }
4616 : :
4617 : : /* ----------------------------------------------------------------
4618 : : * ExecModifyTable
4619 : : *
4620 : : * Perform table modifications as required, and return RETURNING results
4621 : : * if needed.
4622 : : * ----------------------------------------------------------------
4623 : : */
4624 : : static TupleTableSlot *
3295 andres@anarazel.de 4625 : 78691 : ExecModifyTable(PlanState *pstate)
4626 : : {
4627 : 78691 : ModifyTableState *node = castNode(ModifyTableState, pstate);
4628 : : ModifyTableContext context;
5993 bruce@momjian.us 4629 : 78691 : EState *estate = node->ps.state;
4630 : 78691 : CmdType operation = node->operation;
4631 : : ResultRelInfo *resultRelInfo;
4632 : : PlanState *subplanstate;
4633 : : TupleTableSlot *slot;
4634 : : TupleTableSlot *oldSlot;
4635 : : ItemPointerData tuple_ctid;
4636 : : HeapTupleData oldtupdata;
4637 : : HeapTuple oldtuple;
4638 : : ItemPointer tupleid;
4639 : : bool tuplock;
4640 : :
3287 andres@anarazel.de 4641 [ - + ]: 78691 : CHECK_FOR_INTERRUPTS();
4642 : :
4643 : : /*
4644 : : * This should NOT get called during EvalPlanQual; we should have passed a
4645 : : * subplan tree to EvalPlanQual, instead. Use a runtime test not just
4646 : : * Assert because this condition is easy to miss in testing. (Note:
4647 : : * although ModifyTable should not get executed within an EvalPlanQual
4648 : : * operation, we do have to allow it to be initialized and shut down in
4649 : : * case it is within a CTE subplan. Hence this test must be here, not in
4650 : : * ExecInitModifyTable.)
4651 : : */
2515 4652 [ - + ]: 78691 : if (estate->es_epq_active != NULL)
5292 tgl@sss.pgh.pa.us 4653 [ # # ]:UBC 0 : elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
4654 : :
4655 : : /*
4656 : : * If we've already completed processing, don't try to do more. We need
4657 : : * this test because ExecPostprocessPlan might call us an extra time, and
4658 : : * our subplan's nodes aren't necessarily robust against being called
4659 : : * extra times.
4660 : : */
5629 tgl@sss.pgh.pa.us 4661 [ + + ]:CBC 78691 : if (node->mt_done)
4662 : 585 : return NULL;
4663 : :
4664 : : /*
4665 : : * On first call, fire BEFORE STATEMENT triggers before proceeding.
4666 : : */
6132 4667 [ + + ]: 78106 : if (node->fireBSTriggers)
4668 : : {
4669 : 72473 : fireBSTriggers(node);
4670 : 72465 : node->fireBSTriggers = false;
4671 : : }
4672 : :
4673 : : /* Preload local variables */
1942 4674 : 78098 : resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
4675 : 78098 : subplanstate = outerPlanState(node);
4676 : :
4677 : : /* Set global context */
1591 alvherre@alvh.no-ip. 4678 : 78098 : context.mtstate = node;
4679 : 78098 : context.epqstate = &node->mt_epqstate;
4680 : 78098 : context.estate = estate;
4681 : :
4682 : : /*
4683 : : * Fetch rows from subplan, and execute the required table modification
4684 : : * for each row.
4685 : : */
4686 : : for (;;)
4687 : : {
4688 : : /*
4689 : : * Reset the per-output-tuple exprcontext. This is needed because
4690 : : * triggers expect to use that context as workspace. It's a bit ugly
4691 : : * to do this below the top level of the plan, however. We might need
4692 : : * to rethink this later.
4693 : : */
5820 tgl@sss.pgh.pa.us 4694 [ + + ]: 11209032 : ResetPerTupleExprContext(estate);
4695 : :
4696 : : /*
4697 : : * Reset per-tuple memory context used for processing on conflict and
4698 : : * returning clauses, to free any expression evaluation storage
4699 : : * allocated in the previous cycle.
4700 : : */
2811 andres@anarazel.de 4701 [ + + ]: 11209032 : if (pstate->ps_ExprContext)
4702 : 2233697 : ResetExprContext(pstate->ps_ExprContext);
4703 : :
4704 : : /*
4705 : : * If there is a pending MERGE ... WHEN NOT MATCHED [BY TARGET] action
4706 : : * to execute, do so now --- see the comments in ExecMerge().
4707 : : */
847 dean.a.rasheed@gmail 4708 [ + + ]: 11209032 : if (node->mt_merge_pending_not_matched != NULL)
4709 : : {
4710 : 2 : context.planSlot = node->mt_merge_pending_not_matched;
555 4711 : 2 : context.cpDeletedSlot = NULL;
4712 : :
847 4713 : 2 : slot = ExecMergeNotMatched(&context, node->resultRelInfo,
4714 : 2 : node->canSetTag);
4715 : :
4716 : : /* Clear the pending action */
4717 : 2 : node->mt_merge_pending_not_matched = NULL;
4718 : :
4719 : : /*
4720 : : * If we got a RETURNING result, return it to the caller. We'll
4721 : : * continue the work on next call.
4722 : : */
4723 [ + - ]: 2 : if (slot)
4724 : 2 : return slot;
4725 : :
847 dean.a.rasheed@gmail 4726 :UBC 0 : continue; /* continue with the next tuple */
4727 : : }
4728 : :
4729 : : /* Fetch the next row from subplan */
1557 alvherre@alvh.no-ip. 4730 :CBC 11209030 : context.planSlot = ExecProcNode(subplanstate);
555 dean.a.rasheed@gmail 4731 : 11208732 : context.cpDeletedSlot = NULL;
4732 : :
4733 : : /* No more tuples to process? */
1557 alvherre@alvh.no-ip. 4734 [ + + + + ]: 11208732 : if (TupIsNull(context.planSlot))
4735 : : break;
4736 : :
4737 : : /*
4738 : : * When there are multiple result relations, each tuple contains a
4739 : : * junk column that gives the OID of the rel from which it came.
4740 : : * Extract it and select the correct result relation.
4741 : : */
1942 tgl@sss.pgh.pa.us 4742 [ + + ]: 11138553 : if (AttributeNumberIsValid(node->mt_resultOidAttno))
4743 : : {
4744 : : Datum datum;
4745 : : bool isNull;
4746 : : Oid resultoid;
4747 : :
1557 alvherre@alvh.no-ip. 4748 : 3448 : datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
4749 : : &isNull);
1942 tgl@sss.pgh.pa.us 4750 [ + + ]: 3448 : if (isNull)
4751 : : {
4752 : : /*
4753 : : * For commands other than MERGE, any tuples having InvalidOid
4754 : : * for tableoid are errors. For MERGE, we may need to handle
4755 : : * them as WHEN NOT MATCHED clauses if any, so do that.
4756 : : *
4757 : : * Note that we use the node's toplevel resultRelInfo, not any
4758 : : * specific partition's.
4759 : : */
1580 alvherre@alvh.no-ip. 4760 [ + - ]: 338 : if (operation == CMD_MERGE)
4761 : : {
1557 4762 : 338 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4763 : :
860 dean.a.rasheed@gmail 4764 : 338 : slot = ExecMerge(&context, node->resultRelInfo,
4765 : 338 : NULL, NULL, node->canSetTag);
4766 : :
4767 : : /*
4768 : : * If we got a RETURNING result, return it to the caller.
4769 : : * We'll continue the work on next call.
4770 : : */
4771 [ + + ]: 330 : if (slot)
4772 : 25 : return slot;
4773 : :
4774 : 305 : continue; /* continue with the next tuple */
4775 : : }
4776 : :
1942 tgl@sss.pgh.pa.us 4777 [ # # ]:UBC 0 : elog(ERROR, "tableoid is NULL");
4778 : : }
1942 tgl@sss.pgh.pa.us 4779 :CBC 3110 : resultoid = DatumGetObjectId(datum);
4780 : :
4781 : : /* If it's not the same as last time, we need to locate the rel */
4782 [ + + ]: 3110 : if (resultoid != node->mt_lastResultOid)
1936 4783 : 2158 : resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
4784 : : false, true);
4785 : : }
4786 : :
4787 : : /*
4788 : : * If we don't have a ForPortionOfState yet, we must be a partition or
4789 : : * inheritance child being hit for the first time. Make a copy from
4790 : : * the root, with our own TupleTableSlot. We do this lazily so that we
4791 : : * don't pay the price of unused partitions.
4792 : : */
47 peter@eisentraut.org 4793 [ + + ]: 11138215 : if (((ModifyTable *) context.mtstate->ps.plan)->forPortionOf &&
4794 [ + + ]: 949 : !resultRelInfo->ri_forPortionOf)
4795 : 74 : ExecInitForPortionOf(context.mtstate, estate, resultRelInfo);
4796 : :
4797 : : /*
4798 : : * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
4799 : : * here is compute the RETURNING expressions.
4800 : : */
3781 rhaas@postgresql.org 4801 [ + + ]: 11138215 : if (resultRelInfo->ri_usesFdwDirectModify)
4802 : : {
4803 [ - + ]: 348 : Assert(resultRelInfo->ri_projectReturning);
4804 : :
4805 : : /*
4806 : : * A scan slot containing the data that was actually inserted,
4807 : : * updated or deleted has already been made available to
4808 : : * ExecProcessReturning by IterateDirectModify, so no need to
4809 : : * provide it here. The individual old and new slots are not
4810 : : * needed, since direct-modify is disabled if the RETURNING list
4811 : : * refers to OLD/NEW values.
4812 : : */
555 dean.a.rasheed@gmail 4813 [ + - - + ]: 348 : Assert((resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD) == 0 &&
4814 : : (resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW) == 0);
4815 : :
163 4816 : 348 : slot = ExecProcessReturning(&context, resultRelInfo,
4817 : : operation == CMD_DELETE,
4818 : : NULL, NULL, context.planSlot);
4819 : :
3781 rhaas@postgresql.org 4820 : 348 : return slot;
4821 : : }
4822 : :
1557 alvherre@alvh.no-ip. 4823 : 11137867 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4824 : 11137867 : slot = context.planSlot;
4825 : :
3162 tgl@sss.pgh.pa.us 4826 : 11137867 : tupleid = NULL;
4507 noah@leadboat.com 4827 : 11137867 : oldtuple = NULL;
4828 : :
4829 : : /*
4830 : : * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
4831 : : * to be updated/deleted/merged. For a heap relation, that's a TID;
4832 : : * otherwise we may have a wholerow junk attr that carries the old
4833 : : * tuple in toto. Keep this in step with the part of
4834 : : * ExecInitModifyTable that sets up ri_RowIdAttNo.
4835 : : */
1580 alvherre@alvh.no-ip. 4836 [ + + + + : 11137867 : if (operation == CMD_UPDATE || operation == CMD_DELETE ||
+ + ]
4837 : : operation == CMD_MERGE)
4838 : : {
4839 : : char relkind;
4840 : : Datum datum;
4841 : : bool isNull;
4842 : :
1942 tgl@sss.pgh.pa.us 4843 : 3203747 : relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4844 [ + + + + ]: 3203747 : if (relkind == RELKIND_RELATION ||
4845 [ + + ]: 339 : relkind == RELKIND_MATVIEW ||
4846 : : relkind == RELKIND_PARTITIONED_TABLE)
4847 : : {
4848 : : /*
4849 : : * ri_RowIdAttNo refers to a ctid attribute. See the comment
4850 : : * in ExecInitModifyTable().
4851 : : */
183 amitlan@postgresql.o 4852 [ - + - - ]: 3203412 : Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo) ||
4853 : : relkind == RELKIND_PARTITIONED_TABLE);
1942 tgl@sss.pgh.pa.us 4854 : 3203412 : datum = ExecGetJunkAttribute(slot,
4855 : 3203412 : resultRelInfo->ri_RowIdAttNo,
4856 : : &isNull);
4857 : :
4858 : : /*
4859 : : * For commands other than MERGE, any tuples having a null row
4860 : : * identifier are errors. For MERGE, we may need to handle
4861 : : * them as WHEN NOT MATCHED clauses if any, so do that.
4862 : : *
4863 : : * Note that we use the node's toplevel resultRelInfo, not any
4864 : : * specific partition's.
4865 : : */
4866 [ + + ]: 3203412 : if (isNull)
4867 : : {
1580 alvherre@alvh.no-ip. 4868 [ + - ]: 1421 : if (operation == CMD_MERGE)
4869 : : {
1557 4870 : 1421 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4871 : :
860 dean.a.rasheed@gmail 4872 : 1421 : slot = ExecMerge(&context, node->resultRelInfo,
4873 : 1421 : NULL, NULL, node->canSetTag);
4874 : :
4875 : : /*
4876 : : * If we got a RETURNING result, return it to the
4877 : : * caller. We'll continue the work on next call.
4878 : : */
4879 [ + + ]: 1394 : if (slot)
4880 : 88 : return slot;
4881 : :
4882 : 1334 : continue; /* continue with the next tuple */
4883 : : }
4884 : :
1942 tgl@sss.pgh.pa.us 4885 [ # # ]:UBC 0 : elog(ERROR, "ctid is NULL");
4886 : : }
4887 : :
1942 tgl@sss.pgh.pa.us 4888 :CBC 3201991 : tupleid = (ItemPointer) DatumGetPointer(datum);
4889 : 3201991 : tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
4890 : 3201991 : tupleid = &tuple_ctid;
4891 : : }
4892 : :
4893 : : /*
4894 : : * Use the wholerow attribute, when available, to reconstruct the
4895 : : * old relation tuple. The old tuple serves one or both of two
4896 : : * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
4897 : : * provides values for any unchanged columns for the NEW tuple of
4898 : : * an UPDATE, because the subplan does not produce all the columns
4899 : : * of the target table.
4900 : : *
4901 : : * Note that the wholerow attribute does not carry system columns,
4902 : : * so foreign table triggers miss seeing those, except that we
4903 : : * know enough here to set t_tableOid. Quite separately from
4904 : : * this, the FDW may fetch its own junk attrs to identify the row.
4905 : : *
4906 : : * Other relevant relkinds, currently limited to views, always
4907 : : * have a wholerow attribute.
4908 : : */
4909 [ + + ]: 335 : else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4910 : : {
4911 : 320 : datum = ExecGetJunkAttribute(slot,
4912 : 320 : resultRelInfo->ri_RowIdAttNo,
4913 : : &isNull);
4914 : :
4915 : : /*
4916 : : * For commands other than MERGE, any tuples having a null row
4917 : : * identifier are errors. For MERGE, we may need to handle
4918 : : * them as WHEN NOT MATCHED clauses if any, so do that.
4919 : : *
4920 : : * Note that we use the node's toplevel resultRelInfo, not any
4921 : : * specific partition's.
4922 : : */
4923 [ + + ]: 320 : if (isNull)
4924 : : {
877 dean.a.rasheed@gmail 4925 [ + - ]: 32 : if (operation == CMD_MERGE)
4926 : : {
4927 : 32 : EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4928 : :
860 4929 : 32 : slot = ExecMerge(&context, node->resultRelInfo,
4930 : 32 : NULL, NULL, node->canSetTag);
4931 : :
4932 : : /*
4933 : : * If we got a RETURNING result, return it to the
4934 : : * caller. We'll continue the work on next call.
4935 : : */
4936 [ + + ]: 28 : if (slot)
4937 : 8 : return slot;
4938 : :
4939 : 20 : continue; /* continue with the next tuple */
4940 : : }
4941 : :
1942 tgl@sss.pgh.pa.us 4942 [ # # ]:UBC 0 : elog(ERROR, "wholerow is NULL");
4943 : : }
4944 : :
1942 tgl@sss.pgh.pa.us 4945 :CBC 288 : oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
4946 : 288 : oldtupdata.t_len =
4947 : 288 : HeapTupleHeaderGetDatumLength(oldtupdata.t_data);
4948 : 288 : ItemPointerSetInvalid(&(oldtupdata.t_self));
4949 : : /* Historically, view triggers see invalid t_tableOid. */
4950 : 288 : oldtupdata.t_tableOid =
4951 [ + + ]: 288 : (relkind == RELKIND_VIEW) ? InvalidOid :
4952 : 106 : RelationGetRelid(resultRelInfo->ri_RelationDesc);
4953 : :
4954 : 288 : oldtuple = &oldtupdata;
4955 : : }
4956 : : else
4957 : : {
4958 : : /* Only foreign tables are allowed to omit a row-ID attr */
4959 [ - + ]: 15 : Assert(relkind == RELKIND_FOREIGN_TABLE);
4960 : : }
4961 : : }
4962 : :
6132 4963 [ + + + + : 11136414 : switch (operation)
- ]
4964 : : {
4965 : 7934120 : case CMD_INSERT:
4966 : : /* Initialize projection info if first time for this table */
1936 4967 [ + + ]: 7934120 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4968 : 53380 : ExecInitInsertProjection(node, resultRelInfo);
1557 alvherre@alvh.no-ip. 4969 : 7934120 : slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
1591 4970 : 7934120 : slot = ExecInsert(&context, resultRelInfo, slot,
1588 4971 : 7934120 : node->canSetTag, NULL, NULL);
6132 tgl@sss.pgh.pa.us 4972 : 7932685 : break;
4973 : :
4974 : 2209417 : case CMD_UPDATE:
669 noah@leadboat.com 4975 : 2209417 : tuplock = false;
4976 : :
4977 : : /* Initialize projection info if first time for this table */
1936 tgl@sss.pgh.pa.us 4978 [ + + ]: 2209417 : if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4979 : 8763 : ExecInitUpdateProjection(node, resultRelInfo);
4980 : :
4981 : : /*
4982 : : * Make the new tuple by combining plan's output tuple with
4983 : : * the old tuple being updated.
4984 : : */
1942 4985 : 2209417 : oldSlot = resultRelInfo->ri_oldTupleSlot;
4986 [ + + ]: 2209417 : if (oldtuple != NULL)
4987 : : {
669 noah@leadboat.com 4988 [ - + ]: 180 : Assert(!resultRelInfo->ri_needLockTagTuple);
4989 : : /* Use the wholerow junk attr as the old tuple. */
1942 tgl@sss.pgh.pa.us 4990 : 180 : ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
4991 : : }
4992 : : else
4993 : : {
4994 : : /* Fetch the most recent version of old tuple. */
4995 : 2209237 : Relation relation = resultRelInfo->ri_RelationDesc;
4996 : :
669 noah@leadboat.com 4997 [ + + ]: 2209237 : if (resultRelInfo->ri_needLockTagTuple)
4998 : : {
4999 : 15808 : LockTuple(relation, tupleid, InplaceUpdateTupleLock);
5000 : 15808 : tuplock = true;
5001 : : }
1942 tgl@sss.pgh.pa.us 5002 [ - + ]: 2209237 : if (!table_tuple_fetch_row_version(relation, tupleid,
5003 : : SnapshotAny,
5004 : : oldSlot))
1942 tgl@sss.pgh.pa.us 5005 [ # # ]:UBC 0 : elog(ERROR, "failed to fetch tuple being updated");
5006 : : }
1230 dean.a.rasheed@gmail 5007 :CBC 2209417 : slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
5008 : : oldSlot);
5009 : :
5010 : : /* Now apply the update. */
1591 alvherre@alvh.no-ip. 5011 : 2209417 : slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
555 dean.a.rasheed@gmail 5012 : 2209417 : oldSlot, slot, node->canSetTag);
669 noah@leadboat.com 5013 [ + + ]: 2209047 : if (tuplock)
5014 : 15808 : UnlockTuple(resultRelInfo->ri_RelationDesc, tupleid,
5015 : : InplaceUpdateTupleLock);
6132 tgl@sss.pgh.pa.us 5016 : 2209047 : break;
5017 : :
5018 : 984115 : case CMD_DELETE:
1591 alvherre@alvh.no-ip. 5019 : 984115 : slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
835 akorotkov@postgresql 5020 : 984115 : true, false, node->canSetTag, NULL, NULL, NULL);
6132 tgl@sss.pgh.pa.us 5021 : 984040 : break;
5022 : :
1580 alvherre@alvh.no-ip. 5023 : 8762 : case CMD_MERGE:
877 dean.a.rasheed@gmail 5024 : 8762 : slot = ExecMerge(&context, resultRelInfo, tupleid, oldtuple,
5025 : 8762 : node->canSetTag);
1580 alvherre@alvh.no-ip. 5026 : 8700 : break;
5027 : :
6132 tgl@sss.pgh.pa.us 5028 :UBC 0 : default:
5029 [ # # ]: 0 : elog(ERROR, "unknown operation");
5030 : : break;
5031 : : }
5032 : :
5033 : : /*
5034 : : * If we got a RETURNING result, return it to caller. We'll continue
5035 : : * the work on next call.
5036 : : */
6132 tgl@sss.pgh.pa.us 5037 [ + + ]:CBC 11134472 : if (slot)
5038 : 5177 : return slot;
5039 : : }
5040 : :
5041 : : /*
5042 : : * Insert remaining tuples for batch insert.
5043 : : */
1338 efujita@postgresql.o 5044 [ + + ]: 70179 : if (estate->es_insert_pending_result_relations != NIL)
5045 : 13 : ExecPendingInserts(estate);
5046 : :
5047 : : /*
5048 : : * We're done, but fire AFTER STATEMENT triggers before exiting.
5049 : : */
6132 tgl@sss.pgh.pa.us 5050 : 70178 : fireASTriggers(node);
5051 : :
5629 5052 : 70178 : node->mt_done = true;
5053 : :
6132 5054 : 70178 : return NULL;
5055 : : }
5056 : :
5057 : : /*
5058 : : * ExecLookupResultRelByOid
5059 : : * If the table with given OID is among the result relations to be
5060 : : * updated by the given ModifyTable node, return its ResultRelInfo.
5061 : : *
5062 : : * If not found, return NULL if missing_ok, else raise error.
5063 : : *
5064 : : * If update_cache is true, then upon successful lookup, update the node's
5065 : : * one-element cache. ONLY ExecModifyTable may pass true for this.
5066 : : */
5067 : : ResultRelInfo *
1936 5068 : 7387 : ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid,
5069 : : bool missing_ok, bool update_cache)
5070 : : {
5071 [ + + ]: 7387 : if (node->mt_resultOidHash)
5072 : : {
5073 : : /* Use the pre-built hash table to locate the rel */
5074 : : MTTargetRelLookup *mtlookup;
5075 : :
5076 : : mtlookup = (MTTargetRelLookup *)
5077 : 741 : hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
5078 [ + - ]: 741 : if (mtlookup)
5079 : : {
5080 [ + + ]: 741 : if (update_cache)
5081 : : {
5082 : 541 : node->mt_lastResultOid = resultoid;
5083 : 541 : node->mt_lastResultIndex = mtlookup->relationIndex;
5084 : : }
5085 : 741 : return node->resultRelInfo + mtlookup->relationIndex;
5086 : : }
5087 : : }
5088 : : else
5089 : : {
5090 : : /* With few target rels, just search the ResultRelInfo array */
5091 [ + + ]: 12002 : for (int ndx = 0; ndx < node->mt_nrels; ndx++)
5092 : : {
5093 : 7179 : ResultRelInfo *rInfo = node->resultRelInfo + ndx;
5094 : :
5095 [ + + ]: 7179 : if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
5096 : : {
5097 [ + + ]: 1823 : if (update_cache)
5098 : : {
5099 : 1617 : node->mt_lastResultOid = resultoid;
5100 : 1617 : node->mt_lastResultIndex = ndx;
5101 : : }
5102 : 1823 : return rInfo;
5103 : : }
5104 : : }
5105 : : }
5106 : :
5107 [ - + ]: 4823 : if (!missing_ok)
1936 tgl@sss.pgh.pa.us 5108 [ # # ]:UBC 0 : elog(ERROR, "incorrect result relation OID %u", resultoid);
1936 tgl@sss.pgh.pa.us 5109 :CBC 4823 : return NULL;
5110 : : }
5111 : :
5112 : : /* ----------------------------------------------------------------
5113 : : * ExecInitModifyTable
5114 : : * ----------------------------------------------------------------
5115 : : */
5116 : : ModifyTableState *
6132 5117 : 73405 : ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
5118 : : {
5119 : : ModifyTableState *mtstate;
1942 5120 : 73405 : Plan *subplan = outerPlan(node);
6132 5121 : 73405 : CmdType operation = node->operation;
493 amitlan@postgresql.o 5122 : 73405 : int total_nrels = list_length(node->resultRelations);
5123 : : int nrels;
533 5124 : 73405 : List *resultRelations = NIL;
5125 : 73405 : List *withCheckOptionLists = NIL;
5126 : 73405 : List *returningLists = NIL;
5127 : 73405 : List *updateColnosLists = NIL;
523 5128 : 73405 : List *mergeActionLists = NIL;
5129 : 73405 : List *mergeJoinConditions = NIL;
32 5130 : 73405 : List *fdwPrivLists = NIL;
5131 : 73405 : Bitmapset *fdwDirectModifyPlans = NULL;
5132 : : ResultRelInfo *resultRelInfo;
5133 : : List *arowmarks;
5134 : : ListCell *l;
5135 : : int i;
5136 : : Relation rel;
5137 : :
5138 : : /* check for unsupported flags */
6132 tgl@sss.pgh.pa.us 5139 [ - + ]: 73405 : Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
5140 : :
5141 : : /*
5142 : : * Only consider unpruned relations for initializing their ResultRelInfo
5143 : : * struct and other fields such as withCheckOptions, etc.
5144 : : *
5145 : : * Note: We must avoid pruning every result relation. This is important
5146 : : * for MERGE, since even if every result relation is pruned from the
5147 : : * subplan, there might still be NOT MATCHED rows, for which there may be
5148 : : * INSERT actions to perform. To allow these actions to be found, at
5149 : : * least one result relation must be kept. Also, when inserting into a
5150 : : * partitioned table, ExecInitPartitionInfo() needs a ResultRelInfo struct
5151 : : * as a reference for building the ResultRelInfo of the target partition.
5152 : : * In either case, it doesn't matter which result relation is kept, so we
5153 : : * just keep the first one, if all others have been pruned. See also,
5154 : : * ExecDoInitialPruning(), which ensures that this first result relation
5155 : : * has been locked.
5156 : : */
533 amitlan@postgresql.o 5157 : 73405 : i = 0;
5158 [ + - + + : 148496 : foreach(l, node->resultRelations)
+ + ]
5159 : : {
5160 : 75091 : Index rti = lfirst_int(l);
5161 : : bool keep_rel;
5162 : :
493 5163 : 75091 : keep_rel = bms_is_member(rti, estate->es_unpruned_relids);
5164 [ + + + + : 75091 : if (!keep_rel && i == total_nrels - 1 && resultRelations == NIL)
+ + ]
5165 : : {
5166 : : /* all result relations pruned; keep the first one */
5167 : 32 : keep_rel = true;
5168 : 32 : rti = linitial_int(node->resultRelations);
5169 : 32 : i = 0;
5170 : : }
5171 : :
5172 [ + + ]: 75091 : if (keep_rel)
5173 : : {
32 5174 : 75030 : List *fdwPrivList = (List *) list_nth(node->fdwPrivLists, i);
5175 : :
533 5176 : 75030 : resultRelations = lappend_int(resultRelations, rti);
5177 [ + + ]: 75030 : if (node->withCheckOptionLists)
5178 : : {
5179 : 1076 : List *withCheckOptions = list_nth_node(List,
5180 : : node->withCheckOptionLists,
5181 : : i);
5182 : :
5183 : 1076 : withCheckOptionLists = lappend(withCheckOptionLists, withCheckOptions);
5184 : : }
5185 [ + + ]: 75030 : if (node->returningLists)
5186 : : {
5187 : 3858 : List *returningList = list_nth_node(List,
5188 : : node->returningLists,
5189 : : i);
5190 : :
5191 : 3858 : returningLists = lappend(returningLists, returningList);
5192 : : }
5193 [ + + ]: 75030 : if (node->updateColnosLists)
5194 : : {
5195 : 10633 : List *updateColnosList = list_nth(node->updateColnosLists, i);
5196 : :
5197 : 10633 : updateColnosLists = lappend(updateColnosLists, updateColnosList);
5198 : : }
523 5199 [ + + ]: 75030 : if (node->mergeActionLists)
5200 : : {
5201 : 1225 : List *mergeActionList = list_nth(node->mergeActionLists, i);
5202 : :
5203 : 1225 : mergeActionLists = lappend(mergeActionLists, mergeActionList);
5204 : : }
5205 [ + + ]: 75030 : if (node->mergeJoinConditions)
5206 : : {
5207 : 1225 : List *mergeJoinCondition = list_nth(node->mergeJoinConditions, i);
5208 : :
5209 : 1225 : mergeJoinConditions = lappend(mergeJoinConditions, mergeJoinCondition);
5210 : : }
5211 : :
5212 : : /*
5213 : : * fdwPrivLists/fdwDirectModifyPlans are re-indexed to match
5214 : : * resultRelations
5215 : : */
32 5216 : 75030 : fdwPrivLists = lappend(fdwPrivLists, fdwPrivList);
5217 [ + + ]: 75030 : if (bms_is_member(i, node->fdwDirectModifyPlans))
5218 : : {
5219 : 110 : int new_index = list_length(resultRelations) - 1;
5220 : :
5221 : 110 : fdwDirectModifyPlans = bms_add_member(fdwDirectModifyPlans,
5222 : : new_index);
5223 : : }
5224 : : }
533 5225 : 75091 : i++;
5226 : : }
5227 : 73405 : nrels = list_length(resultRelations);
493 5228 [ - + ]: 73405 : Assert(nrels > 0);
5229 : :
5230 : : /*
5231 : : * create state structure
5232 : : */
6132 tgl@sss.pgh.pa.us 5233 : 73405 : mtstate = makeNode(ModifyTableState);
5234 : 73405 : mtstate->ps.plan = (Plan *) node;
5235 : 73405 : mtstate->ps.state = estate;
3295 andres@anarazel.de 5236 : 73405 : mtstate->ps.ExecProcNode = ExecModifyTable;
5237 : :
5629 tgl@sss.pgh.pa.us 5238 : 73405 : mtstate->operation = operation;
5239 : 73405 : mtstate->canSetTag = node->canSetTag;
5240 : 73405 : mtstate->mt_done = false;
5241 : :
1942 5242 : 73405 : mtstate->mt_nrels = nrels;
227 michael@paquier.xyz 5243 : 73405 : mtstate->resultRelInfo = palloc_array(ResultRelInfo, nrels);
5244 : :
847 dean.a.rasheed@gmail 5245 : 73405 : mtstate->mt_merge_pending_not_matched = NULL;
1580 alvherre@alvh.no-ip. 5246 : 73405 : mtstate->mt_merge_inserted = 0;
5247 : 73405 : mtstate->mt_merge_updated = 0;
5248 : 73405 : mtstate->mt_merge_deleted = 0;
533 amitlan@postgresql.o 5249 : 73405 : mtstate->mt_updateColnosLists = updateColnosLists;
523 5250 : 73405 : mtstate->mt_mergeActionLists = mergeActionLists;
5251 : 73405 : mtstate->mt_mergeJoinConditions = mergeJoinConditions;
32 5252 : 73405 : mtstate->mt_fdwPrivLists = fdwPrivLists;
5253 : :
5254 : : /*----------
5255 : : * Resolve the target relation. This is the same as:
5256 : : *
5257 : : * - the relation for which we will fire FOR STATEMENT triggers,
5258 : : * - the relation into whose tuple format all captured transition tuples
5259 : : * must be converted, and
5260 : : * - the root partitioned table used for tuple routing.
5261 : : *
5262 : : * If it's a partitioned or inherited table, the root partition or
5263 : : * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
5264 : : * given explicitly in node->rootRelation. Otherwise, the target relation
5265 : : * is the sole relation in the node->resultRelations list and, since it can
5266 : : * never be pruned, also in the resultRelations list constructed above.
5267 : : *----------
5268 : : */
2111 heikki.linnakangas@i 5269 [ + + ]: 73405 : if (node->rootRelation > 0)
5270 : : {
533 amitlan@postgresql.o 5271 [ - + ]: 1954 : Assert(bms_is_member(node->rootRelation, estate->es_unpruned_relids));
2111 heikki.linnakangas@i 5272 : 1954 : mtstate->rootResultRelInfo = makeNode(ResultRelInfo);
5273 : 1954 : ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
5274 : : node->rootRelation);
5275 : : }
5276 : : else
5277 : : {
1005 tgl@sss.pgh.pa.us 5278 [ - + ]: 71451 : Assert(list_length(node->resultRelations) == 1);
523 amitlan@postgresql.o 5279 [ - + ]: 71451 : Assert(list_length(resultRelations) == 1);
2105 heikki.linnakangas@i 5280 : 71451 : mtstate->rootResultRelInfo = mtstate->resultRelInfo;
5281 : 71451 : ExecInitResultRelation(estate, mtstate->resultRelInfo,
523 amitlan@postgresql.o 5282 : 71451 : linitial_int(resultRelations));
5283 : : }
5284 : :
5285 : : /* set up epqstate with dummy subplan data for the moment */
1163 tgl@sss.pgh.pa.us 5286 : 73405 : EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
5287 : : node->epqParam, resultRelations);
6132 5288 : 73405 : mtstate->fireBSTriggers = true;
5289 : :
5290 : : /*
5291 : : * Build state for collecting transition tuples. This requires having a
5292 : : * valid trigger query context, so skip it in explain-only mode.
5293 : : */
2105 heikki.linnakangas@i 5294 [ + + ]: 73405 : if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
5295 : 72740 : ExecSetupTransitionCaptureState(mtstate, estate);
5296 : :
5297 : : /*
5298 : : * Open all the result relations and initialize the ResultRelInfo structs.
5299 : : * (But root relation was initialized above, if it's part of the array.)
5300 : : * We must do this before initializing the subplan, because direct-modify
5301 : : * FDWs expect their ResultRelInfos to be available.
5302 : : */
5629 tgl@sss.pgh.pa.us 5303 : 73405 : resultRelInfo = mtstate->resultRelInfo;
6132 5304 : 73405 : i = 0;
533 amitlan@postgresql.o 5305 [ + - + + : 148195 : foreach(l, resultRelations)
+ + ]
5306 : : {
2111 heikki.linnakangas@i 5307 : 75026 : Index resultRelation = lfirst_int(l);
877 dean.a.rasheed@gmail 5308 : 75026 : List *mergeActions = NIL;
5309 : :
523 amitlan@postgresql.o 5310 [ + + ]: 75026 : if (mergeActionLists)
5311 : 1225 : mergeActions = list_nth(mergeActionLists, i);
5312 : :
2105 heikki.linnakangas@i 5313 [ + + ]: 75026 : if (resultRelInfo != mtstate->rootResultRelInfo)
5314 : : {
5315 : 3575 : ExecInitResultRelation(estate, resultRelInfo, resultRelation);
5316 : :
5317 : : /*
5318 : : * For child result relations, store the root result relation
5319 : : * pointer. We do so for the convenience of places that want to
5320 : : * look at the query's original target relation but don't have the
5321 : : * mtstate handy.
5322 : : */
1936 tgl@sss.pgh.pa.us 5323 : 3575 : resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
5324 : : }
5325 : :
5326 : : /* Initialize the usesFdwDirectModify flag */
1591 alvherre@alvh.no-ip. 5327 : 75026 : resultRelInfo->ri_usesFdwDirectModify =
32 amitlan@postgresql.o 5328 : 75026 : bms_is_member(i, fdwDirectModifyPlans);
5329 : :
5330 : : /*
5331 : : * Verify result relation is a valid target for the current operation
5332 : : */
324 dean.a.rasheed@gmail 5333 : 75026 : CheckValidResultRel(resultRelInfo, operation, node->onConflictAction,
5334 : : mergeActions, node);
5335 : :
1942 tgl@sss.pgh.pa.us 5336 : 74790 : resultRelInfo++;
5337 : 74790 : i++;
5338 : : }
5339 : :
5340 : : /*
5341 : : * Now we may initialize the subplan.
5342 : : */
5343 : 73169 : outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
5344 : :
5345 : : /*
5346 : : * Do additional per-result-relation initialization.
5347 : : */
5348 [ + + ]: 147937 : for (i = 0; i < nrels; i++)
5349 : : {
5350 : 74768 : resultRelInfo = &mtstate->resultRelInfo[i];
5351 : :
5352 : : /* Let FDWs init themselves for foreign-table result rels */
3781 rhaas@postgresql.org 5353 [ + + ]: 74768 : if (!resultRelInfo->ri_usesFdwDirectModify &&
5354 [ + + ]: 74662 : resultRelInfo->ri_FdwRoutine != NULL &&
4885 tgl@sss.pgh.pa.us 5355 [ + - ]: 172 : resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
5356 : : {
32 amitlan@postgresql.o 5357 : 172 : List *fdw_private = (List *) list_nth(fdwPrivLists, i);
5358 : :
4885 tgl@sss.pgh.pa.us 5359 : 172 : resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
5360 : : resultRelInfo,
5361 : : fdw_private,
5362 : : i,
5363 : : eflags);
5364 : : }
5365 : :
5366 : : /*
5367 : : * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
5368 : : * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
5369 : : * tables, the FDW might have created additional junk attr(s), but
5370 : : * those are no concern of ours.
5371 : : */
1580 alvherre@alvh.no-ip. 5372 [ + + + + : 74768 : if (operation == CMD_UPDATE || operation == CMD_DELETE ||
+ + ]
5373 : : operation == CMD_MERGE)
5374 : : {
5375 : : char relkind;
5376 : :
1936 tgl@sss.pgh.pa.us 5377 : 20453 : relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
5378 [ + + + + ]: 20453 : if (relkind == RELKIND_RELATION ||
5379 [ + + ]: 410 : relkind == RELKIND_MATVIEW ||
5380 : : relkind == RELKIND_PARTITIONED_TABLE)
5381 : : {
5382 : 20073 : resultRelInfo->ri_RowIdAttNo =
5383 : 20073 : ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
5384 : :
5385 : : /*
5386 : : * For heap relations, a ctid junk attribute must be present.
5387 : : * Partitioned tables should only appear here when all leaf
5388 : : * partitions were pruned, in which case no rows can be
5389 : : * produced and ctid is not needed.
5390 : : */
183 amitlan@postgresql.o 5391 [ + + ]: 20073 : if (relkind == RELKIND_PARTITIONED_TABLE)
5392 [ - + ]: 30 : Assert(nrels == 1);
5393 [ - + ]: 20043 : else if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
1936 tgl@sss.pgh.pa.us 5394 [ # # ]:UBC 0 : elog(ERROR, "could not find junk ctid column");
5395 : : }
1936 tgl@sss.pgh.pa.us 5396 [ + + ]:CBC 380 : else if (relkind == RELKIND_FOREIGN_TABLE)
5397 : : {
5398 : : /*
5399 : : * We don't support MERGE with foreign tables for now. (It's
5400 : : * problematic because the implementation uses CTID.)
5401 : : */
1580 alvherre@alvh.no-ip. 5402 [ - + ]: 190 : Assert(operation != CMD_MERGE);
5403 : :
5404 : : /*
5405 : : * When there is a row-level trigger, there should be a
5406 : : * wholerow attribute. We also require it to be present in
5407 : : * UPDATE and MERGE, so we can get the values of unchanged
5408 : : * columns.
5409 : : */
1936 tgl@sss.pgh.pa.us 5410 : 190 : resultRelInfo->ri_RowIdAttNo =
5411 : 190 : ExecFindJunkAttributeInTlist(subplan->targetlist,
5412 : : "wholerow");
1580 alvherre@alvh.no-ip. 5413 [ + + - + ]: 190 : if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
1936 tgl@sss.pgh.pa.us 5414 [ - + ]: 109 : !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
1936 tgl@sss.pgh.pa.us 5415 [ # # ]:UBC 0 : elog(ERROR, "could not find junk wholerow column");
5416 : : }
5417 : : else
5418 : : {
5419 : : /* Other valid target relkinds must provide wholerow */
1936 tgl@sss.pgh.pa.us 5420 :CBC 190 : resultRelInfo->ri_RowIdAttNo =
5421 : 190 : ExecFindJunkAttributeInTlist(subplan->targetlist,
5422 : : "wholerow");
5423 [ - + ]: 190 : if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
1936 tgl@sss.pgh.pa.us 5424 [ # # ]:UBC 0 : elog(ERROR, "could not find junk wholerow column");
5425 : : }
5426 : : }
5427 : : }
5428 : :
5429 : : /*
5430 : : * If this is an inherited update/delete/merge, there will be a junk
5431 : : * attribute named "tableoid" present in the subplan's targetlist. It
5432 : : * will be used to identify the result relation for a given tuple to be
5433 : : * updated/deleted/merged.
5434 : : */
1936 tgl@sss.pgh.pa.us 5435 :CBC 73169 : mtstate->mt_resultOidAttno =
5436 : 73169 : ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
493 amitlan@postgresql.o 5437 [ + + - + ]: 73169 : Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || total_nrels == 1);
1936 tgl@sss.pgh.pa.us 5438 : 73169 : mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
5439 : 73169 : mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
5440 : :
5441 : : /* Get the root target relation */
2105 heikki.linnakangas@i 5442 : 73169 : rel = mtstate->rootResultRelInfo->ri_RelationDesc;
5443 : :
5444 : : /*
5445 : : * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
5446 : : * or MERGE might need this too, but only if it actually moves tuples
5447 : : * between partitions; in that case setup is done by
5448 : : * ExecCrossPartitionUpdate.
5449 : : */
3109 rhaas@postgresql.org 5450 [ + + + + ]: 73169 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5451 : : operation == CMD_INSERT)
3075 5452 : 2969 : mtstate->mt_partition_tuple_routing =
1936 tgl@sss.pgh.pa.us 5453 : 2969 : ExecSetupPartitionTupleRouting(estate, rel);
5454 : :
5455 : : /*
5456 : : * Initialize any WITH CHECK OPTION constraints if needed.
5457 : : */
4755 sfrost@snowman.net 5458 : 73169 : resultRelInfo = mtstate->resultRelInfo;
533 amitlan@postgresql.o 5459 [ + + + + : 74245 : foreach(l, withCheckOptionLists)
+ + ]
5460 : : {
4755 sfrost@snowman.net 5461 : 1076 : List *wcoList = (List *) lfirst(l);
5462 : 1076 : List *wcoExprs = NIL;
5463 : : ListCell *ll;
5464 : :
5465 [ + - + + : 3179 : foreach(ll, wcoList)
+ + ]
5466 : : {
5467 : 2103 : WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
3420 andres@anarazel.de 5468 : 2103 : ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
5469 : : &mtstate->ps);
5470 : :
4755 sfrost@snowman.net 5471 : 2103 : wcoExprs = lappend(wcoExprs, wcoExpr);
5472 : : }
5473 : :
5474 : 1076 : resultRelInfo->ri_WithCheckOptions = wcoList;
5475 : 1076 : resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
5476 : 1076 : resultRelInfo++;
5477 : : }
5478 : :
5479 : : /*
5480 : : * Initialize RETURNING projections if needed.
5481 : : */
533 amitlan@postgresql.o 5482 [ + + ]: 73169 : if (returningLists)
5483 : : {
5484 : : TupleTableSlot *slot;
5485 : : ExprContext *econtext;
5486 : :
5487 : : /*
5488 : : * Initialize result tuple slot and assign its rowtype using the plan
5489 : : * node's declared targetlist, which the planner set up to be the same
5490 : : * as the first (before runtime pruning) RETURNING list. We assume
5491 : : * all the result rels will produce compatible output.
5492 : : */
2809 andres@anarazel.de 5493 : 3638 : ExecInitResultTupleSlotTL(&mtstate->ps, &TTSOpsVirtual);
6132 tgl@sss.pgh.pa.us 5494 : 3638 : slot = mtstate->ps.ps_ResultTupleSlot;
5495 : :
5496 : : /* Need an econtext too */
3420 andres@anarazel.de 5497 [ + - ]: 3638 : if (mtstate->ps.ps_ExprContext == NULL)
5498 : 3638 : ExecAssignExprContext(estate, &mtstate->ps);
5499 : 3638 : econtext = mtstate->ps.ps_ExprContext;
5500 : :
5501 : : /*
5502 : : * Build a projection for each result rel.
5503 : : */
5629 tgl@sss.pgh.pa.us 5504 : 3638 : resultRelInfo = mtstate->resultRelInfo;
533 amitlan@postgresql.o 5505 [ + - + + : 7496 : foreach(l, returningLists)
+ + ]
5506 : : {
6132 tgl@sss.pgh.pa.us 5507 : 3858 : List *rlist = (List *) lfirst(l);
5508 : :
3032 rhaas@postgresql.org 5509 : 3858 : resultRelInfo->ri_returningList = rlist;
6132 tgl@sss.pgh.pa.us 5510 : 3858 : resultRelInfo->ri_projectReturning =
3420 andres@anarazel.de 5511 : 3858 : ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
3321 tgl@sss.pgh.pa.us 5512 : 3858 : resultRelInfo->ri_RelationDesc->rd_att);
6132 5513 : 3858 : resultRelInfo++;
5514 : : }
5515 : : }
5516 : : else
5517 : : {
5518 : : /*
5519 : : * We still must construct a dummy result tuple type, because InitPlan
5520 : : * expects one (maybe should change that?).
5521 : : */
2815 andres@anarazel.de 5522 : 69531 : ExecInitResultTypeTL(&mtstate->ps);
5523 : :
6132 tgl@sss.pgh.pa.us 5524 : 69531 : mtstate->ps.ps_ExprContext = NULL;
5525 : : }
5526 : :
5527 : : /* Set the list of arbiter indexes if needed for ON CONFLICT */
3043 alvherre@alvh.no-ip. 5528 : 73169 : resultRelInfo = mtstate->resultRelInfo;
5529 [ + + ]: 73169 : if (node->onConflictAction != ONCONFLICT_NONE)
5530 : : {
5531 : : /* insert may only have one relation, inheritance is not expanded */
493 amitlan@postgresql.o 5532 [ - + ]: 1193 : Assert(total_nrels == 1);
3043 alvherre@alvh.no-ip. 5533 : 1193 : resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
5534 : : }
5535 : :
5536 : : /*
5537 : : * For ON CONFLICT DO SELECT/UPDATE, initialize the ON CONFLICT action
5538 : : * state.
5539 : : */
163 dean.a.rasheed@gmail 5540 [ + + ]: 73169 : if (node->onConflictAction == ONCONFLICT_UPDATE ||
5541 [ + + ]: 72500 : node->onConflictAction == ONCONFLICT_SELECT)
5542 : : {
5543 : 889 : OnConflictActionState *onconfl = makeNode(OnConflictActionState);
5544 : :
5545 : : /* already exists if created by RETURNING processing above */
4096 andres@anarazel.de 5546 [ + + ]: 889 : if (mtstate->ps.ps_ExprContext == NULL)
5547 : 453 : ExecAssignExprContext(estate, &mtstate->ps);
5548 : :
5549 : : /* action state for DO SELECT/UPDATE */
1902 tgl@sss.pgh.pa.us 5550 : 889 : resultRelInfo->ri_onConflict = onconfl;
5551 : :
5552 : : /* lock strength for DO SELECT [FOR UPDATE/SHARE] */
163 dean.a.rasheed@gmail 5553 : 889 : onconfl->oc_LockStrength = node->onConflictLockStrength;
5554 : :
5555 : : /* initialize slot for the existing tuple */
1902 tgl@sss.pgh.pa.us 5556 : 889 : onconfl->oc_Existing =
2693 andres@anarazel.de 5557 : 889 : table_slot_create(resultRelInfo->ri_RelationDesc,
5558 : 889 : &mtstate->ps.state->es_tupleTable);
5559 : :
5560 : : /*
5561 : : * For ON CONFLICT DO UPDATE, initialize target list and projection.
5562 : : */
163 dean.a.rasheed@gmail 5563 [ + + ]: 889 : if (node->onConflictAction == ONCONFLICT_UPDATE)
5564 : : {
5565 : : ExprContext *econtext;
5566 : : TupleDesc relationDesc;
5567 : :
5568 : 669 : econtext = mtstate->ps.ps_ExprContext;
5569 : 669 : relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
5570 : :
5571 : : /*
5572 : : * Create the tuple slot for the UPDATE SET projection. We want a
5573 : : * slot of the table's type here, because the slot will be used to
5574 : : * insert into the table, and for RETURNING processing - which may
5575 : : * access system attributes.
5576 : : */
5577 : 669 : onconfl->oc_ProjSlot =
5578 : 669 : table_slot_create(resultRelInfo->ri_RelationDesc,
5579 : 669 : &mtstate->ps.state->es_tupleTable);
5580 : :
5581 : : /* build UPDATE SET projection state */
5582 : 669 : onconfl->oc_ProjInfo =
5583 : 669 : ExecBuildUpdateProjection(node->onConflictSet,
5584 : : true,
5585 : : node->onConflictCols,
5586 : : relationDesc,
5587 : : econtext,
5588 : : onconfl->oc_ProjSlot,
5589 : : &mtstate->ps);
5590 : : }
5591 : :
5592 : : /* initialize state to evaluate the WHERE clause, if any */
4096 andres@anarazel.de 5593 [ + + ]: 889 : if (node->onConflictWhere)
5594 : : {
5595 : : ExprState *qualexpr;
5596 : :
3420 5597 : 207 : qualexpr = ExecInitQual((List *) node->onConflictWhere,
5598 : : &mtstate->ps);
1902 tgl@sss.pgh.pa.us 5599 : 207 : onconfl->oc_WhereClause = qualexpr;
5600 : : }
5601 : : }
5602 : :
5603 : : /*
5604 : : * If needed, initialize the target range for FOR PORTION OF.
5605 : : */
115 peter@eisentraut.org 5606 [ + + ]: 73169 : if (node->forPortionOf)
5607 : : {
5608 : : ResultRelInfo *rootRelInfo;
5609 : : TupleDesc tupDesc;
5610 : : ForPortionOfExpr *forPortionOf;
5611 : : Datum targetRange;
5612 : : bool isNull;
5613 : : ExprContext *econtext;
5614 : : ExprState *exprState;
5615 : : ForPortionOfState *fpoState;
5616 : :
5617 : 892 : rootRelInfo = mtstate->resultRelInfo;
5618 [ + + ]: 892 : if (rootRelInfo->ri_RootResultRelInfo)
5619 : 74 : rootRelInfo = rootRelInfo->ri_RootResultRelInfo;
5620 : :
5621 : 892 : tupDesc = rootRelInfo->ri_RelationDesc->rd_att;
5622 : 892 : forPortionOf = (ForPortionOfExpr *) node->forPortionOf;
5623 : :
5624 : : /* Eval the FOR PORTION OF target */
5625 [ + + ]: 892 : if (mtstate->ps.ps_ExprContext == NULL)
5626 : 872 : ExecAssignExprContext(estate, &mtstate->ps);
5627 : 892 : econtext = mtstate->ps.ps_ExprContext;
5628 : :
5629 : 892 : exprState = ExecPrepareExpr((Expr *) forPortionOf->targetRange, estate);
5630 : 892 : targetRange = ExecEvalExpr(exprState, econtext, &isNull);
5631 : :
5632 : : /*
5633 : : * FOR PORTION OF ... TO ... FROM should never give us a NULL target,
5634 : : * but FOR PORTION OF (...) could.
5635 : : */
5636 [ + + ]: 892 : if (isNull)
5637 [ + - ]: 16 : ereport(ERROR,
5638 : : (errmsg("FOR PORTION OF target was null")),
5639 : : executor_errposition(estate, forPortionOf->targetLocation));
5640 : :
5641 : : /* Create state for FOR PORTION OF operation */
5642 : :
5643 : 876 : fpoState = makeNode(ForPortionOfState);
5644 : 876 : fpoState->fp_rangeName = forPortionOf->range_name;
5645 : 876 : fpoState->fp_rangeType = forPortionOf->rangeType;
5646 : 876 : fpoState->fp_rangeAttno = forPortionOf->rangeVar->varattno;
5647 : 876 : fpoState->fp_targetRange = targetRange;
5648 : :
5649 : : /* Initialize slot for the existing tuple */
5650 : :
5651 : 876 : fpoState->fp_Existing =
5652 : 876 : table_slot_create(rootRelInfo->ri_RelationDesc,
5653 : 876 : &mtstate->ps.state->es_tupleTable);
5654 : :
5655 : : /* Create the tuple slot for INSERTing the temporal leftovers */
5656 : :
5657 : 876 : fpoState->fp_Leftover =
5658 : 876 : ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc, &TTSOpsVirtual);
5659 : :
5660 : 876 : rootRelInfo->ri_forPortionOf = fpoState;
5661 : :
5662 : : /*
5663 : : * Make sure the root relation has the FOR PORTION OF clause too. Each
5664 : : * partition needs its own TupleTableSlot, since they can have
5665 : : * different descriptors, so they'll use the root fpoState to
5666 : : * initialize one if necessary.
5667 : : */
5668 [ + + ]: 876 : if (node->rootRelation > 0)
5669 : 74 : mtstate->rootResultRelInfo->ri_forPortionOf = fpoState;
5670 : :
5671 [ + + ]: 876 : if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5672 [ + - ]: 58 : mtstate->mt_partition_tuple_routing == NULL)
5673 : : {
5674 : : /*
5675 : : * We will need tuple routing to insert temporal leftovers. Since
5676 : : * we are initializing things before ExecCrossPartitionUpdate
5677 : : * runs, we must do everything it needs as well.
5678 : : */
5679 : 58 : Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
5680 : : MemoryContext oldcxt;
5681 : :
5682 : : /* Things built here have to last for the query duration. */
5683 : 58 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
5684 : :
5685 : 58 : mtstate->mt_partition_tuple_routing =
5686 : 58 : ExecSetupPartitionTupleRouting(estate, rootRel);
5687 : :
5688 : : /*
5689 : : * Before a partition's tuple can be re-routed, it must first be
5690 : : * converted to the root's format, so we'll need a slot for
5691 : : * storing such tuples.
5692 : : */
5693 [ - + ]: 58 : Assert(mtstate->mt_root_tuple_slot == NULL);
5694 : 58 : mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
5695 : :
5696 : 58 : MemoryContextSwitchTo(oldcxt);
5697 : : }
5698 : :
5699 : : /*
5700 : : * Don't free the ExprContext here because the result must last for
5701 : : * the whole query.
5702 : : */
5703 : : }
5704 : :
5705 : : /*
5706 : : * If we have any secondary relations in an UPDATE or DELETE, they need to
5707 : : * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
5708 : : * EvalPlanQual mechanism needs to be told about them. This also goes for
5709 : : * the source relations in a MERGE. Locate the relevant ExecRowMarks.
5710 : : */
1942 tgl@sss.pgh.pa.us 5711 : 73153 : arowmarks = NIL;
6116 5712 [ + + + + : 75041 : foreach(l, node->rowMarks)
+ + ]
5713 : : {
3393 5714 : 1888 : PlanRowMark *rc = lfirst_node(PlanRowMark, l);
190 amitlan@postgresql.o 5715 : 1888 : RangeTblEntry *rte = exec_rt_fetch(rc->rti, estate);
5716 : : ExecRowMark *erm;
5717 : : ExecAuxRowMark *aerm;
5718 : :
5719 : : /* ignore "parent" rowmarks; they are irrelevant at runtime */
5720 [ + + ]: 1888 : if (rc->isParent)
5721 : 94 : continue;
5722 : :
5723 : : /*
5724 : : * Also ignore rowmarks belonging to child tables that have been
5725 : : * pruned in ExecDoInitialPruning().
5726 : : */
5727 [ + + ]: 1794 : if (rte->rtekind == RTE_RELATION &&
533 5728 [ - + ]: 1418 : !bms_is_member(rc->rti, estate->es_unpruned_relids))
6116 tgl@sss.pgh.pa.us 5729 :UBC 0 : continue;
5730 : :
5731 : : /* Find ExecRowMark and build ExecAuxRowMark */
4092 tgl@sss.pgh.pa.us 5732 :CBC 1794 : erm = ExecFindRowMark(estate, rc->rti, false);
1942 5733 : 1794 : aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
5734 : 1794 : arowmarks = lappend(arowmarks, aerm);
5735 : : }
5736 : :
5737 : : /* For a MERGE command, initialize its state */
1580 alvherre@alvh.no-ip. 5738 [ + + ]: 73153 : if (mtstate->operation == CMD_MERGE)
5739 : 1060 : ExecInitMerge(mtstate, estate);
5740 : :
1942 tgl@sss.pgh.pa.us 5741 : 73153 : EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
5742 : :
5743 : : /*
5744 : : * If there are a lot of result relations, use a hash table to speed the
5745 : : * lookups. If there are not a lot, a simple linear search is faster.
5746 : : *
5747 : : * It's not clear where the threshold is, but try 64 for starters. In a
5748 : : * debugging build, use a small threshold so that we get some test
5749 : : * coverage of both code paths.
5750 : : */
5751 : : #ifdef USE_ASSERT_CHECKING
5752 : : #define MT_NRELS_HASH 4
5753 : : #else
5754 : : #define MT_NRELS_HASH 64
5755 : : #endif
5756 [ + + ]: 73153 : if (nrels >= MT_NRELS_HASH)
5757 : : {
5758 : : HASHCTL hash_ctl;
5759 : :
5760 : 219 : hash_ctl.keysize = sizeof(Oid);
5761 : 219 : hash_ctl.entrysize = sizeof(MTTargetRelLookup);
5762 : 219 : hash_ctl.hcxt = CurrentMemoryContext;
5763 : 219 : mtstate->mt_resultOidHash =
5764 : 219 : hash_create("ModifyTable target hash",
5765 : : nrels, &hash_ctl,
5766 : : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
5767 [ + + ]: 1227 : for (i = 0; i < nrels; i++)
5768 : : {
5769 : : Oid hashkey;
5770 : : MTTargetRelLookup *mtlookup;
5771 : : bool found;
5772 : :
5773 : 1008 : resultRelInfo = &mtstate->resultRelInfo[i];
5774 : 1008 : hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
5775 : : mtlookup = (MTTargetRelLookup *)
5776 : 1008 : hash_search(mtstate->mt_resultOidHash, &hashkey,
5777 : : HASH_ENTER, &found);
5778 [ - + ]: 1008 : Assert(!found);
5779 : 1008 : mtlookup->relationIndex = i;
5780 : : }
5781 : : }
5782 : : else
5783 : 72934 : mtstate->mt_resultOidHash = NULL;
5784 : :
5785 : : /*
5786 : : * Determine if the FDW supports batch insert and determine the batch size
5787 : : * (a FDW may support batching, but it may be disabled for the
5788 : : * server/table).
5789 : : *
5790 : : * We only do this for INSERT, so that for UPDATE/DELETE the batch size
5791 : : * remains set to 0.
5792 : : */
2011 tomas.vondra@postgre 5793 [ + + ]: 73153 : if (operation == CMD_INSERT)
5794 : : {
5795 : : /* insert may only have one relation, inheritance is not expanded */
493 amitlan@postgresql.o 5796 [ - + ]: 54315 : Assert(total_nrels == 1);
2011 tomas.vondra@postgre 5797 : 54315 : resultRelInfo = mtstate->resultRelInfo;
1936 tgl@sss.pgh.pa.us 5798 [ + - ]: 54315 : if (!resultRelInfo->ri_usesFdwDirectModify &&
5799 [ + + ]: 54315 : resultRelInfo->ri_FdwRoutine != NULL &&
5800 [ + - ]: 88 : resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
5801 [ + - ]: 88 : resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
5802 : : {
5803 : 88 : resultRelInfo->ri_BatchSize =
5804 : 88 : resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
2011 tomas.vondra@postgre 5805 [ - + ]: 88 : Assert(resultRelInfo->ri_BatchSize >= 1);
5806 : : }
5807 : : else
1936 tgl@sss.pgh.pa.us 5808 : 54227 : resultRelInfo->ri_BatchSize = 1;
5809 : : }
5810 : :
5811 : : /*
5812 : : * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
5813 : : * to estate->es_auxmodifytables so that it will be run to completion by
5814 : : * ExecPostprocessPlan. (It'd actually work fine to add the primary
5815 : : * ModifyTable node too, but there's no need.) Note the use of lcons not
5816 : : * lappend: we need later-initialized ModifyTable nodes to be shut down
5817 : : * before earlier ones. This ensures that we don't throw away RETURNING
5818 : : * rows that need to be seen by a later CTE subplan.
5819 : : */
5629 5820 [ + + ]: 73153 : if (!mtstate->canSetTag)
5821 : 702 : estate->es_auxmodifytables = lcons(mtstate,
5822 : : estate->es_auxmodifytables);
5823 : :
6132 5824 : 73153 : return mtstate;
5825 : : }
5826 : :
5827 : : /* ----------------------------------------------------------------
5828 : : * ExecEndModifyTable
5829 : : *
5830 : : * Shuts down the plan.
5831 : : *
5832 : : * Returns nothing of interest.
5833 : : * ----------------------------------------------------------------
5834 : : */
5835 : : void
5836 : 70055 : ExecEndModifyTable(ModifyTableState *node)
5837 : : {
5838 : : int i;
5839 : :
5840 : : /*
5841 : : * Allow any FDWs to shut down
5842 : : */
1942 5843 [ + + ]: 141509 : for (i = 0; i < node->mt_nrels; i++)
5844 : : {
5845 : : int j;
4885 5846 : 71454 : ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
5847 : :
3781 rhaas@postgresql.org 5848 [ + + ]: 71454 : if (!resultRelInfo->ri_usesFdwDirectModify &&
5849 [ + + ]: 71356 : resultRelInfo->ri_FdwRoutine != NULL &&
4885 tgl@sss.pgh.pa.us 5850 [ + - ]: 158 : resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
5851 : 158 : resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
5852 : : resultRelInfo);
5853 : :
5854 : : /*
5855 : : * Cleanup the initialized batch slots. This only matters for FDWs
5856 : : * with batching, but the other cases will have ri_NumSlotsInitialized
5857 : : * == 0.
5858 : : */
1870 tomas.vondra@postgre 5859 [ + + ]: 71482 : for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
5860 : : {
5861 : 28 : ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
5862 : 28 : ExecDropSingleTupleTableSlot(resultRelInfo->ri_PlanSlots[j]);
5863 : : }
5864 : : }
5865 : :
5866 : : /*
5867 : : * Close all the partitioned tables, leaf partitions, and their indices
5868 : : * and release the slot used for tuple routing, if set.
5869 : : */
3124 rhaas@postgresql.org 5870 [ + + ]: 70055 : if (node->mt_partition_tuple_routing)
5871 : : {
3032 5872 : 3040 : ExecCleanupTupleRouting(node, node->mt_partition_tuple_routing);
5873 : :
2808 alvherre@alvh.no-ip. 5874 [ + + ]: 3040 : if (node->mt_root_tuple_slot)
5875 : 484 : ExecDropSingleTupleTableSlot(node->mt_root_tuple_slot);
5876 : : }
5877 : :
5878 : : /*
5879 : : * Terminate EPQ execution if active
5880 : : */
6116 tgl@sss.pgh.pa.us 5881 : 70055 : EvalPlanQualEnd(&node->mt_epqstate);
5882 : :
5883 : : /*
5884 : : * shut down subplan
5885 : : */
1942 5886 : 70055 : ExecEndNode(outerPlanState(node));
6132 5887 : 70055 : }
5888 : :
5889 : : void
5857 tgl@sss.pgh.pa.us 5890 :UBC 0 : ExecReScanModifyTable(ModifyTableState *node)
5891 : : {
5892 : : /*
5893 : : * Currently, we don't need to support rescan on ModifyTable nodes. The
5894 : : * semantics of that would be a bit debatable anyway.
5895 : : */
6132 5896 [ # # ]: 0 : elog(ERROR, "ExecReScanModifyTable is not implemented");
5897 : : }
5898 : :
5899 : : /* ----------------------------------------------------------------
5900 : : * ExecInitForPortionOf
5901 : : *
5902 : : * Initializes resultRelInfo->ri_forPortionOf for child tables.
5903 : : *
5904 : : * Partitions share the root leftover slot, since they must insert via
5905 : : * the root relation to get tuple routing. Plain inheritance children
5906 : : * must keep their own leftover slot and insert back into the child, or
5907 : : * else child-only column values and physical placement would be lost.
5908 : : * ----------------------------------------------------------------
5909 : : */
5910 : : static void
47 peter@eisentraut.org 5911 :CBC 74 : ExecInitForPortionOf(ModifyTableState *mtstate, EState *estate,
5912 : : ResultRelInfo *resultRelInfo)
5913 : : {
5914 : : MemoryContext oldcxt;
5915 : : ForPortionOfState *leafState;
5916 : 74 : ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
5917 : : ForPortionOfState *fpoState;
5918 : : TupleConversionMap *map;
5919 : :
5920 [ - + ]: 74 : if (!rootRelInfo)
47 peter@eisentraut.org 5921 [ # # ]:UBC 0 : elog(ERROR, "no root relation but ri_forPortionOf is uninitialized");
5922 : :
47 peter@eisentraut.org 5923 :CBC 74 : fpoState = rootRelInfo->ri_forPortionOf;
5924 [ - + ]: 74 : Assert(fpoState);
5925 : :
5926 : : /* Things built here have to last for the query duration. */
5927 : 74 : oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
5928 : :
5929 : 74 : leafState = makeNode(ForPortionOfState);
5930 : :
5931 : 74 : leafState->fp_rangeName = fpoState->fp_rangeName;
5932 : 74 : leafState->fp_rangeType = fpoState->fp_rangeType;
5933 : 74 : leafState->fp_targetRange = fpoState->fp_targetRange;
5934 : 74 : map = ExecGetChildToRootMap(resultRelInfo);
5935 : :
5936 : : /*
5937 : : * fp_rangeAttno must match the tuple layout used for reading the old
5938 : : * range value. The query uses the target relation's attno, so translate
5939 : : * it to the child attno when the child has a different column layout.
5940 : : */
5941 [ + + ]: 74 : if (map)
5942 : 32 : leafState->fp_rangeAttno = map->attrMap->attnums[fpoState->fp_rangeAttno - 1];
5943 : : else
5944 : 42 : leafState->fp_rangeAttno = fpoState->fp_rangeAttno;
5945 : :
5946 : : /*
5947 : : * For partitioned tables we must read the leftovers using the child
5948 : : * table's tuple descriptor, but then insert them into the root table
5949 : : * (using its tuple descriptor) so we get tuple routing.
5950 : : *
5951 : : * For traditional table inheritance, we read and insert directly into
5952 : : * this resultRelInfo; no tuple routing via the parent is required.
5953 : : */
5954 [ + + ]: 74 : if (rootRelInfo->ri_RelationDesc->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5955 : 58 : leafState->fp_Leftover = fpoState->fp_Leftover;
5956 : : else
5957 : 16 : leafState->fp_Leftover =
5958 : 16 : ExecInitExtraTupleSlot(mtstate->ps.state,
5959 : 16 : RelationGetDescr(resultRelInfo->ri_RelationDesc),
5960 : : &TTSOpsVirtual);
5961 : :
5962 : : /* Each child relation needs a slot matching its tuple descriptor. */
5963 : 74 : leafState->fp_Existing =
5964 : 74 : table_slot_create(resultRelInfo->ri_RelationDesc,
5965 : 74 : &mtstate->ps.state->es_tupleTable);
5966 : :
5967 : 74 : resultRelInfo->ri_forPortionOf = leafState;
5968 : :
5969 : 74 : MemoryContextSwitchTo(oldcxt);
5970 : 74 : }
|