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/xact.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.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 1394 of file nodeModifyTable.c.

1401{
1402 int i;
1403 int numInserted = numSlots;
1404 TupleTableSlot *slot = NULL;
1406
1407 /*
1408 * insert into foreign table: let the FDW do it
1409 */
1410 rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1411 resultRelInfo,
1412 slots,
1413 planSlots,
1414 &numInserted);
1415
1416 for (i = 0; i < numInserted; i++)
1417 {
1418 slot = rslots[i];
1419
1420 /*
1421 * AFTER ROW Triggers might reference the tableoid column, so
1422 * (re-)initialize tts_tableOid before evaluating them.
1423 */
1424 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1425
1426 /* AFTER ROW INSERT Triggers */
1427 ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1428 mtstate->mt_transition_capture);
1429
1430 /*
1431 * Check any WITH CHECK OPTION constraints from parent views. See the
1432 * comment in ExecInsert.
1433 */
1434 if (resultRelInfo->ri_WithCheckOptions != NIL)
1435 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1436 }
1437
1438 if (canSetTag && numInserted > 0)
1439 estate->es_processed += numInserted;
1440
1441 /* Clean up all the slots, ready for the next batch */
1442 for (i = 0; i < numSlots; i++)
1443 {
1444 ExecClearTuple(slots[i]);
1446 }
1447 resultRelInfo->ri_NumSlots = 0;
1448}
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition execMain.c:2232
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:717
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition fdwapi.h:233
struct TransitionCaptureState * mt_transition_capture
Definition execnodes.h:1445
Relation ri_RelationDesc
Definition execnodes.h:483
List * ri_WithCheckOptions
Definition execnodes.h:552
struct FdwRoutine * ri_FdwRoutine
Definition execnodes.h:536
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition trigger.c:2543
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:457

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 208 of file nodeModifyTable.c.

209{
210 TupleDesc resultDesc = RelationGetDescr(resultRel);
211 int attno = 0;
212 ListCell *lc;
213
214 foreach(lc, targetList)
215 {
218
219 Assert(!tle->resjunk); /* caller removed junk items already */
220
221 if (attno >= resultDesc->natts)
224 errmsg("table row type and query-specified row type do not match"),
225 errdetail("Query has too many columns.")));
226 attr = TupleDescAttr(resultDesc, attno);
227 attno++;
228
229 /*
230 * Special cases here should match planner's expand_insert_targetlist.
231 */
232 if (attr->attisdropped)
233 {
234 /*
235 * For a dropped column, we can't check atttypid (it's likely 0).
236 * In any case the planner has most likely inserted an INT4 null.
237 * What we insist on is just *some* NULL constant.
238 */
239 if (!IsA(tle->expr, Const) ||
240 !((Const *) tle->expr)->constisnull)
243 errmsg("table row type and query-specified row type do not match"),
244 errdetail("Query provides a value for a dropped column at ordinal position %d.",
245 attno)));
246 }
247 else if (attr->attgenerated)
248 {
249 /*
250 * For a generated column, the planner will have inserted a null
251 * of the column's base type (to avoid possibly failing on domain
252 * not-null constraints). It doesn't seem worth insisting on that
253 * exact type though, since a null value is type-independent. As
254 * above, just insist on *some* NULL constant.
255 */
256 if (!IsA(tle->expr, Const) ||
257 !((Const *) tle->expr)->constisnull)
260 errmsg("table row type and query-specified row type do not match"),
261 errdetail("Query provides a value for a generated column at ordinal position %d.",
262 attno)));
263 }
264 else
265 {
266 /* Normal case: demand type match */
267 if (exprType((Node *) tle->expr) != attr->atttypid)
270 errmsg("table row type and query-specified row type do not match"),
271 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
272 format_type_be(attr->atttypid),
273 attno,
274 format_type_be(exprType((Node *) tle->expr)))));
275 }
276 }
277 if (attno != resultDesc->natts)
280 errmsg("table row type and query-specified row type do not match"),
281 errdetail("Query has too few columns.")));
282}
#define Assert(condition)
Definition c.h:885
int errcode(int sqlerrcode)
Definition elog.c:874
int errmsg(const char *fmt,...)
Definition elog.c:1093
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
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:160

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 407 of file nodeModifyTable.c.

411{
412 Relation rel = relinfo->ri_RelationDesc;
413
414 /* Redundantly check isolation level */
416 return;
417
419 elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
420 ExecCheckTupleVisible(estate, rel, tempSlot);
422}
#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 373 of file nodeModifyTable.c.

376{
378 return;
379
380 if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
381 {
383 TransactionId xmin;
384 bool isnull;
385
387 Assert(!isnull);
389
390 /*
391 * We should not raise a serialization failure if the conflict is
392 * against a tuple inserted by our own transaction, even if it's not
393 * visible to our snapshot. (This would happen, for example, if
394 * conflicting keys are proposed for insertion in a single command.)
395 */
399 errmsg("could not serialize access due to concurrent update")));
400 }
401}
uint32 TransactionId
Definition c.h:678
#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:292
Snapshot es_snapshot
Definition execnodes.h:663
#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:419
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition xact.c:942

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 554 of file nodeModifyTable.c.

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

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 1957 of file nodeModifyTable.c.

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

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 2399 of file nodeModifyTable.c.

2405{
2406 ListCell *lc;
2409
2410 rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2412
2413 /*
2414 * For any foreign keys that point directly into a non-root ancestors of
2415 * the source partition, we can in theory fire an update event to enforce
2416 * those constraints using their triggers, if we could tell that both the
2417 * source and the destination partitions are under the same ancestor. But
2418 * for now, we simply report an error that those cannot be enforced.
2419 */
2420 foreach(lc, ancestorRels)
2421 {
2423 TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2424 bool has_noncloned_fkey = false;
2425
2426 /* Root ancestor's triggers will be processed. */
2427 if (rInfo == rootRelInfo)
2428 continue;
2429
2430 if (trigdesc && trigdesc->trig_update_after_row)
2431 {
2432 for (int i = 0; i < trigdesc->numtriggers; i++)
2433 {
2434 Trigger *trig = &trigdesc->triggers[i];
2435
2436 if (!trig->tgisclone &&
2438 {
2439 has_noncloned_fkey = true;
2440 break;
2441 }
2442 }
2443 }
2444
2446 ereport(ERROR,
2448 errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2449 errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2450 RelationGetRelationName(rInfo->ri_RelationDesc),
2451 RelationGetRelationName(rootRelInfo->ri_RelationDesc)),
2452 errhint("Consider defining the foreign key on table \"%s\".",
2453 RelationGetRelationName(rootRelInfo->ri_RelationDesc))));
2454 }
2455
2456 /* Perform the root table's triggers. */
2459 tupleid, NULL, newslot, NIL, NULL, true);
2460}
int errhint(const char *fmt,...) pg_attribute_printf(1
List * ExecGetAncestorResultRels(EState *estate, ResultRelInfo *resultRelInfo)
Definition execMain.c:1434
#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:3144
#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 1599 of file nodeModifyTable.c.

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

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 1519 of file nodeModifyTable.c.

1521{
1522 EState *estate = context->estate;
1523
1524 return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1525 estate->es_output_cid,
1526 estate->es_snapshot,
1527 estate->es_crosscheck_snapshot,
1528 true /* wait for commit */ ,
1529 &context->tmfd,
1530 changingPart);
1531}
Snapshot es_crosscheck_snapshot
Definition execnodes.h:664
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 1541 of file nodeModifyTable.c.

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

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 1487 of file nodeModifyTable.c.

1490{
1491 if (result)
1492 *result = TM_Ok;
1493
1494 /* BEFORE ROW DELETE triggers */
1495 if (resultRelInfo->ri_TrigDesc &&
1496 resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1497 {
1498 /* Flush any pending inserts, so rows are visible to the triggers */
1500 ExecPendingInserts(context->estate);
1501
1502 return ExecBRDeleteTriggers(context->estate, context->epqstate,
1503 resultRelInfo, tupleid, oldtuple,
1504 epqreturnslot, result, &context->tmfd,
1505 context->mtstate->operation == CMD_MERGE);
1506 }
1507
1508 return true;
1509}
static void ExecPendingInserts(EState *estate)
List * es_insert_pending_result_relations
Definition execnodes.h:774
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:2701

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 5425 of file nodeModifyTable.c.

5426{
5427 int i;
5428
5429 /*
5430 * Allow any FDWs to shut down
5431 */
5432 for (i = 0; i < node->mt_nrels; i++)
5433 {
5434 int j;
5435 ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
5436
5437 if (!resultRelInfo->ri_usesFdwDirectModify &&
5438 resultRelInfo->ri_FdwRoutine != NULL &&
5439 resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
5440 resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
5441 resultRelInfo);
5442
5443 /*
5444 * Cleanup the initialized batch slots. This only matters for FDWs
5445 * with batching, but the other cases will have ri_NumSlotsInitialized
5446 * == 0.
5447 */
5448 for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
5449 {
5450 ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
5452 }
5453 }
5454
5455 /*
5456 * Close all the partitioned tables, leaf partitions, and their indices
5457 * and release the slot used for tuple routing, if set.
5458 */
5460 {
5462
5463 if (node->mt_root_tuple_slot)
5465 }
5466
5467 /*
5468 * Terminate EPQ execution if active
5469 */
5471
5472 /*
5473 * shut down subplan
5474 */
5476}
void EvalPlanQualEnd(EPQState *epqstate)
Definition execMain.c:3183
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
#define outerPlanState(node)
Definition execnodes.h:1264
int j
Definition isn.c:78
EndForeignModify_function EndForeignModify
Definition fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition execnodes.h:1411
EPQState mt_epqstate
Definition execnodes.h:1421
TupleTableSlot ** ri_Slots
Definition execnodes.h:548
int ri_NumSlotsInitialized
Definition execnodes.h:546
TupleTableSlot ** ri_PlanSlots
Definition execnodes.h:549
bool ri_usesFdwDirectModify
Definition execnodes.h:542

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 778 of file nodeModifyTable.c.

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

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 822 of file nodeModifyTable.c.

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

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 440 of file nodeModifyTable.c.

443{
444 Relation rel = resultRelInfo->ri_RelationDesc;
445 TupleDesc tupdesc = RelationGetDescr(rel);
446 int natts = tupdesc->natts;
449 Bitmapset *updatedCols;
451
452 /* Nothing to do if no generated columns */
453 if (!(tupdesc->constr && (tupdesc->constr->has_generated_stored || tupdesc->constr->has_generated_virtual)))
454 return;
455
456 /*
457 * In an UPDATE, we can skip computing any generated columns that do not
458 * depend on any UPDATE target column. But if there is a BEFORE ROW
459 * UPDATE trigger, we cannot skip because the trigger might change more
460 * columns.
461 */
462 if (cmdtype == CMD_UPDATE &&
464 updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
465 else
466 updatedCols = NULL;
467
468 /*
469 * Make sure these data structures are built in the per-query memory
470 * context so they'll survive throughout the query.
471 */
473
474 ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
476
477 for (int i = 0; i < natts; i++)
478 {
479 char attgenerated = TupleDescAttr(tupdesc, i)->attgenerated;
480
481 if (attgenerated)
482 {
483 Expr *expr;
484
485 /* Fetch the GENERATED AS expression tree */
486 expr = (Expr *) build_column_default(rel, i + 1);
487 if (expr == NULL)
488 elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
489 i + 1, RelationGetRelationName(rel));
490
491 /*
492 * If it's an update with a known set of update target columns,
493 * see if we can skip the computation.
494 */
495 if (updatedCols)
496 {
497 Bitmapset *attrs_used = NULL;
498
499 pull_varattnos((Node *) expr, 1, &attrs_used);
500
501 if (!bms_overlap(updatedCols, attrs_used))
502 continue; /* need not update this column */
503 }
504
505 /* No luck, so prepare the expression for execution */
506 if (attgenerated == ATTRIBUTE_GENERATED_STORED)
507 {
508 ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
510 }
511
512 /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
513 if (cmdtype == CMD_UPDATE)
514 resultRelInfo->ri_extraUpdatedCols =
515 bms_add_member(resultRelInfo->ri_extraUpdatedCols,
517 }
518 }
519
520 if (ri_NumGeneratedNeeded == 0)
521 {
522 /* didn't need it after all */
525 }
526
527 /* Save in appropriate set of fields */
528 if (cmdtype == CMD_UPDATE)
529 {
530 /* Don't call twice */
531 Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
532
533 resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
535
536 resultRelInfo->ri_extraUpdatedCols_valid = true;
537 }
538 else
539 {
540 /* Don't call twice */
541 Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
542
543 resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
545 }
546
548}
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:765
Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition execUtils.c:1382
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:503
Bitmapset * ri_extraUpdatedCols
Definition execnodes.h:501
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 650 of file nodeModifyTable.c.

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

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 3873 of file nodeModifyTable.c.

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

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 4153 of file nodeModifyTable.c.

4155{
4156 EState *estate = mtstate->ps.state;
4157
4158 Assert(!resultRelInfo->ri_projectNewInfoValid);
4159
4160 resultRelInfo->ri_oldTupleSlot =
4161 table_slot_create(resultRelInfo->ri_RelationDesc,
4162 &estate->es_tupleTable);
4163 resultRelInfo->ri_newTupleSlot =
4164 table_slot_create(resultRelInfo->ri_RelationDesc,
4165 &estate->es_tupleTable);
4166 resultRelInfo->ri_projectNewInfoValid = true;
4167}

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 4826 of file nodeModifyTable.c.

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

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 720 of file nodeModifyTable.c.

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

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 860 of file nodeModifyTable.c.

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

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 4777 of file nodeModifyTable.c.

4779{
4780 if (node->mt_resultOidHash)
4781 {
4782 /* Use the pre-built hash table to locate the rel */
4784
4787 if (mtlookup)
4788 {
4789 if (update_cache)
4790 {
4792 node->mt_lastResultIndex = mtlookup->relationIndex;
4793 }
4794 return node->resultRelInfo + mtlookup->relationIndex;
4795 }
4796 }
4797 else
4798 {
4799 /* With few target rels, just search the ResultRelInfo array */
4800 for (int ndx = 0; ndx < node->mt_nrels; ndx++)
4801 {
4803
4804 if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
4805 {
4806 if (update_cache)
4807 {
4809 node->mt_lastResultIndex = ndx;
4810 }
4811 return rInfo;
4812 }
4813 }
4814 }
4815
4816 if (!missing_ok)
4817 elog(ERROR, "incorrect result relation OID %u", resultoid);
4818 return NULL;
4819}
@ 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 3124 of file nodeModifyTable.c.

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

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

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 3790 of file nodeModifyTable.c.

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

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 4344 of file nodeModifyTable.c.

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

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 2742 of file nodeModifyTable.c.

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

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

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 2864 of file nodeModifyTable.c.

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

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 1454 of file nodeModifyTable.c.

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

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 4284 of file nodeModifyTable.c.

4290{
4291 ResultRelInfo *partrel;
4292 TupleConversionMap *map;
4293
4294 /*
4295 * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
4296 * not find a valid partition for the tuple in 'slot' then an error is
4297 * raised. An error may also be raised if the found partition is not a
4298 * valid target for INSERTs. This is required since a partitioned table
4299 * UPDATE to another partition becomes a DELETE+INSERT.
4300 */
4301 partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
4302
4303 /*
4304 * If we're capturing transition tuples, we might need to convert from the
4305 * partition rowtype to root partitioned table's rowtype. But if there
4306 * are no BEFORE triggers on the partition that could change the tuple, we
4307 * can just remember the original unconverted tuple to avoid a needless
4308 * round trip conversion.
4309 */
4310 if (mtstate->mt_transition_capture != NULL)
4311 {
4313
4316
4319 }
4320
4321 /*
4322 * Convert the tuple, if necessary.
4323 */
4324 map = ExecGetRootToChildMap(partrel, estate);
4325 if (map != NULL)
4326 {
4327 TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
4328
4329 slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
4330 }
4331
4332 *partRelInfo = partrel;
4333 return slot;
4334}
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleTableSlot * ri_PartitionTupleSlot
Definition execnodes.h:622
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 304 of file nodeModifyTable.c.

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

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 5479 of file nodeModifyTable.c.

5480{
5481 /*
5482 * Currently, we don't need to support rescan on ModifyTable nodes. The
5483 * semantics of that would be a bit debatable anyway.
5484 */
5485 elog(ERROR, "ExecReScanModifyTable is not implemented");
5486}

References elog, and ERROR.

Referenced by ExecReScan().

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 4255 of file nodeModifyTable.c.

4256{
4257 ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
4258 ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
4259
4260 /* Check for transition tables on the directly targeted relation. */
4261 mtstate->mt_transition_capture =
4263 RelationGetRelid(targetRelInfo->ri_RelationDesc),
4264 mtstate->operation);
4265 if (plan->operation == CMD_INSERT &&
4266 plan->onConflictAction == ONCONFLICT_UPDATE)
4267 mtstate->mt_oc_transition_capture =
4269 RelationGetRelid(targetRelInfo->ri_RelationDesc),
4270 CMD_UPDATE);
4271}
#define plan(x)
Definition pg_regress.c:161
struct TransitionCaptureState * mt_oc_transition_capture
Definition execnodes.h:1448
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition trigger.c:4957

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 2492 of file nodeModifyTable.c.

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

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

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

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

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 2119 of file nodeModifyTable.c.

2122{
2124
2125 if (result)
2126 *result = TM_Ok;
2127
2128 ExecMaterializeSlot(slot);
2129
2130 /*
2131 * Open the table's indexes, if we have not done so already, so that we
2132 * can add new index entries for the updated tuple.
2133 */
2134 if (resultRelationDesc->rd_rel->relhasindex &&
2135 resultRelInfo->ri_IndexRelationDescs == NULL)
2136 ExecOpenIndices(resultRelInfo, false);
2137
2138 /* BEFORE ROW UPDATE triggers */
2139 if (resultRelInfo->ri_TrigDesc &&
2140 resultRelInfo->ri_TrigDesc->trig_update_before_row)
2141 {
2142 /* Flush any pending inserts, so rows are visible to the triggers */
2144 ExecPendingInserts(context->estate);
2145
2146 return ExecBRUpdateTriggers(context->estate, context->epqstate,
2147 resultRelInfo, tupleid, oldtuple, slot,
2148 result, &context->tmfd,
2149 context->mtstate->operation == CMD_MERGE);
2150 }
2151
2152 return true;
2153}
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:2971

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 4210 of file nodeModifyTable.c.

4211{
4212 ModifyTable *plan = (ModifyTable *) node->ps.plan;
4213 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4214
4215 switch (node->operation)
4216 {
4217 case CMD_INSERT:
4218 if (plan->onConflictAction == ONCONFLICT_UPDATE)
4220 resultRelInfo,
4222 ExecASInsertTriggers(node->ps.state, resultRelInfo,
4223 node->mt_transition_capture);
4224 break;
4225 case CMD_UPDATE:
4226 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4227 node->mt_transition_capture);
4228 break;
4229 case CMD_DELETE:
4230 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4231 node->mt_transition_capture);
4232 break;
4233 case CMD_MERGE:
4235 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4236 node->mt_transition_capture);
4238 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4239 node->mt_transition_capture);
4241 ExecASInsertTriggers(node->ps.state, resultRelInfo,
4242 node->mt_transition_capture);
4243 break;
4244 default:
4245 elog(ERROR, "unknown operation");
4246 break;
4247 }
4248}
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2953
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2681
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition trigger.c:2452

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 4173 of file nodeModifyTable.c.

4174{
4175 ModifyTable *plan = (ModifyTable *) node->ps.plan;
4176 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4177
4178 switch (node->operation)
4179 {
4180 case CMD_INSERT:
4181 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4182 if (plan->onConflictAction == ONCONFLICT_UPDATE)
4184 resultRelInfo);
4185 break;
4186 case CMD_UPDATE:
4187 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4188 break;
4189 case CMD_DELETE:
4190 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4191 break;
4192 case CMD_MERGE:
4194 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4196 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4198 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4199 break;
4200 default:
4201 elog(ERROR, "unknown operation");
4202 break;
4203 }
4204}
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2401
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2630
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:2895

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().