PostgreSQL Source Code git master
Loading...
Searching...
No Matches
nodeModifyTable.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/tableam.h"
#include "access/tupconvert.h"
#include "access/xact.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
#include "executor/instrument.h"
#include "executor/nodeModifyTable.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/injection_point.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
Include dependency graph for nodeModifyTable.c:

Go to the source code of this file.

Data Structures

struct  MTTargetRelLookup
 
struct  ModifyTableContext
 
struct  UpdateContext
 

Macros

#define MT_NRELS_HASH   64
 

Typedefs

typedef struct MTTargetRelLookup MTTargetRelLookup
 
typedef struct ModifyTableContext ModifyTableContext
 
typedef struct UpdateContext UpdateContext
 

Functions

static void ExecBatchInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
 
static void ExecPendingInserts (EState *estate)
 
static void ExecCrossPartitionUpdateForeignKey (ModifyTableContext *context, ResultRelInfo *sourcePartInfo, ResultRelInfo *destPartInfo, ItemPointer tupleid, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
static bool ExecOnConflictLockRow (ModifyTableContext *context, TupleTableSlot *existing, ItemPointer conflictTid, Relation relation, LockTupleMode lockmode, bool isUpdate)
 
static bool ExecOnConflictUpdate (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *excludedSlot, bool canSetTag, TupleTableSlot **returning)
 
static bool ExecOnConflictSelect (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *excludedSlot, bool canSetTag, TupleTableSlot **returning)
 
static TupleTableSlotExecPrepareTupleRouting (ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot, ResultRelInfo **partRelInfo)
 
static TupleTableSlotExecMerge (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
 
static void ExecInitMerge (ModifyTableState *mtstate, EState *estate)
 
static TupleTableSlotExecMergeMatched (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag, bool *matched)
 
static TupleTableSlotExecMergeNotMatched (ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool canSetTag)
 
static void ExecCheckPlanOutput (Relation resultRel, List *targetList)
 
static TupleTableSlotExecProcessReturning (ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool isDelete, TupleTableSlot *oldSlot, TupleTableSlot *newSlot, TupleTableSlot *planSlot)
 
static void ExecCheckTupleVisible (EState *estate, Relation rel, TupleTableSlot *slot)
 
static void ExecCheckTIDVisible (EState *estate, ResultRelInfo *relinfo, ItemPointer tid, TupleTableSlot *tempSlot)
 
void ExecInitGenerated (ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
 
void ExecComputeStoredGenerated (ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
 
static void ExecInitInsertProjection (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void ExecInitUpdateProjection (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static TupleTableSlotExecGetInsertNewTuple (ResultRelInfo *relinfo, TupleTableSlot *planSlot)
 
TupleTableSlotExecGetUpdateNewTuple (ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
 
static TupleTableSlotExecInsert (ModifyTableContext *context, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, bool canSetTag, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
 
static bool ExecDeletePrologue (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot **epqreturnslot, TM_Result *result)
 
static TM_Result ExecDeleteAct (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, bool changingPart)
 
static void ExecDeleteEpilogue (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool changingPart)
 
static TupleTableSlotExecDelete (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool processReturning, bool changingPart, bool canSetTag, TM_Result *tmresult, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
 
static bool ExecCrossPartitionUpdate (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt, TM_Result *tmresult, TupleTableSlot **retry_slot, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
 
static bool ExecUpdatePrologue (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TM_Result *result)
 
static void ExecUpdatePrepareSlot (ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
 
static TM_Result ExecUpdateAct (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt)
 
static void ExecUpdateEpilogue (ModifyTableContext *context, UpdateContext *updateCxt, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot)
 
static TupleTableSlotExecUpdate (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *oldSlot, TupleTableSlot *slot, bool canSetTag)
 
void ExecInitMergeTupleSlots (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void fireBSTriggers (ModifyTableState *node)
 
static void fireASTriggers (ModifyTableState *node)
 
static void ExecSetupTransitionCaptureState (ModifyTableState *mtstate, EState *estate)
 
static TupleTableSlotExecModifyTable (PlanState *pstate)
 
ResultRelInfoExecLookupResultRelByOid (ModifyTableState *node, Oid resultoid, bool missing_ok, bool update_cache)
 
ModifyTableStateExecInitModifyTable (ModifyTable *node, EState *estate, int eflags)
 
void ExecEndModifyTable (ModifyTableState *node)
 
void ExecReScanModifyTable (ModifyTableState *node)
 

Macro Definition Documentation

◆ MT_NRELS_HASH

#define MT_NRELS_HASH   64

Typedef Documentation

◆ ModifyTableContext

◆ MTTargetRelLookup

◆ UpdateContext

Function Documentation

◆ ExecBatchInsert()

static void ExecBatchInsert ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo,
TupleTableSlot **  slots,
TupleTableSlot **  planSlots,
int  numSlots,
EState estate,
bool  canSetTag 
)
static

Definition at line 1396 of file nodeModifyTable.c.

1403{
1404 int i;
1405 int numInserted = numSlots;
1406 TupleTableSlot *slot = NULL;
1408
1409 /*
1410 * insert into foreign table: let the FDW do it
1411 */
1412 rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1413 resultRelInfo,
1414 slots,
1415 planSlots,
1416 &numInserted);
1417
1418 for (i = 0; i < numInserted; i++)
1419 {
1420 slot = rslots[i];
1421
1422 /*
1423 * AFTER ROW Triggers might reference the tableoid column, so
1424 * (re-)initialize tts_tableOid before evaluating them.
1425 */
1426 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1427
1428 /* AFTER ROW INSERT Triggers */
1429 ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1430 mtstate->mt_transition_capture);
1431
1432 /*
1433 * Check any WITH CHECK OPTION constraints from parent views. See the
1434 * comment in ExecInsert.
1435 */
1436 if (resultRelInfo->ri_WithCheckOptions != NIL)
1437 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1438 }
1439
1440 if (canSetTag && numInserted > 0)
1441 estate->es_processed += numInserted;
1442
1443 /* Clean up all the slots, ready for the next batch */
1444 for (i = 0; i < numSlots; i++)
1445 {
1446 ExecClearTuple(slots[i]);
1448 }
1449 resultRelInfo->ri_NumSlots = 0;
1450}
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition execMain.c:2247
int i
Definition isn.c:77
@ WCO_VIEW_CHECK
#define NIL
Definition pg_list.h:68
static int fb(int x)
#define RelationGetRelid(relation)
Definition rel.h:514
uint64 es_processed
Definition execnodes.h:726
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition fdwapi.h:233
struct TransitionCaptureState * mt_transition_capture
Definition execnodes.h:1454
Relation ri_RelationDesc
Definition execnodes.h:492
List * ri_WithCheckOptions
Definition execnodes.h:561
struct FdwRoutine * ri_FdwRoutine
Definition execnodes.h:545
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition trigger.c:2545
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476

References EState::es_processed, ExecARInsertTriggers(), ExecClearTuple(), FdwRoutine::ExecForeignBatchInsert, ExecWithCheckOptions(), fb(), i, ModifyTableState::mt_transition_capture, NIL, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumSlots, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_WithCheckOptions, TupleTableSlot::tts_tableOid, and WCO_VIEW_CHECK.

Referenced by ExecInsert(), and ExecPendingInserts().

◆ ExecCheckPlanOutput()

static void ExecCheckPlanOutput ( Relation  resultRel,
List targetList 
)
static

Definition at line 210 of file nodeModifyTable.c.

211{
212 TupleDesc resultDesc = RelationGetDescr(resultRel);
213 int attno = 0;
214 ListCell *lc;
215
216 foreach(lc, targetList)
217 {
220
221 Assert(!tle->resjunk); /* caller removed junk items already */
222
223 if (attno >= resultDesc->natts)
226 errmsg("table row type and query-specified row type do not match"),
227 errdetail("Query has too many columns.")));
228 attr = TupleDescAttr(resultDesc, attno);
229 attno++;
230
231 /*
232 * Special cases here should match planner's expand_insert_targetlist.
233 */
234 if (attr->attisdropped)
235 {
236 /*
237 * For a dropped column, we can't check atttypid (it's likely 0).
238 * In any case the planner has most likely inserted an INT4 null.
239 * What we insist on is just *some* NULL constant.
240 */
241 if (!IsA(tle->expr, Const) ||
242 !((Const *) tle->expr)->constisnull)
245 errmsg("table row type and query-specified row type do not match"),
246 errdetail("Query provides a value for a dropped column at ordinal position %d.",
247 attno)));
248 }
249 else if (attr->attgenerated)
250 {
251 /*
252 * For a generated column, the planner will have inserted a null
253 * of the column's base type (to avoid possibly failing on domain
254 * not-null constraints). It doesn't seem worth insisting on that
255 * exact type though, since a null value is type-independent. As
256 * above, just insist on *some* NULL constant.
257 */
258 if (!IsA(tle->expr, Const) ||
259 !((Const *) tle->expr)->constisnull)
262 errmsg("table row type and query-specified row type do not match"),
263 errdetail("Query provides a value for a generated column at ordinal position %d.",
264 attno)));
265 }
266 else
267 {
268 /* Normal case: demand type match */
269 if (exprType((Node *) tle->expr) != attr->atttypid)
272 errmsg("table row type and query-specified row type do not match"),
273 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
274 format_type_be(attr->atttypid),
275 attno,
276 format_type_be(exprType((Node *) tle->expr)))));
277 }
278 }
279 if (attno != resultDesc->natts)
282 errmsg("table row type and query-specified row type do not match"),
283 errdetail("Query has too few columns.")));
284}
#define Assert(condition)
Definition c.h:945
int errcode(int sqlerrcode)
Definition elog.c:874
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
char * format_type_be(Oid type_oid)
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
#define IsA(nodeptr, _type_)
Definition nodes.h:164
static char * errmsg
FormData_pg_attribute * Form_pg_attribute
#define lfirst(lc)
Definition pg_list.h:172
#define RelationGetDescr(relation)
Definition rel.h:540
Definition nodes.h:135
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178

References Assert, ereport, errcode(), errdetail(), errmsg, ERROR, exprType(), fb(), format_type_be(), IsA, lfirst, TupleDescData::natts, RelationGetDescr, and TupleDescAttr().

Referenced by ExecInitInsertProjection(), and ExecInitMerge().

◆ ExecCheckTIDVisible()

static void ExecCheckTIDVisible ( EState estate,
ResultRelInfo relinfo,
ItemPointer  tid,
TupleTableSlot tempSlot 
)
static

Definition at line 409 of file nodeModifyTable.c.

413{
414 Relation rel = relinfo->ri_RelationDesc;
415
416 /* Redundantly check isolation level */
418 return;
419
421 elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
422 ExecCheckTupleVisible(estate, rel, tempSlot);
424}
#define elog(elevel,...)
Definition elog.h:226
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
#define SnapshotAny
Definition snapmgr.h:33
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition tableam.h:1274
#define IsolationUsesXactSnapshot()
Definition xact.h:52

References elog, ERROR, ExecCheckTupleVisible(), ExecClearTuple(), fb(), IsolationUsesXactSnapshot, SnapshotAny, and table_tuple_fetch_row_version().

Referenced by ExecInsert().

◆ ExecCheckTupleVisible()

static void ExecCheckTupleVisible ( EState estate,
Relation  rel,
TupleTableSlot slot 
)
static

Definition at line 375 of file nodeModifyTable.c.

378{
380 return;
381
382 if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
383 {
385 TransactionId xmin;
386 bool isnull;
387
389 Assert(!isnull);
391
392 /*
393 * We should not raise a serialization failure if the conflict is
394 * against a tuple inserted by our own transaction, even if it's not
395 * visible to our snapshot. (This would happen, for example, if
396 * conflicting keys are proposed for insertion in a single command.)
397 */
401 errmsg("could not serialize access due to concurrent update")));
402 }
403}
uint32 TransactionId
Definition c.h:738
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77
uint64_t Datum
Definition postgres.h:70
static TransactionId DatumGetTransactionId(Datum X)
Definition postgres.h:282
Snapshot es_snapshot
Definition execnodes.h:672
#define MinTransactionIdAttributeNumber
Definition sysattr.h:22
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition tableam.h:1321
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:438
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition xact.c:943

References Assert, DatumGetTransactionId(), ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errmsg, ERROR, EState::es_snapshot, fb(), IsolationUsesXactSnapshot, MinTransactionIdAttributeNumber, slot_getsysattr(), table_tuple_satisfies_snapshot(), and TransactionIdIsCurrentTransactionId().

Referenced by ExecCheckTIDVisible(), ExecOnConflictSelect(), and ExecOnConflictUpdate().

◆ ExecComputeStoredGenerated()

void ExecComputeStoredGenerated ( ResultRelInfo resultRelInfo,
EState estate,
TupleTableSlot slot,
CmdType  cmdtype 
)

Definition at line 556 of file nodeModifyTable.c.

559{
560 Relation rel = resultRelInfo->ri_RelationDesc;
561 TupleDesc tupdesc = RelationGetDescr(rel);
562 int natts = tupdesc->natts;
563 ExprContext *econtext = GetPerTupleExprContext(estate);
566 Datum *values;
567 bool *nulls;
568
569 /* We should not be called unless this is true */
570 Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
571
572 /*
573 * Initialize the expressions if we didn't already, and check whether we
574 * can exit early because nothing needs to be computed.
575 */
576 if (cmdtype == CMD_UPDATE)
577 {
578 if (resultRelInfo->ri_GeneratedExprsU == NULL)
579 ExecInitGenerated(resultRelInfo, estate, cmdtype);
580 if (resultRelInfo->ri_NumGeneratedNeededU == 0)
581 return;
582 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
583 }
584 else
585 {
586 if (resultRelInfo->ri_GeneratedExprsI == NULL)
587 ExecInitGenerated(resultRelInfo, estate, cmdtype);
588 /* Early exit is impossible given the prior Assert */
589 Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
590 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
591 }
592
594
595 values = palloc_array(Datum, natts);
596 nulls = palloc_array(bool, natts);
597
598 slot_getallattrs(slot);
599 memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
600
601 for (int i = 0; i < natts; i++)
602 {
603 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
604
605 if (ri_GeneratedExprs[i])
606 {
607 Datum val;
608 bool isnull;
609
610 Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
611
612 econtext->ecxt_scantuple = slot;
613
614 val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
615
616 /*
617 * We must make a copy of val as we have no guarantees about where
618 * memory for a pass-by-reference Datum is located.
619 */
620 if (!isnull)
621 val = datumCopy(val, attr->attbyval, attr->attlen);
622
623 values[i] = val;
624 nulls[i] = isnull;
625 }
626 else
627 {
628 if (!nulls[i])
629 values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
630 }
631 }
632
633 ExecClearTuple(slot);
634 memcpy(slot->tts_values, values, sizeof(*values) * natts);
635 memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
638
640}
static Datum values[MAXATTR]
Definition bootstrap.c:188
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
#define GetPerTupleExprContext(estate)
Definition executor.h:660
#define GetPerTupleMemoryContext(estate)
Definition executor.h:665
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:396
#define palloc_array(type, count)
Definition fe_memutils.h:76
long val
Definition informix.c:689
void ExecInitGenerated(ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
@ CMD_UPDATE
Definition nodes.h:276
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:284
ExprState ** ri_GeneratedExprsI
Definition execnodes.h:578
int ri_NumGeneratedNeededU
Definition execnodes.h:583
ExprState ** ri_GeneratedExprsU
Definition execnodes.h:579
int ri_NumGeneratedNeededI
Definition execnodes.h:582
bool has_generated_stored
Definition tupdesc.h:46
TupleConstr * constr
Definition tupdesc.h:159
bool * tts_isnull
Definition tuptable.h:133
Datum * tts_values
Definition tuptable.h:131
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:193
static void slot_getallattrs(TupleTableSlot *slot)
Definition tuptable.h:390
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition tuptable.h:494

References Assert, CompactAttribute::attbyval, CompactAttribute::attlen, CMD_UPDATE, TupleDescData::constr, datumCopy(), ExprContext::ecxt_scantuple, ExecClearTuple(), ExecEvalExpr(), ExecInitGenerated(), ExecMaterializeSlot(), ExecStoreVirtualTuple(), fb(), GetPerTupleExprContext, GetPerTupleMemoryContext, TupleConstr::has_generated_stored, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc_array, RelationGetDescr, ResultRelInfo::ri_GeneratedExprsI, ResultRelInfo::ri_GeneratedExprsU, ResultRelInfo::ri_NumGeneratedNeededI, ResultRelInfo::ri_NumGeneratedNeededU, ResultRelInfo::ri_RelationDesc, slot_getallattrs(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TupleDescAttr(), TupleDescCompactAttr(), val, and values.

Referenced by CopyFrom(), ExecInsert(), ExecSimpleRelationInsert(), ExecSimpleRelationUpdate(), and ExecUpdatePrepareSlot().

◆ ExecCrossPartitionUpdate()

static bool ExecCrossPartitionUpdate ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot slot,
bool  canSetTag,
UpdateContext updateCxt,
TM_Result tmresult,
TupleTableSlot **  retry_slot,
TupleTableSlot **  inserted_tuple,
ResultRelInfo **  insert_destrel 
)
static

Definition at line 1959 of file nodeModifyTable.c.

1969{
1970 ModifyTableState *mtstate = context->mtstate;
1971 EState *estate = mtstate->ps.state;
1973 bool tuple_deleted;
1975
1976 context->cpDeletedSlot = NULL;
1977 context->cpUpdateReturningSlot = NULL;
1978 *retry_slot = NULL;
1979
1980 /*
1981 * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1982 * to migrate to a different partition. Maybe this can be implemented
1983 * some day, but it seems a fringe feature with little redeeming value.
1984 */
1985 if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1986 ereport(ERROR,
1988 errmsg("invalid ON UPDATE specification"),
1989 errdetail("The result tuple would appear in a different partition than the original tuple.")));
1990
1991 /*
1992 * When an UPDATE is run directly on a leaf partition, simply fail with a
1993 * partition constraint violation error.
1994 */
1995 if (resultRelInfo == mtstate->rootResultRelInfo)
1996 ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1997
1998 /* Initialize tuple routing info if not already done. */
1999 if (mtstate->mt_partition_tuple_routing == NULL)
2000 {
2003
2004 /* Things built here have to last for the query duration. */
2006
2009
2010 /*
2011 * Before a partition's tuple can be re-routed, it must first be
2012 * converted to the root's format, so we'll need a slot for storing
2013 * such tuples.
2014 */
2015 Assert(mtstate->mt_root_tuple_slot == NULL);
2017
2019 }
2020
2021 /*
2022 * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
2023 * We want to return rows from INSERT.
2024 */
2025 ExecDelete(context, resultRelInfo,
2026 tupleid, oldtuple,
2027 false, /* processReturning */
2028 true, /* changingPart */
2029 false, /* canSetTag */
2031
2032 /*
2033 * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
2034 * it was already deleted by self, or it was concurrently deleted by
2035 * another transaction), then we should skip the insert as well;
2036 * otherwise, an UPDATE could cause an increase in the total number of
2037 * rows across all partitions, which is clearly wrong.
2038 *
2039 * For a normal UPDATE, the case where the tuple has been the subject of a
2040 * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
2041 * machinery, but for an UPDATE that we've translated into a DELETE from
2042 * this partition and an INSERT into some other partition, that's not
2043 * available, because CTID chains can't span relation boundaries. We
2044 * mimic the semantics to a limited extent by skipping the INSERT if the
2045 * DELETE fails to find a tuple. This ensures that two concurrent
2046 * attempts to UPDATE the same tuple at the same time can't turn one tuple
2047 * into two, and that an UPDATE of a just-deleted tuple can't resurrect
2048 * it.
2049 */
2050 if (!tuple_deleted)
2051 {
2052 /*
2053 * epqslot will be typically NULL. But when ExecDelete() finds that
2054 * another transaction has concurrently updated the same row, it
2055 * re-fetches the row, skips the delete, and epqslot is set to the
2056 * re-fetched tuple slot. In that case, we need to do all the checks
2057 * again. For MERGE, we leave everything to the caller (it must do
2058 * additional rechecking, and might end up executing a different
2059 * action entirely).
2060 */
2061 if (mtstate->operation == CMD_MERGE)
2062 return *tmresult == TM_Ok;
2063 else if (TupIsNull(epqslot))
2064 return true;
2065 else
2066 {
2067 /* Fetch the most recent version of old tuple. */
2069
2070 /* ... but first, make sure ri_oldTupleSlot is initialized. */
2071 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2072 ExecInitUpdateProjection(mtstate, resultRelInfo);
2073 oldSlot = resultRelInfo->ri_oldTupleSlot;
2075 tupleid,
2077 oldSlot))
2078 elog(ERROR, "failed to fetch tuple being updated");
2079 /* and project the new tuple to retry the UPDATE with */
2080 *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot,
2081 oldSlot);
2082 return false;
2083 }
2084 }
2085
2086 /*
2087 * resultRelInfo is one of the per-relation resultRelInfos. So we should
2088 * convert the tuple into root's tuple descriptor if needed, since
2089 * ExecInsert() starts the search from root.
2090 */
2091 tupconv_map = ExecGetChildToRootMap(resultRelInfo);
2092 if (tupconv_map != NULL)
2093 slot = execute_attr_map_slot(tupconv_map->attrMap,
2094 slot,
2095 mtstate->mt_root_tuple_slot);
2096
2097 /* Tuple routing starts from the root table. */
2098 context->cpUpdateReturningSlot =
2099 ExecInsert(context, mtstate->rootResultRelInfo, slot, canSetTag,
2101
2102 /*
2103 * Reset the transition state that may possibly have been written by
2104 * INSERT.
2105 */
2106 if (mtstate->mt_transition_capture)
2108
2109 /* We're done moving. */
2110 return true;
2111}
#define unlikely(x)
Definition c.h:432
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition execMain.c:1928
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition execUtils.c:1305
static TupleTableSlot * ExecInsert(ModifyTableContext *context, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, bool canSetTag, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecInitUpdateProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static TupleTableSlot * ExecDelete(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool processReturning, bool changingPart, bool canSetTag, TM_Result *tmresult, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
@ ONCONFLICT_UPDATE
Definition nodes.h:430
@ CMD_MERGE
Definition nodes.h:279
MemoryContext es_query_cxt
Definition execnodes.h:722
TupleTableSlot * cpDeletedSlot
TupleTableSlot * cpUpdateReturningSlot
ModifyTableState * mtstate
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition execnodes.h:1451
TupleTableSlot * mt_root_tuple_slot
Definition execnodes.h:1448
ResultRelInfo * rootResultRelInfo
Definition execnodes.h:1428
Plan * plan
Definition execnodes.h:1177
EState * state
Definition execnodes.h:1179
bool ri_projectNewInfoValid
Definition execnodes.h:521
TupleTableSlot * ri_oldTupleSlot
Definition execnodes.h:519
TupleTableSlot * tcs_original_insert_tuple
Definition trigger.h:76
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition tableam.c:92
@ TM_Ok
Definition tableam.h:78
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition tupconvert.c:193
#define TupIsNull(slot)
Definition tuptable.h:325

References Assert, CMD_MERGE, ModifyTableContext::cpDeletedSlot, ModifyTableContext::cpUpdateReturningSlot, elog, ereport, errcode(), errdetail(), errmsg, ERROR, EState::es_query_cxt, ExecDelete(), ExecGetChildToRootMap(), ExecGetUpdateNewTuple(), ExecInitUpdateProjection(), ExecInsert(), ExecPartitionCheckEmitError(), ExecSetupPartitionTupleRouting(), execute_attr_map_slot(), fb(), MemoryContextSwitchTo(), ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, ONCONFLICT_UPDATE, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, ModifyTableState::rootResultRelInfo, SnapshotAny, PlanState::state, table_slot_create(), table_tuple_fetch_row_version(), TransitionCaptureState::tcs_original_insert_tuple, TM_Ok, TupIsNull, and unlikely.

Referenced by ExecUpdateAct().

◆ ExecCrossPartitionUpdateForeignKey()

static void ExecCrossPartitionUpdateForeignKey ( ModifyTableContext context,
ResultRelInfo sourcePartInfo,
ResultRelInfo destPartInfo,
ItemPointer  tupleid,
TupleTableSlot oldslot,
TupleTableSlot newslot 
)
static

Definition at line 2401 of file nodeModifyTable.c.

2407{
2408 ListCell *lc;
2411
2412 rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2414
2415 /*
2416 * For any foreign keys that point directly into a non-root ancestors of
2417 * the source partition, we can in theory fire an update event to enforce
2418 * those constraints using their triggers, if we could tell that both the
2419 * source and the destination partitions are under the same ancestor. But
2420 * for now, we simply report an error that those cannot be enforced.
2421 */
2422 foreach(lc, ancestorRels)
2423 {
2425 TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2426 bool has_noncloned_fkey = false;
2427
2428 /* Root ancestor's triggers will be processed. */
2429 if (rInfo == rootRelInfo)
2430 continue;
2431
2432 if (trigdesc && trigdesc->trig_update_after_row)
2433 {
2434 for (int i = 0; i < trigdesc->numtriggers; i++)
2435 {
2436 Trigger *trig = &trigdesc->triggers[i];
2437
2438 if (!trig->tgisclone &&
2440 {
2441 has_noncloned_fkey = true;
2442 break;
2443 }
2444 }
2445 }
2446
2448 ereport(ERROR,
2450 errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2451 errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2452 RelationGetRelationName(rInfo->ri_RelationDesc),
2453 RelationGetRelationName(rootRelInfo->ri_RelationDesc)),
2454 errhint("Consider defining the foreign key on table \"%s\".",
2455 RelationGetRelationName(rootRelInfo->ri_RelationDesc))));
2456 }
2457
2458 /* Perform the root table's triggers. */
2461 tupleid, NULL, newslot, NIL, NULL, true);
2462}
int errhint(const char *fmt,...) pg_attribute_printf(1
List * ExecGetAncestorResultRels(EState *estate, ResultRelInfo *resultRelInfo)
Definition execMain.c:1449
#define RelationGetRelationName(relation)
Definition rel.h:548
int RI_FKey_trigger_type(Oid tgfoid)
Definition pg_list.h:54
int numtriggers
Definition reltrigger.h:50
Trigger * triggers
Definition reltrigger.h:49
bool trig_update_after_row
Definition reltrigger.h:62
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ResultRelInfo *src_partinfo, ResultRelInfo *dst_partinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition trigger.c:3146
#define RI_TRIGGER_PK
Definition trigger.h:286

References ereport, errcode(), errdetail(), errhint(), errmsg, ERROR, ModifyTableContext::estate, ExecARUpdateTriggers(), ExecGetAncestorResultRels(), fb(), i, lfirst, NIL, TriggerDesc::numtriggers, RelationGetRelationName, RI_FKey_trigger_type(), RI_TRIGGER_PK, TriggerDesc::trig_update_after_row, and TriggerDesc::triggers.

Referenced by ExecUpdateAct().

◆ ExecDelete()

static TupleTableSlot * ExecDelete ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
bool  processReturning,
bool  changingPart,
bool  canSetTag,
TM_Result tmresult,
bool tupleDeleted,
TupleTableSlot **  epqreturnslot 
)
static

Definition at line 1601 of file nodeModifyTable.c.

1611{
1612 EState *estate = context->estate;
1614 TupleTableSlot *slot = NULL;
1615 TM_Result result;
1616 bool saveOld;
1617
1618 if (tupleDeleted)
1619 *tupleDeleted = false;
1620
1621 /*
1622 * Prepare for the delete. This includes BEFORE ROW triggers, so we're
1623 * done if it says we are.
1624 */
1625 if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple,
1627 return NULL;
1628
1629 /* INSTEAD OF ROW DELETE Triggers */
1630 if (resultRelInfo->ri_TrigDesc &&
1631 resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
1632 {
1633 bool dodelete;
1634
1635 Assert(oldtuple != NULL);
1636 dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
1637
1638 if (!dodelete) /* "do nothing" */
1639 return NULL;
1640 }
1641 else if (resultRelInfo->ri_FdwRoutine)
1642 {
1643 /*
1644 * delete from foreign table: let the FDW do it
1645 *
1646 * We offer the returning slot as a place to store RETURNING data,
1647 * although the FDW can return some other slot if it wants.
1648 */
1649 slot = ExecGetReturningSlot(estate, resultRelInfo);
1650 slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
1651 resultRelInfo,
1652 slot,
1653 context->planSlot);
1654
1655 if (slot == NULL) /* "do nothing" */
1656 return NULL;
1657
1658 /*
1659 * RETURNING expressions might reference the tableoid column, so
1660 * (re)initialize tts_tableOid before evaluating them.
1661 */
1662 if (TTS_EMPTY(slot))
1664
1666 }
1667 else
1668 {
1669 /*
1670 * delete the tuple
1671 *
1672 * Note: if context->estate->es_crosscheck_snapshot isn't
1673 * InvalidSnapshot, we check that the row to be deleted is visible to
1674 * that snapshot, and throw a can't-serialize error if not. This is a
1675 * special-case behavior needed for referential integrity updates in
1676 * transaction-snapshot mode transactions.
1677 */
1678ldelete:
1679 result = ExecDeleteAct(context, resultRelInfo, tupleid, changingPart);
1680
1681 if (tmresult)
1682 *tmresult = result;
1683
1684 switch (result)
1685 {
1686 case TM_SelfModified:
1687
1688 /*
1689 * The target tuple was already updated or deleted by the
1690 * current command, or by a later command in the current
1691 * transaction. The former case is possible in a join DELETE
1692 * where multiple tuples join to the same target tuple. This
1693 * is somewhat questionable, but Postgres has always allowed
1694 * it: we just ignore additional deletion attempts.
1695 *
1696 * The latter case arises if the tuple is modified by a
1697 * command in a BEFORE trigger, or perhaps by a command in a
1698 * volatile function used in the query. In such situations we
1699 * should not ignore the deletion, but it is equally unsafe to
1700 * proceed. We don't want to discard the original DELETE
1701 * while keeping the triggered actions based on its deletion;
1702 * and it would be no better to allow the original DELETE
1703 * while discarding updates that it triggered. The row update
1704 * carries some information that might be important according
1705 * to business rules; so throwing an error is the only safe
1706 * course.
1707 *
1708 * If a trigger actually intends this type of interaction, it
1709 * can re-execute the DELETE and then return NULL to cancel
1710 * the outer delete.
1711 */
1712 if (context->tmfd.cmax != estate->es_output_cid)
1713 ereport(ERROR,
1715 errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1716 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1717
1718 /* Else, already deleted by self; nothing to do */
1719 return NULL;
1720
1721 case TM_Ok:
1722 break;
1723
1724 case TM_Updated:
1725 {
1726 TupleTableSlot *inputslot;
1728
1730 ereport(ERROR,
1732 errmsg("could not serialize access due to concurrent update")));
1733
1734 /*
1735 * Already know that we're going to need to do EPQ, so
1736 * fetch tuple directly into the right slot.
1737 */
1738 EvalPlanQualBegin(context->epqstate);
1739 inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
1740 resultRelInfo->ri_RangeTableIndex);
1741
1743 estate->es_snapshot,
1744 inputslot, estate->es_output_cid,
1747 &context->tmfd);
1748
1749 switch (result)
1750 {
1751 case TM_Ok:
1752 Assert(context->tmfd.traversed);
1753 epqslot = EvalPlanQual(context->epqstate,
1755 resultRelInfo->ri_RangeTableIndex,
1756 inputslot);
1757 if (TupIsNull(epqslot))
1758 /* Tuple not passing quals anymore, exiting... */
1759 return NULL;
1760
1761 /*
1762 * If requested, skip delete and pass back the
1763 * updated row.
1764 */
1765 if (epqreturnslot)
1766 {
1768 return NULL;
1769 }
1770 else
1771 goto ldelete;
1772
1773 case TM_SelfModified:
1774
1775 /*
1776 * This can be reached when following an update
1777 * chain from a tuple updated by another session,
1778 * reaching a tuple that was already updated in
1779 * this transaction. If previously updated by this
1780 * command, ignore the delete, otherwise error
1781 * out.
1782 *
1783 * See also TM_SelfModified response to
1784 * table_tuple_delete() above.
1785 */
1786 if (context->tmfd.cmax != estate->es_output_cid)
1787 ereport(ERROR,
1789 errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1790 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1791 return NULL;
1792
1793 case TM_Deleted:
1794 /* tuple already deleted; nothing to do */
1795 return NULL;
1796
1797 default:
1798
1799 /*
1800 * TM_Invisible should be impossible because we're
1801 * waiting for updated row versions, and would
1802 * already have errored out if the first version
1803 * is invisible.
1804 *
1805 * TM_Updated should be impossible, because we're
1806 * locking the latest version via
1807 * TUPLE_LOCK_FLAG_FIND_LAST_VERSION.
1808 */
1809 elog(ERROR, "unexpected table_tuple_lock status: %u",
1810 result);
1811 return NULL;
1812 }
1813
1814 Assert(false);
1815 break;
1816 }
1817
1818 case TM_Deleted:
1820 ereport(ERROR,
1822 errmsg("could not serialize access due to concurrent delete")));
1823 /* tuple already deleted; nothing to do */
1824 return NULL;
1825
1826 default:
1827 elog(ERROR, "unrecognized table_tuple_delete status: %u",
1828 result);
1829 return NULL;
1830 }
1831
1832 /*
1833 * Note: Normally one would think that we have to delete index tuples
1834 * associated with the heap tuple now...
1835 *
1836 * ... but in POSTGRES, we have no need to do this because VACUUM will
1837 * take care of it later. We can't delete index tuples immediately
1838 * anyway, since the tuple is still visible to other transactions.
1839 */
1840 }
1841
1842 if (canSetTag)
1843 (estate->es_processed)++;
1844
1845 /* Tell caller that the delete actually happened. */
1846 if (tupleDeleted)
1847 *tupleDeleted = true;
1848
1849 ExecDeleteEpilogue(context, resultRelInfo, tupleid, oldtuple, changingPart);
1850
1851 /*
1852 * Process RETURNING if present and if requested.
1853 *
1854 * If this is part of a cross-partition UPDATE, and the RETURNING list
1855 * refers to any OLD column values, save the old tuple here for later
1856 * processing of the RETURNING list by ExecInsert().
1857 */
1858 saveOld = changingPart && resultRelInfo->ri_projectReturning &&
1860
1861 if (resultRelInfo->ri_projectReturning && (processReturning || saveOld))
1862 {
1863 /*
1864 * We have to put the target tuple into a slot, which means first we
1865 * gotta fetch it. We can use the trigger tuple slot.
1866 */
1868
1869 if (resultRelInfo->ri_FdwRoutine)
1870 {
1871 /* FDW must have provided a slot containing the deleted row */
1872 Assert(!TupIsNull(slot));
1873 }
1874 else
1875 {
1876 slot = ExecGetReturningSlot(estate, resultRelInfo);
1877 if (oldtuple != NULL)
1878 {
1879 ExecForceStoreHeapTuple(oldtuple, slot, false);
1880 }
1881 else
1882 {
1884 SnapshotAny, slot))
1885 elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
1886 }
1887 }
1888
1889 /*
1890 * If required, save the old tuple for later processing of the
1891 * RETURNING list by ExecInsert().
1892 */
1893 if (saveOld)
1894 {
1896
1897 /*
1898 * Convert the tuple into the root partition's format/slot, if
1899 * needed. ExecInsert() will then convert it to the new
1900 * partition's format/slot, if necessary.
1901 */
1902 tupconv_map = ExecGetChildToRootMap(resultRelInfo);
1903 if (tupconv_map != NULL)
1904 {
1906 TupleTableSlot *oldSlot = slot;
1907
1908 slot = execute_attr_map_slot(tupconv_map->attrMap,
1909 slot,
1910 ExecGetReturningSlot(estate,
1911 rootRelInfo));
1912
1913 slot->tts_tableOid = oldSlot->tts_tableOid;
1914 ItemPointerCopy(&oldSlot->tts_tid, &slot->tts_tid);
1915 }
1916
1917 context->cpDeletedSlot = slot;
1918
1919 return NULL;
1920 }
1921
1922 rslot = ExecProcessReturning(context, resultRelInfo, true,
1923 slot, NULL, context->planSlot);
1924
1925 /*
1926 * Before releasing the target tuple again, make sure rslot has a
1927 * local copy of any pass-by-reference values.
1928 */
1930
1931 ExecClearTuple(slot);
1932
1933 return rslot;
1934 }
1935
1936 return NULL;
1937}
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition execMain.c:2795
void EvalPlanQualBegin(EPQState *epqstate)
Definition execMain.c:2950
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition execMain.c:2668
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1253
#define EEO_FLAG_HAS_OLD
Definition execnodes.h:87
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition itemptr.h:172
@ LockWaitBlock
Definition lockoptions.h:40
@ LockTupleExclusive
Definition lockoptions.h:59
static bool ExecDeletePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot **epqreturnslot, TM_Result *result)
static TM_Result ExecDeleteAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, bool changingPart)
static void ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool changingPart)
static TupleTableSlot * ExecProcessReturning(ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool isDelete, TupleTableSlot *oldSlot, TupleTableSlot *newSlot, TupleTableSlot *planSlot)
CommandId es_output_cid
Definition execnodes.h:694
uint8 flags
Definition execnodes.h:100
ExecForeignDelete_function ExecForeignDelete
Definition fdwapi.h:236
TM_FailureData tmfd
TupleTableSlot * planSlot
ExprState pi_state
Definition execnodes.h:397
TriggerDesc * ri_TrigDesc
Definition execnodes.h:527
Index ri_RangeTableIndex
Definition execnodes.h:489
ProjectionInfo * ri_projectReturning
Definition execnodes.h:589
CommandId cmax
Definition tableam.h:151
bool trig_delete_instead_row
Definition reltrigger.h:68
ItemPointerData tts_tid
Definition tuptable.h:142
TM_Result
Definition tableam.h:73
@ TM_Deleted
Definition tableam.h:93
@ TM_Updated
Definition tableam.h:90
@ TM_SelfModified
Definition tableam.h:84
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition tableam.h:1570
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition tableam.h:267
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition trigger.c:2850
#define TTS_EMPTY(slot)
Definition tuptable.h:92

References Assert, TM_FailureData::cmax, ModifyTableContext::cpDeletedSlot, EEO_FLAG_HAS_OLD, elog, ModifyTableContext::epqstate, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg, ERROR, EState::es_output_cid, EState::es_processed, EState::es_snapshot, ModifyTableContext::estate, EvalPlanQual(), EvalPlanQualBegin(), EvalPlanQualSlot(), ExecClearTuple(), ExecDeleteAct(), ExecDeleteEpilogue(), ExecDeletePrologue(), ExecForceStoreHeapTuple(), FdwRoutine::ExecForeignDelete, ExecGetChildToRootMap(), ExecGetReturningSlot(), ExecIRDeleteTriggers(), ExecMaterializeSlot(), ExecProcessReturning(), ExecStoreAllNullTuple(), execute_attr_map_slot(), fb(), ExprState::flags, IsolationUsesXactSnapshot, ItemPointerCopy(), LockTupleExclusive, LockWaitBlock, ModifyTableContext::mtstate, ProjectionInfo::pi_state, ModifyTableContext::planSlot, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ModifyTableState::rootResultRelInfo, SnapshotAny, table_tuple_fetch_row_version(), table_tuple_lock(), TM_Deleted, TM_Ok, TM_SelfModified, TM_Updated, ModifyTableContext::tmfd, TM_FailureData::traversed, TriggerDesc::trig_delete_instead_row, TTS_EMPTY, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_tid, TupIsNull, and TUPLE_LOCK_FLAG_FIND_LAST_VERSION.

Referenced by ExecCrossPartitionUpdate(), and ExecModifyTable().

◆ ExecDeleteAct()

static TM_Result ExecDeleteAct ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
bool  changingPart 
)
static

Definition at line 1521 of file nodeModifyTable.c.

1523{
1524 EState *estate = context->estate;
1525
1526 return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1527 estate->es_output_cid,
1528 estate->es_snapshot,
1529 estate->es_crosscheck_snapshot,
1530 true /* wait for commit */ ,
1531 &context->tmfd,
1532 changingPart);
1533}
Snapshot es_crosscheck_snapshot
Definition execnodes.h:673
static TM_Result table_tuple_delete(Relation rel, ItemPointer tid, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart)
Definition tableam.h:1478

References EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, fb(), ResultRelInfo::ri_RelationDesc, table_tuple_delete(), and ModifyTableContext::tmfd.

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecDeleteEpilogue()

static void ExecDeleteEpilogue ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
bool  changingPart 
)
static

Definition at line 1543 of file nodeModifyTable.c.

1545{
1546 ModifyTableState *mtstate = context->mtstate;
1547 EState *estate = context->estate;
1549
1550 /*
1551 * If this delete is the result of a partition key update that moved the
1552 * tuple to a new partition, put this row into the transition OLD TABLE,
1553 * if there is one. We need to do this separately for DELETE and INSERT
1554 * because they happen on different tables.
1555 */
1557 if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1559 {
1560 ExecARUpdateTriggers(estate, resultRelInfo,
1561 NULL, NULL,
1562 tupleid, oldtuple,
1563 NULL, NULL, mtstate->mt_transition_capture,
1564 false);
1565
1566 /*
1567 * We've already captured the OLD TABLE row, so make sure any AR
1568 * DELETE trigger fired below doesn't capture it again.
1569 */
1571 }
1572
1573 /* AFTER ROW DELETE Triggers */
1574 ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1576}
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition trigger.c:2803

References CMD_UPDATE, ModifyTableContext::estate, ExecARDeleteTriggers(), ExecARUpdateTriggers(), fb(), ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, ModifyTableState::operation, and TransitionCaptureState::tcs_update_old_table.

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecDeletePrologue()

static bool ExecDeletePrologue ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot **  epqreturnslot,
TM_Result result 
)
static

Definition at line 1489 of file nodeModifyTable.c.

1492{
1493 if (result)
1494 *result = TM_Ok;
1495
1496 /* BEFORE ROW DELETE triggers */
1497 if (resultRelInfo->ri_TrigDesc &&
1498 resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1499 {
1500 /* Flush any pending inserts, so rows are visible to the triggers */
1502 ExecPendingInserts(context->estate);
1503
1504 return ExecBRDeleteTriggers(context->estate, context->epqstate,
1505 resultRelInfo, tupleid, oldtuple,
1506 epqreturnslot, result, &context->tmfd,
1507 context->mtstate->operation == CMD_MERGE);
1508 }
1509
1510 return true;
1511}
static void ExecPendingInserts(EState *estate)
List * es_insert_pending_result_relations
Definition execnodes.h:783
bool trig_delete_before_row
Definition reltrigger.h:66
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_delete)
Definition trigger.c:2703

References CMD_MERGE, ModifyTableContext::epqstate, EState::es_insert_pending_result_relations, ModifyTableContext::estate, ExecBRDeleteTriggers(), ExecPendingInserts(), fb(), ModifyTableContext::mtstate, NIL, ModifyTableState::operation, ResultRelInfo::ri_TrigDesc, TM_Ok, ModifyTableContext::tmfd, and TriggerDesc::trig_delete_before_row.

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 5432 of file nodeModifyTable.c.

5433{
5434 int i;
5435
5436 /*
5437 * Allow any FDWs to shut down
5438 */
5439 for (i = 0; i < node->mt_nrels; i++)
5440 {
5441 int j;
5442 ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
5443
5444 if (!resultRelInfo->ri_usesFdwDirectModify &&
5445 resultRelInfo->ri_FdwRoutine != NULL &&
5446 resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
5447 resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
5448 resultRelInfo);
5449
5450 /*
5451 * Cleanup the initialized batch slots. This only matters for FDWs
5452 * with batching, but the other cases will have ri_NumSlotsInitialized
5453 * == 0.
5454 */
5455 for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
5456 {
5457 ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
5459 }
5460 }
5461
5462 /*
5463 * Close all the partitioned tables, leaf partitions, and their indices
5464 * and release the slot used for tuple routing, if set.
5465 */
5467 {
5469
5470 if (node->mt_root_tuple_slot)
5472 }
5473
5474 /*
5475 * Terminate EPQ execution if active
5476 */
5478
5479 /*
5480 * shut down subplan
5481 */
5483}
void EvalPlanQualEnd(EPQState *epqstate)
Definition execMain.c:3198
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
#define outerPlanState(node)
Definition execnodes.h:1273
int j
Definition isn.c:78
EndForeignModify_function EndForeignModify
Definition fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition execnodes.h:1420
EPQState mt_epqstate
Definition execnodes.h:1430
TupleTableSlot ** ri_Slots
Definition execnodes.h:557
int ri_NumSlotsInitialized
Definition execnodes.h:555
TupleTableSlot ** ri_PlanSlots
Definition execnodes.h:558
bool ri_usesFdwDirectModify
Definition execnodes.h:551

References FdwRoutine::EndForeignModify, EvalPlanQualEnd(), ExecCleanupTupleRouting(), ExecDropSingleTupleTableSlot(), ExecEndNode(), fb(), i, j, ModifyTableState::mt_epqstate, ModifyTableState::mt_nrels, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, outerPlanState, ModifyTableState::ps, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumSlotsInitialized, ResultRelInfo::ri_PlanSlots, ResultRelInfo::ri_Slots, ResultRelInfo::ri_usesFdwDirectModify, and PlanState::state.

Referenced by ExecEndNode().

◆ ExecGetInsertNewTuple()

static TupleTableSlot * ExecGetInsertNewTuple ( ResultRelInfo relinfo,
TupleTableSlot planSlot 
)
static

Definition at line 780 of file nodeModifyTable.c.

782{
783 ProjectionInfo *newProj = relinfo->ri_projectNew;
784 ExprContext *econtext;
785
786 /*
787 * If there's no projection to be done, just make sure the slot is of the
788 * right type for the target rel. If the planSlot is the right type we
789 * can use it as-is, else copy the data into ri_newTupleSlot.
790 */
791 if (newProj == NULL)
792 {
793 if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
794 {
795 ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
796 return relinfo->ri_newTupleSlot;
797 }
798 else
799 return planSlot;
800 }
801
802 /*
803 * Else project; since the projection output slot is ri_newTupleSlot, this
804 * will also fix any slot-type problem.
805 *
806 * Note: currently, this is dead code, because INSERT cases don't receive
807 * any junk columns so there's never a projection to be done.
808 */
809 econtext = newProj->pi_exprContext;
810 econtext->ecxt_outertuple = planSlot;
811 return ExecProject(newProj);
812}
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition executor.h:486
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:288
const TupleTableSlotOps *const tts_ops
Definition tuptable.h:127
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:543

References ExprContext::ecxt_outertuple, ExecCopySlot(), ExecProject(), fb(), and TupleTableSlot::tts_ops.

Referenced by ExecModifyTable().

◆ ExecGetUpdateNewTuple()

TupleTableSlot * ExecGetUpdateNewTuple ( ResultRelInfo relinfo,
TupleTableSlot planSlot,
TupleTableSlot oldSlot 
)

Definition at line 824 of file nodeModifyTable.c.

827{
828 ProjectionInfo *newProj = relinfo->ri_projectNew;
829 ExprContext *econtext;
830
831 /* Use a few extra Asserts to protect against outside callers */
832 Assert(relinfo->ri_projectNewInfoValid);
833 Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
835
836 econtext = newProj->pi_exprContext;
837 econtext->ecxt_outertuple = planSlot;
838 econtext->ecxt_scantuple = oldSlot;
839 return ExecProject(newProj);
840}

References Assert, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, ExecProject(), fb(), and TTS_EMPTY.

Referenced by ExecBRUpdateTriggers(), ExecCrossPartitionUpdate(), ExecModifyTable(), and ExecUpdate().

◆ ExecInitGenerated()

void ExecInitGenerated ( ResultRelInfo resultRelInfo,
EState estate,
CmdType  cmdtype 
)

Definition at line 442 of file nodeModifyTable.c.

445{
446 Relation rel = resultRelInfo->ri_RelationDesc;
447 TupleDesc tupdesc = RelationGetDescr(rel);
448 int natts = tupdesc->natts;
451 Bitmapset *updatedCols;
453
454 /* Nothing to do if no generated columns */
455 if (!(tupdesc->constr && (tupdesc->constr->has_generated_stored || tupdesc->constr->has_generated_virtual)))
456 return;
457
458 /*
459 * In an UPDATE, we can skip computing any generated columns that do not
460 * depend on any UPDATE target column. But if there is a BEFORE ROW
461 * UPDATE trigger, we cannot skip because the trigger might change more
462 * columns.
463 */
464 if (cmdtype == CMD_UPDATE &&
466 updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
467 else
468 updatedCols = NULL;
469
470 /*
471 * Make sure these data structures are built in the per-query memory
472 * context so they'll survive throughout the query.
473 */
475
476 ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
478
479 for (int i = 0; i < natts; i++)
480 {
481 char attgenerated = TupleDescAttr(tupdesc, i)->attgenerated;
482
483 if (attgenerated)
484 {
485 Expr *expr;
486
487 /* Fetch the GENERATED AS expression tree */
488 expr = (Expr *) build_column_default(rel, i + 1);
489 if (expr == NULL)
490 elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
491 i + 1, RelationGetRelationName(rel));
492
493 /*
494 * If it's an update with a known set of update target columns,
495 * see if we can skip the computation.
496 */
497 if (updatedCols)
498 {
499 Bitmapset *attrs_used = NULL;
500
501 pull_varattnos((Node *) expr, 1, &attrs_used);
502
503 if (!bms_overlap(updatedCols, attrs_used))
504 continue; /* need not update this column */
505 }
506
507 /* No luck, so prepare the expression for execution */
508 if (attgenerated == ATTRIBUTE_GENERATED_STORED)
509 {
510 ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
512 }
513
514 /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
515 if (cmdtype == CMD_UPDATE)
516 resultRelInfo->ri_extraUpdatedCols =
517 bms_add_member(resultRelInfo->ri_extraUpdatedCols,
519 }
520 }
521
522 if (ri_NumGeneratedNeeded == 0)
523 {
524 /* didn't need it after all */
527 }
528
529 /* Save in appropriate set of fields */
530 if (cmdtype == CMD_UPDATE)
531 {
532 /* Don't call twice */
533 Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
534
535 resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
537
538 resultRelInfo->ri_extraUpdatedCols_valid = true;
539 }
540 else
541 {
542 /* Don't call twice */
543 Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
544
545 resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
547 }
548
550}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:575
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition execExpr.c:786
Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1387
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc0(Size size)
Definition mcxt.c:1417
Node * build_column_default(Relation rel, int attrno)
TriggerDesc * trigdesc
Definition rel.h:117
bool ri_extraUpdatedCols_valid
Definition execnodes.h:512
Bitmapset * ri_extraUpdatedCols
Definition execnodes.h:510
bool trig_update_before_row
Definition reltrigger.h:61
bool has_generated_virtual
Definition tupdesc.h:47
#define FirstLowInvalidHeapAttributeNumber
Definition sysattr.h:27
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition var.c:296

References Assert, bms_add_member(), bms_overlap(), build_column_default(), CMD_UPDATE, TupleDescData::constr, elog, ERROR, EState::es_query_cxt, ExecGetUpdatedCols(), ExecPrepareExpr(), fb(), FirstLowInvalidHeapAttributeNumber, TupleConstr::has_generated_stored, TupleConstr::has_generated_virtual, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc0(), pfree(), pull_varattnos(), RelationGetDescr, RelationGetRelationName, ResultRelInfo::ri_extraUpdatedCols, ResultRelInfo::ri_extraUpdatedCols_valid, ResultRelInfo::ri_GeneratedExprsI, ResultRelInfo::ri_GeneratedExprsU, ResultRelInfo::ri_NumGeneratedNeededI, ResultRelInfo::ri_NumGeneratedNeededU, ResultRelInfo::ri_RelationDesc, TriggerDesc::trig_update_before_row, RelationData::trigdesc, and TupleDescAttr().

Referenced by ExecComputeStoredGenerated(), and ExecGetExtraUpdatedCols().

◆ ExecInitInsertProjection()

static void ExecInitInsertProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 652 of file nodeModifyTable.c.

654{
655 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
656 Plan *subplan = outerPlan(node);
657 EState *estate = mtstate->ps.state;
659 bool need_projection = false;
660 ListCell *l;
661
662 /* Extract non-junk columns of the subplan's result tlist. */
663 foreach(l, subplan->targetlist)
664 {
666
667 if (!tle->resjunk)
669 else
670 need_projection = true;
671 }
672
673 /*
674 * The junk-free list must produce a tuple suitable for the result
675 * relation.
676 */
678
679 /* We'll need a slot matching the table's format. */
680 resultRelInfo->ri_newTupleSlot =
681 table_slot_create(resultRelInfo->ri_RelationDesc,
682 &estate->es_tupleTable);
683
684 /* Build ProjectionInfo if needed (it probably isn't). */
685 if (need_projection)
686 {
688
689 /* need an expression context to do the projection */
690 if (mtstate->ps.ps_ExprContext == NULL)
691 ExecAssignExprContext(estate, &mtstate->ps);
692
693 resultRelInfo->ri_projectNew =
695 mtstate->ps.ps_ExprContext,
696 resultRelInfo->ri_newTupleSlot,
697 &mtstate->ps,
698 relDesc);
699 }
700
701 resultRelInfo->ri_projectNewInfoValid = true;
702}
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition execExpr.c:391
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition execUtils.c:490
List * lappend(List *list, void *datum)
Definition list.c:339
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define outerPlan(node)
Definition plannodes.h:265
List * es_tupleTable
Definition execnodes.h:724
ExprContext * ps_ExprContext
Definition execnodes.h:1216
List * targetlist
Definition plannodes.h:233
TupleTableSlot * ri_newTupleSlot
Definition execnodes.h:517
ProjectionInfo * ri_projectNew
Definition execnodes.h:515

References EState::es_tupleTable, ExecAssignExprContext(), ExecBuildProjectionInfo(), ExecCheckPlanOutput(), fb(), lappend(), lfirst, NIL, outerPlan, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, RelationGetDescr, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_projectNew, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, PlanState::state, table_slot_create(), and Plan::targetlist.

Referenced by ExecModifyTable().

◆ ExecInitMerge()

void ExecInitMerge ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 3880 of file nodeModifyTable.c.

3881{
3882 List *mergeActionLists = mtstate->mt_mergeActionLists;
3883 List *mergeJoinConditions = mtstate->mt_mergeJoinConditions;
3885 ResultRelInfo *resultRelInfo;
3886 ExprContext *econtext;
3887 ListCell *lc;
3888 int i;
3889
3890 if (mergeActionLists == NIL)
3891 return;
3892
3893 mtstate->mt_merge_subcommands = 0;
3894
3895 if (mtstate->ps.ps_ExprContext == NULL)
3896 ExecAssignExprContext(estate, &mtstate->ps);
3897 econtext = mtstate->ps.ps_ExprContext;
3898
3899 /*
3900 * Create a MergeActionState for each action on the mergeActionList and
3901 * add it to either a list of matched actions or not-matched actions.
3902 *
3903 * Similar logic appears in ExecInitPartitionInfo(), so if changing
3904 * anything here, do so there too.
3905 */
3906 i = 0;
3907 foreach(lc, mergeActionLists)
3908 {
3909 List *mergeActionList = lfirst(lc);
3910 Node *joinCondition;
3912 ListCell *l;
3913
3914 joinCondition = (Node *) list_nth(mergeJoinConditions, i);
3915 resultRelInfo = mtstate->resultRelInfo + i;
3916 i++;
3918
3919 /* initialize slots for MERGE fetches from this rel */
3920 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3921 ExecInitMergeTupleSlots(mtstate, resultRelInfo);
3922
3923 /* initialize state for join condition checking */
3924 resultRelInfo->ri_MergeJoinCondition =
3925 ExecInitQual((List *) joinCondition, &mtstate->ps);
3926
3927 foreach(l, mergeActionList)
3928 {
3930 MergeActionState *action_state;
3933
3934 /*
3935 * Build action merge state for this rel. (For partitions,
3936 * equivalent code exists in ExecInitPartitionInfo.)
3937 */
3938 action_state = makeNode(MergeActionState);
3939 action_state->mas_action = action;
3940 action_state->mas_whenqual = ExecInitQual((List *) action->qual,
3941 &mtstate->ps);
3942
3943 /*
3944 * We create three lists - one for each MergeMatchKind - and stick
3945 * the MergeActionState into the appropriate list.
3946 */
3947 resultRelInfo->ri_MergeActions[action->matchKind] =
3948 lappend(resultRelInfo->ri_MergeActions[action->matchKind],
3949 action_state);
3950
3951 switch (action->commandType)
3952 {
3953 case CMD_INSERT:
3954 /* INSERT actions always use rootRelInfo */
3955 ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
3956 action->targetList);
3957
3958 /*
3959 * If the MERGE targets a partitioned table, any INSERT
3960 * actions must be routed through it, not the child
3961 * relations. Initialize the routing struct and the root
3962 * table's "new" tuple slot for that, if not already done.
3963 * The projection we prepare, for all relations, uses the
3964 * root relation descriptor, and targets the plan's root
3965 * slot. (This is consistent with the fact that we
3966 * checked the plan output to match the root relation,
3967 * above.)
3968 */
3969 if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
3971 {
3972 if (mtstate->mt_partition_tuple_routing == NULL)
3973 {
3974 /*
3975 * Initialize planstate for routing if not already
3976 * done.
3977 *
3978 * Note that the slot is managed as a standalone
3979 * slot belonging to ModifyTableState, so we pass
3980 * NULL for the 2nd argument.
3981 */
3982 mtstate->mt_root_tuple_slot =
3983 table_slot_create(rootRelInfo->ri_RelationDesc,
3984 NULL);
3987 rootRelInfo->ri_RelationDesc);
3988 }
3989 tgtslot = mtstate->mt_root_tuple_slot;
3990 tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3991 }
3992 else
3993 {
3994 /*
3995 * If the MERGE targets an inherited table, we insert
3996 * into the root table, so we must initialize its
3997 * "new" tuple slot, if not already done, and use its
3998 * relation descriptor for the projection.
3999 *
4000 * For non-inherited tables, rootRelInfo and
4001 * resultRelInfo are the same, and the "new" tuple
4002 * slot will already have been initialized.
4003 */
4004 if (rootRelInfo->ri_newTupleSlot == NULL)
4005 rootRelInfo->ri_newTupleSlot =
4006 table_slot_create(rootRelInfo->ri_RelationDesc,
4007 &estate->es_tupleTable);
4008
4009 tgtslot = rootRelInfo->ri_newTupleSlot;
4010 tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
4011 }
4012
4013 action_state->mas_proj =
4014 ExecBuildProjectionInfo(action->targetList, econtext,
4015 tgtslot,
4016 &mtstate->ps,
4017 tgtdesc);
4018
4020 break;
4021 case CMD_UPDATE:
4022 action_state->mas_proj =
4024 true,
4025 action->updateColnos,
4027 econtext,
4028 resultRelInfo->ri_newTupleSlot,
4029 &mtstate->ps);
4031 break;
4032 case CMD_DELETE:
4034 break;
4035 case CMD_NOTHING:
4036 break;
4037 default:
4038 elog(ERROR, "unknown action in MERGE WHEN clause");
4039 break;
4040 }
4041 }
4042 }
4043
4044 /*
4045 * If the MERGE targets an inherited table, any INSERT actions will use
4046 * rootRelInfo, and rootRelInfo will not be in the resultRelInfo array.
4047 * Therefore we must initialize its WITH CHECK OPTION constraints and
4048 * RETURNING projection, as ExecInitModifyTable did for the resultRelInfo
4049 * entries.
4050 *
4051 * Note that the planner does not build a withCheckOptionList or
4052 * returningList for the root relation, but as in ExecInitPartitionInfo,
4053 * we can use the first resultRelInfo entry as a reference to calculate
4054 * the attno's for the root table.
4055 */
4056 if (rootRelInfo != mtstate->resultRelInfo &&
4058 (mtstate->mt_merge_subcommands & MERGE_INSERT) != 0)
4059 {
4060 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
4061 Relation rootRelation = rootRelInfo->ri_RelationDesc;
4065 bool found_whole_row;
4066
4067 if (node->withCheckOptionLists != NIL)
4068 {
4069 List *wcoList;
4070 List *wcoExprs = NIL;
4071
4072 /* There should be as many WCO lists as result rels */
4073 Assert(list_length(node->withCheckOptionLists) ==
4074 list_length(node->resultRelations));
4075
4076 /*
4077 * Use the first WCO list as a reference. In the most common case,
4078 * this will be for the same relation as rootRelInfo, and so there
4079 * will be no need to adjust its attno's.
4080 */
4081 wcoList = linitial(node->withCheckOptionLists);
4082 if (rootRelation != firstResultRel)
4083 {
4084 /* Convert any Vars in it to contain the root's attno's */
4085 part_attmap =
4088 false);
4089
4090 wcoList = (List *)
4092 firstVarno, 0,
4094 RelationGetForm(rootRelation)->reltype,
4095 &found_whole_row);
4096 }
4097
4098 foreach(lc, wcoList)
4099 {
4102 &mtstate->ps);
4103
4105 }
4106
4107 rootRelInfo->ri_WithCheckOptions = wcoList;
4108 rootRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4109 }
4110
4111 if (node->returningLists != NIL)
4112 {
4113 List *returningList;
4114
4115 /* There should be as many returning lists as result rels */
4116 Assert(list_length(node->returningLists) ==
4117 list_length(node->resultRelations));
4118
4119 /*
4120 * Use the first returning list as a reference. In the most common
4121 * case, this will be for the same relation as rootRelInfo, and so
4122 * there will be no need to adjust its attno's.
4123 */
4124 returningList = linitial(node->returningLists);
4125 if (rootRelation != firstResultRel)
4126 {
4127 /* Convert any Vars in it to contain the root's attno's */
4128 if (part_attmap == NULL)
4129 part_attmap =
4132 false);
4133
4134 returningList = (List *)
4135 map_variable_attnos((Node *) returningList,
4136 firstVarno, 0,
4138 RelationGetForm(rootRelation)->reltype,
4139 &found_whole_row);
4140 }
4141 rootRelInfo->ri_returningList = returningList;
4142
4143 /* Initialize the RETURNING projection */
4144 rootRelInfo->ri_projectReturning =
4145 ExecBuildProjectionInfo(returningList, econtext,
4146 mtstate->ps.ps_ResultTupleSlot,
4147 &mtstate->ps,
4148 RelationGetDescr(rootRelation));
4149 }
4150 }
4151}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:175
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition execExpr.c:250
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition execExpr.c:568
#define MERGE_UPDATE
Definition execnodes.h:1406
#define MERGE_INSERT
Definition execnodes.h:1405
#define MERGE_DELETE
Definition execnodes.h:1407
void ExecInitMergeTupleSlots(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
@ CMD_INSERT
Definition nodes.h:277
@ CMD_DELETE
Definition nodes.h:278
@ CMD_NOTHING
Definition nodes.h:282
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
#define lfirst_node(type, lc)
Definition pg_list.h:176
static int list_length(const List *l)
Definition pg_list.h:152
static void * list_nth(const List *list, int n)
Definition pg_list.h:299
#define linitial(l)
Definition pg_list.h:178
#define RelationGetForm(relation)
Definition rel.h:508
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
MergeAction * mas_action
Definition execnodes.h:461
ProjectionInfo * mas_proj
Definition execnodes.h:462
ExprState * mas_whenqual
Definition execnodes.h:464
List * mt_mergeJoinConditions
Definition execnodes.h:1484
List * mt_mergeActionLists
Definition execnodes.h:1483
TupleTableSlot * ps_ResultTupleSlot
Definition execnodes.h:1215
Form_pg_class rd_rel
Definition rel.h:111
ExprState * ri_MergeJoinCondition
Definition execnodes.h:601
List * ri_MergeActions[NUM_MERGE_MATCH_KINDS]
Definition execnodes.h:598

References Assert, build_attrmap_by_name(), castNode, CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_UPDATE, elog, ERROR, EState::es_tupleTable, ExecAssignExprContext(), ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecCheckPlanOutput(), ExecInitMergeTupleSlots(), ExecInitQual(), ExecSetupPartitionTupleRouting(), fb(), i, lappend(), lfirst, lfirst_node, linitial, list_length(), list_nth(), makeNode, map_variable_attnos(), MergeActionState::mas_action, MergeActionState::mas_proj, MergeActionState::mas_whenqual, MERGE_DELETE, MERGE_INSERT, MERGE_UPDATE, ModifyTableState::mt_merge_subcommands, ModifyTableState::mt_mergeActionLists, ModifyTableState::mt_mergeJoinConditions, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, NIL, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, RelationData::rd_rel, RelationGetDescr, RelationGetForm, ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_MergeActions, ResultRelInfo::ri_MergeJoinCondition, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ModifyTableState::rootResultRelInfo, table_slot_create(), unlikely, and ModifyTable::withCheckOptionLists.

Referenced by ExecInitModifyTable().

◆ ExecInitMergeTupleSlots()

void ExecInitMergeTupleSlots ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)

Definition at line 4160 of file nodeModifyTable.c.

4162{
4163 EState *estate = mtstate->ps.state;
4164
4165 Assert(!resultRelInfo->ri_projectNewInfoValid);
4166
4167 resultRelInfo->ri_oldTupleSlot =
4168 table_slot_create(resultRelInfo->ri_RelationDesc,
4169 &estate->es_tupleTable);
4170 resultRelInfo->ri_newTupleSlot =
4171 table_slot_create(resultRelInfo->ri_RelationDesc,
4172 &estate->es_tupleTable);
4173 resultRelInfo->ri_projectNewInfoValid = true;
4174}

References Assert, EState::es_tupleTable, ModifyTableState::ps, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, PlanState::state, and table_slot_create().

Referenced by ExecInitMerge(), and ExecInitPartitionInfo().

◆ ExecInitModifyTable()

ModifyTableState * ExecInitModifyTable ( ModifyTable node,
EState estate,
int  eflags 
)

Definition at line 4833 of file nodeModifyTable.c.

4834{
4835 ModifyTableState *mtstate;
4836 Plan *subplan = outerPlan(node);
4837 CmdType operation = node->operation;
4839 int nrels;
4840 List *resultRelations = NIL;
4841 List *withCheckOptionLists = NIL;
4842 List *returningLists = NIL;
4843 List *updateColnosLists = NIL;
4844 List *mergeActionLists = NIL;
4845 List *mergeJoinConditions = NIL;
4846 ResultRelInfo *resultRelInfo;
4847 List *arowmarks;
4848 ListCell *l;
4849 int i;
4850 Relation rel;
4851
4852 /* check for unsupported flags */
4853 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
4854
4855 /*
4856 * Only consider unpruned relations for initializing their ResultRelInfo
4857 * struct and other fields such as withCheckOptions, etc.
4858 *
4859 * Note: We must avoid pruning every result relation. This is important
4860 * for MERGE, since even if every result relation is pruned from the
4861 * subplan, there might still be NOT MATCHED rows, for which there may be
4862 * INSERT actions to perform. To allow these actions to be found, at
4863 * least one result relation must be kept. Also, when inserting into a
4864 * partitioned table, ExecInitPartitionInfo() needs a ResultRelInfo struct
4865 * as a reference for building the ResultRelInfo of the target partition.
4866 * In either case, it doesn't matter which result relation is kept, so we
4867 * just keep the first one, if all others have been pruned. See also,
4868 * ExecDoInitialPruning(), which ensures that this first result relation
4869 * has been locked.
4870 */
4871 i = 0;
4872 foreach(l, node->resultRelations)
4873 {
4874 Index rti = lfirst_int(l);
4875 bool keep_rel;
4876
4878 if (!keep_rel && i == total_nrels - 1 && resultRelations == NIL)
4879 {
4880 /* all result relations pruned; keep the first one */
4881 keep_rel = true;
4882 rti = linitial_int(node->resultRelations);
4883 i = 0;
4884 }
4885
4886 if (keep_rel)
4887 {
4888 resultRelations = lappend_int(resultRelations, rti);
4889 if (node->withCheckOptionLists)
4890 {
4893 i);
4894
4895 withCheckOptionLists = lappend(withCheckOptionLists, withCheckOptions);
4896 }
4897 if (node->returningLists)
4898 {
4899 List *returningList = list_nth_node(List,
4900 node->returningLists,
4901 i);
4902
4903 returningLists = lappend(returningLists, returningList);
4904 }
4905 if (node->updateColnosLists)
4906 {
4908
4909 updateColnosLists = lappend(updateColnosLists, updateColnosList);
4910 }
4911 if (node->mergeActionLists)
4912 {
4913 List *mergeActionList = list_nth(node->mergeActionLists, i);
4914
4915 mergeActionLists = lappend(mergeActionLists, mergeActionList);
4916 }
4917 if (node->mergeJoinConditions)
4918 {
4919 List *mergeJoinCondition = list_nth(node->mergeJoinConditions, i);
4920
4921 mergeJoinConditions = lappend(mergeJoinConditions, mergeJoinCondition);
4922 }
4923 }
4924 i++;
4925 }
4926 nrels = list_length(resultRelations);
4927 Assert(nrels > 0);
4928
4929 /*
4930 * create state structure
4931 */
4932 mtstate = makeNode(ModifyTableState);
4933 mtstate->ps.plan = (Plan *) node;
4934 mtstate->ps.state = estate;
4935 mtstate->ps.ExecProcNode = ExecModifyTable;
4936
4937 mtstate->operation = operation;
4938 mtstate->canSetTag = node->canSetTag;
4939 mtstate->mt_done = false;
4940
4941 mtstate->mt_nrels = nrels;
4942 mtstate->resultRelInfo = palloc_array(ResultRelInfo, nrels);
4943
4945 mtstate->mt_merge_inserted = 0;
4946 mtstate->mt_merge_updated = 0;
4947 mtstate->mt_merge_deleted = 0;
4948 mtstate->mt_updateColnosLists = updateColnosLists;
4949 mtstate->mt_mergeActionLists = mergeActionLists;
4950 mtstate->mt_mergeJoinConditions = mergeJoinConditions;
4951
4952 /*----------
4953 * Resolve the target relation. This is the same as:
4954 *
4955 * - the relation for which we will fire FOR STATEMENT triggers,
4956 * - the relation into whose tuple format all captured transition tuples
4957 * must be converted, and
4958 * - the root partitioned table used for tuple routing.
4959 *
4960 * If it's a partitioned or inherited table, the root partition or
4961 * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
4962 * given explicitly in node->rootRelation. Otherwise, the target relation
4963 * is the sole relation in the node->resultRelations list and, since it can
4964 * never be pruned, also in the resultRelations list constructed above.
4965 *----------
4966 */
4967 if (node->rootRelation > 0)
4968 {
4972 node->rootRelation);
4973 }
4974 else
4975 {
4976 Assert(list_length(node->resultRelations) == 1);
4977 Assert(list_length(resultRelations) == 1);
4978 mtstate->rootResultRelInfo = mtstate->resultRelInfo;
4979 ExecInitResultRelation(estate, mtstate->resultRelInfo,
4980 linitial_int(resultRelations));
4981 }
4982
4983 /* set up epqstate with dummy subplan data for the moment */
4984 EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
4985 node->epqParam, resultRelations);
4986 mtstate->fireBSTriggers = true;
4987
4988 /*
4989 * Build state for collecting transition tuples. This requires having a
4990 * valid trigger query context, so skip it in explain-only mode.
4991 */
4992 if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
4993 ExecSetupTransitionCaptureState(mtstate, estate);
4994
4995 /*
4996 * Open all the result relations and initialize the ResultRelInfo structs.
4997 * (But root relation was initialized above, if it's part of the array.)
4998 * We must do this before initializing the subplan, because direct-modify
4999 * FDWs expect their ResultRelInfos to be available.
5000 */
5001 resultRelInfo = mtstate->resultRelInfo;
5002 i = 0;
5003 foreach(l, resultRelations)
5004 {
5005 Index resultRelation = lfirst_int(l);
5007
5008 if (mergeActionLists)
5009 mergeActions = list_nth(mergeActionLists, i);
5010
5011 if (resultRelInfo != mtstate->rootResultRelInfo)
5012 {
5013 ExecInitResultRelation(estate, resultRelInfo, resultRelation);
5014
5015 /*
5016 * For child result relations, store the root result relation
5017 * pointer. We do so for the convenience of places that want to
5018 * look at the query's original target relation but don't have the
5019 * mtstate handy.
5020 */
5021 resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
5022 }
5023
5024 /* Initialize the usesFdwDirectModify flag */
5025 resultRelInfo->ri_usesFdwDirectModify =
5027
5028 /*
5029 * Verify result relation is a valid target for the current operation
5030 */
5031 CheckValidResultRel(resultRelInfo, operation, node->onConflictAction,
5032 mergeActions);
5033
5034 resultRelInfo++;
5035 i++;
5036 }
5037
5038 /*
5039 * Now we may initialize the subplan.
5040 */
5041 outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
5042
5043 /*
5044 * Do additional per-result-relation initialization.
5045 */
5046 for (i = 0; i < nrels; i++)
5047 {
5048 resultRelInfo = &mtstate->resultRelInfo[i];
5049
5050 /* Let FDWs init themselves for foreign-table result rels */
5051 if (!resultRelInfo->ri_usesFdwDirectModify &&
5052 resultRelInfo->ri_FdwRoutine != NULL &&
5053 resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
5054 {
5055 List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
5056
5057 resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
5058 resultRelInfo,
5059 fdw_private,
5060 i,
5061 eflags);
5062 }
5063
5064 /*
5065 * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
5066 * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
5067 * tables, the FDW might have created additional junk attr(s), but
5068 * those are no concern of ours.
5069 */
5070 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
5071 operation == CMD_MERGE)
5072 {
5073 char relkind;
5074
5075 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
5076 if (relkind == RELKIND_RELATION ||
5077 relkind == RELKIND_MATVIEW ||
5078 relkind == RELKIND_PARTITIONED_TABLE)
5079 {
5080 resultRelInfo->ri_RowIdAttNo =
5081 ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
5082
5083 /*
5084 * For heap relations, a ctid junk attribute must be present.
5085 * Partitioned tables should only appear here when all leaf
5086 * partitions were pruned, in which case no rows can be
5087 * produced and ctid is not needed.
5088 */
5089 if (relkind == RELKIND_PARTITIONED_TABLE)
5090 Assert(nrels == 1);
5091 else if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
5092 elog(ERROR, "could not find junk ctid column");
5093 }
5094 else if (relkind == RELKIND_FOREIGN_TABLE)
5095 {
5096 /*
5097 * We don't support MERGE with foreign tables for now. (It's
5098 * problematic because the implementation uses CTID.)
5099 */
5100 Assert(operation != CMD_MERGE);
5101
5102 /*
5103 * When there is a row-level trigger, there should be a
5104 * wholerow attribute. We also require it to be present in
5105 * UPDATE and MERGE, so we can get the values of unchanged
5106 * columns.
5107 */
5108 resultRelInfo->ri_RowIdAttNo =
5110 "wholerow");
5111 if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
5112 !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
5113 elog(ERROR, "could not find junk wholerow column");
5114 }
5115 else
5116 {
5117 /* Other valid target relkinds must provide wholerow */
5118 resultRelInfo->ri_RowIdAttNo =
5120 "wholerow");
5121 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
5122 elog(ERROR, "could not find junk wholerow column");
5123 }
5124 }
5125 }
5126
5127 /*
5128 * If this is an inherited update/delete/merge, there will be a junk
5129 * attribute named "tableoid" present in the subplan's targetlist. It
5130 * will be used to identify the result relation for a given tuple to be
5131 * updated/deleted/merged.
5132 */
5133 mtstate->mt_resultOidAttno =
5134 ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
5136 mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
5137 mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
5138
5139 /* Get the root target relation */
5140 rel = mtstate->rootResultRelInfo->ri_RelationDesc;
5141
5142 /*
5143 * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
5144 * or MERGE might need this too, but only if it actually moves tuples
5145 * between partitions; in that case setup is done by
5146 * ExecCrossPartitionUpdate.
5147 */
5148 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5149 operation == CMD_INSERT)
5151 ExecSetupPartitionTupleRouting(estate, rel);
5152
5153 /*
5154 * Initialize any WITH CHECK OPTION constraints if needed.
5155 */
5156 resultRelInfo = mtstate->resultRelInfo;
5157 foreach(l, withCheckOptionLists)
5158 {
5159 List *wcoList = (List *) lfirst(l);
5160 List *wcoExprs = NIL;
5161 ListCell *ll;
5162
5163 foreach(ll, wcoList)
5164 {
5166 ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
5167 &mtstate->ps);
5168
5170 }
5171
5172 resultRelInfo->ri_WithCheckOptions = wcoList;
5173 resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
5174 resultRelInfo++;
5175 }
5176
5177 /*
5178 * Initialize RETURNING projections if needed.
5179 */
5180 if (returningLists)
5181 {
5182 TupleTableSlot *slot;
5183 ExprContext *econtext;
5184
5185 /*
5186 * Initialize result tuple slot and assign its rowtype using the plan
5187 * node's declared targetlist, which the planner set up to be the same
5188 * as the first (before runtime pruning) RETURNING list. We assume
5189 * all the result rels will produce compatible output.
5190 */
5192 slot = mtstate->ps.ps_ResultTupleSlot;
5193
5194 /* Need an econtext too */
5195 if (mtstate->ps.ps_ExprContext == NULL)
5196 ExecAssignExprContext(estate, &mtstate->ps);
5197 econtext = mtstate->ps.ps_ExprContext;
5198
5199 /*
5200 * Build a projection for each result rel.
5201 */
5202 resultRelInfo = mtstate->resultRelInfo;
5203 foreach(l, returningLists)
5204 {
5205 List *rlist = (List *) lfirst(l);
5206
5207 resultRelInfo->ri_returningList = rlist;
5208 resultRelInfo->ri_projectReturning =
5209 ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
5210 resultRelInfo->ri_RelationDesc->rd_att);
5211 resultRelInfo++;
5212 }
5213 }
5214 else
5215 {
5216 /*
5217 * We still must construct a dummy result tuple type, because InitPlan
5218 * expects one (maybe should change that?).
5219 */
5220 ExecInitResultTypeTL(&mtstate->ps);
5221
5222 mtstate->ps.ps_ExprContext = NULL;
5223 }
5224
5225 /* Set the list of arbiter indexes if needed for ON CONFLICT */
5226 resultRelInfo = mtstate->resultRelInfo;
5227 if (node->onConflictAction != ONCONFLICT_NONE)
5228 {
5229 /* insert may only have one relation, inheritance is not expanded */
5230 Assert(total_nrels == 1);
5231 resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
5232 }
5233
5234 /*
5235 * For ON CONFLICT DO SELECT/UPDATE, initialize the ON CONFLICT action
5236 * state.
5237 */
5238 if (node->onConflictAction == ONCONFLICT_UPDATE ||
5240 {
5242
5243 /* already exists if created by RETURNING processing above */
5244 if (mtstate->ps.ps_ExprContext == NULL)
5245 ExecAssignExprContext(estate, &mtstate->ps);
5246
5247 /* action state for DO SELECT/UPDATE */
5248 resultRelInfo->ri_onConflict = onconfl;
5249
5250 /* lock strength for DO SELECT [FOR UPDATE/SHARE] */
5252
5253 /* initialize slot for the existing tuple */
5254 onconfl->oc_Existing =
5255 table_slot_create(resultRelInfo->ri_RelationDesc,
5256 &mtstate->ps.state->es_tupleTable);
5257
5258 /*
5259 * For ON CONFLICT DO UPDATE, initialize target list and projection.
5260 */
5262 {
5263 ExprContext *econtext;
5265
5266 econtext = mtstate->ps.ps_ExprContext;
5267 relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
5268
5269 /*
5270 * Create the tuple slot for the UPDATE SET projection. We want a
5271 * slot of the table's type here, because the slot will be used to
5272 * insert into the table, and for RETURNING processing - which may
5273 * access system attributes.
5274 */
5275 onconfl->oc_ProjSlot =
5276 table_slot_create(resultRelInfo->ri_RelationDesc,
5277 &mtstate->ps.state->es_tupleTable);
5278
5279 /* build UPDATE SET projection state */
5280 onconfl->oc_ProjInfo =
5282 true,
5283 node->onConflictCols,
5285 econtext,
5286 onconfl->oc_ProjSlot,
5287 &mtstate->ps);
5288 }
5289
5290 /* initialize state to evaluate the WHERE clause, if any */
5291 if (node->onConflictWhere)
5292 {
5293 ExprState *qualexpr;
5294
5295 qualexpr = ExecInitQual((List *) node->onConflictWhere,
5296 &mtstate->ps);
5297 onconfl->oc_WhereClause = qualexpr;
5298 }
5299 }
5300
5301 /*
5302 * If we have any secondary relations in an UPDATE or DELETE, they need to
5303 * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
5304 * EvalPlanQual mechanism needs to be told about them. This also goes for
5305 * the source relations in a MERGE. Locate the relevant ExecRowMarks.
5306 */
5307 arowmarks = NIL;
5308 foreach(l, node->rowMarks)
5309 {
5311 RangeTblEntry *rte = exec_rt_fetch(rc->rti, estate);
5314
5315 /* ignore "parent" rowmarks; they are irrelevant at runtime */
5316 if (rc->isParent)
5317 continue;
5318
5319 /*
5320 * Also ignore rowmarks belonging to child tables that have been
5321 * pruned in ExecDoInitialPruning().
5322 */
5323 if (rte->rtekind == RTE_RELATION &&
5324 !bms_is_member(rc->rti, estate->es_unpruned_relids))
5325 continue;
5326
5327 /* Find ExecRowMark and build ExecAuxRowMark */
5328 erm = ExecFindRowMark(estate, rc->rti, false);
5331 }
5332
5333 /* For a MERGE command, initialize its state */
5334 if (mtstate->operation == CMD_MERGE)
5335 ExecInitMerge(mtstate, estate);
5336
5337 EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
5338
5339 /*
5340 * If there are a lot of result relations, use a hash table to speed the
5341 * lookups. If there are not a lot, a simple linear search is faster.
5342 *
5343 * It's not clear where the threshold is, but try 64 for starters. In a
5344 * debugging build, use a small threshold so that we get some test
5345 * coverage of both code paths.
5346 */
5347#ifdef USE_ASSERT_CHECKING
5348#define MT_NRELS_HASH 4
5349#else
5350#define MT_NRELS_HASH 64
5351#endif
5352 if (nrels >= MT_NRELS_HASH)
5353 {
5355
5356 hash_ctl.keysize = sizeof(Oid);
5357 hash_ctl.entrysize = sizeof(MTTargetRelLookup);
5359 mtstate->mt_resultOidHash =
5360 hash_create("ModifyTable target hash",
5361 nrels, &hash_ctl,
5363 for (i = 0; i < nrels; i++)
5364 {
5365 Oid hashkey;
5367 bool found;
5368
5369 resultRelInfo = &mtstate->resultRelInfo[i];
5370 hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
5373 HASH_ENTER, &found);
5374 Assert(!found);
5375 mtlookup->relationIndex = i;
5376 }
5377 }
5378 else
5379 mtstate->mt_resultOidHash = NULL;
5380
5381 /*
5382 * Determine if the FDW supports batch insert and determine the batch size
5383 * (a FDW may support batching, but it may be disabled for the
5384 * server/table).
5385 *
5386 * We only do this for INSERT, so that for UPDATE/DELETE the batch size
5387 * remains set to 0.
5388 */
5389 if (operation == CMD_INSERT)
5390 {
5391 /* insert may only have one relation, inheritance is not expanded */
5392 Assert(total_nrels == 1);
5393 resultRelInfo = mtstate->resultRelInfo;
5394 if (!resultRelInfo->ri_usesFdwDirectModify &&
5395 resultRelInfo->ri_FdwRoutine != NULL &&
5396 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
5397 resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
5398 {
5399 resultRelInfo->ri_BatchSize =
5400 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
5401 Assert(resultRelInfo->ri_BatchSize >= 1);
5402 }
5403 else
5404 resultRelInfo->ri_BatchSize = 1;
5405 }
5406
5407 /*
5408 * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
5409 * to estate->es_auxmodifytables so that it will be run to completion by
5410 * ExecPostprocessPlan. (It'd actually work fine to add the primary
5411 * ModifyTable node too, but there's no need.) Note the use of lcons not
5412 * lappend: we need later-initialized ModifyTable nodes to be shut down
5413 * before earlier ones. This ensures that we don't throw away RETURNING
5414 * rows that need to be seen by a later CTE subplan.
5415 */
5416 if (!mtstate->canSetTag)
5417 estate->es_auxmodifytables = lcons(mtstate,
5418 estate->es_auxmodifytables);
5419
5420 return mtstate;
5421}
#define AttributeNumberIsValid(attributeNumber)
Definition attnum.h:34
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
unsigned int Index
Definition c.h:700
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:952
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition dynahash.c:358
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition execJunk.c:222
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition execMain.c:2575
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition execMain.c:2598
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, OnConflictAction onConflictAction, List *mergeActions)
Definition execMain.c:1056
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam, List *resultRelations)
Definition execMain.c:2737
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition execMain.c:2778
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
void ExecInitResultTypeTL(PlanState *planstate)
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition execUtils.c:885
#define EXEC_FLAG_BACKWARD
Definition executor.h:70
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition executor.h:701
#define EXEC_FLAG_EXPLAIN_ONLY
Definition executor.h:67
#define EXEC_FLAG_MARK
Definition executor.h:71
@ HASH_ENTER
Definition hsearch.h:114
#define HASH_CONTEXT
Definition hsearch.h:102
#define HASH_ELEM
Definition hsearch.h:95
#define HASH_BLOBS
Definition hsearch.h:97
List * lappend_int(List *list, int datum)
Definition list.c:357
List * lcons(void *datum, List *list)
Definition list.c:495
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
#define MT_NRELS_HASH
static void ExecInitMerge(ModifyTableState *mtstate, EState *estate)
@ ONCONFLICT_NONE
Definition nodes.h:428
@ ONCONFLICT_SELECT
Definition nodes.h:431
CmdType
Definition nodes.h:273
@ RTE_RELATION
#define lfirst_int(lc)
Definition pg_list.h:173
#define linitial_int(l)
Definition pg_list.h:179
#define list_nth_node(type, list, n)
Definition pg_list.h:327
#define InvalidOid
unsigned int Oid
Bitmapset * es_unpruned_relids
Definition execnodes.h:685
List * es_auxmodifytables
Definition execnodes.h:739
BeginForeignModify_function BeginForeignModify
Definition fdwapi.h:231
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition fdwapi.h:234
Size keysize
Definition hsearch.h:75
TupleTableSlot * mt_merge_pending_not_matched
Definition execnodes.h:1470
double mt_merge_deleted
Definition execnodes.h:1475
List * mt_updateColnosLists
Definition execnodes.h:1482
double mt_merge_inserted
Definition execnodes.h:1473
double mt_merge_updated
Definition execnodes.h:1474
HTAB * mt_resultOidHash
Definition execnodes.h:1442
List * updateColnosLists
Definition plannodes.h:348
List * arbiterIndexes
Definition plannodes.h:368
List * onConflictCols
Definition plannodes.h:374
List * mergeJoinConditions
Definition plannodes.h:384
CmdType operation
Definition plannodes.h:338
List * resultRelations
Definition plannodes.h:346
Bitmapset * fdwDirectModifyPlans
Definition plannodes.h:360
List * onConflictSet
Definition plannodes.h:372
List * mergeActionLists
Definition plannodes.h:382
bool canSetTag
Definition plannodes.h:340
List * fdwPrivLists
Definition plannodes.h:358
List * returningLists
Definition plannodes.h:356
List * withCheckOptionLists
Definition plannodes.h:350
LockClauseStrength onConflictLockStrength
Definition plannodes.h:370
Index rootRelation
Definition plannodes.h:344
Node * onConflictWhere
Definition plannodes.h:376
List * rowMarks
Definition plannodes.h:362
OnConflictAction onConflictAction
Definition plannodes.h:366
LockClauseStrength oc_LockStrength
Definition execnodes.h:447
ExecProcNodeMtd ExecProcNode
Definition execnodes.h:1183
TupleDesc rd_att
Definition rel.h:112
OnConflictActionState * ri_onConflict
Definition execnodes.h:595
List * ri_onConflictArbiterIndexes
Definition execnodes.h:592
struct ResultRelInfo * ri_RootResultRelInfo
Definition execnodes.h:630
List * ri_WithCheckOptionExprs
Definition execnodes.h:564
List * ri_returningList
Definition execnodes.h:586
AttrNumber ri_RowIdAttNo
Definition execnodes.h:507

References ModifyTable::arbiterIndexes, Assert, AttributeNumberIsValid, FdwRoutine::BeginForeignModify, bms_is_member(), ModifyTableState::canSetTag, ModifyTable::canSetTag, CheckValidResultRel(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, CurrentMemoryContext, elog, ModifyTable::epqParam, ERROR, EState::es_auxmodifytables, EState::es_tupleTable, EState::es_unpruned_relids, EvalPlanQualInit(), EvalPlanQualSetPlan(), EXEC_FLAG_BACKWARD, EXEC_FLAG_EXPLAIN_ONLY, EXEC_FLAG_MARK, exec_rt_fetch(), ExecAssignExprContext(), ExecBuildAuxRowMark(), ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecFindJunkAttributeInTlist(), ExecFindRowMark(), FdwRoutine::ExecForeignBatchInsert, ExecInitMerge(), ExecInitNode(), ExecInitQual(), ExecInitResultRelation(), ExecInitResultTupleSlotTL(), ExecInitResultTypeTL(), ExecModifyTable(), PlanState::ExecProcNode, ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), fb(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, FdwRoutine::GetForeignModifyBatchSize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_ENTER, hash_search(), i, InvalidOid, PlanRowMark::isParent, HASHCTL::keysize, lappend(), lappend_int(), lcons(), lfirst, lfirst_int, lfirst_node, linitial_int, list_length(), list_nth(), list_nth_node, makeNode, ModifyTable::mergeActionLists, ModifyTable::mergeJoinConditions, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, ModifyTableState::mt_merge_pending_not_matched, ModifyTableState::mt_merge_updated, ModifyTableState::mt_mergeActionLists, ModifyTableState::mt_mergeJoinConditions, ModifyTableState::mt_nrels, MT_NRELS_HASH, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_resultOidAttno, ModifyTableState::mt_resultOidHash, ModifyTableState::mt_updateColnosLists, NIL, OnConflictActionState::oc_LockStrength, ONCONFLICT_NONE, ONCONFLICT_SELECT, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictLockStrength, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTableState::operation, ModifyTable::operation, outerPlan, outerPlanState, palloc_array, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_usesFdwDirectModify, ResultRelInfo::ri_WithCheckOptionExprs, ResultRelInfo::ri_WithCheckOptions, ModifyTable::rootRelation, ModifyTableState::rootResultRelInfo, ModifyTable::rowMarks, RTE_RELATION, PlanRowMark::rti, PlanState::state, table_slot_create(), Plan::targetlist, TTSOpsVirtual, ModifyTable::updateColnosLists, and ModifyTable::withCheckOptionLists.

Referenced by ExecInitNode().

◆ ExecInitUpdateProjection()

static void ExecInitUpdateProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 722 of file nodeModifyTable.c.

724{
725 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
726 Plan *subplan = outerPlan(node);
727 EState *estate = mtstate->ps.state;
729 int whichrel;
731
732 /*
733 * Usually, mt_lastResultIndex matches the target rel. If it happens not
734 * to, we can get the index the hard way with an integer division.
735 */
736 whichrel = mtstate->mt_lastResultIndex;
737 if (resultRelInfo != mtstate->resultRelInfo + whichrel)
738 {
739 whichrel = resultRelInfo - mtstate->resultRelInfo;
740 Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
741 }
742
744
745 /*
746 * For UPDATE, we use the old tuple to fill up missing values in the tuple
747 * produced by the subplan to get the new tuple. We need two slots, both
748 * matching the table's desired format.
749 */
750 resultRelInfo->ri_oldTupleSlot =
751 table_slot_create(resultRelInfo->ri_RelationDesc,
752 &estate->es_tupleTable);
753 resultRelInfo->ri_newTupleSlot =
754 table_slot_create(resultRelInfo->ri_RelationDesc,
755 &estate->es_tupleTable);
756
757 /* need an expression context to do the projection */
758 if (mtstate->ps.ps_ExprContext == NULL)
759 ExecAssignExprContext(estate, &mtstate->ps);
760
761 resultRelInfo->ri_projectNew =
763 false, /* subplan did the evaluation */
765 relDesc,
766 mtstate->ps.ps_ExprContext,
767 resultRelInfo->ri_newTupleSlot,
768 &mtstate->ps);
769
770 resultRelInfo->ri_projectNewInfoValid = true;
771}

References Assert, EState::es_tupleTable, ExecAssignExprContext(), ExecBuildUpdateProjection(), fb(), list_nth(), ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_updateColnosLists, outerPlan, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, RelationGetDescr, ModifyTableState::resultRelInfo, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNew, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, PlanState::state, table_slot_create(), and Plan::targetlist.

Referenced by ExecCrossPartitionUpdate(), ExecModifyTable(), and ExecUpdate().

◆ ExecInsert()

static TupleTableSlot * ExecInsert ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
TupleTableSlot slot,
bool  canSetTag,
TupleTableSlot **  inserted_tuple,
ResultRelInfo **  insert_destrel 
)
static

Definition at line 862 of file nodeModifyTable.c.

868{
869 ModifyTableState *mtstate = context->mtstate;
870 EState *estate = context->estate;
873 TupleTableSlot *planSlot = context->planSlot;
874 TupleTableSlot *result = NULL;
876 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
877 OnConflictAction onconflict = node->onConflictAction;
878 PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
880
881 /*
882 * If the input result relation is a partitioned table, find the leaf
883 * partition to insert the tuple into.
884 */
885 if (proute)
886 {
888
889 slot = ExecPrepareTupleRouting(mtstate, estate, proute,
890 resultRelInfo, slot,
891 &partRelInfo);
892 resultRelInfo = partRelInfo;
893 }
894
896
897 resultRelationDesc = resultRelInfo->ri_RelationDesc;
898
899 /*
900 * Open the table's indexes, if we have not done so already, so that we
901 * can add new index entries for the inserted tuple.
902 */
903 if (resultRelationDesc->rd_rel->relhasindex &&
904 resultRelInfo->ri_IndexRelationDescs == NULL)
905 ExecOpenIndices(resultRelInfo, onconflict != ONCONFLICT_NONE);
906
907 /*
908 * BEFORE ROW INSERT Triggers.
909 *
910 * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
911 * INSERT ... ON CONFLICT statement. We cannot check for constraint
912 * violations before firing these triggers, because they can change the
913 * values to insert. Also, they can run arbitrary user-defined code with
914 * side-effects that we can't cancel by just not inserting the tuple.
915 */
916 if (resultRelInfo->ri_TrigDesc &&
917 resultRelInfo->ri_TrigDesc->trig_insert_before_row)
918 {
919 /* Flush any pending inserts, so rows are visible to the triggers */
920 if (estate->es_insert_pending_result_relations != NIL)
921 ExecPendingInserts(estate);
922
923 if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
924 return NULL; /* "do nothing" */
925 }
926
927 /* INSTEAD OF ROW INSERT Triggers */
928 if (resultRelInfo->ri_TrigDesc &&
929 resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
930 {
931 if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
932 return NULL; /* "do nothing" */
933 }
934 else if (resultRelInfo->ri_FdwRoutine)
935 {
936 /*
937 * GENERATED expressions might reference the tableoid column, so
938 * (re-)initialize tts_tableOid before evaluating them.
939 */
940 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
941
942 /*
943 * Compute stored generated columns
944 */
945 if (resultRelationDesc->rd_att->constr &&
946 resultRelationDesc->rd_att->constr->has_generated_stored)
947 ExecComputeStoredGenerated(resultRelInfo, estate, slot,
948 CMD_INSERT);
949
950 /*
951 * If the FDW supports batching, and batching is requested, accumulate
952 * rows and insert them in batches. Otherwise use the per-row inserts.
953 */
954 if (resultRelInfo->ri_BatchSize > 1)
955 {
956 bool flushed = false;
957
958 /*
959 * When we've reached the desired batch size, perform the
960 * insertion.
961 */
962 if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize)
963 {
964 ExecBatchInsert(mtstate, resultRelInfo,
965 resultRelInfo->ri_Slots,
966 resultRelInfo->ri_PlanSlots,
967 resultRelInfo->ri_NumSlots,
968 estate, canSetTag);
969 flushed = true;
970 }
971
972 oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
973
974 if (resultRelInfo->ri_Slots == NULL)
975 {
976 resultRelInfo->ri_Slots = palloc_array(TupleTableSlot *, resultRelInfo->ri_BatchSize);
977 resultRelInfo->ri_PlanSlots = palloc_array(TupleTableSlot *, resultRelInfo->ri_BatchSize);
978 }
979
980 /*
981 * Initialize the batch slots. We don't know how many slots will
982 * be needed, so we initialize them as the batch grows, and we
983 * keep them across batches. To mitigate an inefficiency in how
984 * resource owner handles objects with many references (as with
985 * many slots all referencing the same tuple descriptor) we copy
986 * the appropriate tuple descriptor for each slot.
987 */
988 if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
989 {
992 CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
993
994 resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
996
997 resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
998 MakeSingleTupleTableSlot(plan_tdesc, planSlot->tts_ops);
999
1000 /* remember how many batch slots we initialized */
1001 resultRelInfo->ri_NumSlotsInitialized++;
1002 }
1003
1004 ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
1005 slot);
1006
1007 ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
1008 planSlot);
1009
1010 /*
1011 * If these are the first tuples stored in the buffers, add the
1012 * target rel and the mtstate to the
1013 * es_insert_pending_result_relations and
1014 * es_insert_pending_modifytables lists respectively, except in
1015 * the case where flushing was done above, in which case they
1016 * would already have been added to the lists, so no need to do
1017 * this.
1018 */
1019 if (resultRelInfo->ri_NumSlots == 0 && !flushed)
1020 {
1021 Assert(!list_member_ptr(estate->es_insert_pending_result_relations,
1022 resultRelInfo));
1023 estate->es_insert_pending_result_relations =
1024 lappend(estate->es_insert_pending_result_relations,
1025 resultRelInfo);
1026 estate->es_insert_pending_modifytables =
1027 lappend(estate->es_insert_pending_modifytables, mtstate);
1028 }
1029 Assert(list_member_ptr(estate->es_insert_pending_result_relations,
1030 resultRelInfo));
1031
1032 resultRelInfo->ri_NumSlots++;
1033
1035
1036 return NULL;
1037 }
1038
1039 /*
1040 * insert into foreign table: let the FDW do it
1041 */
1042 slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
1043 resultRelInfo,
1044 slot,
1045 planSlot);
1046
1047 if (slot == NULL) /* "do nothing" */
1048 return NULL;
1049
1050 /*
1051 * AFTER ROW Triggers or RETURNING expressions might reference the
1052 * tableoid column, so (re-)initialize tts_tableOid before evaluating
1053 * them. (This covers the case where the FDW replaced the slot.)
1054 */
1055 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1056 }
1057 else
1058 {
1060
1061 /*
1062 * Constraints and GENERATED expressions might reference the tableoid
1063 * column, so (re-)initialize tts_tableOid before evaluating them.
1064 */
1066
1067 /*
1068 * Compute stored generated columns
1069 */
1070 if (resultRelationDesc->rd_att->constr &&
1071 resultRelationDesc->rd_att->constr->has_generated_stored)
1072 ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1073 CMD_INSERT);
1074
1075 /*
1076 * Check any RLS WITH CHECK policies.
1077 *
1078 * Normally we should check INSERT policies. But if the insert is the
1079 * result of a partition key update that moved the tuple to a new
1080 * partition, we should instead check UPDATE policies, because we are
1081 * executing policies defined on the target table, and not those
1082 * defined on the child partitions.
1083 *
1084 * If we're running MERGE, we refer to the action that we're executing
1085 * to know if we're doing an INSERT or UPDATE to a partition table.
1086 */
1087 if (mtstate->operation == CMD_UPDATE)
1089 else if (mtstate->operation == CMD_MERGE)
1090 wco_kind = (mtstate->mt_merge_action->mas_action->commandType == CMD_UPDATE) ?
1092 else
1094
1095 /*
1096 * ExecWithCheckOptions() will skip any WCOs which are not of the kind
1097 * we are looking for at this point.
1098 */
1099 if (resultRelInfo->ri_WithCheckOptions != NIL)
1100 ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
1101
1102 /*
1103 * Check the constraints of the tuple.
1104 */
1105 if (resultRelationDesc->rd_att->constr)
1106 ExecConstraints(resultRelInfo, slot, estate);
1107
1108 /*
1109 * Also check the tuple against the partition constraint, if there is
1110 * one; except that if we got here via tuple-routing, we don't need to
1111 * if there's no BR trigger defined on the partition.
1112 */
1113 if (resultRelationDesc->rd_rel->relispartition &&
1114 (resultRelInfo->ri_RootResultRelInfo == NULL ||
1115 (resultRelInfo->ri_TrigDesc &&
1116 resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
1117 ExecPartitionCheck(resultRelInfo, slot, estate, true);
1118
1119 if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
1120 {
1121 /* Perform a speculative insertion. */
1125 bool specConflict;
1126 List *arbiterIndexes;
1127
1129 arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
1130
1131 /*
1132 * Do a non-conclusive check for conflicts first.
1133 *
1134 * We're not holding any locks yet, so this doesn't guarantee that
1135 * the later insert won't conflict. But it avoids leaving behind
1136 * a lot of canceled speculative insertions, if you run a lot of
1137 * INSERT ON CONFLICT statements that do conflict.
1138 *
1139 * We loop back here if we find a conflict below, either during
1140 * the pre-check, or when we re-check after inserting the tuple
1141 * speculatively. Better allow interrupts in case some bug makes
1142 * this an infinite loop.
1143 */
1144 vlock:
1146 specConflict = false;
1147 if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
1149 arbiterIndexes))
1150 {
1151 /* committed conflict tuple found */
1152 if (onconflict == ONCONFLICT_UPDATE)
1153 {
1154 /*
1155 * In case of ON CONFLICT DO UPDATE, execute the UPDATE
1156 * part. Be prepared to retry if the UPDATE fails because
1157 * of another concurrent UPDATE/DELETE to the conflict
1158 * tuple.
1159 */
1160 TupleTableSlot *returning = NULL;
1161
1162 if (ExecOnConflictUpdate(context, resultRelInfo,
1163 &conflictTid, slot, canSetTag,
1164 &returning))
1165 {
1166 InstrCountTuples2(&mtstate->ps, 1);
1167 return returning;
1168 }
1169 else
1170 goto vlock;
1171 }
1172 else if (onconflict == ONCONFLICT_SELECT)
1173 {
1174 /*
1175 * In case of ON CONFLICT DO SELECT, optionally lock the
1176 * conflicting tuple, fetch it and project RETURNING on
1177 * it. Be prepared to retry if locking fails because of a
1178 * concurrent UPDATE/DELETE to the conflict tuple.
1179 */
1180 TupleTableSlot *returning = NULL;
1181
1182 if (ExecOnConflictSelect(context, resultRelInfo,
1183 &conflictTid, slot, canSetTag,
1184 &returning))
1185 {
1186 InstrCountTuples2(&mtstate->ps, 1);
1187 return returning;
1188 }
1189 else
1190 goto vlock;
1191 }
1192 else
1193 {
1194 /*
1195 * In case of ON CONFLICT DO NOTHING, do nothing. However,
1196 * verify that the tuple is visible to the executor's MVCC
1197 * snapshot at higher isolation levels.
1198 *
1199 * Using ExecGetReturningSlot() to store the tuple for the
1200 * recheck isn't that pretty, but we can't trivially use
1201 * the input slot, because it might not be of a compatible
1202 * type. As there's no conflicting usage of
1203 * ExecGetReturningSlot() in the DO NOTHING case...
1204 */
1205 Assert(onconflict == ONCONFLICT_NOTHING);
1206 ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
1207 ExecGetReturningSlot(estate, resultRelInfo));
1208 InstrCountTuples2(&mtstate->ps, 1);
1209 return NULL;
1210 }
1211 }
1212
1213 /*
1214 * Before we start insertion proper, acquire our "speculative
1215 * insertion lock". Others can use that to wait for us to decide
1216 * if we're going to go ahead with the insertion, instead of
1217 * waiting for the whole transaction to complete.
1218 */
1219 INJECTION_POINT("exec-insert-before-insert-speculative", NULL);
1221
1222 /* insert the tuple, with the speculative token */
1224 estate->es_output_cid,
1225 0,
1226 NULL,
1227 specToken);
1228
1229 /* insert index entries for tuple */
1230 recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1231 estate, EIIT_NO_DUPE_ERROR,
1232 slot, arbiterIndexes,
1233 &specConflict);
1234
1235 /* adjust the tuple's state accordingly */
1238
1239 /*
1240 * Wake up anyone waiting for our decision. They will re-check
1241 * the tuple, see that it's no longer speculative, and wait on our
1242 * XID as if this was a regularly inserted tuple all along. Or if
1243 * we killed the tuple, they will see it's dead, and proceed as if
1244 * the tuple never existed.
1245 */
1247
1248 /*
1249 * If there was a conflict, start from the beginning. We'll do
1250 * the pre-check again, which will now find the conflicting tuple
1251 * (unless it aborts before we get there).
1252 */
1253 if (specConflict)
1254 {
1256 goto vlock;
1257 }
1258
1259 /* Since there was no insertion conflict, we're done */
1260 }
1261 else
1262 {
1263 /* insert the tuple normally */
1265 estate->es_output_cid,
1266 0, NULL);
1267
1268 /* insert index entries for tuple */
1269 if (resultRelInfo->ri_NumIndices > 0)
1270 recheckIndexes = ExecInsertIndexTuples(resultRelInfo, estate,
1271 0, slot, NIL,
1272 NULL);
1273 }
1274 }
1275
1276 if (canSetTag)
1277 (estate->es_processed)++;
1278
1279 /*
1280 * If this insert is the result of a partition key update that moved the
1281 * tuple to a new partition, put this row into the transition NEW TABLE,
1282 * if there is one. We need to do this separately for DELETE and INSERT
1283 * because they happen on different tables.
1284 */
1285 ar_insert_trig_tcs = mtstate->mt_transition_capture;
1286 if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
1287 && mtstate->mt_transition_capture->tcs_update_new_table)
1288 {
1289 ExecARUpdateTriggers(estate, resultRelInfo,
1290 NULL, NULL,
1291 NULL,
1292 NULL,
1293 slot,
1294 NULL,
1295 mtstate->mt_transition_capture,
1296 false);
1297
1298 /*
1299 * We've already captured the NEW TABLE row, so make sure any AR
1300 * INSERT trigger fired below doesn't capture it again.
1301 */
1303 }
1304
1305 /* AFTER ROW INSERT Triggers */
1306 ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
1308
1310
1311 /*
1312 * Check any WITH CHECK OPTION constraints from parent views. We are
1313 * required to do this after testing all constraints and uniqueness
1314 * violations per the SQL spec, so we do it after actually inserting the
1315 * record into the heap and all indexes.
1316 *
1317 * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
1318 * tuple will never be seen, if it violates the WITH CHECK OPTION.
1319 *
1320 * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1321 * are looking for at this point.
1322 */
1323 if (resultRelInfo->ri_WithCheckOptions != NIL)
1324 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1325
1326 /* Process RETURNING if present */
1327 if (resultRelInfo->ri_projectReturning)
1328 {
1330
1331 /*
1332 * If this is part of a cross-partition UPDATE, and the RETURNING list
1333 * refers to any OLD columns, ExecDelete() will have saved the tuple
1334 * deleted from the original partition, which we must use here to
1335 * compute the OLD column values. Otherwise, all OLD column values
1336 * will be NULL.
1337 */
1338 if (context->cpDeletedSlot)
1339 {
1341
1342 /*
1343 * Convert the OLD tuple to the new partition's format/slot, if
1344 * needed. Note that ExecDelete() already converted it to the
1345 * root's partition's format/slot.
1346 */
1347 oldSlot = context->cpDeletedSlot;
1348 tupconv_map = ExecGetRootToChildMap(resultRelInfo, estate);
1349 if (tupconv_map != NULL)
1350 {
1352 oldSlot,
1353 ExecGetReturningSlot(estate,
1354 resultRelInfo));
1355
1356 oldSlot->tts_tableOid = context->cpDeletedSlot->tts_tableOid;
1357 ItemPointerCopy(&context->cpDeletedSlot->tts_tid, &oldSlot->tts_tid);
1358 }
1359 }
1360
1361 result = ExecProcessReturning(context, resultRelInfo, false,
1362 oldSlot, slot, planSlot);
1363
1364 /*
1365 * For a cross-partition UPDATE, release the old tuple, first making
1366 * sure that the result slot has a local copy of any pass-by-reference
1367 * values.
1368 */
1369 if (context->cpDeletedSlot)
1370 {
1371 ExecMaterializeSlot(result);
1373 if (context->cpDeletedSlot != oldSlot)
1374 ExecClearTuple(context->cpDeletedSlot);
1375 context->cpDeletedSlot = NULL;
1376 }
1377 }
1378
1379 if (inserted_tuple)
1380 *inserted_tuple = slot;
1381 if (insert_destrel)
1382 *insert_destrel = resultRelInfo;
1383
1384 return result;
1385}
uint32_t uint32
Definition c.h:618
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, EState *estate, bits32 flags, TupleTableSlot *slot, List *arbiterIndexes, bool *specConflict)
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
bool ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, const ItemPointerData *tupleid, List *arbiterIndexes)
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition execMain.c:1875
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition execMain.c:1999
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
TupleConversionMap * ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate)
Definition execUtils.c:1331
#define InstrCountTuples2(node, delta)
Definition execnodes.h:1276
#define EIIT_NO_DUPE_ERROR
Definition executor.h:749
#define INJECTION_POINT(name, arg)
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition itemptr.h:184
bool list_member_ptr(const List *list, const void *datum)
Definition list.c:682
void list_free(List *list)
Definition list.c:1546
uint32 SpeculativeInsertionLockAcquire(TransactionId xid)
Definition lmgr.c:786
void SpeculativeInsertionLockRelease(TransactionId xid)
Definition lmgr.c:812
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
static bool ExecOnConflictSelect(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *excludedSlot, bool canSetTag, TupleTableSlot **returning)
static void ExecCheckTIDVisible(EState *estate, ResultRelInfo *relinfo, ItemPointer tid, TupleTableSlot *tempSlot)
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot, ResultRelInfo **partRelInfo)
static bool ExecOnConflictUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *excludedSlot, bool canSetTag, TupleTableSlot **returning)
static void ExecBatchInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
OnConflictAction
Definition nodes.h:427
@ ONCONFLICT_NOTHING
Definition nodes.h:429
WCOKind
@ WCO_RLS_INSERT_CHECK
@ WCO_RLS_UPDATE_CHECK
ExecForeignInsert_function ExecForeignInsert
Definition fdwapi.h:232
RelationPtr ri_IndexRelationDescs
Definition execnodes.h:498
bool trig_insert_instead_row
Definition reltrigger.h:58
bool trig_insert_before_row
Definition reltrigger.h:56
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
static void table_tuple_insert_speculative(Relation rel, TupleTableSlot *slot, CommandId cid, int options, BulkInsertStateData *bistate, uint32 specToken)
Definition tableam.h:1407
static void table_tuple_complete_speculative(Relation rel, TupleTableSlot *slot, uint32 specToken, bool succeeded)
Definition tableam.h:1421
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, BulkInsertStateData *bistate)
Definition tableam.h:1388
bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition trigger.c:2467
bool ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition trigger.c:2571
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
TransactionId GetCurrentTransactionId(void)
Definition xact.c:456

References Assert, CHECK_FOR_INTERRUPTS, CMD_INSERT, CMD_MERGE, CMD_UPDATE, MergeAction::commandType, ModifyTableContext::cpDeletedSlot, CreateTupleDescCopy(), EIIT_NO_DUPE_ERROR, EState::es_insert_pending_modifytables, EState::es_insert_pending_result_relations, EState::es_output_cid, EState::es_processed, EState::es_query_cxt, ModifyTableContext::estate, ExecARInsertTriggers(), ExecARUpdateTriggers(), ExecBatchInsert(), ExecBRInsertTriggers(), ExecCheckIndexConstraints(), ExecCheckTIDVisible(), ExecClearTuple(), ExecComputeStoredGenerated(), ExecConstraints(), ExecCopySlot(), FdwRoutine::ExecForeignInsert, ExecGetReturningSlot(), ExecGetRootToChildMap(), ExecInsertIndexTuples(), ExecIRInsertTriggers(), ExecMaterializeSlot(), ExecOnConflictSelect(), ExecOnConflictUpdate(), ExecOpenIndices(), ExecPartitionCheck(), ExecPendingInserts(), ExecPrepareTupleRouting(), ExecProcessReturning(), execute_attr_map_slot(), ExecWithCheckOptions(), fb(), GetCurrentTransactionId(), INJECTION_POINT, InstrCountTuples2, ItemPointerCopy(), ItemPointerSetInvalid(), lappend(), list_free(), list_member_ptr(), MakeSingleTupleTableSlot(), MergeActionState::mas_action, MemoryContextSwitchTo(), ModifyTableState::mt_merge_action, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_SELECT, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, palloc_array, PlanState::plan, ModifyTableContext::planSlot, ModifyTableState::ps, RelationGetRelid, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_IndexRelationDescs, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_NumSlots, ResultRelInfo::ri_NumSlotsInitialized, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_PlanSlots, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_Slots, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, SpeculativeInsertionLockAcquire(), SpeculativeInsertionLockRelease(), table_tuple_complete_speculative(), table_tuple_insert(), table_tuple_insert_speculative(), TransitionCaptureState::tcs_update_new_table, TriggerDesc::trig_insert_before_row, TriggerDesc::trig_insert_instead_row, TupleTableSlot::tts_ops, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_tid, TupleTableSlot::tts_tupleDescriptor, WCO_RLS_INSERT_CHECK, WCO_RLS_UPDATE_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecCrossPartitionUpdate(), ExecMergeNotMatched(), and ExecModifyTable().

◆ ExecLookupResultRelByOid()

ResultRelInfo * ExecLookupResultRelByOid ( ModifyTableState node,
Oid  resultoid,
bool  missing_ok,
bool  update_cache 
)

Definition at line 4784 of file nodeModifyTable.c.

4786{
4787 if (node->mt_resultOidHash)
4788 {
4789 /* Use the pre-built hash table to locate the rel */
4791
4794 if (mtlookup)
4795 {
4796 if (update_cache)
4797 {
4799 node->mt_lastResultIndex = mtlookup->relationIndex;
4800 }
4801 return node->resultRelInfo + mtlookup->relationIndex;
4802 }
4803 }
4804 else
4805 {
4806 /* With few target rels, just search the ResultRelInfo array */
4807 for (int ndx = 0; ndx < node->mt_nrels; ndx++)
4808 {
4810
4811 if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
4812 {
4813 if (update_cache)
4814 {
4816 node->mt_lastResultIndex = ndx;
4817 }
4818 return rInfo;
4819 }
4820 }
4821 }
4822
4823 if (!missing_ok)
4824 elog(ERROR, "incorrect result relation OID %u", resultoid);
4825 return NULL;
4826}
@ HASH_FIND
Definition hsearch.h:113

References elog, ERROR, fb(), HASH_FIND, hash_search(), ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_nrels, ModifyTableState::mt_resultOidHash, RelationGetRelid, and ModifyTableState::resultRelInfo.

Referenced by ExecFindPartition(), and ExecModifyTable().

◆ ExecMerge()

static TupleTableSlot * ExecMerge ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
bool  canSetTag 
)
static

Definition at line 3126 of file nodeModifyTable.c.

3128{
3130 bool matched;
3131
3132 /*-----
3133 * If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
3134 * valid, depending on whether the result relation is a table or a view.
3135 * We execute the first action for which the additional WHEN MATCHED AND
3136 * quals pass. If an action without quals is found, that action is
3137 * executed.
3138 *
3139 * Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
3140 * is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
3141 * in sequence until one passes. This is almost identical to the WHEN
3142 * MATCHED case, and both cases are handled by ExecMergeMatched().
3143 *
3144 * Finally, in the WHEN NOT MATCHED [BY TARGET] case, both tupleid and
3145 * oldtuple are invalid, and we look at the given WHEN NOT MATCHED [BY
3146 * TARGET] actions in sequence until one passes.
3147 *
3148 * Things get interesting in case of concurrent update/delete of the
3149 * target tuple. Such concurrent update/delete is detected while we are
3150 * executing a WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action.
3151 *
3152 * A concurrent update can:
3153 *
3154 * 1. modify the target tuple so that the results from checking any
3155 * additional quals attached to WHEN MATCHED or WHEN NOT MATCHED BY
3156 * SOURCE actions potentially change, but the result from the join
3157 * quals does not change.
3158 *
3159 * In this case, we are still dealing with the same kind of match
3160 * (MATCHED or NOT MATCHED BY SOURCE). We recheck the same list of
3161 * actions from the start and choose the first one that satisfies the
3162 * new target tuple.
3163 *
3164 * 2. modify the target tuple in the WHEN MATCHED case so that the join
3165 * quals no longer pass and hence the source and target tuples no
3166 * longer match.
3167 *
3168 * In this case, we are now dealing with a NOT MATCHED case, and we
3169 * process both WHEN NOT MATCHED BY SOURCE and WHEN NOT MATCHED [BY
3170 * TARGET] actions. First ExecMergeMatched() processes the list of
3171 * WHEN NOT MATCHED BY SOURCE actions in sequence until one passes,
3172 * then ExecMergeNotMatched() processes any WHEN NOT MATCHED [BY
3173 * TARGET] actions in sequence until one passes. Thus we may execute
3174 * two actions; one of each kind.
3175 *
3176 * Thus we support concurrent updates that turn MATCHED candidate rows
3177 * into NOT MATCHED rows. However, we do not attempt to support cases
3178 * that would turn NOT MATCHED rows into MATCHED rows, or which would
3179 * cause a target row to match a different source row.
3180 *
3181 * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED
3182 * [BY TARGET].
3183 *
3184 * ExecMergeMatched() takes care of following the update chain and
3185 * re-finding the qualifying WHEN MATCHED or WHEN NOT MATCHED BY SOURCE
3186 * action, as long as the target tuple still exists. If the target tuple
3187 * gets deleted or a concurrent update causes the join quals to fail, it
3188 * returns a matched status of false and we call ExecMergeNotMatched().
3189 * Given that ExecMergeMatched() always makes progress by following the
3190 * update chain and we never switch from ExecMergeNotMatched() to
3191 * ExecMergeMatched(), there is no risk of a livelock.
3192 */
3193 matched = tupleid != NULL || oldtuple != NULL;
3194 if (matched)
3195 rslot = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
3196 canSetTag, &matched);
3197
3198 /*
3199 * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
3200 * join, or a previously MATCHED tuple for which ExecMergeMatched() set
3201 * "matched" to false, indicating that it no longer matches).
3202 */
3203 if (!matched)
3204 {
3205 /*
3206 * If a concurrent update turned a MATCHED case into a NOT MATCHED
3207 * case, and we have both WHEN NOT MATCHED BY SOURCE and WHEN NOT
3208 * MATCHED [BY TARGET] actions, and there is a RETURNING clause,
3209 * ExecMergeMatched() may have already executed a WHEN NOT MATCHED BY
3210 * SOURCE action, and computed the row to return. If so, we cannot
3211 * execute a WHEN NOT MATCHED [BY TARGET] action now, so mark it as
3212 * pending (to be processed on the next call to ExecModifyTable()).
3213 * Otherwise, just process the action now.
3214 */
3215 if (rslot == NULL)
3216 rslot = ExecMergeNotMatched(context, resultRelInfo, canSetTag);
3217 else
3218 context->mtstate->mt_merge_pending_not_matched = context->planSlot;
3219 }
3220
3221 return rslot;
3222}
static TupleTableSlot * ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag, bool *matched)
static TupleTableSlot * ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool canSetTag)

References ExecMergeMatched(), ExecMergeNotMatched(), fb(), ModifyTableState::mt_merge_pending_not_matched, ModifyTableContext::mtstate, and ModifyTableContext::planSlot.

Referenced by ExecModifyTable().

◆ ExecMergeMatched()

static TupleTableSlot * ExecMergeMatched ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
bool  canSetTag,
bool matched 
)
static

Definition at line 3252 of file nodeModifyTable.c.

3255{
3256 ModifyTableState *mtstate = context->mtstate;
3257 List **mergeActions = resultRelInfo->ri_MergeActions;
3262 EState *estate = context->estate;
3263 ExprContext *econtext = mtstate->ps.ps_ExprContext;
3264 bool isNull;
3265 EPQState *epqstate = &mtstate->mt_epqstate;
3266 ListCell *l;
3267
3268 /* Expect matched to be true on entry */
3269 Assert(*matched);
3270
3271 /*
3272 * If there are no WHEN MATCHED or WHEN NOT MATCHED BY SOURCE actions, we
3273 * are done.
3274 */
3277 return NULL;
3278
3279 /*
3280 * Make tuple and any needed join variables available to ExecQual and
3281 * ExecProject. The target's existing tuple is installed in the scantuple.
3282 * This target relation's slot is required only in the case of a MATCHED
3283 * or NOT MATCHED BY SOURCE tuple and UPDATE/DELETE actions.
3284 */
3285 econtext->ecxt_scantuple = resultRelInfo->ri_oldTupleSlot;
3286 econtext->ecxt_innertuple = context->planSlot;
3287 econtext->ecxt_outertuple = NULL;
3288
3289 /*
3290 * This routine is only invoked for matched target rows, so we should
3291 * either have the tupleid of the target row, or an old tuple from the
3292 * target wholerow junk attr.
3293 */
3294 Assert(tupleid != NULL || oldtuple != NULL);
3296 if (oldtuple != NULL)
3297 {
3298 Assert(!resultRelInfo->ri_needLockTagTuple);
3299 ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
3300 false);
3301 }
3302 else
3303 {
3304 if (resultRelInfo->ri_needLockTagTuple)
3305 {
3306 /*
3307 * This locks even for CMD_DELETE, for CMD_NOTHING, and for tuples
3308 * that don't match mas_whenqual. MERGE on system catalogs is a
3309 * minor use case, so don't bother optimizing those.
3310 */
3311 LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
3313 lockedtid = *tupleid;
3314 }
3316 tupleid,
3318 resultRelInfo->ri_oldTupleSlot))
3319 elog(ERROR, "failed to fetch the target tuple");
3320 }
3321
3322 /*
3323 * Test the join condition. If it's satisfied, perform a MATCHED action.
3324 * Otherwise, perform a NOT MATCHED BY SOURCE action.
3325 *
3326 * Note that this join condition will be NULL if there are no NOT MATCHED
3327 * BY SOURCE actions --- see transform_MERGE_to_join(). In that case, we
3328 * need only consider MATCHED actions here.
3329 */
3330 if (ExecQual(resultRelInfo->ri_MergeJoinCondition, econtext))
3332 else
3334
3336
3337 foreach(l, actionStates)
3338 {
3340 CmdType commandType = relaction->mas_action->commandType;
3341 TM_Result result;
3343
3344 /*
3345 * Test condition, if any.
3346 *
3347 * In the absence of any condition, we perform the action
3348 * unconditionally (no need to check separately since ExecQual() will
3349 * return true if there are no conditions to evaluate).
3350 */
3351 if (!ExecQual(relaction->mas_whenqual, econtext))
3352 continue;
3353
3354 /*
3355 * Check if the existing target tuple meets the USING checks of
3356 * UPDATE/DELETE RLS policies. If those checks fail, we throw an
3357 * error.
3358 *
3359 * The WITH CHECK quals for UPDATE RLS policies are applied in
3360 * ExecUpdateAct() and hence we need not do anything special to handle
3361 * them.
3362 *
3363 * NOTE: We must do this after WHEN quals are evaluated, so that we
3364 * check policies only when they matter.
3365 */
3366 if (resultRelInfo->ri_WithCheckOptions && commandType != CMD_NOTHING)
3367 {
3368 ExecWithCheckOptions(commandType == CMD_UPDATE ?
3370 resultRelInfo,
3371 resultRelInfo->ri_oldTupleSlot,
3372 context->mtstate->ps.state);
3373 }
3374
3375 /* Perform stated action */
3376 switch (commandType)
3377 {
3378 case CMD_UPDATE:
3379
3380 /*
3381 * Project the output tuple, and use that to update the table.
3382 * We don't need to filter out junk attributes, because the
3383 * UPDATE action's targetlist doesn't have any.
3384 */
3385 newslot = ExecProject(relaction->mas_proj);
3386
3387 mtstate->mt_merge_action = relaction;
3388 if (!ExecUpdatePrologue(context, resultRelInfo,
3389 tupleid, NULL, newslot, &result))
3390 {
3391 if (result == TM_Ok)
3392 goto out; /* "do nothing" */
3393
3394 break; /* concurrent update/delete */
3395 }
3396
3397 /* INSTEAD OF ROW UPDATE Triggers */
3398 if (resultRelInfo->ri_TrigDesc &&
3399 resultRelInfo->ri_TrigDesc->trig_update_instead_row)
3400 {
3401 if (!ExecIRUpdateTriggers(estate, resultRelInfo,
3402 oldtuple, newslot))
3403 goto out; /* "do nothing" */
3404 }
3405 else
3406 {
3407 /* checked ri_needLockTagTuple above */
3408 Assert(oldtuple == NULL);
3409
3410 result = ExecUpdateAct(context, resultRelInfo, tupleid,
3411 NULL, newslot, canSetTag,
3412 &updateCxt);
3413
3414 /*
3415 * As in ExecUpdate(), if ExecUpdateAct() reports that a
3416 * cross-partition update was done, then there's nothing
3417 * else for us to do --- the UPDATE has been turned into a
3418 * DELETE and an INSERT, and we must not perform any of
3419 * the usual post-update tasks. Also, the RETURNING tuple
3420 * (if any) has been projected, so we can just return
3421 * that.
3422 */
3423 if (updateCxt.crossPartUpdate)
3424 {
3425 mtstate->mt_merge_updated += 1;
3426 rslot = context->cpUpdateReturningSlot;
3427 goto out;
3428 }
3429 }
3430
3431 if (result == TM_Ok)
3432 {
3433 ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
3434 tupleid, NULL, newslot);
3435 mtstate->mt_merge_updated += 1;
3436 }
3437 break;
3438
3439 case CMD_DELETE:
3440 mtstate->mt_merge_action = relaction;
3441 if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
3442 NULL, NULL, &result))
3443 {
3444 if (result == TM_Ok)
3445 goto out; /* "do nothing" */
3446
3447 break; /* concurrent update/delete */
3448 }
3449
3450 /* INSTEAD OF ROW DELETE Triggers */
3451 if (resultRelInfo->ri_TrigDesc &&
3452 resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
3453 {
3454 if (!ExecIRDeleteTriggers(estate, resultRelInfo,
3455 oldtuple))
3456 goto out; /* "do nothing" */
3457 }
3458 else
3459 {
3460 /* checked ri_needLockTagTuple above */
3461 Assert(oldtuple == NULL);
3462
3463 result = ExecDeleteAct(context, resultRelInfo, tupleid,
3464 false);
3465 }
3466
3467 if (result == TM_Ok)
3468 {
3469 ExecDeleteEpilogue(context, resultRelInfo, tupleid, NULL,
3470 false);
3471 mtstate->mt_merge_deleted += 1;
3472 }
3473 break;
3474
3475 case CMD_NOTHING:
3476 /* Doing nothing is always OK */
3477 result = TM_Ok;
3478 break;
3479
3480 default:
3481 elog(ERROR, "unknown action in MERGE WHEN clause");
3482 }
3483
3484 switch (result)
3485 {
3486 case TM_Ok:
3487 /* all good; perform final actions */
3488 if (canSetTag && commandType != CMD_NOTHING)
3489 (estate->es_processed)++;
3490
3491 break;
3492
3493 case TM_SelfModified:
3494
3495 /*
3496 * The target tuple was already updated or deleted by the
3497 * current command, or by a later command in the current
3498 * transaction. The former case is explicitly disallowed by
3499 * the SQL standard for MERGE, which insists that the MERGE
3500 * join condition should not join a target row to more than
3501 * one source row.
3502 *
3503 * The latter case arises if the tuple is modified by a
3504 * command in a BEFORE trigger, or perhaps by a command in a
3505 * volatile function used in the query. In such situations we
3506 * should not ignore the MERGE action, but it is equally
3507 * unsafe to proceed. We don't want to discard the original
3508 * MERGE action while keeping the triggered actions based on
3509 * it; and it would be no better to allow the original MERGE
3510 * action while discarding the updates that it triggered. So
3511 * throwing an error is the only safe course.
3512 */
3513 if (context->tmfd.cmax != estate->es_output_cid)
3514 ereport(ERROR,
3516 errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3517 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3518
3520 ereport(ERROR,
3522 /* translator: %s is a SQL command name */
3523 errmsg("%s command cannot affect row a second time",
3524 "MERGE"),
3525 errhint("Ensure that not more than one source row matches any one target row.")));
3526
3527 /* This shouldn't happen */
3528 elog(ERROR, "attempted to update or delete invisible tuple");
3529 break;
3530
3531 case TM_Deleted:
3533 ereport(ERROR,
3535 errmsg("could not serialize access due to concurrent delete")));
3536
3537 /*
3538 * If the tuple was already deleted, set matched to false to
3539 * let caller handle it under NOT MATCHED [BY TARGET] clauses.
3540 */
3541 *matched = false;
3542 goto out;
3543
3544 case TM_Updated:
3545 {
3546 bool was_matched;
3549 *inputslot;
3550 LockTupleMode lockmode;
3551
3553 ereport(ERROR,
3555 errmsg("could not serialize access due to concurrent update")));
3556
3557 /*
3558 * The target tuple was concurrently updated by some other
3559 * transaction. If we are currently processing a MATCHED
3560 * action, use EvalPlanQual() with the new version of the
3561 * tuple and recheck the join qual, to detect a change
3562 * from the MATCHED to the NOT MATCHED cases. If we are
3563 * already processing a NOT MATCHED BY SOURCE action, we
3564 * skip this (cannot switch from NOT MATCHED BY SOURCE to
3565 * MATCHED).
3566 */
3567 was_matched = relaction->mas_action->matchKind == MERGE_WHEN_MATCHED;
3568 resultRelationDesc = resultRelInfo->ri_RelationDesc;
3569 lockmode = ExecUpdateLockMode(estate, resultRelInfo);
3570
3571 if (was_matched)
3572 inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
3573 resultRelInfo->ri_RangeTableIndex);
3574 else
3575 inputslot = resultRelInfo->ri_oldTupleSlot;
3576
3578 estate->es_snapshot,
3579 inputslot, estate->es_output_cid,
3580 lockmode, LockWaitBlock,
3582 &context->tmfd);
3583 switch (result)
3584 {
3585 case TM_Ok:
3586
3587 /*
3588 * If the tuple was updated and migrated to
3589 * another partition concurrently, the current
3590 * MERGE implementation can't follow. There's
3591 * probably a better way to handle this case, but
3592 * it'd require recognizing the relation to which
3593 * the tuple moved, and setting our current
3594 * resultRelInfo to that.
3595 */
3597 ereport(ERROR,
3599 errmsg("tuple to be merged was already moved to another partition due to concurrent update")));
3600
3601 /*
3602 * If this was a MATCHED case, use EvalPlanQual()
3603 * to recheck the join condition.
3604 */
3605 if (was_matched)
3606 {
3607 epqslot = EvalPlanQual(epqstate,
3609 resultRelInfo->ri_RangeTableIndex,
3610 inputslot);
3611
3612 /*
3613 * If the subplan didn't return a tuple, then
3614 * we must be dealing with an inner join for
3615 * which the join condition no longer matches.
3616 * This can only happen if there are no NOT
3617 * MATCHED actions, and so there is nothing
3618 * more to do.
3619 */
3620 if (TupIsNull(epqslot))
3621 goto out;
3622
3623 /*
3624 * If we got a NULL ctid from the subplan, the
3625 * join quals no longer pass and we switch to
3626 * the NOT MATCHED BY SOURCE case.
3627 */
3629 resultRelInfo->ri_RowIdAttNo,
3630 &isNull);
3631 if (isNull)
3632 *matched = false;
3633
3634 /*
3635 * Otherwise, recheck the join quals to see if
3636 * we need to switch to the NOT MATCHED BY
3637 * SOURCE case.
3638 */
3639 if (resultRelInfo->ri_needLockTagTuple)
3640 {
3642 UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3644 LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
3646 lockedtid = *tupleid;
3647 }
3648
3650 tupleid,
3652 resultRelInfo->ri_oldTupleSlot))
3653 elog(ERROR, "failed to fetch the target tuple");
3654
3655 if (*matched)
3656 *matched = ExecQual(resultRelInfo->ri_MergeJoinCondition,
3657 econtext);
3658
3659 /* Switch lists, if necessary */
3660 if (!*matched)
3661 {
3663
3664 /*
3665 * If we have both NOT MATCHED BY SOURCE
3666 * and NOT MATCHED BY TARGET actions (a
3667 * full join between the source and target
3668 * relations), the single previously
3669 * matched tuple from the outer plan node
3670 * is treated as two not matched tuples,
3671 * in the same way as if they had not
3672 * matched to start with. Therefore, we
3673 * must adjust the outer plan node's tuple
3674 * count, if we're instrumenting the
3675 * query, to get the correct "skipped" row
3676 * count --- see show_modifytable_info().
3677 */
3678 if (outerPlanState(mtstate)->instrument &&
3681 InstrUpdateTupleCount(outerPlanState(mtstate)->instrument, 1.0);
3682 }
3683 }
3684
3685 /*
3686 * Loop back and process the MATCHED or NOT
3687 * MATCHED BY SOURCE actions from the start.
3688 */
3689 goto lmerge_matched;
3690
3691 case TM_Deleted:
3692
3693 /*
3694 * tuple already deleted; tell caller to run NOT
3695 * MATCHED [BY TARGET] actions
3696 */
3697 *matched = false;
3698 goto out;
3699
3700 case TM_SelfModified:
3701
3702 /*
3703 * This can be reached when following an update
3704 * chain from a tuple updated by another session,
3705 * reaching a tuple that was already updated or
3706 * deleted by the current command, or by a later
3707 * command in the current transaction. As above,
3708 * this should always be treated as an error.
3709 */
3710 if (context->tmfd.cmax != estate->es_output_cid)
3711 ereport(ERROR,
3713 errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3714 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3715
3717 ereport(ERROR,
3719 /* translator: %s is a SQL command name */
3720 errmsg("%s command cannot affect row a second time",
3721 "MERGE"),
3722 errhint("Ensure that not more than one source row matches any one target row.")));
3723
3724 /* This shouldn't happen */
3725 elog(ERROR, "attempted to update or delete invisible tuple");
3726 goto out;
3727
3728 default:
3729 /* see table_tuple_lock call in ExecDelete() */
3730 elog(ERROR, "unexpected table_tuple_lock status: %u",
3731 result);
3732 goto out;
3733 }
3734 }
3735
3736 case TM_Invisible:
3737 case TM_WouldBlock:
3738 case TM_BeingModified:
3739 /* these should not occur */
3740 elog(ERROR, "unexpected tuple operation result: %d", result);
3741 break;
3742 }
3743
3744 /* Process RETURNING if present */
3745 if (resultRelInfo->ri_projectReturning)
3746 {
3747 switch (commandType)
3748 {
3749 case CMD_UPDATE:
3750 rslot = ExecProcessReturning(context,
3751 resultRelInfo,
3752 false,
3753 resultRelInfo->ri_oldTupleSlot,
3754 newslot,
3755 context->planSlot);
3756 break;
3757
3758 case CMD_DELETE:
3759 rslot = ExecProcessReturning(context,
3760 resultRelInfo,
3761 true,
3762 resultRelInfo->ri_oldTupleSlot,
3763 NULL,
3764 context->planSlot);
3765 break;
3766
3767 case CMD_NOTHING:
3768 break;
3769
3770 default:
3771 elog(ERROR, "unrecognized commandType: %d",
3772 (int) commandType);
3773 }
3774 }
3775
3776 /*
3777 * We've activated one of the WHEN clauses, so we don't search
3778 * further. This is required behaviour, not an optimization.
3779 */
3780 break;
3781 }
3782
3783 /*
3784 * Successfully executed an action or no qualifying action was found.
3785 */
3786out:
3788 UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3790 return rslot;
3791}
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition execMain.c:2549
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:522
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition executor.h:226
struct parser_state ps
void InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
Definition instrument.c:136
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition itemptr.h:197
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition itemptr.h:83
void UnlockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition lmgr.c:601
void LockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition lmgr.c:562
#define InplaceUpdateTupleLock
Definition lockdefs.h:48
LockTupleMode
Definition lockoptions.h:51
static TM_Result ExecUpdateAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt)
static void ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot)
static bool ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TM_Result *result)
@ WCO_RLS_MERGE_UPDATE_CHECK
@ WCO_RLS_MERGE_DELETE_CHECK
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition primnodes.h:2025
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition primnodes.h:2024
@ MERGE_WHEN_MATCHED
Definition primnodes.h:2023
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:286
CmdType commandType
Definition primnodes.h:2034
MergeActionState * mt_merge_action
Definition execnodes.h:1463
bool ri_needLockTagTuple
Definition execnodes.h:524
TransactionId xmax
Definition tableam.h:150
bool trig_update_instead_row
Definition reltrigger.h:63
@ TM_BeingModified
Definition tableam.h:100
@ TM_WouldBlock
Definition tableam.h:103
@ TM_Invisible
Definition tableam.h:81
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition trigger.c:3216

References Assert, TM_FailureData::cmax, CMD_DELETE, CMD_NOTHING, CMD_UPDATE, MergeAction::commandType, ModifyTableContext::cpUpdateReturningSlot, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg, ERROR, EState::es_output_cid, EState::es_processed, EState::es_snapshot, ModifyTableContext::estate, EvalPlanQual(), EvalPlanQualSlot(), ExecDeleteAct(), ExecDeleteEpilogue(), ExecDeletePrologue(), ExecForceStoreHeapTuple(), ExecGetJunkAttribute(), ExecIRDeleteTriggers(), ExecIRUpdateTriggers(), ExecProcessReturning(), ExecProject(), ExecQual(), ExecUpdateAct(), ExecUpdateEpilogue(), ExecUpdateLockMode(), ExecUpdatePrologue(), ExecWithCheckOptions(), fb(), InplaceUpdateTupleLock, InstrUpdateTupleCount(), IsolationUsesXactSnapshot, ItemPointerIndicatesMovedPartitions(), ItemPointerIsValid(), ItemPointerSetInvalid(), lfirst, LockTuple(), LockWaitBlock, MergeActionState::mas_action, MERGE_WHEN_MATCHED, MERGE_WHEN_NOT_MATCHED_BY_SOURCE, MERGE_WHEN_NOT_MATCHED_BY_TARGET, ModifyTableState::mt_epqstate, ModifyTableState::mt_merge_action, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_updated, ModifyTableContext::mtstate, NIL, outerPlanState, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, ResultRelInfo::ri_MergeActions, ResultRelInfo::ri_MergeJoinCondition, ResultRelInfo::ri_needLockTagTuple, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, SnapshotAny, PlanState::state, table_tuple_fetch_row_version(), table_tuple_lock(), TM_BeingModified, TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TM_WouldBlock, ModifyTableContext::tmfd, TransactionIdIsCurrentTransactionId(), TriggerDesc::trig_delete_instead_row, TriggerDesc::trig_update_instead_row, TupIsNull, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, UnlockTuple(), WCO_RLS_MERGE_DELETE_CHECK, WCO_RLS_MERGE_UPDATE_CHECK, and TM_FailureData::xmax.

Referenced by ExecMerge().

◆ ExecMergeNotMatched()

static TupleTableSlot * ExecMergeNotMatched ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
bool  canSetTag 
)
static

Definition at line 3797 of file nodeModifyTable.c.

3799{
3800 ModifyTableState *mtstate = context->mtstate;
3801 ExprContext *econtext = mtstate->ps.ps_ExprContext;
3804 ListCell *l;
3805
3806 /*
3807 * For INSERT actions, the root relation's merge action is OK since the
3808 * INSERT's targetlist and the WHEN conditions can only refer to the
3809 * source relation and hence it does not matter which result relation we
3810 * work with.
3811 *
3812 * XXX does this mean that we can avoid creating copies of actionStates on
3813 * partitioned tables, for not-matched actions?
3814 */
3816
3817 /*
3818 * Make source tuple available to ExecQual and ExecProject. We don't need
3819 * the target tuple, since the WHEN quals and targetlist can't refer to
3820 * the target columns.
3821 */
3822 econtext->ecxt_scantuple = NULL;
3823 econtext->ecxt_innertuple = context->planSlot;
3824 econtext->ecxt_outertuple = NULL;
3825
3826 foreach(l, actionStates)
3827 {
3829 CmdType commandType = action->mas_action->commandType;
3831
3832 /*
3833 * Test condition, if any.
3834 *
3835 * In the absence of any condition, we perform the action
3836 * unconditionally (no need to check separately since ExecQual() will
3837 * return true if there are no conditions to evaluate).
3838 */
3839 if (!ExecQual(action->mas_whenqual, econtext))
3840 continue;
3841
3842 /* Perform stated action */
3843 switch (commandType)
3844 {
3845 case CMD_INSERT:
3846
3847 /*
3848 * Project the tuple. In case of a partitioned table, the
3849 * projection was already built to use the root's descriptor,
3850 * so we don't need to map the tuple here.
3851 */
3852 newslot = ExecProject(action->mas_proj);
3853 mtstate->mt_merge_action = action;
3854
3855 rslot = ExecInsert(context, mtstate->rootResultRelInfo,
3856 newslot, canSetTag, NULL, NULL);
3857 mtstate->mt_merge_inserted += 1;
3858 break;
3859 case CMD_NOTHING:
3860 /* Do nothing */
3861 break;
3862 default:
3863 elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
3864 }
3865
3866 /*
3867 * We've activated one of the WHEN clauses, so we don't search
3868 * further. This is required behaviour, not an optimization.
3869 */
3870 break;
3871 }
3872
3873 return rslot;
3874}

References CMD_INSERT, CMD_NOTHING, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ERROR, ExecInsert(), ExecProject(), ExecQual(), fb(), lfirst, MERGE_WHEN_NOT_MATCHED_BY_TARGET, ModifyTableState::mt_merge_action, ModifyTableState::mt_merge_inserted, ModifyTableContext::mtstate, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, ResultRelInfo::ri_MergeActions, and ModifyTableState::rootResultRelInfo.

Referenced by ExecMerge(), and ExecModifyTable().

◆ ExecModifyTable()

static TupleTableSlot * ExecModifyTable ( PlanState pstate)
static

Definition at line 4351 of file nodeModifyTable.c.

4352{
4354 ModifyTableContext context;
4355 EState *estate = node->ps.state;
4356 CmdType operation = node->operation;
4357 ResultRelInfo *resultRelInfo;
4359 TupleTableSlot *slot;
4363 HeapTuple oldtuple;
4365 bool tuplock;
4366
4368
4369 /*
4370 * This should NOT get called during EvalPlanQual; we should have passed a
4371 * subplan tree to EvalPlanQual, instead. Use a runtime test not just
4372 * Assert because this condition is easy to miss in testing. (Note:
4373 * although ModifyTable should not get executed within an EvalPlanQual
4374 * operation, we do have to allow it to be initialized and shut down in
4375 * case it is within a CTE subplan. Hence this test must be here, not in
4376 * ExecInitModifyTable.)
4377 */
4378 if (estate->es_epq_active != NULL)
4379 elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
4380
4381 /*
4382 * If we've already completed processing, don't try to do more. We need
4383 * this test because ExecPostprocessPlan might call us an extra time, and
4384 * our subplan's nodes aren't necessarily robust against being called
4385 * extra times.
4386 */
4387 if (node->mt_done)
4388 return NULL;
4389
4390 /*
4391 * On first call, fire BEFORE STATEMENT triggers before proceeding.
4392 */
4393 if (node->fireBSTriggers)
4394 {
4395 fireBSTriggers(node);
4396 node->fireBSTriggers = false;
4397 }
4398
4399 /* Preload local variables */
4400 resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
4402
4403 /* Set global context */
4404 context.mtstate = node;
4405 context.epqstate = &node->mt_epqstate;
4406 context.estate = estate;
4407
4408 /*
4409 * Fetch rows from subplan, and execute the required table modification
4410 * for each row.
4411 */
4412 for (;;)
4413 {
4414 /*
4415 * Reset the per-output-tuple exprcontext. This is needed because
4416 * triggers expect to use that context as workspace. It's a bit ugly
4417 * to do this below the top level of the plan, however. We might need
4418 * to rethink this later.
4419 */
4421
4422 /*
4423 * Reset per-tuple memory context used for processing on conflict and
4424 * returning clauses, to free any expression evaluation storage
4425 * allocated in the previous cycle.
4426 */
4427 if (pstate->ps_ExprContext)
4429
4430 /*
4431 * If there is a pending MERGE ... WHEN NOT MATCHED [BY TARGET] action
4432 * to execute, do so now --- see the comments in ExecMerge().
4433 */
4435 {
4436 context.planSlot = node->mt_merge_pending_not_matched;
4437 context.cpDeletedSlot = NULL;
4438
4439 slot = ExecMergeNotMatched(&context, node->resultRelInfo,
4440 node->canSetTag);
4441
4442 /* Clear the pending action */
4444
4445 /*
4446 * If we got a RETURNING result, return it to the caller. We'll
4447 * continue the work on next call.
4448 */
4449 if (slot)
4450 return slot;
4451
4452 continue; /* continue with the next tuple */
4453 }
4454
4455 /* Fetch the next row from subplan */
4457 context.cpDeletedSlot = NULL;
4458
4459 /* No more tuples to process? */
4460 if (TupIsNull(context.planSlot))
4461 break;
4462
4463 /*
4464 * When there are multiple result relations, each tuple contains a
4465 * junk column that gives the OID of the rel from which it came.
4466 * Extract it and select the correct result relation.
4467 */
4469 {
4470 Datum datum;
4471 bool isNull;
4472 Oid resultoid;
4473
4474 datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
4475 &isNull);
4476 if (isNull)
4477 {
4478 /*
4479 * For commands other than MERGE, any tuples having InvalidOid
4480 * for tableoid are errors. For MERGE, we may need to handle
4481 * them as WHEN NOT MATCHED clauses if any, so do that.
4482 *
4483 * Note that we use the node's toplevel resultRelInfo, not any
4484 * specific partition's.
4485 */
4486 if (operation == CMD_MERGE)
4487 {
4488 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4489
4490 slot = ExecMerge(&context, node->resultRelInfo,
4491 NULL, NULL, node->canSetTag);
4492
4493 /*
4494 * If we got a RETURNING result, return it to the caller.
4495 * We'll continue the work on next call.
4496 */
4497 if (slot)
4498 return slot;
4499
4500 continue; /* continue with the next tuple */
4501 }
4502
4503 elog(ERROR, "tableoid is NULL");
4504 }
4505 resultoid = DatumGetObjectId(datum);
4506
4507 /* If it's not the same as last time, we need to locate the rel */
4508 if (resultoid != node->mt_lastResultOid)
4509 resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
4510 false, true);
4511 }
4512
4513 /*
4514 * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
4515 * here is compute the RETURNING expressions.
4516 */
4517 if (resultRelInfo->ri_usesFdwDirectModify)
4518 {
4519 Assert(resultRelInfo->ri_projectReturning);
4520
4521 /*
4522 * A scan slot containing the data that was actually inserted,
4523 * updated or deleted has already been made available to
4524 * ExecProcessReturning by IterateDirectModify, so no need to
4525 * provide it here. The individual old and new slots are not
4526 * needed, since direct-modify is disabled if the RETURNING list
4527 * refers to OLD/NEW values.
4528 */
4529 Assert((resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD) == 0 &&
4530 (resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW) == 0);
4531
4532 slot = ExecProcessReturning(&context, resultRelInfo,
4533 operation == CMD_DELETE,
4534 NULL, NULL, context.planSlot);
4535
4536 return slot;
4537 }
4538
4539 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4540 slot = context.planSlot;
4541
4542 tupleid = NULL;
4543 oldtuple = NULL;
4544
4545 /*
4546 * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
4547 * to be updated/deleted/merged. For a heap relation, that's a TID;
4548 * otherwise we may have a wholerow junk attr that carries the old
4549 * tuple in toto. Keep this in step with the part of
4550 * ExecInitModifyTable that sets up ri_RowIdAttNo.
4551 */
4552 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4553 operation == CMD_MERGE)
4554 {
4555 char relkind;
4556 Datum datum;
4557 bool isNull;
4558
4559 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4560 if (relkind == RELKIND_RELATION ||
4561 relkind == RELKIND_MATVIEW ||
4562 relkind == RELKIND_PARTITIONED_TABLE)
4563 {
4564 /*
4565 * ri_RowIdAttNo refers to a ctid attribute. See the comment
4566 * in ExecInitModifyTable().
4567 */
4569 relkind == RELKIND_PARTITIONED_TABLE);
4570 datum = ExecGetJunkAttribute(slot,
4571 resultRelInfo->ri_RowIdAttNo,
4572 &isNull);
4573
4574 /*
4575 * For commands other than MERGE, any tuples having a null row
4576 * identifier are errors. For MERGE, we may need to handle
4577 * them as WHEN NOT MATCHED clauses if any, so do that.
4578 *
4579 * Note that we use the node's toplevel resultRelInfo, not any
4580 * specific partition's.
4581 */
4582 if (isNull)
4583 {
4584 if (operation == CMD_MERGE)
4585 {
4586 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4587
4588 slot = ExecMerge(&context, node->resultRelInfo,
4589 NULL, NULL, node->canSetTag);
4590
4591 /*
4592 * If we got a RETURNING result, return it to the
4593 * caller. We'll continue the work on next call.
4594 */
4595 if (slot)
4596 return slot;
4597
4598 continue; /* continue with the next tuple */
4599 }
4600
4601 elog(ERROR, "ctid is NULL");
4602 }
4603
4605 tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
4607 }
4608
4609 /*
4610 * Use the wholerow attribute, when available, to reconstruct the
4611 * old relation tuple. The old tuple serves one or both of two
4612 * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
4613 * provides values for any unchanged columns for the NEW tuple of
4614 * an UPDATE, because the subplan does not produce all the columns
4615 * of the target table.
4616 *
4617 * Note that the wholerow attribute does not carry system columns,
4618 * so foreign table triggers miss seeing those, except that we
4619 * know enough here to set t_tableOid. Quite separately from
4620 * this, the FDW may fetch its own junk attrs to identify the row.
4621 *
4622 * Other relevant relkinds, currently limited to views, always
4623 * have a wholerow attribute.
4624 */
4625 else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4626 {
4627 datum = ExecGetJunkAttribute(slot,
4628 resultRelInfo->ri_RowIdAttNo,
4629 &isNull);
4630
4631 /*
4632 * For commands other than MERGE, any tuples having a null row
4633 * identifier are errors. For MERGE, we may need to handle
4634 * them as WHEN NOT MATCHED clauses if any, so do that.
4635 *
4636 * Note that we use the node's toplevel resultRelInfo, not any
4637 * specific partition's.
4638 */
4639 if (isNull)
4640 {
4641 if (operation == CMD_MERGE)
4642 {
4643 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4644
4645 slot = ExecMerge(&context, node->resultRelInfo,
4646 NULL, NULL, node->canSetTag);
4647
4648 /*
4649 * If we got a RETURNING result, return it to the
4650 * caller. We'll continue the work on next call.
4651 */
4652 if (slot)
4653 return slot;
4654
4655 continue; /* continue with the next tuple */
4656 }
4657
4658 elog(ERROR, "wholerow is NULL");
4659 }
4660
4661 oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
4662 oldtupdata.t_len =
4665 /* Historically, view triggers see invalid t_tableOid. */
4666 oldtupdata.t_tableOid =
4667 (relkind == RELKIND_VIEW) ? InvalidOid :
4668 RelationGetRelid(resultRelInfo->ri_RelationDesc);
4669
4670 oldtuple = &oldtupdata;
4671 }
4672 else
4673 {
4674 /* Only foreign tables are allowed to omit a row-ID attr */
4675 Assert(relkind == RELKIND_FOREIGN_TABLE);
4676 }
4677 }
4678
4679 switch (operation)
4680 {
4681 case CMD_INSERT:
4682 /* Initialize projection info if first time for this table */
4683 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4684 ExecInitInsertProjection(node, resultRelInfo);
4685 slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
4686 slot = ExecInsert(&context, resultRelInfo, slot,
4687 node->canSetTag, NULL, NULL);
4688 break;
4689
4690 case CMD_UPDATE:
4691 tuplock = false;
4692
4693 /* Initialize projection info if first time for this table */
4694 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4695 ExecInitUpdateProjection(node, resultRelInfo);
4696
4697 /*
4698 * Make the new tuple by combining plan's output tuple with
4699 * the old tuple being updated.
4700 */
4701 oldSlot = resultRelInfo->ri_oldTupleSlot;
4702 if (oldtuple != NULL)
4703 {
4704 Assert(!resultRelInfo->ri_needLockTagTuple);
4705 /* Use the wholerow junk attr as the old tuple. */
4706 ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
4707 }
4708 else
4709 {
4710 /* Fetch the most recent version of old tuple. */
4711 Relation relation = resultRelInfo->ri_RelationDesc;
4712
4713 if (resultRelInfo->ri_needLockTagTuple)
4714 {
4716 tuplock = true;
4717 }
4720 oldSlot))
4721 elog(ERROR, "failed to fetch tuple being updated");
4722 }
4723 slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
4724 oldSlot);
4725
4726 /* Now apply the update. */
4727 slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
4728 oldSlot, slot, node->canSetTag);
4729 if (tuplock)
4730 UnlockTuple(resultRelInfo->ri_RelationDesc, tupleid,
4732 break;
4733
4734 case CMD_DELETE:
4735 slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
4736 true, false, node->canSetTag, NULL, NULL, NULL);
4737 break;
4738
4739 case CMD_MERGE:
4740 slot = ExecMerge(&context, resultRelInfo, tupleid, oldtuple,
4741 node->canSetTag);
4742 break;
4743
4744 default:
4745 elog(ERROR, "unknown operation");
4746 break;
4747 }
4748
4749 /*
4750 * If we got a RETURNING result, return it to caller. We'll continue
4751 * the work on next call.
4752 */
4753 if (slot)
4754 return slot;
4755 }
4756
4757 /*
4758 * Insert remaining tuples for batch insert.
4759 */
4761 ExecPendingInserts(estate);
4762
4763 /*
4764 * We're done, but fire AFTER STATEMENT triggers before exiting.
4765 */
4766 fireASTriggers(node);
4767
4768 node->mt_done = true;
4769
4770 return NULL;
4771}
#define EEO_FLAG_HAS_NEW
Definition execnodes.h:89
#define ResetPerTupleExprContext(estate)
Definition executor.h:669
#define ResetExprContext(econtext)
Definition executor.h:654
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:315
#define EvalPlanQualSetSlot(epqstate, slot)
Definition executor.h:290
#define DatumGetHeapTupleHeader(X)
Definition fmgr.h:296
static uint32 HeapTupleHeaderGetDatumLength(const HeapTupleHeaderData *tup)
ItemPointerData * ItemPointer
Definition itemptr.h:49
static void ExecInitInsertProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
ResultRelInfo * ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid, bool missing_ok, bool update_cache)
static TupleTableSlot * ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *oldSlot, TupleTableSlot *slot, bool canSetTag)
static TupleTableSlot * ExecGetInsertNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot)
static void fireBSTriggers(ModifyTableState *node)
static void fireASTriggers(ModifyTableState *node)
static TupleTableSlot * ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
static Oid DatumGetObjectId(Datum X)
Definition postgres.h:242
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
struct EPQState * es_epq_active
Definition execnodes.h:754

References Assert, AttributeNumberIsValid, ModifyTableState::canSetTag, castNode, CHECK_FOR_INTERRUPTS, CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, ModifyTableContext::cpDeletedSlot, DatumGetHeapTupleHeader, DatumGetObjectId(), DatumGetPointer(), EEO_FLAG_HAS_NEW, EEO_FLAG_HAS_OLD, elog, ModifyTableContext::epqstate, ERROR, EState::es_epq_active, EState::es_insert_pending_result_relations, ModifyTableContext::estate, EvalPlanQualSetSlot, ExecDelete(), ExecForceStoreHeapTuple(), ExecGetInsertNewTuple(), ExecGetJunkAttribute(), ExecGetUpdateNewTuple(), ExecInitInsertProjection(), ExecInitUpdateProjection(), ExecInsert(), ExecLookupResultRelByOid(), ExecMerge(), ExecMergeNotMatched(), ExecPendingInserts(), ExecProcessReturning(), ExecProcNode(), ExecUpdate(), fb(), fireASTriggers(), fireBSTriggers(), ModifyTableState::fireBSTriggers, ExprState::flags, HeapTupleHeaderGetDatumLength(), InplaceUpdateTupleLock, InvalidOid, ItemPointerSetInvalid(), LockTuple(), ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_merge_pending_not_matched, ModifyTableState::mt_resultOidAttno, ModifyTableContext::mtstate, NIL, ModifyTableState::operation, outerPlanState, ProjectionInfo::pi_state, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, RelationData::rd_rel, RelationGetRelid, ResetExprContext, ResetPerTupleExprContext, ModifyTableState::resultRelInfo, ResultRelInfo::ri_needLockTagTuple, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_usesFdwDirectModify, SnapshotAny, PlanState::state, table_tuple_fetch_row_version(), TupIsNull, unlikely, and UnlockTuple().

Referenced by ExecInitModifyTable().

◆ ExecOnConflictLockRow()

static bool ExecOnConflictLockRow ( ModifyTableContext context,
TupleTableSlot existing,
ItemPointer  conflictTid,
Relation  relation,
LockTupleMode  lockmode,
bool  isUpdate 
)
static

Definition at line 2744 of file nodeModifyTable.c.

2750{
2751 TM_FailureData tmfd;
2754 TransactionId xmin;
2755 bool isnull;
2756
2757 /*
2758 * Lock tuple with lockmode. Don't follow updates when tuple cannot be
2759 * locked without doing so. A row locking conflict here means our
2760 * previous conclusion that the tuple is conclusively committed is not
2761 * true anymore.
2762 */
2763 test = table_tuple_lock(relation, conflictTid,
2764 context->estate->es_snapshot,
2765 existing, context->estate->es_output_cid,
2766 lockmode, LockWaitBlock, 0,
2767 &tmfd);
2768 switch (test)
2769 {
2770 case TM_Ok:
2771 /* success! */
2772 break;
2773
2774 case TM_Invisible:
2775
2776 /*
2777 * This can occur when a just inserted tuple is updated again in
2778 * the same command. E.g. because multiple rows with the same
2779 * conflicting key values are inserted.
2780 *
2781 * This is somewhat similar to the ExecUpdate() TM_SelfModified
2782 * case. We do not want to proceed because it would lead to the
2783 * same row being updated a second time in some unspecified order,
2784 * and in contrast to plain UPDATEs there's no historical behavior
2785 * to break.
2786 *
2787 * It is the user's responsibility to prevent this situation from
2788 * occurring. These problems are why the SQL standard similarly
2789 * specifies that for SQL MERGE, an exception must be raised in
2790 * the event of an attempt to update the same row twice.
2791 */
2794 &isnull);
2795 Assert(!isnull);
2797
2799 ereport(ERROR,
2801 /* translator: %s is a SQL command name */
2802 errmsg("%s command cannot affect row a second time",
2803 isUpdate ? "ON CONFLICT DO UPDATE" : "ON CONFLICT DO SELECT"),
2804 errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
2805
2806 /* This shouldn't happen */
2807 elog(ERROR, "attempted to lock invisible tuple");
2808 break;
2809
2810 case TM_SelfModified:
2811
2812 /*
2813 * This state should never be reached. As a dirty snapshot is used
2814 * to find conflicting tuples, speculative insertion wouldn't have
2815 * seen this row to conflict with.
2816 */
2817 elog(ERROR, "unexpected self-updated tuple");
2818 break;
2819
2820 case TM_Updated:
2822 ereport(ERROR,
2824 errmsg("could not serialize access due to concurrent update")));
2825
2826 /*
2827 * Tell caller to try again from the very start.
2828 *
2829 * It does not make sense to use the usual EvalPlanQual() style
2830 * loop here, as the new version of the row might not conflict
2831 * anymore, or the conflicting tuple has actually been deleted.
2832 */
2834 return false;
2835
2836 case TM_Deleted:
2838 ereport(ERROR,
2840 errmsg("could not serialize access due to concurrent delete")));
2841
2842 /* see TM_Updated case */
2844 return false;
2845
2846 default:
2847 elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
2848 }
2849
2850 /* Success, the tuple is locked. */
2851 return true;
2852}
static void test(void)

References Assert, DatumGetTransactionId(), elog, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg, ERROR, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, ExecClearTuple(), fb(), IsolationUsesXactSnapshot, LockWaitBlock, MinTransactionIdAttributeNumber, slot_getsysattr(), table_tuple_lock(), test(), TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, and TransactionIdIsCurrentTransactionId().

Referenced by ExecOnConflictSelect(), and ExecOnConflictUpdate().

◆ ExecOnConflictSelect()

static bool ExecOnConflictSelect ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  conflictTid,
TupleTableSlot excludedSlot,
bool  canSetTag,
TupleTableSlot **  returning 
)
static

Definition at line 2999 of file nodeModifyTable.c.

3005{
3006 ModifyTableState *mtstate = context->mtstate;
3007 ExprContext *econtext = mtstate->ps.ps_ExprContext;
3008 Relation relation = resultRelInfo->ri_RelationDesc;
3011 LockClauseStrength lockStrength = resultRelInfo->ri_onConflict->oc_LockStrength;
3012
3013 /*
3014 * Parse analysis should have blocked ON CONFLICT for all system
3015 * relations, which includes these. There's no fundamental obstacle to
3016 * supporting this; we'd just need to handle LOCKTAG_TUPLE appropriately.
3017 */
3018 Assert(!resultRelInfo->ri_needLockTagTuple);
3019
3020 /* Fetch/lock existing tuple, according to the requested lock strength */
3021 if (lockStrength == LCS_NONE)
3022 {
3023 if (!table_tuple_fetch_row_version(relation,
3026 existing))
3027 elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
3028 }
3029 else
3030 {
3031 LockTupleMode lockmode;
3032
3033 switch (lockStrength)
3034 {
3035 case LCS_FORKEYSHARE:
3036 lockmode = LockTupleKeyShare;
3037 break;
3038 case LCS_FORSHARE:
3039 lockmode = LockTupleShare;
3040 break;
3041 case LCS_FORNOKEYUPDATE:
3042 lockmode = LockTupleNoKeyExclusive;
3043 break;
3044 case LCS_FORUPDATE:
3045 lockmode = LockTupleExclusive;
3046 break;
3047 default:
3048 elog(ERROR, "Unexpected lock strength %d", (int) lockStrength);
3049 }
3050
3052 resultRelInfo->ri_RelationDesc, lockmode, false))
3053 return false;
3054 }
3055
3056 /*
3057 * Verify that the tuple is visible to our MVCC snapshot if the current
3058 * isolation level mandates that. See comments in ExecOnConflictUpdate().
3059 */
3060 ExecCheckTupleVisible(context->estate, relation, existing);
3061
3062 /*
3063 * Make tuple and any needed join variables available to ExecQual. The
3064 * EXCLUDED tuple is installed in ecxt_innertuple, while the target's
3065 * existing tuple is installed in the scantuple. EXCLUDED has been made
3066 * to reference INNER_VAR in setrefs.c, but there is no other redirection.
3067 */
3068 econtext->ecxt_scantuple = existing;
3069 econtext->ecxt_innertuple = excludedSlot;
3070 econtext->ecxt_outertuple = NULL;
3071
3072 if (!ExecQual(onConflictSelectWhere, econtext))
3073 {
3074 ExecClearTuple(existing); /* see return below */
3075 InstrCountFiltered1(&mtstate->ps, 1);
3076 return true; /* done with the tuple */
3077 }
3078
3079 if (resultRelInfo->ri_WithCheckOptions != NIL)
3080 {
3081 /*
3082 * Check target's existing tuple against SELECT-applicable USING
3083 * security barrier quals (if any), enforced here as RLS checks/WCOs.
3084 *
3085 * The rewriter creates WCOs from the USING quals of SELECT policies,
3086 * and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK. If FOR
3087 * UPDATE/SHARE was specified, UPDATE permissions are required on the
3088 * target table, and the rewriter also adds WCOs built from the USING
3089 * quals of UPDATE policies, using WCOs of the same kind, and this
3090 * check enforces them too.
3091 */
3093 existing,
3094 mtstate->ps.state);
3095 }
3096
3097 /* RETURNING is required for DO SELECT */
3098 Assert(resultRelInfo->ri_projectReturning);
3099
3100 *returning = ExecProcessReturning(context, resultRelInfo, false,
3101 existing, existing, context->planSlot);
3102
3103 if (canSetTag)
3104 context->estate->es_processed++;
3105
3106 /*
3107 * Before releasing the existing tuple, make sure that the returning slot
3108 * has a local copy of any pass-by-reference values.
3109 */
3110 ExecMaterializeSlot(*returning);
3111
3112 /*
3113 * Clear out existing tuple, as there might not be another conflict among
3114 * the next input rows. Don't want to hold resources till the end of the
3115 * query.
3116 */
3118
3119 return true;
3120}
#define InstrCountFiltered1(node, delta)
Definition execnodes.h:1281
@ LockTupleNoKeyExclusive
Definition lockoptions.h:57
@ LockTupleShare
Definition lockoptions.h:55
@ LockTupleKeyShare
Definition lockoptions.h:53
LockClauseStrength
Definition lockoptions.h:22
@ LCS_FORUPDATE
Definition lockoptions.h:28
@ LCS_NONE
Definition lockoptions.h:23
@ LCS_FORSHARE
Definition lockoptions.h:26
@ LCS_FORKEYSHARE
Definition lockoptions.h:25
@ LCS_FORNOKEYUPDATE
Definition lockoptions.h:27
static bool ExecOnConflictLockRow(ModifyTableContext *context, TupleTableSlot *existing, ItemPointer conflictTid, Relation relation, LockTupleMode lockmode, bool isUpdate)
@ WCO_RLS_CONFLICT_CHECK
ExprState * oc_WhereClause
Definition execnodes.h:448
TupleTableSlot * oc_Existing
Definition execnodes.h:444

References Assert, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ERROR, EState::es_processed, ModifyTableContext::estate, ExecCheckTupleVisible(), ExecClearTuple(), ExecMaterializeSlot(), ExecOnConflictLockRow(), ExecProcessReturning(), ExecQual(), ExecWithCheckOptions(), fb(), InstrCountFiltered1, LCS_FORKEYSHARE, LCS_FORNOKEYUPDATE, LCS_FORSHARE, LCS_FORUPDATE, LCS_NONE, LockTupleExclusive, LockTupleKeyShare, LockTupleNoKeyExclusive, LockTupleShare, ModifyTableContext::mtstate, NIL, OnConflictActionState::oc_Existing, OnConflictActionState::oc_LockStrength, OnConflictActionState::oc_WhereClause, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, ResultRelInfo::ri_needLockTagTuple, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_WithCheckOptions, SnapshotAny, PlanState::state, table_tuple_fetch_row_version(), and WCO_RLS_CONFLICT_CHECK.

Referenced by ExecInsert().

◆ ExecOnConflictUpdate()

static bool ExecOnConflictUpdate ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  conflictTid,
TupleTableSlot excludedSlot,
bool  canSetTag,
TupleTableSlot **  returning 
)
static

Definition at line 2866 of file nodeModifyTable.c.

2872{
2873 ModifyTableState *mtstate = context->mtstate;
2874 ExprContext *econtext = mtstate->ps.ps_ExprContext;
2875 Relation relation = resultRelInfo->ri_RelationDesc;
2878 LockTupleMode lockmode;
2879
2880 /*
2881 * Parse analysis should have blocked ON CONFLICT for all system
2882 * relations, which includes these. There's no fundamental obstacle to
2883 * supporting this; we'd just need to handle LOCKTAG_TUPLE like the other
2884 * ExecUpdate() caller.
2885 */
2886 Assert(!resultRelInfo->ri_needLockTagTuple);
2887
2888 /* Determine lock mode to use */
2889 lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
2890
2891 /* Lock tuple for update */
2893 resultRelInfo->ri_RelationDesc, lockmode, true))
2894 return false;
2895
2896 /*
2897 * Verify that the tuple is visible to our MVCC snapshot if the current
2898 * isolation level mandates that.
2899 *
2900 * It's not sufficient to rely on the check within ExecUpdate() as e.g.
2901 * CONFLICT ... WHERE clause may prevent us from reaching that.
2902 *
2903 * This means we only ever continue when a new command in the current
2904 * transaction could see the row, even though in READ COMMITTED mode the
2905 * tuple will not be visible according to the current statement's
2906 * snapshot. This is in line with the way UPDATE deals with newer tuple
2907 * versions.
2908 */
2909 ExecCheckTupleVisible(context->estate, relation, existing);
2910
2911 /*
2912 * Make tuple and any needed join variables available to ExecQual and
2913 * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
2914 * the target's existing tuple is installed in the scantuple. EXCLUDED
2915 * has been made to reference INNER_VAR in setrefs.c, but there is no
2916 * other redirection.
2917 */
2918 econtext->ecxt_scantuple = existing;
2919 econtext->ecxt_innertuple = excludedSlot;
2920 econtext->ecxt_outertuple = NULL;
2921
2922 if (!ExecQual(onConflictSetWhere, econtext))
2923 {
2924 ExecClearTuple(existing); /* see return below */
2925 InstrCountFiltered1(&mtstate->ps, 1);
2926 return true; /* done with the tuple */
2927 }
2928
2929 if (resultRelInfo->ri_WithCheckOptions != NIL)
2930 {
2931 /*
2932 * Check target's existing tuple against UPDATE-applicable USING
2933 * security barrier quals (if any), enforced here as RLS checks/WCOs.
2934 *
2935 * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
2936 * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK.
2937 * Since SELECT permission on the target table is always required for
2938 * INSERT ... ON CONFLICT DO UPDATE, the rewriter also adds SELECT RLS
2939 * checks/WCOs for SELECT security quals, using WCOs of the same kind,
2940 * and this check enforces them too.
2941 *
2942 * The rewriter will also have associated UPDATE-applicable straight
2943 * RLS checks/WCOs for the benefit of the ExecUpdate() call that
2944 * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
2945 * kinds, so there is no danger of spurious over-enforcement in the
2946 * INSERT or UPDATE path.
2947 */
2949 existing,
2950 mtstate->ps.state);
2951 }
2952
2953 /* Project the new tuple version */
2954 ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
2955
2956 /*
2957 * Note that it is possible that the target tuple has been modified in
2958 * this session, after the above table_tuple_lock. We choose to not error
2959 * out in that case, in line with ExecUpdate's treatment of similar cases.
2960 * This can happen if an UPDATE is triggered from within ExecQual(),
2961 * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
2962 * wCTE in the ON CONFLICT's SET.
2963 */
2964
2965 /* Execute UPDATE with projection */
2966 *returning = ExecUpdate(context, resultRelInfo,
2968 resultRelInfo->ri_onConflict->oc_ProjSlot,
2969 canSetTag);
2970
2971 /*
2972 * Clear out existing tuple, as there might not be another conflict among
2973 * the next input rows. Don't want to hold resources till the end of the
2974 * query. First though, make sure that the returning slot, if any, has a
2975 * local copy of any OLD pass-by-reference values, if it refers to any OLD
2976 * columns.
2977 */
2978 if (*returning != NULL &&
2980 ExecMaterializeSlot(*returning);
2981
2983
2984 return true;
2985}
ProjectionInfo * oc_ProjInfo
Definition execnodes.h:446
TupleTableSlot * oc_ProjSlot
Definition execnodes.h:445

References Assert, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, EEO_FLAG_HAS_OLD, ModifyTableContext::estate, ExecCheckTupleVisible(), ExecClearTuple(), ExecMaterializeSlot(), ExecOnConflictLockRow(), ExecProject(), ExecQual(), ExecUpdate(), ExecUpdateLockMode(), ExecWithCheckOptions(), fb(), ExprState::flags, InstrCountFiltered1, ModifyTableContext::mtstate, NIL, OnConflictActionState::oc_Existing, OnConflictActionState::oc_ProjInfo, OnConflictActionState::oc_ProjSlot, OnConflictActionState::oc_WhereClause, ProjectionInfo::pi_state, ModifyTableState::ps, PlanState::ps_ExprContext, ResultRelInfo::ri_needLockTagTuple, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_WithCheckOptions, PlanState::state, and WCO_RLS_CONFLICT_CHECK.

Referenced by ExecInsert().

◆ ExecPendingInserts()

static void ExecPendingInserts ( EState estate)
static

Definition at line 1456 of file nodeModifyTable.c.

1457{
1458 ListCell *l1,
1459 *l2;
1460
1463 {
1464 ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l1);
1465 ModifyTableState *mtstate = (ModifyTableState *) lfirst(l2);
1466
1467 Assert(mtstate);
1468 ExecBatchInsert(mtstate, resultRelInfo,
1469 resultRelInfo->ri_Slots,
1470 resultRelInfo->ri_PlanSlots,
1471 resultRelInfo->ri_NumSlots,
1472 estate, mtstate->canSetTag);
1473 }
1474
1479}
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:518
List * es_insert_pending_modifytables
Definition execnodes.h:784

References Assert, ModifyTableState::canSetTag, EState::es_insert_pending_modifytables, EState::es_insert_pending_result_relations, ExecBatchInsert(), forboth, lfirst, list_free(), NIL, ResultRelInfo::ri_NumSlots, ResultRelInfo::ri_PlanSlots, and ResultRelInfo::ri_Slots.

Referenced by ExecDeletePrologue(), ExecInsert(), ExecModifyTable(), and ExecUpdatePrologue().

◆ ExecPrepareTupleRouting()

static TupleTableSlot * ExecPrepareTupleRouting ( ModifyTableState mtstate,
EState estate,
PartitionTupleRouting proute,
ResultRelInfo targetRelInfo,
TupleTableSlot slot,
ResultRelInfo **  partRelInfo 
)
static

Definition at line 4291 of file nodeModifyTable.c.

4297{
4298 ResultRelInfo *partrel;
4299 TupleConversionMap *map;
4300
4301 /*
4302 * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
4303 * not find a valid partition for the tuple in 'slot' then an error is
4304 * raised. An error may also be raised if the found partition is not a
4305 * valid target for INSERTs. This is required since a partitioned table
4306 * UPDATE to another partition becomes a DELETE+INSERT.
4307 */
4308 partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
4309
4310 /*
4311 * If we're capturing transition tuples, we might need to convert from the
4312 * partition rowtype to root partitioned table's rowtype. But if there
4313 * are no BEFORE triggers on the partition that could change the tuple, we
4314 * can just remember the original unconverted tuple to avoid a needless
4315 * round trip conversion.
4316 */
4317 if (mtstate->mt_transition_capture != NULL)
4318 {
4320
4323
4326 }
4327
4328 /*
4329 * Convert the tuple, if necessary.
4330 */
4331 map = ExecGetRootToChildMap(partrel, estate);
4332 if (map != NULL)
4333 {
4334 TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
4335
4336 slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
4337 }
4338
4339 *partRelInfo = partrel;
4340 return slot;
4341}
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleTableSlot * ri_PartitionTupleSlot
Definition execnodes.h:631
AttrMap * attrMap
Definition tupconvert.h:28

References TupleConversionMap::attrMap, ExecFindPartition(), ExecGetRootToChildMap(), execute_attr_map_slot(), fb(), ModifyTableState::mt_transition_capture, ResultRelInfo::ri_PartitionTupleSlot, ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_original_insert_tuple, and TriggerDesc::trig_insert_before_row.

Referenced by ExecInsert().

◆ ExecProcessReturning()

static TupleTableSlot * ExecProcessReturning ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
bool  isDelete,
TupleTableSlot oldSlot,
TupleTableSlot newSlot,
TupleTableSlot planSlot 
)
static

Definition at line 306 of file nodeModifyTable.c.

312{
313 EState *estate = context->estate;
315 ExprContext *econtext = projectReturning->pi_exprContext;
316
317 /* Make tuple and any needed join variables available to ExecProject */
318 if (isDelete)
319 {
320 /* return old tuple by default */
321 if (oldSlot)
322 econtext->ecxt_scantuple = oldSlot;
323 }
324 else
325 {
326 /* return new tuple by default */
327 if (newSlot)
328 econtext->ecxt_scantuple = newSlot;
329 }
330 econtext->ecxt_outertuple = planSlot;
331
332 /* Make old/new tuples available to ExecProject, if required */
333 if (oldSlot)
334 econtext->ecxt_oldtuple = oldSlot;
335 else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD)
336 econtext->ecxt_oldtuple = ExecGetAllNullSlot(estate, resultRelInfo);
337 else
338 econtext->ecxt_oldtuple = NULL; /* No references to OLD columns */
339
340 if (newSlot)
341 econtext->ecxt_newtuple = newSlot;
342 else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW)
343 econtext->ecxt_newtuple = ExecGetAllNullSlot(estate, resultRelInfo);
344 else
345 econtext->ecxt_newtuple = NULL; /* No references to NEW columns */
346
347 /*
348 * Tell ExecProject whether or not the OLD/NEW rows actually exist. This
349 * information is required to evaluate ReturningExpr nodes and also in
350 * ExecEvalSysVar() and ExecEvalWholeRowVar().
351 */
352 if (oldSlot == NULL)
353 projectReturning->pi_state.flags |= EEO_FLAG_OLD_IS_NULL;
354 else
355 projectReturning->pi_state.flags &= ~EEO_FLAG_OLD_IS_NULL;
356
357 if (newSlot == NULL)
358 projectReturning->pi_state.flags |= EEO_FLAG_NEW_IS_NULL;
359 else
360 projectReturning->pi_state.flags &= ~EEO_FLAG_NEW_IS_NULL;
361
362 /* Compute the RETURNING expressions */
364}
TupleTableSlot * ExecGetAllNullSlot(EState *estate, ResultRelInfo *relInfo)
Definition execUtils.c:1278
#define EEO_FLAG_NEW_IS_NULL
Definition execnodes.h:93
#define EEO_FLAG_OLD_IS_NULL
Definition execnodes.h:91
TupleTableSlot * ecxt_newtuple
Definition execnodes.h:323
TupleTableSlot * ecxt_oldtuple
Definition execnodes.h:321

References ExprContext::ecxt_newtuple, ExprContext::ecxt_oldtuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, EEO_FLAG_HAS_NEW, EEO_FLAG_HAS_OLD, EEO_FLAG_NEW_IS_NULL, EEO_FLAG_OLD_IS_NULL, ModifyTableContext::estate, ExecGetAllNullSlot(), ExecProject(), fb(), and ResultRelInfo::ri_projectReturning.

Referenced by ExecDelete(), ExecInsert(), ExecMergeMatched(), ExecModifyTable(), ExecOnConflictSelect(), and ExecUpdate().

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 5486 of file nodeModifyTable.c.

5487{
5488 /*
5489 * Currently, we don't need to support rescan on ModifyTable nodes. The
5490 * semantics of that would be a bit debatable anyway.
5491 */
5492 elog(ERROR, "ExecReScanModifyTable is not implemented");
5493}

References elog, and ERROR.

Referenced by ExecReScan().

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 4262 of file nodeModifyTable.c.

4263{
4264 ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
4265 ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
4266
4267 /* Check for transition tables on the directly targeted relation. */
4268 mtstate->mt_transition_capture =
4270 RelationGetRelid(targetRelInfo->ri_RelationDesc),
4271 mtstate->operation);
4272 if (plan->operation == CMD_INSERT &&
4273 plan->onConflictAction == ONCONFLICT_UPDATE)
4274 mtstate->mt_oc_transition_capture =
4276 RelationGetRelid(targetRelInfo->ri_RelationDesc),
4277 CMD_UPDATE);
4278}
#define plan(x)
Definition pg_regress.c:161
struct TransitionCaptureState * mt_oc_transition_capture
Definition execnodes.h:1457
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition trigger.c:4959

References CMD_INSERT, CMD_UPDATE, MakeTransitionCaptureState(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, ModifyTableState::operation, PlanState::plan, plan, ModifyTableState::ps, RelationGetRelid, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, and ModifyTableState::rootResultRelInfo.

Referenced by ExecInitModifyTable().

◆ ExecUpdate()

static TupleTableSlot * ExecUpdate ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot oldSlot,
TupleTableSlot slot,
bool  canSetTag 
)
static

Definition at line 2494 of file nodeModifyTable.c.

2497{
2498 EState *estate = context->estate;
2501 TM_Result result;
2502
2503 /*
2504 * abort the operation if not running transactions
2505 */
2507 elog(ERROR, "cannot UPDATE during bootstrap");
2508
2509 /*
2510 * Prepare for the update. This includes BEFORE ROW triggers, so we're
2511 * done if it says we are.
2512 */
2513 if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
2514 return NULL;
2515
2516 /* INSTEAD OF ROW UPDATE Triggers */
2517 if (resultRelInfo->ri_TrigDesc &&
2518 resultRelInfo->ri_TrigDesc->trig_update_instead_row)
2519 {
2520 if (!ExecIRUpdateTriggers(estate, resultRelInfo,
2521 oldtuple, slot))
2522 return NULL; /* "do nothing" */
2523 }
2524 else if (resultRelInfo->ri_FdwRoutine)
2525 {
2526 /* Fill in GENERATEd columns */
2527 ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2528
2529 /*
2530 * update in foreign table: let the FDW do it
2531 */
2532 slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
2533 resultRelInfo,
2534 slot,
2535 context->planSlot);
2536
2537 if (slot == NULL) /* "do nothing" */
2538 return NULL;
2539
2540 /*
2541 * AFTER ROW Triggers or RETURNING expressions might reference the
2542 * tableoid column, so (re-)initialize tts_tableOid before evaluating
2543 * them. (This covers the case where the FDW replaced the slot.)
2544 */
2546 }
2547 else
2548 {
2550
2551 /*
2552 * If we generate a new candidate tuple after EvalPlanQual testing, we
2553 * must loop back here to try again. (We don't need to redo triggers,
2554 * however. If there are any BEFORE triggers then trigger.c will have
2555 * done table_tuple_lock to lock the correct tuple, so there's no need
2556 * to do them again.)
2557 */
2558redo_act:
2559 lockedtid = *tupleid;
2560 result = ExecUpdateAct(context, resultRelInfo, tupleid, oldtuple, slot,
2561 canSetTag, &updateCxt);
2562
2563 /*
2564 * If ExecUpdateAct reports that a cross-partition update was done,
2565 * then the RETURNING tuple (if any) has been projected and there's
2566 * nothing else for us to do.
2567 */
2568 if (updateCxt.crossPartUpdate)
2569 return context->cpUpdateReturningSlot;
2570
2571 switch (result)
2572 {
2573 case TM_SelfModified:
2574
2575 /*
2576 * The target tuple was already updated or deleted by the
2577 * current command, or by a later command in the current
2578 * transaction. The former case is possible in a join UPDATE
2579 * where multiple tuples join to the same target tuple. This
2580 * is pretty questionable, but Postgres has always allowed it:
2581 * we just execute the first update action and ignore
2582 * additional update attempts.
2583 *
2584 * The latter case arises if the tuple is modified by a
2585 * command in a BEFORE trigger, or perhaps by a command in a
2586 * volatile function used in the query. In such situations we
2587 * should not ignore the update, but it is equally unsafe to
2588 * proceed. We don't want to discard the original UPDATE
2589 * while keeping the triggered actions based on it; and we
2590 * have no principled way to merge this update with the
2591 * previous ones. So throwing an error is the only safe
2592 * course.
2593 *
2594 * If a trigger actually intends this type of interaction, it
2595 * can re-execute the UPDATE (assuming it can figure out how)
2596 * and then return NULL to cancel the outer update.
2597 */
2598 if (context->tmfd.cmax != estate->es_output_cid)
2599 ereport(ERROR,
2601 errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2602 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2603
2604 /* Else, already updated by self; nothing to do */
2605 return NULL;
2606
2607 case TM_Ok:
2608 break;
2609
2610 case TM_Updated:
2611 {
2612 TupleTableSlot *inputslot;
2614
2616 ereport(ERROR,
2618 errmsg("could not serialize access due to concurrent update")));
2619
2620 /*
2621 * Already know that we're going to need to do EPQ, so
2622 * fetch tuple directly into the right slot.
2623 */
2624 inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
2625 resultRelInfo->ri_RangeTableIndex);
2626
2628 estate->es_snapshot,
2629 inputslot, estate->es_output_cid,
2630 updateCxt.lockmode, LockWaitBlock,
2632 &context->tmfd);
2633
2634 switch (result)
2635 {
2636 case TM_Ok:
2637 Assert(context->tmfd.traversed);
2638
2639 epqslot = EvalPlanQual(context->epqstate,
2641 resultRelInfo->ri_RangeTableIndex,
2642 inputslot);
2643 if (TupIsNull(epqslot))
2644 /* Tuple not passing quals anymore, exiting... */
2645 return NULL;
2646
2647 /* Make sure ri_oldTupleSlot is initialized. */
2648 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2650 resultRelInfo);
2651
2652 if (resultRelInfo->ri_needLockTagTuple)
2653 {
2658 }
2659
2660 /* Fetch the most recent version of old tuple. */
2661 oldSlot = resultRelInfo->ri_oldTupleSlot;
2663 tupleid,
2665 oldSlot))
2666 elog(ERROR, "failed to fetch tuple being updated");
2667 slot = ExecGetUpdateNewTuple(resultRelInfo,
2668 epqslot, oldSlot);
2669 goto redo_act;
2670
2671 case TM_Deleted:
2672 /* tuple already deleted; nothing to do */
2673 return NULL;
2674
2675 case TM_SelfModified:
2676
2677 /*
2678 * This can be reached when following an update
2679 * chain from a tuple updated by another session,
2680 * reaching a tuple that was already updated in
2681 * this transaction. If previously modified by
2682 * this command, ignore the redundant update,
2683 * otherwise error out.
2684 *
2685 * See also TM_SelfModified response to
2686 * table_tuple_update() above.
2687 */
2688 if (context->tmfd.cmax != estate->es_output_cid)
2689 ereport(ERROR,
2691 errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2692 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2693 return NULL;
2694
2695 default:
2696 /* see table_tuple_lock call in ExecDelete() */
2697 elog(ERROR, "unexpected table_tuple_lock status: %u",
2698 result);
2699 return NULL;
2700 }
2701 }
2702
2703 break;
2704
2705 case TM_Deleted:
2707 ereport(ERROR,
2709 errmsg("could not serialize access due to concurrent delete")));
2710 /* tuple already deleted; nothing to do */
2711 return NULL;
2712
2713 default:
2714 elog(ERROR, "unrecognized table_tuple_update status: %u",
2715 result);
2716 return NULL;
2717 }
2718 }
2719
2720 if (canSetTag)
2721 (estate->es_processed)++;
2722
2723 ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, tupleid, oldtuple,
2724 slot);
2725
2726 /* Process RETURNING if present */
2727 if (resultRelInfo->ri_projectReturning)
2728 return ExecProcessReturning(context, resultRelInfo, false,
2729 oldSlot, slot, context->planSlot);
2730
2731 return NULL;
2732}
#define IsBootstrapProcessingMode()
Definition miscadmin.h:477
static void ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
ExecForeignUpdate_function ExecForeignUpdate
Definition fdwapi.h:235

References Assert, TM_FailureData::cmax, ModifyTableContext::cpUpdateReturningSlot, elog, ModifyTableContext::epqstate, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg, ERROR, EState::es_output_cid, EState::es_processed, EState::es_snapshot, ModifyTableContext::estate, EvalPlanQual(), EvalPlanQualSlot(), FdwRoutine::ExecForeignUpdate, ExecGetUpdateNewTuple(), ExecInitUpdateProjection(), ExecIRUpdateTriggers(), ExecProcessReturning(), ExecUpdateAct(), ExecUpdateEpilogue(), ExecUpdatePrepareSlot(), ExecUpdatePrologue(), fb(), InplaceUpdateTupleLock, IsBootstrapProcessingMode, IsolationUsesXactSnapshot, LockTuple(), LockWaitBlock, ModifyTableContext::mtstate, ModifyTableContext::planSlot, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_needLockTagTuple, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, SnapshotAny, table_tuple_fetch_row_version(), table_tuple_lock(), TM_Deleted, TM_Ok, TM_SelfModified, TM_Updated, ModifyTableContext::tmfd, TM_FailureData::traversed, TriggerDesc::trig_update_instead_row, TupleTableSlot::tts_tableOid, TupIsNull, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, unlikely, and UnlockTuple().

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

◆ ExecUpdateAct()

static TM_Result ExecUpdateAct ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot slot,
bool  canSetTag,
UpdateContext updateCxt 
)
static

Definition at line 2199 of file nodeModifyTable.c.

2202{
2203 EState *estate = context->estate;
2206 TM_Result result;
2207
2208 updateCxt->crossPartUpdate = false;
2209
2210 /*
2211 * If we move the tuple to a new partition, we loop back here to recompute
2212 * GENERATED values (which are allowed to be different across partitions)
2213 * and recheck any RLS policies and constraints. We do not fire any
2214 * BEFORE triggers of the new partition, however.
2215 */
2216lreplace:
2217 /* Fill in GENERATEd columns */
2218 ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2219
2220 /* ensure slot is independent, consider e.g. EPQ */
2221 ExecMaterializeSlot(slot);
2222
2223 /*
2224 * If partition constraint fails, this row might get moved to another
2225 * partition, in which case we should check the RLS CHECK policy just
2226 * before inserting into the new partition, rather than doing it here.
2227 * This is because a trigger on that partition might again change the row.
2228 * So skip the WCO checks if the partition constraint fails.
2229 */
2231 resultRelationDesc->rd_rel->relispartition &&
2232 !ExecPartitionCheck(resultRelInfo, slot, estate, false);
2233
2234 /* Check any RLS UPDATE WITH CHECK policies */
2236 resultRelInfo->ri_WithCheckOptions != NIL)
2237 {
2238 /*
2239 * ExecWithCheckOptions() will skip any WCOs which are not of the kind
2240 * we are looking for at this point.
2241 */
2243 resultRelInfo, slot, estate);
2244 }
2245
2246 /*
2247 * If a partition check failed, try to move the row into the right
2248 * partition.
2249 */
2251 {
2253 *retry_slot;
2255
2256 /*
2257 * ExecCrossPartitionUpdate will first DELETE the row from the
2258 * partition it's currently in and then insert it back into the root
2259 * table, which will re-route it to the correct partition. However,
2260 * if the tuple has been concurrently updated, a retry is needed.
2261 */
2262 if (ExecCrossPartitionUpdate(context, resultRelInfo,
2263 tupleid, oldtuple, slot,
2264 canSetTag, updateCxt,
2265 &result,
2266 &retry_slot,
2269 {
2270 /* success! */
2271 updateCxt->crossPartUpdate = true;
2272
2273 /*
2274 * If the partitioned table being updated is referenced in foreign
2275 * keys, queue up trigger events to check that none of them were
2276 * violated. No special treatment is needed in
2277 * non-cross-partition update situations, because the leaf
2278 * partition's AR update triggers will take care of that. During
2279 * cross-partition updates implemented as delete on the source
2280 * partition followed by insert on the destination partition,
2281 * AR-UPDATE triggers of the root table (that is, the table
2282 * mentioned in the query) must be fired.
2283 *
2284 * NULL insert_destrel means that the move failed to occur, that
2285 * is, the update failed, so no need to anything in that case.
2286 */
2287 if (insert_destrel &&
2288 resultRelInfo->ri_TrigDesc &&
2289 resultRelInfo->ri_TrigDesc->trig_update_after_row)
2291 resultRelInfo,
2293 tupleid, slot,
2295
2296 return TM_Ok;
2297 }
2298
2299 /*
2300 * No luck, a retry is needed. If running MERGE, we do not do so
2301 * here; instead let it handle that on its own rules.
2302 */
2303 if (context->mtstate->operation == CMD_MERGE)
2304 return result;
2305
2306 /*
2307 * ExecCrossPartitionUpdate installed an updated version of the new
2308 * tuple in the retry slot; start over.
2309 */
2310 slot = retry_slot;
2311 goto lreplace;
2312 }
2313
2314 /*
2315 * Check the constraints of the tuple. We've already checked the
2316 * partition constraint above; however, we must still ensure the tuple
2317 * passes all other constraints, so we will call ExecConstraints() and
2318 * have it validate all remaining checks.
2319 */
2320 if (resultRelationDesc->rd_att->constr)
2321 ExecConstraints(resultRelInfo, slot, estate);
2322
2323 /*
2324 * replace the heap tuple
2325 *
2326 * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
2327 * the row to be updated is visible to that snapshot, and throw a
2328 * can't-serialize error if not. This is a special-case behavior needed
2329 * for referential integrity updates in transaction-snapshot mode
2330 * transactions.
2331 */
2333 estate->es_output_cid,
2334 estate->es_snapshot,
2335 estate->es_crosscheck_snapshot,
2336 true /* wait for commit */ ,
2337 &context->tmfd, &updateCxt->lockmode,
2338 &updateCxt->updateIndexes);
2339
2340 return result;
2341}
static void ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context, ResultRelInfo *sourcePartInfo, ResultRelInfo *destPartInfo, ItemPointer tupleid, TupleTableSlot *oldslot, TupleTableSlot *newslot)
static bool ExecCrossPartitionUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt, TM_Result *tmresult, TupleTableSlot **retry_slot, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
static TM_Result table_tuple_update(Relation rel, ItemPointer otid, TupleTableSlot *slot, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
Definition tableam.h:1523

References CMD_MERGE, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, ExecConstraints(), ExecCrossPartitionUpdate(), ExecCrossPartitionUpdateForeignKey(), ExecMaterializeSlot(), ExecPartitionCheck(), ExecUpdatePrepareSlot(), ExecWithCheckOptions(), fb(), ModifyTableContext::mtstate, NIL, ModifyTableState::operation, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, table_tuple_update(), TM_Ok, ModifyTableContext::tmfd, TriggerDesc::trig_update_after_row, and WCO_RLS_UPDATE_CHECK.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ ExecUpdateEpilogue()

static void ExecUpdateEpilogue ( ModifyTableContext context,
UpdateContext updateCxt,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot slot 
)
static

Definition at line 2350 of file nodeModifyTable.c.

2353{
2354 ModifyTableState *mtstate = context->mtstate;
2356
2357 /* insert index entries for tuple if necessary */
2358 if (resultRelInfo->ri_NumIndices > 0 && (updateCxt->updateIndexes != TU_None))
2359 {
2360 bits32 flags = EIIT_IS_UPDATE;
2361
2362 if (updateCxt->updateIndexes == TU_Summarizing)
2363 flags |= EIIT_ONLY_SUMMARIZING;
2364 recheckIndexes = ExecInsertIndexTuples(resultRelInfo, context->estate,
2365 flags, slot, NIL,
2366 NULL);
2367 }
2368
2369 /* AFTER ROW UPDATE Triggers */
2370 ExecARUpdateTriggers(context->estate, resultRelInfo,
2371 NULL, NULL,
2372 tupleid, oldtuple, slot,
2374 mtstate->operation == CMD_INSERT ?
2375 mtstate->mt_oc_transition_capture :
2376 mtstate->mt_transition_capture,
2377 false);
2378
2380
2381 /*
2382 * Check any WITH CHECK OPTION constraints from parent views. We are
2383 * required to do this after testing all constraints and uniqueness
2384 * violations per the SQL spec, so we do it after actually updating the
2385 * record in the heap and all indexes.
2386 *
2387 * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
2388 * are looking for at this point.
2389 */
2390 if (resultRelInfo->ri_WithCheckOptions != NIL)
2391 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo,
2392 slot, context->estate);
2393}
uint32 bits32
Definition c.h:627
#define EIIT_IS_UPDATE
Definition executor.h:748
#define EIIT_ONLY_SUMMARIZING
Definition executor.h:750
#define false
@ TU_Summarizing
Definition tableam.h:119
@ TU_None
Definition tableam.h:113

References CMD_INSERT, EIIT_IS_UPDATE, EIIT_ONLY_SUMMARIZING, ModifyTableContext::estate, ExecARUpdateTriggers(), ExecInsertIndexTuples(), ExecWithCheckOptions(), fb(), list_free(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, NIL, ModifyTableState::operation, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_WithCheckOptions, TU_None, TU_Summarizing, and WCO_VIEW_CHECK.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ ExecUpdatePrepareSlot()

static void ExecUpdatePrepareSlot ( ResultRelInfo resultRelInfo,
TupleTableSlot slot,
EState estate 
)
static

Definition at line 2164 of file nodeModifyTable.c.

2167{
2169
2170 /*
2171 * Constraints and GENERATED expressions might reference the tableoid
2172 * column, so (re-)initialize tts_tableOid before evaluating them.
2173 */
2175
2176 /*
2177 * Compute stored generated columns
2178 */
2179 if (resultRelationDesc->rd_att->constr &&
2180 resultRelationDesc->rd_att->constr->has_generated_stored)
2181 ExecComputeStoredGenerated(resultRelInfo, estate, slot,
2182 CMD_UPDATE);
2183}

References CMD_UPDATE, ExecComputeStoredGenerated(), fb(), RelationGetRelid, ResultRelInfo::ri_RelationDesc, and TupleTableSlot::tts_tableOid.

Referenced by ExecUpdate(), and ExecUpdateAct().

◆ ExecUpdatePrologue()

static bool ExecUpdatePrologue ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot slot,
TM_Result result 
)
static

Definition at line 2121 of file nodeModifyTable.c.

2124{
2126
2127 if (result)
2128 *result = TM_Ok;
2129
2130 ExecMaterializeSlot(slot);
2131
2132 /*
2133 * Open the table's indexes, if we have not done so already, so that we
2134 * can add new index entries for the updated tuple.
2135 */
2136 if (resultRelationDesc->rd_rel->relhasindex &&
2137 resultRelInfo->ri_IndexRelationDescs == NULL)
2138 ExecOpenIndices(resultRelInfo, false);
2139
2140 /* BEFORE ROW UPDATE triggers */
2141 if (resultRelInfo->ri_TrigDesc &&
2142 resultRelInfo->ri_TrigDesc->trig_update_before_row)
2143 {
2144 /* Flush any pending inserts, so rows are visible to the triggers */
2146 ExecPendingInserts(context->estate);
2147
2148 return ExecBRUpdateTriggers(context->estate, context->epqstate,
2149 resultRelInfo, tupleid, oldtuple, slot,
2150 result, &context->tmfd,
2151 context->mtstate->operation == CMD_MERGE);
2152 }
2153
2154 return true;
2155}
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_update)
Definition trigger.c:2973

References CMD_MERGE, ModifyTableContext::epqstate, EState::es_insert_pending_result_relations, ModifyTableContext::estate, ExecBRUpdateTriggers(), ExecMaterializeSlot(), ExecOpenIndices(), ExecPendingInserts(), fb(), ModifyTableContext::mtstate, NIL, ModifyTableState::operation, ResultRelInfo::ri_IndexRelationDescs, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, TM_Ok, ModifyTableContext::tmfd, and TriggerDesc::trig_update_before_row.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 4217 of file nodeModifyTable.c.

4218{
4219 ModifyTable *plan = (ModifyTable *) node->ps.plan;
4220 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4221
4222 switch (node->operation)
4223 {
4224 case CMD_INSERT:
4225 if (plan->onConflictAction == ONCONFLICT_UPDATE)
4227 resultRelInfo,
4229 ExecASInsertTriggers(node->ps.state, resultRelInfo,
4230 node->mt_transition_capture);
4231 break;
4232 case CMD_UPDATE:
4233 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4234 node->mt_transition_capture);
4235 break;
4236 case CMD_DELETE:
4237 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4238 node->mt_transition_capture);
4239 break;
4240 case CMD_MERGE:
4242 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4243 node->mt_transition_capture);
4245 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4246 node->mt_transition_capture);
4248 ExecASInsertTriggers(node->ps.state, resultRelInfo,
4249 node->mt_transition_capture);
4250 break;
4251 default:
4252 elog(ERROR, "unknown operation");
4253 break;
4254 }
4255}
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2955
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2683
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2454

References CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, elog, ERROR, ExecASDeleteTriggers(), ExecASInsertTriggers(), ExecASUpdateTriggers(), MERGE_DELETE, MERGE_INSERT, MERGE_UPDATE, ModifyTableState::mt_merge_subcommands, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, ModifyTableState::operation, PlanState::plan, plan, ModifyTableState::ps, ModifyTableState::rootResultRelInfo, and PlanState::state.

Referenced by ExecModifyTable().

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 4180 of file nodeModifyTable.c.

4181{
4182 ModifyTable *plan = (ModifyTable *) node->ps.plan;
4183 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4184
4185 switch (node->operation)
4186 {
4187 case CMD_INSERT:
4188 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4189 if (plan->onConflictAction == ONCONFLICT_UPDATE)
4191 resultRelInfo);
4192 break;
4193 case CMD_UPDATE:
4194 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4195 break;
4196 case CMD_DELETE:
4197 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4198 break;
4199 case CMD_MERGE:
4201 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4203 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4205 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4206 break;
4207 default:
4208 elog(ERROR, "unknown operation");
4209 break;
4210 }
4211}
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2403
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2632
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2897

References CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, elog, ERROR, ExecBSDeleteTriggers(), ExecBSInsertTriggers(), ExecBSUpdateTriggers(), MERGE_DELETE, MERGE_INSERT, MERGE_UPDATE, ModifyTableState::mt_merge_subcommands, ONCONFLICT_UPDATE, ModifyTableState::operation, PlanState::plan, plan, ModifyTableState::ps, ModifyTableState::rootResultRelInfo, and PlanState::state.

Referenced by ExecModifyTable().