PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
nodeModifyTable.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "rewrite/rewriteHandler.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
Include dependency graph for nodeModifyTable.c:

Go to the source code of this file.

Data Structures

struct  MTTargetRelLookup
 
struct  ModifyTableContext
 
struct  UpdateContext
 

Macros

#define MT_NRELS_HASH   64
 

Typedefs

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

Functions

static void ExecBatchInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
 
static void ExecPendingInserts (EState *estate)
 
static void ExecCrossPartitionUpdateForeignKey (ModifyTableContext *context, ResultRelInfo *sourcePartInfo, ResultRelInfo *destPartInfo, ItemPointer tupleid, TupleTableSlot *oldslot, TupleTableSlot *newslot)
 
static bool ExecOnConflictUpdate (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *excludedSlot, bool canSetTag, TupleTableSlot **returning)
 
static TupleTableSlotExecPrepareTupleRouting (ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot, ResultRelInfo **partRelInfo)
 
static TupleTableSlotExecMerge (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
 
static void ExecInitMerge (ModifyTableState *mtstate, EState *estate)
 
static TupleTableSlotExecMergeMatched (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag, bool *matched)
 
static TupleTableSlotExecMergeNotMatched (ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool canSetTag)
 
static void ExecCheckPlanOutput (Relation resultRel, List *targetList)
 
static TupleTableSlotExecProcessReturning (ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
 
static void ExecCheckTupleVisible (EState *estate, Relation rel, TupleTableSlot *slot)
 
static void ExecCheckTIDVisible (EState *estate, ResultRelInfo *relinfo, ItemPointer tid, TupleTableSlot *tempSlot)
 
void ExecInitStoredGenerated (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 *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 1226 of file nodeModifyTable.c.

1233{
1234 int i;
1235 int numInserted = numSlots;
1236 TupleTableSlot *slot = NULL;
1237 TupleTableSlot **rslots;
1238
1239 /*
1240 * insert into foreign table: let the FDW do it
1241 */
1242 rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1243 resultRelInfo,
1244 slots,
1245 planSlots,
1246 &numInserted);
1247
1248 for (i = 0; i < numInserted; i++)
1249 {
1250 slot = rslots[i];
1251
1252 /*
1253 * AFTER ROW Triggers might reference the tableoid column, so
1254 * (re-)initialize tts_tableOid before evaluating them.
1255 */
1256 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1257
1258 /* AFTER ROW INSERT Triggers */
1259 ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1260 mtstate->mt_transition_capture);
1261
1262 /*
1263 * Check any WITH CHECK OPTION constraints from parent views. See the
1264 * comment in ExecInsert.
1265 */
1266 if (resultRelInfo->ri_WithCheckOptions != NIL)
1267 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1268 }
1269
1270 if (canSetTag && numInserted > 0)
1271 estate->es_processed += numInserted;
1272
1273 /* Clean up all the slots, ready for the next batch */
1274 for (i = 0; i < numSlots; i++)
1275 {
1276 ExecClearTuple(slots[i]);
1277 ExecClearTuple(planSlots[i]);
1278 }
1279 resultRelInfo->ri_NumSlots = 0;
1280}
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2060
int i
Definition: isn.c:72
@ WCO_VIEW_CHECK
Definition: parsenodes.h:1359
#define NIL
Definition: pg_list.h:68
#define RelationGetRelid(relation)
Definition: rel.h:505
uint64 es_processed
Definition: execnodes.h:679
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1403
Relation ri_RelationDesc
Definition: execnodes.h:459
List * ri_WithCheckOptions
Definition: execnodes.h:525
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:509
Oid tts_tableOid
Definition: tuptable.h:130
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2535
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454

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

Referenced by ExecInsert(), and ExecPendingInserts().

◆ ExecCheckPlanOutput()

static void ExecCheckPlanOutput ( Relation  resultRel,
List targetList 
)
static

Definition at line 187 of file nodeModifyTable.c.

188{
189 TupleDesc resultDesc = RelationGetDescr(resultRel);
190 int attno = 0;
191 ListCell *lc;
192
193 foreach(lc, targetList)
194 {
195 TargetEntry *tle = (TargetEntry *) lfirst(lc);
197
198 Assert(!tle->resjunk); /* caller removed junk items already */
199
200 if (attno >= resultDesc->natts)
202 (errcode(ERRCODE_DATATYPE_MISMATCH),
203 errmsg("table row type and query-specified row type do not match"),
204 errdetail("Query has too many columns.")));
205 attr = TupleDescAttr(resultDesc, attno);
206 attno++;
207
208 if (!attr->attisdropped)
209 {
210 /* Normal case: demand type match */
211 if (exprType((Node *) tle->expr) != attr->atttypid)
213 (errcode(ERRCODE_DATATYPE_MISMATCH),
214 errmsg("table row type and query-specified row type do not match"),
215 errdetail("Table has type %s at ordinal position %d, but query expects %s.",
216 format_type_be(attr->atttypid),
217 attno,
218 format_type_be(exprType((Node *) tle->expr)))));
219 }
220 else
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 }
236 if (attno != resultDesc->natts)
238 (errcode(ERRCODE_DATATYPE_MISMATCH),
239 errmsg("table row type and query-specified row type do not match"),
240 errdetail("Query has too few columns.")));
241}
#define Assert(condition)
Definition: c.h:812
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
#define lfirst(lc)
Definition: pg_list.h:172
#define RelationGetDescr(relation)
Definition: rel.h:531
Definition: nodes.h:129
Expr * expr
Definition: primnodes.h:2190
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:152

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

326{
327 Relation rel = relinfo->ri_RelationDesc;
328
329 /* Redundantly check isolation level */
331 return;
332
333 if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
334 elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
335 ExecCheckTupleVisible(estate, rel, tempSlot);
336 ExecClearTuple(tempSlot);
337}
#define elog(elevel,...)
Definition: elog.h:225
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
#define SnapshotAny
Definition: snapmgr.h:33
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1293
#define IsolationUsesXactSnapshot()
Definition: xact.h:51

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

Referenced by ExecInsert().

◆ ExecCheckTupleVisible()

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

Definition at line 288 of file nodeModifyTable.c.

291{
293 return;
294
295 if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
296 {
297 Datum xminDatum;
298 TransactionId xmin;
299 bool isnull;
300
301 xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
302 Assert(!isnull);
303 xmin = DatumGetTransactionId(xminDatum);
304
305 /*
306 * We should not raise a serialization failure if the conflict is
307 * against a tuple inserted by our own transaction, even if it's not
308 * visible to our snapshot. (This would happen, for example, if
309 * conflicting keys are proposed for insertion in a single command.)
310 */
314 errmsg("could not serialize access due to concurrent update")));
315 }
316}
uint32 TransactionId
Definition: c.h:606
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:76
uintptr_t Datum
Definition: postgres.h:64
static TransactionId DatumGetTransactionId(Datum X)
Definition: postgres.h:262
Snapshot es_snapshot
Definition: execnodes.h:632
#define MinTransactionIdAttributeNumber
Definition: sysattr.h:22
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition: tableam.h:1340
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:416
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:940

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

Referenced by ExecCheckTIDVisible(), and ExecOnConflictUpdate().

◆ ExecComputeStoredGenerated()

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

Definition at line 452 of file nodeModifyTable.c.

455{
456 Relation rel = resultRelInfo->ri_RelationDesc;
457 TupleDesc tupdesc = RelationGetDescr(rel);
458 int natts = tupdesc->natts;
459 ExprContext *econtext = GetPerTupleExprContext(estate);
460 ExprState **ri_GeneratedExprs;
461 MemoryContext oldContext;
462 Datum *values;
463 bool *nulls;
464
465 /* We should not be called unless this is true */
466 Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
467
468 /*
469 * Initialize the expressions if we didn't already, and check whether we
470 * can exit early because nothing needs to be computed.
471 */
472 if (cmdtype == CMD_UPDATE)
473 {
474 if (resultRelInfo->ri_GeneratedExprsU == NULL)
475 ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
476 if (resultRelInfo->ri_NumGeneratedNeededU == 0)
477 return;
478 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
479 }
480 else
481 {
482 if (resultRelInfo->ri_GeneratedExprsI == NULL)
483 ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
484 /* Early exit is impossible given the prior Assert */
485 Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
486 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
487 }
488
490
491 values = palloc(sizeof(*values) * natts);
492 nulls = palloc(sizeof(*nulls) * natts);
493
494 slot_getallattrs(slot);
495 memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
496
497 for (int i = 0; i < natts; i++)
498 {
499 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
500
501 if (ri_GeneratedExprs[i])
502 {
503 Datum val;
504 bool isnull;
505
506 Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
507
508 econtext->ecxt_scantuple = slot;
509
510 val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
511
512 /*
513 * We must make a copy of val as we have no guarantees about where
514 * memory for a pass-by-reference Datum is located.
515 */
516 if (!isnull)
517 val = datumCopy(val, attr->attbyval, attr->attlen);
518
519 values[i] = val;
520 nulls[i] = isnull;
521 }
522 else
523 {
524 if (!nulls[i])
525 values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
526 }
527 }
528
529 ExecClearTuple(slot);
530 memcpy(slot->tts_values, values, sizeof(*values) * natts);
531 memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
534
535 MemoryContextSwitchTo(oldContext);
536}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1739
#define GetPerTupleExprContext(estate)
Definition: executor.h:563
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:568
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:346
long val
Definition: informix.c:689
void * palloc(Size size)
Definition: mcxt.c:1317
void ExecInitStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
@ CMD_UPDATE
Definition: nodes.h:266
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
int16 attlen
Definition: tupdesc.h:69
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:258
ExprState ** ri_GeneratedExprsI
Definition: execnodes.h:536
int ri_NumGeneratedNeededU
Definition: execnodes.h:541
ExprState ** ri_GeneratedExprsU
Definition: execnodes.h:537
int ri_NumGeneratedNeededI
Definition: execnodes.h:540
bool has_generated_stored
Definition: tupdesc.h:45
TupleConstr * constr
Definition: tupdesc.h:133
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:169
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:472

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

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

◆ ExecCrossPartitionUpdate()

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

Definition at line 1744 of file nodeModifyTable.c.

1754{
1755 ModifyTableState *mtstate = context->mtstate;
1756 EState *estate = mtstate->ps.state;
1757 TupleConversionMap *tupconv_map;
1758 bool tuple_deleted;
1759 TupleTableSlot *epqslot = NULL;
1760
1761 context->cpUpdateReturningSlot = NULL;
1762 *retry_slot = NULL;
1763
1764 /*
1765 * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1766 * to migrate to a different partition. Maybe this can be implemented
1767 * some day, but it seems a fringe feature with little redeeming value.
1768 */
1769 if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1770 ereport(ERROR,
1771 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1772 errmsg("invalid ON UPDATE specification"),
1773 errdetail("The result tuple would appear in a different partition than the original tuple.")));
1774
1775 /*
1776 * When an UPDATE is run directly on a leaf partition, simply fail with a
1777 * partition constraint violation error.
1778 */
1779 if (resultRelInfo == mtstate->rootResultRelInfo)
1780 ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1781
1782 /* Initialize tuple routing info if not already done. */
1783 if (mtstate->mt_partition_tuple_routing == NULL)
1784 {
1785 Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
1786 MemoryContext oldcxt;
1787
1788 /* Things built here have to last for the query duration. */
1789 oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
1790
1792 ExecSetupPartitionTupleRouting(estate, rootRel);
1793
1794 /*
1795 * Before a partition's tuple can be re-routed, it must first be
1796 * converted to the root's format, so we'll need a slot for storing
1797 * such tuples.
1798 */
1799 Assert(mtstate->mt_root_tuple_slot == NULL);
1800 mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
1801
1802 MemoryContextSwitchTo(oldcxt);
1803 }
1804
1805 /*
1806 * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
1807 * We want to return rows from INSERT.
1808 */
1809 ExecDelete(context, resultRelInfo,
1810 tupleid, oldtuple,
1811 false, /* processReturning */
1812 true, /* changingPart */
1813 false, /* canSetTag */
1814 tmresult, &tuple_deleted, &epqslot);
1815
1816 /*
1817 * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
1818 * it was already deleted by self, or it was concurrently deleted by
1819 * another transaction), then we should skip the insert as well;
1820 * otherwise, an UPDATE could cause an increase in the total number of
1821 * rows across all partitions, which is clearly wrong.
1822 *
1823 * For a normal UPDATE, the case where the tuple has been the subject of a
1824 * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
1825 * machinery, but for an UPDATE that we've translated into a DELETE from
1826 * this partition and an INSERT into some other partition, that's not
1827 * available, because CTID chains can't span relation boundaries. We
1828 * mimic the semantics to a limited extent by skipping the INSERT if the
1829 * DELETE fails to find a tuple. This ensures that two concurrent
1830 * attempts to UPDATE the same tuple at the same time can't turn one tuple
1831 * into two, and that an UPDATE of a just-deleted tuple can't resurrect
1832 * it.
1833 */
1834 if (!tuple_deleted)
1835 {
1836 /*
1837 * epqslot will be typically NULL. But when ExecDelete() finds that
1838 * another transaction has concurrently updated the same row, it
1839 * re-fetches the row, skips the delete, and epqslot is set to the
1840 * re-fetched tuple slot. In that case, we need to do all the checks
1841 * again. For MERGE, we leave everything to the caller (it must do
1842 * additional rechecking, and might end up executing a different
1843 * action entirely).
1844 */
1845 if (mtstate->operation == CMD_MERGE)
1846 return *tmresult == TM_Ok;
1847 else if (TupIsNull(epqslot))
1848 return true;
1849 else
1850 {
1851 /* Fetch the most recent version of old tuple. */
1852 TupleTableSlot *oldSlot;
1853
1854 /* ... but first, make sure ri_oldTupleSlot is initialized. */
1855 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
1856 ExecInitUpdateProjection(mtstate, resultRelInfo);
1857 oldSlot = resultRelInfo->ri_oldTupleSlot;
1859 tupleid,
1861 oldSlot))
1862 elog(ERROR, "failed to fetch tuple being updated");
1863 /* and project the new tuple to retry the UPDATE with */
1864 *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot,
1865 oldSlot);
1866 return false;
1867 }
1868 }
1869
1870 /*
1871 * resultRelInfo is one of the per-relation resultRelInfos. So we should
1872 * convert the tuple into root's tuple descriptor if needed, since
1873 * ExecInsert() starts the search from root.
1874 */
1875 tupconv_map = ExecGetChildToRootMap(resultRelInfo);
1876 if (tupconv_map != NULL)
1877 slot = execute_attr_map_slot(tupconv_map->attrMap,
1878 slot,
1879 mtstate->mt_root_tuple_slot);
1880
1881 /* Tuple routing starts from the root table. */
1882 context->cpUpdateReturningSlot =
1883 ExecInsert(context, mtstate->rootResultRelInfo, slot, canSetTag,
1884 inserted_tuple, insert_destrel);
1885
1886 /*
1887 * Reset the transition state that may possibly have been written by
1888 * INSERT.
1889 */
1890 if (mtstate->mt_transition_capture)
1892
1893 /* We're done moving. */
1894 return true;
1895}
#define unlikely(x)
Definition: c.h:330
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1854
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1251
static TupleTableSlot * ExecInsert(ModifyTableContext *context, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, bool canSetTag, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecInitUpdateProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static TupleTableSlot * ExecDelete(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool processReturning, bool changingPart, bool canSetTag, TM_Result *tmresult, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
@ ONCONFLICT_UPDATE
Definition: nodes.h:420
@ CMD_MERGE
Definition: nodes.h:269
MemoryContext es_query_cxt
Definition: execnodes.h:675
TupleTableSlot * cpUpdateReturningSlot
ModifyTableState * mtstate
CmdType operation
Definition: execnodes.h:1365
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1400
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1397
PlanState ps
Definition: execnodes.h:1364
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1377
Plan * plan
Definition: execnodes.h:1126
EState * state
Definition: execnodes.h:1128
bool ri_projectNewInfoValid
Definition: execnodes.h:486
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:484
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:76
AttrMap * attrMap
Definition: tupconvert.h:28
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
@ TM_Ok
Definition: tableam.h:84
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:192
#define TupIsNull(slot)
Definition: tuptable.h:306

References Assert, TupleConversionMap::attrMap, CMD_MERGE, ModifyTableContext::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 2180 of file nodeModifyTable.c.

2186{
2187 ListCell *lc;
2188 ResultRelInfo *rootRelInfo;
2189 List *ancestorRels;
2190
2191 rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2192 ancestorRels = ExecGetAncestorResultRels(context->estate, sourcePartInfo);
2193
2194 /*
2195 * For any foreign keys that point directly into a non-root ancestors of
2196 * the source partition, we can in theory fire an update event to enforce
2197 * those constraints using their triggers, if we could tell that both the
2198 * source and the destination partitions are under the same ancestor. But
2199 * for now, we simply report an error that those cannot be enforced.
2200 */
2201 foreach(lc, ancestorRels)
2202 {
2203 ResultRelInfo *rInfo = lfirst(lc);
2204 TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2205 bool has_noncloned_fkey = false;
2206
2207 /* Root ancestor's triggers will be processed. */
2208 if (rInfo == rootRelInfo)
2209 continue;
2210
2211 if (trigdesc && trigdesc->trig_update_after_row)
2212 {
2213 for (int i = 0; i < trigdesc->numtriggers; i++)
2214 {
2215 Trigger *trig = &trigdesc->triggers[i];
2216
2217 if (!trig->tgisclone &&
2219 {
2220 has_noncloned_fkey = true;
2221 break;
2222 }
2223 }
2224 }
2225
2226 if (has_noncloned_fkey)
2227 ereport(ERROR,
2228 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2229 errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2230 errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2233 errhint("Consider defining the foreign key on table \"%s\".",
2235 }
2236
2237 /* Perform the root table's triggers. */
2239 rootRelInfo, sourcePartInfo, destPartInfo,
2240 tupleid, NULL, newslot, NIL, NULL, true);
2241}
int errhint(const char *fmt,...)
Definition: elog.c:1317
List * ExecGetAncestorResultRels(EState *estate, ResultRelInfo *resultRelInfo)
Definition: execMain.c:1378
#define RelationGetRelationName(relation)
Definition: rel.h:539
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3119
Definition: pg_list.h:54
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:590
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:492
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:3098
#define RI_TRIGGER_PK
Definition: trigger.h:282

References ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, ModifyTableContext::estate, ExecARUpdateTriggers(), ExecGetAncestorResultRels(), i, lfirst, NIL, TriggerDesc::numtriggers, RelationGetRelationName, RI_FKey_trigger_type(), ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_TrigDesc, RI_TRIGGER_PK, Trigger::tgfoid, Trigger::tgisclone, TriggerDesc::trig_update_after_row, and TriggerDesc::triggers.

Referenced by ExecUpdateAct().

◆ ExecDelete()

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

Definition at line 1430 of file nodeModifyTable.c.

1440{
1441 EState *estate = context->estate;
1442 Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1443 TupleTableSlot *slot = NULL;
1444 TM_Result result;
1445
1446 if (tupleDeleted)
1447 *tupleDeleted = false;
1448
1449 /*
1450 * Prepare for the delete. This includes BEFORE ROW triggers, so we're
1451 * done if it says we are.
1452 */
1453 if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple,
1454 epqreturnslot, tmresult))
1455 return NULL;
1456
1457 /* INSTEAD OF ROW DELETE Triggers */
1458 if (resultRelInfo->ri_TrigDesc &&
1459 resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
1460 {
1461 bool dodelete;
1462
1463 Assert(oldtuple != NULL);
1464 dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
1465
1466 if (!dodelete) /* "do nothing" */
1467 return NULL;
1468 }
1469 else if (resultRelInfo->ri_FdwRoutine)
1470 {
1471 /*
1472 * delete from foreign table: let the FDW do it
1473 *
1474 * We offer the returning slot as a place to store RETURNING data,
1475 * although the FDW can return some other slot if it wants.
1476 */
1477 slot = ExecGetReturningSlot(estate, resultRelInfo);
1478 slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
1479 resultRelInfo,
1480 slot,
1481 context->planSlot);
1482
1483 if (slot == NULL) /* "do nothing" */
1484 return NULL;
1485
1486 /*
1487 * RETURNING expressions might reference the tableoid column, so
1488 * (re)initialize tts_tableOid before evaluating them.
1489 */
1490 if (TTS_EMPTY(slot))
1492
1493 slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1494 }
1495 else
1496 {
1497 /*
1498 * delete the tuple
1499 *
1500 * Note: if context->estate->es_crosscheck_snapshot isn't
1501 * InvalidSnapshot, we check that the row to be deleted is visible to
1502 * that snapshot, and throw a can't-serialize error if not. This is a
1503 * special-case behavior needed for referential integrity updates in
1504 * transaction-snapshot mode transactions.
1505 */
1506ldelete:
1507 result = ExecDeleteAct(context, resultRelInfo, tupleid, changingPart);
1508
1509 if (tmresult)
1510 *tmresult = result;
1511
1512 switch (result)
1513 {
1514 case TM_SelfModified:
1515
1516 /*
1517 * The target tuple was already updated or deleted by the
1518 * current command, or by a later command in the current
1519 * transaction. The former case is possible in a join DELETE
1520 * where multiple tuples join to the same target tuple. This
1521 * is somewhat questionable, but Postgres has always allowed
1522 * it: we just ignore additional deletion attempts.
1523 *
1524 * The latter case arises if the tuple is modified by a
1525 * command in a BEFORE trigger, or perhaps by a command in a
1526 * volatile function used in the query. In such situations we
1527 * should not ignore the deletion, but it is equally unsafe to
1528 * proceed. We don't want to discard the original DELETE
1529 * while keeping the triggered actions based on its deletion;
1530 * and it would be no better to allow the original DELETE
1531 * while discarding updates that it triggered. The row update
1532 * carries some information that might be important according
1533 * to business rules; so throwing an error is the only safe
1534 * course.
1535 *
1536 * If a trigger actually intends this type of interaction, it
1537 * can re-execute the DELETE and then return NULL to cancel
1538 * the outer delete.
1539 */
1540 if (context->tmfd.cmax != estate->es_output_cid)
1541 ereport(ERROR,
1542 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1543 errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1544 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1545
1546 /* Else, already deleted by self; nothing to do */
1547 return NULL;
1548
1549 case TM_Ok:
1550 break;
1551
1552 case TM_Updated:
1553 {
1554 TupleTableSlot *inputslot;
1555 TupleTableSlot *epqslot;
1556
1558 ereport(ERROR,
1560 errmsg("could not serialize access due to concurrent update")));
1561
1562 /*
1563 * Already know that we're going to need to do EPQ, so
1564 * fetch tuple directly into the right slot.
1565 */
1566 EvalPlanQualBegin(context->epqstate);
1567 inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
1568 resultRelInfo->ri_RangeTableIndex);
1569
1570 result = table_tuple_lock(resultRelationDesc, tupleid,
1571 estate->es_snapshot,
1572 inputslot, estate->es_output_cid,
1575 &context->tmfd);
1576
1577 switch (result)
1578 {
1579 case TM_Ok:
1580 Assert(context->tmfd.traversed);
1581 epqslot = EvalPlanQual(context->epqstate,
1582 resultRelationDesc,
1583 resultRelInfo->ri_RangeTableIndex,
1584 inputslot);
1585 if (TupIsNull(epqslot))
1586 /* Tuple not passing quals anymore, exiting... */
1587 return NULL;
1588
1589 /*
1590 * If requested, skip delete and pass back the
1591 * updated row.
1592 */
1593 if (epqreturnslot)
1594 {
1595 *epqreturnslot = epqslot;
1596 return NULL;
1597 }
1598 else
1599 goto ldelete;
1600
1601 case TM_SelfModified:
1602
1603 /*
1604 * This can be reached when following an update
1605 * chain from a tuple updated by another session,
1606 * reaching a tuple that was already updated in
1607 * this transaction. If previously updated by this
1608 * command, ignore the delete, otherwise error
1609 * out.
1610 *
1611 * See also TM_SelfModified response to
1612 * table_tuple_delete() above.
1613 */
1614 if (context->tmfd.cmax != estate->es_output_cid)
1615 ereport(ERROR,
1616 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1617 errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1618 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1619 return NULL;
1620
1621 case TM_Deleted:
1622 /* tuple already deleted; nothing to do */
1623 return NULL;
1624
1625 default:
1626
1627 /*
1628 * TM_Invisible should be impossible because we're
1629 * waiting for updated row versions, and would
1630 * already have errored out if the first version
1631 * is invisible.
1632 *
1633 * TM_Updated should be impossible, because we're
1634 * locking the latest version via
1635 * TUPLE_LOCK_FLAG_FIND_LAST_VERSION.
1636 */
1637 elog(ERROR, "unexpected table_tuple_lock status: %u",
1638 result);
1639 return NULL;
1640 }
1641
1642 Assert(false);
1643 break;
1644 }
1645
1646 case TM_Deleted:
1648 ereport(ERROR,
1650 errmsg("could not serialize access due to concurrent delete")));
1651 /* tuple already deleted; nothing to do */
1652 return NULL;
1653
1654 default:
1655 elog(ERROR, "unrecognized table_tuple_delete status: %u",
1656 result);
1657 return NULL;
1658 }
1659
1660 /*
1661 * Note: Normally one would think that we have to delete index tuples
1662 * associated with the heap tuple now...
1663 *
1664 * ... but in POSTGRES, we have no need to do this because VACUUM will
1665 * take care of it later. We can't delete index tuples immediately
1666 * anyway, since the tuple is still visible to other transactions.
1667 */
1668 }
1669
1670 if (canSetTag)
1671 (estate->es_processed)++;
1672
1673 /* Tell caller that the delete actually happened. */
1674 if (tupleDeleted)
1675 *tupleDeleted = true;
1676
1677 ExecDeleteEpilogue(context, resultRelInfo, tupleid, oldtuple, changingPart);
1678
1679 /* Process RETURNING if present and if requested */
1680 if (processReturning && resultRelInfo->ri_projectReturning)
1681 {
1682 /*
1683 * We have to put the target tuple into a slot, which means first we
1684 * gotta fetch it. We can use the trigger tuple slot.
1685 */
1686 TupleTableSlot *rslot;
1687
1688 if (resultRelInfo->ri_FdwRoutine)
1689 {
1690 /* FDW must have provided a slot containing the deleted row */
1691 Assert(!TupIsNull(slot));
1692 }
1693 else
1694 {
1695 slot = ExecGetReturningSlot(estate, resultRelInfo);
1696 if (oldtuple != NULL)
1697 {
1698 ExecForceStoreHeapTuple(oldtuple, slot, false);
1699 }
1700 else
1701 {
1702 if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
1703 SnapshotAny, slot))
1704 elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
1705 }
1706 }
1707
1708 rslot = ExecProcessReturning(resultRelInfo, slot, context->planSlot);
1709
1710 /*
1711 * Before releasing the target tuple again, make sure rslot has a
1712 * local copy of any pass-by-reference values.
1713 */
1714 ExecMaterializeSlot(rslot);
1715
1716 ExecClearTuple(slot);
1717
1718 return rslot;
1719 }
1720
1721 return NULL;
1722}
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition: execMain.c:2607
void EvalPlanQualBegin(EPQState *epqstate)
Definition: execMain.c:2760
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2479
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1763
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1656
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1227
@ 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 TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
static void ExecDeleteEpilogue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool changingPart)
CommandId es_output_cid
Definition: execnodes.h:647
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:236
TM_FailureData tmfd
TupleTableSlot * planSlot
Index ri_RangeTableIndex
Definition: execnodes.h:456
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:547
bool traversed
Definition: tableam.h:152
CommandId cmax
Definition: tableam.h:151
bool trig_delete_instead_row
Definition: reltrigger.h:68
TM_Result
Definition: tableam.h:79
@ TM_Deleted
Definition: tableam.h:99
@ TM_Updated
Definition: tableam.h:96
@ TM_SelfModified
Definition: tableam.h:90
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition: tableam.h:1585
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:267
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition: trigger.c:2812
#define TTS_EMPTY(slot)
Definition: tuptable.h:96

References Assert, TM_FailureData::cmax, 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, ExecGetReturningSlot(), ExecIRDeleteTriggers(), ExecMaterializeSlot(), ExecProcessReturning(), ExecStoreAllNullTuple(), IsolationUsesXactSnapshot, LockTupleExclusive, LockWaitBlock, ModifyTableContext::planSlot, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, 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_delete_instead_row, TTS_EMPTY, TupleTableSlot::tts_tableOid, 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 1350 of file nodeModifyTable.c.

1352{
1353 EState *estate = context->estate;
1354
1355 return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1356 estate->es_output_cid,
1357 estate->es_snapshot,
1358 estate->es_crosscheck_snapshot,
1359 true /* wait for commit */ ,
1360 &context->tmfd,
1361 changingPart);
1362}
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:633
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:1496

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

1374{
1375 ModifyTableState *mtstate = context->mtstate;
1376 EState *estate = context->estate;
1377 TransitionCaptureState *ar_delete_trig_tcs;
1378
1379 /*
1380 * If this delete is the result of a partition key update that moved the
1381 * tuple to a new partition, put this row into the transition OLD TABLE,
1382 * if there is one. We need to do this separately for DELETE and INSERT
1383 * because they happen on different tables.
1384 */
1385 ar_delete_trig_tcs = mtstate->mt_transition_capture;
1386 if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1388 {
1389 ExecARUpdateTriggers(estate, resultRelInfo,
1390 NULL, NULL,
1391 tupleid, oldtuple,
1392 NULL, NULL, mtstate->mt_transition_capture,
1393 false);
1394
1395 /*
1396 * We've already captured the OLD TABLE row, so make sure any AR
1397 * DELETE trigger fired below doesn't capture it again.
1398 */
1399 ar_delete_trig_tcs = NULL;
1400 }
1401
1402 /* AFTER ROW DELETE Triggers */
1403 ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1404 ar_delete_trig_tcs, changingPart);
1405}
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:2775

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

1322{
1323 if (result)
1324 *result = TM_Ok;
1325
1326 /* BEFORE ROW DELETE triggers */
1327 if (resultRelInfo->ri_TrigDesc &&
1328 resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1329 {
1330 /* Flush any pending inserts, so rows are visible to the triggers */
1332 ExecPendingInserts(context->estate);
1333
1334 return ExecBRDeleteTriggers(context->estate, context->epqstate,
1335 resultRelInfo, tupleid, oldtuple,
1336 epqreturnslot, result, &context->tmfd);
1337 }
1338
1339 return true;
1340}
static void ExecPendingInserts(EState *estate)
List * es_insert_pending_result_relations
Definition: execnodes.h:736
bool trig_delete_before_row
Definition: reltrigger.h:66
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot, TM_Result *tmresult, TM_FailureData *tmfd)
Definition: trigger.c:2684

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

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 4765 of file nodeModifyTable.c.

4766{
4767 int i;
4768
4769 /*
4770 * Allow any FDWs to shut down
4771 */
4772 for (i = 0; i < node->mt_nrels; i++)
4773 {
4774 int j;
4775 ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
4776
4777 if (!resultRelInfo->ri_usesFdwDirectModify &&
4778 resultRelInfo->ri_FdwRoutine != NULL &&
4779 resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
4780 resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
4781 resultRelInfo);
4782
4783 /*
4784 * Cleanup the initialized batch slots. This only matters for FDWs
4785 * with batching, but the other cases will have ri_NumSlotsInitialized
4786 * == 0.
4787 */
4788 for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
4789 {
4790 ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
4792 }
4793 }
4794
4795 /*
4796 * Close all the partitioned tables, leaf partitions, and their indices
4797 * and release the slot used for tuple routing, if set.
4798 */
4800 {
4802
4803 if (node->mt_root_tuple_slot)
4805 }
4806
4807 /*
4808 * Terminate EPQ execution if active
4809 */
4811
4812 /*
4813 * shut down subplan
4814 */
4816}
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2991
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1441
#define outerPlanState(node)
Definition: execnodes.h:1222
int j
Definition: isn.c:73
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1369
EPQState mt_epqstate
Definition: execnodes.h:1379
TupleTableSlot ** ri_Slots
Definition: execnodes.h:521
int ri_NumSlotsInitialized
Definition: execnodes.h:519
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:522
bool ri_usesFdwDirectModify
Definition: execnodes.h:515

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

678{
679 ProjectionInfo *newProj = relinfo->ri_projectNew;
680 ExprContext *econtext;
681
682 /*
683 * If there's no projection to be done, just make sure the slot is of the
684 * right type for the target rel. If the planSlot is the right type we
685 * can use it as-is, else copy the data into ri_newTupleSlot.
686 */
687 if (newProj == NULL)
688 {
689 if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
690 {
691 ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
692 return relinfo->ri_newTupleSlot;
693 }
694 else
695 return planSlot;
696 }
697
698 /*
699 * Else project; since the projection output slot is ri_newTupleSlot, this
700 * will also fix any slot-type problem.
701 *
702 * Note: currently, this is dead code, because INSERT cases don't receive
703 * any junk columns so there's never a projection to be done.
704 */
705 econtext = newProj->pi_exprContext;
706 econtext->ecxt_outertuple = planSlot;
707 return ExecProject(newProj);
708}
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:389
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:262
ExprContext * pi_exprContext
Definition: execnodes.h:367
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:482
ProjectionInfo * ri_projectNew
Definition: execnodes.h:480
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:121
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509

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

Referenced by ExecModifyTable().

◆ ExecGetUpdateNewTuple()

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

Definition at line 720 of file nodeModifyTable.c.

723{
724 ProjectionInfo *newProj = relinfo->ri_projectNew;
725 ExprContext *econtext;
726
727 /* Use a few extra Asserts to protect against outside callers */
729 Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
730 Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
731
732 econtext = newProj->pi_exprContext;
733 econtext->ecxt_outertuple = planSlot;
734 econtext->ecxt_scantuple = oldSlot;
735 return ExecProject(newProj);
736}

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

◆ ExecInitInsertProjection()

static void ExecInitInsertProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 548 of file nodeModifyTable.c.

550{
551 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
552 Plan *subplan = outerPlan(node);
553 EState *estate = mtstate->ps.state;
554 List *insertTargetList = NIL;
555 bool need_projection = false;
556 ListCell *l;
557
558 /* Extract non-junk columns of the subplan's result tlist. */
559 foreach(l, subplan->targetlist)
560 {
561 TargetEntry *tle = (TargetEntry *) lfirst(l);
562
563 if (!tle->resjunk)
564 insertTargetList = lappend(insertTargetList, tle);
565 else
566 need_projection = true;
567 }
568
569 /*
570 * The junk-free list must produce a tuple suitable for the result
571 * relation.
572 */
573 ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
574
575 /* We'll need a slot matching the table's format. */
576 resultRelInfo->ri_newTupleSlot =
577 table_slot_create(resultRelInfo->ri_RelationDesc,
578 &estate->es_tupleTable);
579
580 /* Build ProjectionInfo if needed (it probably isn't). */
581 if (need_projection)
582 {
583 TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
584
585 /* need an expression context to do the projection */
586 if (mtstate->ps.ps_ExprContext == NULL)
587 ExecAssignExprContext(estate, &mtstate->ps);
588
589 resultRelInfo->ri_projectNew =
590 ExecBuildProjectionInfo(insertTargetList,
591 mtstate->ps.ps_ExprContext,
592 resultRelInfo->ri_newTupleSlot,
593 &mtstate->ps,
594 relDesc);
595 }
596
597 resultRelInfo->ri_projectNewInfoValid = true;
598}
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:365
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:183
List * es_tupleTable
Definition: execnodes.h:677
ExprContext * ps_ExprContext
Definition: execnodes.h:1165

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

3465{
3466 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
3467 ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
3468 ResultRelInfo *resultRelInfo;
3469 ExprContext *econtext;
3470 ListCell *lc;
3471 int i;
3472
3473 if (node->mergeActionLists == NIL)
3474 return;
3475
3476 mtstate->mt_merge_subcommands = 0;
3477
3478 if (mtstate->ps.ps_ExprContext == NULL)
3479 ExecAssignExprContext(estate, &mtstate->ps);
3480 econtext = mtstate->ps.ps_ExprContext;
3481
3482 /*
3483 * Create a MergeActionState for each action on the mergeActionList and
3484 * add it to either a list of matched actions or not-matched actions.
3485 *
3486 * Similar logic appears in ExecInitPartitionInfo(), so if changing
3487 * anything here, do so there too.
3488 */
3489 i = 0;
3490 foreach(lc, node->mergeActionLists)
3491 {
3492 List *mergeActionList = lfirst(lc);
3493 Node *joinCondition;
3494 TupleDesc relationDesc;
3495 ListCell *l;
3496
3497 joinCondition = (Node *) list_nth(node->mergeJoinConditions, i);
3498 resultRelInfo = mtstate->resultRelInfo + i;
3499 i++;
3500 relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3501
3502 /* initialize slots for MERGE fetches from this rel */
3503 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3504 ExecInitMergeTupleSlots(mtstate, resultRelInfo);
3505
3506 /* initialize state for join condition checking */
3507 resultRelInfo->ri_MergeJoinCondition =
3508 ExecInitQual((List *) joinCondition, &mtstate->ps);
3509
3510 foreach(l, mergeActionList)
3511 {
3513 MergeActionState *action_state;
3514 TupleTableSlot *tgtslot;
3515 TupleDesc tgtdesc;
3516
3517 /*
3518 * Build action merge state for this rel. (For partitions,
3519 * equivalent code exists in ExecInitPartitionInfo.)
3520 */
3521 action_state = makeNode(MergeActionState);
3522 action_state->mas_action = action;
3523 action_state->mas_whenqual = ExecInitQual((List *) action->qual,
3524 &mtstate->ps);
3525
3526 /*
3527 * We create three lists - one for each MergeMatchKind - and stick
3528 * the MergeActionState into the appropriate list.
3529 */
3530 resultRelInfo->ri_MergeActions[action->matchKind] =
3531 lappend(resultRelInfo->ri_MergeActions[action->matchKind],
3532 action_state);
3533
3534 switch (action->commandType)
3535 {
3536 case CMD_INSERT:
3537 ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
3538 action->targetList);
3539
3540 /*
3541 * If the MERGE targets a partitioned table, any INSERT
3542 * actions must be routed through it, not the child
3543 * relations. Initialize the routing struct and the root
3544 * table's "new" tuple slot for that, if not already done.
3545 * The projection we prepare, for all relations, uses the
3546 * root relation descriptor, and targets the plan's root
3547 * slot. (This is consistent with the fact that we
3548 * checked the plan output to match the root relation,
3549 * above.)
3550 */
3551 if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
3552 RELKIND_PARTITIONED_TABLE)
3553 {
3554 if (mtstate->mt_partition_tuple_routing == NULL)
3555 {
3556 /*
3557 * Initialize planstate for routing if not already
3558 * done.
3559 *
3560 * Note that the slot is managed as a standalone
3561 * slot belonging to ModifyTableState, so we pass
3562 * NULL for the 2nd argument.
3563 */
3564 mtstate->mt_root_tuple_slot =
3565 table_slot_create(rootRelInfo->ri_RelationDesc,
3566 NULL);
3569 rootRelInfo->ri_RelationDesc);
3570 }
3571 tgtslot = mtstate->mt_root_tuple_slot;
3572 tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3573 }
3574 else
3575 {
3576 /* not partitioned? use the stock relation and slot */
3577 tgtslot = resultRelInfo->ri_newTupleSlot;
3578 tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3579 }
3580
3581 action_state->mas_proj =
3582 ExecBuildProjectionInfo(action->targetList, econtext,
3583 tgtslot,
3584 &mtstate->ps,
3585 tgtdesc);
3586
3588 break;
3589 case CMD_UPDATE:
3590 action_state->mas_proj =
3592 true,
3593 action->updateColnos,
3594 relationDesc,
3595 econtext,
3596 resultRelInfo->ri_newTupleSlot,
3597 &mtstate->ps);
3599 break;
3600 case CMD_DELETE:
3602 break;
3603 case CMD_NOTHING:
3604 break;
3605 default:
3606 elog(ERROR, "unknown operation");
3607 break;
3608 }
3609 }
3610 }
3611}
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:224
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:525
#define MERGE_UPDATE
Definition: execnodes.h:1355
#define MERGE_INSERT
Definition: execnodes.h:1354
#define MERGE_DELETE
Definition: execnodes.h:1356
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
void ExecInitMergeTupleSlots(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_NOTHING
Definition: nodes.h:272
#define makeNode(_type_)
Definition: nodes.h:155
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
MergeAction * mas_action
Definition: execnodes.h:428
ProjectionInfo * mas_proj
Definition: execnodes.h:429
ExprState * mas_whenqual
Definition: execnodes.h:431
int mt_merge_subcommands
Definition: execnodes.h:1409
List * mergeJoinConditions
Definition: plannodes.h:255
List * mergeActionLists
Definition: plannodes.h:253

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

Referenced by ExecInitModifyTable().

◆ ExecInitMergeTupleSlots()

void ExecInitMergeTupleSlots ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)

Definition at line 3620 of file nodeModifyTable.c.

3622{
3623 EState *estate = mtstate->ps.state;
3624
3625 Assert(!resultRelInfo->ri_projectNewInfoValid);
3626
3627 resultRelInfo->ri_oldTupleSlot =
3628 table_slot_create(resultRelInfo->ri_RelationDesc,
3629 &estate->es_tupleTable);
3630 resultRelInfo->ri_newTupleSlot =
3631 table_slot_create(resultRelInfo->ri_RelationDesc,
3632 &estate->es_tupleTable);
3633 resultRelInfo->ri_projectNewInfoValid = true;
3634}

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

4281{
4282 ModifyTableState *mtstate;
4283 Plan *subplan = outerPlan(node);
4284 CmdType operation = node->operation;
4285 int nrels = list_length(node->resultRelations);
4286 ResultRelInfo *resultRelInfo;
4287 List *arowmarks;
4288 ListCell *l;
4289 int i;
4290 Relation rel;
4291
4292 /* check for unsupported flags */
4293 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
4294
4295 /*
4296 * create state structure
4297 */
4298 mtstate = makeNode(ModifyTableState);
4299 mtstate->ps.plan = (Plan *) node;
4300 mtstate->ps.state = estate;
4301 mtstate->ps.ExecProcNode = ExecModifyTable;
4302
4303 mtstate->operation = operation;
4304 mtstate->canSetTag = node->canSetTag;
4305 mtstate->mt_done = false;
4306
4307 mtstate->mt_nrels = nrels;
4308 mtstate->resultRelInfo = (ResultRelInfo *)
4309 palloc(nrels * sizeof(ResultRelInfo));
4310
4311 mtstate->mt_merge_pending_not_matched = NULL;
4312 mtstate->mt_merge_inserted = 0;
4313 mtstate->mt_merge_updated = 0;
4314 mtstate->mt_merge_deleted = 0;
4315
4316 /*----------
4317 * Resolve the target relation. This is the same as:
4318 *
4319 * - the relation for which we will fire FOR STATEMENT triggers,
4320 * - the relation into whose tuple format all captured transition tuples
4321 * must be converted, and
4322 * - the root partitioned table used for tuple routing.
4323 *
4324 * If it's a partitioned or inherited table, the root partition or
4325 * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
4326 * given explicitly in node->rootRelation. Otherwise, the target relation
4327 * is the sole relation in the node->resultRelations list.
4328 *----------
4329 */
4330 if (node->rootRelation > 0)
4331 {
4334 node->rootRelation);
4335 }
4336 else
4337 {
4338 Assert(list_length(node->resultRelations) == 1);
4339 mtstate->rootResultRelInfo = mtstate->resultRelInfo;
4340 ExecInitResultRelation(estate, mtstate->resultRelInfo,
4342 }
4343
4344 /* set up epqstate with dummy subplan data for the moment */
4345 EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
4346 node->epqParam, node->resultRelations);
4347 mtstate->fireBSTriggers = true;
4348
4349 /*
4350 * Build state for collecting transition tuples. This requires having a
4351 * valid trigger query context, so skip it in explain-only mode.
4352 */
4353 if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
4354 ExecSetupTransitionCaptureState(mtstate, estate);
4355
4356 /*
4357 * Open all the result relations and initialize the ResultRelInfo structs.
4358 * (But root relation was initialized above, if it's part of the array.)
4359 * We must do this before initializing the subplan, because direct-modify
4360 * FDWs expect their ResultRelInfos to be available.
4361 */
4362 resultRelInfo = mtstate->resultRelInfo;
4363 i = 0;
4364 foreach(l, node->resultRelations)
4365 {
4366 Index resultRelation = lfirst_int(l);
4367 List *mergeActions = NIL;
4368
4369 if (node->mergeActionLists)
4370 mergeActions = list_nth(node->mergeActionLists, i);
4371
4372 if (resultRelInfo != mtstate->rootResultRelInfo)
4373 {
4374 ExecInitResultRelation(estate, resultRelInfo, resultRelation);
4375
4376 /*
4377 * For child result relations, store the root result relation
4378 * pointer. We do so for the convenience of places that want to
4379 * look at the query's original target relation but don't have the
4380 * mtstate handy.
4381 */
4382 resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
4383 }
4384
4385 /* Initialize the usesFdwDirectModify flag */
4386 resultRelInfo->ri_usesFdwDirectModify =
4388
4389 /*
4390 * Verify result relation is a valid target for the current operation
4391 */
4392 CheckValidResultRel(resultRelInfo, operation, mergeActions);
4393
4394 resultRelInfo++;
4395 i++;
4396 }
4397
4398 /*
4399 * Now we may initialize the subplan.
4400 */
4401 outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
4402
4403 /*
4404 * Do additional per-result-relation initialization.
4405 */
4406 for (i = 0; i < nrels; i++)
4407 {
4408 resultRelInfo = &mtstate->resultRelInfo[i];
4409
4410 /* Let FDWs init themselves for foreign-table result rels */
4411 if (!resultRelInfo->ri_usesFdwDirectModify &&
4412 resultRelInfo->ri_FdwRoutine != NULL &&
4413 resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
4414 {
4415 List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
4416
4417 resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
4418 resultRelInfo,
4419 fdw_private,
4420 i,
4421 eflags);
4422 }
4423
4424 /*
4425 * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
4426 * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
4427 * tables, the FDW might have created additional junk attr(s), but
4428 * those are no concern of ours.
4429 */
4430 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4431 operation == CMD_MERGE)
4432 {
4433 char relkind;
4434
4435 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4436 if (relkind == RELKIND_RELATION ||
4437 relkind == RELKIND_MATVIEW ||
4438 relkind == RELKIND_PARTITIONED_TABLE)
4439 {
4440 resultRelInfo->ri_RowIdAttNo =
4441 ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
4442 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4443 elog(ERROR, "could not find junk ctid column");
4444 }
4445 else if (relkind == RELKIND_FOREIGN_TABLE)
4446 {
4447 /*
4448 * We don't support MERGE with foreign tables for now. (It's
4449 * problematic because the implementation uses CTID.)
4450 */
4451 Assert(operation != CMD_MERGE);
4452
4453 /*
4454 * When there is a row-level trigger, there should be a
4455 * wholerow attribute. We also require it to be present in
4456 * UPDATE and MERGE, so we can get the values of unchanged
4457 * columns.
4458 */
4459 resultRelInfo->ri_RowIdAttNo =
4461 "wholerow");
4462 if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
4463 !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4464 elog(ERROR, "could not find junk wholerow column");
4465 }
4466 else
4467 {
4468 /* Other valid target relkinds must provide wholerow */
4469 resultRelInfo->ri_RowIdAttNo =
4471 "wholerow");
4472 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4473 elog(ERROR, "could not find junk wholerow column");
4474 }
4475 }
4476 }
4477
4478 /*
4479 * If this is an inherited update/delete/merge, there will be a junk
4480 * attribute named "tableoid" present in the subplan's targetlist. It
4481 * will be used to identify the result relation for a given tuple to be
4482 * updated/deleted/merged.
4483 */
4484 mtstate->mt_resultOidAttno =
4485 ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
4486 Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || nrels == 1);
4487 mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
4488 mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
4489
4490 /* Get the root target relation */
4491 rel = mtstate->rootResultRelInfo->ri_RelationDesc;
4492
4493 /*
4494 * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
4495 * or MERGE might need this too, but only if it actually moves tuples
4496 * between partitions; in that case setup is done by
4497 * ExecCrossPartitionUpdate.
4498 */
4499 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
4500 operation == CMD_INSERT)
4502 ExecSetupPartitionTupleRouting(estate, rel);
4503
4504 /*
4505 * Initialize any WITH CHECK OPTION constraints if needed.
4506 */
4507 resultRelInfo = mtstate->resultRelInfo;
4508 foreach(l, node->withCheckOptionLists)
4509 {
4510 List *wcoList = (List *) lfirst(l);
4511 List *wcoExprs = NIL;
4512 ListCell *ll;
4513
4514 foreach(ll, wcoList)
4515 {
4516 WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
4517 ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
4518 &mtstate->ps);
4519
4520 wcoExprs = lappend(wcoExprs, wcoExpr);
4521 }
4522
4523 resultRelInfo->ri_WithCheckOptions = wcoList;
4524 resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4525 resultRelInfo++;
4526 }
4527
4528 /*
4529 * Initialize RETURNING projections if needed.
4530 */
4531 if (node->returningLists)
4532 {
4533 TupleTableSlot *slot;
4534 ExprContext *econtext;
4535
4536 /*
4537 * Initialize result tuple slot and assign its rowtype using the first
4538 * RETURNING list. We assume the rest will look the same.
4539 */
4540 mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
4541
4542 /* Set up a slot for the output of the RETURNING projection(s) */
4544 slot = mtstate->ps.ps_ResultTupleSlot;
4545
4546 /* Need an econtext too */
4547 if (mtstate->ps.ps_ExprContext == NULL)
4548 ExecAssignExprContext(estate, &mtstate->ps);
4549 econtext = mtstate->ps.ps_ExprContext;
4550
4551 /*
4552 * Build a projection for each result rel.
4553 */
4554 resultRelInfo = mtstate->resultRelInfo;
4555 foreach(l, node->returningLists)
4556 {
4557 List *rlist = (List *) lfirst(l);
4558
4559 resultRelInfo->ri_returningList = rlist;
4560 resultRelInfo->ri_projectReturning =
4561 ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
4562 resultRelInfo->ri_RelationDesc->rd_att);
4563 resultRelInfo++;
4564 }
4565 }
4566 else
4567 {
4568 /*
4569 * We still must construct a dummy result tuple type, because InitPlan
4570 * expects one (maybe should change that?).
4571 */
4572 mtstate->ps.plan->targetlist = NIL;
4573 ExecInitResultTypeTL(&mtstate->ps);
4574
4575 mtstate->ps.ps_ExprContext = NULL;
4576 }
4577
4578 /* Set the list of arbiter indexes if needed for ON CONFLICT */
4579 resultRelInfo = mtstate->resultRelInfo;
4580 if (node->onConflictAction != ONCONFLICT_NONE)
4581 {
4582 /* insert may only have one relation, inheritance is not expanded */
4583 Assert(nrels == 1);
4584 resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
4585 }
4586
4587 /*
4588 * If needed, Initialize target list, projection and qual for ON CONFLICT
4589 * DO UPDATE.
4590 */
4592 {
4594 ExprContext *econtext;
4595 TupleDesc relationDesc;
4596
4597 /* already exists if created by RETURNING processing above */
4598 if (mtstate->ps.ps_ExprContext == NULL)
4599 ExecAssignExprContext(estate, &mtstate->ps);
4600
4601 econtext = mtstate->ps.ps_ExprContext;
4602 relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
4603
4604 /* create state for DO UPDATE SET operation */
4605 resultRelInfo->ri_onConflict = onconfl;
4606
4607 /* initialize slot for the existing tuple */
4608 onconfl->oc_Existing =
4609 table_slot_create(resultRelInfo->ri_RelationDesc,
4610 &mtstate->ps.state->es_tupleTable);
4611
4612 /*
4613 * Create the tuple slot for the UPDATE SET projection. We want a slot
4614 * of the table's type here, because the slot will be used to insert
4615 * into the table, and for RETURNING processing - which may access
4616 * system attributes.
4617 */
4618 onconfl->oc_ProjSlot =
4619 table_slot_create(resultRelInfo->ri_RelationDesc,
4620 &mtstate->ps.state->es_tupleTable);
4621
4622 /* build UPDATE SET projection state */
4623 onconfl->oc_ProjInfo =
4625 true,
4626 node->onConflictCols,
4627 relationDesc,
4628 econtext,
4629 onconfl->oc_ProjSlot,
4630 &mtstate->ps);
4631
4632 /* initialize state to evaluate the WHERE clause, if any */
4633 if (node->onConflictWhere)
4634 {
4635 ExprState *qualexpr;
4636
4637 qualexpr = ExecInitQual((List *) node->onConflictWhere,
4638 &mtstate->ps);
4639 onconfl->oc_WhereClause = qualexpr;
4640 }
4641 }
4642
4643 /*
4644 * If we have any secondary relations in an UPDATE or DELETE, they need to
4645 * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
4646 * EvalPlanQual mechanism needs to be told about them. This also goes for
4647 * the source relations in a MERGE. Locate the relevant ExecRowMarks.
4648 */
4649 arowmarks = NIL;
4650 foreach(l, node->rowMarks)
4651 {
4653 ExecRowMark *erm;
4654 ExecAuxRowMark *aerm;
4655
4656 /* ignore "parent" rowmarks; they are irrelevant at runtime */
4657 if (rc->isParent)
4658 continue;
4659
4660 /* Find ExecRowMark and build ExecAuxRowMark */
4661 erm = ExecFindRowMark(estate, rc->rti, false);
4662 aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
4663 arowmarks = lappend(arowmarks, aerm);
4664 }
4665
4666 /* For a MERGE command, initialize its state */
4667 if (mtstate->operation == CMD_MERGE)
4668 ExecInitMerge(mtstate, estate);
4669
4670 EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
4671
4672 /*
4673 * If there are a lot of result relations, use a hash table to speed the
4674 * lookups. If there are not a lot, a simple linear search is faster.
4675 *
4676 * It's not clear where the threshold is, but try 64 for starters. In a
4677 * debugging build, use a small threshold so that we get some test
4678 * coverage of both code paths.
4679 */
4680#ifdef USE_ASSERT_CHECKING
4681#define MT_NRELS_HASH 4
4682#else
4683#define MT_NRELS_HASH 64
4684#endif
4685 if (nrels >= MT_NRELS_HASH)
4686 {
4687 HASHCTL hash_ctl;
4688
4689 hash_ctl.keysize = sizeof(Oid);
4690 hash_ctl.entrysize = sizeof(MTTargetRelLookup);
4691 hash_ctl.hcxt = CurrentMemoryContext;
4692 mtstate->mt_resultOidHash =
4693 hash_create("ModifyTable target hash",
4694 nrels, &hash_ctl,
4696 for (i = 0; i < nrels; i++)
4697 {
4698 Oid hashkey;
4699 MTTargetRelLookup *mtlookup;
4700 bool found;
4701
4702 resultRelInfo = &mtstate->resultRelInfo[i];
4703 hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
4704 mtlookup = (MTTargetRelLookup *)
4705 hash_search(mtstate->mt_resultOidHash, &hashkey,
4706 HASH_ENTER, &found);
4707 Assert(!found);
4708 mtlookup->relationIndex = i;
4709 }
4710 }
4711 else
4712 mtstate->mt_resultOidHash = NULL;
4713
4714 /*
4715 * Determine if the FDW supports batch insert and determine the batch size
4716 * (a FDW may support batching, but it may be disabled for the
4717 * server/table).
4718 *
4719 * We only do this for INSERT, so that for UPDATE/DELETE the batch size
4720 * remains set to 0.
4721 */
4722 if (operation == CMD_INSERT)
4723 {
4724 /* insert may only have one relation, inheritance is not expanded */
4725 Assert(nrels == 1);
4726 resultRelInfo = mtstate->resultRelInfo;
4727 if (!resultRelInfo->ri_usesFdwDirectModify &&
4728 resultRelInfo->ri_FdwRoutine != NULL &&
4729 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
4730 resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
4731 {
4732 resultRelInfo->ri_BatchSize =
4733 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
4734 Assert(resultRelInfo->ri_BatchSize >= 1);
4735 }
4736 else
4737 resultRelInfo->ri_BatchSize = 1;
4738 }
4739
4740 /*
4741 * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
4742 * to estate->es_auxmodifytables so that it will be run to completion by
4743 * ExecPostprocessPlan. (It'd actually work fine to add the primary
4744 * ModifyTable node too, but there's no need.) Note the use of lcons not
4745 * lappend: we need later-initialized ModifyTable nodes to be shut down
4746 * before earlier ones. This ensures that we don't throw away RETURNING
4747 * rows that need to be seen by a later CTE subplan.
4748 */
4749 if (!mtstate->canSetTag)
4750 estate->es_auxmodifytables = lcons(mtstate,
4751 estate->es_auxmodifytables);
4752
4753 return mtstate;
4754}
#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:568
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2386
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2409
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, List *mergeActions)
Definition: execMain.c:1026
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam, List *resultRelations)
Definition: execMain.c:2548
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2590
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1942
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1986
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:859
#define EXEC_FLAG_BACKWARD
Definition: executor.h:68
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:65
#define EXEC_FLAG_MARK
Definition: executor.h:69
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
List * lcons(void *datum, List *list)
Definition: list.c:495
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
struct MTTargetRelLookup MTTargetRelLookup
#define MT_NRELS_HASH
static void ExecInitMerge(ModifyTableState *mtstate, EState *estate)
@ ONCONFLICT_NONE
Definition: nodes.h:418
CmdType
Definition: nodes.h:263
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define lfirst_int(lc)
Definition: pg_list.h:173
#define linitial_int(l)
Definition: pg_list.h:179
#define linitial(l)
Definition: pg_list.h:178
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
List * es_auxmodifytables
Definition: execnodes.h:692
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:1419
double mt_merge_deleted
Definition: execnodes.h:1424
double mt_merge_inserted
Definition: execnodes.h:1422
double mt_merge_updated
Definition: execnodes.h:1423
HTAB * mt_resultOidHash
Definition: execnodes.h:1391
List * arbiterIndexes
Definition: plannodes.h:247
List * onConflictCols
Definition: plannodes.h:249
CmdType operation
Definition: plannodes.h:233
int epqParam
Definition: plannodes.h:245
List * resultRelations
Definition: plannodes.h:238
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:243
List * onConflictSet
Definition: plannodes.h:248
bool canSetTag
Definition: plannodes.h:234
List * fdwPrivLists
Definition: plannodes.h:242
List * returningLists
Definition: plannodes.h:241
List * withCheckOptionLists
Definition: plannodes.h:240
Index rootRelation
Definition: plannodes.h:236
Node * onConflictWhere
Definition: plannodes.h:250
List * rowMarks
Definition: plannodes.h:244
OnConflictAction onConflictAction
Definition: plannodes.h:246
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:413
TupleTableSlot * oc_Existing
Definition: execnodes.h:412
ExprState * oc_WhereClause
Definition: execnodes.h:415
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:414
bool isParent
Definition: plannodes.h:1387
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1164
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1132
List * targetlist
Definition: plannodes.h:153
TupleDesc rd_att
Definition: rel.h:112
Form_pg_class rd_rel
Definition: rel.h:111
OnConflictSetState * ri_onConflict
Definition: execnodes.h:553
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:550
List * ri_WithCheckOptionExprs
Definition: execnodes.h:528
List * ri_returningList
Definition: execnodes.h:544
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:474
int ri_BatchSize
Definition: execnodes.h:520

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, 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(), lcons(), lfirst, lfirst_int, lfirst_node, linitial, linitial_int, list_length(), list_nth(), makeNode, ModifyTable::mergeActionLists, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, ModifyTableState::mt_merge_pending_not_matched, ModifyTableState::mt_merge_updated, ModifyTableState::mt_nrels, MT_NRELS_HASH, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_resultOidAttno, ModifyTableState::mt_resultOidHash, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTableState::operation, ModifyTable::operation, outerPlan, outerPlanState, palloc(), PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, MTTargetRelLookup::relationIndex, ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_usesFdwDirectModify, ResultRelInfo::ri_WithCheckOptionExprs, ResultRelInfo::ri_WithCheckOptions, ModifyTable::rootRelation, ModifyTableState::rootResultRelInfo, ModifyTable::rowMarks, PlanRowMark::rti, PlanState::state, table_slot_create(), Plan::targetlist, TTSOpsVirtual, and ModifyTable::withCheckOptionLists.

Referenced by ExecInitNode().

◆ ExecInitStoredGenerated()

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

Definition at line 352 of file nodeModifyTable.c.

355{
356 Relation rel = resultRelInfo->ri_RelationDesc;
357 TupleDesc tupdesc = RelationGetDescr(rel);
358 int natts = tupdesc->natts;
359 ExprState **ri_GeneratedExprs;
360 int ri_NumGeneratedNeeded;
361 Bitmapset *updatedCols;
362 MemoryContext oldContext;
363
364 /* Nothing to do if no generated columns */
365 if (!(tupdesc->constr && tupdesc->constr->has_generated_stored))
366 return;
367
368 /*
369 * In an UPDATE, we can skip computing any generated columns that do not
370 * depend on any UPDATE target column. But if there is a BEFORE ROW
371 * UPDATE trigger, we cannot skip because the trigger might change more
372 * columns.
373 */
374 if (cmdtype == CMD_UPDATE &&
376 updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
377 else
378 updatedCols = NULL;
379
380 /*
381 * Make sure these data structures are built in the per-query memory
382 * context so they'll survive throughout the query.
383 */
384 oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
385
386 ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
387 ri_NumGeneratedNeeded = 0;
388
389 for (int i = 0; i < natts; i++)
390 {
391 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
392 {
393 Expr *expr;
394
395 /* Fetch the GENERATED AS expression tree */
396 expr = (Expr *) build_column_default(rel, i + 1);
397 if (expr == NULL)
398 elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
399 i + 1, RelationGetRelationName(rel));
400
401 /*
402 * If it's an update with a known set of update target columns,
403 * see if we can skip the computation.
404 */
405 if (updatedCols)
406 {
407 Bitmapset *attrs_used = NULL;
408
409 pull_varattnos((Node *) expr, 1, &attrs_used);
410
411 if (!bms_overlap(updatedCols, attrs_used))
412 continue; /* need not update this column */
413 }
414
415 /* No luck, so prepare the expression for execution */
416 ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
417 ri_NumGeneratedNeeded++;
418
419 /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
420 if (cmdtype == CMD_UPDATE)
421 resultRelInfo->ri_extraUpdatedCols =
422 bms_add_member(resultRelInfo->ri_extraUpdatedCols,
424 }
425 }
426
427 /* Save in appropriate set of fields */
428 if (cmdtype == CMD_UPDATE)
429 {
430 /* Don't call twice */
431 Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
432
433 resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
434 resultRelInfo->ri_NumGeneratedNeededU = ri_NumGeneratedNeeded;
435 }
436 else
437 {
438 /* Don't call twice */
439 Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
440
441 resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
442 resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
443 }
444
445 MemoryContextSwitchTo(oldContext);
446}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:582
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:743
Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition: execUtils.c:1333
void * palloc0(Size size)
Definition: mcxt.c:1347
Node * build_column_default(Relation rel, int attrno)
TriggerDesc * trigdesc
Definition: rel.h:117
Bitmapset * ri_extraUpdatedCols
Definition: execnodes.h:477
bool trig_update_before_row
Definition: reltrigger.h:61
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:295

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, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc0(), pull_varattnos(), RelationGetDescr, RelationGetRelationName, ResultRelInfo::ri_extraUpdatedCols, 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().

◆ ExecInitUpdateProjection()

static void ExecInitUpdateProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 618 of file nodeModifyTable.c.

620{
621 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
622 Plan *subplan = outerPlan(node);
623 EState *estate = mtstate->ps.state;
624 TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
625 int whichrel;
626 List *updateColnos;
627
628 /*
629 * Usually, mt_lastResultIndex matches the target rel. If it happens not
630 * to, we can get the index the hard way with an integer division.
631 */
632 whichrel = mtstate->mt_lastResultIndex;
633 if (resultRelInfo != mtstate->resultRelInfo + whichrel)
634 {
635 whichrel = resultRelInfo - mtstate->resultRelInfo;
636 Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
637 }
638
639 updateColnos = (List *) list_nth(node->updateColnosLists, whichrel);
640
641 /*
642 * For UPDATE, we use the old tuple to fill up missing values in the tuple
643 * produced by the subplan to get the new tuple. We need two slots, both
644 * matching the table's desired format.
645 */
646 resultRelInfo->ri_oldTupleSlot =
647 table_slot_create(resultRelInfo->ri_RelationDesc,
648 &estate->es_tupleTable);
649 resultRelInfo->ri_newTupleSlot =
650 table_slot_create(resultRelInfo->ri_RelationDesc,
651 &estate->es_tupleTable);
652
653 /* need an expression context to do the projection */
654 if (mtstate->ps.ps_ExprContext == NULL)
655 ExecAssignExprContext(estate, &mtstate->ps);
656
657 resultRelInfo->ri_projectNew =
658 ExecBuildUpdateProjection(subplan->targetlist,
659 false, /* subplan did the evaluation */
660 updateColnos,
661 relDesc,
662 mtstate->ps.ps_ExprContext,
663 resultRelInfo->ri_newTupleSlot,
664 &mtstate->ps);
665
666 resultRelInfo->ri_projectNewInfoValid = true;
667}
List * updateColnosLists
Definition: plannodes.h:239

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

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

764{
765 ModifyTableState *mtstate = context->mtstate;
766 EState *estate = context->estate;
767 Relation resultRelationDesc;
768 List *recheckIndexes = NIL;
769 TupleTableSlot *planSlot = context->planSlot;
770 TupleTableSlot *result = NULL;
771 TransitionCaptureState *ar_insert_trig_tcs;
772 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
773 OnConflictAction onconflict = node->onConflictAction;
775 MemoryContext oldContext;
776
777 /*
778 * If the input result relation is a partitioned table, find the leaf
779 * partition to insert the tuple into.
780 */
781 if (proute)
782 {
783 ResultRelInfo *partRelInfo;
784
785 slot = ExecPrepareTupleRouting(mtstate, estate, proute,
786 resultRelInfo, slot,
787 &partRelInfo);
788 resultRelInfo = partRelInfo;
789 }
790
792
793 resultRelationDesc = resultRelInfo->ri_RelationDesc;
794
795 /*
796 * Open the table's indexes, if we have not done so already, so that we
797 * can add new index entries for the inserted tuple.
798 */
799 if (resultRelationDesc->rd_rel->relhasindex &&
800 resultRelInfo->ri_IndexRelationDescs == NULL)
801 ExecOpenIndices(resultRelInfo, onconflict != ONCONFLICT_NONE);
802
803 /*
804 * BEFORE ROW INSERT Triggers.
805 *
806 * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
807 * INSERT ... ON CONFLICT statement. We cannot check for constraint
808 * violations before firing these triggers, because they can change the
809 * values to insert. Also, they can run arbitrary user-defined code with
810 * side-effects that we can't cancel by just not inserting the tuple.
811 */
812 if (resultRelInfo->ri_TrigDesc &&
813 resultRelInfo->ri_TrigDesc->trig_insert_before_row)
814 {
815 /* Flush any pending inserts, so rows are visible to the triggers */
817 ExecPendingInserts(estate);
818
819 if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
820 return NULL; /* "do nothing" */
821 }
822
823 /* INSTEAD OF ROW INSERT Triggers */
824 if (resultRelInfo->ri_TrigDesc &&
825 resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
826 {
827 if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
828 return NULL; /* "do nothing" */
829 }
830 else if (resultRelInfo->ri_FdwRoutine)
831 {
832 /*
833 * GENERATED expressions might reference the tableoid column, so
834 * (re-)initialize tts_tableOid before evaluating them.
835 */
836 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
837
838 /*
839 * Compute stored generated columns
840 */
841 if (resultRelationDesc->rd_att->constr &&
842 resultRelationDesc->rd_att->constr->has_generated_stored)
843 ExecComputeStoredGenerated(resultRelInfo, estate, slot,
844 CMD_INSERT);
845
846 /*
847 * If the FDW supports batching, and batching is requested, accumulate
848 * rows and insert them in batches. Otherwise use the per-row inserts.
849 */
850 if (resultRelInfo->ri_BatchSize > 1)
851 {
852 bool flushed = false;
853
854 /*
855 * When we've reached the desired batch size, perform the
856 * insertion.
857 */
858 if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize)
859 {
860 ExecBatchInsert(mtstate, resultRelInfo,
861 resultRelInfo->ri_Slots,
862 resultRelInfo->ri_PlanSlots,
863 resultRelInfo->ri_NumSlots,
864 estate, canSetTag);
865 flushed = true;
866 }
867
868 oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
869
870 if (resultRelInfo->ri_Slots == NULL)
871 {
872 resultRelInfo->ri_Slots = palloc(sizeof(TupleTableSlot *) *
873 resultRelInfo->ri_BatchSize);
874 resultRelInfo->ri_PlanSlots = palloc(sizeof(TupleTableSlot *) *
875 resultRelInfo->ri_BatchSize);
876 }
877
878 /*
879 * Initialize the batch slots. We don't know how many slots will
880 * be needed, so we initialize them as the batch grows, and we
881 * keep them across batches. To mitigate an inefficiency in how
882 * resource owner handles objects with many references (as with
883 * many slots all referencing the same tuple descriptor) we copy
884 * the appropriate tuple descriptor for each slot.
885 */
886 if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
887 {
889 TupleDesc plan_tdesc =
891
892 resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
893 MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
894
895 resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
896 MakeSingleTupleTableSlot(plan_tdesc, planSlot->tts_ops);
897
898 /* remember how many batch slots we initialized */
899 resultRelInfo->ri_NumSlotsInitialized++;
900 }
901
902 ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
903 slot);
904
905 ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
906 planSlot);
907
908 /*
909 * If these are the first tuples stored in the buffers, add the
910 * target rel and the mtstate to the
911 * es_insert_pending_result_relations and
912 * es_insert_pending_modifytables lists respectively, except in
913 * the case where flushing was done above, in which case they
914 * would already have been added to the lists, so no need to do
915 * this.
916 */
917 if (resultRelInfo->ri_NumSlots == 0 && !flushed)
918 {
920 resultRelInfo));
923 resultRelInfo);
925 lappend(estate->es_insert_pending_modifytables, mtstate);
926 }
928 resultRelInfo));
929
930 resultRelInfo->ri_NumSlots++;
931
932 MemoryContextSwitchTo(oldContext);
933
934 return NULL;
935 }
936
937 /*
938 * insert into foreign table: let the FDW do it
939 */
940 slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
941 resultRelInfo,
942 slot,
943 planSlot);
944
945 if (slot == NULL) /* "do nothing" */
946 return NULL;
947
948 /*
949 * AFTER ROW Triggers or RETURNING expressions might reference the
950 * tableoid column, so (re-)initialize tts_tableOid before evaluating
951 * them. (This covers the case where the FDW replaced the slot.)
952 */
953 slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
954 }
955 else
956 {
957 WCOKind wco_kind;
958
959 /*
960 * Constraints and GENERATED expressions might reference the tableoid
961 * column, so (re-)initialize tts_tableOid before evaluating them.
962 */
963 slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
964
965 /*
966 * Compute stored generated columns
967 */
968 if (resultRelationDesc->rd_att->constr &&
969 resultRelationDesc->rd_att->constr->has_generated_stored)
970 ExecComputeStoredGenerated(resultRelInfo, estate, slot,
971 CMD_INSERT);
972
973 /*
974 * Check any RLS WITH CHECK policies.
975 *
976 * Normally we should check INSERT policies. But if the insert is the
977 * result of a partition key update that moved the tuple to a new
978 * partition, we should instead check UPDATE policies, because we are
979 * executing policies defined on the target table, and not those
980 * defined on the child partitions.
981 *
982 * If we're running MERGE, we refer to the action that we're executing
983 * to know if we're doing an INSERT or UPDATE to a partition table.
984 */
985 if (mtstate->operation == CMD_UPDATE)
986 wco_kind = WCO_RLS_UPDATE_CHECK;
987 else if (mtstate->operation == CMD_MERGE)
988 wco_kind = (mtstate->mt_merge_action->mas_action->commandType == CMD_UPDATE) ?
990 else
991 wco_kind = WCO_RLS_INSERT_CHECK;
992
993 /*
994 * ExecWithCheckOptions() will skip any WCOs which are not of the kind
995 * we are looking for at this point.
996 */
997 if (resultRelInfo->ri_WithCheckOptions != NIL)
998 ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
999
1000 /*
1001 * Check the constraints of the tuple.
1002 */
1003 if (resultRelationDesc->rd_att->constr)
1004 ExecConstraints(resultRelInfo, slot, estate);
1005
1006 /*
1007 * Also check the tuple against the partition constraint, if there is
1008 * one; except that if we got here via tuple-routing, we don't need to
1009 * if there's no BR trigger defined on the partition.
1010 */
1011 if (resultRelationDesc->rd_rel->relispartition &&
1012 (resultRelInfo->ri_RootResultRelInfo == NULL ||
1013 (resultRelInfo->ri_TrigDesc &&
1014 resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
1015 ExecPartitionCheck(resultRelInfo, slot, estate, true);
1016
1017 if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
1018 {
1019 /* Perform a speculative insertion. */
1020 uint32 specToken;
1021 ItemPointerData conflictTid;
1022 ItemPointerData invalidItemPtr;
1023 bool specConflict;
1024 List *arbiterIndexes;
1025
1026 ItemPointerSetInvalid(&invalidItemPtr);
1027 arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
1028
1029 /*
1030 * Do a non-conclusive check for conflicts first.
1031 *
1032 * We're not holding any locks yet, so this doesn't guarantee that
1033 * the later insert won't conflict. But it avoids leaving behind
1034 * a lot of canceled speculative insertions, if you run a lot of
1035 * INSERT ON CONFLICT statements that do conflict.
1036 *
1037 * We loop back here if we find a conflict below, either during
1038 * the pre-check, or when we re-check after inserting the tuple
1039 * speculatively. Better allow interrupts in case some bug makes
1040 * this an infinite loop.
1041 */
1042 vlock:
1044 specConflict = false;
1045 if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
1046 &conflictTid, &invalidItemPtr,
1047 arbiterIndexes))
1048 {
1049 /* committed conflict tuple found */
1050 if (onconflict == ONCONFLICT_UPDATE)
1051 {
1052 /*
1053 * In case of ON CONFLICT DO UPDATE, execute the UPDATE
1054 * part. Be prepared to retry if the UPDATE fails because
1055 * of another concurrent UPDATE/DELETE to the conflict
1056 * tuple.
1057 */
1058 TupleTableSlot *returning = NULL;
1059
1060 if (ExecOnConflictUpdate(context, resultRelInfo,
1061 &conflictTid, slot, canSetTag,
1062 &returning))
1063 {
1064 InstrCountTuples2(&mtstate->ps, 1);
1065 return returning;
1066 }
1067 else
1068 goto vlock;
1069 }
1070 else
1071 {
1072 /*
1073 * In case of ON CONFLICT DO NOTHING, do nothing. However,
1074 * verify that the tuple is visible to the executor's MVCC
1075 * snapshot at higher isolation levels.
1076 *
1077 * Using ExecGetReturningSlot() to store the tuple for the
1078 * recheck isn't that pretty, but we can't trivially use
1079 * the input slot, because it might not be of a compatible
1080 * type. As there's no conflicting usage of
1081 * ExecGetReturningSlot() in the DO NOTHING case...
1082 */
1083 Assert(onconflict == ONCONFLICT_NOTHING);
1084 ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
1085 ExecGetReturningSlot(estate, resultRelInfo));
1086 InstrCountTuples2(&mtstate->ps, 1);
1087 return NULL;
1088 }
1089 }
1090
1091 /*
1092 * Before we start insertion proper, acquire our "speculative
1093 * insertion lock". Others can use that to wait for us to decide
1094 * if we're going to go ahead with the insertion, instead of
1095 * waiting for the whole transaction to complete.
1096 */
1098
1099 /* insert the tuple, with the speculative token */
1100 table_tuple_insert_speculative(resultRelationDesc, slot,
1101 estate->es_output_cid,
1102 0,
1103 NULL,
1104 specToken);
1105
1106 /* insert index entries for tuple */
1107 recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1108 slot, estate, false, true,
1109 &specConflict,
1110 arbiterIndexes,
1111 false);
1112
1113 /* adjust the tuple's state accordingly */
1114 table_tuple_complete_speculative(resultRelationDesc, slot,
1115 specToken, !specConflict);
1116
1117 /*
1118 * Wake up anyone waiting for our decision. They will re-check
1119 * the tuple, see that it's no longer speculative, and wait on our
1120 * XID as if this was a regularly inserted tuple all along. Or if
1121 * we killed the tuple, they will see it's dead, and proceed as if
1122 * the tuple never existed.
1123 */
1125
1126 /*
1127 * If there was a conflict, start from the beginning. We'll do
1128 * the pre-check again, which will now find the conflicting tuple
1129 * (unless it aborts before we get there).
1130 */
1131 if (specConflict)
1132 {
1133 list_free(recheckIndexes);
1134 goto vlock;
1135 }
1136
1137 /* Since there was no insertion conflict, we're done */
1138 }
1139 else
1140 {
1141 /* insert the tuple normally */
1142 table_tuple_insert(resultRelationDesc, slot,
1143 estate->es_output_cid,
1144 0, NULL);
1145
1146 /* insert index entries for tuple */
1147 if (resultRelInfo->ri_NumIndices > 0)
1148 recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1149 slot, estate, false,
1150 false, NULL, NIL,
1151 false);
1152 }
1153 }
1154
1155 if (canSetTag)
1156 (estate->es_processed)++;
1157
1158 /*
1159 * If this insert is the result of a partition key update that moved the
1160 * tuple to a new partition, put this row into the transition NEW TABLE,
1161 * if there is one. We need to do this separately for DELETE and INSERT
1162 * because they happen on different tables.
1163 */
1164 ar_insert_trig_tcs = mtstate->mt_transition_capture;
1165 if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
1167 {
1168 ExecARUpdateTriggers(estate, resultRelInfo,
1169 NULL, NULL,
1170 NULL,
1171 NULL,
1172 slot,
1173 NULL,
1174 mtstate->mt_transition_capture,
1175 false);
1176
1177 /*
1178 * We've already captured the NEW TABLE row, so make sure any AR
1179 * INSERT trigger fired below doesn't capture it again.
1180 */
1181 ar_insert_trig_tcs = NULL;
1182 }
1183
1184 /* AFTER ROW INSERT Triggers */
1185 ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
1186 ar_insert_trig_tcs);
1187
1188 list_free(recheckIndexes);
1189
1190 /*
1191 * Check any WITH CHECK OPTION constraints from parent views. We are
1192 * required to do this after testing all constraints and uniqueness
1193 * violations per the SQL spec, so we do it after actually inserting the
1194 * record into the heap and all indexes.
1195 *
1196 * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
1197 * tuple will never be seen, if it violates the WITH CHECK OPTION.
1198 *
1199 * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1200 * are looking for at this point.
1201 */
1202 if (resultRelInfo->ri_WithCheckOptions != NIL)
1203 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1204
1205 /* Process RETURNING if present */
1206 if (resultRelInfo->ri_projectReturning)
1207 result = ExecProcessReturning(resultRelInfo, slot, planSlot);
1208
1209 if (inserted_tuple)
1210 *inserted_tuple = slot;
1211 if (insert_destrel)
1212 *insert_destrel = resultRelInfo;
1213
1214 return result;
1215}
uint32_t uint32
Definition: c.h:485
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:160
bool ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, ItemPointer tupleid, List *arbiterIndexes)
Definition: execIndexing.c:536
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool update, bool noDupErr, bool *specConflict, List *arbiterIndexes, bool onlySummarizing)
Definition: execIndexing.c:303
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1801
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1925
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1425
#define InstrCountTuples2(node, delta)
Definition: execnodes.h:1225
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
bool list_member_ptr(const List *list, const void *datum)
Definition: list.c:682
void list_free(List *list)
Definition: list.c:1546
uint32 SpeculativeInsertionLockAcquire(TransactionId xid)
Definition: lmgr.c:771
void SpeculativeInsertionLockRelease(TransactionId xid)
Definition: lmgr.c:797
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
static void ExecCheckTIDVisible(EState *estate, ResultRelInfo *relinfo, ItemPointer tid, TupleTableSlot *tempSlot)
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot, ResultRelInfo **partRelInfo)
static bool ExecOnConflictUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *excludedSlot, bool canSetTag, TupleTableSlot **returning)
static void ExecBatchInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
OnConflictAction
Definition: nodes.h:417
@ ONCONFLICT_NOTHING
Definition: nodes.h:419
WCOKind
Definition: parsenodes.h:1358
@ WCO_RLS_INSERT_CHECK
Definition: parsenodes.h:1360
@ WCO_RLS_UPDATE_CHECK
Definition: parsenodes.h:1361
List * es_insert_pending_modifytables
Definition: execnodes.h:737
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:232
CmdType commandType
Definition: primnodes.h:2007
MergeActionState * mt_merge_action
Definition: execnodes.h:1412
int ri_NumIndices
Definition: execnodes.h:462
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:465
bool trig_insert_instead_row
Definition: reltrigger.h:58
bool trig_insert_before_row
Definition: reltrigger.h:56
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
static void table_tuple_complete_speculative(Relation rel, TupleTableSlot *slot, uint32 specToken, bool succeeded)
Definition: tableam.h:1440
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate)
Definition: tableam.h:1407
static void table_tuple_insert_speculative(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate, uint32 specToken)
Definition: tableam.h:1426
bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2459
bool ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2552
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:235
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:453

References Assert, CHECK_FOR_INTERRUPTS, CMD_INSERT, CMD_MERGE, CMD_UPDATE, MergeAction::commandType, TupleDescData::constr, 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(), ExecComputeStoredGenerated(), ExecConstraints(), ExecCopySlot(), FdwRoutine::ExecForeignInsert, ExecGetReturningSlot(), ExecInsertIndexTuples(), ExecIRInsertTriggers(), ExecMaterializeSlot(), ExecOnConflictUpdate(), ExecOpenIndices(), ExecPartitionCheck(), ExecPendingInserts(), ExecPrepareTupleRouting(), ExecProcessReturning(), ExecWithCheckOptions(), GetCurrentTransactionId(), TupleConstr::has_generated_stored, if(), InstrCountTuples2, ItemPointerSetInvalid(), lappend(), list_free(), list_member_ptr(), MakeSingleTupleTableSlot(), MergeActionState::mas_action, MemoryContextSwitchTo(), ModifyTableState::mt_merge_action, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, palloc(), PlanState::plan, ModifyTableContext::planSlot, ModifyTableState::ps, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_IndexRelationDescs, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_NumSlots, ResultRelInfo::ri_NumSlotsInitialized, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_PlanSlots, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_Slots, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, SpeculativeInsertionLockAcquire(), SpeculativeInsertionLockRelease(), table_tuple_complete_speculative(), table_tuple_insert(), table_tuple_insert_speculative(), TransitionCaptureState::tcs_update_new_table, TriggerDesc::trig_insert_before_row, TriggerDesc::trig_insert_instead_row, TupleTableSlot::tts_ops, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_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 4231 of file nodeModifyTable.c.

4233{
4234 if (node->mt_resultOidHash)
4235 {
4236 /* Use the pre-built hash table to locate the rel */
4237 MTTargetRelLookup *mtlookup;
4238
4239 mtlookup = (MTTargetRelLookup *)
4240 hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
4241 if (mtlookup)
4242 {
4243 if (update_cache)
4244 {
4245 node->mt_lastResultOid = resultoid;
4246 node->mt_lastResultIndex = mtlookup->relationIndex;
4247 }
4248 return node->resultRelInfo + mtlookup->relationIndex;
4249 }
4250 }
4251 else
4252 {
4253 /* With few target rels, just search the ResultRelInfo array */
4254 for (int ndx = 0; ndx < node->mt_nrels; ndx++)
4255 {
4256 ResultRelInfo *rInfo = node->resultRelInfo + ndx;
4257
4258 if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
4259 {
4260 if (update_cache)
4261 {
4262 node->mt_lastResultOid = resultoid;
4263 node->mt_lastResultIndex = ndx;
4264 }
4265 return rInfo;
4266 }
4267 }
4268 }
4269
4270 if (!missing_ok)
4271 elog(ERROR, "incorrect result relation OID %u", resultoid);
4272 return NULL;
4273}
@ 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 2744 of file nodeModifyTable.c.

2746{
2747 TupleTableSlot *rslot = NULL;
2748 bool matched;
2749
2750 /*-----
2751 * If we are dealing with a WHEN MATCHED case, tupleid or oldtuple is
2752 * valid, depending on whether the result relation is a table or a view.
2753 * We execute the first action for which the additional WHEN MATCHED AND
2754 * quals pass. If an action without quals is found, that action is
2755 * executed.
2756 *
2757 * Similarly, in the WHEN NOT MATCHED BY SOURCE case, tupleid or oldtuple
2758 * is valid, and we look at the given WHEN NOT MATCHED BY SOURCE actions
2759 * in sequence until one passes. This is almost identical to the WHEN
2760 * MATCHED case, and both cases are handled by ExecMergeMatched().
2761 *
2762 * Finally, in the WHEN NOT MATCHED [BY TARGET] case, both tupleid and
2763 * oldtuple are invalid, and we look at the given WHEN NOT MATCHED [BY
2764 * TARGET] actions in sequence until one passes.
2765 *
2766 * Things get interesting in case of concurrent update/delete of the
2767 * target tuple. Such concurrent update/delete is detected while we are
2768 * executing a WHEN MATCHED or WHEN NOT MATCHED BY SOURCE action.
2769 *
2770 * A concurrent update can:
2771 *
2772 * 1. modify the target tuple so that the results from checking any
2773 * additional quals attached to WHEN MATCHED or WHEN NOT MATCHED BY
2774 * SOURCE actions potentially change, but the result from the join
2775 * quals does not change.
2776 *
2777 * In this case, we are still dealing with the same kind of match
2778 * (MATCHED or NOT MATCHED BY SOURCE). We recheck the same list of
2779 * actions from the start and choose the first one that satisfies the
2780 * new target tuple.
2781 *
2782 * 2. modify the target tuple in the WHEN MATCHED case so that the join
2783 * quals no longer pass and hence the source and target tuples no
2784 * longer match.
2785 *
2786 * In this case, we are now dealing with a NOT MATCHED case, and we
2787 * process both WHEN NOT MATCHED BY SOURCE and WHEN NOT MATCHED [BY
2788 * TARGET] actions. First ExecMergeMatched() processes the list of
2789 * WHEN NOT MATCHED BY SOURCE actions in sequence until one passes,
2790 * then ExecMergeNotMatched() processes any WHEN NOT MATCHED [BY
2791 * TARGET] actions in sequence until one passes. Thus we may execute
2792 * two actions; one of each kind.
2793 *
2794 * Thus we support concurrent updates that turn MATCHED candidate rows
2795 * into NOT MATCHED rows. However, we do not attempt to support cases
2796 * that would turn NOT MATCHED rows into MATCHED rows, or which would
2797 * cause a target row to match a different source row.
2798 *
2799 * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED
2800 * [BY TARGET].
2801 *
2802 * ExecMergeMatched() takes care of following the update chain and
2803 * re-finding the qualifying WHEN MATCHED or WHEN NOT MATCHED BY SOURCE
2804 * action, as long as the target tuple still exists. If the target tuple
2805 * gets deleted or a concurrent update causes the join quals to fail, it
2806 * returns a matched status of false and we call ExecMergeNotMatched().
2807 * Given that ExecMergeMatched() always makes progress by following the
2808 * update chain and we never switch from ExecMergeNotMatched() to
2809 * ExecMergeMatched(), there is no risk of a livelock.
2810 */
2811 matched = tupleid != NULL || oldtuple != NULL;
2812 if (matched)
2813 rslot = ExecMergeMatched(context, resultRelInfo, tupleid, oldtuple,
2814 canSetTag, &matched);
2815
2816 /*
2817 * Deal with the NOT MATCHED case (either a NOT MATCHED tuple from the
2818 * join, or a previously MATCHED tuple for which ExecMergeMatched() set
2819 * "matched" to false, indicating that it no longer matches).
2820 */
2821 if (!matched)
2822 {
2823 /*
2824 * If a concurrent update turned a MATCHED case into a NOT MATCHED
2825 * case, and we have both WHEN NOT MATCHED BY SOURCE and WHEN NOT
2826 * MATCHED [BY TARGET] actions, and there is a RETURNING clause,
2827 * ExecMergeMatched() may have already executed a WHEN NOT MATCHED BY
2828 * SOURCE action, and computed the row to return. If so, we cannot
2829 * execute a WHEN NOT MATCHED [BY TARGET] action now, so mark it as
2830 * pending (to be processed on the next call to ExecModifyTable()).
2831 * Otherwise, just process the action now.
2832 */
2833 if (rslot == NULL)
2834 rslot = ExecMergeNotMatched(context, resultRelInfo, canSetTag);
2835 else
2836 context->mtstate->mt_merge_pending_not_matched = context->planSlot;
2837 }
2838
2839 return rslot;
2840}
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 2870 of file nodeModifyTable.c.

2873{
2874 ModifyTableState *mtstate = context->mtstate;
2875 List **mergeActions = resultRelInfo->ri_MergeActions;
2876 ItemPointerData lockedtid;
2877 List *actionStates;
2878 TupleTableSlot *newslot = NULL;
2879 TupleTableSlot *rslot = NULL;
2880 EState *estate = context->estate;
2881 ExprContext *econtext = mtstate->ps.ps_ExprContext;
2882 bool isNull;
2883 EPQState *epqstate = &mtstate->mt_epqstate;
2884 ListCell *l;
2885
2886 /* Expect matched to be true on entry */
2887 Assert(*matched);
2888
2889 /*
2890 * If there are no WHEN MATCHED or WHEN NOT MATCHED BY SOURCE actions, we
2891 * are done.
2892 */
2893 if (mergeActions[MERGE_WHEN_MATCHED] == NIL &&
2894 mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] == NIL)
2895 return NULL;
2896
2897 /*
2898 * Make tuple and any needed join variables available to ExecQual and
2899 * ExecProject. The target's existing tuple is installed in the scantuple.
2900 * This target relation's slot is required only in the case of a MATCHED
2901 * or NOT MATCHED BY SOURCE tuple and UPDATE/DELETE actions.
2902 */
2903 econtext->ecxt_scantuple = resultRelInfo->ri_oldTupleSlot;
2904 econtext->ecxt_innertuple = context->planSlot;
2905 econtext->ecxt_outertuple = NULL;
2906
2907 /*
2908 * This routine is only invoked for matched target rows, so we should
2909 * either have the tupleid of the target row, or an old tuple from the
2910 * target wholerow junk attr.
2911 */
2912 Assert(tupleid != NULL || oldtuple != NULL);
2913 ItemPointerSetInvalid(&lockedtid);
2914 if (oldtuple != NULL)
2915 {
2916 Assert(!resultRelInfo->ri_needLockTagTuple);
2917 ExecForceStoreHeapTuple(oldtuple, resultRelInfo->ri_oldTupleSlot,
2918 false);
2919 }
2920 else
2921 {
2922 if (resultRelInfo->ri_needLockTagTuple)
2923 {
2924 /*
2925 * This locks even for CMD_DELETE, for CMD_NOTHING, and for tuples
2926 * that don't match mas_whenqual. MERGE on system catalogs is a
2927 * minor use case, so don't bother optimizing those.
2928 */
2929 LockTuple(resultRelInfo->ri_RelationDesc, tupleid,
2931 lockedtid = *tupleid;
2932 }
2934 tupleid,
2936 resultRelInfo->ri_oldTupleSlot))
2937 elog(ERROR, "failed to fetch the target tuple");
2938 }
2939
2940 /*
2941 * Test the join condition. If it's satisfied, perform a MATCHED action.
2942 * Otherwise, perform a NOT MATCHED BY SOURCE action.
2943 *
2944 * Note that this join condition will be NULL if there are no NOT MATCHED
2945 * BY SOURCE actions --- see transform_MERGE_to_join(). In that case, we
2946 * need only consider MATCHED actions here.
2947 */
2948 if (ExecQual(resultRelInfo->ri_MergeJoinCondition, econtext))
2949 actionStates = mergeActions[MERGE_WHEN_MATCHED];
2950 else
2951 actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
2952
2953lmerge_matched:
2954
2955 foreach(l, actionStates)
2956 {
2957 MergeActionState *relaction = (MergeActionState *) lfirst(l);
2958 CmdType commandType = relaction->mas_action->commandType;
2959 TM_Result result;
2960 UpdateContext updateCxt = {0};
2961
2962 /*
2963 * Test condition, if any.
2964 *
2965 * In the absence of any condition, we perform the action
2966 * unconditionally (no need to check separately since ExecQual() will
2967 * return true if there are no conditions to evaluate).
2968 */
2969 if (!ExecQual(relaction->mas_whenqual, econtext))
2970 continue;
2971
2972 /*
2973 * Check if the existing target tuple meets the USING checks of
2974 * UPDATE/DELETE RLS policies. If those checks fail, we throw an
2975 * error.
2976 *
2977 * The WITH CHECK quals for UPDATE RLS policies are applied in
2978 * ExecUpdateAct() and hence we need not do anything special to handle
2979 * them.
2980 *
2981 * NOTE: We must do this after WHEN quals are evaluated, so that we
2982 * check policies only when they matter.
2983 */
2984 if (resultRelInfo->ri_WithCheckOptions && commandType != CMD_NOTHING)
2985 {
2986 ExecWithCheckOptions(commandType == CMD_UPDATE ?
2988 resultRelInfo,
2989 resultRelInfo->ri_oldTupleSlot,
2990 context->mtstate->ps.state);
2991 }
2992
2993 /* Perform stated action */
2994 switch (commandType)
2995 {
2996 case CMD_UPDATE:
2997
2998 /*
2999 * Project the output tuple, and use that to update the table.
3000 * We don't need to filter out junk attributes, because the
3001 * UPDATE action's targetlist doesn't have any.
3002 */
3003 newslot = ExecProject(relaction->mas_proj);
3004
3005 mtstate->mt_merge_action = relaction;
3006 if (!ExecUpdatePrologue(context, resultRelInfo,
3007 tupleid, NULL, newslot, &result))
3008 {
3009 if (result == TM_Ok)
3010 goto out; /* "do nothing" */
3011
3012 break; /* concurrent update/delete */
3013 }
3014
3015 /* INSTEAD OF ROW UPDATE Triggers */
3016 if (resultRelInfo->ri_TrigDesc &&
3017 resultRelInfo->ri_TrigDesc->trig_update_instead_row)
3018 {
3019 if (!ExecIRUpdateTriggers(estate, resultRelInfo,
3020 oldtuple, newslot))
3021 goto out; /* "do nothing" */
3022 }
3023 else
3024 {
3025 /* checked ri_needLockTagTuple above */
3026 Assert(oldtuple == NULL);
3027
3028 result = ExecUpdateAct(context, resultRelInfo, tupleid,
3029 NULL, newslot, canSetTag,
3030 &updateCxt);
3031
3032 /*
3033 * As in ExecUpdate(), if ExecUpdateAct() reports that a
3034 * cross-partition update was done, then there's nothing
3035 * else for us to do --- the UPDATE has been turned into a
3036 * DELETE and an INSERT, and we must not perform any of
3037 * the usual post-update tasks. Also, the RETURNING tuple
3038 * (if any) has been projected, so we can just return
3039 * that.
3040 */
3041 if (updateCxt.crossPartUpdate)
3042 {
3043 mtstate->mt_merge_updated += 1;
3044 rslot = context->cpUpdateReturningSlot;
3045 goto out;
3046 }
3047 }
3048
3049 if (result == TM_Ok)
3050 {
3051 ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
3052 tupleid, NULL, newslot);
3053 mtstate->mt_merge_updated += 1;
3054 }
3055 break;
3056
3057 case CMD_DELETE:
3058 mtstate->mt_merge_action = relaction;
3059 if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
3060 NULL, NULL, &result))
3061 {
3062 if (result == TM_Ok)
3063 goto out; /* "do nothing" */
3064
3065 break; /* concurrent update/delete */
3066 }
3067
3068 /* INSTEAD OF ROW DELETE Triggers */
3069 if (resultRelInfo->ri_TrigDesc &&
3070 resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
3071 {
3072 if (!ExecIRDeleteTriggers(estate, resultRelInfo,
3073 oldtuple))
3074 goto out; /* "do nothing" */
3075 }
3076 else
3077 {
3078 /* checked ri_needLockTagTuple above */
3079 Assert(oldtuple == NULL);
3080
3081 result = ExecDeleteAct(context, resultRelInfo, tupleid,
3082 false);
3083 }
3084
3085 if (result == TM_Ok)
3086 {
3087 ExecDeleteEpilogue(context, resultRelInfo, tupleid, NULL,
3088 false);
3089 mtstate->mt_merge_deleted += 1;
3090 }
3091 break;
3092
3093 case CMD_NOTHING:
3094 /* Doing nothing is always OK */
3095 result = TM_Ok;
3096 break;
3097
3098 default:
3099 elog(ERROR, "unknown action in MERGE WHEN clause");
3100 }
3101
3102 switch (result)
3103 {
3104 case TM_Ok:
3105 /* all good; perform final actions */
3106 if (canSetTag && commandType != CMD_NOTHING)
3107 (estate->es_processed)++;
3108
3109 break;
3110
3111 case TM_SelfModified:
3112
3113 /*
3114 * The target tuple was already updated or deleted by the
3115 * current command, or by a later command in the current
3116 * transaction. The former case is explicitly disallowed by
3117 * the SQL standard for MERGE, which insists that the MERGE
3118 * join condition should not join a target row to more than
3119 * one source row.
3120 *
3121 * The latter case arises if the tuple is modified by a
3122 * command in a BEFORE trigger, or perhaps by a command in a
3123 * volatile function used in the query. In such situations we
3124 * should not ignore the MERGE action, but it is equally
3125 * unsafe to proceed. We don't want to discard the original
3126 * MERGE action while keeping the triggered actions based on
3127 * it; and it would be no better to allow the original MERGE
3128 * action while discarding the updates that it triggered. So
3129 * throwing an error is the only safe course.
3130 */
3131 if (context->tmfd.cmax != estate->es_output_cid)
3132 ereport(ERROR,
3133 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3134 errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3135 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3136
3138 ereport(ERROR,
3139 (errcode(ERRCODE_CARDINALITY_VIOLATION),
3140 /* translator: %s is a SQL command name */
3141 errmsg("%s command cannot affect row a second time",
3142 "MERGE"),
3143 errhint("Ensure that not more than one source row matches any one target row.")));
3144
3145 /* This shouldn't happen */
3146 elog(ERROR, "attempted to update or delete invisible tuple");
3147 break;
3148
3149 case TM_Deleted:
3151 ereport(ERROR,
3153 errmsg("could not serialize access due to concurrent delete")));
3154
3155 /*
3156 * If the tuple was already deleted, set matched to false to
3157 * let caller handle it under NOT MATCHED [BY TARGET] clauses.
3158 */
3159 *matched = false;
3160 goto out;
3161
3162 case TM_Updated:
3163 {
3164 bool was_matched;
3165 Relation resultRelationDesc;
3166 TupleTableSlot *epqslot,
3167 *inputslot;
3168 LockTupleMode lockmode;
3169
3170 /*
3171 * The target tuple was concurrently updated by some other
3172 * transaction. If we are currently processing a MATCHED
3173 * action, use EvalPlanQual() with the new version of the
3174 * tuple and recheck the join qual, to detect a change
3175 * from the MATCHED to the NOT MATCHED cases. If we are
3176 * already processing a NOT MATCHED BY SOURCE action, we
3177 * skip this (cannot switch from NOT MATCHED BY SOURCE to
3178 * MATCHED).
3179 */
3180 was_matched = relaction->mas_action->matchKind == MERGE_WHEN_MATCHED;
3181 resultRelationDesc = resultRelInfo->ri_RelationDesc;
3182 lockmode = ExecUpdateLockMode(estate, resultRelInfo);
3183
3184 if (was_matched)
3185 inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
3186 resultRelInfo->ri_RangeTableIndex);
3187 else
3188 inputslot = resultRelInfo->ri_oldTupleSlot;
3189
3190 result = table_tuple_lock(resultRelationDesc, tupleid,
3191 estate->es_snapshot,
3192 inputslot, estate->es_output_cid,
3193 lockmode, LockWaitBlock,
3195 &context->tmfd);
3196 switch (result)
3197 {
3198 case TM_Ok:
3199
3200 /*
3201 * If the tuple was updated and migrated to
3202 * another partition concurrently, the current
3203 * MERGE implementation can't follow. There's
3204 * probably a better way to handle this case, but
3205 * it'd require recognizing the relation to which
3206 * the tuple moved, and setting our current
3207 * resultRelInfo to that.
3208 */
3210 ereport(ERROR,
3212 errmsg("tuple to be merged was already moved to another partition due to concurrent update")));
3213
3214 /*
3215 * If this was a MATCHED case, use EvalPlanQual()
3216 * to recheck the join condition.
3217 */
3218 if (was_matched)
3219 {
3220 epqslot = EvalPlanQual(epqstate,
3221 resultRelationDesc,
3222 resultRelInfo->ri_RangeTableIndex,
3223 inputslot);
3224
3225 /*
3226 * If the subplan didn't return a tuple, then
3227 * we must be dealing with an inner join for
3228 * which the join condition no longer matches.
3229 * This can only happen if there are no NOT
3230 * MATCHED actions, and so there is nothing
3231 * more to do.
3232 */
3233 if (TupIsNull(epqslot))
3234 goto out;
3235
3236 /*
3237 * If we got a NULL ctid from the subplan, the
3238 * join quals no longer pass and we switch to
3239 * the NOT MATCHED BY SOURCE case.
3240 */
3241 (void) ExecGetJunkAttribute(epqslot,
3242 resultRelInfo->ri_RowIdAttNo,
3243 &isNull);
3244 if (isNull)
3245 *matched = false;
3246
3247 /*
3248 * Otherwise, recheck the join quals to see if
3249 * we need to switch to the NOT MATCHED BY
3250 * SOURCE case.
3251 */
3252 if (resultRelInfo->ri_needLockTagTuple)
3253 {
3254 if (ItemPointerIsValid(&lockedtid))
3255 UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3257 LockTuple(resultRelInfo->ri_RelationDesc, &context->tmfd.ctid,
3259 lockedtid = context->tmfd.ctid;
3260 }
3261 if (!table_tuple_fetch_row_version(resultRelationDesc,
3262 &context->tmfd.ctid,
3264 resultRelInfo->ri_oldTupleSlot))
3265 elog(ERROR, "failed to fetch the target tuple");
3266
3267 if (*matched)
3268 *matched = ExecQual(resultRelInfo->ri_MergeJoinCondition,
3269 econtext);
3270
3271 /* Switch lists, if necessary */
3272 if (!*matched)
3273 actionStates = mergeActions[MERGE_WHEN_NOT_MATCHED_BY_SOURCE];
3274 }
3275
3276 /*
3277 * Loop back and process the MATCHED or NOT
3278 * MATCHED BY SOURCE actions from the start.
3279 */
3280 goto lmerge_matched;
3281
3282 case TM_Deleted:
3283
3284 /*
3285 * tuple already deleted; tell caller to run NOT
3286 * MATCHED [BY TARGET] actions
3287 */
3288 *matched = false;
3289 goto out;
3290
3291 case TM_SelfModified:
3292
3293 /*
3294 * This can be reached when following an update
3295 * chain from a tuple updated by another session,
3296 * reaching a tuple that was already updated or
3297 * deleted by the current command, or by a later
3298 * command in the current transaction. As above,
3299 * this should always be treated as an error.
3300 */
3301 if (context->tmfd.cmax != estate->es_output_cid)
3302 ereport(ERROR,
3303 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3304 errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3305 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3306
3308 ereport(ERROR,
3309 (errcode(ERRCODE_CARDINALITY_VIOLATION),
3310 /* translator: %s is a SQL command name */
3311 errmsg("%s command cannot affect row a second time",
3312 "MERGE"),
3313 errhint("Ensure that not more than one source row matches any one target row.")));
3314
3315 /* This shouldn't happen */
3316 elog(ERROR, "attempted to update or delete invisible tuple");
3317 goto out;
3318
3319 default:
3320 /* see table_tuple_lock call in ExecDelete() */
3321 elog(ERROR, "unexpected table_tuple_lock status: %u",
3322 result);
3323 goto out;
3324 }
3325 }
3326
3327 case TM_Invisible:
3328 case TM_WouldBlock:
3329 case TM_BeingModified:
3330 /* these should not occur */
3331 elog(ERROR, "unexpected tuple operation result: %d", result);
3332 break;
3333 }
3334
3335 /* Process RETURNING if present */
3336 if (resultRelInfo->ri_projectReturning)
3337 {
3338 switch (commandType)
3339 {
3340 case CMD_UPDATE:
3341 rslot = ExecProcessReturning(resultRelInfo, newslot,
3342 context->planSlot);
3343 break;
3344
3345 case CMD_DELETE:
3346 rslot = ExecProcessReturning(resultRelInfo,
3347 resultRelInfo->ri_oldTupleSlot,
3348 context->planSlot);
3349 break;
3350
3351 case CMD_NOTHING:
3352 break;
3353
3354 default:
3355 elog(ERROR, "unrecognized commandType: %d",
3356 (int) commandType);
3357 }
3358 }
3359
3360 /*
3361 * We've activated one of the WHEN clauses, so we don't search
3362 * further. This is required behaviour, not an optimization.
3363 */
3364 break;
3365 }
3366
3367 /*
3368 * Successfully executed an action or no qualifying action was found.
3369 */
3370out:
3371 if (ItemPointerIsValid(&lockedtid))
3372 UnlockTuple(resultRelInfo->ri_RelationDesc, &lockedtid,
3374 return rslot;
3375}
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2360
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:426
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: executor.h:184
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition: itemptr.h:197
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:557
void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:594
#define InplaceUpdateTupleLock
Definition: lockdefs.h:48
LockTupleMode
Definition: lockoptions.h:50
static TM_Result ExecUpdateAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt)
static void ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot)
static bool ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TM_Result *result)
@ WCO_RLS_MERGE_UPDATE_CHECK
Definition: parsenodes.h:1363
@ WCO_RLS_MERGE_DELETE_CHECK
Definition: parsenodes.h:1364
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition: primnodes.h:1997
@ MERGE_WHEN_MATCHED
Definition: primnodes.h:1996
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:260
MergeMatchKind matchKind
Definition: primnodes.h:2006
ExprState * ri_MergeJoinCondition
Definition: execnodes.h:559
bool ri_needLockTagTuple
Definition: execnodes.h:489
List * ri_MergeActions[NUM_MERGE_MATCH_KINDS]
Definition: execnodes.h:556
TransactionId xmax
Definition: tableam.h:150
ItemPointerData ctid
Definition: tableam.h:149
bool trig_update_instead_row
Definition: reltrigger.h:63
@ TM_BeingModified
Definition: tableam.h:106
@ TM_WouldBlock
Definition: tableam.h:109
@ TM_Invisible
Definition: tableam.h:87
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:3157

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

Referenced by ExecMerge().

◆ ExecMergeNotMatched()

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

Definition at line 3381 of file nodeModifyTable.c.

3383{
3384 ModifyTableState *mtstate = context->mtstate;
3385 ExprContext *econtext = mtstate->ps.ps_ExprContext;
3386 List *actionStates;
3387 TupleTableSlot *rslot = NULL;
3388 ListCell *l;
3389
3390 /*
3391 * For INSERT actions, the root relation's merge action is OK since the
3392 * INSERT's targetlist and the WHEN conditions can only refer to the
3393 * source relation and hence it does not matter which result relation we
3394 * work with.
3395 *
3396 * XXX does this mean that we can avoid creating copies of actionStates on
3397 * partitioned tables, for not-matched actions?
3398 */
3399 actionStates = resultRelInfo->ri_MergeActions[MERGE_WHEN_NOT_MATCHED_BY_TARGET];
3400
3401 /*
3402 * Make source tuple available to ExecQual and ExecProject. We don't need
3403 * the target tuple, since the WHEN quals and targetlist can't refer to
3404 * the target columns.
3405 */
3406 econtext->ecxt_scantuple = NULL;
3407 econtext->ecxt_innertuple = context->planSlot;
3408 econtext->ecxt_outertuple = NULL;
3409
3410 foreach(l, actionStates)
3411 {
3413 CmdType commandType = action->mas_action->commandType;
3414 TupleTableSlot *newslot;
3415
3416 /*
3417 * Test condition, if any.
3418 *
3419 * In the absence of any condition, we perform the action
3420 * unconditionally (no need to check separately since ExecQual() will
3421 * return true if there are no conditions to evaluate).
3422 */
3423 if (!ExecQual(action->mas_whenqual, econtext))
3424 continue;
3425
3426 /* Perform stated action */
3427 switch (commandType)
3428 {
3429 case CMD_INSERT:
3430
3431 /*
3432 * Project the tuple. In case of a partitioned table, the
3433 * projection was already built to use the root's descriptor,
3434 * so we don't need to map the tuple here.
3435 */
3436 newslot = ExecProject(action->mas_proj);
3437 mtstate->mt_merge_action = action;
3438
3439 rslot = ExecInsert(context, mtstate->rootResultRelInfo,
3440 newslot, canSetTag, NULL, NULL);
3441 mtstate->mt_merge_inserted += 1;
3442 break;
3443 case CMD_NOTHING:
3444 /* Do nothing */
3445 break;
3446 default:
3447 elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
3448 }
3449
3450 /*
3451 * We've activated one of the WHEN clauses, so we don't search
3452 * further. This is required behaviour, not an optimization.
3453 */
3454 break;
3455 }
3456
3457 return rslot;
3458}
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition: primnodes.h:1998

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

3812{
3814 ModifyTableContext context;
3815 EState *estate = node->ps.state;
3816 CmdType operation = node->operation;
3817 ResultRelInfo *resultRelInfo;
3818 PlanState *subplanstate;
3819 TupleTableSlot *slot;
3820 TupleTableSlot *oldSlot;
3821 ItemPointerData tuple_ctid;
3822 HeapTupleData oldtupdata;
3823 HeapTuple oldtuple;
3824 ItemPointer tupleid;
3825 bool tuplock;
3826
3828
3829 /*
3830 * This should NOT get called during EvalPlanQual; we should have passed a
3831 * subplan tree to EvalPlanQual, instead. Use a runtime test not just
3832 * Assert because this condition is easy to miss in testing. (Note:
3833 * although ModifyTable should not get executed within an EvalPlanQual
3834 * operation, we do have to allow it to be initialized and shut down in
3835 * case it is within a CTE subplan. Hence this test must be here, not in
3836 * ExecInitModifyTable.)
3837 */
3838 if (estate->es_epq_active != NULL)
3839 elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
3840
3841 /*
3842 * If we've already completed processing, don't try to do more. We need
3843 * this test because ExecPostprocessPlan might call us an extra time, and
3844 * our subplan's nodes aren't necessarily robust against being called
3845 * extra times.
3846 */
3847 if (node->mt_done)
3848 return NULL;
3849
3850 /*
3851 * On first call, fire BEFORE STATEMENT triggers before proceeding.
3852 */
3853 if (node->fireBSTriggers)
3854 {
3855 fireBSTriggers(node);
3856 node->fireBSTriggers = false;
3857 }
3858
3859 /* Preload local variables */
3860 resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
3861 subplanstate = outerPlanState(node);
3862
3863 /* Set global context */
3864 context.mtstate = node;
3865 context.epqstate = &node->mt_epqstate;
3866 context.estate = estate;
3867
3868 /*
3869 * Fetch rows from subplan, and execute the required table modification
3870 * for each row.
3871 */
3872 for (;;)
3873 {
3874 /*
3875 * Reset the per-output-tuple exprcontext. This is needed because
3876 * triggers expect to use that context as workspace. It's a bit ugly
3877 * to do this below the top level of the plan, however. We might need
3878 * to rethink this later.
3879 */
3881
3882 /*
3883 * Reset per-tuple memory context used for processing on conflict and
3884 * returning clauses, to free any expression evaluation storage
3885 * allocated in the previous cycle.
3886 */
3887 if (pstate->ps_ExprContext)
3889
3890 /*
3891 * If there is a pending MERGE ... WHEN NOT MATCHED [BY TARGET] action
3892 * to execute, do so now --- see the comments in ExecMerge().
3893 */
3894 if (node->mt_merge_pending_not_matched != NULL)
3895 {
3896 context.planSlot = node->mt_merge_pending_not_matched;
3897
3898 slot = ExecMergeNotMatched(&context, node->resultRelInfo,
3899 node->canSetTag);
3900
3901 /* Clear the pending action */
3902 node->mt_merge_pending_not_matched = NULL;
3903
3904 /*
3905 * If we got a RETURNING result, return it to the caller. We'll
3906 * continue the work on next call.
3907 */
3908 if (slot)
3909 return slot;
3910
3911 continue; /* continue with the next tuple */
3912 }
3913
3914 /* Fetch the next row from subplan */
3915 context.planSlot = ExecProcNode(subplanstate);
3916
3917 /* No more tuples to process? */
3918 if (TupIsNull(context.planSlot))
3919 break;
3920
3921 /*
3922 * When there are multiple result relations, each tuple contains a
3923 * junk column that gives the OID of the rel from which it came.
3924 * Extract it and select the correct result relation.
3925 */
3927 {
3928 Datum datum;
3929 bool isNull;
3930 Oid resultoid;
3931
3932 datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
3933 &isNull);
3934 if (isNull)
3935 {
3936 /*
3937 * For commands other than MERGE, any tuples having InvalidOid
3938 * for tableoid are errors. For MERGE, we may need to handle
3939 * them as WHEN NOT MATCHED clauses if any, so do that.
3940 *
3941 * Note that we use the node's toplevel resultRelInfo, not any
3942 * specific partition's.
3943 */
3944 if (operation == CMD_MERGE)
3945 {
3946 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3947
3948 slot = ExecMerge(&context, node->resultRelInfo,
3949 NULL, NULL, node->canSetTag);
3950
3951 /*
3952 * If we got a RETURNING result, return it to the caller.
3953 * We'll continue the work on next call.
3954 */
3955 if (slot)
3956 return slot;
3957
3958 continue; /* continue with the next tuple */
3959 }
3960
3961 elog(ERROR, "tableoid is NULL");
3962 }
3963 resultoid = DatumGetObjectId(datum);
3964
3965 /* If it's not the same as last time, we need to locate the rel */
3966 if (resultoid != node->mt_lastResultOid)
3967 resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
3968 false, true);
3969 }
3970
3971 /*
3972 * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
3973 * here is compute the RETURNING expressions.
3974 */
3975 if (resultRelInfo->ri_usesFdwDirectModify)
3976 {
3977 Assert(resultRelInfo->ri_projectReturning);
3978
3979 /*
3980 * A scan slot containing the data that was actually inserted,
3981 * updated or deleted has already been made available to
3982 * ExecProcessReturning by IterateDirectModify, so no need to
3983 * provide it here.
3984 */
3985 slot = ExecProcessReturning(resultRelInfo, NULL, context.planSlot);
3986
3987 return slot;
3988 }
3989
3990 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3991 slot = context.planSlot;
3992
3993 tupleid = NULL;
3994 oldtuple = NULL;
3995
3996 /*
3997 * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
3998 * to be updated/deleted/merged. For a heap relation, that's a TID;
3999 * otherwise we may have a wholerow junk attr that carries the old
4000 * tuple in toto. Keep this in step with the part of
4001 * ExecInitModifyTable that sets up ri_RowIdAttNo.
4002 */
4003 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4004 operation == CMD_MERGE)
4005 {
4006 char relkind;
4007 Datum datum;
4008 bool isNull;
4009
4010 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4011 if (relkind == RELKIND_RELATION ||
4012 relkind == RELKIND_MATVIEW ||
4013 relkind == RELKIND_PARTITIONED_TABLE)
4014 {
4015 /* ri_RowIdAttNo refers to a ctid attribute */
4017 datum = ExecGetJunkAttribute(slot,
4018 resultRelInfo->ri_RowIdAttNo,
4019 &isNull);
4020
4021 /*
4022 * For commands other than MERGE, any tuples having a null row
4023 * identifier are errors. For MERGE, we may need to handle
4024 * them as WHEN NOT MATCHED clauses if any, so do that.
4025 *
4026 * Note that we use the node's toplevel resultRelInfo, not any
4027 * specific partition's.
4028 */
4029 if (isNull)
4030 {
4031 if (operation == CMD_MERGE)
4032 {
4033 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4034
4035 slot = ExecMerge(&context, node->resultRelInfo,
4036 NULL, NULL, node->canSetTag);
4037
4038 /*
4039 * If we got a RETURNING result, return it to the
4040 * caller. We'll continue the work on next call.
4041 */
4042 if (slot)
4043 return slot;
4044
4045 continue; /* continue with the next tuple */
4046 }
4047
4048 elog(ERROR, "ctid is NULL");
4049 }
4050
4051 tupleid = (ItemPointer) DatumGetPointer(datum);
4052 tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
4053 tupleid = &tuple_ctid;
4054 }
4055
4056 /*
4057 * Use the wholerow attribute, when available, to reconstruct the
4058 * old relation tuple. The old tuple serves one or both of two
4059 * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
4060 * provides values for any unchanged columns for the NEW tuple of
4061 * an UPDATE, because the subplan does not produce all the columns
4062 * of the target table.
4063 *
4064 * Note that the wholerow attribute does not carry system columns,
4065 * so foreign table triggers miss seeing those, except that we
4066 * know enough here to set t_tableOid. Quite separately from
4067 * this, the FDW may fetch its own junk attrs to identify the row.
4068 *
4069 * Other relevant relkinds, currently limited to views, always
4070 * have a wholerow attribute.
4071 */
4072 else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4073 {
4074 datum = ExecGetJunkAttribute(slot,
4075 resultRelInfo->ri_RowIdAttNo,
4076 &isNull);
4077
4078 /*
4079 * For commands other than MERGE, any tuples having a null row
4080 * identifier are errors. For MERGE, we may need to handle
4081 * them as WHEN NOT MATCHED clauses if any, so do that.
4082 *
4083 * Note that we use the node's toplevel resultRelInfo, not any
4084 * specific partition's.
4085 */
4086 if (isNull)
4087 {
4088 if (operation == CMD_MERGE)
4089 {
4090 EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
4091
4092 slot = ExecMerge(&context, node->resultRelInfo,
4093 NULL, NULL, node->canSetTag);
4094
4095 /*
4096 * If we got a RETURNING result, return it to the
4097 * caller. We'll continue the work on next call.
4098 */
4099 if (slot)
4100 return slot;
4101
4102 continue; /* continue with the next tuple */
4103 }
4104
4105 elog(ERROR, "wholerow is NULL");
4106 }
4107
4108 oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
4109 oldtupdata.t_len =
4111 ItemPointerSetInvalid(&(oldtupdata.t_self));
4112 /* Historically, view triggers see invalid t_tableOid. */
4113 oldtupdata.t_tableOid =
4114 (relkind == RELKIND_VIEW) ? InvalidOid :
4115 RelationGetRelid(resultRelInfo->ri_RelationDesc);
4116
4117 oldtuple = &oldtupdata;
4118 }
4119 else
4120 {
4121 /* Only foreign tables are allowed to omit a row-ID attr */
4122 Assert(relkind == RELKIND_FOREIGN_TABLE);
4123 }
4124 }
4125
4126 switch (operation)
4127 {
4128 case CMD_INSERT:
4129 /* Initialize projection info if first time for this table */
4130 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4131 ExecInitInsertProjection(node, resultRelInfo);
4132 slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
4133 slot = ExecInsert(&context, resultRelInfo, slot,
4134 node->canSetTag, NULL, NULL);
4135 break;
4136
4137 case CMD_UPDATE:
4138 tuplock = false;
4139
4140 /* Initialize projection info if first time for this table */
4141 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
4142 ExecInitUpdateProjection(node, resultRelInfo);
4143
4144 /*
4145 * Make the new tuple by combining plan's output tuple with
4146 * the old tuple being updated.
4147 */
4148 oldSlot = resultRelInfo->ri_oldTupleSlot;
4149 if (oldtuple != NULL)
4150 {
4151 Assert(!resultRelInfo->ri_needLockTagTuple);
4152 /* Use the wholerow junk attr as the old tuple. */
4153 ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
4154 }
4155 else
4156 {
4157 /* Fetch the most recent version of old tuple. */
4158 Relation relation = resultRelInfo->ri_RelationDesc;
4159
4160 if (resultRelInfo->ri_needLockTagTuple)
4161 {
4162 LockTuple(relation, tupleid, InplaceUpdateTupleLock);
4163 tuplock = true;
4164 }
4165 if (!table_tuple_fetch_row_version(relation, tupleid,
4167 oldSlot))
4168 elog(ERROR, "failed to fetch tuple being updated");
4169 }
4170 slot = ExecGetUpdateNewTuple(resultRelInfo, context.planSlot,
4171 oldSlot);
4172
4173 /* Now apply the update. */
4174 slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
4175 slot, node->canSetTag);
4176 if (tuplock)
4177 UnlockTuple(resultRelInfo->ri_RelationDesc, tupleid,
4179 break;
4180
4181 case CMD_DELETE:
4182 slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
4183 true, false, node->canSetTag, NULL, NULL, NULL);
4184 break;
4185
4186 case CMD_MERGE:
4187 slot = ExecMerge(&context, resultRelInfo, tupleid, oldtuple,
4188 node->canSetTag);
4189 break;
4190
4191 default:
4192 elog(ERROR, "unknown operation");
4193 break;
4194 }
4195
4196 /*
4197 * If we got a RETURNING result, return it to caller. We'll continue
4198 * the work on next call.
4199 */
4200 if (slot)
4201 return slot;
4202 }
4203
4204 /*
4205 * Insert remaining tuples for batch insert.
4206 */
4208 ExecPendingInserts(estate);
4209
4210 /*
4211 * We're done, but fire AFTER STATEMENT triggers before exiting.
4212 */
4213 fireASTriggers(node);
4214
4215 node->mt_done = true;
4216
4217 return NULL;
4218}
#define ResetPerTupleExprContext(estate)
Definition: executor.h:572
#define ResetExprContext(econtext)
Definition: executor.h:557
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:267
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:242
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:450
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 * ExecGetInsertNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot)
static TupleTableSlot * ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag)
static void fireBSTriggers(ModifyTableState *node)
static void fireASTriggers(ModifyTableState *node)
static TupleTableSlot * ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool canSetTag)
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:242
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
struct EPQState * es_epq_active
Definition: execnodes.h:707
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, DatumGetHeapTupleHeader, DatumGetObjectId(), DatumGetPointer(), 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, 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, 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 2524 of file nodeModifyTable.c.

2530{
2531 ModifyTableState *mtstate = context->mtstate;
2532 ExprContext *econtext = mtstate->ps.ps_ExprContext;
2533 Relation relation = resultRelInfo->ri_RelationDesc;
2534 ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
2535 TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
2536 TM_FailureData tmfd;
2537 LockTupleMode lockmode;
2539 Datum xminDatum;
2540 TransactionId xmin;
2541 bool isnull;
2542
2543 /*
2544 * Parse analysis should have blocked ON CONFLICT for all system
2545 * relations, which includes these. There's no fundamental obstacle to
2546 * supporting this; we'd just need to handle LOCKTAG_TUPLE like the other
2547 * ExecUpdate() caller.
2548 */
2549 Assert(!resultRelInfo->ri_needLockTagTuple);
2550
2551 /* Determine lock mode to use */
2552 lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
2553
2554 /*
2555 * Lock tuple for update. Don't follow updates when tuple cannot be
2556 * locked without doing so. A row locking conflict here means our
2557 * previous conclusion that the tuple is conclusively committed is not
2558 * true anymore.
2559 */
2560 test = table_tuple_lock(relation, conflictTid,
2561 context->estate->es_snapshot,
2562 existing, context->estate->es_output_cid,
2563 lockmode, LockWaitBlock, 0,
2564 &tmfd);
2565 switch (test)
2566 {
2567 case TM_Ok:
2568 /* success! */
2569 break;
2570
2571 case TM_Invisible:
2572
2573 /*
2574 * This can occur when a just inserted tuple is updated again in
2575 * the same command. E.g. because multiple rows with the same
2576 * conflicting key values are inserted.
2577 *
2578 * This is somewhat similar to the ExecUpdate() TM_SelfModified
2579 * case. We do not want to proceed because it would lead to the
2580 * same row being updated a second time in some unspecified order,
2581 * and in contrast to plain UPDATEs there's no historical behavior
2582 * to break.
2583 *
2584 * It is the user's responsibility to prevent this situation from
2585 * occurring. These problems are why the SQL standard similarly
2586 * specifies that for SQL MERGE, an exception must be raised in
2587 * the event of an attempt to update the same row twice.
2588 */
2589 xminDatum = slot_getsysattr(existing,
2591 &isnull);
2592 Assert(!isnull);
2593 xmin = DatumGetTransactionId(xminDatum);
2594
2596 ereport(ERROR,
2597 (errcode(ERRCODE_CARDINALITY_VIOLATION),
2598 /* translator: %s is a SQL command name */
2599 errmsg("%s command cannot affect row a second time",
2600 "ON CONFLICT DO UPDATE"),
2601 errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
2602
2603 /* This shouldn't happen */
2604 elog(ERROR, "attempted to lock invisible tuple");
2605 break;
2606
2607 case TM_SelfModified:
2608
2609 /*
2610 * This state should never be reached. As a dirty snapshot is used
2611 * to find conflicting tuples, speculative insertion wouldn't have
2612 * seen this row to conflict with.
2613 */
2614 elog(ERROR, "unexpected self-updated tuple");
2615 break;
2616
2617 case TM_Updated:
2619 ereport(ERROR,
2621 errmsg("could not serialize access due to concurrent update")));
2622
2623 /*
2624 * As long as we don't support an UPDATE of INSERT ON CONFLICT for
2625 * a partitioned table we shouldn't reach to a case where tuple to
2626 * be lock is moved to another partition due to concurrent update
2627 * of the partition key.
2628 */
2630
2631 /*
2632 * Tell caller to try again from the very start.
2633 *
2634 * It does not make sense to use the usual EvalPlanQual() style
2635 * loop here, as the new version of the row might not conflict
2636 * anymore, or the conflicting tuple has actually been deleted.
2637 */
2638 ExecClearTuple(existing);
2639 return false;
2640
2641 case TM_Deleted:
2643 ereport(ERROR,
2645 errmsg("could not serialize access due to concurrent delete")));
2646
2647 /* see TM_Updated case */
2649 ExecClearTuple(existing);
2650 return false;
2651
2652 default:
2653 elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
2654 }
2655
2656 /* Success, the tuple is locked. */
2657
2658 /*
2659 * Verify that the tuple is visible to our MVCC snapshot if the current
2660 * isolation level mandates that.
2661 *
2662 * It's not sufficient to rely on the check within ExecUpdate() as e.g.
2663 * CONFLICT ... WHERE clause may prevent us from reaching that.
2664 *
2665 * This means we only ever continue when a new command in the current
2666 * transaction could see the row, even though in READ COMMITTED mode the
2667 * tuple will not be visible according to the current statement's
2668 * snapshot. This is in line with the way UPDATE deals with newer tuple
2669 * versions.
2670 */
2671 ExecCheckTupleVisible(context->estate, relation, existing);
2672
2673 /*
2674 * Make tuple and any needed join variables available to ExecQual and
2675 * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
2676 * the target's existing tuple is installed in the scantuple. EXCLUDED
2677 * has been made to reference INNER_VAR in setrefs.c, but there is no
2678 * other redirection.
2679 */
2680 econtext->ecxt_scantuple = existing;
2681 econtext->ecxt_innertuple = excludedSlot;
2682 econtext->ecxt_outertuple = NULL;
2683
2684 if (!ExecQual(onConflictSetWhere, econtext))
2685 {
2686 ExecClearTuple(existing); /* see return below */
2687 InstrCountFiltered1(&mtstate->ps, 1);
2688 return true; /* done with the tuple */
2689 }
2690
2691 if (resultRelInfo->ri_WithCheckOptions != NIL)
2692 {
2693 /*
2694 * Check target's existing tuple against UPDATE-applicable USING
2695 * security barrier quals (if any), enforced here as RLS checks/WCOs.
2696 *
2697 * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
2698 * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
2699 * but that's almost the extent of its special handling for ON
2700 * CONFLICT DO UPDATE.
2701 *
2702 * The rewriter will also have associated UPDATE applicable straight
2703 * RLS checks/WCOs for the benefit of the ExecUpdate() call that
2704 * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
2705 * kinds, so there is no danger of spurious over-enforcement in the
2706 * INSERT or UPDATE path.
2707 */
2709 existing,
2710 mtstate->ps.state);
2711 }
2712
2713 /* Project the new tuple version */
2714 ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
2715
2716 /*
2717 * Note that it is possible that the target tuple has been modified in
2718 * this session, after the above table_tuple_lock. We choose to not error
2719 * out in that case, in line with ExecUpdate's treatment of similar cases.
2720 * This can happen if an UPDATE is triggered from within ExecQual(),
2721 * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
2722 * wCTE in the ON CONFLICT's SET.
2723 */
2724
2725 /* Execute UPDATE with projection */
2726 *returning = ExecUpdate(context, resultRelInfo,
2727 conflictTid, NULL,
2728 resultRelInfo->ri_onConflict->oc_ProjSlot,
2729 canSetTag);
2730
2731 /*
2732 * Clear out existing tuple, as there might not be another conflict among
2733 * the next input rows. Don't want to hold resources till the end of the
2734 * query.
2735 */
2736 ExecClearTuple(existing);
2737 return true;
2738}
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1230
@ WCO_RLS_CONFLICT_CHECK
Definition: parsenodes.h:1362
static void test(void)

References Assert, TM_FailureData::ctid, DatumGetTransactionId(), 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_snapshot, ModifyTableContext::estate, ExecCheckTupleVisible(), ExecClearTuple(), ExecProject(), ExecQual(), ExecUpdate(), ExecUpdateLockMode(), ExecWithCheckOptions(), InstrCountFiltered1, IsolationUsesXactSnapshot, ItemPointerIndicatesMovedPartitions(), LockWaitBlock, MinTransactionIdAttributeNumber, ModifyTableContext::mtstate, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ModifyTableState::ps, PlanState::ps_ExprContext, ResultRelInfo::ri_needLockTagTuple, ResultRelInfo::ri_onConflict, 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 1286 of file nodeModifyTable.c.

1287{
1288 ListCell *l1,
1289 *l2;
1290
1293 {
1294 ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(l1);
1295 ModifyTableState *mtstate = (ModifyTableState *) lfirst(l2);
1296
1297 Assert(mtstate);
1298 ExecBatchInsert(mtstate, resultRelInfo,
1299 resultRelInfo->ri_Slots,
1300 resultRelInfo->ri_PlanSlots,
1301 resultRelInfo->ri_NumSlots,
1302 estate, mtstate->canSetTag);
1303 }
1304
1309}
#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 3751 of file nodeModifyTable.c.

3757{
3758 ResultRelInfo *partrel;
3759 TupleConversionMap *map;
3760
3761 /*
3762 * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
3763 * not find a valid partition for the tuple in 'slot' then an error is
3764 * raised. An error may also be raised if the found partition is not a
3765 * valid target for INSERTs. This is required since a partitioned table
3766 * UPDATE to another partition becomes a DELETE+INSERT.
3767 */
3768 partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
3769
3770 /*
3771 * If we're capturing transition tuples, we might need to convert from the
3772 * partition rowtype to root partitioned table's rowtype. But if there
3773 * are no BEFORE triggers on the partition that could change the tuple, we
3774 * can just remember the original unconverted tuple to avoid a needless
3775 * round trip conversion.
3776 */
3777 if (mtstate->mt_transition_capture != NULL)
3778 {
3779 bool has_before_insert_row_trig;
3780
3781 has_before_insert_row_trig = (partrel->ri_TrigDesc &&
3783
3785 !has_before_insert_row_trig ? slot : NULL;
3786 }
3787
3788 /*
3789 * Convert the tuple, if necessary.
3790 */
3791 map = ExecGetRootToChildMap(partrel, estate);
3792 if (map != NULL)
3793 {
3794 TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
3795
3796 slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
3797 }
3798
3799 *partRelInfo = partrel;
3800 return slot;
3801}
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleConversionMap * ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate)
Definition: execUtils.c:1277
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:591

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 ( ResultRelInfo resultRelInfo,
TupleTableSlot tupleSlot,
TupleTableSlot planSlot 
)
static

Definition at line 256 of file nodeModifyTable.c.

259{
260 ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
261 ExprContext *econtext = projectReturning->pi_exprContext;
262
263 /* Make tuple and any needed join variables available to ExecProject */
264 if (tupleSlot)
265 econtext->ecxt_scantuple = tupleSlot;
266 econtext->ecxt_outertuple = planSlot;
267
268 /*
269 * RETURNING expressions might reference the tableoid column, so
270 * reinitialize tts_tableOid before evaluating them.
271 */
272 econtext->ecxt_scantuple->tts_tableOid =
273 RelationGetRelid(resultRelInfo->ri_RelationDesc);
274
275 /* Compute the RETURNING expressions */
276 return ExecProject(projectReturning);
277}

References ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, ExecProject(), ProjectionInfo::pi_exprContext, RelationGetRelid, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, and TupleTableSlot::tts_tableOid.

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

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 4819 of file nodeModifyTable.c.

4820{
4821 /*
4822 * Currently, we don't need to support rescan on ModifyTable nodes. The
4823 * semantics of that would be a bit debatable anyway.
4824 */
4825 elog(ERROR, "ExecReScanModifyTable is not implemented");
4826}

References elog, and ERROR.

Referenced by ExecReScan().

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 3722 of file nodeModifyTable.c.

3723{
3724 ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
3725 ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
3726
3727 /* Check for transition tables on the directly targeted relation. */
3728 mtstate->mt_transition_capture =
3729 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3730 RelationGetRelid(targetRelInfo->ri_RelationDesc),
3731 mtstate->operation);
3732 if (plan->operation == CMD_INSERT &&
3733 plan->onConflictAction == ONCONFLICT_UPDATE)
3734 mtstate->mt_oc_transition_capture =
3735 MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3736 RelationGetRelid(targetRelInfo->ri_RelationDesc),
3737 CMD_UPDATE);
3738}
#define plan(x)
Definition: pg_regress.c:161
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1406
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4888

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 slot,
bool  canSetTag 
)
static

Definition at line 2272 of file nodeModifyTable.c.

2275{
2276 EState *estate = context->estate;
2277 Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2278 UpdateContext updateCxt = {0};
2279 TM_Result result;
2280
2281 /*
2282 * abort the operation if not running transactions
2283 */
2285 elog(ERROR, "cannot UPDATE during bootstrap");
2286
2287 /*
2288 * Prepare for the update. This includes BEFORE ROW triggers, so we're
2289 * done if it says we are.
2290 */
2291 if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot, NULL))
2292 return NULL;
2293
2294 /* INSTEAD OF ROW UPDATE Triggers */
2295 if (resultRelInfo->ri_TrigDesc &&
2296 resultRelInfo->ri_TrigDesc->trig_update_instead_row)
2297 {
2298 if (!ExecIRUpdateTriggers(estate, resultRelInfo,
2299 oldtuple, slot))
2300 return NULL; /* "do nothing" */
2301 }
2302 else if (resultRelInfo->ri_FdwRoutine)
2303 {
2304 /* Fill in GENERATEd columns */
2305 ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2306
2307 /*
2308 * update in foreign table: let the FDW do it
2309 */
2310 slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
2311 resultRelInfo,
2312 slot,
2313 context->planSlot);
2314
2315 if (slot == NULL) /* "do nothing" */
2316 return NULL;
2317
2318 /*
2319 * AFTER ROW Triggers or RETURNING expressions might reference the
2320 * tableoid column, so (re-)initialize tts_tableOid before evaluating
2321 * them. (This covers the case where the FDW replaced the slot.)
2322 */
2323 slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2324 }
2325 else
2326 {
2327 ItemPointerData lockedtid;
2328
2329 /*
2330 * If we generate a new candidate tuple after EvalPlanQual testing, we
2331 * must loop back here to try again. (We don't need to redo triggers,
2332 * however. If there are any BEFORE triggers then trigger.c will have
2333 * done table_tuple_lock to lock the correct tuple, so there's no need
2334 * to do them again.)
2335 */
2336redo_act:
2337 lockedtid = *tupleid;
2338 result = ExecUpdateAct(context, resultRelInfo, tupleid, oldtuple, slot,
2339 canSetTag, &updateCxt);
2340
2341 /*
2342 * If ExecUpdateAct reports that a cross-partition update was done,
2343 * then the RETURNING tuple (if any) has been projected and there's
2344 * nothing else for us to do.
2345 */
2346 if (updateCxt.crossPartUpdate)
2347 return context->cpUpdateReturningSlot;
2348
2349 switch (result)
2350 {
2351 case TM_SelfModified:
2352
2353 /*
2354 * The target tuple was already updated or deleted by the
2355 * current command, or by a later command in the current
2356 * transaction. The former case is possible in a join UPDATE
2357 * where multiple tuples join to the same target tuple. This
2358 * is pretty questionable, but Postgres has always allowed it:
2359 * we just execute the first update action and ignore
2360 * additional update attempts.
2361 *
2362 * The latter case arises if the tuple is modified by a
2363 * command in a BEFORE trigger, or perhaps by a command in a
2364 * volatile function used in the query. In such situations we
2365 * should not ignore the update, but it is equally unsafe to
2366 * proceed. We don't want to discard the original UPDATE
2367 * while keeping the triggered actions based on it; and we
2368 * have no principled way to merge this update with the
2369 * previous ones. So throwing an error is the only safe
2370 * course.
2371 *
2372 * If a trigger actually intends this type of interaction, it
2373 * can re-execute the UPDATE (assuming it can figure out how)
2374 * and then return NULL to cancel the outer update.
2375 */
2376 if (context->tmfd.cmax != estate->es_output_cid)
2377 ereport(ERROR,
2378 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2379 errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2380 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2381
2382 /* Else, already updated by self; nothing to do */
2383 return NULL;
2384
2385 case TM_Ok:
2386 break;
2387
2388 case TM_Updated:
2389 {
2390 TupleTableSlot *inputslot;
2391 TupleTableSlot *epqslot;
2392 TupleTableSlot *oldSlot;
2393
2395 ereport(ERROR,
2397 errmsg("could not serialize access due to concurrent update")));
2398
2399 /*
2400 * Already know that we're going to need to do EPQ, so
2401 * fetch tuple directly into the right slot.
2402 */
2403 inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
2404 resultRelInfo->ri_RangeTableIndex);
2405
2406 result = table_tuple_lock(resultRelationDesc, tupleid,
2407 estate->es_snapshot,
2408 inputslot, estate->es_output_cid,
2409 updateCxt.lockmode, LockWaitBlock,
2411 &context->tmfd);
2412
2413 switch (result)
2414 {
2415 case TM_Ok:
2416 Assert(context->tmfd.traversed);
2417
2418 epqslot = EvalPlanQual(context->epqstate,
2419 resultRelationDesc,
2420 resultRelInfo->ri_RangeTableIndex,
2421 inputslot);
2422 if (TupIsNull(epqslot))
2423 /* Tuple not passing quals anymore, exiting... */
2424 return NULL;
2425
2426 /* Make sure ri_oldTupleSlot is initialized. */
2427 if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2429 resultRelInfo);
2430
2431 if (resultRelInfo->ri_needLockTagTuple)
2432 {
2433 UnlockTuple(resultRelationDesc,
2434 &lockedtid, InplaceUpdateTupleLock);
2435 LockTuple(resultRelationDesc,
2436 tupleid, InplaceUpdateTupleLock);
2437 }
2438
2439 /* Fetch the most recent version of old tuple. */
2440 oldSlot = resultRelInfo->ri_oldTupleSlot;
2441 if (!table_tuple_fetch_row_version(resultRelationDesc,
2442 tupleid,
2444 oldSlot))
2445 elog(ERROR, "failed to fetch tuple being updated");
2446 slot = ExecGetUpdateNewTuple(resultRelInfo,
2447 epqslot, oldSlot);
2448 goto redo_act;
2449
2450 case TM_Deleted:
2451 /* tuple already deleted; nothing to do */
2452 return NULL;
2453
2454 case TM_SelfModified:
2455
2456 /*
2457 * This can be reached when following an update
2458 * chain from a tuple updated by another session,
2459 * reaching a tuple that was already updated in
2460 * this transaction. If previously modified by
2461 * this command, ignore the redundant update,
2462 * otherwise error out.
2463 *
2464 * See also TM_SelfModified response to
2465 * table_tuple_update() above.
2466 */
2467 if (context->tmfd.cmax != estate->es_output_cid)
2468 ereport(ERROR,
2469 (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2470 errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2471 errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2472 return NULL;
2473
2474 default:
2475 /* see table_tuple_lock call in ExecDelete() */
2476 elog(ERROR, "unexpected table_tuple_lock status: %u",
2477 result);
2478 return NULL;
2479 }
2480 }
2481
2482 break;
2483
2484 case TM_Deleted:
2486 ereport(ERROR,
2488 errmsg("could not serialize access due to concurrent delete")));
2489 /* tuple already deleted; nothing to do */
2490 return NULL;
2491
2492 default:
2493 elog(ERROR, "unrecognized table_tuple_update status: %u",
2494 result);
2495 return NULL;
2496 }
2497 }
2498
2499 if (canSetTag)
2500 (estate->es_processed)++;
2501
2502 ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, tupleid, oldtuple,
2503 slot);
2504
2505 /* Process RETURNING if present */
2506 if (resultRelInfo->ri_projectReturning)
2507 return ExecProcessReturning(resultRelInfo, slot, context->planSlot);
2508
2509 return NULL;
2510}
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:466
static void ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:235
LockTupleMode lockmode

References Assert, TM_FailureData::cmax, 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 1982 of file nodeModifyTable.c.

1985{
1986 EState *estate = context->estate;
1987 Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1988 bool partition_constraint_failed;
1989 TM_Result result;
1990
1991 updateCxt->crossPartUpdate = false;
1992
1993 /*
1994 * If we move the tuple to a new partition, we loop back here to recompute
1995 * GENERATED values (which are allowed to be different across partitions)
1996 * and recheck any RLS policies and constraints. We do not fire any
1997 * BEFORE triggers of the new partition, however.
1998 */
1999lreplace:
2000 /* Fill in GENERATEd columns */
2001 ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2002
2003 /* ensure slot is independent, consider e.g. EPQ */
2004 ExecMaterializeSlot(slot);
2005
2006 /*
2007 * If partition constraint fails, this row might get moved to another
2008 * partition, in which case we should check the RLS CHECK policy just
2009 * before inserting into the new partition, rather than doing it here.
2010 * This is because a trigger on that partition might again change the row.
2011 * So skip the WCO checks if the partition constraint fails.
2012 */
2013 partition_constraint_failed =
2014 resultRelationDesc->rd_rel->relispartition &&
2015 !ExecPartitionCheck(resultRelInfo, slot, estate, false);
2016
2017 /* Check any RLS UPDATE WITH CHECK policies */
2018 if (!partition_constraint_failed &&
2019 resultRelInfo->ri_WithCheckOptions != NIL)
2020 {
2021 /*
2022 * ExecWithCheckOptions() will skip any WCOs which are not of the kind
2023 * we are looking for at this point.
2024 */
2026 resultRelInfo, slot, estate);
2027 }
2028
2029 /*
2030 * If a partition check failed, try to move the row into the right
2031 * partition.
2032 */
2033 if (partition_constraint_failed)
2034 {
2035 TupleTableSlot *inserted_tuple,
2036 *retry_slot;
2037 ResultRelInfo *insert_destrel = NULL;
2038
2039 /*
2040 * ExecCrossPartitionUpdate will first DELETE the row from the
2041 * partition it's currently in and then insert it back into the root
2042 * table, which will re-route it to the correct partition. However,
2043 * if the tuple has been concurrently updated, a retry is needed.
2044 */
2045 if (ExecCrossPartitionUpdate(context, resultRelInfo,
2046 tupleid, oldtuple, slot,
2047 canSetTag, updateCxt,
2048 &result,
2049 &retry_slot,
2050 &inserted_tuple,
2051 &insert_destrel))
2052 {
2053 /* success! */
2054 updateCxt->crossPartUpdate = true;
2055
2056 /*
2057 * If the partitioned table being updated is referenced in foreign
2058 * keys, queue up trigger events to check that none of them were
2059 * violated. No special treatment is needed in
2060 * non-cross-partition update situations, because the leaf
2061 * partition's AR update triggers will take care of that. During
2062 * cross-partition updates implemented as delete on the source
2063 * partition followed by insert on the destination partition,
2064 * AR-UPDATE triggers of the root table (that is, the table
2065 * mentioned in the query) must be fired.
2066 *
2067 * NULL insert_destrel means that the move failed to occur, that
2068 * is, the update failed, so no need to anything in that case.
2069 */
2070 if (insert_destrel &&
2071 resultRelInfo->ri_TrigDesc &&
2072 resultRelInfo->ri_TrigDesc->trig_update_after_row)
2074 resultRelInfo,
2075 insert_destrel,
2076 tupleid, slot,
2077 inserted_tuple);
2078
2079 return TM_Ok;
2080 }
2081
2082 /*
2083 * No luck, a retry is needed. If running MERGE, we do not do so
2084 * here; instead let it handle that on its own rules.
2085 */
2086 if (context->mtstate->operation == CMD_MERGE)
2087 return result;
2088
2089 /*
2090 * ExecCrossPartitionUpdate installed an updated version of the new
2091 * tuple in the retry slot; start over.
2092 */
2093 slot = retry_slot;
2094 goto lreplace;
2095 }
2096
2097 /*
2098 * Check the constraints of the tuple. We've already checked the
2099 * partition constraint above; however, we must still ensure the tuple
2100 * passes all other constraints, so we will call ExecConstraints() and
2101 * have it validate all remaining checks.
2102 */
2103 if (resultRelationDesc->rd_att->constr)
2104 ExecConstraints(resultRelInfo, slot, estate);
2105
2106 /*
2107 * replace the heap tuple
2108 *
2109 * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
2110 * the row to be updated is visible to that snapshot, and throw a
2111 * can't-serialize error if not. This is a special-case behavior needed
2112 * for referential integrity updates in transaction-snapshot mode
2113 * transactions.
2114 */
2115 result = table_tuple_update(resultRelationDesc, tupleid, slot,
2116 estate->es_output_cid,
2117 estate->es_snapshot,
2118 estate->es_crosscheck_snapshot,
2119 true /* wait for commit */ ,
2120 &context->tmfd, &updateCxt->lockmode,
2121 &updateCxt->updateIndexes);
2122
2123 return result;
2124}
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:1540

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

2136{
2137 ModifyTableState *mtstate = context->mtstate;
2138 List *recheckIndexes = NIL;
2139
2140 /* insert index entries for tuple if necessary */
2141 if (resultRelInfo->ri_NumIndices > 0 && (updateCxt->updateIndexes != TU_None))
2142 recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
2143 slot, context->estate,
2144 true, false,
2145 NULL, NIL,
2146 (updateCxt->updateIndexes == TU_Summarizing));
2147
2148 /* AFTER ROW UPDATE Triggers */
2149 ExecARUpdateTriggers(context->estate, resultRelInfo,
2150 NULL, NULL,
2151 tupleid, oldtuple, slot,
2152 recheckIndexes,
2153 mtstate->operation == CMD_INSERT ?
2154 mtstate->mt_oc_transition_capture :
2155 mtstate->mt_transition_capture,
2156 false);
2157
2158 list_free(recheckIndexes);
2159
2160 /*
2161 * Check any WITH CHECK OPTION constraints from parent views. We are
2162 * required to do this after testing all constraints and uniqueness
2163 * violations per the SQL spec, so we do it after actually updating the
2164 * record in the heap and all indexes.
2165 *
2166 * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
2167 * are looking for at this point.
2168 */
2169 if (resultRelInfo->ri_WithCheckOptions != NIL)
2170 ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo,
2171 slot, context->estate);
2172}
@ TU_Summarizing
Definition: tableam.h:125
@ TU_None
Definition: tableam.h:119

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

1950{
1951 Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1952
1953 /*
1954 * Constraints and GENERATED expressions might reference the tableoid
1955 * column, so (re-)initialize tts_tableOid before evaluating them.
1956 */
1957 slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1958
1959 /*
1960 * Compute stored generated columns
1961 */
1962 if (resultRelationDesc->rd_att->constr &&
1963 resultRelationDesc->rd_att->constr->has_generated_stored)
1964 ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1965 CMD_UPDATE);
1966}

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

1908{
1909 Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1910
1911 if (result)
1912 *result = TM_Ok;
1913
1914 ExecMaterializeSlot(slot);
1915
1916 /*
1917 * Open the table's indexes, if we have not done so already, so that we
1918 * can add new index entries for the updated tuple.
1919 */
1920 if (resultRelationDesc->rd_rel->relhasindex &&
1921 resultRelInfo->ri_IndexRelationDescs == NULL)
1922 ExecOpenIndices(resultRelInfo, false);
1923
1924 /* BEFORE ROW UPDATE triggers */
1925 if (resultRelInfo->ri_TrigDesc &&
1926 resultRelInfo->ri_TrigDesc->trig_update_before_row)
1927 {
1928 /* Flush any pending inserts, so rows are visible to the triggers */
1930 ExecPendingInserts(context->estate);
1931
1932 return ExecBRUpdateTriggers(context->estate, context->epqstate,
1933 resultRelInfo, tupleid, oldtuple, slot,
1934 result, &context->tmfd);
1935 }
1936
1937 return true;
1938}
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_Result *tmresult, TM_FailureData *tmfd)
Definition: trigger.c:2935

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

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 3677 of file nodeModifyTable.c.

3678{
3679 ModifyTable *plan = (ModifyTable *) node->ps.plan;
3680 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3681
3682 switch (node->operation)
3683 {
3684 case CMD_INSERT:
3685 if (plan->onConflictAction == ONCONFLICT_UPDATE)
3687 resultRelInfo,
3689 ExecASInsertTriggers(node->ps.state, resultRelInfo,
3690 node->mt_transition_capture);
3691 break;
3692 case CMD_UPDATE:
3693 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3694 node->mt_transition_capture);
3695 break;
3696 case CMD_DELETE:
3697 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3698 node->mt_transition_capture);
3699 break;
3700 case CMD_MERGE:
3702 ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3703 node->mt_transition_capture);
3705 ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3706 node->mt_transition_capture);
3708 ExecASInsertTriggers(node->ps.state, resultRelInfo,
3709 node->mt_transition_capture);
3710 break;
3711 default:
3712 elog(ERROR, "unknown operation");
3713 break;
3714 }
3715}
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2917
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2664
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2446

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

3641{
3642 ModifyTable *plan = (ModifyTable *) node->ps.plan;
3643 ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3644
3645 switch (node->operation)
3646 {
3647 case CMD_INSERT:
3648 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3649 if (plan->onConflictAction == ONCONFLICT_UPDATE)
3651 resultRelInfo);
3652 break;
3653 case CMD_UPDATE:
3654 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
3655 break;
3656 case CMD_DELETE:
3657 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
3658 break;
3659 case CMD_MERGE:
3661 ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3663 ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
3665 ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
3666 break;
3667 default:
3668 elog(ERROR, "unknown operation");
3669 break;
3670 }
3671}
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2395
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2613
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2859

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