PostgreSQL Source Code git master
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 "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/datum.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 ExecOnConflictUpdate (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, CmdType cmdType, 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

typedef struct UpdateContext 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 1366 of file nodeModifyTable.c.

1373{
1374 int i;
1375 int numInserted = numSlots;
1376 TupleTableSlot *slot = NULL;
1377 TupleTableSlot **rslots;
1378
1379 /*
1380 * insert into foreign table: let the FDW do it
1381 */
1382 rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1383 resultRelInfo,
1384 slots,
1385 planSlots,
1386 &numInserted);
1387
1388 for (i = 0; i < numInserted; i++)
1389 {
1390 slot = rslots[i];
1391
1392 /*
1393 * AFTER ROW Triggers might reference the tableoid column, so
1394 * (re-)initialize tts_tableOid before evaluating them.
1395 */
1396 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1397
1398 /* AFTER ROW INSERT Triggers */
1399 ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1400 mtstate->mt_transition_capture);
1401
1402 /*
1403 * Check any WITH CHECK OPTION constraints from parent views. See the
1404 * comment in ExecInsert.
1405 */
1406 if (resultRelInfo->ri_WithCheckOptions != NIL)
1407 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1408 }
1409
1410 if (canSetTag && numInserted > 0)
1411 estate->es_processed += numInserted;
1412
1413 /* Clean up all the slots, ready for the next batch */
1414 for (i = 0; i < numSlots; i++)
1415 {
1416 ExecClearTuple(slots[i]);
1417 ExecClearTuple(planSlots[i]);
1418 }
1419 resultRelInfo->ri_NumSlots = 0;
1420}
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2304
int i
Definition: isn.c:77
@ WCO_VIEW_CHECK
Definition: parsenodes.h:1373
#define NIL
Definition: pg_list.h:68
#define RelationGetRelid(relation)
Definition: rel.h:516
uint64 es_processed
Definition: execnodes.h:712
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1436
Relation ri_RelationDesc
Definition: execnodes.h:475
List * ri_WithCheckOptions
Definition: execnodes.h:544
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:528
Oid tts_tableOid
Definition: tuptable.h:130
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2541
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458

References EState::es_processed, ExecARInsertTriggers(), ExecClearTuple(), FdwRoutine::ExecForeignBatchInsert, ExecWithCheckOptions(), 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 194 of file nodeModifyTable.c.

195{
196 TupleDesc resultDesc = RelationGetDescr(resultRel);
197 int attno = 0;
198 ListCell *lc;
199
200 foreach(lc, targetList)
201 {
202 TargetEntry *tle = (TargetEntry *) lfirst(lc);
204
205 Assert(!tle->resjunk); /* caller removed junk items already */
206
207 if (attno >= resultDesc->natts)
209 (errcode(ERRCODE_DATATYPE_MISMATCH),
210 errmsg("table row type and query-specified row type do not match"),
211 errdetail("Query has too many columns.")));
212 attr = TupleDescAttr(resultDesc, attno);
213 attno++;
214
215 /*
216 * Special cases here should match planner's expand_insert_targetlist.
217 */
218 if (attr->attisdropped)
219 {
220 /*
221 * For a dropped column, we can't check atttypid (it's likely 0).
222 * In any case the planner has most likely inserted an INT4 null.
223 * What we insist on is just *some* NULL constant.
224 */
225 if (!IsA(tle->expr, Const) ||
226 !((Const *) tle->expr)->constisnull)
228 (errcode(ERRCODE_DATATYPE_MISMATCH),
229 errmsg("table row type and query-specified row type do not match"),
230 errdetail("Query provides a value for a dropped column at ordinal position %d.",
231 attno)));
232 }
233 else if (attr->attgenerated)
234 {
235 /*
236 * For a generated column, the planner will have inserted a null
237 * of the column's base type (to avoid possibly failing on domain
238 * not-null constraints). It doesn't seem worth insisting on that
239 * exact type though, since a null value is type-independent. As
240 * above, just insist on *some* NULL constant.
241 */
242 if (!IsA(tle->expr, Const) ||
243 !((Const *) tle->expr)->constisnull)
245 (errcode(ERRCODE_DATATYPE_MISMATCH),
246 errmsg("table row type and query-specified row type do not match"),
247 errdetail("Query provides a value for a generated column at ordinal position %d.",
248 attno)));
249 }
250 else
251 {
252 /* Normal case: demand type match */
253 if (exprType((Node *) tle->expr) != attr->atttypid)
255 (errcode(ERRCODE_DATATYPE_MISMATCH),
256 errmsg("table row type and query-specified row type do not match"),
257 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
258 format_type_be(attr->atttypid),
259 attno,
260 format_type_be(exprType((Node *) tle->expr)))));
261 }
262 }
263 if (attno != resultDesc->natts)
265 (errcode(ERRCODE_DATATYPE_MISMATCH),
266 errmsg("table row type and query-specified row type do not match"),
267 errdetail("Query has too few columns.")));
268}
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
Assert(PointerIsAligned(start, uint64))
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
#define lfirst(lc)
Definition: pg_list.h:172
#define RelationGetDescr(relation)
Definition: rel.h:542
Definition: nodes.h:135
Expr * expr
Definition: primnodes.h:2219
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160

References Assert(), ereport, errcode(), errdetail(), errmsg(), ERROR, TargetEntry::expr, exprType(), 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 396 of file nodeModifyTable.c.

400{
401 Relation rel = relinfo->ri_RelationDesc;
402
403 /* Redundantly check isolation level */
405 return;
406
407 if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
408 elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
409 ExecCheckTupleVisible(estate, rel, tempSlot);
410 ExecClearTuple(tempSlot);
411}
#define elog(elevel,...)
Definition: elog.h:225
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:1248
#define IsolationUsesXactSnapshot()
Definition: xact.h:51

References elog, ERROR, ExecCheckTupleVisible(), ExecClearTuple(), IsolationUsesXactSnapshot, ResultRelInfo::ri_RelationDesc, SnapshotAny, and table_tuple_fetch_row_version().

Referenced by ExecInsert().

◆ ExecCheckTupleVisible()

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

Definition at line 362 of file nodeModifyTable.c.

365{
367 return;
368
369 if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
370 {
371 Datum xminDatum;
372 TransactionId xmin;
373 bool isnull;
374
375 xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
376 Assert(!isnull);
377 xmin = DatumGetTransactionId(xminDatum);
378
379 /*
380 * We should not raise a serialization failure if the conflict is
381 * against a tuple inserted by our own transaction, even if it's not
382 * visible to our snapshot. (This would happen, for example, if
383 * conflicting keys are proposed for insertion in a single command.)
384 */
388 errmsg("could not serialize access due to concurrent update")));
389 }
390}
uint32 TransactionId
Definition: c.h:623
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:77
uintptr_t Datum
Definition: postgres.h:69
static TransactionId DatumGetTransactionId(Datum X)
Definition: postgres.h:267
Snapshot es_snapshot
Definition: execnodes.h:657
#define MinTransactionIdAttributeNumber
Definition: sysattr.h:22
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition: tableam.h:1295
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:420
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:941

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

Referenced by ExecCheckTIDVisible(), and ExecOnConflictUpdate().

◆ ExecComputeStoredGenerated()

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

Definition at line 543 of file nodeModifyTable.c.

546{
547 Relation rel = resultRelInfo->ri_RelationDesc;
548 TupleDesc tupdesc = RelationGetDescr(rel);
549 int natts = tupdesc->natts;
550 ExprContext *econtext = GetPerTupleExprContext(estate);
551 ExprState **ri_GeneratedExprs;
552 MemoryContext oldContext;
553 Datum *values;
554 bool *nulls;
555
556 /* We should not be called unless this is true */
557 Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
558
559 /*
560 * Initialize the expressions if we didn't already, and check whether we
561 * can exit early because nothing needs to be computed.
562 */
563 if (cmdtype == CMD_UPDATE)
564 {
565 if (resultRelInfo->ri_GeneratedExprsU == NULL)
566 ExecInitGenerated(resultRelInfo, estate, cmdtype);
567 if (resultRelInfo->ri_NumGeneratedNeededU == 0)
568 return;
569 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
570 }
571 else
572 {
573 if (resultRelInfo->ri_GeneratedExprsI == NULL)
574 ExecInitGenerated(resultRelInfo, estate, cmdtype);
575 /* Early exit is impossible given the prior Assert */
576 Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
577 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
578 }
579
581
582 values = palloc(sizeof(*values) * natts);
583 nulls = palloc(sizeof(*nulls) * natts);
584
585 slot_getallattrs(slot);
586 memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
587
588 for (int i = 0; i < natts; i++)
589 {
590 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
591
592 if (ri_GeneratedExprs[i])
593 {
594 Datum val;
595 bool isnull;
596
597 Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
598
599 econtext->ecxt_scantuple = slot;
600
601 val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
602
603 /*
604 * We must make a copy of val as we have no guarantees about where
605 * memory for a pass-by-reference Datum is located.
606 */
607 if (!isnull)
608 val = datumCopy(val, attr->attbyval, attr->attlen);
609
610 values[i] = val;
611 nulls[i] = isnull;
612 }
613 else
614 {
615 if (!nulls[i])
616 values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
617 }
618 }
619
620 ExecClearTuple(slot);
621 memcpy(slot->tts_values, values, sizeof(*values) * natts);
622 memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
625
626 MemoryContextSwitchTo(oldContext);
627}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1741
#define GetPerTupleExprContext(estate)
Definition: executor.h:678
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:683
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:415
long val
Definition: informix.c:689
void * palloc(Size size)
Definition: mcxt.c:1945
void ExecInitGenerated(ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
@ CMD_UPDATE
Definition: nodes.h:272
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
int16 attlen
Definition: tupdesc.h:71
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:268
ExprState ** ri_GeneratedExprsI
Definition: execnodes.h:561
int ri_NumGeneratedNeededU
Definition: execnodes.h:566
ExprState ** ri_GeneratedExprsU
Definition: execnodes.h:562
int ri_NumGeneratedNeededI
Definition: execnodes.h:565
bool has_generated_stored
Definition: tupdesc.h:46
TupleConstr * constr
Definition: tupdesc.h:141
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:372
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:476

References Assert(), CompactAttribute::attbyval, CompactAttribute::attlen, CMD_UPDATE, TupleDescData::constr, datumCopy(), ExprContext::ecxt_scantuple, ExecClearTuple(), ExecEvalExpr(), ExecInitGenerated(), ExecMaterializeSlot(), ExecStoreVirtualTuple(), GetPerTupleExprContext, GetPerTupleMemoryContext, TupleConstr::has_generated_stored, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc(), 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 1928 of file nodeModifyTable.c.

1938{
1939 ModifyTableState *mtstate = context->mtstate;
1940 EState *estate = mtstate->ps.state;
1941 TupleConversionMap *tupconv_map;
1942 bool tuple_deleted;
1943 TupleTableSlot *epqslot = NULL;
1944
1945 context->cpDeletedSlot = NULL;
1946 context->cpUpdateReturningSlot = NULL;
1947 *retry_slot = NULL;
1948
1949 /*
1950 * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1951 * to migrate to a different partition. Maybe this can be implemented
1952 * some day, but it seems a fringe feature with little redeeming value.
1953 */
1954 if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1955 ereport(ERROR,
1956 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1957 errmsg("invalid ON UPDATE specification"),
1958 errdetail("The result tuple would appear in a different partition than the original tuple.")));
1959
1960 /*
1961 * When an UPDATE is run directly on a leaf partition, simply fail with a
1962 * partition constraint violation error.
1963 */
1964 if (resultRelInfo == mtstate->rootResultRelInfo)
1965 ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1966
1967 /* Initialize tuple routing info if not already done. */
1968 if (mtstate->mt_partition_tuple_routing == NULL)
1969 {
1970 Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
1971 MemoryContext oldcxt;
1972
1973 /* Things built here have to last for the query duration. */
1974 oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
1975
1977 ExecSetupPartitionTupleRouting(estate, rootRel);
1978
1979 /*
1980 * Before a partition's tuple can be re-routed, it must first be
1981 * converted to the root's format, so we'll need a slot for storing
1982 * such tuples.
1983 */
1984 Assert(mtstate->mt_root_tuple_slot == NULL);
1985 mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
1986
1987 MemoryContextSwitchTo(oldcxt);
1988 }
1989
1990 /*
1991 * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
1992 * We want to return rows from INSERT.
1993 */
1994 ExecDelete(context, resultRelInfo,
1995 tupleid, oldtuple,
1996 false, /* processReturning */
1997 true, /* changingPart */
1998 false, /* canSetTag */
1999 tmresult, &tuple_deleted, &epqslot);
2000
2001 /*
2002 * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
2003 * it was already deleted by self, or it was concurrently deleted by
2004 * another transaction), then we should skip the insert as well;
2005 * otherwise, an UPDATE could cause an increase in the total number of
2006 * rows across all partitions, which is clearly wrong.
2007 *
2008 * For a normal UPDATE, the case where the tuple has been the subject of a
2009 * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
2010 * machinery, but for an UPDATE that we've translated into a DELETE from
2011 * this partition and an INSERT into some other partition, that's not
2012 * available, because CTID chains can't span relation boundaries. We
2013 * mimic the semantics to a limited extent by skipping the INSERT if the
2014 * DELETE fails to find a tuple. This ensures that two concurrent
2015 * attempts to UPDATE the same tuple at the same time can't turn one tuple
2016 * into two, and that an UPDATE of a just-deleted tuple can't resurrect
2017 * it.
2018 */
2019 if (!tuple_deleted)
2020 {
2021 /*
2022 * epqslot will be typically NULL. But when ExecDelete() finds that
2023 * another transaction has concurrently updated the same row, it
2024 * re-fetches the row, skips the delete, and epqslot is set to the
2025 * re-fetched tuple slot. In that case, we need to do all the checks
2026 * again. For MERGE, we leave everything to the caller (it must do
2027 * additional rechecking, and might end up executing a different
2028 * action entirely).
2029 */
2030 if (mtstate->operation == CMD_MERGE)
2031 return *tmresult == TM_Ok;
2032 else if (TupIsNull(epqslot))
2033 return true;
2034 else
2035 {
2036 /* Fetch the most recent version of old tuple. */
2037 TupleTableSlot *oldSlot;
2038
2039 /* ... but first, make sure ri_oldTupleSlot is initialized. */
2040 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2041 ExecInitUpdateProjection(mtstate, resultRelInfo);
2042 oldSlot = resultRelInfo->ri_oldTupleSlot;
2044 tupleid,
2046 oldSlot))
2047 elog(ERROR, "failed to fetch tuple being updated");
2048 /* and project the new tuple to retry the UPDATE with */
2049 *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot,
2050 oldSlot);
2051 return false;
2052 }
2053 }
2054
2055 /*
2056 * resultRelInfo is one of the per-relation resultRelInfos. So we should
2057 * convert the tuple into root's tuple descriptor if needed, since
2058 * ExecInsert() starts the search from root.
2059 */
2060 tupconv_map = ExecGetChildToRootMap(resultRelInfo);
2061 if (tupconv_map != NULL)
2062 slot = execute_attr_map_slot(tupconv_map->attrMap,
2063 slot,
2064 mtstate->mt_root_tuple_slot);
2065
2066 /* Tuple routing starts from the root table. */
2067 context->cpUpdateReturningSlot =
2068 ExecInsert(context, mtstate->rootResultRelInfo, slot, canSetTag,
2069 inserted_tuple, insert_destrel);
2070
2071 /*
2072 * Reset the transition state that may possibly have been written by
2073 * INSERT.
2074 */
2075 if (mtstate->mt_transition_capture)
2077
2078 /* We're done moving. */
2079 return true;
2080}
#define unlikely(x)
Definition: c.h:347
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1985
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1301
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:426
@ CMD_MERGE
Definition: nodes.h:275
MemoryContext es_query_cxt
Definition: execnodes.h:708
TupleTableSlot * cpDeletedSlot
TupleTableSlot * cpUpdateReturningSlot
ModifyTableState * mtstate
CmdType operation
Definition: execnodes.h:1398
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1433
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1430
PlanState ps
Definition: execnodes.h:1397
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1410
Plan * plan
Definition: execnodes.h:1159
EState * state
Definition: execnodes.h:1161
bool ri_projectNewInfoValid
Definition: execnodes.h:504
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:502
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:76
AttrMap * attrMap
Definition: tupconvert.h:28
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:92
@ TM_Ok
Definition: tableam.h:77
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:192
#define TupIsNull(slot)
Definition: tuptable.h:310

References Assert(), TupleConversionMap::attrMap, 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(), 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 2365 of file nodeModifyTable.c.

2371{
2372 ListCell *lc;
2373 ResultRelInfo *rootRelInfo;
2374 List *ancestorRels;
2375
2376 rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2377 ancestorRels = ExecGetAncestorResultRels(context->estate, sourcePartInfo);
2378
2379 /*
2380 * For any foreign keys that point directly into a non-root ancestors of
2381 * the source partition, we can in theory fire an update event to enforce
2382 * those constraints using their triggers, if we could tell that both the
2383 * source and the destination partitions are under the same ancestor. But
2384 * for now, we simply report an error that those cannot be enforced.
2385 */
2386 foreach(lc, ancestorRels)
2387 {
2388 ResultRelInfo *rInfo = lfirst(lc);
2389 TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2390 bool has_noncloned_fkey = false;
2391
2392 /* Root ancestor's triggers will be processed. */
2393 if (rInfo == rootRelInfo)
2394 continue;
2395
2396 if (trigdesc && trigdesc->trig_update_after_row)
2397 {
2398 for (int i = 0; i < trigdesc->numtriggers; i++)
2399 {
2400 Trigger *trig = &trigdesc->triggers[i];
2401
2402 if (!trig->tgisclone &&
2404 {
2405 has_noncloned_fkey = true;
2406 break;
2407 }
2408 }
2409 }
2410
2411 if (has_noncloned_fkey)
2412 ereport(ERROR,
2413 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2414 errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2415 errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2418 errhint("Consider defining the foreign key on table \"%s\".",
2420 }
2421
2422 /* Perform the root table's triggers. */
2424 rootRelInfo, sourcePartInfo, destPartInfo,
2425 tupleid, NULL, newslot, NIL, NULL, true);
2426}
int errhint(const char *fmt,...)
Definition: elog.c:1318
List * ExecGetAncestorResultRels(EState *estate, ResultRelInfo *resultRelInfo)
Definition: execMain.c:1506
#define RelationGetRelationName(relation)
Definition: rel.h:550
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3210
Definition: pg_list.h:54
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:615
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:510
int numtriggers
Definition: reltrigger.h:50
Trigger * triggers
Definition: reltrigger.h:49
bool trig_update_after_row
Definition: reltrigger.h:62
Oid tgfoid
Definition: reltrigger.h:28
bool tgisclone
Definition: reltrigger.h:32
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:3106
#define RI_TRIGGER_PK
Definition: trigger.h:283

References ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, ModifyTableContext::estate, ExecARUpdateTriggers(), ExecGetAncestorResultRels(), i, lfirst, NIL, TriggerDesc::numtriggers, RelationGetRelationName, RI_FKey_trigger_type(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, RI_TRIGGER_PK, Trigger::tgfoid, Trigger::tgisclone, 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 1570 of file nodeModifyTable.c.

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

References Assert(), TupleConversionMap::attrMap, TM_FailureData::cmax, CMD_DELETE, 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(), 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 1490 of file nodeModifyTable.c.

1492{
1493 EState *estate = context->estate;
1494
1495 return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1496 estate->es_output_cid,
1497 estate->es_snapshot,
1498 estate->es_crosscheck_snapshot,
1499 true /* wait for commit */ ,
1500 &context->tmfd,
1501 changingPart);
1502}
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:658
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:1451

References EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, 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 1512 of file nodeModifyTable.c.

1514{
1515 ModifyTableState *mtstate = context->mtstate;
1516 EState *estate = context->estate;
1517 TransitionCaptureState *ar_delete_trig_tcs;
1518
1519 /*
1520 * If this delete is the result of a partition key update that moved the
1521 * tuple to a new partition, put this row into the transition OLD TABLE,
1522 * if there is one. We need to do this separately for DELETE and INSERT
1523 * because they happen on different tables.
1524 */
1525 ar_delete_trig_tcs = mtstate->mt_transition_capture;
1526 if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1528 {
1529 ExecARUpdateTriggers(estate, resultRelInfo,
1530 NULL, NULL,
1531 tupleid, oldtuple,
1532 NULL, NULL, mtstate->mt_transition_capture,
1533 false);
1534
1535 /*
1536 * We've already captured the OLD TABLE row, so make sure any AR
1537 * DELETE trigger fired below doesn't capture it again.
1538 */
1539 ar_delete_trig_tcs = NULL;
1540 }
1541
1542 /* AFTER ROW DELETE Triggers */
1543 ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1544 ar_delete_trig_tcs, changingPart);
1545}
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:2781

References CMD_UPDATE, ModifyTableContext::estate, ExecARDeleteTriggers(), ExecARUpdateTriggers(), 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 1459 of file nodeModifyTable.c.

1462{
1463 if (result)
1464 *result = TM_Ok;
1465
1466 /* BEFORE ROW DELETE triggers */
1467 if (resultRelInfo->ri_TrigDesc &&
1468 resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1469 {
1470 /* Flush any pending inserts, so rows are visible to the triggers */
1472 ExecPendingInserts(context->estate);
1473
1474 return ExecBRDeleteTriggers(context->estate, context->epqstate,
1475 resultRelInfo, tupleid, oldtuple,
1476 epqreturnslot, result, &context->tmfd);
1477 }
1478
1479 return true;
1480}
static void ExecPendingInserts(EState *estate)
List * es_insert_pending_result_relations
Definition: execnodes.h:770
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)
Definition: trigger.c:2690

References ModifyTableContext::epqstate, EState::es_insert_pending_result_relations, ModifyTableContext::estate, ExecBRDeleteTriggers(), ExecPendingInserts(), NIL, 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 5066 of file nodeModifyTable.c.

5067{
5068 int i;
5069
5070 /*
5071 * Allow any FDWs to shut down
5072 */
5073 for (i = 0; i < node->mt_nrels; i++)
5074 {
5075 int j;
5076 ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
5077
5078 if (!resultRelInfo->ri_usesFdwDirectModify &&
5079 resultRelInfo->ri_FdwRoutine != NULL &&
5080 resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
5081 resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
5082 resultRelInfo);
5083
5084 /*
5085 * Cleanup the initialized batch slots. This only matters for FDWs
5086 * with batching, but the other cases will have ri_NumSlotsInitialized
5087 * == 0.
5088 */
5089 for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
5090 {
5091 ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
5093 }
5094 }
5095
5096 /*
5097 * Close all the partitioned tables, leaf partitions, and their indices
5098 * and release the slot used for tuple routing, if set.
5099 */
5101 {
5103
5104 if (node->mt_root_tuple_slot)
5106 }
5107
5108 /*
5109 * Terminate EPQ execution if active
5110 */
5112
5113 /*
5114 * shut down subplan
5115 */
5117}
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:3249
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
#define outerPlanState(node)
Definition: execnodes.h:1255
int j
Definition: isn.c:78
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1402
EPQState mt_epqstate
Definition: execnodes.h:1412
TupleTableSlot ** ri_Slots
Definition: execnodes.h:540
int ri_NumSlotsInitialized
Definition: execnodes.h:538
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:541
bool ri_usesFdwDirectModify
Definition: execnodes.h:534

References FdwRoutine::EndForeignModify, EvalPlanQualEnd(), ExecCleanupTupleRouting(), ExecDropSingleTupleTableSlot(), ExecEndNode(), 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 767 of file nodeModifyTable.c.

769{
770 ProjectionInfo *newProj = relinfo->ri_projectNew;
771 ExprContext *econtext;
772
773 /*
774 * If there's no projection to be done, just make sure the slot is of the
775 * right type for the target rel. If the planSlot is the right type we
776 * can use it as-is, else copy the data into ri_newTupleSlot.
777 */
778 if (newProj == NULL)
779 {
780 if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
781 {
782 ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
783 return relinfo->ri_newTupleSlot;
784 }
785 else
786 return planSlot;
787 }
788
789 /*
790 * Else project; since the projection output slot is ri_newTupleSlot, this
791 * will also fix any slot-type problem.
792 *
793 * Note: currently, this is dead code, because INSERT cases don't receive
794 * any junk columns so there's never a projection to be done.
795 */
796 econtext = newProj->pi_exprContext;
797 econtext->ecxt_outertuple = planSlot;
798 return ExecProject(newProj);
799}
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:505
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:272
ExprContext * pi_exprContext
Definition: execnodes.h:383
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:500
ProjectionInfo * ri_projectNew
Definition: execnodes.h:498
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:121
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:525

References ExprContext::ecxt_outertuple, ExecCopySlot(), ExecProject(), ProjectionInfo::pi_exprContext, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_projectNew, and TupleTableSlot::tts_ops.

Referenced by ExecModifyTable().

◆ ExecGetUpdateNewTuple()

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

Definition at line 811 of file nodeModifyTable.c.

814{
815 ProjectionInfo *newProj = relinfo->ri_projectNew;
816 ExprContext *econtext;
817
818 /* Use a few extra Asserts to protect against outside callers */
820 Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
821 Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
822
823 econtext = newProj->pi_exprContext;
824 econtext->ecxt_outertuple = planSlot;
825 econtext->ecxt_scantuple = oldSlot;
826 return ExecProject(newProj);
827}

References Assert(), ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, ExecProject(), ProjectionInfo::pi_exprContext, ResultRelInfo::ri_projectNew, ResultRelInfo::ri_projectNewInfoValid, and TTS_EMPTY.

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

◆ ExecInitGenerated()

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

Definition at line 429 of file nodeModifyTable.c.

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

641{
642 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
643 Plan *subplan = outerPlan(node);
644 EState *estate = mtstate->ps.state;
645 List *insertTargetList = NIL;
646 bool need_projection = false;
647 ListCell *l;
648
649 /* Extract non-junk columns of the subplan's result tlist. */
650 foreach(l, subplan->targetlist)
651 {
652 TargetEntry *tle = (TargetEntry *) lfirst(l);
653
654 if (!tle->resjunk)
655 insertTargetList = lappend(insertTargetList, tle);
656 else
657 need_projection = true;
658 }
659
660 /*
661 * The junk-free list must produce a tuple suitable for the result
662 * relation.
663 */
664 ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
665
666 /* We'll need a slot matching the table's format. */
667 resultRelInfo->ri_newTupleSlot =
668 table_slot_create(resultRelInfo->ri_RelationDesc,
669 &estate->es_tupleTable);
670
671 /* Build ProjectionInfo if needed (it probably isn't). */
672 if (need_projection)
673 {
674 TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
675
676 /* need an expression context to do the projection */
677 if (mtstate->ps.ps_ExprContext == NULL)
678 ExecAssignExprContext(estate, &mtstate->ps);
679
680 resultRelInfo->ri_projectNew =
681 ExecBuildProjectionInfo(insertTargetList,
682 mtstate->ps.ps_ExprContext,
683 resultRelInfo->ri_newTupleSlot,
684 &mtstate->ps,
685 relDesc);
686 }
687
688 resultRelInfo->ri_projectNewInfoValid = true;
689}
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:486
List * lappend(List *list, void *datum)
Definition: list.c:339
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define outerPlan(node)
Definition: plannodes.h:241
List * es_tupleTable
Definition: execnodes.h:710
ExprContext * ps_ExprContext
Definition: execnodes.h:1198

References EState::es_tupleTable, ExecAssignExprContext(), ExecBuildProjectionInfo(), ExecCheckPlanOutput(), 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, and table_slot_create().

Referenced by ExecModifyTable().

◆ ExecInitMerge()

void ExecInitMerge ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 3664 of file nodeModifyTable.c.

3665{
3666 List *mergeActionLists = mtstate->mt_mergeActionLists;
3667 List *mergeJoinConditions = mtstate->mt_mergeJoinConditions;
3668 ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
3669 ResultRelInfo *resultRelInfo;
3670 ExprContext *econtext;
3671 ListCell *lc;
3672 int i;
3673
3674 if (mergeActionLists == NIL)
3675 return;
3676
3677 mtstate->mt_merge_subcommands = 0;
3678
3679 if (mtstate->ps.ps_ExprContext == NULL)
3680 ExecAssignExprContext(estate, &mtstate->ps);
3681 econtext = mtstate->ps.ps_ExprContext;
3682
3683 /*
3684 * Create a MergeActionState for each action on the mergeActionList and
3685 * add it to either a list of matched actions or not-matched actions.
3686 *
3687 * Similar logic appears in ExecInitPartitionInfo(), so if changing
3688 * anything here, do so there too.
3689 */
3690 i = 0;
3691 foreach(lc, mergeActionLists)
3692 {
3693 List *mergeActionList = lfirst(lc);
3694 Node *joinCondition;
3695 TupleDesc relationDesc;
3696 ListCell *l;
3697
3698 joinCondition = (Node *) list_nth(mergeJoinConditions, i);
3699 resultRelInfo = mtstate->resultRelInfo + i;
3700 i++;
3701 relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3702
3703 /* initialize slots for MERGE fetches from this rel */
3704 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3705 ExecInitMergeTupleSlots(mtstate, resultRelInfo);
3706
3707 /* initialize state for join condition checking */
3708 resultRelInfo->ri_MergeJoinCondition =
3709 ExecInitQual((List *) joinCondition, &mtstate->ps);
3710
3711 foreach(l, mergeActionList)
3712 {
3714 MergeActionState *action_state;
3715 TupleTableSlot *tgtslot;
3716 TupleDesc tgtdesc;
3717
3718 /*
3719 * Build action merge state for this rel. (For partitions,
3720 * equivalent code exists in ExecInitPartitionInfo.)
3721 */
3722 action_state = makeNode(MergeActionState);
3723 action_state->mas_action = action;
3724 action_state->mas_whenqual = ExecInitQual((List *) action->qual,
3725 &mtstate->ps);
3726
3727 /*
3728 * We create three lists - one for each MergeMatchKind - and stick
3729 * the MergeActionState into the appropriate list.
3730 */
3731 resultRelInfo->ri_MergeActions[action->matchKind] =
3732 lappend(resultRelInfo->ri_MergeActions[action->matchKind],
3733 action_state);
3734
3735 switch (action->commandType)
3736 {
3737 case CMD_INSERT:
3739 action->targetList);
3740
3741 /*
3742 * If the MERGE targets a partitioned table, any INSERT
3743 * actions must be routed through it, not the child
3744 * relations. Initialize the routing struct and the root
3745 * table's "new" tuple slot for that, if not already done.
3746 * The projection we prepare, for all relations, uses the
3747 * root relation descriptor, and targets the plan's root
3748 * slot. (This is consistent with the fact that we
3749 * checked the plan output to match the root relation,
3750 * above.)
3751 */
3752 if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
3753 RELKIND_PARTITIONED_TABLE)
3754 {
3755 if (mtstate->mt_partition_tuple_routing == NULL)
3756 {
3757 /*
3758 * Initialize planstate for routing if not already
3759 * done.
3760 *
3761 * Note that the slot is managed as a standalone
3762 * slot belonging to ModifyTableState, so we pass
3763 * NULL for the 2nd argument.
3764 */
3765 mtstate->mt_root_tuple_slot =
3767 NULL);
3770 rootRelInfo->ri_RelationDesc);
3771 }
3772 tgtslot = mtstate->mt_root_tuple_slot;
3773 tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3774 }
3775 else
3776 {
3777 /* not partitioned? use the stock relation and slot */
3778 tgtslot = resultRelInfo->ri_newTupleSlot;
3779 tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3780 }
3781
3782 action_state->mas_proj =
3783 ExecBuildProjectionInfo(action->targetList, econtext,
3784 tgtslot,
3785 &mtstate->ps,
3786 tgtdesc);
3787
3789 break;
3790 case CMD_UPDATE:
3791 action_state->mas_proj =
3793 true,
3794 action->updateColnos,
3795 relationDesc,
3796 econtext,
3797 resultRelInfo->ri_newTupleSlot,
3798 &mtstate->ps);
3800 break;
3801 case CMD_DELETE:
3803 break;
3804 case CMD_NOTHING:
3805 break;
3806 default:
3807 elog(ERROR, "unknown action in MERGE WHEN clause");
3808 break;
3809 }
3810 }
3811 }
3812}
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:1388
#define MERGE_INSERT
Definition: execnodes.h:1387
#define MERGE_DELETE
Definition: execnodes.h:1389
void ExecInitMergeTupleSlots(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
@ CMD_INSERT
Definition: nodes.h:273
@ CMD_NOTHING
Definition: nodes.h:278
#define makeNode(_type_)
Definition: nodes.h:161
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
MergeAction * mas_action
Definition: execnodes.h:444
ProjectionInfo * mas_proj
Definition: execnodes.h:445
ExprState * mas_whenqual
Definition: execnodes.h:447
List * mt_mergeJoinConditions
Definition: execnodes.h:1466
int mt_merge_subcommands
Definition: execnodes.h:1442
List * mt_mergeActionLists
Definition: execnodes.h:1465
Form_pg_class rd_rel
Definition: rel.h:111
ExprState * ri_MergeJoinCondition
Definition: execnodes.h:584
List * ri_MergeActions[NUM_MERGE_MATCH_KINDS]
Definition: execnodes.h:581

References generate_unaccent_rules::action, CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_UPDATE, elog, ERROR, ExecAssignExprContext(), ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecCheckPlanOutput(), ExecInitMergeTupleSlots(), ExecInitQual(), ExecSetupPartitionTupleRouting(), i, lappend(), lfirst, list_nth(), makeNode, 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, ModifyTableState::ps, PlanState::ps_ExprContext, RelationData::rd_rel, RelationGetDescr, ModifyTableState::resultRelInfo, ResultRelInfo::ri_MergeActions, ResultRelInfo::ri_MergeJoinCondition, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, ModifyTableState::rootResultRelInfo, table_slot_create(), and unlikely.

Referenced by ExecInitModifyTable().

◆ ExecInitMergeTupleSlots()

void ExecInitMergeTupleSlots ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)

Definition at line 3821 of file nodeModifyTable.c.

3823{
3824 EState *estate = mtstate->ps.state;
3825
3826 Assert(!resultRelInfo->ri_projectNewInfoValid);
3827
3828 resultRelInfo->ri_oldTupleSlot =
3829 table_slot_create(resultRelInfo->ri_RelationDesc,
3830 &estate->es_tupleTable);
3831 resultRelInfo->ri_newTupleSlot =
3832 table_slot_create(resultRelInfo->ri_RelationDesc,
3833 &estate->es_tupleTable);
3834 resultRelInfo->ri_projectNewInfoValid = true;
3835}

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

4490{
4491 ModifyTableState *mtstate;
4492 Plan *subplan = outerPlan(node);
4493 CmdType operation = node->operation;
4494 int total_nrels = list_length(node->resultRelations);
4495 int nrels;
4496 List *resultRelations = NIL;
4497 List *withCheckOptionLists = NIL;
4498 List *returningLists = NIL;
4499 List *updateColnosLists = NIL;
4500 List *mergeActionLists = NIL;
4501 List *mergeJoinConditions = NIL;
4502 ResultRelInfo *resultRelInfo;
4503 List *arowmarks;
4504 ListCell *l;
4505 int i;
4506 Relation rel;
4507
4508 /* check for unsupported flags */
4509 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
4510
4511 /*
4512 * Only consider unpruned relations for initializing their ResultRelInfo
4513 * struct and other fields such as withCheckOptions, etc.
4514 *
4515 * Note: We must avoid pruning every result relation. This is important
4516 * for MERGE, since even if every result relation is pruned from the
4517 * subplan, there might still be NOT MATCHED rows, for which there may be
4518 * INSERT actions to perform. To allow these actions to be found, at
4519 * least one result relation must be kept. Also, when inserting into a
4520 * partitioned table, ExecInitPartitionInfo() needs a ResultRelInfo struct
4521 * as a reference for building the ResultRelInfo of the target partition.
4522 * In either case, it doesn't matter which result relation is kept, so we
4523 * just keep the first one, if all others have been pruned. See also,
4524 * ExecDoInitialPruning(), which ensures that this first result relation
4525 * has been locked.
4526 */
4527 i = 0;
4528 foreach(l, node->resultRelations)
4529 {
4530 Index rti = lfirst_int(l);
4531 bool keep_rel;
4532
4533 keep_rel = bms_is_member(rti, estate->es_unpruned_relids);
4534 if (!keep_rel && i == total_nrels - 1 && resultRelations == NIL)
4535 {
4536 /* all result relations pruned; keep the first one */
4537 keep_rel = true;
4538 rti = linitial_int(node->resultRelations);
4539 i = 0;
4540 }
4541
4542 if (keep_rel)
4543 {
4544 resultRelations = lappend_int(resultRelations, rti);
4545 if (node->withCheckOptionLists)
4546 {
4547 List *withCheckOptions = list_nth_node(List,
4549 i);
4550
4551 withCheckOptionLists = lappend(withCheckOptionLists, withCheckOptions);
4552 }
4553 if (node->returningLists)
4554 {
4555 List *returningList = list_nth_node(List,
4556 node->returningLists,
4557 i);
4558
4559 returningLists = lappend(returningLists, returningList);
4560 }
4561 if (node->updateColnosLists)
4562 {
4563 List *updateColnosList = list_nth(node->updateColnosLists, i);
4564
4565 updateColnosLists = lappend(updateColnosLists, updateColnosList);
4566 }
4567 if (node->mergeActionLists)
4568 {
4569 List *mergeActionList = list_nth(node->mergeActionLists, i);
4570
4571 mergeActionLists = lappend(mergeActionLists, mergeActionList);
4572 }
4573 if (node->mergeJoinConditions)
4574 {
4575 List *mergeJoinCondition = list_nth(node->mergeJoinConditions, i);
4576
4577 mergeJoinConditions = lappend(mergeJoinConditions, mergeJoinCondition);
4578 }
4579 }
4580 i++;
4581 }
4582 nrels = list_length(resultRelations);
4583 Assert(nrels > 0);
4584
4585 /*
4586 * create state structure
4587 */
4588 mtstate = makeNode(ModifyTableState);
4589 mtstate->ps.plan = (Plan *) node;
4590 mtstate->ps.state = estate;
4591 mtstate->ps.ExecProcNode = ExecModifyTable;
4592
4593 mtstate->operation = operation;
4594 mtstate->canSetTag = node->canSetTag;
4595 mtstate->mt_done = false;
4596
4597 mtstate->mt_nrels = nrels;
4598 mtstate->resultRelInfo = (ResultRelInfo *)
4599 palloc(nrels * sizeof(ResultRelInfo));
4600
4601 mtstate->mt_merge_pending_not_matched = NULL;
4602 mtstate->mt_merge_inserted = 0;
4603 mtstate->mt_merge_updated = 0;
4604 mtstate->mt_merge_deleted = 0;
4605 mtstate->mt_updateColnosLists = updateColnosLists;
4606 mtstate->mt_mergeActionLists = mergeActionLists;
4607 mtstate->mt_mergeJoinConditions = mergeJoinConditions;
4608
4609 /*----------
4610 * Resolve the target relation. This is the same as:
4611 *
4612 * - the relation for which we will fire FOR STATEMENT triggers,
4613 * - the relation into whose tuple format all captured transition tuples
4614 * must be converted, and
4615 * - the root partitioned table used for tuple routing.
4616 *
4617 * If it's a partitioned or inherited table, the root partition or
4618 * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
4619 * given explicitly in node->rootRelation. Otherwise, the target relation
4620 * is the sole relation in the node->resultRelations list and, since it can
4621 * never be pruned, also in the resultRelations list constructed above.
4622 *----------
4623 */
4624 if (node->rootRelation > 0)
4625 {
4629 node->rootRelation);
4630 }
4631 else
4632 {
4633 Assert(list_length(node->resultRelations) == 1);
4634 Assert(list_length(resultRelations) == 1);
4635 mtstate->rootResultRelInfo = mtstate->resultRelInfo;
4636 ExecInitResultRelation(estate, mtstate->resultRelInfo,
4637 linitial_int(resultRelations));
4638 }
4639
4640 /* set up epqstate with dummy subplan data for the moment */
4641 EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
4642 node->epqParam, resultRelations);
4643 mtstate->fireBSTriggers = true;
4644
4645 /*
4646 * Build state for collecting transition tuples. This requires having a
4647 * valid trigger query context, so skip it in explain-only mode.
4648 */
4649 if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
4650 ExecSetupTransitionCaptureState(mtstate, estate);
4651
4652 /*
4653 * Open all the result relations and initialize the ResultRelInfo structs.
4654 * (But root relation was initialized above, if it's part of the array.)
4655 * We must do this before initializing the subplan, because direct-modify
4656 * FDWs expect their ResultRelInfos to be available.
4657 */
4658 resultRelInfo = mtstate->resultRelInfo;
4659 i = 0;
4660 foreach(l, resultRelations)
4661 {
4662 Index resultRelation = lfirst_int(l);
4663 List *mergeActions = NIL;
4664
4665 if (mergeActionLists)
4666 mergeActions = list_nth(mergeActionLists, i);
4667
4668 if (resultRelInfo != mtstate->rootResultRelInfo)
4669 {
4670 ExecInitResultRelation(estate, resultRelInfo, resultRelation);
4671
4672 /*
4673 * For child result relations, store the root result relation
4674 * pointer. We do so for the convenience of places that want to
4675 * look at the query's original target relation but don't have the
4676 * mtstate handy.
4677 */
4678 resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
4679 }
4680
4681 /* Initialize the usesFdwDirectModify flag */
4682 resultRelInfo->ri_usesFdwDirectModify =
4684
4685 /*
4686 * Verify result relation is a valid target for the current operation
4687 */
4688 CheckValidResultRel(resultRelInfo, operation, mergeActions);
4689
4690 resultRelInfo++;
4691 i++;
4692 }
4693
4694 /*
4695 * Now we may initialize the subplan.
4696 */
4697 outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
4698
4699 /*
4700 * Do additional per-result-relation initialization.
4701 */
4702 for (i = 0; i < nrels; i++)
4703 {
4704 resultRelInfo = &mtstate->resultRelInfo[i];
4705
4706 /* Let FDWs init themselves for foreign-table result rels */
4707 if (!resultRelInfo->ri_usesFdwDirectModify &&
4708 resultRelInfo->ri_FdwRoutine != NULL &&
4709 resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
4710 {
4711 List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
4712
4713 resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
4714 resultRelInfo,
4715 fdw_private,
4716 i,
4717 eflags);
4718 }
4719
4720 /*
4721 * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
4722 * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
4723 * tables, the FDW might have created additional junk attr(s), but
4724 * those are no concern of ours.
4725 */
4726 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4727 operation == CMD_MERGE)
4728 {
4729 char relkind;
4730
4731 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4732 if (relkind == RELKIND_RELATION ||
4733 relkind == RELKIND_MATVIEW ||
4734 relkind == RELKIND_PARTITIONED_TABLE)
4735 {
4736 resultRelInfo->ri_RowIdAttNo =
4737 ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
4738 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4739 elog(ERROR, "could not find junk ctid column");
4740 }
4741 else if (relkind == RELKIND_FOREIGN_TABLE)
4742 {
4743 /*
4744 * We don't support MERGE with foreign tables for now. (It's
4745 * problematic because the implementation uses CTID.)
4746 */
4747 Assert(operation != CMD_MERGE);
4748
4749 /*
4750 * When there is a row-level trigger, there should be a
4751 * wholerow attribute. We also require it to be present in
4752 * UPDATE and MERGE, so we can get the values of unchanged
4753 * columns.
4754 */
4755 resultRelInfo->ri_RowIdAttNo =
4757 "wholerow");
4758 if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
4759 !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4760 elog(ERROR, "could not find junk wholerow column");
4761 }
4762 else
4763 {
4764 /* Other valid target relkinds must provide wholerow */
4765 resultRelInfo->ri_RowIdAttNo =
4767 "wholerow");
4768 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4769 elog(ERROR, "could not find junk wholerow column");
4770 }
4771 }
4772 }
4773
4774 /*
4775 * If this is an inherited update/delete/merge, there will be a junk
4776 * attribute named "tableoid" present in the subplan's targetlist. It
4777 * will be used to identify the result relation for a given tuple to be
4778 * updated/deleted/merged.
4779 */
4780 mtstate->mt_resultOidAttno =
4781 ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
4782 Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || total_nrels == 1);
4783 mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
4784 mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
4785
4786 /* Get the root target relation */
4787 rel = mtstate->rootResultRelInfo->ri_RelationDesc;
4788
4789 /*
4790 * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
4791 * or MERGE might need this too, but only if it actually moves tuples
4792 * between partitions; in that case setup is done by
4793 * ExecCrossPartitionUpdate.
4794 */
4795 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
4796 operation == CMD_INSERT)
4798 ExecSetupPartitionTupleRouting(estate, rel);
4799
4800 /*
4801 * Initialize any WITH CHECK OPTION constraints if needed.
4802 */
4803 resultRelInfo = mtstate->resultRelInfo;
4804 foreach(l, withCheckOptionLists)
4805 {
4806 List *wcoList = (List *) lfirst(l);
4807 List *wcoExprs = NIL;
4808 ListCell *ll;
4809
4810 foreach(ll, wcoList)
4811 {
4812 WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
4813 ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
4814 &mtstate->ps);
4815
4816 wcoExprs = lappend(wcoExprs, wcoExpr);
4817 }
4818
4819 resultRelInfo->ri_WithCheckOptions = wcoList;
4820 resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4821 resultRelInfo++;
4822 }
4823
4824 /*
4825 * Initialize RETURNING projections if needed.
4826 */
4827 if (returningLists)
4828 {
4829 TupleTableSlot *slot;
4830 ExprContext *econtext;
4831
4832 /*
4833 * Initialize result tuple slot and assign its rowtype using the first
4834 * RETURNING list. We assume the rest will look the same.
4835 */
4836 mtstate->ps.plan->targetlist = (List *) linitial(returningLists);
4837
4838 /* Set up a slot for the output of the RETURNING projection(s) */
4840 slot = mtstate->ps.ps_ResultTupleSlot;
4841
4842 /* Need an econtext too */
4843 if (mtstate->ps.ps_ExprContext == NULL)
4844 ExecAssignExprContext(estate, &mtstate->ps);
4845 econtext = mtstate->ps.ps_ExprContext;
4846
4847 /*
4848 * Build a projection for each result rel.
4849 */
4850 resultRelInfo = mtstate->resultRelInfo;
4851 foreach(l, returningLists)
4852 {
4853 List *rlist = (List *) lfirst(l);
4854
4855 resultRelInfo->ri_returningList = rlist;
4856 resultRelInfo->ri_projectReturning =
4857 ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
4858 resultRelInfo->ri_RelationDesc->rd_att);
4859 resultRelInfo++;
4860 }
4861 }
4862 else
4863 {
4864 /*
4865 * We still must construct a dummy result tuple type, because InitPlan
4866 * expects one (maybe should change that?).
4867 */
4868 mtstate->ps.plan->targetlist = NIL;
4869 ExecInitResultTypeTL(&mtstate->ps);
4870
4871 mtstate->ps.ps_ExprContext = NULL;
4872 }
4873
4874 /* Set the list of arbiter indexes if needed for ON CONFLICT */
4875 resultRelInfo = mtstate->resultRelInfo;
4876 if (node->onConflictAction != ONCONFLICT_NONE)
4877 {
4878 /* insert may only have one relation, inheritance is not expanded */
4879 Assert(total_nrels == 1);
4880 resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
4881 }
4882
4883 /*
4884 * If needed, Initialize target list, projection and qual for ON CONFLICT
4885 * DO UPDATE.
4886 */
4888 {
4890 ExprContext *econtext;
4891 TupleDesc relationDesc;
4892
4893 /* already exists if created by RETURNING processing above */
4894 if (mtstate->ps.ps_ExprContext == NULL)
4895 ExecAssignExprContext(estate, &mtstate->ps);
4896
4897 econtext = mtstate->ps.ps_ExprContext;
4898 relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
4899
4900 /* create state for DO UPDATE SET operation */
4901 resultRelInfo->ri_onConflict = onconfl;
4902
4903 /* initialize slot for the existing tuple */
4904 onconfl->oc_Existing =
4905 table_slot_create(resultRelInfo->ri_RelationDesc,
4906 &mtstate->ps.state->es_tupleTable);
4907
4908 /*
4909 * Create the tuple slot for the UPDATE SET projection. We want a slot
4910 * of the table's type here, because the slot will be used to insert
4911 * into the table, and for RETURNING processing - which may access
4912 * system attributes.
4913 */
4914 onconfl->oc_ProjSlot =
4915 table_slot_create(resultRelInfo->ri_RelationDesc,
4916 &mtstate->ps.state->es_tupleTable);
4917
4918 /* build UPDATE SET projection state */
4919 onconfl->oc_ProjInfo =
4921 true,
4922 node->onConflictCols,
4923 relationDesc,
4924 econtext,
4925 onconfl->oc_ProjSlot,
4926 &mtstate->ps);
4927
4928 /* initialize state to evaluate the WHERE clause, if any */
4929 if (node->onConflictWhere)
4930 {
4931 ExprState *qualexpr;
4932
4933 qualexpr = ExecInitQual((List *) node->onConflictWhere,
4934 &mtstate->ps);
4935 onconfl->oc_WhereClause = qualexpr;
4936 }
4937 }
4938
4939 /*
4940 * If we have any secondary relations in an UPDATE or DELETE, they need to
4941 * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
4942 * EvalPlanQual mechanism needs to be told about them. This also goes for
4943 * the source relations in a MERGE. Locate the relevant ExecRowMarks.
4944 */
4945 arowmarks = NIL;
4946 foreach(l, node->rowMarks)
4947 {
4949 ExecRowMark *erm;
4950 ExecAuxRowMark *aerm;
4951
4952 /*
4953 * Ignore "parent" rowmarks, because they are irrelevant at runtime.
4954 * Also ignore the rowmarks belonging to child tables that have been
4955 * pruned in ExecDoInitialPruning().
4956 */
4957 if (rc->isParent ||
4958 !bms_is_member(rc->rti, estate->es_unpruned_relids))
4959 continue;
4960
4961 /* Find ExecRowMark and build ExecAuxRowMark */
4962 erm = ExecFindRowMark(estate, rc->rti, false);
4963 aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
4964 arowmarks = lappend(arowmarks, aerm);
4965 }
4966
4967 /* For a MERGE command, initialize its state */
4968 if (mtstate->operation == CMD_MERGE)
4969 ExecInitMerge(mtstate, estate);
4970
4971 EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
4972
4973 /*
4974 * If there are a lot of result relations, use a hash table to speed the
4975 * lookups. If there are not a lot, a simple linear search is faster.
4976 *
4977 * It's not clear where the threshold is, but try 64 for starters. In a
4978 * debugging build, use a small threshold so that we get some test
4979 * coverage of both code paths.
4980 */
4981#ifdef USE_ASSERT_CHECKING
4982#define MT_NRELS_HASH 4
4983#else
4984#define MT_NRELS_HASH 64
4985#endif
4986 if (nrels >= MT_NRELS_HASH)
4987 {
4988 HASHCTL hash_ctl;
4989
4990 hash_ctl.keysize = sizeof(Oid);
4991 hash_ctl.entrysize = sizeof(MTTargetRelLookup);
4992 hash_ctl.hcxt = CurrentMemoryContext;
4993 mtstate->mt_resultOidHash =
4994 hash_create("ModifyTable target hash",
4995 nrels, &hash_ctl,
4997 for (i = 0; i < nrels; i++)
4998 {
4999 Oid hashkey;
5000 MTTargetRelLookup *mtlookup;
5001 bool found;
5002
5003 resultRelInfo = &mtstate->resultRelInfo[i];
5004 hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
5005 mtlookup = (MTTargetRelLookup *)
5006 hash_search(mtstate->mt_resultOidHash, &hashkey,
5007 HASH_ENTER, &found);
5008 Assert(!found);
5009 mtlookup->relationIndex = i;
5010 }
5011 }
5012 else
5013 mtstate->mt_resultOidHash = NULL;
5014
5015 /*
5016 * Determine if the FDW supports batch insert and determine the batch size
5017 * (a FDW may support batching, but it may be disabled for the
5018 * server/table).
5019 *
5020 * We only do this for INSERT, so that for UPDATE/DELETE the batch size
5021 * remains set to 0.
5022 */
5023 if (operation == CMD_INSERT)
5024 {
5025 /* insert may only have one relation, inheritance is not expanded */
5026 Assert(total_nrels == 1);
5027 resultRelInfo = mtstate->resultRelInfo;
5028 if (!resultRelInfo->ri_usesFdwDirectModify &&
5029 resultRelInfo->ri_FdwRoutine != NULL &&
5030 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
5031 resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
5032 {
5033 resultRelInfo->ri_BatchSize =
5034 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
5035 Assert(resultRelInfo->ri_BatchSize >= 1);
5036 }
5037 else
5038 resultRelInfo->ri_BatchSize = 1;
5039 }
5040
5041 /*
5042 * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
5043 * to estate->es_auxmodifytables so that it will be run to completion by
5044 * ExecPostprocessPlan. (It'd actually work fine to add the primary
5045 * ModifyTable node too, but there's no need.) Note the use of lcons not
5046 * lappend: we need later-initialized ModifyTable nodes to be shut down
5047 * before earlier ones. This ensures that we don't throw away RETURNING
5048 * rows that need to be seen by a later CTE subplan.
5049 */
5050 if (!mtstate->canSetTag)
5051 estate->es_auxmodifytables = lcons(mtstate,
5052 estate->es_auxmodifytables);
5053
5054 return mtstate;
5055}
#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:585
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:956
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2632
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2655
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, List *mergeActions)
Definition: execMain.c:1152
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam, List *resultRelations)
Definition: execMain.c:2794
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2836
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1944
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1988
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:881
#define EXEC_FLAG_BACKWARD
Definition: executor.h:69
#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:159
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
struct MTTargetRelLookup MTTargetRelLookup
#define MT_NRELS_HASH
static void ExecInitMerge(ModifyTableState *mtstate, EState *estate)
@ ONCONFLICT_NONE
Definition: nodes.h:424
CmdType
Definition: nodes.h:269
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define lfirst_int(lc)
Definition: pg_list.h:173
#define linitial_int(l)
Definition: pg_list.h:179
#define linitial(l)
Definition: pg_list.h:178
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
Bitmapset * es_unpruned_relids
Definition: execnodes.h:671
List * es_auxmodifytables
Definition: execnodes.h:726
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:231
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:234
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
TupleTableSlot * mt_merge_pending_not_matched
Definition: execnodes.h:1452
double mt_merge_deleted
Definition: execnodes.h:1457
List * mt_updateColnosLists
Definition: execnodes.h:1464
double mt_merge_inserted
Definition: execnodes.h:1455
double mt_merge_updated
Definition: execnodes.h:1456
HTAB * mt_resultOidHash
Definition: execnodes.h:1424
List * updateColnosLists
Definition: plannodes.h:304
List * arbiterIndexes
Definition: plannodes.h:324
List * onConflictCols
Definition: plannodes.h:328
List * mergeJoinConditions
Definition: plannodes.h:338
CmdType operation
Definition: plannodes.h:292
int epqParam
Definition: plannodes.h:320
List * resultRelations
Definition: plannodes.h:302
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:316
List * onConflictSet
Definition: plannodes.h:326
List * mergeActionLists
Definition: plannodes.h:336
bool canSetTag
Definition: plannodes.h:294
List * fdwPrivLists
Definition: plannodes.h:314
List * returningLists
Definition: plannodes.h:312
List * withCheckOptionLists
Definition: plannodes.h:306
Index rootRelation
Definition: plannodes.h:298
Node * onConflictWhere
Definition: plannodes.h:330
List * rowMarks
Definition: plannodes.h:318
OnConflictAction onConflictAction
Definition: plannodes.h:322
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:429
TupleTableSlot * oc_Existing
Definition: execnodes.h:428
ExprState * oc_WhereClause
Definition: execnodes.h:431
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:430
bool isParent
Definition: plannodes.h:1554
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1197
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1165
List * targetlist
Definition: plannodes.h:209
TupleDesc rd_att
Definition: rel.h:112
OnConflictSetState * ri_onConflict
Definition: execnodes.h:578
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:575
List * ri_WithCheckOptionExprs
Definition: execnodes.h:547
List * ri_returningList
Definition: execnodes.h:569
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:490
int ri_BatchSize
Definition: execnodes.h:539

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, HASHCTL::entrysize, 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, ExecAssignExprContext(), ExecBuildAuxRowMark(), ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecFindJunkAttributeInTlist(), ExecFindRowMark(), FdwRoutine::ExecForeignBatchInsert, ExecInitMerge(), ExecInitNode(), ExecInitQual(), ExecInitResultRelation(), ExecInitResultTupleSlotTL(), ExecInitResultTypeTL(), ExecModifyTable(), PlanState::ExecProcNode, ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, FdwRoutine::GetForeignModifyBatchSize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_ENTER, hash_search(), HASHCTL::hcxt, i, InvalidOid, PlanRowMark::isParent, HASHCTL::keysize, lappend(), lappend_int(), lcons(), lfirst, lfirst_int, lfirst_node, linitial, 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, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTableState::operation, ModifyTable::operation, outerPlan, outerPlanState, palloc(), PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, MTTargetRelLookup::relationIndex, 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, 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 709 of file nodeModifyTable.c.

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

References Assert(), EState::es_tupleTable, ExecAssignExprContext(), ExecBuildUpdateProjection(), 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, and table_slot_create().

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

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

References Assert(), TupleConversionMap::attrMap, CHECK_FOR_INTERRUPTS, CMD_INSERT, CMD_MERGE, CMD_UPDATE, MergeAction::commandType, TupleDescData::constr, ModifyTableContext::cpDeletedSlot, CreateTupleDescCopy(), 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(), ExecOnConflictUpdate(), ExecOpenIndices(), ExecPartitionCheck(), ExecPendingInserts(), ExecPrepareTupleRouting(), ExecProcessReturning(), execute_attr_map_slot(), ExecWithCheckOptions(), GetCurrentTransactionId(), TupleConstr::has_generated_stored, if(), 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_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, palloc(), PlanState::plan, ModifyTableContext::planSlot, ModifyTableState::ps, RelationData::rd_att, RelationData::rd_rel, 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 4440 of file nodeModifyTable.c.

4442{
4443 if (node->mt_resultOidHash)
4444 {
4445 /* Use the pre-built hash table to locate the rel */
4446 MTTargetRelLookup *mtlookup;
4447
4448 mtlookup = (MTTargetRelLookup *)
4449 hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
4450 if (mtlookup)
4451 {
4452 if (update_cache)
4453 {
4454 node->mt_lastResultOid = resultoid;
4455 node->mt_lastResultIndex = mtlookup->relationIndex;
4456 }
4457 return node->resultRelInfo + mtlookup->relationIndex;
4458 }
4459 }
4460 else
4461 {
4462 /* With few target rels, just search the ResultRelInfo array */
4463 for (int ndx = 0; ndx < node->mt_nrels; ndx++)
4464 {
4465 ResultRelInfo *rInfo = node->resultRelInfo + ndx;
4466
4467 if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
4468 {
4469 if (update_cache)
4470 {
4471 node->mt_lastResultOid = resultoid;
4472 node->mt_lastResultIndex = ndx;
4473 }
4474 return rInfo;
4475 }
4476 }
4477 }
4478
4479 if (!missing_ok)
4480 elog(ERROR, "incorrect result relation OID %u", resultoid);
4481 return NULL;
4482}
@ HASH_FIND
Definition: hsearch.h:113

References elog, ERROR, HASH_FIND, hash_search(), ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_nrels, ModifyTableState::mt_resultOidHash, RelationGetRelid, MTTargetRelLookup::relationIndex, ModifyTableState::resultRelInfo, and ResultRelInfo::ri_RelationDesc.

Referenced by ExecFindPartition(), and ExecModifyTable().

◆ ExecMerge()

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

Definition at line 2937 of file nodeModifyTable.c.

2939{
2940 TupleTableSlot *rslot = NULL;
2941 bool matched;
2942
2943 /*-----
2944 * If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
2945 * valid, depending on whether the result relation is a table or a view.
2946 * We execute the first action for which the additional WHEN MATCHED AND
2947 * quals pass. If an action without quals is found, that action is
2948 * executed.
2949 *
2950 * Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
2951 * is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
2952 * in sequence until one passes. This is almost identical to the WHEN
2953 * MATCHED case, and both cases are handled by ExecMergeMatched().
2954 *
2955 * Finally, in the WHEN NOT MATCHED [BY TARGET] case, both tupleid and
2956 * oldtuple are invalid, and we look at the given WHEN NOT MATCHED [BY
2957 * TARGET] actions in sequence until one passes.
2958 *
2959 * Things get interesting in case of concurrent update/delete of the
2960 * target tuple. Such concurrent update/delete is detected while we are
2961 * executing a WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action.
2962 *
2963 * A concurrent update can:
2964 *
2965 * 1. modify the target tuple so that the results from checking any
2966 * additional quals attached to WHEN MATCHED or WHEN NOT MATCHED BY
2967 * SOURCE actions potentially change, but the result from the join
2968 * quals does not change.
2969 *
2970 * In this case, we are still dealing with the same kind of match
2971 * (MATCHED or NOT MATCHED BY SOURCE). We recheck the same list of
2972 * actions from the start and choose the first one that satisfies the
2973 * new target tuple.
2974 *
2975 * 2. modify the target tuple in the WHEN MATCHED case so that the join
2976 * quals no longer pass and hence the source and target tuples no
2977 * longer match.
2978 *
2979 * In this case, we are now dealing with a NOT MATCHED case, and we
2980 * process both WHEN NOT MATCHED BY SOURCE and WHEN NOT MATCHED [BY
2981 * TARGET] actions. First ExecMergeMatched() processes the list of
2982 * WHEN NOT MATCHED BY SOURCE actions in sequence until one passes,
2983 * then ExecMergeNotMatched() processes any WHEN NOT MATCHED [BY
2984 * TARGET] actions in sequence until one passes. Thus we may execute
2985 * two actions; one of each kind.
2986 *
2987 * Thus we support concurrent updates that turn MATCHED candidate rows
2988 * into NOT MATCHED rows. However, we do not attempt to support cases
2989 * that would turn NOT MATCHED rows into MATCHED rows, or which would
2990 * cause a target row to match a different source row.
2991 *
2992 * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED
2993 * [BY TARGET].
2994 *
2995 * ExecMergeMatched() takes care of following the update chain and
2996 * re-finding the qualifying WHEN MATCHED or WHEN NOT MATCHED BY SOURCE
2997 * action, as long as the target tuple still exists. If the target tuple
2998 * gets deleted or a concurrent update causes the join quals to fail, it
2999 * returns a matched status of false and we call ExecMergeNotMatched().
3000 * Given that ExecMergeMatched() always makes progress by following the
3001 * update chain and we never switch from ExecMergeNotMatched() to
3002 * ExecMergeMatched(), there is no risk of a livelock.
3003 */
3004 matched = tupleid != NULL || oldtuple != NULL;
3005 if (matched)
3006 rslot = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
3007 canSetTag, &matched);
3008
3009 /*
3010 * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
3011 * join, or a previously MATCHED tuple for which ExecMergeMatched() set
3012 * "matched" to false, indicating that it no longer matches).
3013 */
3014 if (!matched)
3015 {
3016 /*
3017 * If a concurrent update turned a MATCHED case into a NOT MATCHED
3018 * case, and we have both WHEN NOT MATCHED BY SOURCE and WHEN NOT
3019 * MATCHED [BY TARGET] actions, and there is a RETURNING clause,
3020 * ExecMergeMatched() may have already executed a WHEN NOT MATCHED BY
3021 * SOURCE action, and computed the row to return. If so, we cannot
3022 * execute a WHEN NOT MATCHED [BY TARGET] action now, so mark it as
3023 * pending (to be processed on the next call to ExecModifyTable()).
3024 * Otherwise, just process the action now.
3025 */
3026 if (rslot == NULL)
3027 rslot = ExecMergeNotMatched(context, resultRelInfo, canSetTag);
3028 else
3029 context->mtstate->mt_merge_pending_not_matched = context->planSlot;
3030 }
3031
3032 return rslot;
3033}
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(), 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 3063 of file nodeModifyTable.c.

3066{
3067 ModifyTableState *mtstate = context->mtstate;
3068 List **mergeActions = resultRelInfo->ri_MergeActions;
3069 ItemPointerData lockedtid;
3070 List *actionStates;
3071 TupleTableSlot *newslot = NULL;
3072 TupleTableSlot *rslot = NULL;
3073 EState *estate = context->estate;
3074 ExprContext *econtext = mtstate->ps.ps_ExprContext;
3075 bool isNull;
3076 EPQState *epqstate = &mtstate->mt_epqstate;
3077 ListCell *l;
3078
3079 /* Expect matched to be true on entry */
3080 Assert(*matched);
3081
3082 /*
3083 * If there are no WHEN MATCHED or WHEN NOT MATCHED BY SOURCE actions, we
3084 * are done.
3085 */
3086 if (mergeActions[MERGE_WHEN_MATCHED] == NIL &&
3087 mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] == NIL)
3088 return NULL;
3089
3090 /*
3091 * Make tuple and any needed join variables available to ExecQual and
3092 * ExecProject. The target's existing tuple is installed in the scantuple.
3093 * This target relation's slot is required only in the case of a MATCHED
3094 * or NOT MATCHED BY SOURCE tuple and UPDATE/DELETE actions.
3095 */
3096 econtext->ecxt_scantuple = resultRelInfo->ri_oldTupleSlot;
3097 econtext->ecxt_innertuple = context->planSlot;
3098 econtext->ecxt_outertuple = NULL;
3099
3100 /*
3101 * This routine is only invoked for matched target rows, so we should
3102 * either have the tupleid of the target row, or an old tuple from the
3103 * target wholerow junk attr.
3104 */
3105 Assert(tupleid != NULL || oldtuple != NULL);
3106 ItemPointerSetInvalid(&lockedtid);
3107 if (oldtuple != NULL)
3108 {
3109 Assert(!resultRelInfo->ri_needLockTagTuple);
3110 ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
3111 false);
3112 }
3113 else
3114 {
3115 if (resultRelInfo->ri_needLockTagTuple)
3116 {
3117 /*
3118 * This locks even for CMD_DELETE, for CMD_NOTHING, and for tuples
3119 * that don't match mas_whenqual. MERGE on system catalogs is a
3120 * minor use case, so don't bother optimizing those.
3121 */
3122 LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
3124 lockedtid = *tupleid;
3125 }
3127 tupleid,
3129 resultRelInfo->ri_oldTupleSlot))
3130 elog(ERROR, "failed to fetch the target tuple");
3131 }
3132
3133 /*
3134 * Test the join condition. If it's satisfied, perform a MATCHED action.
3135 * Otherwise, perform a NOT MATCHED BY SOURCE action.
3136 *
3137 * Note that this join condition will be NULL if there are no NOT MATCHED
3138 * BY SOURCE actions --- see transform_MERGE_to_join(). In that case, we
3139 * need only consider MATCHED actions here.
3140 */
3141 if (ExecQual(resultRelInfo->ri_MergeJoinCondition, econtext))
3142 actionStates = mergeActions[MERGE_WHEN_MATCHED];
3143 else
3144 actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
3145
3146lmerge_matched:
3147
3148 foreach(l, actionStates)
3149 {
3150 MergeActionState *relaction = (MergeActionState *) lfirst(l);
3151 CmdType commandType = relaction->mas_action->commandType;
3152 TM_Result result;
3153 UpdateContext updateCxt = {0};
3154
3155 /*
3156 * Test condition, if any.
3157 *
3158 * In the absence of any condition, we perform the action
3159 * unconditionally (no need to check separately since ExecQual() will
3160 * return true if there are no conditions to evaluate).
3161 */
3162 if (!ExecQual(relaction->mas_whenqual, econtext))
3163 continue;
3164
3165 /*
3166 * Check if the existing target tuple meets the USING checks of
3167 * UPDATE/DELETE RLS policies. If those checks fail, we throw an
3168 * error.
3169 *
3170 * The WITH CHECK quals for UPDATE RLS policies are applied in
3171 * ExecUpdateAct() and hence we need not do anything special to handle
3172 * them.
3173 *
3174 * NOTE: We must do this after WHEN quals are evaluated, so that we
3175 * check policies only when they matter.
3176 */
3177 if (resultRelInfo->ri_WithCheckOptions && commandType != CMD_NOTHING)
3178 {
3179 ExecWithCheckOptions(commandType == CMD_UPDATE ?
3181 resultRelInfo,
3182 resultRelInfo->ri_oldTupleSlot,
3183 context->mtstate->ps.state);
3184 }
3185
3186 /* Perform stated action */
3187 switch (commandType)
3188 {
3189 case CMD_UPDATE:
3190
3191 /*
3192 * Project the output tuple, and use that to update the table.
3193 * We don't need to filter out junk attributes, because the
3194 * UPDATE action's targetlist doesn't have any.
3195 */
3196 newslot = ExecProject(relaction->mas_proj);
3197
3198 mtstate->mt_merge_action = relaction;
3199 if (!ExecUpdatePrologue(context, resultRelInfo,
3200 tupleid, NULL, newslot, &result))
3201 {
3202 if (result == TM_Ok)
3203 goto out; /* "do nothing" */
3204
3205 break; /* concurrent update/delete */
3206 }
3207
3208 /* INSTEAD OF ROW UPDATE Triggers */
3209 if (resultRelInfo->ri_TrigDesc &&
3210 resultRelInfo->ri_TrigDesc->trig_update_instead_row)
3211 {
3212 if (!ExecIRUpdateTriggers(estate, resultRelInfo,
3213 oldtuple, newslot))
3214 goto out; /* "do nothing" */
3215 }
3216 else
3217 {
3218 /* checked ri_needLockTagTuple above */
3219 Assert(oldtuple == NULL);
3220
3221 result = ExecUpdateAct(context, resultRelInfo, tupleid,
3222 NULL, newslot, canSetTag,
3223 &updateCxt);
3224
3225 /*
3226 * As in ExecUpdate(), if ExecUpdateAct() reports that a
3227 * cross-partition update was done, then there's nothing
3228 * else for us to do --- the UPDATE has been turned into a
3229 * DELETE and an INSERT, and we must not perform any of
3230 * the usual post-update tasks. Also, the RETURNING tuple
3231 * (if any) has been projected, so we can just return
3232 * that.
3233 */
3234 if (updateCxt.crossPartUpdate)
3235 {
3236 mtstate->mt_merge_updated += 1;
3237 rslot = context->cpUpdateReturningSlot;
3238 goto out;
3239 }
3240 }
3241
3242 if (result == TM_Ok)
3243 {
3244 ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
3245 tupleid, NULL, newslot);
3246 mtstate->mt_merge_updated += 1;
3247 }
3248 break;
3249
3250 case CMD_DELETE:
3251 mtstate->mt_merge_action = relaction;
3252 if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
3253 NULL, NULL, &result))
3254 {
3255 if (result == TM_Ok)
3256 goto out; /* "do nothing" */
3257
3258 break; /* concurrent update/delete */
3259 }
3260
3261 /* INSTEAD OF ROW DELETE Triggers */
3262 if (resultRelInfo->ri_TrigDesc &&
3263 resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
3264 {
3265 if (!ExecIRDeleteTriggers(estate, resultRelInfo,
3266 oldtuple))
3267 goto out; /* "do nothing" */
3268 }
3269 else
3270 {
3271 /* checked ri_needLockTagTuple above */
3272 Assert(oldtuple == NULL);
3273
3274 result = ExecDeleteAct(context, resultRelInfo, tupleid,
3275 false);
3276 }
3277
3278 if (result == TM_Ok)
3279 {
3280 ExecDeleteEpilogue(context, resultRelInfo, tupleid, NULL,
3281 false);
3282 mtstate->mt_merge_deleted += 1;
3283 }
3284 break;
3285
3286 case CMD_NOTHING:
3287 /* Doing nothing is always OK */
3288 result = TM_Ok;
3289 break;
3290
3291 default:
3292 elog(ERROR, "unknown action in MERGE WHEN clause");
3293 }
3294
3295 switch (result)
3296 {
3297 case TM_Ok:
3298 /* all good; perform final actions */
3299 if (canSetTag && commandType != CMD_NOTHING)
3300 (estate->es_processed)++;
3301
3302 break;
3303
3304 case TM_SelfModified:
3305
3306 /*
3307 * The target tuple was already updated or deleted by the
3308 * current command, or by a later command in the current
3309 * transaction. The former case is explicitly disallowed by
3310 * the SQL standard for MERGE, which insists that the MERGE
3311 * join condition should not join a target row to more than
3312 * one source row.
3313 *
3314 * The latter case arises if the tuple is modified by a
3315 * command in a BEFORE trigger, or perhaps by a command in a
3316 * volatile function used in the query. In such situations we
3317 * should not ignore the MERGE action, but it is equally
3318 * unsafe to proceed. We don't want to discard the original
3319 * MERGE action while keeping the triggered actions based on
3320 * it; and it would be no better to allow the original MERGE
3321 * action while discarding the updates that it triggered. So
3322 * throwing an error is the only safe course.
3323 */
3324 if (context->tmfd.cmax != estate->es_output_cid)
3325 ereport(ERROR,
3326 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3327 errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3328 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3329
3331 ereport(ERROR,
3332 (errcode(ERRCODE_CARDINALITY_VIOLATION),
3333 /* translator: %s is a SQL command name */
3334 errmsg("%s command cannot affect row a second time",
3335 "MERGE"),
3336 errhint("Ensure that not more than one source row matches any one target row.")));
3337
3338 /* This shouldn't happen */
3339 elog(ERROR, "attempted to update or delete invisible tuple");
3340 break;
3341
3342 case TM_Deleted:
3344 ereport(ERROR,
3346 errmsg("could not serialize access due to concurrent delete")));
3347
3348 /*
3349 * If the tuple was already deleted, set matched to false to
3350 * let caller handle it under NOT MATCHED [BY TARGET] clauses.
3351 */
3352 *matched = false;
3353 goto out;
3354
3355 case TM_Updated:
3356 {
3357 bool was_matched;
3358 Relation resultRelationDesc;
3359 TupleTableSlot *epqslot,
3360 *inputslot;
3361 LockTupleMode lockmode;
3362
3363 /*
3364 * The target tuple was concurrently updated by some other
3365 * transaction. If we are currently processing a MATCHED
3366 * action, use EvalPlanQual() with the new version of the
3367 * tuple and recheck the join qual, to detect a change
3368 * from the MATCHED to the NOT MATCHED cases. If we are
3369 * already processing a NOT MATCHED BY SOURCE action, we
3370 * skip this (cannot switch from NOT MATCHED BY SOURCE to
3371 * MATCHED).
3372 */
3373 was_matched = relaction->mas_action->matchKind == MERGE_WHEN_MATCHED;
3374 resultRelationDesc = resultRelInfo->ri_RelationDesc;
3375 lockmode = ExecUpdateLockMode(estate, resultRelInfo);
3376
3377 if (was_matched)
3378 inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
3379 resultRelInfo->ri_RangeTableIndex);
3380 else
3381 inputslot = resultRelInfo->ri_oldTupleSlot;
3382
3383 result = table_tuple_lock(resultRelationDesc, tupleid,
3384 estate->es_snapshot,
3385 inputslot, estate->es_output_cid,
3386 lockmode, LockWaitBlock,
3388 &context->tmfd);
3389 switch (result)
3390 {
3391 case TM_Ok:
3392
3393 /*
3394 * If the tuple was updated and migrated to
3395 * another partition concurrently, the current
3396 * MERGE implementation can't follow. There's
3397 * probably a better way to handle this case, but
3398 * it'd require recognizing the relation to which
3399 * the tuple moved, and setting our current
3400 * resultRelInfo to that.
3401 */
3403 ereport(ERROR,
3405 errmsg("tuple to be merged was already moved to another partition due to concurrent update")));
3406
3407 /*
3408 * If this was a MATCHED case, use EvalPlanQual()
3409 * to recheck the join condition.
3410 */
3411 if (was_matched)
3412 {
3413 epqslot = EvalPlanQual(epqstate,
3414 resultRelationDesc,
3415 resultRelInfo->ri_RangeTableIndex,
3416 inputslot);
3417
3418 /*
3419 * If the subplan didn't return a tuple, then
3420 * we must be dealing with an inner join for
3421 * which the join condition no longer matches.
3422 * This can only happen if there are no NOT
3423 * MATCHED actions, and so there is nothing
3424 * more to do.
3425 */
3426 if (TupIsNull(epqslot))
3427 goto out;
3428
3429 /*
3430 * If we got a NULL ctid from the subplan, the
3431 * join quals no longer pass and we switch to
3432 * the NOT MATCHED BY SOURCE case.
3433 */
3434 (void) ExecGetJunkAttribute(epqslot,
3435 resultRelInfo->ri_RowIdAttNo,
3436 &isNull);
3437 if (isNull)
3438 *matched = false;
3439
3440 /*
3441 * Otherwise, recheck the join quals to see if
3442 * we need to switch to the NOT MATCHED BY
3443 * SOURCE case.
3444 */
3445 if (resultRelInfo->ri_needLockTagTuple)
3446 {
3447 if (ItemPointerIsValid(&lockedtid))
3448 UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3450 LockTuple(resultRelInfo->ri_RelationDesc, &context->tmfd.ctid,
3452 lockedtid = context->tmfd.ctid;
3453 }
3454 if (!table_tuple_fetch_row_version(resultRelationDesc,
3455 &context->tmfd.ctid,
3457 resultRelInfo->ri_oldTupleSlot))
3458 elog(ERROR, "failed to fetch the target tuple");
3459
3460 if (*matched)
3461 *matched = ExecQual(resultRelInfo->ri_MergeJoinCondition,
3462 econtext);
3463
3464 /* Switch lists, if necessary */
3465 if (!*matched)
3466 actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
3467 }
3468
3469 /*
3470 * Loop back and process the MATCHED or NOT
3471 * MATCHED BY SOURCE actions from the start.
3472 */
3473 goto lmerge_matched;
3474
3475 case TM_Deleted:
3476
3477 /*
3478 * tuple already deleted; tell caller to run NOT
3479 * MATCHED [BY TARGET] actions
3480 */
3481 *matched = false;
3482 goto out;
3483
3484 case TM_SelfModified:
3485
3486 /*
3487 * This can be reached when following an update
3488 * chain from a tuple updated by another session,
3489 * reaching a tuple that was already updated or
3490 * deleted by the current command, or by a later
3491 * command in the current transaction. As above,
3492 * this should always be treated as an error.
3493 */
3494 if (context->tmfd.cmax != estate->es_output_cid)
3495 ereport(ERROR,
3496 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3497 errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3498 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3499
3501 ereport(ERROR,
3502 (errcode(ERRCODE_CARDINALITY_VIOLATION),
3503 /* translator: %s is a SQL command name */
3504 errmsg("%s command cannot affect row a second time",
3505 "MERGE"),
3506 errhint("Ensure that not more than one source row matches any one target row.")));
3507
3508 /* This shouldn't happen */
3509 elog(ERROR, "attempted to update or delete invisible tuple");
3510 goto out;
3511
3512 default:
3513 /* see table_tuple_lock call in ExecDelete() */
3514 elog(ERROR, "unexpected table_tuple_lock status: %u",
3515 result);
3516 goto out;
3517 }
3518 }
3519
3520 case TM_Invisible:
3521 case TM_WouldBlock:
3522 case TM_BeingModified:
3523 /* these should not occur */
3524 elog(ERROR, "unexpected tuple operation result: %d", result);
3525 break;
3526 }
3527
3528 /* Process RETURNING if present */
3529 if (resultRelInfo->ri_projectReturning)
3530 {
3531 switch (commandType)
3532 {
3533 case CMD_UPDATE:
3534 rslot = ExecProcessReturning(context,
3535 resultRelInfo,
3536 CMD_UPDATE,
3537 resultRelInfo->ri_oldTupleSlot,
3538 newslot,
3539 context->planSlot);
3540 break;
3541
3542 case CMD_DELETE:
3543 rslot = ExecProcessReturning(context,
3544 resultRelInfo,
3545 CMD_DELETE,
3546 resultRelInfo->ri_oldTupleSlot,
3547 NULL,
3548 context->planSlot);
3549 break;
3550
3551 case CMD_NOTHING:
3552 break;
3553
3554 default:
3555 elog(ERROR, "unrecognized commandType: %d",
3556 (int) commandType);
3557 }
3558 }
3559
3560 /*
3561 * We've activated one of the WHEN clauses, so we don't search
3562 * further. This is required behaviour, not an optimization.
3563 */
3564 break;
3565 }
3566
3567 /*
3568 * Successfully executed an action or no qualifying action was found.
3569 */
3570out:
3571 if (ItemPointerIsValid(&lockedtid))
3572 UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3574 return rslot;
3575}
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2606
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:541
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: executor.h:222
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition: itemptr.h:197
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:562
void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:601
#define InplaceUpdateTupleLock
Definition: lockdefs.h:48
LockTupleMode
Definition: lockoptions.h:50
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
Definition: parsenodes.h:1377
@ WCO_RLS_MERGE_DELETE_CHECK
Definition: parsenodes.h:1378
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition: primnodes.h:2002
@ MERGE_WHEN_MATCHED
Definition: primnodes.h:2001
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:270
MergeMatchKind matchKind
Definition: primnodes.h:2011
bool ri_needLockTagTuple
Definition: execnodes.h:507
TransactionId xmax
Definition: tableam.h:144
ItemPointerData ctid
Definition: tableam.h:143
bool trig_update_instead_row
Definition: reltrigger.h:63
@ TM_BeingModified
Definition: tableam.h:99
@ TM_WouldBlock
Definition: tableam.h:102
@ TM_Invisible
Definition: tableam.h:80
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:3165

References Assert(), TM_FailureData::cmax, CMD_DELETE, CMD_NOTHING, CMD_UPDATE, MergeAction::commandType, ModifyTableContext::cpUpdateReturningSlot, UpdateContext::crossPartUpdate, TM_FailureData::ctid, 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(), InplaceUpdateTupleLock, IsolationUsesXactSnapshot, ItemPointerIndicatesMovedPartitions(), ItemPointerIsValid(), ItemPointerSetInvalid(), lfirst, LockTuple(), LockWaitBlock, MergeActionState::mas_action, MergeActionState::mas_proj, MergeActionState::mas_whenqual, MergeAction::matchKind, MERGE_WHEN_MATCHED, MERGE_WHEN_NOT_MATCHED_BY_SOURCE, ModifyTableState::mt_epqstate, ModifyTableState::mt_merge_action, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_updated, ModifyTableContext::mtstate, NIL, 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 3581 of file nodeModifyTable.c.

3583{
3584 ModifyTableState *mtstate = context->mtstate;
3585 ExprContext *econtext = mtstate->ps.ps_ExprContext;
3586 List *actionStates;
3587 TupleTableSlot *rslot = NULL;
3588 ListCell *l;
3589
3590 /*
3591 * For INSERT actions, the root relation's merge action is OK since the
3592 * INSERT's targetlist and the WHEN conditions can only refer to the
3593 * source relation and hence it does not matter which result relation we
3594 * work with.
3595 *
3596 * XXX does this mean that we can avoid creating copies of actionStates on
3597 * partitioned tables, for not-matched actions?
3598 */
3599 actionStates = resultRelInfo->ri_MergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET];
3600
3601 /*
3602 * Make source tuple available to ExecQual and ExecProject. We don't need
3603 * the target tuple, since the WHEN quals and targetlist can't refer to
3604 * the target columns.
3605 */
3606 econtext->ecxt_scantuple = NULL;
3607 econtext->ecxt_innertuple = context->planSlot;
3608 econtext->ecxt_outertuple = NULL;
3609
3610 foreach(l, actionStates)
3611 {
3613 CmdType commandType = action->mas_action->commandType;
3614 TupleTableSlot *newslot;
3615
3616 /*
3617 * Test condition, if any.
3618 *
3619 * In the absence of any condition, we perform the action
3620 * unconditionally (no need to check separately since ExecQual() will
3621 * return true if there are no conditions to evaluate).
3622 */
3623 if (!ExecQual(action->mas_whenqual, econtext))
3624 continue;
3625
3626 /* Perform stated action */
3627 switch (commandType)
3628 {
3629 case CMD_INSERT:
3630
3631 /*
3632 * Project the tuple. In case of a partitioned table, the
3633 * projection was already built to use the root's descriptor,
3634 * so we don't need to map the tuple here.
3635 */
3636 newslot = ExecProject(action->mas_proj);
3637 mtstate->mt_merge_action = action;
3638
3639 rslot = ExecInsert(context, mtstate->rootResultRelInfo,
3640 newslot, canSetTag, NULL, NULL);
3641 mtstate->mt_merge_inserted += 1;
3642 break;
3643 case CMD_NOTHING:
3644 /* Do nothing */
3645 break;
3646 default:
3647 elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
3648 }
3649
3650 /*
3651 * We've activated one of the WHEN clauses, so we don't search
3652 * further. This is required behaviour, not an optimization.
3653 */
3654 break;
3655 }
3656
3657 return rslot;
3658}
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition: primnodes.h:2003

References generate_unaccent_rules::action, CMD_INSERT, CMD_NOTHING, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ERROR, ExecInsert(), ExecProject(), ExecQual(), 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 4012 of file nodeModifyTable.c.

4013{
4015 ModifyTableContext context;
4016 EState *estate = node->ps.state;
4017 CmdType operation = node->operation;
4018 ResultRelInfo *resultRelInfo;
4019 PlanState *subplanstate;
4020 TupleTableSlot *slot;
4021 TupleTableSlot *oldSlot;
4022 ItemPointerData tuple_ctid;
4023 HeapTupleData oldtupdata;
4024 HeapTuple oldtuple;
4025 ItemPointer tupleid;
4026 bool tuplock;
4027
4029
4030 /*
4031 * This should NOT get called during EvalPlanQual; we should have passed a
4032 * subplan tree to EvalPlanQual, instead. Use a runtime test not just
4033 * Assert because this condition is easy to miss in testing. (Note:
4034 * although ModifyTable should not get executed within an EvalPlanQual
4035 * operation, we do have to allow it to be initialized and shut down in
4036 * case it is within a CTE subplan. Hence this test must be here, not in
4037 * ExecInitModifyTable.)
4038 */
4039 if (estate->es_epq_active != NULL)
4040 elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
4041
4042 /*
4043 * If we've already completed processing, don't try to do more. We need
4044 * this test because ExecPostprocessPlan might call us an extra time, and
4045 * our subplan's nodes aren't necessarily robust against being called
4046 * extra times.
4047 */
4048 if (node->mt_done)
4049 return NULL;
4050
4051 /*
4052 * On first call, fire BEFORE STATEMENT triggers before proceeding.
4053 */
4054 if (node->fireBSTriggers)
4055 {
4056 fireBSTriggers(node);
4057 node->fireBSTriggers = false;
4058 }
4059
4060 /* Preload local variables */
4061 resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
4062 subplanstate = outerPlanState(node);
4063
4064 /* Set global context */
4065 context.mtstate = node;
4066 context.epqstate = &node->mt_epqstate;
4067 context.estate = estate;
4068
4069 /*
4070 * Fetch rows from subplan, and execute the required table modification
4071 * for each row.
4072 */
4073 for (;;)
4074 {
4075 /*
4076 * Reset the per-output-tuple exprcontext. This is needed because
4077 * triggers expect to use that context as workspace. It's a bit ugly
4078 * to do this below the top level of the plan, however. We might need
4079 * to rethink this later.
4080 */
4082
4083 /*
4084 * Reset per-tuple memory context used for processing on conflict and
4085 * returning clauses, to free any expression evaluation storage
4086 * allocated in the previous cycle.
4087 */
4088 if (pstate->ps_ExprContext)
4090
4091 /*
4092 * If there is a pending MERGE ... WHEN NOT MATCHED [BY TARGET] action
4093 * to execute, do so now --- see the comments in ExecMerge().
4094 */
4095 if (node->mt_merge_pending_not_matched != NULL)
4096 {
4097 context.planSlot = node->mt_merge_pending_not_matched;
4098 context.cpDeletedSlot = NULL;
4099
4100 slot = ExecMergeNotMatched(&context, node->resultRelInfo,
4101 node->canSetTag);
4102
4103 /* Clear the pending action */
4104 node->mt_merge_pending_not_matched = NULL;
4105
4106 /*
4107 * If we got a RETURNING result, return it to the caller. We'll
4108 * continue the work on next call.
4109 */
4110 if (slot)
4111 return slot;
4112
4113 continue; /* continue with the next tuple */
4114 }
4115
4116 /* Fetch the next row from subplan */
4117 context.planSlot = ExecProcNode(subplanstate);
4118 context.cpDeletedSlot = NULL;
4119
4120 /* No more tuples to process? */
4121 if (TupIsNull(context.planSlot))
4122 break;
4123
4124 /*
4125 * When there are multiple result relations, each tuple contains a
4126 * junk column that gives the OID of the rel from which it came.
4127 * Extract it and select the correct result relation.
4128 */
4130 {
4131 Datum datum;
4132 bool isNull;
4133 Oid resultoid;
4134
4135 datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
4136 &isNull);
4137 if (isNull)
4138 {
4139 /*
4140 * For commands other than MERGE, any tuples having InvalidOid
4141 * for tableoid are errors. For MERGE, we may need to handle
4142 * them as WHEN NOT MATCHED clauses if any, so do that.
4143 *
4144 * Note that we use the node's toplevel resultRelInfo, not any
4145 * specific partition's.
4146 */
4147 if (operation == CMD_MERGE)
4148 {
4149 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4150
4151 slot = ExecMerge(&context, node->resultRelInfo,
4152 NULL, NULL, node->canSetTag);
4153
4154 /*
4155 * If we got a RETURNING result, return it to the caller.
4156 * We'll continue the work on next call.
4157 */
4158 if (slot)
4159 return slot;
4160
4161 continue; /* continue with the next tuple */
4162 }
4163
4164 elog(ERROR, "tableoid is NULL");
4165 }
4166 resultoid = DatumGetObjectId(datum);
4167
4168 /* If it's not the same as last time, we need to locate the rel */
4169 if (resultoid != node->mt_lastResultOid)
4170 resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
4171 false, true);
4172 }
4173
4174 /*
4175 * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
4176 * here is compute the RETURNING expressions.
4177 */
4178 if (resultRelInfo->ri_usesFdwDirectModify)
4179 {
4180 Assert(resultRelInfo->ri_projectReturning);
4181
4182 /*
4183 * A scan slot containing the data that was actually inserted,
4184 * updated or deleted has already been made available to
4185 * ExecProcessReturning by IterateDirectModify, so no need to
4186 * provide it here. The individual old and new slots are not
4187 * needed, since direct-modify is disabled if the RETURNING list
4188 * refers to OLD/NEW values.
4189 */
4190 Assert((resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD) == 0 &&
4191 (resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW) == 0);
4192
4193 slot = ExecProcessReturning(&context, resultRelInfo, operation,
4194 NULL, NULL, context.planSlot);
4195
4196 return slot;
4197 }
4198
4199 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4200 slot = context.planSlot;
4201
4202 tupleid = NULL;
4203 oldtuple = NULL;
4204
4205 /*
4206 * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
4207 * to be updated/deleted/merged. For a heap relation, that's a TID;
4208 * otherwise we may have a wholerow junk attr that carries the old
4209 * tuple in toto. Keep this in step with the part of
4210 * ExecInitModifyTable that sets up ri_RowIdAttNo.
4211 */
4212 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4213 operation == CMD_MERGE)
4214 {
4215 char relkind;
4216 Datum datum;
4217 bool isNull;
4218
4219 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4220 if (relkind == RELKIND_RELATION ||
4221 relkind == RELKIND_MATVIEW ||
4222 relkind == RELKIND_PARTITIONED_TABLE)
4223 {
4224 /* ri_RowIdAttNo refers to a ctid attribute */
4226 datum = ExecGetJunkAttribute(slot,
4227 resultRelInfo->ri_RowIdAttNo,
4228 &isNull);
4229
4230 /*
4231 * For commands other than MERGE, any tuples having a null row
4232 * identifier are errors. For MERGE, we may need to handle
4233 * them as WHEN NOT MATCHED clauses if any, so do that.
4234 *
4235 * Note that we use the node's toplevel resultRelInfo, not any
4236 * specific partition's.
4237 */
4238 if (isNull)
4239 {
4240 if (operation == CMD_MERGE)
4241 {
4242 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4243
4244 slot = ExecMerge(&context, node->resultRelInfo,
4245 NULL, NULL, node->canSetTag);
4246
4247 /*
4248 * If we got a RETURNING result, return it to the
4249 * caller. We'll continue the work on next call.
4250 */
4251 if (slot)
4252 return slot;
4253
4254 continue; /* continue with the next tuple */
4255 }
4256
4257 elog(ERROR, "ctid is NULL");
4258 }
4259
4260 tupleid = (ItemPointer) DatumGetPointer(datum);
4261 tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
4262 tupleid = &tuple_ctid;
4263 }
4264
4265 /*
4266 * Use the wholerow attribute, when available, to reconstruct the
4267 * old relation tuple. The old tuple serves one or both of two
4268 * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
4269 * provides values for any unchanged columns for the NEW tuple of
4270 * an UPDATE, because the subplan does not produce all the columns
4271 * of the target table.
4272 *
4273 * Note that the wholerow attribute does not carry system columns,
4274 * so foreign table triggers miss seeing those, except that we
4275 * know enough here to set t_tableOid. Quite separately from
4276 * this, the FDW may fetch its own junk attrs to identify the row.
4277 *
4278 * Other relevant relkinds, currently limited to views, always
4279 * have a wholerow attribute.
4280 */
4281 else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4282 {
4283 datum = ExecGetJunkAttribute(slot,
4284 resultRelInfo->ri_RowIdAttNo,
4285 &isNull);
4286
4287 /*
4288 * For commands other than MERGE, any tuples having a null row
4289 * identifier are errors. For MERGE, we may need to handle
4290 * them as WHEN NOT MATCHED clauses if any, so do that.
4291 *
4292 * Note that we use the node's toplevel resultRelInfo, not any
4293 * specific partition's.
4294 */
4295 if (isNull)
4296 {
4297 if (operation == CMD_MERGE)
4298 {
4299 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4300
4301 slot = ExecMerge(&context, node->resultRelInfo,
4302 NULL, NULL, node->canSetTag);
4303
4304 /*
4305 * If we got a RETURNING result, return it to the
4306 * caller. We'll continue the work on next call.
4307 */
4308 if (slot)
4309 return slot;
4310
4311 continue; /* continue with the next tuple */
4312 }
4313
4314 elog(ERROR, "wholerow is NULL");
4315 }
4316
4317 oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
4318 oldtupdata.t_len =
4320 ItemPointerSetInvalid(&(oldtupdata.t_self));
4321 /* Historically, view triggers see invalid t_tableOid. */
4322 oldtupdata.t_tableOid =
4323 (relkind == RELKIND_VIEW) ? InvalidOid :
4324 RelationGetRelid(resultRelInfo->ri_RelationDesc);
4325
4326 oldtuple = &oldtupdata;
4327 }
4328 else
4329 {
4330 /* Only foreign tables are allowed to omit a row-ID attr */
4331 Assert(relkind == RELKIND_FOREIGN_TABLE);
4332 }
4333 }
4334
4335 switch (operation)
4336 {
4337 case CMD_INSERT:
4338 /* Initialize projection info if first time for this table */
4339 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4340 ExecInitInsertProjection(node, resultRelInfo);
4341 slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
4342 slot = ExecInsert(&context, resultRelInfo, slot,
4343 node->canSetTag, NULL, NULL);
4344 break;
4345
4346 case CMD_UPDATE:
4347 tuplock = false;
4348
4349 /* Initialize projection info if first time for this table */
4350 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4351 ExecInitUpdateProjection(node, resultRelInfo);
4352
4353 /*
4354 * Make the new tuple by combining plan's output tuple with
4355 * the old tuple being updated.
4356 */
4357 oldSlot = resultRelInfo->ri_oldTupleSlot;
4358 if (oldtuple != NULL)
4359 {
4360 Assert(!resultRelInfo->ri_needLockTagTuple);
4361 /* Use the wholerow junk attr as the old tuple. */
4362 ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
4363 }
4364 else
4365 {
4366 /* Fetch the most recent version of old tuple. */
4367 Relation relation = resultRelInfo->ri_RelationDesc;
4368
4369 if (resultRelInfo->ri_needLockTagTuple)
4370 {
4371 LockTuple(relation, tupleid, InplaceUpdateTupleLock);
4372 tuplock = true;
4373 }
4374 if (!table_tuple_fetch_row_version(relation, tupleid,
4376 oldSlot))
4377 elog(ERROR, "failed to fetch tuple being updated");
4378 }
4379 slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
4380 oldSlot);
4381
4382 /* Now apply the update. */
4383 slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
4384 oldSlot, slot, node->canSetTag);
4385 if (tuplock)
4386 UnlockTuple(resultRelInfo->ri_RelationDesc, tupleid,
4388 break;
4389
4390 case CMD_DELETE:
4391 slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
4392 true, false, node->canSetTag, NULL, NULL, NULL);
4393 break;
4394
4395 case CMD_MERGE:
4396 slot = ExecMerge(&context, resultRelInfo, tupleid, oldtuple,
4397 node->canSetTag);
4398 break;
4399
4400 default:
4401 elog(ERROR, "unknown operation");
4402 break;
4403 }
4404
4405 /*
4406 * If we got a RETURNING result, return it to caller. We'll continue
4407 * the work on next call.
4408 */
4409 if (slot)
4410 return slot;
4411 }
4412
4413 /*
4414 * Insert remaining tuples for batch insert.
4415 */
4417 ExecPendingInserts(estate);
4418
4419 /*
4420 * We're done, but fire AFTER STATEMENT triggers before exiting.
4421 */
4422 fireASTriggers(node);
4423
4424 node->mt_done = true;
4425
4426 return NULL;
4427}
#define EEO_FLAG_HAS_NEW
Definition: execnodes.h:81
#define ResetPerTupleExprContext(estate)
Definition: executor.h:687
#define ResetExprContext(econtext)
Definition: executor.h:672
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:336
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:287
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
static uint32 HeapTupleHeaderGetDatumLength(const HeapTupleHeaderData *tup)
Definition: htup_details.h:492
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)
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:247
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
struct EPQState * es_epq_active
Definition: execnodes.h:741
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66

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(), 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, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, table_tuple_fetch_row_version(), TupIsNull, unlikely, and UnlockTuple().

Referenced by ExecInitModifyTable().

◆ ExecOnConflictUpdate()

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

Definition at line 2710 of file nodeModifyTable.c.

2716{
2717 ModifyTableState *mtstate = context->mtstate;
2718 ExprContext *econtext = mtstate->ps.ps_ExprContext;
2719 Relation relation = resultRelInfo->ri_RelationDesc;
2720 ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
2721 TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
2722 TM_FailureData tmfd;
2723 LockTupleMode lockmode;
2725 Datum xminDatum;
2726 TransactionId xmin;
2727 bool isnull;
2728
2729 /*
2730 * Parse analysis should have blocked ON CONFLICT for all system
2731 * relations, which includes these. There's no fundamental obstacle to
2732 * supporting this; we'd just need to handle LOCKTAG_TUPLE like the other
2733 * ExecUpdate() caller.
2734 */
2735 Assert(!resultRelInfo->ri_needLockTagTuple);
2736
2737 /* Determine lock mode to use */
2738 lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
2739
2740 /*
2741 * Lock tuple for update. Don't follow updates when tuple cannot be
2742 * locked without doing so. A row locking conflict here means our
2743 * previous conclusion that the tuple is conclusively committed is not
2744 * true anymore.
2745 */
2746 test = table_tuple_lock(relation, conflictTid,
2747 context->estate->es_snapshot,
2748 existing, context->estate->es_output_cid,
2749 lockmode, LockWaitBlock, 0,
2750 &tmfd);
2751 switch (test)
2752 {
2753 case TM_Ok:
2754 /* success! */
2755 break;
2756
2757 case TM_Invisible:
2758
2759 /*
2760 * This can occur when a just inserted tuple is updated again in
2761 * the same command. E.g. because multiple rows with the same
2762 * conflicting key values are inserted.
2763 *
2764 * This is somewhat similar to the ExecUpdate() TM_SelfModified
2765 * case. We do not want to proceed because it would lead to the
2766 * same row being updated a second time in some unspecified order,
2767 * and in contrast to plain UPDATEs there's no historical behavior
2768 * to break.
2769 *
2770 * It is the user's responsibility to prevent this situation from
2771 * occurring. These problems are why the SQL standard similarly
2772 * specifies that for SQL MERGE, an exception must be raised in
2773 * the event of an attempt to update the same row twice.
2774 */
2775 xminDatum = slot_getsysattr(existing,
2777 &isnull);
2778 Assert(!isnull);
2779 xmin = DatumGetTransactionId(xminDatum);
2780
2782 ereport(ERROR,
2783 (errcode(ERRCODE_CARDINALITY_VIOLATION),
2784 /* translator: %s is a SQL command name */
2785 errmsg("%s command cannot affect row a second time",
2786 "ON CONFLICT DO UPDATE"),
2787 errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
2788
2789 /* This shouldn't happen */
2790 elog(ERROR, "attempted to lock invisible tuple");
2791 break;
2792
2793 case TM_SelfModified:
2794
2795 /*
2796 * This state should never be reached. As a dirty snapshot is used
2797 * to find conflicting tuples, speculative insertion wouldn't have
2798 * seen this row to conflict with.
2799 */
2800 elog(ERROR, "unexpected self-updated tuple");
2801 break;
2802
2803 case TM_Updated:
2805 ereport(ERROR,
2807 errmsg("could not serialize access due to concurrent update")));
2808
2809 /*
2810 * As long as we don't support an UPDATE of INSERT ON CONFLICT for
2811 * a partitioned table we shouldn't reach to a case where tuple to
2812 * be lock is moved to another partition due to concurrent update
2813 * of the partition key.
2814 */
2816
2817 /*
2818 * Tell caller to try again from the very start.
2819 *
2820 * It does not make sense to use the usual EvalPlanQual() style
2821 * loop here, as the new version of the row might not conflict
2822 * anymore, or the conflicting tuple has actually been deleted.
2823 */
2824 ExecClearTuple(existing);
2825 return false;
2826
2827 case TM_Deleted:
2829 ereport(ERROR,
2831 errmsg("could not serialize access due to concurrent delete")));
2832
2833 /* see TM_Updated case */
2835 ExecClearTuple(existing);
2836 return false;
2837
2838 default:
2839 elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
2840 }
2841
2842 /* Success, the tuple is locked. */
2843
2844 /*
2845 * Verify that the tuple is visible to our MVCC snapshot if the current
2846 * isolation level mandates that.
2847 *
2848 * It's not sufficient to rely on the check within ExecUpdate() as e.g.
2849 * CONFLICT ... WHERE clause may prevent us from reaching that.
2850 *
2851 * This means we only ever continue when a new command in the current
2852 * transaction could see the row, even though in READ COMMITTED mode the
2853 * tuple will not be visible according to the current statement's
2854 * snapshot. This is in line with the way UPDATE deals with newer tuple
2855 * versions.
2856 */
2857 ExecCheckTupleVisible(context->estate, relation, existing);
2858
2859 /*
2860 * Make tuple and any needed join variables available to ExecQual and
2861 * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
2862 * the target's existing tuple is installed in the scantuple. EXCLUDED
2863 * has been made to reference INNER_VAR in setrefs.c, but there is no
2864 * other redirection.
2865 */
2866 econtext->ecxt_scantuple = existing;
2867 econtext->ecxt_innertuple = excludedSlot;
2868 econtext->ecxt_outertuple = NULL;
2869
2870 if (!ExecQual(onConflictSetWhere, econtext))
2871 {
2872 ExecClearTuple(existing); /* see return below */
2873 InstrCountFiltered1(&mtstate->ps, 1);
2874 return true; /* done with the tuple */
2875 }
2876
2877 if (resultRelInfo->ri_WithCheckOptions != NIL)
2878 {
2879 /*
2880 * Check target's existing tuple against UPDATE-applicable USING
2881 * security barrier quals (if any), enforced here as RLS checks/WCOs.
2882 *
2883 * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
2884 * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
2885 * but that's almost the extent of its special handling for ON
2886 * CONFLICT DO UPDATE.
2887 *
2888 * The rewriter will also have associated UPDATE applicable straight
2889 * RLS checks/WCOs for the benefit of the ExecUpdate() call that
2890 * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
2891 * kinds, so there is no danger of spurious over-enforcement in the
2892 * INSERT or UPDATE path.
2893 */
2895 existing,
2896 mtstate->ps.state);
2897 }
2898
2899 /* Project the new tuple version */
2900 ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
2901
2902 /*
2903 * Note that it is possible that the target tuple has been modified in
2904 * this session, after the above table_tuple_lock. We choose to not error
2905 * out in that case, in line with ExecUpdate's treatment of similar cases.
2906 * This can happen if an UPDATE is triggered from within ExecQual(),
2907 * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
2908 * wCTE in the ON CONFLICT's SET.
2909 */
2910
2911 /* Execute UPDATE with projection */
2912 *returning = ExecUpdate(context, resultRelInfo,
2913 conflictTid, NULL, existing,
2914 resultRelInfo->ri_onConflict->oc_ProjSlot,
2915 canSetTag);
2916
2917 /*
2918 * Clear out existing tuple, as there might not be another conflict among
2919 * the next input rows. Don't want to hold resources till the end of the
2920 * query. First though, make sure that the returning slot, if any, has a
2921 * local copy of any OLD pass-by-reference values, if it refers to any OLD
2922 * columns.
2923 */
2924 if (*returning != NULL &&
2926 ExecMaterializeSlot(*returning);
2927
2928 ExecClearTuple(existing);
2929
2930 return true;
2931}
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1263
@ WCO_RLS_CONFLICT_CHECK
Definition: parsenodes.h:1376
static void test(void)

References Assert(), TM_FailureData::ctid, DatumGetTransactionId(), ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, EEO_FLAG_HAS_OLD, elog, ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errhint(), errmsg(), ERROR, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, ExecCheckTupleVisible(), ExecClearTuple(), ExecMaterializeSlot(), ExecProject(), ExecQual(), ExecUpdate(), ExecUpdateLockMode(), ExecWithCheckOptions(), ExprState::flags, InstrCountFiltered1, IsolationUsesXactSnapshot, ItemPointerIndicatesMovedPartitions(), LockWaitBlock, MinTransactionIdAttributeNumber, ModifyTableContext::mtstate, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::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, slot_getsysattr(), PlanState::state, table_tuple_lock(), test(), TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TransactionIdIsCurrentTransactionId(), and WCO_RLS_CONFLICT_CHECK.

Referenced by ExecInsert().

◆ ExecPendingInserts()

static void ExecPendingInserts ( EState estate)
static

Definition at line 1426 of file nodeModifyTable.c.

1427{
1428 ListCell *l1,
1429 *l2;
1430
1433 {
1434 ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l1);
1435 ModifyTableState *mtstate = (ModifyTableState *) lfirst(l2);
1436
1437 Assert(mtstate);
1438 ExecBatchInsert(mtstate, resultRelInfo,
1439 resultRelInfo->ri_Slots,
1440 resultRelInfo->ri_PlanSlots,
1441 resultRelInfo->ri_NumSlots,
1442 estate, mtstate->canSetTag);
1443 }
1444
1449}
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518

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

3958{
3959 ResultRelInfo *partrel;
3960 TupleConversionMap *map;
3961
3962 /*
3963 * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
3964 * not find a valid partition for the tuple in 'slot' then an error is
3965 * raised. An error may also be raised if the found partition is not a
3966 * valid target for INSERTs. This is required since a partitioned table
3967 * UPDATE to another partition becomes a DELETE+INSERT.
3968 */
3969 partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
3970
3971 /*
3972 * If we're capturing transition tuples, we might need to convert from the
3973 * partition rowtype to root partitioned table's rowtype. But if there
3974 * are no BEFORE triggers on the partition that could change the tuple, we
3975 * can just remember the original unconverted tuple to avoid a needless
3976 * round trip conversion.
3977 */
3978 if (mtstate->mt_transition_capture != NULL)
3979 {
3980 bool has_before_insert_row_trig;
3981
3982 has_before_insert_row_trig = (partrel->ri_TrigDesc &&
3984
3986 !has_before_insert_row_trig ? slot : NULL;
3987 }
3988
3989 /*
3990 * Convert the tuple, if necessary.
3991 */
3992 map = ExecGetRootToChildMap(partrel, estate);
3993 if (map != NULL)
3994 {
3995 TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
3996
3997 slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
3998 }
3999
4000 *partRelInfo = partrel;
4001 return slot;
4002}
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:616

References TupleConversionMap::attrMap, ExecFindPartition(), ExecGetRootToChildMap(), execute_attr_map_slot(), 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,
CmdType  cmdType,
TupleTableSlot oldSlot,
TupleTableSlot newSlot,
TupleTableSlot planSlot 
)
static

Definition at line 287 of file nodeModifyTable.c.

293{
294 EState *estate = context->estate;
295 ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
296 ExprContext *econtext = projectReturning->pi_exprContext;
297
298 /* Make tuple and any needed join variables available to ExecProject */
299 switch (cmdType)
300 {
301 case CMD_INSERT:
302 case CMD_UPDATE:
303 /* return new tuple by default */
304 if (newSlot)
305 econtext->ecxt_scantuple = newSlot;
306 break;
307
308 case CMD_DELETE:
309 /* return old tuple by default */
310 if (oldSlot)
311 econtext->ecxt_scantuple = oldSlot;
312 break;
313
314 default:
315 elog(ERROR, "unrecognized commandType: %d", (int) cmdType);
316 }
317 econtext->ecxt_outertuple = planSlot;
318
319 /* Make old/new tuples available to ExecProject, if required */
320 if (oldSlot)
321 econtext->ecxt_oldtuple = oldSlot;
322 else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD)
323 econtext->ecxt_oldtuple = ExecGetAllNullSlot(estate, resultRelInfo);
324 else
325 econtext->ecxt_oldtuple = NULL; /* No references to OLD columns */
326
327 if (newSlot)
328 econtext->ecxt_newtuple = newSlot;
329 else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW)
330 econtext->ecxt_newtuple = ExecGetAllNullSlot(estate, resultRelInfo);
331 else
332 econtext->ecxt_newtuple = NULL; /* No references to NEW columns */
333
334 /*
335 * Tell ExecProject whether or not the OLD/NEW rows actually exist. This
336 * information is required to evaluate ReturningExpr nodes and also in
337 * ExecEvalSysVar() and ExecEvalWholeRowVar().
338 */
339 if (oldSlot == NULL)
340 projectReturning->pi_state.flags |= EEO_FLAG_OLD_IS_NULL;
341 else
342 projectReturning->pi_state.flags &= ~EEO_FLAG_OLD_IS_NULL;
343
344 if (newSlot == NULL)
345 projectReturning->pi_state.flags |= EEO_FLAG_NEW_IS_NULL;
346 else
347 projectReturning->pi_state.flags &= ~EEO_FLAG_NEW_IS_NULL;
348
349 /* Compute the RETURNING expressions */
350 return ExecProject(projectReturning);
351}
TupleTableSlot * ExecGetAllNullSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1274
#define EEO_FLAG_NEW_IS_NULL
Definition: execnodes.h:85
#define EEO_FLAG_OLD_IS_NULL
Definition: execnodes.h:83
TupleTableSlot * ecxt_newtuple
Definition: execnodes.h:307
TupleTableSlot * ecxt_oldtuple
Definition: execnodes.h:305

References CMD_DELETE, CMD_INSERT, CMD_UPDATE, 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, elog, ERROR, ModifyTableContext::estate, ExecGetAllNullSlot(), ExecProject(), ExprState::flags, ProjectionInfo::pi_exprContext, ProjectionInfo::pi_state, and ResultRelInfo::ri_projectReturning.

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

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 5120 of file nodeModifyTable.c.

5121{
5122 /*
5123 * Currently, we don't need to support rescan on ModifyTable nodes. The
5124 * semantics of that would be a bit debatable anyway.
5125 */
5126 elog(ERROR, "ExecReScanModifyTable is not implemented");
5127}

References elog, and ERROR.

Referenced by ExecReScan().

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 3923 of file nodeModifyTable.c.

3924{
3925 ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
3926 ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
3927
3928 /* Check for transition tables on the directly targeted relation. */
3929 mtstate->mt_transition_capture =
3930 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3931 RelationGetRelid(targetRelInfo->ri_RelationDesc),
3932 mtstate->operation);
3933 if (plan->operation == CMD_INSERT &&
3934 plan->onConflictAction == ONCONFLICT_UPDATE)
3935 mtstate->mt_oc_transition_capture =
3936 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3937 RelationGetRelid(targetRelInfo->ri_RelationDesc),
3938 CMD_UPDATE);
3939}
#define plan(x)
Definition: pg_regress.c:161
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1439
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4917

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

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

References Assert(), TM_FailureData::cmax, CMD_UPDATE, ModifyTableContext::cpUpdateReturningSlot, UpdateContext::crossPartUpdate, 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(), InplaceUpdateTupleLock, IsBootstrapProcessingMode, IsolationUsesXactSnapshot, UpdateContext::lockmode, 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 2167 of file nodeModifyTable.c.

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

References CMD_MERGE, TupleDescData::constr, UpdateContext::crossPartUpdate, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, ExecConstraints(), ExecCrossPartitionUpdate(), ExecCrossPartitionUpdateForeignKey(), ExecMaterializeSlot(), ExecPartitionCheck(), ExecUpdatePrepareSlot(), ExecWithCheckOptions(), UpdateContext::lockmode, ModifyTableContext::mtstate, NIL, ModifyTableState::operation, RelationData::rd_att, RelationData::rd_rel, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, table_tuple_update(), TM_Ok, ModifyTableContext::tmfd, TriggerDesc::trig_update_after_row, UpdateContext::updateIndexes, 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 2318 of file nodeModifyTable.c.

2321{
2322 ModifyTableState *mtstate = context->mtstate;
2323 List *recheckIndexes = NIL;
2324
2325 /* insert index entries for tuple if necessary */
2326 if (resultRelInfo->ri_NumIndices > 0 && (updateCxt->updateIndexes != TU_None))
2327 recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
2328 slot, context->estate,
2329 true, false,
2330 NULL, NIL,
2331 (updateCxt->updateIndexes == TU_Summarizing));
2332
2333 /* AFTER ROW UPDATE Triggers */
2334 ExecARUpdateTriggers(context->estate, resultRelInfo,
2335 NULL, NULL,
2336 tupleid, oldtuple, slot,
2337 recheckIndexes,
2338 mtstate->operation == CMD_INSERT ?
2339 mtstate->mt_oc_transition_capture :
2340 mtstate->mt_transition_capture,
2341 false);
2342
2343 list_free(recheckIndexes);
2344
2345 /*
2346 * Check any WITH CHECK OPTION constraints from parent views. We are
2347 * required to do this after testing all constraints and uniqueness
2348 * violations per the SQL spec, so we do it after actually updating the
2349 * record in the heap and all indexes.
2350 *
2351 * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
2352 * are looking for at this point.
2353 */
2354 if (resultRelInfo->ri_WithCheckOptions != NIL)
2355 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo,
2356 slot, context->estate);
2357}
@ TU_Summarizing
Definition: tableam.h:118
@ TU_None
Definition: tableam.h:112

References CMD_INSERT, ModifyTableContext::estate, ExecARUpdateTriggers(), ExecInsertIndexTuples(), ExecWithCheckOptions(), 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, UpdateContext::updateIndexes, and WCO_VIEW_CHECK.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ ExecUpdatePrepareSlot()

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

Definition at line 2132 of file nodeModifyTable.c.

2135{
2136 Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2137
2138 /*
2139 * Constraints and GENERATED expressions might reference the tableoid
2140 * column, so (re-)initialize tts_tableOid before evaluating them.
2141 */
2142 slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2143
2144 /*
2145 * Compute stored generated columns
2146 */
2147 if (resultRelationDesc->rd_att->constr &&
2148 resultRelationDesc->rd_att->constr->has_generated_stored)
2149 ExecComputeStoredGenerated(resultRelInfo, estate, slot,
2150 CMD_UPDATE);
2151}

References CMD_UPDATE, TupleDescData::constr, ExecComputeStoredGenerated(), TupleConstr::has_generated_stored, RelationData::rd_att, 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 2090 of file nodeModifyTable.c.

2093{
2094 Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2095
2096 if (result)
2097 *result = TM_Ok;
2098
2099 ExecMaterializeSlot(slot);
2100
2101 /*
2102 * Open the table's indexes, if we have not done so already, so that we
2103 * can add new index entries for the updated tuple.
2104 */
2105 if (resultRelationDesc->rd_rel->relhasindex &&
2106 resultRelInfo->ri_IndexRelationDescs == NULL)
2107 ExecOpenIndices(resultRelInfo, false);
2108
2109 /* BEFORE ROW UPDATE triggers */
2110 if (resultRelInfo->ri_TrigDesc &&
2111 resultRelInfo->ri_TrigDesc->trig_update_before_row)
2112 {
2113 /* Flush any pending inserts, so rows are visible to the triggers */
2115 ExecPendingInserts(context->estate);
2116
2117 return ExecBRUpdateTriggers(context->estate, context->epqstate,
2118 resultRelInfo, tupleid, oldtuple, slot,
2119 result, &context->tmfd);
2120 }
2121
2122 return true;
2123}
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd)
Definition: trigger.c:2941

References ModifyTableContext::epqstate, EState::es_insert_pending_result_relations, ModifyTableContext::estate, ExecBRUpdateTriggers(), ExecMaterializeSlot(), ExecOpenIndices(), ExecPendingInserts(), NIL, RelationData::rd_rel, 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 3878 of file nodeModifyTable.c.

3879{
3880 ModifyTable *plan = (ModifyTable *) node->ps.plan;
3881 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3882
3883 switch (node->operation)
3884 {
3885 case CMD_INSERT:
3886 if (plan->onConflictAction == ONCONFLICT_UPDATE)
3888 resultRelInfo,
3890 ExecASInsertTriggers(node->ps.state, resultRelInfo,
3891 node->mt_transition_capture);
3892 break;
3893 case CMD_UPDATE:
3894 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3895 node->mt_transition_capture);
3896 break;
3897 case CMD_DELETE:
3898 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3899 node->mt_transition_capture);
3900 break;
3901 case CMD_MERGE:
3903 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3904 node->mt_transition_capture);
3906 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3907 node->mt_transition_capture);
3909 ExecASInsertTriggers(node->ps.state, resultRelInfo,
3910 node->mt_transition_capture);
3911 break;
3912 default:
3913 elog(ERROR, "unknown operation");
3914 break;
3915 }
3916}
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2923
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2670
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2450

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

3842{
3843 ModifyTable *plan = (ModifyTable *) node->ps.plan;
3844 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3845
3846 switch (node->operation)
3847 {
3848 case CMD_INSERT:
3849 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3850 if (plan->onConflictAction == ONCONFLICT_UPDATE)
3852 resultRelInfo);
3853 break;
3854 case CMD_UPDATE:
3855 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
3856 break;
3857 case CMD_DELETE:
3858 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
3859 break;
3860 case CMD_MERGE:
3862 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3864 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
3866 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
3867 break;
3868 default:
3869 elog(ERROR, "unknown operation");
3870 break;
3871 }
3872}
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2399
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2619
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2865

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