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 "rewrite/rewriteManip.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/injection_point.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
Include dependency graph for nodeModifyTable.c:

Go to the source code of this file.

Data Structures

struct  MTTargetRelLookup
 
struct  ModifyTableContext
 
struct  UpdateContext
 

Macros

#define MT_NRELS_HASH   64
 

Typedefs

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

Functions

static void ExecBatchInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
 
static void ExecPendingInserts (EState *estate)
 
static void ExecCrossPartitionUpdateForeignKey (ModifyTableContext *context, ResultRelInfo *sourcePartInfo, ResultRelInfo *destPartInfo, ItemPointer tupleid, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
static bool 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 1367 of file nodeModifyTable.c.

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

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

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

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

Referenced by ExecInitInsertProjection(), and ExecInitMerge().

◆ ExecCheckTIDVisible()

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

Definition at line 398 of file nodeModifyTable.c.

402{
403 Relation rel = relinfo->ri_RelationDesc;
404
405 /* Redundantly check isolation level */
407 return;
408
409 if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
410 elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
411 ExecCheckTupleVisible(estate, rel, tempSlot);
412 ExecClearTuple(tempSlot);
413}
#define elog(elevel,...)
Definition: elog.h:226
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
#define SnapshotAny
Definition: snapmgr.h:33
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1263
#define IsolationUsesXactSnapshot()
Definition: xact.h:52

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

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

References Assert(), DatumGetTransactionId(), ereport, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errmsg(), ERROR, EState::es_snapshot, 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 545 of file nodeModifyTable.c.

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

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

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

◆ ExecCrossPartitionUpdate()

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

Definition at line 1930 of file nodeModifyTable.c.

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

References Assert(), 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 2368 of file nodeModifyTable.c.

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

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

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

References Assert(), 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 1492 of file nodeModifyTable.c.

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

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

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

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

1463{
1464 if (result)
1465 *result = TM_Ok;
1466
1467 /* BEFORE ROW DELETE triggers */
1468 if (resultRelInfo->ri_TrigDesc &&
1469 resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1470 {
1471 /* Flush any pending inserts, so rows are visible to the triggers */
1473 ExecPendingInserts(context->estate);
1474
1475 return ExecBRDeleteTriggers(context->estate, context->epqstate,
1476 resultRelInfo, tupleid, oldtuple,
1477 epqreturnslot, result, &context->tmfd,
1478 context->mtstate->operation == CMD_MERGE);
1479 }
1480
1481 return true;
1482}
static void ExecPendingInserts(EState *estate)
List * es_insert_pending_result_relations
Definition: execnodes.h:771
bool trig_delete_before_row
Definition: reltrigger.h:66
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd, bool is_merge_delete)
Definition: trigger.c:2701

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

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 5212 of file nodeModifyTable.c.

5213{
5214 int i;
5215
5216 /*
5217 * Allow any FDWs to shut down
5218 */
5219 for (i = 0; i < node->mt_nrels; i++)
5220 {
5221 int j;
5222 ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
5223
5224 if (!resultRelInfo->ri_usesFdwDirectModify &&
5225 resultRelInfo->ri_FdwRoutine != NULL &&
5226 resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
5227 resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
5228 resultRelInfo);
5229
5230 /*
5231 * Cleanup the initialized batch slots. This only matters for FDWs
5232 * with batching, but the other cases will have ri_NumSlotsInitialized
5233 * == 0.
5234 */
5235 for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
5236 {
5237 ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
5239 }
5240 }
5241
5242 /*
5243 * Close all the partitioned tables, leaf partitions, and their indices
5244 * and release the slot used for tuple routing, if set.
5245 */
5247 {
5249
5250 if (node->mt_root_tuple_slot)
5252 }
5253
5254 /*
5255 * Terminate EPQ execution if active
5256 */
5258
5259 /*
5260 * shut down subplan
5261 */
5263}
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:3179
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
#define outerPlanState(node)
Definition: execnodes.h:1261
int j
Definition: isn.c:78
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1408
EPQState mt_epqstate
Definition: execnodes.h:1418
TupleTableSlot ** ri_Slots
Definition: execnodes.h:545
int ri_NumSlotsInitialized
Definition: execnodes.h:543
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:546
bool ri_usesFdwDirectModify
Definition: execnodes.h:539

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

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

References ExprContext::ecxt_outertuple, ExecCopySlot(), ExecProject(), 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 813 of file nodeModifyTable.c.

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

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

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

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

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

3690{
3691 List *mergeActionLists = mtstate->mt_mergeActionLists;
3692 List *mergeJoinConditions = mtstate->mt_mergeJoinConditions;
3693 ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
3694 ResultRelInfo *resultRelInfo;
3695 ExprContext *econtext;
3696 ListCell *lc;
3697 int i;
3698
3699 if (mergeActionLists == NIL)
3700 return;
3701
3702 mtstate->mt_merge_subcommands = 0;
3703
3704 if (mtstate->ps.ps_ExprContext == NULL)
3705 ExecAssignExprContext(estate, &mtstate->ps);
3706 econtext = mtstate->ps.ps_ExprContext;
3707
3708 /*
3709 * Create a MergeActionState for each action on the mergeActionList and
3710 * add it to either a list of matched actions or not-matched actions.
3711 *
3712 * Similar logic appears in ExecInitPartitionInfo(), so if changing
3713 * anything here, do so there too.
3714 */
3715 i = 0;
3716 foreach(lc, mergeActionLists)
3717 {
3718 List *mergeActionList = lfirst(lc);
3719 Node *joinCondition;
3720 TupleDesc relationDesc;
3721 ListCell *l;
3722
3723 joinCondition = (Node *) list_nth(mergeJoinConditions, i);
3724 resultRelInfo = mtstate->resultRelInfo + i;
3725 i++;
3726 relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3727
3728 /* initialize slots for MERGE fetches from this rel */
3729 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3730 ExecInitMergeTupleSlots(mtstate, resultRelInfo);
3731
3732 /* initialize state for join condition checking */
3733 resultRelInfo->ri_MergeJoinCondition =
3734 ExecInitQual((List *) joinCondition, &mtstate->ps);
3735
3736 foreach(l, mergeActionList)
3737 {
3739 MergeActionState *action_state;
3740 TupleTableSlot *tgtslot;
3741 TupleDesc tgtdesc;
3742
3743 /*
3744 * Build action merge state for this rel. (For partitions,
3745 * equivalent code exists in ExecInitPartitionInfo.)
3746 */
3747 action_state = makeNode(MergeActionState);
3748 action_state->mas_action = action;
3749 action_state->mas_whenqual = ExecInitQual((List *) action->qual,
3750 &mtstate->ps);
3751
3752 /*
3753 * We create three lists - one for each MergeMatchKind - and stick
3754 * the MergeActionState into the appropriate list.
3755 */
3756 resultRelInfo->ri_MergeActions[action->matchKind] =
3757 lappend(resultRelInfo->ri_MergeActions[action->matchKind],
3758 action_state);
3759
3760 switch (action->commandType)
3761 {
3762 case CMD_INSERT:
3763 /* INSERT actions always use rootRelInfo */
3765 action->targetList);
3766
3767 /*
3768 * If the MERGE targets a partitioned table, any INSERT
3769 * actions must be routed through it, not the child
3770 * relations. Initialize the routing struct and the root
3771 * table's "new" tuple slot for that, if not already done.
3772 * The projection we prepare, for all relations, uses the
3773 * root relation descriptor, and targets the plan's root
3774 * slot. (This is consistent with the fact that we
3775 * checked the plan output to match the root relation,
3776 * above.)
3777 */
3778 if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
3779 RELKIND_PARTITIONED_TABLE)
3780 {
3781 if (mtstate->mt_partition_tuple_routing == NULL)
3782 {
3783 /*
3784 * Initialize planstate for routing if not already
3785 * done.
3786 *
3787 * Note that the slot is managed as a standalone
3788 * slot belonging to ModifyTableState, so we pass
3789 * NULL for the 2nd argument.
3790 */
3791 mtstate->mt_root_tuple_slot =
3793 NULL);
3796 rootRelInfo->ri_RelationDesc);
3797 }
3798 tgtslot = mtstate->mt_root_tuple_slot;
3799 tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3800 }
3801 else
3802 {
3803 /*
3804 * If the MERGE targets an inherited table, we insert
3805 * into the root table, so we must initialize its
3806 * "new" tuple slot, if not already done, and use its
3807 * relation descriptor for the projection.
3808 *
3809 * For non-inherited tables, rootRelInfo and
3810 * resultRelInfo are the same, and the "new" tuple
3811 * slot will already have been initialized.
3812 */
3813 if (rootRelInfo->ri_newTupleSlot == NULL)
3814 rootRelInfo->ri_newTupleSlot =
3816 &estate->es_tupleTable);
3817
3818 tgtslot = rootRelInfo->ri_newTupleSlot;
3819 tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3820 }
3821
3822 action_state->mas_proj =
3823 ExecBuildProjectionInfo(action->targetList, econtext,
3824 tgtslot,
3825 &mtstate->ps,
3826 tgtdesc);
3827
3829 break;
3830 case CMD_UPDATE:
3831 action_state->mas_proj =
3833 true,
3834 action->updateColnos,
3835 relationDesc,
3836 econtext,
3837 resultRelInfo->ri_newTupleSlot,
3838 &mtstate->ps);
3840 break;
3841 case CMD_DELETE:
3843 break;
3844 case CMD_NOTHING:
3845 break;
3846 default:
3847 elog(ERROR, "unknown action in MERGE WHEN clause");
3848 break;
3849 }
3850 }
3851 }
3852
3853 /*
3854 * If the MERGE targets an inherited table, any INSERT actions will use
3855 * rootRelInfo, and rootRelInfo will not be in the resultRelInfo array.
3856 * Therefore we must initialize its WITH CHECK OPTION constraints and
3857 * RETURNING projection, as ExecInitModifyTable did for the resultRelInfo
3858 * entries.
3859 *
3860 * Note that the planner does not build a withCheckOptionList or
3861 * returningList for the root relation, but as in ExecInitPartitionInfo,
3862 * we can use the first resultRelInfo entry as a reference to calculate
3863 * the attno's for the root table.
3864 */
3865 if (rootRelInfo != mtstate->resultRelInfo &&
3866 rootRelInfo->ri_RelationDesc->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
3867 (mtstate->mt_merge_subcommands & MERGE_INSERT) != 0)
3868 {
3869 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
3870 Relation rootRelation = rootRelInfo->ri_RelationDesc;
3871 Relation firstResultRel = mtstate->resultRelInfo[0].ri_RelationDesc;
3872 int firstVarno = mtstate->resultRelInfo[0].ri_RangeTableIndex;
3873 AttrMap *part_attmap = NULL;
3874 bool found_whole_row;
3875
3876 if (node->withCheckOptionLists != NIL)
3877 {
3878 List *wcoList;
3879 List *wcoExprs = NIL;
3880
3881 /* There should be as many WCO lists as result rels */
3884
3885 /*
3886 * Use the first WCO list as a reference. In the most common case,
3887 * this will be for the same relation as rootRelInfo, and so there
3888 * will be no need to adjust its attno's.
3889 */
3890 wcoList = linitial(node->withCheckOptionLists);
3891 if (rootRelation != firstResultRel)
3892 {
3893 /* Convert any Vars in it to contain the root's attno's */
3894 part_attmap =
3896 RelationGetDescr(firstResultRel),
3897 false);
3898
3899 wcoList = (List *)
3900 map_variable_attnos((Node *) wcoList,
3901 firstVarno, 0,
3902 part_attmap,
3903 RelationGetForm(rootRelation)->reltype,
3904 &found_whole_row);
3905 }
3906
3907 foreach(lc, wcoList)
3908 {
3910 ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
3911 &mtstate->ps);
3912
3913 wcoExprs = lappend(wcoExprs, wcoExpr);
3914 }
3915
3916 rootRelInfo->ri_WithCheckOptions = wcoList;
3917 rootRelInfo->ri_WithCheckOptionExprs = wcoExprs;
3918 }
3919
3920 if (node->returningLists != NIL)
3921 {
3922 List *returningList;
3923
3924 /* There should be as many returning lists as result rels */
3927
3928 /*
3929 * Use the first returning list as a reference. In the most common
3930 * case, this will be for the same relation as rootRelInfo, and so
3931 * there will be no need to adjust its attno's.
3932 */
3933 returningList = linitial(node->returningLists);
3934 if (rootRelation != firstResultRel)
3935 {
3936 /* Convert any Vars in it to contain the root's attno's */
3937 if (part_attmap == NULL)
3938 part_attmap =
3940 RelationGetDescr(firstResultRel),
3941 false);
3942
3943 returningList = (List *)
3944 map_variable_attnos((Node *) returningList,
3945 firstVarno, 0,
3946 part_attmap,
3947 RelationGetForm(rootRelation)->reltype,
3948 &found_whole_row);
3949 }
3950 rootRelInfo->ri_returningList = returningList;
3951
3952 /* Initialize the RETURNING projection */
3953 rootRelInfo->ri_projectReturning =
3954 ExecBuildProjectionInfo(returningList, econtext,
3955 mtstate->ps.ps_ResultTupleSlot,
3956 &mtstate->ps,
3957 RelationGetDescr(rootRelation));
3958 }
3959 }
3960}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:229
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:547
#define MERGE_UPDATE
Definition: execnodes.h:1394
#define MERGE_INSERT
Definition: execnodes.h:1393
#define MERGE_DELETE
Definition: execnodes.h:1395
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void ExecInitMergeTupleSlots(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
@ CMD_INSERT
Definition: nodes.h:277
@ CMD_NOTHING
Definition: nodes.h:282
#define makeNode(_type_)
Definition: nodes.h:161
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define RelationGetForm(relation)
Definition: rel.h:509
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Definition: attmap.h:35
MergeAction * mas_action
Definition: execnodes.h:449
ProjectionInfo * mas_proj
Definition: execnodes.h:450
ExprState * mas_whenqual
Definition: execnodes.h:452
List * mt_mergeJoinConditions
Definition: execnodes.h:1472
int mt_merge_subcommands
Definition: execnodes.h:1448
List * mt_mergeActionLists
Definition: execnodes.h:1471
List * resultRelations
Definition: plannodes.h:342
List * returningLists
Definition: plannodes.h:352
List * withCheckOptionLists
Definition: plannodes.h:346
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1203
Form_pg_class rd_rel
Definition: rel.h:111
ExprState * ri_MergeJoinCondition
Definition: execnodes.h:589
List * ri_MergeActions[NUM_MERGE_MATCH_KINDS]
Definition: execnodes.h:586
List * ri_WithCheckOptionExprs
Definition: execnodes.h:552
List * ri_returningList
Definition: execnodes.h:574

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

Referenced by ExecInitModifyTable().

◆ ExecInitMergeTupleSlots()

void ExecInitMergeTupleSlots ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)

Definition at line 3969 of file nodeModifyTable.c.

3971{
3972 EState *estate = mtstate->ps.state;
3973
3974 Assert(!resultRelInfo->ri_projectNewInfoValid);
3975
3976 resultRelInfo->ri_oldTupleSlot =
3977 table_slot_create(resultRelInfo->ri_RelationDesc,
3978 &estate->es_tupleTable);
3979 resultRelInfo->ri_newTupleSlot =
3980 table_slot_create(resultRelInfo->ri_RelationDesc,
3981 &estate->es_tupleTable);
3982 resultRelInfo->ri_projectNewInfoValid = true;
3983}

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

4638{
4639 ModifyTableState *mtstate;
4640 Plan *subplan = outerPlan(node);
4641 CmdType operation = node->operation;
4642 int total_nrels = list_length(node->resultRelations);
4643 int nrels;
4644 List *resultRelations = NIL;
4645 List *withCheckOptionLists = NIL;
4646 List *returningLists = NIL;
4647 List *updateColnosLists = NIL;
4648 List *mergeActionLists = NIL;
4649 List *mergeJoinConditions = NIL;
4650 ResultRelInfo *resultRelInfo;
4651 List *arowmarks;
4652 ListCell *l;
4653 int i;
4654 Relation rel;
4655
4656 /* check for unsupported flags */
4657 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
4658
4659 /*
4660 * Only consider unpruned relations for initializing their ResultRelInfo
4661 * struct and other fields such as withCheckOptions, etc.
4662 *
4663 * Note: We must avoid pruning every result relation. This is important
4664 * for MERGE, since even if every result relation is pruned from the
4665 * subplan, there might still be NOT MATCHED rows, for which there may be
4666 * INSERT actions to perform. To allow these actions to be found, at
4667 * least one result relation must be kept. Also, when inserting into a
4668 * partitioned table, ExecInitPartitionInfo() needs a ResultRelInfo struct
4669 * as a reference for building the ResultRelInfo of the target partition.
4670 * In either case, it doesn't matter which result relation is kept, so we
4671 * just keep the first one, if all others have been pruned. See also,
4672 * ExecDoInitialPruning(), which ensures that this first result relation
4673 * has been locked.
4674 */
4675 i = 0;
4676 foreach(l, node->resultRelations)
4677 {
4678 Index rti = lfirst_int(l);
4679 bool keep_rel;
4680
4681 keep_rel = bms_is_member(rti, estate->es_unpruned_relids);
4682 if (!keep_rel && i == total_nrels - 1 && resultRelations == NIL)
4683 {
4684 /* all result relations pruned; keep the first one */
4685 keep_rel = true;
4686 rti = linitial_int(node->resultRelations);
4687 i = 0;
4688 }
4689
4690 if (keep_rel)
4691 {
4692 resultRelations = lappend_int(resultRelations, rti);
4693 if (node->withCheckOptionLists)
4694 {
4695 List *withCheckOptions = list_nth_node(List,
4697 i);
4698
4699 withCheckOptionLists = lappend(withCheckOptionLists, withCheckOptions);
4700 }
4701 if (node->returningLists)
4702 {
4703 List *returningList = list_nth_node(List,
4704 node->returningLists,
4705 i);
4706
4707 returningLists = lappend(returningLists, returningList);
4708 }
4709 if (node->updateColnosLists)
4710 {
4711 List *updateColnosList = list_nth(node->updateColnosLists, i);
4712
4713 updateColnosLists = lappend(updateColnosLists, updateColnosList);
4714 }
4715 if (node->mergeActionLists)
4716 {
4717 List *mergeActionList = list_nth(node->mergeActionLists, i);
4718
4719 mergeActionLists = lappend(mergeActionLists, mergeActionList);
4720 }
4721 if (node->mergeJoinConditions)
4722 {
4723 List *mergeJoinCondition = list_nth(node->mergeJoinConditions, i);
4724
4725 mergeJoinConditions = lappend(mergeJoinConditions, mergeJoinCondition);
4726 }
4727 }
4728 i++;
4729 }
4730 nrels = list_length(resultRelations);
4731 Assert(nrels > 0);
4732
4733 /*
4734 * create state structure
4735 */
4736 mtstate = makeNode(ModifyTableState);
4737 mtstate->ps.plan = (Plan *) node;
4738 mtstate->ps.state = estate;
4739 mtstate->ps.ExecProcNode = ExecModifyTable;
4740
4741 mtstate->operation = operation;
4742 mtstate->canSetTag = node->canSetTag;
4743 mtstate->mt_done = false;
4744
4745 mtstate->mt_nrels = nrels;
4746 mtstate->resultRelInfo = palloc_array(ResultRelInfo, nrels);
4747
4748 mtstate->mt_merge_pending_not_matched = NULL;
4749 mtstate->mt_merge_inserted = 0;
4750 mtstate->mt_merge_updated = 0;
4751 mtstate->mt_merge_deleted = 0;
4752 mtstate->mt_updateColnosLists = updateColnosLists;
4753 mtstate->mt_mergeActionLists = mergeActionLists;
4754 mtstate->mt_mergeJoinConditions = mergeJoinConditions;
4755
4756 /*----------
4757 * Resolve the target relation. This is the same as:
4758 *
4759 * - the relation for which we will fire FOR STATEMENT triggers,
4760 * - the relation into whose tuple format all captured transition tuples
4761 * must be converted, and
4762 * - the root partitioned table used for tuple routing.
4763 *
4764 * If it's a partitioned or inherited table, the root partition or
4765 * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
4766 * given explicitly in node->rootRelation. Otherwise, the target relation
4767 * is the sole relation in the node->resultRelations list and, since it can
4768 * never be pruned, also in the resultRelations list constructed above.
4769 *----------
4770 */
4771 if (node->rootRelation > 0)
4772 {
4776 node->rootRelation);
4777 }
4778 else
4779 {
4780 Assert(list_length(node->resultRelations) == 1);
4781 Assert(list_length(resultRelations) == 1);
4782 mtstate->rootResultRelInfo = mtstate->resultRelInfo;
4783 ExecInitResultRelation(estate, mtstate->resultRelInfo,
4784 linitial_int(resultRelations));
4785 }
4786
4787 /* set up epqstate with dummy subplan data for the moment */
4788 EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
4789 node->epqParam, resultRelations);
4790 mtstate->fireBSTriggers = true;
4791
4792 /*
4793 * Build state for collecting transition tuples. This requires having a
4794 * valid trigger query context, so skip it in explain-only mode.
4795 */
4796 if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
4797 ExecSetupTransitionCaptureState(mtstate, estate);
4798
4799 /*
4800 * Open all the result relations and initialize the ResultRelInfo structs.
4801 * (But root relation was initialized above, if it's part of the array.)
4802 * We must do this before initializing the subplan, because direct-modify
4803 * FDWs expect their ResultRelInfos to be available.
4804 */
4805 resultRelInfo = mtstate->resultRelInfo;
4806 i = 0;
4807 foreach(l, resultRelations)
4808 {
4809 Index resultRelation = lfirst_int(l);
4810 List *mergeActions = NIL;
4811
4812 if (mergeActionLists)
4813 mergeActions = list_nth(mergeActionLists, i);
4814
4815 if (resultRelInfo != mtstate->rootResultRelInfo)
4816 {
4817 ExecInitResultRelation(estate, resultRelInfo, resultRelation);
4818
4819 /*
4820 * For child result relations, store the root result relation
4821 * pointer. We do so for the convenience of places that want to
4822 * look at the query's original target relation but don't have the
4823 * mtstate handy.
4824 */
4825 resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
4826 }
4827
4828 /* Initialize the usesFdwDirectModify flag */
4829 resultRelInfo->ri_usesFdwDirectModify =
4831
4832 /*
4833 * Verify result relation is a valid target for the current operation
4834 */
4835 CheckValidResultRel(resultRelInfo, operation, node->onConflictAction,
4836 mergeActions);
4837
4838 resultRelInfo++;
4839 i++;
4840 }
4841
4842 /*
4843 * Now we may initialize the subplan.
4844 */
4845 outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
4846
4847 /*
4848 * Do additional per-result-relation initialization.
4849 */
4850 for (i = 0; i < nrels; i++)
4851 {
4852 resultRelInfo = &mtstate->resultRelInfo[i];
4853
4854 /* Let FDWs init themselves for foreign-table result rels */
4855 if (!resultRelInfo->ri_usesFdwDirectModify &&
4856 resultRelInfo->ri_FdwRoutine != NULL &&
4857 resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
4858 {
4859 List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
4860
4861 resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
4862 resultRelInfo,
4863 fdw_private,
4864 i,
4865 eflags);
4866 }
4867
4868 /*
4869 * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
4870 * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
4871 * tables, the FDW might have created additional junk attr(s), but
4872 * those are no concern of ours.
4873 */
4874 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4875 operation == CMD_MERGE)
4876 {
4877 char relkind;
4878
4879 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4880 if (relkind == RELKIND_RELATION ||
4881 relkind == RELKIND_MATVIEW ||
4882 relkind == RELKIND_PARTITIONED_TABLE)
4883 {
4884 resultRelInfo->ri_RowIdAttNo =
4885 ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
4886 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4887 elog(ERROR, "could not find junk ctid column");
4888 }
4889 else if (relkind == RELKIND_FOREIGN_TABLE)
4890 {
4891 /*
4892 * We don't support MERGE with foreign tables for now. (It's
4893 * problematic because the implementation uses CTID.)
4894 */
4895 Assert(operation != CMD_MERGE);
4896
4897 /*
4898 * When there is a row-level trigger, there should be a
4899 * wholerow attribute. We also require it to be present in
4900 * UPDATE and MERGE, so we can get the values of unchanged
4901 * columns.
4902 */
4903 resultRelInfo->ri_RowIdAttNo =
4905 "wholerow");
4906 if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
4907 !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4908 elog(ERROR, "could not find junk wholerow column");
4909 }
4910 else
4911 {
4912 /* Other valid target relkinds must provide wholerow */
4913 resultRelInfo->ri_RowIdAttNo =
4915 "wholerow");
4916 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4917 elog(ERROR, "could not find junk wholerow column");
4918 }
4919 }
4920 }
4921
4922 /*
4923 * If this is an inherited update/delete/merge, there will be a junk
4924 * attribute named "tableoid" present in the subplan's targetlist. It
4925 * will be used to identify the result relation for a given tuple to be
4926 * updated/deleted/merged.
4927 */
4928 mtstate->mt_resultOidAttno =
4929 ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
4930 Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || total_nrels == 1);
4931 mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
4932 mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
4933
4934 /* Get the root target relation */
4935 rel = mtstate->rootResultRelInfo->ri_RelationDesc;
4936
4937 /*
4938 * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
4939 * or MERGE might need this too, but only if it actually moves tuples
4940 * between partitions; in that case setup is done by
4941 * ExecCrossPartitionUpdate.
4942 */
4943 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
4944 operation == CMD_INSERT)
4946 ExecSetupPartitionTupleRouting(estate, rel);
4947
4948 /*
4949 * Initialize any WITH CHECK OPTION constraints if needed.
4950 */
4951 resultRelInfo = mtstate->resultRelInfo;
4952 foreach(l, withCheckOptionLists)
4953 {
4954 List *wcoList = (List *) lfirst(l);
4955 List *wcoExprs = NIL;
4956 ListCell *ll;
4957
4958 foreach(ll, wcoList)
4959 {
4960 WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
4961 ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
4962 &mtstate->ps);
4963
4964 wcoExprs = lappend(wcoExprs, wcoExpr);
4965 }
4966
4967 resultRelInfo->ri_WithCheckOptions = wcoList;
4968 resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4969 resultRelInfo++;
4970 }
4971
4972 /*
4973 * Initialize RETURNING projections if needed.
4974 */
4975 if (returningLists)
4976 {
4977 TupleTableSlot *slot;
4978 ExprContext *econtext;
4979
4980 /*
4981 * Initialize result tuple slot and assign its rowtype using the plan
4982 * node's declared targetlist, which the planner set up to be the same
4983 * as the first (before runtime pruning) RETURNING list. We assume
4984 * all the result rels will produce compatible output.
4985 */
4987 slot = mtstate->ps.ps_ResultTupleSlot;
4988
4989 /* Need an econtext too */
4990 if (mtstate->ps.ps_ExprContext == NULL)
4991 ExecAssignExprContext(estate, &mtstate->ps);
4992 econtext = mtstate->ps.ps_ExprContext;
4993
4994 /*
4995 * Build a projection for each result rel.
4996 */
4997 resultRelInfo = mtstate->resultRelInfo;
4998 foreach(l, returningLists)
4999 {
5000 List *rlist = (List *) lfirst(l);
5001
5002 resultRelInfo->ri_returningList = rlist;
5003 resultRelInfo->ri_projectReturning =
5004 ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
5005 resultRelInfo->ri_RelationDesc->rd_att);
5006 resultRelInfo++;
5007 }
5008 }
5009 else
5010 {
5011 /*
5012 * We still must construct a dummy result tuple type, because InitPlan
5013 * expects one (maybe should change that?).
5014 */
5015 ExecInitResultTypeTL(&mtstate->ps);
5016
5017 mtstate->ps.ps_ExprContext = NULL;
5018 }
5019
5020 /* Set the list of arbiter indexes if needed for ON CONFLICT */
5021 resultRelInfo = mtstate->resultRelInfo;
5022 if (node->onConflictAction != ONCONFLICT_NONE)
5023 {
5024 /* insert may only have one relation, inheritance is not expanded */
5025 Assert(total_nrels == 1);
5026 resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
5027 }
5028
5029 /*
5030 * If needed, Initialize target list, projection and qual for ON CONFLICT
5031 * DO UPDATE.
5032 */
5034 {
5036 ExprContext *econtext;
5037 TupleDesc relationDesc;
5038
5039 /* already exists if created by RETURNING processing above */
5040 if (mtstate->ps.ps_ExprContext == NULL)
5041 ExecAssignExprContext(estate, &mtstate->ps);
5042
5043 econtext = mtstate->ps.ps_ExprContext;
5044 relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
5045
5046 /* create state for DO UPDATE SET operation */
5047 resultRelInfo->ri_onConflict = onconfl;
5048
5049 /* initialize slot for the existing tuple */
5050 onconfl->oc_Existing =
5051 table_slot_create(resultRelInfo->ri_RelationDesc,
5052 &mtstate->ps.state->es_tupleTable);
5053
5054 /*
5055 * Create the tuple slot for the UPDATE SET projection. We want a slot
5056 * of the table's type here, because the slot will be used to insert
5057 * into the table, and for RETURNING processing - which may access
5058 * system attributes.
5059 */
5060 onconfl->oc_ProjSlot =
5061 table_slot_create(resultRelInfo->ri_RelationDesc,
5062 &mtstate->ps.state->es_tupleTable);
5063
5064 /* build UPDATE SET projection state */
5065 onconfl->oc_ProjInfo =
5067 true,
5068 node->onConflictCols,
5069 relationDesc,
5070 econtext,
5071 onconfl->oc_ProjSlot,
5072 &mtstate->ps);
5073
5074 /* initialize state to evaluate the WHERE clause, if any */
5075 if (node->onConflictWhere)
5076 {
5077 ExprState *qualexpr;
5078
5079 qualexpr = ExecInitQual((List *) node->onConflictWhere,
5080 &mtstate->ps);
5081 onconfl->oc_WhereClause = qualexpr;
5082 }
5083 }
5084
5085 /*
5086 * If we have any secondary relations in an UPDATE or DELETE, they need to
5087 * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
5088 * EvalPlanQual mechanism needs to be told about them. This also goes for
5089 * the source relations in a MERGE. Locate the relevant ExecRowMarks.
5090 */
5091 arowmarks = NIL;
5092 foreach(l, node->rowMarks)
5093 {
5095 ExecRowMark *erm;
5096 ExecAuxRowMark *aerm;
5097
5098 /*
5099 * Ignore "parent" rowmarks, because they are irrelevant at runtime.
5100 * Also ignore the rowmarks belonging to child tables that have been
5101 * pruned in ExecDoInitialPruning().
5102 */
5103 if (rc->isParent ||
5104 !bms_is_member(rc->rti, estate->es_unpruned_relids))
5105 continue;
5106
5107 /* Find ExecRowMark and build ExecAuxRowMark */
5108 erm = ExecFindRowMark(estate, rc->rti, false);
5109 aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
5110 arowmarks = lappend(arowmarks, aerm);
5111 }
5112
5113 /* For a MERGE command, initialize its state */
5114 if (mtstate->operation == CMD_MERGE)
5115 ExecInitMerge(mtstate, estate);
5116
5117 EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
5118
5119 /*
5120 * If there are a lot of result relations, use a hash table to speed the
5121 * lookups. If there are not a lot, a simple linear search is faster.
5122 *
5123 * It's not clear where the threshold is, but try 64 for starters. In a
5124 * debugging build, use a small threshold so that we get some test
5125 * coverage of both code paths.
5126 */
5127#ifdef USE_ASSERT_CHECKING
5128#define MT_NRELS_HASH 4
5129#else
5130#define MT_NRELS_HASH 64
5131#endif
5132 if (nrels >= MT_NRELS_HASH)
5133 {
5134 HASHCTL hash_ctl;
5135
5136 hash_ctl.keysize = sizeof(Oid);
5137 hash_ctl.entrysize = sizeof(MTTargetRelLookup);
5138 hash_ctl.hcxt = CurrentMemoryContext;
5139 mtstate->mt_resultOidHash =
5140 hash_create("ModifyTable target hash",
5141 nrels, &hash_ctl,
5143 for (i = 0; i < nrels; i++)
5144 {
5145 Oid hashkey;
5146 MTTargetRelLookup *mtlookup;
5147 bool found;
5148
5149 resultRelInfo = &mtstate->resultRelInfo[i];
5150 hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
5151 mtlookup = (MTTargetRelLookup *)
5152 hash_search(mtstate->mt_resultOidHash, &hashkey,
5153 HASH_ENTER, &found);
5154 Assert(!found);
5155 mtlookup->relationIndex = i;
5156 }
5157 }
5158 else
5159 mtstate->mt_resultOidHash = NULL;
5160
5161 /*
5162 * Determine if the FDW supports batch insert and determine the batch size
5163 * (a FDW may support batching, but it may be disabled for the
5164 * server/table).
5165 *
5166 * We only do this for INSERT, so that for UPDATE/DELETE the batch size
5167 * remains set to 0.
5168 */
5169 if (operation == CMD_INSERT)
5170 {
5171 /* insert may only have one relation, inheritance is not expanded */
5172 Assert(total_nrels == 1);
5173 resultRelInfo = mtstate->resultRelInfo;
5174 if (!resultRelInfo->ri_usesFdwDirectModify &&
5175 resultRelInfo->ri_FdwRoutine != NULL &&
5176 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
5177 resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
5178 {
5179 resultRelInfo->ri_BatchSize =
5180 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
5181 Assert(resultRelInfo->ri_BatchSize >= 1);
5182 }
5183 else
5184 resultRelInfo->ri_BatchSize = 1;
5185 }
5186
5187 /*
5188 * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
5189 * to estate->es_auxmodifytables so that it will be run to completion by
5190 * ExecPostprocessPlan. (It'd actually work fine to add the primary
5191 * ModifyTable node too, but there's no need.) Note the use of lcons not
5192 * lappend: we need later-initialized ModifyTable nodes to be shut down
5193 * before earlier ones. This ensures that we don't throw away RETURNING
5194 * rows that need to be seen by a later CTE subplan.
5195 */
5196 if (!mtstate->canSetTag)
5197 estate->es_auxmodifytables = lcons(mtstate,
5198 estate->es_auxmodifytables);
5199
5200 return mtstate;
5201}
#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:633
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:952
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:358
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2556
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2579
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, OnConflictAction onConflictAction, List *mergeActions)
Definition: execMain.c:1050
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam, List *resultRelations)
Definition: execMain.c:2718
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2759
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1944
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1988
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:880
#define EXEC_FLAG_BACKWARD
Definition: executor.h:69
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:66
#define EXEC_FLAG_MARK
Definition: executor.h:70
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
List * lappend_int(List *list, int datum)
Definition: list.c:357
List * lcons(void *datum, List *list)
Definition: list.c:495
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
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:428
CmdType
Definition: nodes.h:273
#define lfirst_int(lc)
Definition: pg_list.h:173
#define linitial_int(l)
Definition: pg_list.h:179
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
Bitmapset * es_unpruned_relids
Definition: execnodes.h:673
List * es_auxmodifytables
Definition: execnodes.h:727
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:1458
double mt_merge_deleted
Definition: execnodes.h:1463
List * mt_updateColnosLists
Definition: execnodes.h:1470
double mt_merge_inserted
Definition: execnodes.h:1461
double mt_merge_updated
Definition: execnodes.h:1462
HTAB * mt_resultOidHash
Definition: execnodes.h:1430
List * updateColnosLists
Definition: plannodes.h:344
List * arbiterIndexes
Definition: plannodes.h:364
List * onConflictCols
Definition: plannodes.h:368
List * mergeJoinConditions
Definition: plannodes.h:378
CmdType operation
Definition: plannodes.h:334
int epqParam
Definition: plannodes.h:360
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:356
List * onConflictSet
Definition: plannodes.h:366
List * mergeActionLists
Definition: plannodes.h:376
bool canSetTag
Definition: plannodes.h:336
List * fdwPrivLists
Definition: plannodes.h:354
Index rootRelation
Definition: plannodes.h:340
Node * onConflictWhere
Definition: plannodes.h:370
List * rowMarks
Definition: plannodes.h:358
OnConflictAction onConflictAction
Definition: plannodes.h:362
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:434
TupleTableSlot * oc_Existing
Definition: execnodes.h:433
ExprState * oc_WhereClause
Definition: execnodes.h:436
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:435
bool isParent
Definition: plannodes.h:1604
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1171
List * targetlist
Definition: plannodes.h:229
TupleDesc rd_att
Definition: rel.h:112
OnConflictSetState * ri_onConflict
Definition: execnodes.h:583
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:580
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:495
int ri_BatchSize
Definition: execnodes.h:544

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_int, list_length(), list_nth(), list_nth_node, makeNode, ModifyTable::mergeActionLists, ModifyTable::mergeJoinConditions, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, ModifyTableState::mt_merge_pending_not_matched, ModifyTableState::mt_merge_updated, ModifyTableState::mt_mergeActionLists, ModifyTableState::mt_mergeJoinConditions, ModifyTableState::mt_nrels, MT_NRELS_HASH, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_resultOidAttno, ModifyTableState::mt_resultOidHash, ModifyTableState::mt_updateColnosLists, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTableState::operation, ModifyTable::operation, outerPlan, outerPlanState, palloc_array, 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 711 of file nodeModifyTable.c.

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

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

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

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(), INJECTION_POINT, InstrCountTuples2, ItemPointerCopy(), ItemPointerSetInvalid(), lappend(), list_free(), list_member_ptr(), MakeSingleTupleTableSlot(), MergeActionState::mas_action, MemoryContextSwitchTo(), ModifyTableState::mt_merge_action, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, palloc_array, 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 4588 of file nodeModifyTable.c.

4590{
4591 if (node->mt_resultOidHash)
4592 {
4593 /* Use the pre-built hash table to locate the rel */
4594 MTTargetRelLookup *mtlookup;
4595
4596 mtlookup = (MTTargetRelLookup *)
4597 hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
4598 if (mtlookup)
4599 {
4600 if (update_cache)
4601 {
4602 node->mt_lastResultOid = resultoid;
4603 node->mt_lastResultIndex = mtlookup->relationIndex;
4604 }
4605 return node->resultRelInfo + mtlookup->relationIndex;
4606 }
4607 }
4608 else
4609 {
4610 /* With few target rels, just search the ResultRelInfo array */
4611 for (int ndx = 0; ndx < node->mt_nrels; ndx++)
4612 {
4613 ResultRelInfo *rInfo = node->resultRelInfo + ndx;
4614
4615 if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
4616 {
4617 if (update_cache)
4618 {
4619 node->mt_lastResultOid = resultoid;
4620 node->mt_lastResultIndex = ndx;
4621 }
4622 return rInfo;
4623 }
4624 }
4625 }
4626
4627 if (!missing_ok)
4628 elog(ERROR, "incorrect result relation OID %u", resultoid);
4629 return NULL;
4630}
@ 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 2940 of file nodeModifyTable.c.

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

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

References Assert(), TM_FailureData::cmax, CMD_DELETE, CMD_NOTHING, CMD_UPDATE, MergeAction::commandType, ModifyTableContext::cpUpdateReturningSlot, UpdateContext::crossPartUpdate, 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, InstrUpdateTupleCount(), 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, MERGE_WHEN_NOT_MATCHED_BY_TARGET, ModifyTableState::mt_epqstate, ModifyTableState::mt_merge_action, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_updated, ModifyTableContext::mtstate, NIL, outerPlanState, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, ResultRelInfo::ri_MergeActions, ResultRelInfo::ri_MergeJoinCondition, ResultRelInfo::ri_needLockTagTuple, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, SnapshotAny, PlanState::state, table_tuple_fetch_row_version(), table_tuple_lock(), TM_BeingModified, TM_Deleted, TM_Invisible, TM_Ok, TM_SelfModified, TM_Updated, TM_WouldBlock, ModifyTableContext::tmfd, TransactionIdIsCurrentTransactionId(), TriggerDesc::trig_delete_instead_row, TriggerDesc::trig_update_instead_row, TupIsNull, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, UnlockTuple(), WCO_RLS_MERGE_DELETE_CHECK, WCO_RLS_MERGE_UPDATE_CHECK, and TM_FailureData::xmax.

Referenced by ExecMerge().

◆ ExecMergeNotMatched()

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

Definition at line 3606 of file nodeModifyTable.c.

3608{
3609 ModifyTableState *mtstate = context->mtstate;
3610 ExprContext *econtext = mtstate->ps.ps_ExprContext;
3611 List *actionStates;
3612 TupleTableSlot *rslot = NULL;
3613 ListCell *l;
3614
3615 /*
3616 * For INSERT actions, the root relation's merge action is OK since the
3617 * INSERT's targetlist and the WHEN conditions can only refer to the
3618 * source relation and hence it does not matter which result relation we
3619 * work with.
3620 *
3621 * XXX does this mean that we can avoid creating copies of actionStates on
3622 * partitioned tables, for not-matched actions?
3623 */
3624 actionStates = resultRelInfo->ri_MergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET];
3625
3626 /*
3627 * Make source tuple available to ExecQual and ExecProject. We don't need
3628 * the target tuple, since the WHEN quals and targetlist can't refer to
3629 * the target columns.
3630 */
3631 econtext->ecxt_scantuple = NULL;
3632 econtext->ecxt_innertuple = context->planSlot;
3633 econtext->ecxt_outertuple = NULL;
3634
3635 foreach(l, actionStates)
3636 {
3638 CmdType commandType = action->mas_action->commandType;
3639 TupleTableSlot *newslot;
3640
3641 /*
3642 * Test condition, if any.
3643 *
3644 * In the absence of any condition, we perform the action
3645 * unconditionally (no need to check separately since ExecQual() will
3646 * return true if there are no conditions to evaluate).
3647 */
3648 if (!ExecQual(action->mas_whenqual, econtext))
3649 continue;
3650
3651 /* Perform stated action */
3652 switch (commandType)
3653 {
3654 case CMD_INSERT:
3655
3656 /*
3657 * Project the tuple. In case of a partitioned table, the
3658 * projection was already built to use the root's descriptor,
3659 * so we don't need to map the tuple here.
3660 */
3661 newslot = ExecProject(action->mas_proj);
3662 mtstate->mt_merge_action = action;
3663
3664 rslot = ExecInsert(context, mtstate->rootResultRelInfo,
3665 newslot, canSetTag, NULL, NULL);
3666 mtstate->mt_merge_inserted += 1;
3667 break;
3668 case CMD_NOTHING:
3669 /* Do nothing */
3670 break;
3671 default:
3672 elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
3673 }
3674
3675 /*
3676 * We've activated one of the WHEN clauses, so we don't search
3677 * further. This is required behaviour, not an optimization.
3678 */
3679 break;
3680 }
3681
3682 return rslot;
3683}

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

4161{
4163 ModifyTableContext context;
4164 EState *estate = node->ps.state;
4165 CmdType operation = node->operation;
4166 ResultRelInfo *resultRelInfo;
4167 PlanState *subplanstate;
4168 TupleTableSlot *slot;
4169 TupleTableSlot *oldSlot;
4170 ItemPointerData tuple_ctid;
4171 HeapTupleData oldtupdata;
4172 HeapTuple oldtuple;
4173 ItemPointer tupleid;
4174 bool tuplock;
4175
4177
4178 /*
4179 * This should NOT get called during EvalPlanQual; we should have passed a
4180 * subplan tree to EvalPlanQual, instead. Use a runtime test not just
4181 * Assert because this condition is easy to miss in testing. (Note:
4182 * although ModifyTable should not get executed within an EvalPlanQual
4183 * operation, we do have to allow it to be initialized and shut down in
4184 * case it is within a CTE subplan. Hence this test must be here, not in
4185 * ExecInitModifyTable.)
4186 */
4187 if (estate->es_epq_active != NULL)
4188 elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
4189
4190 /*
4191 * If we've already completed processing, don't try to do more. We need
4192 * this test because ExecPostprocessPlan might call us an extra time, and
4193 * our subplan's nodes aren't necessarily robust against being called
4194 * extra times.
4195 */
4196 if (node->mt_done)
4197 return NULL;
4198
4199 /*
4200 * On first call, fire BEFORE STATEMENT triggers before proceeding.
4201 */
4202 if (node->fireBSTriggers)
4203 {
4204 fireBSTriggers(node);
4205 node->fireBSTriggers = false;
4206 }
4207
4208 /* Preload local variables */
4209 resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
4210 subplanstate = outerPlanState(node);
4211
4212 /* Set global context */
4213 context.mtstate = node;
4214 context.epqstate = &node->mt_epqstate;
4215 context.estate = estate;
4216
4217 /*
4218 * Fetch rows from subplan, and execute the required table modification
4219 * for each row.
4220 */
4221 for (;;)
4222 {
4223 /*
4224 * Reset the per-output-tuple exprcontext. This is needed because
4225 * triggers expect to use that context as workspace. It's a bit ugly
4226 * to do this below the top level of the plan, however. We might need
4227 * to rethink this later.
4228 */
4230
4231 /*
4232 * Reset per-tuple memory context used for processing on conflict and
4233 * returning clauses, to free any expression evaluation storage
4234 * allocated in the previous cycle.
4235 */
4236 if (pstate->ps_ExprContext)
4238
4239 /*
4240 * If there is a pending MERGE ... WHEN NOT MATCHED [BY TARGET] action
4241 * to execute, do so now --- see the comments in ExecMerge().
4242 */
4243 if (node->mt_merge_pending_not_matched != NULL)
4244 {
4245 context.planSlot = node->mt_merge_pending_not_matched;
4246 context.cpDeletedSlot = NULL;
4247
4248 slot = ExecMergeNotMatched(&context, node->resultRelInfo,
4249 node->canSetTag);
4250
4251 /* Clear the pending action */
4252 node->mt_merge_pending_not_matched = NULL;
4253
4254 /*
4255 * If we got a RETURNING result, return it to the caller. We'll
4256 * continue the work on next call.
4257 */
4258 if (slot)
4259 return slot;
4260
4261 continue; /* continue with the next tuple */
4262 }
4263
4264 /* Fetch the next row from subplan */
4265 context.planSlot = ExecProcNode(subplanstate);
4266 context.cpDeletedSlot = NULL;
4267
4268 /* No more tuples to process? */
4269 if (TupIsNull(context.planSlot))
4270 break;
4271
4272 /*
4273 * When there are multiple result relations, each tuple contains a
4274 * junk column that gives the OID of the rel from which it came.
4275 * Extract it and select the correct result relation.
4276 */
4278 {
4279 Datum datum;
4280 bool isNull;
4281 Oid resultoid;
4282
4283 datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
4284 &isNull);
4285 if (isNull)
4286 {
4287 /*
4288 * For commands other than MERGE, any tuples having InvalidOid
4289 * for tableoid are errors. For MERGE, we may need to handle
4290 * them as WHEN NOT MATCHED clauses if any, so do that.
4291 *
4292 * Note that we use the node's toplevel resultRelInfo, not any
4293 * specific partition's.
4294 */
4295 if (operation == CMD_MERGE)
4296 {
4297 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4298
4299 slot = ExecMerge(&context, node->resultRelInfo,
4300 NULL, NULL, node->canSetTag);
4301
4302 /*
4303 * If we got a RETURNING result, return it to the caller.
4304 * We'll continue the work on next call.
4305 */
4306 if (slot)
4307 return slot;
4308
4309 continue; /* continue with the next tuple */
4310 }
4311
4312 elog(ERROR, "tableoid is NULL");
4313 }
4314 resultoid = DatumGetObjectId(datum);
4315
4316 /* If it's not the same as last time, we need to locate the rel */
4317 if (resultoid != node->mt_lastResultOid)
4318 resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
4319 false, true);
4320 }
4321
4322 /*
4323 * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
4324 * here is compute the RETURNING expressions.
4325 */
4326 if (resultRelInfo->ri_usesFdwDirectModify)
4327 {
4328 Assert(resultRelInfo->ri_projectReturning);
4329
4330 /*
4331 * A scan slot containing the data that was actually inserted,
4332 * updated or deleted has already been made available to
4333 * ExecProcessReturning by IterateDirectModify, so no need to
4334 * provide it here. The individual old and new slots are not
4335 * needed, since direct-modify is disabled if the RETURNING list
4336 * refers to OLD/NEW values.
4337 */
4338 Assert((resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD) == 0 &&
4339 (resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW) == 0);
4340
4341 slot = ExecProcessReturning(&context, resultRelInfo, operation,
4342 NULL, NULL, context.planSlot);
4343
4344 return slot;
4345 }
4346
4347 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4348 slot = context.planSlot;
4349
4350 tupleid = NULL;
4351 oldtuple = NULL;
4352
4353 /*
4354 * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
4355 * to be updated/deleted/merged. For a heap relation, that's a TID;
4356 * otherwise we may have a wholerow junk attr that carries the old
4357 * tuple in toto. Keep this in step with the part of
4358 * ExecInitModifyTable that sets up ri_RowIdAttNo.
4359 */
4360 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4361 operation == CMD_MERGE)
4362 {
4363 char relkind;
4364 Datum datum;
4365 bool isNull;
4366
4367 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4368 if (relkind == RELKIND_RELATION ||
4369 relkind == RELKIND_MATVIEW ||
4370 relkind == RELKIND_PARTITIONED_TABLE)
4371 {
4372 /* ri_RowIdAttNo refers to a ctid attribute */
4374 datum = ExecGetJunkAttribute(slot,
4375 resultRelInfo->ri_RowIdAttNo,
4376 &isNull);
4377
4378 /*
4379 * For commands other than MERGE, any tuples having a null row
4380 * identifier are errors. For MERGE, we may need to handle
4381 * them as WHEN NOT MATCHED clauses if any, so do that.
4382 *
4383 * Note that we use the node's toplevel resultRelInfo, not any
4384 * specific partition's.
4385 */
4386 if (isNull)
4387 {
4388 if (operation == CMD_MERGE)
4389 {
4390 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4391
4392 slot = ExecMerge(&context, node->resultRelInfo,
4393 NULL, NULL, node->canSetTag);
4394
4395 /*
4396 * If we got a RETURNING result, return it to the
4397 * caller. We'll continue the work on next call.
4398 */
4399 if (slot)
4400 return slot;
4401
4402 continue; /* continue with the next tuple */
4403 }
4404
4405 elog(ERROR, "ctid is NULL");
4406 }
4407
4408 tupleid = (ItemPointer) DatumGetPointer(datum);
4409 tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
4410 tupleid = &tuple_ctid;
4411 }
4412
4413 /*
4414 * Use the wholerow attribute, when available, to reconstruct the
4415 * old relation tuple. The old tuple serves one or both of two
4416 * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
4417 * provides values for any unchanged columns for the NEW tuple of
4418 * an UPDATE, because the subplan does not produce all the columns
4419 * of the target table.
4420 *
4421 * Note that the wholerow attribute does not carry system columns,
4422 * so foreign table triggers miss seeing those, except that we
4423 * know enough here to set t_tableOid. Quite separately from
4424 * this, the FDW may fetch its own junk attrs to identify the row.
4425 *
4426 * Other relevant relkinds, currently limited to views, always
4427 * have a wholerow attribute.
4428 */
4429 else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4430 {
4431 datum = ExecGetJunkAttribute(slot,
4432 resultRelInfo->ri_RowIdAttNo,
4433 &isNull);
4434
4435 /*
4436 * For commands other than MERGE, any tuples having a null row
4437 * identifier are errors. For MERGE, we may need to handle
4438 * them as WHEN NOT MATCHED clauses if any, so do that.
4439 *
4440 * Note that we use the node's toplevel resultRelInfo, not any
4441 * specific partition's.
4442 */
4443 if (isNull)
4444 {
4445 if (operation == CMD_MERGE)
4446 {
4447 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4448
4449 slot = ExecMerge(&context, node->resultRelInfo,
4450 NULL, NULL, node->canSetTag);
4451
4452 /*
4453 * If we got a RETURNING result, return it to the
4454 * caller. We'll continue the work on next call.
4455 */
4456 if (slot)
4457 return slot;
4458
4459 continue; /* continue with the next tuple */
4460 }
4461
4462 elog(ERROR, "wholerow is NULL");
4463 }
4464
4465 oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
4466 oldtupdata.t_len =
4468 ItemPointerSetInvalid(&(oldtupdata.t_self));
4469 /* Historically, view triggers see invalid t_tableOid. */
4470 oldtupdata.t_tableOid =
4471 (relkind == RELKIND_VIEW) ? InvalidOid :
4472 RelationGetRelid(resultRelInfo->ri_RelationDesc);
4473
4474 oldtuple = &oldtupdata;
4475 }
4476 else
4477 {
4478 /* Only foreign tables are allowed to omit a row-ID attr */
4479 Assert(relkind == RELKIND_FOREIGN_TABLE);
4480 }
4481 }
4482
4483 switch (operation)
4484 {
4485 case CMD_INSERT:
4486 /* Initialize projection info if first time for this table */
4487 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4488 ExecInitInsertProjection(node, resultRelInfo);
4489 slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
4490 slot = ExecInsert(&context, resultRelInfo, slot,
4491 node->canSetTag, NULL, NULL);
4492 break;
4493
4494 case CMD_UPDATE:
4495 tuplock = false;
4496
4497 /* Initialize projection info if first time for this table */
4498 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4499 ExecInitUpdateProjection(node, resultRelInfo);
4500
4501 /*
4502 * Make the new tuple by combining plan's output tuple with
4503 * the old tuple being updated.
4504 */
4505 oldSlot = resultRelInfo->ri_oldTupleSlot;
4506 if (oldtuple != NULL)
4507 {
4508 Assert(!resultRelInfo->ri_needLockTagTuple);
4509 /* Use the wholerow junk attr as the old tuple. */
4510 ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
4511 }
4512 else
4513 {
4514 /* Fetch the most recent version of old tuple. */
4515 Relation relation = resultRelInfo->ri_RelationDesc;
4516
4517 if (resultRelInfo->ri_needLockTagTuple)
4518 {
4519 LockTuple(relation, tupleid, InplaceUpdateTupleLock);
4520 tuplock = true;
4521 }
4522 if (!table_tuple_fetch_row_version(relation, tupleid,
4524 oldSlot))
4525 elog(ERROR, "failed to fetch tuple being updated");
4526 }
4527 slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
4528 oldSlot);
4529
4530 /* Now apply the update. */
4531 slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
4532 oldSlot, slot, node->canSetTag);
4533 if (tuplock)
4534 UnlockTuple(resultRelInfo->ri_RelationDesc, tupleid,
4536 break;
4537
4538 case CMD_DELETE:
4539 slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
4540 true, false, node->canSetTag, NULL, NULL, NULL);
4541 break;
4542
4543 case CMD_MERGE:
4544 slot = ExecMerge(&context, resultRelInfo, tupleid, oldtuple,
4545 node->canSetTag);
4546 break;
4547
4548 default:
4549 elog(ERROR, "unknown operation");
4550 break;
4551 }
4552
4553 /*
4554 * If we got a RETURNING result, return it to caller. We'll continue
4555 * the work on next call.
4556 */
4557 if (slot)
4558 return slot;
4559 }
4560
4561 /*
4562 * Insert remaining tuples for batch insert.
4563 */
4565 ExecPendingInserts(estate);
4566
4567 /*
4568 * We're done, but fire AFTER STATEMENT triggers before exiting.
4569 */
4570 fireASTriggers(node);
4571
4572 node->mt_done = true;
4573
4574 return NULL;
4575}
#define EEO_FLAG_HAS_NEW
Definition: execnodes.h:78
#define ResetPerTupleExprContext(estate)
Definition: executor.h:665
#define ResetExprContext(econtext)
Definition: executor.h:650
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:314
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:289
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h: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)
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:252
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
struct EPQState * es_epq_active
Definition: execnodes.h:742
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 2713 of file nodeModifyTable.c.

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

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

1428{
1429 ListCell *l1,
1430 *l2;
1431
1434 {
1435 ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l1);
1436 ModifyTableState *mtstate = (ModifyTableState *) lfirst(l2);
1437
1438 Assert(mtstate);
1439 ExecBatchInsert(mtstate, resultRelInfo,
1440 resultRelInfo->ri_Slots,
1441 resultRelInfo->ri_PlanSlots,
1442 resultRelInfo->ri_NumSlots,
1443 estate, mtstate->canSetTag);
1444 }
1445
1450}
#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 4100 of file nodeModifyTable.c.

4106{
4107 ResultRelInfo *partrel;
4108 TupleConversionMap *map;
4109
4110 /*
4111 * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
4112 * not find a valid partition for the tuple in 'slot' then an error is
4113 * raised. An error may also be raised if the found partition is not a
4114 * valid target for INSERTs. This is required since a partitioned table
4115 * UPDATE to another partition becomes a DELETE+INSERT.
4116 */
4117 partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
4118
4119 /*
4120 * If we're capturing transition tuples, we might need to convert from the
4121 * partition rowtype to root partitioned table's rowtype. But if there
4122 * are no BEFORE triggers on the partition that could change the tuple, we
4123 * can just remember the original unconverted tuple to avoid a needless
4124 * round trip conversion.
4125 */
4126 if (mtstate->mt_transition_capture != NULL)
4127 {
4128 bool has_before_insert_row_trig;
4129
4130 has_before_insert_row_trig = (partrel->ri_TrigDesc &&
4132
4134 !has_before_insert_row_trig ? slot : NULL;
4135 }
4136
4137 /*
4138 * Convert the tuple, if necessary.
4139 */
4140 map = ExecGetRootToChildMap(partrel, estate);
4141 if (map != NULL)
4142 {
4143 TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
4144
4145 slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
4146 }
4147
4148 *partRelInfo = partrel;
4149 return slot;
4150}
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:619

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

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

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

5267{
5268 /*
5269 * Currently, we don't need to support rescan on ModifyTable nodes. The
5270 * semantics of that would be a bit debatable anyway.
5271 */
5272 elog(ERROR, "ExecReScanModifyTable is not implemented");
5273}

References elog, and ERROR.

Referenced by ExecReScan().

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 4071 of file nodeModifyTable.c.

4072{
4073 ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
4074 ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
4075
4076 /* Check for transition tables on the directly targeted relation. */
4077 mtstate->mt_transition_capture =
4078 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
4079 RelationGetRelid(targetRelInfo->ri_RelationDesc),
4080 mtstate->operation);
4081 if (plan->operation == CMD_INSERT &&
4082 plan->onConflictAction == ONCONFLICT_UPDATE)
4083 mtstate->mt_oc_transition_capture =
4084 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
4085 RelationGetRelid(targetRelInfo->ri_RelationDesc),
4086 CMD_UPDATE);
4087}
#define plan(x)
Definition: pg_regress.c:161
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1445
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4968

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

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

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

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

◆ ExecUpdateAct()

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

Definition at line 2170 of file nodeModifyTable.c.

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

References CMD_MERGE, TupleDescData::constr, UpdateContext::crossPartUpdate, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, ExecConstraints(), ExecCrossPartitionUpdate(), ExecCrossPartitionUpdateForeignKey(), ExecMaterializeSlot(), ExecPartitionCheck(), ExecUpdatePrepareSlot(), ExecWithCheckOptions(), UpdateContext::lockmode, ModifyTableContext::mtstate, NIL, ModifyTableState::operation, RelationData::rd_att, RelationData::rd_rel, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, table_tuple_update(), TM_Ok, ModifyTableContext::tmfd, TriggerDesc::trig_update_after_row, UpdateContext::updateIndexes, and WCO_RLS_UPDATE_CHECK.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ ExecUpdateEpilogue()

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

Definition at line 2321 of file nodeModifyTable.c.

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

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

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ ExecUpdatePrepareSlot()

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

Definition at line 2135 of file nodeModifyTable.c.

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

References CMD_UPDATE, TupleDescData::constr, ExecComputeStoredGenerated(), TupleConstr::has_generated_stored, RelationData::rd_att, RelationGetRelid, ResultRelInfo::ri_RelationDesc, and TupleTableSlot::tts_tableOid.

Referenced by ExecUpdate(), and ExecUpdateAct().

◆ ExecUpdatePrologue()

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

Definition at line 2092 of file nodeModifyTable.c.

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

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

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 4026 of file nodeModifyTable.c.

4027{
4028 ModifyTable *plan = (ModifyTable *) node->ps.plan;
4029 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
4030
4031 switch (node->operation)
4032 {
4033 case CMD_INSERT:
4034 if (plan->onConflictAction == ONCONFLICT_UPDATE)
4036 resultRelInfo,
4038 ExecASInsertTriggers(node->ps.state, resultRelInfo,
4039 node->mt_transition_capture);
4040 break;
4041 case CMD_UPDATE:
4042 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4043 node->mt_transition_capture);
4044 break;
4045 case CMD_DELETE:
4046 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4047 node->mt_transition_capture);
4048 break;
4049 case CMD_MERGE:
4051 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
4052 node->mt_transition_capture);
4054 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
4055 node->mt_transition_capture);
4057 ExecASInsertTriggers(node->ps.state, resultRelInfo,
4058 node->mt_transition_capture);
4059 break;
4060 default:
4061 elog(ERROR, "unknown operation");
4062 break;
4063 }
4064}
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2953
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2681
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2452

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

Referenced by ExecModifyTable().

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 3989 of file nodeModifyTable.c.

3990{
3991 ModifyTable *plan = (ModifyTable *) node->ps.plan;
3992 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3993
3994 switch (node->operation)
3995 {
3996 case CMD_INSERT:
3997 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3998 if (plan->onConflictAction == ONCONFLICT_UPDATE)
4000 resultRelInfo);
4001 break;
4002 case CMD_UPDATE:
4003 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4004 break;
4005 case CMD_DELETE:
4006 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4007 break;
4008 case CMD_MERGE:
4010 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
4012 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
4014 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
4015 break;
4016 default:
4017 elog(ERROR, "unknown operation");
4018 break;
4019 }
4020}
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2401
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2630
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2895

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

Referenced by ExecModifyTable().