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

1353{
1354 int i;
1355 int numInserted = numSlots;
1356 TupleTableSlot *slot = NULL;
1357 TupleTableSlot **rslots;
1358
1359 /*
1360 * insert into foreign table: let the FDW do it
1361 */
1362 rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1363 resultRelInfo,
1364 slots,
1365 planSlots,
1366 &numInserted);
1367
1368 for (i = 0; i < numInserted; i++)
1369 {
1370 slot = rslots[i];
1371
1372 /*
1373 * AFTER ROW Triggers might reference the tableoid column, so
1374 * (re-)initialize tts_tableOid before evaluating them.
1375 */
1376 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1377
1378 /* AFTER ROW INSERT Triggers */
1379 ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1380 mtstate->mt_transition_capture);
1381
1382 /*
1383 * Check any WITH CHECK OPTION constraints from parent views. See the
1384 * comment in ExecInsert.
1385 */
1386 if (resultRelInfo->ri_WithCheckOptions != NIL)
1387 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1388 }
1389
1390 if (canSetTag && numInserted > 0)
1391 estate->es_processed += numInserted;
1392
1393 /* Clean up all the slots, ready for the next batch */
1394 for (i = 0; i < numSlots; i++)
1395 {
1396 ExecClearTuple(slots[i]);
1397 ExecClearTuple(planSlots[i]);
1398 }
1399 resultRelInfo->ri_NumSlots = 0;
1400}
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2085
int i
Definition: isn.c:72
@ WCO_VIEW_CHECK
Definition: parsenodes.h:1368
#define NIL
Definition: pg_list.h:68
#define RelationGetRelid(relation)
Definition: rel.h:512
uint64 es_processed
Definition: execnodes.h:704
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1427
Relation ri_RelationDesc
Definition: execnodes.h:474
List * ri_WithCheckOptions
Definition: execnodes.h:543
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:527
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:454

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 if (!attr->attisdropped)
216 {
217 /* Normal case: demand type match */
218 if (exprType((Node *) tle->expr) != attr->atttypid)
220 (errcode(ERRCODE_DATATYPE_MISMATCH),
221 errmsg("table row type and query-specified row type do not match"),
222 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
223 format_type_be(attr->atttypid),
224 attno,
225 format_type_be(exprType((Node *) tle->expr)))));
226 }
227 else
228 {
229 /*
230 * For a dropped column, we can't check atttypid (it's likely 0).
231 * In any case the planner has most likely inserted an INT4 null.
232 * What we insist on is just *some* NULL constant.
233 */
234 if (!IsA(tle->expr, Const) ||
235 !((Const *) tle->expr)->constisnull)
237 (errcode(ERRCODE_DATATYPE_MISMATCH),
238 errmsg("table row type and query-specified row type do not match"),
239 errdetail("Query provides a value for a dropped column at ordinal position %d.",
240 attno)));
241 }
242 }
243 if (attno != resultDesc->natts)
245 (errcode(ERRCODE_DATATYPE_MISMATCH),
246 errmsg("table row type and query-specified row type do not match"),
247 errdetail("Query has too few columns.")));
248}
#define Assert(condition)
Definition: c.h:815
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
#define lfirst(lc)
Definition: pg_list.h:172
#define RelationGetDescr(relation)
Definition: rel.h:538
Definition: nodes.h:129
Expr * expr
Definition: primnodes.h:2219
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:154

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

380{
381 Relation rel = relinfo->ri_RelationDesc;
382
383 /* Redundantly check isolation level */
385 return;
386
387 if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
388 elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
389 ExecCheckTupleVisible(estate, rel, tempSlot);
390 ExecClearTuple(tempSlot);
391}
#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:1294
#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 342 of file nodeModifyTable.c.

345{
347 return;
348
349 if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
350 {
351 Datum xminDatum;
352 TransactionId xmin;
353 bool isnull;
354
355 xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
356 Assert(!isnull);
357 xmin = DatumGetTransactionId(xminDatum);
358
359 /*
360 * We should not raise a serialization failure if the conflict is
361 * against a tuple inserted by our own transaction, even if it's not
362 * visible to our snapshot. (This would happen, for example, if
363 * conflicting keys are proposed for insertion in a single command.)
364 */
368 errmsg("could not serialize access due to concurrent update")));
369 }
370}
uint32 TransactionId
Definition: c.h:609
#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:650
#define MinTransactionIdAttributeNumber
Definition: sysattr.h:22
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition: tableam.h:1341
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:416
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:940

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

526{
527 Relation rel = resultRelInfo->ri_RelationDesc;
528 TupleDesc tupdesc = RelationGetDescr(rel);
529 int natts = tupdesc->natts;
530 ExprContext *econtext = GetPerTupleExprContext(estate);
531 ExprState **ri_GeneratedExprs;
532 MemoryContext oldContext;
533 Datum *values;
534 bool *nulls;
535
536 /* We should not be called unless this is true */
537 Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
538
539 /*
540 * Initialize the expressions if we didn't already, and check whether we
541 * can exit early because nothing needs to be computed.
542 */
543 if (cmdtype == CMD_UPDATE)
544 {
545 if (resultRelInfo->ri_GeneratedExprsU == NULL)
546 ExecInitGenerated(resultRelInfo, estate, cmdtype);
547 if (resultRelInfo->ri_NumGeneratedNeededU == 0)
548 return;
549 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
550 }
551 else
552 {
553 if (resultRelInfo->ri_GeneratedExprsI == NULL)
554 ExecInitGenerated(resultRelInfo, estate, cmdtype);
555 /* Early exit is impossible given the prior Assert */
556 Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
557 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
558 }
559
561
562 values = palloc(sizeof(*values) * natts);
563 nulls = palloc(sizeof(*nulls) * natts);
564
565 slot_getallattrs(slot);
566 memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
567
568 for (int i = 0; i < natts; i++)
569 {
570 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
571
572 if (ri_GeneratedExprs[i])
573 {
574 Datum val;
575 bool isnull;
576
577 Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
578
579 econtext->ecxt_scantuple = slot;
580
581 val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
582
583 /*
584 * We must make a copy of val as we have no guarantees about where
585 * memory for a pass-by-reference Datum is located.
586 */
587 if (!isnull)
588 val = datumCopy(val, attr->attbyval, attr->attlen);
589
590 values[i] = val;
591 nulls[i] = isnull;
592 }
593 else
594 {
595 if (!nulls[i])
596 values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
597 }
598 }
599
600 ExecClearTuple(slot);
601 memcpy(slot->tts_values, values, sizeof(*values) * natts);
602 memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
605
606 MemoryContextSwitchTo(oldContext);
607}
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:1739
#define GetPerTupleExprContext(estate)
Definition: executor.h:563
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:568
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:346
long val
Definition: informix.c:689
void * palloc(Size size)
Definition: mcxt.c:1317
void ExecInitGenerated(ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
@ CMD_UPDATE
Definition: nodes.h:266
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
int16 attlen
Definition: tupdesc.h:71
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:267
ExprState ** ri_GeneratedExprsI
Definition: execnodes.h:554
int ri_NumGeneratedNeededU
Definition: execnodes.h:559
ExprState ** ri_GeneratedExprsU
Definition: execnodes.h:555
int ri_NumGeneratedNeededI
Definition: execnodes.h:558
bool has_generated_stored
Definition: tupdesc.h:46
TupleConstr * constr
Definition: tupdesc.h:135
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:169
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:472

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

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

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

2351{
2352 ListCell *lc;
2353 ResultRelInfo *rootRelInfo;
2354 List *ancestorRels;
2355
2356 rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2357 ancestorRels = ExecGetAncestorResultRels(context->estate, sourcePartInfo);
2358
2359 /*
2360 * For any foreign keys that point directly into a non-root ancestors of
2361 * the source partition, we can in theory fire an update event to enforce
2362 * those constraints using their triggers, if we could tell that both the
2363 * source and the destination partitions are under the same ancestor. But
2364 * for now, we simply report an error that those cannot be enforced.
2365 */
2366 foreach(lc, ancestorRels)
2367 {
2368 ResultRelInfo *rInfo = lfirst(lc);
2369 TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2370 bool has_noncloned_fkey = false;
2371
2372 /* Root ancestor's triggers will be processed. */
2373 if (rInfo == rootRelInfo)
2374 continue;
2375
2376 if (trigdesc && trigdesc->trig_update_after_row)
2377 {
2378 for (int i = 0; i < trigdesc->numtriggers; i++)
2379 {
2380 Trigger *trig = &trigdesc->triggers[i];
2381
2382 if (!trig->tgisclone &&
2384 {
2385 has_noncloned_fkey = true;
2386 break;
2387 }
2388 }
2389 }
2390
2391 if (has_noncloned_fkey)
2392 ereport(ERROR,
2393 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2394 errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2395 errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2398 errhint("Consider defining the foreign key on table \"%s\".",
2400 }
2401
2402 /* Perform the root table's triggers. */
2404 rootRelInfo, sourcePartInfo, destPartInfo,
2405 tupleid, NULL, newslot, NIL, NULL, true);
2406}
int errhint(const char *fmt,...)
Definition: elog.c:1317
List * ExecGetAncestorResultRels(EState *estate, ResultRelInfo *resultRelInfo)
Definition: execMain.c:1398
#define RelationGetRelationName(relation)
Definition: rel.h:546
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3210
Definition: pg_list.h:54
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:608
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:509
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:282

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

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

1472{
1473 EState *estate = context->estate;
1474
1475 return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1476 estate->es_output_cid,
1477 estate->es_snapshot,
1478 estate->es_crosscheck_snapshot,
1479 true /* wait for commit */ ,
1480 &context->tmfd,
1481 changingPart);
1482}
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:651
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:1497

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

1494{
1495 ModifyTableState *mtstate = context->mtstate;
1496 EState *estate = context->estate;
1497 TransitionCaptureState *ar_delete_trig_tcs;
1498
1499 /*
1500 * If this delete is the result of a partition key update that moved the
1501 * tuple to a new partition, put this row into the transition OLD TABLE,
1502 * if there is one. We need to do this separately for DELETE and INSERT
1503 * because they happen on different tables.
1504 */
1505 ar_delete_trig_tcs = mtstate->mt_transition_capture;
1506 if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1508 {
1509 ExecARUpdateTriggers(estate, resultRelInfo,
1510 NULL, NULL,
1511 tupleid, oldtuple,
1512 NULL, NULL, mtstate->mt_transition_capture,
1513 false);
1514
1515 /*
1516 * We've already captured the OLD TABLE row, so make sure any AR
1517 * DELETE trigger fired below doesn't capture it again.
1518 */
1519 ar_delete_trig_tcs = NULL;
1520 }
1521
1522 /* AFTER ROW DELETE Triggers */
1523 ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1524 ar_delete_trig_tcs, changingPart);
1525}
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 1439 of file nodeModifyTable.c.

1442{
1443 if (result)
1444 *result = TM_Ok;
1445
1446 /* BEFORE ROW DELETE triggers */
1447 if (resultRelInfo->ri_TrigDesc &&
1448 resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1449 {
1450 /* Flush any pending inserts, so rows are visible to the triggers */
1452 ExecPendingInserts(context->estate);
1453
1454 return ExecBRDeleteTriggers(context->estate, context->epqstate,
1455 resultRelInfo, tupleid, oldtuple,
1456 epqreturnslot, result, &context->tmfd);
1457 }
1458
1459 return true;
1460}
static void ExecPendingInserts(EState *estate)
List * es_insert_pending_result_relations
Definition: execnodes.h:761
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 5003 of file nodeModifyTable.c.

5004{
5005 int i;
5006
5007 /*
5008 * Allow any FDWs to shut down
5009 */
5010 for (i = 0; i < node->mt_nrels; i++)
5011 {
5012 int j;
5013 ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
5014
5015 if (!resultRelInfo->ri_usesFdwDirectModify &&
5016 resultRelInfo->ri_FdwRoutine != NULL &&
5017 resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
5018 resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
5019 resultRelInfo);
5020
5021 /*
5022 * Cleanup the initialized batch slots. This only matters for FDWs
5023 * with batching, but the other cases will have ri_NumSlotsInitialized
5024 * == 0.
5025 */
5026 for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
5027 {
5028 ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
5030 }
5031 }
5032
5033 /*
5034 * Close all the partitioned tables, leaf partitions, and their indices
5035 * and release the slot used for tuple routing, if set.
5036 */
5038 {
5040
5041 if (node->mt_root_tuple_slot)
5043 }
5044
5045 /*
5046 * Terminate EPQ execution if active
5047 */
5049
5050 /*
5051 * shut down subplan
5052 */
5054}
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:3025
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1441
#define outerPlanState(node)
Definition: execnodes.h:1246
int j
Definition: isn.c:73
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1393
EPQState mt_epqstate
Definition: execnodes.h:1403
TupleTableSlot ** ri_Slots
Definition: execnodes.h:539
int ri_NumSlotsInitialized
Definition: execnodes.h:537
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:540
bool ri_usesFdwDirectModify
Definition: execnodes.h:533

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

749{
750 ProjectionInfo *newProj = relinfo->ri_projectNew;
751 ExprContext *econtext;
752
753 /*
754 * If there's no projection to be done, just make sure the slot is of the
755 * right type for the target rel. If the planSlot is the right type we
756 * can use it as-is, else copy the data into ri_newTupleSlot.
757 */
758 if (newProj == NULL)
759 {
760 if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
761 {
762 ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
763 return relinfo->ri_newTupleSlot;
764 }
765 else
766 return planSlot;
767 }
768
769 /*
770 * Else project; since the projection output slot is ri_newTupleSlot, this
771 * will also fix any slot-type problem.
772 *
773 * Note: currently, this is dead code, because INSERT cases don't receive
774 * any junk columns so there's never a projection to be done.
775 */
776 econtext = newProj->pi_exprContext;
777 econtext->ecxt_outertuple = planSlot;
778 return ExecProject(newProj);
779}
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:389
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:271
ExprContext * pi_exprContext
Definition: execnodes.h:382
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:499
ProjectionInfo * ri_projectNew
Definition: execnodes.h:497
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:121
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509

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

794{
795 ProjectionInfo *newProj = relinfo->ri_projectNew;
796 ExprContext *econtext;
797
798 /* Use a few extra Asserts to protect against outside callers */
800 Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
801 Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
802
803 econtext = newProj->pi_exprContext;
804 econtext->ecxt_outertuple = planSlot;
805 econtext->ecxt_scantuple = oldSlot;
806 return ExecProject(newProj);
807}

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

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

621{
622 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
623 Plan *subplan = outerPlan(node);
624 EState *estate = mtstate->ps.state;
625 List *insertTargetList = NIL;
626 bool need_projection = false;
627 ListCell *l;
628
629 /* Extract non-junk columns of the subplan's result tlist. */
630 foreach(l, subplan->targetlist)
631 {
632 TargetEntry *tle = (TargetEntry *) lfirst(l);
633
634 if (!tle->resjunk)
635 insertTargetList = lappend(insertTargetList, tle);
636 else
637 need_projection = true;
638 }
639
640 /*
641 * The junk-free list must produce a tuple suitable for the result
642 * relation.
643 */
644 ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
645
646 /* We'll need a slot matching the table's format. */
647 resultRelInfo->ri_newTupleSlot =
648 table_slot_create(resultRelInfo->ri_RelationDesc,
649 &estate->es_tupleTable);
650
651 /* Build ProjectionInfo if needed (it probably isn't). */
652 if (need_projection)
653 {
654 TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
655
656 /* need an expression context to do the projection */
657 if (mtstate->ps.ps_ExprContext == NULL)
658 ExecAssignExprContext(estate, &mtstate->ps);
659
660 resultRelInfo->ri_projectNew =
661 ExecBuildProjectionInfo(insertTargetList,
662 mtstate->ps.ps_ExprContext,
663 resultRelInfo->ri_newTupleSlot,
664 &mtstate->ps,
665 relDesc);
666 }
667
668 resultRelInfo->ri_projectNewInfoValid = true;
669}
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:231
List * es_tupleTable
Definition: execnodes.h:702
ExprContext * ps_ExprContext
Definition: execnodes.h:1189

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

3645{
3646 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
3647 ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
3648 ResultRelInfo *resultRelInfo;
3649 ExprContext *econtext;
3650 ListCell *lc;
3651 int i;
3652
3653 if (node->mergeActionLists == NIL)
3654 return;
3655
3656 mtstate->mt_merge_subcommands = 0;
3657
3658 if (mtstate->ps.ps_ExprContext == NULL)
3659 ExecAssignExprContext(estate, &mtstate->ps);
3660 econtext = mtstate->ps.ps_ExprContext;
3661
3662 /*
3663 * Create a MergeActionState for each action on the mergeActionList and
3664 * add it to either a list of matched actions or not-matched actions.
3665 *
3666 * Similar logic appears in ExecInitPartitionInfo(), so if changing
3667 * anything here, do so there too.
3668 */
3669 i = 0;
3670 foreach(lc, node->mergeActionLists)
3671 {
3672 List *mergeActionList = lfirst(lc);
3673 Node *joinCondition;
3674 TupleDesc relationDesc;
3675 ListCell *l;
3676
3677 joinCondition = (Node *) list_nth(node->mergeJoinConditions, i);
3678 resultRelInfo = mtstate->resultRelInfo + i;
3679 i++;
3680 relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3681
3682 /* initialize slots for MERGE fetches from this rel */
3683 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3684 ExecInitMergeTupleSlots(mtstate, resultRelInfo);
3685
3686 /* initialize state for join condition checking */
3687 resultRelInfo->ri_MergeJoinCondition =
3688 ExecInitQual((List *) joinCondition, &mtstate->ps);
3689
3690 foreach(l, mergeActionList)
3691 {
3693 MergeActionState *action_state;
3694 TupleTableSlot *tgtslot;
3695 TupleDesc tgtdesc;
3696
3697 /*
3698 * Build action merge state for this rel. (For partitions,
3699 * equivalent code exists in ExecInitPartitionInfo.)
3700 */
3701 action_state = makeNode(MergeActionState);
3702 action_state->mas_action = action;
3703 action_state->mas_whenqual = ExecInitQual((List *) action->qual,
3704 &mtstate->ps);
3705
3706 /*
3707 * We create three lists - one for each MergeMatchKind - and stick
3708 * the MergeActionState into the appropriate list.
3709 */
3710 resultRelInfo->ri_MergeActions[action->matchKind] =
3711 lappend(resultRelInfo->ri_MergeActions[action->matchKind],
3712 action_state);
3713
3714 switch (action->commandType)
3715 {
3716 case CMD_INSERT:
3717 ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
3718 action->targetList);
3719
3720 /*
3721 * If the MERGE targets a partitioned table, any INSERT
3722 * actions must be routed through it, not the child
3723 * relations. Initialize the routing struct and the root
3724 * table's "new" tuple slot for that, if not already done.
3725 * The projection we prepare, for all relations, uses the
3726 * root relation descriptor, and targets the plan's root
3727 * slot. (This is consistent with the fact that we
3728 * checked the plan output to match the root relation,
3729 * above.)
3730 */
3731 if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
3732 RELKIND_PARTITIONED_TABLE)
3733 {
3734 if (mtstate->mt_partition_tuple_routing == NULL)
3735 {
3736 /*
3737 * Initialize planstate for routing if not already
3738 * done.
3739 *
3740 * Note that the slot is managed as a standalone
3741 * slot belonging to ModifyTableState, so we pass
3742 * NULL for the 2nd argument.
3743 */
3744 mtstate->mt_root_tuple_slot =
3745 table_slot_create(rootRelInfo->ri_RelationDesc,
3746 NULL);
3749 rootRelInfo->ri_RelationDesc);
3750 }
3751 tgtslot = mtstate->mt_root_tuple_slot;
3752 tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3753 }
3754 else
3755 {
3756 /* not partitioned? use the stock relation and slot */
3757 tgtslot = resultRelInfo->ri_newTupleSlot;
3758 tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3759 }
3760
3761 action_state->mas_proj =
3762 ExecBuildProjectionInfo(action->targetList, econtext,
3763 tgtslot,
3764 &mtstate->ps,
3765 tgtdesc);
3766
3768 break;
3769 case CMD_UPDATE:
3770 action_state->mas_proj =
3772 true,
3773 action->updateColnos,
3774 relationDesc,
3775 econtext,
3776 resultRelInfo->ri_newTupleSlot,
3777 &mtstate->ps);
3779 break;
3780 case CMD_DELETE:
3782 break;
3783 case CMD_NOTHING:
3784 break;
3785 default:
3786 elog(ERROR, "unknown operation");
3787 break;
3788 }
3789 }
3790 }
3791}
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:1379
#define MERGE_INSERT
Definition: execnodes.h:1378
#define MERGE_DELETE
Definition: execnodes.h:1380
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
void ExecInitMergeTupleSlots(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_NOTHING
Definition: nodes.h:272
#define makeNode(_type_)
Definition: nodes.h:155
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
MergeAction * mas_action
Definition: execnodes.h:443
ProjectionInfo * mas_proj
Definition: execnodes.h:444
ExprState * mas_whenqual
Definition: execnodes.h:446
int mt_merge_subcommands
Definition: execnodes.h:1433
List * mergeJoinConditions
Definition: plannodes.h:328
List * mergeActionLists
Definition: plannodes.h:326

References generate_unaccent_rules::action, CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_UPDATE, elog, ERROR, ExecAssignExprContext(), ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecCheckPlanOutput(), ExecInitMergeTupleSlots(), ExecInitQual(), ExecSetupPartitionTupleRouting(), i, if(), lappend(), lfirst, list_nth(), makeNode, MergeActionState::mas_action, MergeActionState::mas_proj, MergeActionState::mas_whenqual, MERGE_DELETE, MERGE_INSERT, MERGE_UPDATE, ModifyTable::mergeActionLists, ModifyTable::mergeJoinConditions, ModifyTableState::mt_merge_subcommands, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, NIL, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, RelationGetDescr, ModifyTableState::resultRelInfo, ModifyTableState::rootResultRelInfo, table_slot_create(), and unlikely.

Referenced by ExecInitModifyTable().

◆ ExecInitMergeTupleSlots()

void ExecInitMergeTupleSlots ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)

Definition at line 3800 of file nodeModifyTable.c.

3802{
3803 EState *estate = mtstate->ps.state;
3804
3805 Assert(!resultRelInfo->ri_projectNewInfoValid);
3806
3807 resultRelInfo->ri_oldTupleSlot =
3808 table_slot_create(resultRelInfo->ri_RelationDesc,
3809 &estate->es_tupleTable);
3810 resultRelInfo->ri_newTupleSlot =
3811 table_slot_create(resultRelInfo->ri_RelationDesc,
3812 &estate->es_tupleTable);
3813 resultRelInfo->ri_projectNewInfoValid = true;
3814}

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

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

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

691{
692 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
693 Plan *subplan = outerPlan(node);
694 EState *estate = mtstate->ps.state;
695 TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
696 int whichrel;
697 List *updateColnos;
698
699 /*
700 * Usually, mt_lastResultIndex matches the target rel. If it happens not
701 * to, we can get the index the hard way with an integer division.
702 */
703 whichrel = mtstate->mt_lastResultIndex;
704 if (resultRelInfo != mtstate->resultRelInfo + whichrel)
705 {
706 whichrel = resultRelInfo - mtstate->resultRelInfo;
707 Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
708 }
709
710 updateColnos = (List *) list_nth(mtstate->mt_updateColnosLists, whichrel);
711
712 /*
713 * For UPDATE, we use the old tuple to fill up missing values in the tuple
714 * produced by the subplan to get the new tuple. We need two slots, both
715 * matching the table's desired format.
716 */
717 resultRelInfo->ri_oldTupleSlot =
718 table_slot_create(resultRelInfo->ri_RelationDesc,
719 &estate->es_tupleTable);
720 resultRelInfo->ri_newTupleSlot =
721 table_slot_create(resultRelInfo->ri_RelationDesc,
722 &estate->es_tupleTable);
723
724 /* need an expression context to do the projection */
725 if (mtstate->ps.ps_ExprContext == NULL)
726 ExecAssignExprContext(estate, &mtstate->ps);
727
728 resultRelInfo->ri_projectNew =
729 ExecBuildUpdateProjection(subplan->targetlist,
730 false, /* subplan did the evaluation */
731 updateColnos,
732 relDesc,
733 mtstate->ps.ps_ExprContext,
734 resultRelInfo->ri_newTupleSlot,
735 &mtstate->ps);
736
737 resultRelInfo->ri_projectNewInfoValid = true;
738}

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

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

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

4421{
4422 if (node->mt_resultOidHash)
4423 {
4424 /* Use the pre-built hash table to locate the rel */
4425 MTTargetRelLookup *mtlookup;
4426
4427 mtlookup = (MTTargetRelLookup *)
4428 hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
4429 if (mtlookup)
4430 {
4431 if (update_cache)
4432 {
4433 node->mt_lastResultOid = resultoid;
4434 node->mt_lastResultIndex = mtlookup->relationIndex;
4435 }
4436 return node->resultRelInfo + mtlookup->relationIndex;
4437 }
4438 }
4439 else
4440 {
4441 /* With few target rels, just search the ResultRelInfo array */
4442 for (int ndx = 0; ndx < node->mt_nrels; ndx++)
4443 {
4444 ResultRelInfo *rInfo = node->resultRelInfo + ndx;
4445
4446 if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
4447 {
4448 if (update_cache)
4449 {
4450 node->mt_lastResultOid = resultoid;
4451 node->mt_lastResultIndex = ndx;
4452 }
4453 return rInfo;
4454 }
4455 }
4456 }
4457
4458 if (!missing_ok)
4459 elog(ERROR, "incorrect result relation OID %u", resultoid);
4460 return NULL;
4461}
@ 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 2917 of file nodeModifyTable.c.

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

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

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

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

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

1407{
1408 ListCell *l1,
1409 *l2;
1410
1413 {
1414 ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l1);
1415 ModifyTableState *mtstate = (ModifyTableState *) lfirst(l2);
1416
1417 Assert(mtstate);
1418 ExecBatchInsert(mtstate, resultRelInfo,
1419 resultRelInfo->ri_Slots,
1420 resultRelInfo->ri_PlanSlots,
1421 resultRelInfo->ri_NumSlots,
1422 estate, mtstate->canSetTag);
1423 }
1424
1429}
#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 3931 of file nodeModifyTable.c.

3937{
3938 ResultRelInfo *partrel;
3939 TupleConversionMap *map;
3940
3941 /*
3942 * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
3943 * not find a valid partition for the tuple in 'slot' then an error is
3944 * raised. An error may also be raised if the found partition is not a
3945 * valid target for INSERTs. This is required since a partitioned table
3946 * UPDATE to another partition becomes a DELETE+INSERT.
3947 */
3948 partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
3949
3950 /*
3951 * If we're capturing transition tuples, we might need to convert from the
3952 * partition rowtype to root partitioned table's rowtype. But if there
3953 * are no BEFORE triggers on the partition that could change the tuple, we
3954 * can just remember the original unconverted tuple to avoid a needless
3955 * round trip conversion.
3956 */
3957 if (mtstate->mt_transition_capture != NULL)
3958 {
3959 bool has_before_insert_row_trig;
3960
3961 has_before_insert_row_trig = (partrel->ri_TrigDesc &&
3963
3965 !has_before_insert_row_trig ? slot : NULL;
3966 }
3967
3968 /*
3969 * Convert the tuple, if necessary.
3970 */
3971 map = ExecGetRootToChildMap(partrel, estate);
3972 if (map != NULL)
3973 {
3974 TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
3975
3976 slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
3977 }
3978
3979 *partRelInfo = partrel;
3980 return slot;
3981}
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:609

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

273{
274 EState *estate = context->estate;
275 ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
276 ExprContext *econtext = projectReturning->pi_exprContext;
277
278 /* Make tuple and any needed join variables available to ExecProject */
279 switch (cmdType)
280 {
281 case CMD_INSERT:
282 case CMD_UPDATE:
283 /* return new tuple by default */
284 if (newSlot)
285 econtext->ecxt_scantuple = newSlot;
286 break;
287
288 case CMD_DELETE:
289 /* return old tuple by default */
290 if (oldSlot)
291 econtext->ecxt_scantuple = oldSlot;
292 break;
293
294 default:
295 elog(ERROR, "unrecognized commandType: %d", (int) cmdType);
296 }
297 econtext->ecxt_outertuple = planSlot;
298
299 /* Make old/new tuples available to ExecProject, if required */
300 if (oldSlot)
301 econtext->ecxt_oldtuple = oldSlot;
302 else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD)
303 econtext->ecxt_oldtuple = ExecGetAllNullSlot(estate, resultRelInfo);
304 else
305 econtext->ecxt_oldtuple = NULL; /* No references to OLD columns */
306
307 if (newSlot)
308 econtext->ecxt_newtuple = newSlot;
309 else if (projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW)
310 econtext->ecxt_newtuple = ExecGetAllNullSlot(estate, resultRelInfo);
311 else
312 econtext->ecxt_newtuple = NULL; /* No references to NEW columns */
313
314 /*
315 * Tell ExecProject whether or not the OLD/NEW rows actually exist. This
316 * information is required to evaluate ReturningExpr nodes and also in
317 * ExecEvalSysVar() and ExecEvalWholeRowVar().
318 */
319 if (oldSlot == NULL)
320 projectReturning->pi_state.flags |= EEO_FLAG_OLD_IS_NULL;
321 else
322 projectReturning->pi_state.flags &= ~EEO_FLAG_OLD_IS_NULL;
323
324 if (newSlot == NULL)
325 projectReturning->pi_state.flags |= EEO_FLAG_NEW_IS_NULL;
326 else
327 projectReturning->pi_state.flags &= ~EEO_FLAG_NEW_IS_NULL;
328
329 /* Compute the RETURNING expressions */
330 return ExecProject(projectReturning);
331}
TupleTableSlot * ExecGetAllNullSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1263
#define EEO_FLAG_NEW_IS_NULL
Definition: execnodes.h:84
#define EEO_FLAG_OLD_IS_NULL
Definition: execnodes.h:82
TupleTableSlot * ecxt_newtuple
Definition: execnodes.h:306
TupleTableSlot * ecxt_oldtuple
Definition: execnodes.h:304

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

5058{
5059 /*
5060 * Currently, we don't need to support rescan on ModifyTable nodes. The
5061 * semantics of that would be a bit debatable anyway.
5062 */
5063 elog(ERROR, "ExecReScanModifyTable is not implemented");
5064}

References elog, and ERROR.

Referenced by ExecReScan().

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 3902 of file nodeModifyTable.c.

3903{
3904 ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
3905 ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
3906
3907 /* Check for transition tables on the directly targeted relation. */
3908 mtstate->mt_transition_capture =
3909 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3910 RelationGetRelid(targetRelInfo->ri_RelationDesc),
3911 mtstate->operation);
3912 if (plan->operation == CMD_INSERT &&
3913 plan->onConflictAction == ONCONFLICT_UPDATE)
3914 mtstate->mt_oc_transition_capture =
3915 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3916 RelationGetRelid(targetRelInfo->ri_RelationDesc),
3917 CMD_UPDATE);
3918}
#define plan(x)
Definition: pg_regress.c:161
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1430
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 2438 of file nodeModifyTable.c.

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

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

References CMD_MERGE, TupleDescData::constr, UpdateContext::crossPartUpdate, EState::es_crosscheck_snapshot, EState::es_output_cid, EState