PostgreSQL Source Code  git master
nodeModifyTable.c File Reference
#include "postgres.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.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 "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/rel.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 TupleTableSlotinternalGetUpdateNewTuple (ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot, MergeActionState *relaction)
 
static TupleTableSlotExecMerge (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, bool canSetTag)
 
static void ExecInitMerge (ModifyTableState *mtstate, EState *estate)
 
static bool ExecMergeMatched (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, bool canSetTag)
 
static void ExecMergeNotMatched (ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool canSetTag)
 
static TupleTableSlotmergeGetUpdateNewTuple (ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot, MergeActionState *relaction)
 
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 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)
 
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, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
 
static bool ExecCrossPartitionUpdate (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
 
static bool ExecUpdatePrologue (ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot)
 
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, List *recheckIndexes)
 
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 1168 of file nodeModifyTable.c.

1175 {
1176  int i;
1177  int numInserted = numSlots;
1178  TupleTableSlot *slot = NULL;
1179  TupleTableSlot **rslots;
1180 
1181  /*
1182  * insert into foreign table: let the FDW do it
1183  */
1184  rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1185  resultRelInfo,
1186  slots,
1187  planSlots,
1188  &numInserted);
1189 
1190  for (i = 0; i < numInserted; i++)
1191  {
1192  slot = rslots[i];
1193 
1194  /*
1195  * AFTER ROW Triggers might reference the tableoid column, so
1196  * (re-)initialize tts_tableOid before evaluating them.
1197  */
1198  slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1199 
1200  /* AFTER ROW INSERT Triggers */
1201  ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1202  mtstate->mt_transition_capture);
1203 
1204  /*
1205  * Check any WITH CHECK OPTION constraints from parent views. See the
1206  * comment in ExecInsert.
1207  */
1208  if (resultRelInfo->ri_WithCheckOptions != NIL)
1209  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1210  }
1211 
1212  if (canSetTag && numInserted > 0)
1213  estate->es_processed += numInserted;
1214 }
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2045
int i
Definition: isn.c:73
@ WCO_VIEW_CHECK
Definition: parsenodes.h:1241
#define NIL
Definition: pg_list.h:66
#define RelationGetRelid(relation)
Definition: rel.h:501
uint64 es_processed
Definition: execnodes.h:659
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1295
Relation ri_RelationDesc
Definition: execnodes.h:448
List * ri_WithCheckOptions
Definition: execnodes.h:508
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:492
Oid tts_tableOid
Definition: tuptable.h:131
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2539

References EState::es_processed, ExecARInsertTriggers(), FdwRoutine::ExecForeignBatchInsert, ExecWithCheckOptions(), i, ModifyTableState::mt_transition_capture, NIL, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, 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 203 of file nodeModifyTable.c.

204 {
205  TupleDesc resultDesc = RelationGetDescr(resultRel);
206  int attno = 0;
207  ListCell *lc;
208 
209  foreach(lc, targetList)
210  {
211  TargetEntry *tle = (TargetEntry *) lfirst(lc);
212  Form_pg_attribute attr;
213 
214  Assert(!tle->resjunk); /* caller removed junk items already */
215 
216  if (attno >= resultDesc->natts)
217  ereport(ERROR,
218  (errcode(ERRCODE_DATATYPE_MISMATCH),
219  errmsg("table row type and query-specified row type do not match"),
220  errdetail("Query has too many columns.")));
221  attr = TupleDescAttr(resultDesc, attno);
222  attno++;
223 
224  if (!attr->attisdropped)
225  {
226  /* Normal case: demand type match */
227  if (exprType((Node *) tle->expr) != attr->atttypid)
228  ereport(ERROR,
229  (errcode(ERRCODE_DATATYPE_MISMATCH),
230  errmsg("table row type and query-specified row type do not match"),
231  errdetail("Table has type %s at ordinal position %d, but query expects %s.",
232  format_type_be(attr->atttypid),
233  attno,
234  format_type_be(exprType((Node *) tle->expr)))));
235  }
236  else
237  {
238  /*
239  * For a dropped column, we can't check atttypid (it's likely 0).
240  * In any case the planner has most likely inserted an INT4 null.
241  * What we insist on is just *some* NULL constant.
242  */
243  if (!IsA(tle->expr, Const) ||
244  !((Const *) tle->expr)->constisnull)
245  ereport(ERROR,
246  (errcode(ERRCODE_DATATYPE_MISMATCH),
247  errmsg("table row type and query-specified row type do not match"),
248  errdetail("Query provides a value for a dropped column at ordinal position %d.",
249  attno)));
250  }
251  }
252  if (attno != resultDesc->natts)
253  ereport(ERROR,
254  (errcode(ERRCODE_DATATYPE_MISMATCH),
255  errmsg("table row type and query-specified row type do not match"),
256  errdetail("Query has too few columns.")));
257 }
int errdetail(const char *fmt,...)
Definition: elog.c:1039
int errcode(int sqlerrcode)
Definition: elog.c:695
int errmsg(const char *fmt,...)
Definition: elog.c:906
#define ERROR
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:145
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
Assert(fmt[strlen(fmt) - 1] !='\n')
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:43
#define IsA(nodeptr, _type_)
Definition: nodes.h:168
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
#define lfirst(lc)
Definition: pg_list.h:170
#define RelationGetDescr(relation)
Definition: rel.h:527
Definition: nodes.h:118
Expr * expr
Definition: primnodes.h:1555
bool resjunk
Definition: primnodes.h:1562
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

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

Referenced by ExecInitInsertProjection(), and ExecInitMerge().

◆ ExecCheckTIDVisible()

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

Definition at line 338 of file nodeModifyTable.c.

342 {
343  Relation rel = relinfo->ri_RelationDesc;
344 
345  /* Redundantly check isolation level */
347  return;
348 
349  if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
350  elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
351  ExecCheckTupleVisible(estate, rel, tempSlot);
352  ExecClearTuple(tempSlot);
353 }
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
#define SnapshotAny
Definition: snapmgr.h:67
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1259
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:433
#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 304 of file nodeModifyTable.c.

307 {
309  return;
310 
311  if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
312  {
313  Datum xminDatum;
314  TransactionId xmin;
315  bool isnull;
316 
317  xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
318  Assert(!isnull);
319  xmin = DatumGetTransactionId(xminDatum);
320 
321  /*
322  * We should not raise a serialization failure if the conflict is
323  * against a tuple inserted by our own transaction, even if it's not
324  * visible to our snapshot. (This would happen, for example, if
325  * conflicting keys are proposed for insertion in a single command.)
326  */
328  ereport(ERROR,
330  errmsg("could not serialize access due to concurrent update")));
331  }
332 }
uint32 TransactionId
Definition: c.h:588
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition: pgbench.c:75
uintptr_t Datum
Definition: postgres.h:412
static TransactionId DatumGetTransactionId(Datum X)
Definition: postgres.h:610
Snapshot es_snapshot
Definition: execnodes.h:612
#define MinTransactionIdAttributeNumber
Definition: sysattr.h:22
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition: tableam.h:1306
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:410
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:925

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

362 {
363  Relation rel = resultRelInfo->ri_RelationDesc;
364  TupleDesc tupdesc = RelationGetDescr(rel);
365  int natts = tupdesc->natts;
366  MemoryContext oldContext;
367  Datum *values;
368  bool *nulls;
369 
370  Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
371 
372  /*
373  * If first time through for this result relation, build expression
374  * nodetrees for rel's stored generation expressions. Keep them in the
375  * per-query memory context so they'll survive throughout the query.
376  */
377  if (resultRelInfo->ri_GeneratedExprs == NULL)
378  {
379  oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
380 
381  resultRelInfo->ri_GeneratedExprs =
382  (ExprState **) palloc(natts * sizeof(ExprState *));
383  resultRelInfo->ri_NumGeneratedNeeded = 0;
384 
385  for (int i = 0; i < natts; i++)
386  {
387  if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
388  {
389  Expr *expr;
390 
391  /*
392  * If it's an update and the current column was not marked as
393  * being updated, then we can skip the computation. But if
394  * there is a BEFORE ROW UPDATE trigger, we cannot skip
395  * because the trigger might affect additional columns.
396  */
397  if (cmdtype == CMD_UPDATE &&
398  !(rel->trigdesc && rel->trigdesc->trig_update_before_row) &&
400  ExecGetExtraUpdatedCols(resultRelInfo, estate)))
401  {
402  resultRelInfo->ri_GeneratedExprs[i] = NULL;
403  continue;
404  }
405 
406  expr = (Expr *) build_column_default(rel, i + 1);
407  if (expr == NULL)
408  elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
409  i + 1, RelationGetRelationName(rel));
410 
411  resultRelInfo->ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
412  resultRelInfo->ri_NumGeneratedNeeded++;
413  }
414  }
415 
416  MemoryContextSwitchTo(oldContext);
417  }
418 
419  /*
420  * If no generated columns have been affected by this change, then skip
421  * the rest.
422  */
423  if (resultRelInfo->ri_NumGeneratedNeeded == 0)
424  return;
425 
426  oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
427 
428  values = palloc(sizeof(*values) * natts);
429  nulls = palloc(sizeof(*nulls) * natts);
430 
431  slot_getallattrs(slot);
432  memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
433 
434  for (int i = 0; i < natts; i++)
435  {
436  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
437 
438  if (attr->attgenerated == ATTRIBUTE_GENERATED_STORED &&
439  resultRelInfo->ri_GeneratedExprs[i])
440  {
441  ExprContext *econtext;
442  Datum val;
443  bool isnull;
444 
445  econtext = GetPerTupleExprContext(estate);
446  econtext->ecxt_scantuple = slot;
447 
448  val = ExecEvalExpr(resultRelInfo->ri_GeneratedExprs[i], econtext, &isnull);
449 
450  /*
451  * We must make a copy of val as we have no guarantees about where
452  * memory for a pass-by-reference Datum is located.
453  */
454  if (!isnull)
455  val = datumCopy(val, attr->attbyval, attr->attlen);
456 
457  values[i] = val;
458  nulls[i] = isnull;
459  }
460  else
461  {
462  if (!nulls[i])
463  values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
464  }
465  }
466 
467  ExecClearTuple(slot);
468  memcpy(slot->tts_values, values, sizeof(*values) * natts);
469  memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
470  ExecStoreVirtualTuple(slot);
471  ExecMaterializeSlot(slot);
472 
473  MemoryContextSwitchTo(oldContext);
474 }
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:428
static Datum values[MAXATTR]
Definition: bootstrap.c:156
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:747
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1552
Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition: execUtils.c:1362
#define GetPerTupleExprContext(estate)
Definition: executor.h:535
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:540
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:318
long val
Definition: informix.c:664
void * palloc(Size size)
Definition: mcxt.c:1199
@ CMD_UPDATE
Definition: nodes.h:266
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:135
#define RelationGetRelationName(relation)
Definition: rel.h:535
Node * build_column_default(Relation rel, int attrno)
MemoryContext es_query_cxt
Definition: execnodes.h:655
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:247
TriggerDesc * trigdesc
Definition: rel.h:116
ExprState ** ri_GeneratedExprs
Definition: execnodes.h:517
int ri_NumGeneratedNeeded
Definition: execnodes.h:520
bool trig_update_before_row
Definition: reltrigger.h:61
bool has_generated_stored
Definition: tupdesc.h:45
TupleConstr * constr
Definition: tupdesc.h:85
bool * tts_isnull
Definition: tuptable.h:128
Datum * tts_values
Definition: tuptable.h:126
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:362
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:451

References Assert(), bms_is_member(), build_column_default(), CMD_UPDATE, TupleDescData::constr, datumCopy(), ExprContext::ecxt_scantuple, elog(), ERROR, EState::es_query_cxt, ExecClearTuple(), ExecEvalExpr(), ExecGetExtraUpdatedCols(), ExecMaterializeSlot(), ExecPrepareExpr(), ExecStoreVirtualTuple(), FirstLowInvalidHeapAttributeNumber, GetPerTupleExprContext, GetPerTupleMemoryContext, TupleConstr::has_generated_stored, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc(), RelationGetDescr, RelationGetRelationName, ResultRelInfo::ri_GeneratedExprs, ResultRelInfo::ri_NumGeneratedNeeded, ResultRelInfo::ri_RelationDesc, slot_getallattrs(), TriggerDesc::trig_update_before_row, RelationData::trigdesc, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TupleDescAttr, 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,
TupleTableSlot **  inserted_tuple,
ResultRelInfo **  insert_destrel 
)
static

Definition at line 1666 of file nodeModifyTable.c.

1674 {
1675  ModifyTableState *mtstate = context->mtstate;
1676  EState *estate = mtstate->ps.state;
1677  TupleConversionMap *tupconv_map;
1678  bool tuple_deleted;
1679  TupleTableSlot *epqslot = NULL;
1680 
1681  context->cpUpdateReturningSlot = NULL;
1682  context->cpUpdateRetrySlot = NULL;
1683 
1684  /*
1685  * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1686  * to migrate to a different partition. Maybe this can be implemented
1687  * some day, but it seems a fringe feature with little redeeming value.
1688  */
1689  if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1690  ereport(ERROR,
1691  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1692  errmsg("invalid ON UPDATE specification"),
1693  errdetail("The result tuple would appear in a different partition than the original tuple.")));
1694 
1695  /*
1696  * When an UPDATE is run directly on a leaf partition, simply fail with a
1697  * partition constraint violation error.
1698  */
1699  if (resultRelInfo == mtstate->rootResultRelInfo)
1700  ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1701 
1702  /* Initialize tuple routing info if not already done. */
1703  if (mtstate->mt_partition_tuple_routing == NULL)
1704  {
1705  Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
1706  MemoryContext oldcxt;
1707 
1708  /* Things built here have to last for the query duration. */
1709  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
1710 
1711  mtstate->mt_partition_tuple_routing =
1712  ExecSetupPartitionTupleRouting(estate, rootRel);
1713 
1714  /*
1715  * Before a partition's tuple can be re-routed, it must first be
1716  * converted to the root's format, so we'll need a slot for storing
1717  * such tuples.
1718  */
1719  Assert(mtstate->mt_root_tuple_slot == NULL);
1720  mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
1721 
1722  MemoryContextSwitchTo(oldcxt);
1723  }
1724 
1725  /*
1726  * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
1727  * We want to return rows from INSERT.
1728  */
1729  ExecDelete(context, resultRelInfo,
1730  tupleid, oldtuple,
1731  false, /* processReturning */
1732  true, /* changingPart */
1733  false, /* canSetTag */
1734  &tuple_deleted, &epqslot);
1735 
1736  /*
1737  * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
1738  * it was already deleted by self, or it was concurrently deleted by
1739  * another transaction), then we should skip the insert as well;
1740  * otherwise, an UPDATE could cause an increase in the total number of
1741  * rows across all partitions, which is clearly wrong.
1742  *
1743  * For a normal UPDATE, the case where the tuple has been the subject of a
1744  * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
1745  * machinery, but for an UPDATE that we've translated into a DELETE from
1746  * this partition and an INSERT into some other partition, that's not
1747  * available, because CTID chains can't span relation boundaries. We
1748  * mimic the semantics to a limited extent by skipping the INSERT if the
1749  * DELETE fails to find a tuple. This ensures that two concurrent
1750  * attempts to UPDATE the same tuple at the same time can't turn one tuple
1751  * into two, and that an UPDATE of a just-deleted tuple can't resurrect
1752  * it.
1753  */
1754  if (!tuple_deleted)
1755  {
1756  /*
1757  * epqslot will be typically NULL. But when ExecDelete() finds that
1758  * another transaction has concurrently updated the same row, it
1759  * re-fetches the row, skips the delete, and epqslot is set to the
1760  * re-fetched tuple slot. In that case, we need to do all the checks
1761  * again.
1762  */
1763  if (TupIsNull(epqslot))
1764  return true;
1765  else
1766  {
1767  /* Fetch the most recent version of old tuple. */
1768  TupleTableSlot *oldSlot;
1769 
1770  /* ... but first, make sure ri_oldTupleSlot is initialized. */
1771  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
1772  ExecInitUpdateProjection(mtstate, resultRelInfo);
1773  oldSlot = resultRelInfo->ri_oldTupleSlot;
1774  if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
1775  tupleid,
1776  SnapshotAny,
1777  oldSlot))
1778  elog(ERROR, "failed to fetch tuple being updated");
1779  /* and project the new tuple to retry the UPDATE with */
1780  context->cpUpdateRetrySlot =
1781  context->GetUpdateNewTuple(resultRelInfo, epqslot, oldSlot,
1782  context->relaction);
1783  return false;
1784  }
1785  }
1786 
1787  /*
1788  * resultRelInfo is one of the per-relation resultRelInfos. So we should
1789  * convert the tuple into root's tuple descriptor if needed, since
1790  * ExecInsert() starts the search from root.
1791  */
1792  tupconv_map = ExecGetChildToRootMap(resultRelInfo);
1793  if (tupconv_map != NULL)
1794  slot = execute_attr_map_slot(tupconv_map->attrMap,
1795  slot,
1796  mtstate->mt_root_tuple_slot);
1797 
1798  /* Tuple routing starts from the root table. */
1799  context->cpUpdateReturningSlot =
1800  ExecInsert(context, mtstate->rootResultRelInfo, slot, canSetTag,
1801  inserted_tuple, insert_destrel);
1802 
1803  /*
1804  * Reset the transition state that may possibly have been written by
1805  * INSERT.
1806  */
1807  if (mtstate->mt_transition_capture)
1809 
1810  /* We're done moving. */
1811  return true;
1812 }
#define unlikely(x)
Definition: c.h:295
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1839
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1236
static TupleTableSlot * ExecInsert(ModifyTableContext *context, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, bool canSetTag, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
static void ExecInitUpdateProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static TupleTableSlot * ExecDelete(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, bool processReturning, bool changingPart, bool canSetTag, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
@ ONCONFLICT_UPDATE
Definition: nodes.h:417
MergeActionState * relaction
TupleTableSlot *(* GetUpdateNewTuple)(ResultRelInfo *resultRelInfo, TupleTableSlot *epqslot, TupleTableSlot *oldSlot, MergeActionState *relaction)
TupleTableSlot * cpUpdateReturningSlot
ModifyTableState * mtstate
TupleTableSlot * cpUpdateRetrySlot
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1292
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1289
PlanState ps
Definition: execnodes.h:1256
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1269
Plan * plan
Definition: execnodes.h:1029
EState * state
Definition: execnodes.h:1031
bool ri_projectNewInfoValid
Definition: execnodes.h:472
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:470
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
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:192
#define TupIsNull(slot)
Definition: tuptable.h:300

References Assert(), TupleConversionMap::attrMap, ModifyTableContext::cpUpdateRetrySlot, ModifyTableContext::cpUpdateReturningSlot, elog(), ereport, errcode(), errdetail(), errmsg(), ERROR, EState::es_query_cxt, ExecDelete(), ExecGetChildToRootMap(), ExecInitUpdateProjection(), ExecInsert(), ExecPartitionCheckEmitError(), ExecSetupPartitionTupleRouting(), execute_attr_map_slot(), ModifyTableContext::GetUpdateNewTuple, MemoryContextSwitchTo(), ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, ONCONFLICT_UPDATE, PlanState::plan, ModifyTableState::ps, ModifyTableContext::relaction, 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, 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 2091 of file nodeModifyTable.c.

2097 {
2098  ListCell *lc;
2099  ResultRelInfo *rootRelInfo;
2100  List *ancestorRels;
2101 
2102  rootRelInfo = sourcePartInfo->ri_RootResultRelInfo;
2103  ancestorRels = ExecGetAncestorResultRels(context->estate, sourcePartInfo);
2104 
2105  /*
2106  * For any foreign keys that point directly into a non-root ancestors of
2107  * the source partition, we can in theory fire an update event to enforce
2108  * those constraints using their triggers, if we could tell that both the
2109  * source and the destination partitions are under the same ancestor. But
2110  * for now, we simply report an error that those cannot be enforced.
2111  */
2112  foreach(lc, ancestorRels)
2113  {
2114  ResultRelInfo *rInfo = lfirst(lc);
2115  TriggerDesc *trigdesc = rInfo->ri_TrigDesc;
2116  bool has_noncloned_fkey = false;
2117 
2118  /* Root ancestor's triggers will be processed. */
2119  if (rInfo == rootRelInfo)
2120  continue;
2121 
2122  if (trigdesc && trigdesc->trig_update_after_row)
2123  {
2124  for (int i = 0; i < trigdesc->numtriggers; i++)
2125  {
2126  Trigger *trig = &trigdesc->triggers[i];
2127 
2128  if (!trig->tgisclone &&
2130  {
2131  has_noncloned_fkey = true;
2132  break;
2133  }
2134  }
2135  }
2136 
2137  if (has_noncloned_fkey)
2138  ereport(ERROR,
2139  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2140  errmsg("cannot move tuple across partitions when a non-root ancestor of the source partition is directly referenced in a foreign key"),
2141  errdetail("A foreign key points to ancestor \"%s\" but not the root ancestor \"%s\".",
2144  errhint("Consider defining the foreign key on table \"%s\".",
2145  RelationGetRelationName(rootRelInfo->ri_RelationDesc))));
2146  }
2147 
2148  /* Perform the root table's triggers. */
2149  ExecARUpdateTriggers(context->estate,
2150  rootRelInfo, sourcePartInfo, destPartInfo,
2151  tupleid, NULL, newslot, NIL, NULL, true);
2152 }
int errhint(const char *fmt,...)
Definition: elog.c:1153
List * ExecGetAncestorResultRels(EState *estate, ResultRelInfo *resultRelInfo)
Definition: execMain.c:1366
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:2995
Definition: pg_list.h:52
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:567
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:475
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:3080
#define RI_TRIGGER_PK
Definition: trigger.h:279

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,
bool tupleDeleted,
TupleTableSlot **  epqreturnslot 
)
static

Definition at line 1358 of file nodeModifyTable.c.

1367 {
1368  EState *estate = context->estate;
1369  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1370  TupleTableSlot *slot = NULL;
1371  TM_Result result;
1372 
1373  if (tupleDeleted)
1374  *tupleDeleted = false;
1375 
1376  /*
1377  * Prepare for the delete. This includes BEFORE ROW triggers, so we're
1378  * done if it says we are.
1379  */
1380  if (!ExecDeletePrologue(context, resultRelInfo, tupleid, oldtuple,
1381  epqreturnslot))
1382  return NULL;
1383 
1384  /* INSTEAD OF ROW DELETE Triggers */
1385  if (resultRelInfo->ri_TrigDesc &&
1386  resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
1387  {
1388  bool dodelete;
1389 
1390  Assert(oldtuple != NULL);
1391  dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
1392 
1393  if (!dodelete) /* "do nothing" */
1394  return NULL;
1395  }
1396  else if (resultRelInfo->ri_FdwRoutine)
1397  {
1398  /*
1399  * delete from foreign table: let the FDW do it
1400  *
1401  * We offer the returning slot as a place to store RETURNING data,
1402  * although the FDW can return some other slot if it wants.
1403  */
1404  slot = ExecGetReturningSlot(estate, resultRelInfo);
1405  slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
1406  resultRelInfo,
1407  slot,
1408  context->planSlot);
1409 
1410  if (slot == NULL) /* "do nothing" */
1411  return NULL;
1412 
1413  /*
1414  * RETURNING expressions might reference the tableoid column, so
1415  * (re)initialize tts_tableOid before evaluating them.
1416  */
1417  if (TTS_EMPTY(slot))
1418  ExecStoreAllNullTuple(slot);
1419 
1420  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1421  }
1422  else
1423  {
1424  /*
1425  * delete the tuple
1426  *
1427  * Note: if context->estate->es_crosscheck_snapshot isn't
1428  * InvalidSnapshot, we check that the row to be deleted is visible to
1429  * that snapshot, and throw a can't-serialize error if not. This is a
1430  * special-case behavior needed for referential integrity updates in
1431  * transaction-snapshot mode transactions.
1432  */
1433 ldelete:
1434  result = ExecDeleteAct(context, resultRelInfo, tupleid, changingPart);
1435 
1436  switch (result)
1437  {
1438  case TM_SelfModified:
1439 
1440  /*
1441  * The target tuple was already updated or deleted by the
1442  * current command, or by a later command in the current
1443  * transaction. The former case is possible in a join DELETE
1444  * where multiple tuples join to the same target tuple. This
1445  * is somewhat questionable, but Postgres has always allowed
1446  * it: we just ignore additional deletion attempts.
1447  *
1448  * The latter case arises if the tuple is modified by a
1449  * command in a BEFORE trigger, or perhaps by a command in a
1450  * volatile function used in the query. In such situations we
1451  * should not ignore the deletion, but it is equally unsafe to
1452  * proceed. We don't want to discard the original DELETE
1453  * while keeping the triggered actions based on its deletion;
1454  * and it would be no better to allow the original DELETE
1455  * while discarding updates that it triggered. The row update
1456  * carries some information that might be important according
1457  * to business rules; so throwing an error is the only safe
1458  * course.
1459  *
1460  * If a trigger actually intends this type of interaction, it
1461  * can re-execute the DELETE and then return NULL to cancel
1462  * the outer delete.
1463  */
1464  if (context->tmfd.cmax != estate->es_output_cid)
1465  ereport(ERROR,
1466  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1467  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1468  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1469 
1470  /* Else, already deleted by self; nothing to do */
1471  return NULL;
1472 
1473  case TM_Ok:
1474  break;
1475 
1476  case TM_Updated:
1477  {
1478  TupleTableSlot *inputslot;
1479  TupleTableSlot *epqslot;
1480 
1482  ereport(ERROR,
1484  errmsg("could not serialize access due to concurrent update")));
1485 
1486  /*
1487  * Already know that we're going to need to do EPQ, so
1488  * fetch tuple directly into the right slot.
1489  */
1490  EvalPlanQualBegin(context->epqstate);
1491  inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
1492  resultRelInfo->ri_RangeTableIndex);
1493 
1494  result = table_tuple_lock(resultRelationDesc, tupleid,
1495  estate->es_snapshot,
1496  inputslot, estate->es_output_cid,
1499  &context->tmfd);
1500 
1501  switch (result)
1502  {
1503  case TM_Ok:
1504  Assert(context->tmfd.traversed);
1505  epqslot = EvalPlanQual(context->epqstate,
1506  resultRelationDesc,
1507  resultRelInfo->ri_RangeTableIndex,
1508  inputslot);
1509  if (TupIsNull(epqslot))
1510  /* Tuple not passing quals anymore, exiting... */
1511  return NULL;
1512 
1513  /*
1514  * If requested, skip delete and pass back the
1515  * updated row.
1516  */
1517  if (epqreturnslot)
1518  {
1519  *epqreturnslot = epqslot;
1520  return NULL;
1521  }
1522  else
1523  goto ldelete;
1524 
1525  case TM_SelfModified:
1526 
1527  /*
1528  * This can be reached when following an update
1529  * chain from a tuple updated by another session,
1530  * reaching a tuple that was already updated in
1531  * this transaction. If previously updated by this
1532  * command, ignore the delete, otherwise error
1533  * out.
1534  *
1535  * See also TM_SelfModified response to
1536  * table_tuple_delete() above.
1537  */
1538  if (context->tmfd.cmax != estate->es_output_cid)
1539  ereport(ERROR,
1540  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1541  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1542  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1543  return NULL;
1544 
1545  case TM_Deleted:
1546  /* tuple already deleted; nothing to do */
1547  return NULL;
1548 
1549  default:
1550 
1551  /*
1552  * TM_Invisible should be impossible because we're
1553  * waiting for updated row versions, and would
1554  * already have errored out if the first version
1555  * is invisible.
1556  *
1557  * TM_Updated should be impossible, because we're
1558  * locking the latest version via
1559  * TUPLE_LOCK_FLAG_FIND_LAST_VERSION.
1560  */
1561  elog(ERROR, "unexpected table_tuple_lock status: %u",
1562  result);
1563  return NULL;
1564  }
1565 
1566  Assert(false);
1567  break;
1568  }
1569 
1570  case TM_Deleted:
1572  ereport(ERROR,
1574  errmsg("could not serialize access due to concurrent delete")));
1575  /* tuple already deleted; nothing to do */
1576  return NULL;
1577 
1578  default:
1579  elog(ERROR, "unrecognized table_tuple_delete status: %u",
1580  result);
1581  return NULL;
1582  }
1583 
1584  /*
1585  * Note: Normally one would think that we have to delete index tuples
1586  * associated with the heap tuple now...
1587  *
1588  * ... but in POSTGRES, we have no need to do this because VACUUM will
1589  * take care of it later. We can't delete index tuples immediately
1590  * anyway, since the tuple is still visible to other transactions.
1591  */
1592  }
1593 
1594  if (canSetTag)
1595  (estate->es_processed)++;
1596 
1597  /* Tell caller that the delete actually happened. */
1598  if (tupleDeleted)
1599  *tupleDeleted = true;
1600 
1601  ExecDeleteEpilogue(context, resultRelInfo, tupleid, oldtuple, changingPart);
1602 
1603  /* Process RETURNING if present and if requested */
1604  if (processReturning && resultRelInfo->ri_projectReturning)
1605  {
1606  /*
1607  * We have to put the target tuple into a slot, which means first we
1608  * gotta fetch it. We can use the trigger tuple slot.
1609  */
1610  TupleTableSlot *rslot;
1611 
1612  if (resultRelInfo->ri_FdwRoutine)
1613  {
1614  /* FDW must have provided a slot containing the deleted row */
1615  Assert(!TupIsNull(slot));
1616  }
1617  else
1618  {
1619  slot = ExecGetReturningSlot(estate, resultRelInfo);
1620  if (oldtuple != NULL)
1621  {
1622  ExecForceStoreHeapTuple(oldtuple, slot, false);
1623  }
1624  else
1625  {
1626  if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
1627  SnapshotAny, slot))
1628  elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
1629  }
1630  }
1631 
1632  rslot = ExecProcessReturning(resultRelInfo, slot, context->planSlot);
1633 
1634  /*
1635  * Before releasing the target tuple again, make sure rslot has a
1636  * local copy of any pass-by-reference values.
1637  */
1638  ExecMaterializeSlot(rslot);
1639 
1640  ExecClearTuple(slot);
1641 
1642  return rslot;
1643  }
1644 
1645  return NULL;
1646 }
void EvalPlanQualBegin(EPQState *epqstate)
Definition: execMain.c:2727
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2464
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition: execMain.c:2574
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1576
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1469
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1212
@ LockWaitBlock
Definition: lockoptions.h:39
@ LockTupleExclusive
Definition: lockoptions.h:58
static bool ExecDeletePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot **epqreturnslot)
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:627
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:236
TM_FailureData tmfd
TupleTableSlot * planSlot
Index ri_RangeTableIndex
Definition: execnodes.h:445
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:526
bool traversed
Definition: tableam.h:129
CommandId cmax
Definition: tableam.h:128
bool trig_delete_instead_row
Definition: reltrigger.h:68
TM_Result
Definition: tableam.h:72
@ TM_Ok
Definition: tableam.h:77
@ TM_Deleted
Definition: tableam.h:92
@ TM_Updated
Definition: tableam.h:89
@ TM_SelfModified
Definition: tableam.h:83
static TM_Result table_tuple_lock(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd)
Definition: tableam.h:1551
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:244
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition: trigger.c:2813
#define TTS_EMPTY(slot)
Definition: tuptable.h:97

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

1280 {
1281  EState *estate = context->estate;
1282 
1283  return table_tuple_delete(resultRelInfo->ri_RelationDesc, tupleid,
1284  estate->es_output_cid,
1285  estate->es_snapshot,
1286  estate->es_crosscheck_snapshot,
1287  true /* wait for commit */ ,
1288  &context->tmfd,
1289  changingPart);
1290 }
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:613
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:1462

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

1302 {
1303  ModifyTableState *mtstate = context->mtstate;
1304  EState *estate = context->estate;
1305  TransitionCaptureState *ar_delete_trig_tcs;
1306 
1307  /*
1308  * If this delete is the result of a partition key update that moved the
1309  * tuple to a new partition, put this row into the transition OLD TABLE,
1310  * if there is one. We need to do this separately for DELETE and INSERT
1311  * because they happen on different tables.
1312  */
1313  ar_delete_trig_tcs = mtstate->mt_transition_capture;
1314  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture &&
1316  {
1317  ExecARUpdateTriggers(estate, resultRelInfo,
1318  NULL, NULL,
1319  tupleid, oldtuple,
1320  NULL, NULL, mtstate->mt_transition_capture,
1321  false);
1322 
1323  /*
1324  * We've already captured the OLD TABLE row, so make sure any AR
1325  * DELETE trigger fired below doesn't capture it again.
1326  */
1327  ar_delete_trig_tcs = NULL;
1328  }
1329 
1330  /* AFTER ROW DELETE Triggers */
1331  ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1332  ar_delete_trig_tcs, changingPart);
1333 }
CmdType operation
Definition: execnodes.h:1257
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture, bool is_crosspart_update)
Definition: trigger.c:2777

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 
)
static

Definition at line 1250 of file nodeModifyTable.c.

1253 {
1254  /* BEFORE ROW DELETE triggers */
1255  if (resultRelInfo->ri_TrigDesc &&
1256  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1257  {
1258  /* Flush any pending inserts, so rows are visible to the triggers */
1260  ExecPendingInserts(context->estate);
1261 
1262  return ExecBRDeleteTriggers(context->estate, context->epqstate,
1263  resultRelInfo, tupleid, oldtuple,
1264  epqreturnslot);
1265  }
1266 
1267  return true;
1268 }
static void ExecPendingInserts(EState *estate)
List * es_insert_pending_result_relations
Definition: execnodes.h:708
bool trig_delete_before_row
Definition: reltrigger.h:66
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot)
Definition: trigger.c:2688

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

Referenced by ExecDelete(), and ExecMergeMatched().

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 4379 of file nodeModifyTable.c.

4380 {
4381  int i;
4382 
4383  /*
4384  * Allow any FDWs to shut down
4385  */
4386  for (i = 0; i < node->mt_nrels; i++)
4387  {
4388  int j;
4389  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
4390 
4391  if (!resultRelInfo->ri_usesFdwDirectModify &&
4392  resultRelInfo->ri_FdwRoutine != NULL &&
4393  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
4394  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
4395  resultRelInfo);
4396 
4397  /*
4398  * Cleanup the initialized batch slots. This only matters for FDWs
4399  * with batching, but the other cases will have ri_NumSlotsInitialized
4400  * == 0.
4401  */
4402  for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
4403  {
4404  ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
4405  ExecDropSingleTupleTableSlot(resultRelInfo->ri_PlanSlots[j]);
4406  }
4407  }
4408 
4409  /*
4410  * Close all the partitioned tables, leaf partitions, and their indices
4411  * and release the slot used for tuple routing, if set.
4412  */
4413  if (node->mt_partition_tuple_routing)
4414  {
4416 
4417  if (node->mt_root_tuple_slot)
4419  }
4420 
4421  /*
4422  * Free the exprcontext
4423  */
4424  ExecFreeExprContext(&node->ps);
4425 
4426  /*
4427  * clean out the tuple table
4428  */
4429  if (node->ps.ps_ResultTupleSlot)
4431 
4432  /*
4433  * Terminate EPQ execution if active
4434  */
4435  EvalPlanQualEnd(&node->mt_epqstate);
4436 
4437  /*
4438  * shut down subplan
4439  */
4440  ExecEndNode(outerPlanState(node));
4441 }
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2939
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:557
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:652
#define outerPlanState(node)
Definition: execnodes.h:1125
int j
Definition: isn.c:74
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1261
EPQState mt_epqstate
Definition: execnodes.h:1271
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1067
TupleTableSlot ** ri_Slots
Definition: execnodes.h:504
int ri_NumSlotsInitialized
Definition: execnodes.h:502
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:505
bool ri_usesFdwDirectModify
Definition: execnodes.h:498

References FdwRoutine::EndForeignModify, EvalPlanQualEnd(), ExecCleanupTupleRouting(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecEndNode(), ExecFreeExprContext(), i, j, ModifyTableState::mt_epqstate, ModifyTableState::mt_nrels, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, outerPlanState, ModifyTableState::ps, PlanState::ps_ResultTupleSlot, 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 614 of file nodeModifyTable.c.

616 {
617  ProjectionInfo *newProj = relinfo->ri_projectNew;
618  ExprContext *econtext;
619 
620  /*
621  * If there's no projection to be done, just make sure the slot is of the
622  * right type for the target rel. If the planSlot is the right type we
623  * can use it as-is, else copy the data into ri_newTupleSlot.
624  */
625  if (newProj == NULL)
626  {
627  if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
628  {
629  ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
630  return relinfo->ri_newTupleSlot;
631  }
632  else
633  return planSlot;
634  }
635 
636  /*
637  * Else project; since the projection output slot is ri_newTupleSlot, this
638  * will also fix any slot-type problem.
639  *
640  * Note: currently, this is dead code, because INSERT cases don't receive
641  * any junk columns so there's never a projection to be done.
642  */
643  econtext = newProj->pi_exprContext;
644  econtext->ecxt_outertuple = planSlot;
645  return ExecProject(newProj);
646 }
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:361
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:251
ExprContext * pi_exprContext
Definition: execnodes.h:356
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:468
ProjectionInfo * ri_projectNew
Definition: execnodes.h:466
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:122
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:483

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

661 {
662  /* Use a few extra Asserts to protect against outside callers */
663  Assert(relinfo->ri_projectNewInfoValid);
664  Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
665  Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
666 
667  return internalGetUpdateNewTuple(relinfo, planSlot, oldSlot, NULL);
668 }
static TupleTableSlot * internalGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot, MergeActionState *relaction)

References Assert(), internalGetUpdateNewTuple(), ResultRelInfo::ri_projectNewInfoValid, and TTS_EMPTY.

Referenced by ExecBRUpdateTriggers(), and ExecUpdate().

◆ ExecInitInsertProjection()

static void ExecInitInsertProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 486 of file nodeModifyTable.c.

488 {
489  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
490  Plan *subplan = outerPlan(node);
491  EState *estate = mtstate->ps.state;
492  List *insertTargetList = NIL;
493  bool need_projection = false;
494  ListCell *l;
495 
496  /* Extract non-junk columns of the subplan's result tlist. */
497  foreach(l, subplan->targetlist)
498  {
499  TargetEntry *tle = (TargetEntry *) lfirst(l);
500 
501  if (!tle->resjunk)
502  insertTargetList = lappend(insertTargetList, tle);
503  else
504  need_projection = true;
505  }
506 
507  /*
508  * The junk-free list must produce a tuple suitable for the result
509  * relation.
510  */
511  ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
512 
513  /* We'll need a slot matching the table's format. */
514  resultRelInfo->ri_newTupleSlot =
515  table_slot_create(resultRelInfo->ri_RelationDesc,
516  &estate->es_tupleTable);
517 
518  /* Build ProjectionInfo if needed (it probably isn't). */
519  if (need_projection)
520  {
521  TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
522 
523  /* need an expression context to do the projection */
524  if (mtstate->ps.ps_ExprContext == NULL)
525  ExecAssignExprContext(estate, &mtstate->ps);
526 
527  resultRelInfo->ri_projectNew =
528  ExecBuildProjectionInfo(insertTargetList,
529  mtstate->ps.ps_ExprContext,
530  resultRelInfo->ri_newTupleSlot,
531  &mtstate->ps,
532  relDesc);
533  }
534 
535  resultRelInfo->ri_projectNewInfoValid = true;
536 }
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:354
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:482
List * lappend(List *list, void *datum)
Definition: list.c:338
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define outerPlan(node)
Definition: plannodes.h:183
List * es_tupleTable
Definition: execnodes.h:657
ExprContext * ps_ExprContext
Definition: execnodes.h:1068

References EState::es_tupleTable, ExecAssignExprContext(), ExecBuildProjectionInfo(), ExecCheckPlanOutput(), lappend(), lfirst, NIL, outerPlan, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, RelationGetDescr, TargetEntry::resjunk, 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 3140 of file nodeModifyTable.c.

3141 {
3142  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
3143  ResultRelInfo *rootRelInfo = mtstate->rootResultRelInfo;
3144  ResultRelInfo *resultRelInfo;
3145  ExprContext *econtext;
3146  ListCell *lc;
3147  int i;
3148 
3149  if (node->mergeActionLists == NIL)
3150  return;
3151 
3152  mtstate->mt_merge_subcommands = 0;
3153 
3154  if (mtstate->ps.ps_ExprContext == NULL)
3155  ExecAssignExprContext(estate, &mtstate->ps);
3156  econtext = mtstate->ps.ps_ExprContext;
3157 
3158  /*
3159  * Create a MergeActionState for each action on the mergeActionList and
3160  * add it to either a list of matched actions or not-matched actions.
3161  *
3162  * Similar logic appears in ExecInitPartitionInfo(), so if changing
3163  * anything here, do so there too.
3164  */
3165  i = 0;
3166  foreach(lc, node->mergeActionLists)
3167  {
3168  List *mergeActionList = lfirst(lc);
3169  TupleDesc relationDesc;
3170  ListCell *l;
3171 
3172  resultRelInfo = mtstate->resultRelInfo + i;
3173  i++;
3174  relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3175 
3176  /* initialize slots for MERGE fetches from this rel */
3177  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3178  ExecInitMergeTupleSlots(mtstate, resultRelInfo);
3179 
3180  foreach(l, mergeActionList)
3181  {
3183  MergeActionState *action_state;
3184  TupleTableSlot *tgtslot;
3185  TupleDesc tgtdesc;
3186  List **list;
3187 
3188  /*
3189  * Build action merge state for this rel. (For partitions,
3190  * equivalent code exists in ExecInitPartitionInfo.)
3191  */
3192  action_state = makeNode(MergeActionState);
3193  action_state->mas_action = action;
3194  action_state->mas_whenqual = ExecInitQual((List *) action->qual,
3195  &mtstate->ps);
3196 
3197  /*
3198  * We create two lists - one for WHEN MATCHED actions and one for
3199  * WHEN NOT MATCHED actions - and stick the MergeActionState into
3200  * the appropriate list.
3201  */
3202  if (action_state->mas_action->matched)
3203  list = &resultRelInfo->ri_matchedMergeAction;
3204  else
3205  list = &resultRelInfo->ri_notMatchedMergeAction;
3206  *list = lappend(*list, action_state);
3207 
3208  switch (action->commandType)
3209  {
3210  case CMD_INSERT:
3211  ExecCheckPlanOutput(rootRelInfo->ri_RelationDesc,
3212  action->targetList);
3213 
3214  /*
3215  * If the MERGE targets a partitioned table, any INSERT
3216  * actions must be routed through it, not the child
3217  * relations. Initialize the routing struct and the root
3218  * table's "new" tuple slot for that, if not already done.
3219  * The projection we prepare, for all relations, uses the
3220  * root relation descriptor, and targets the plan's root
3221  * slot. (This is consistent with the fact that we
3222  * checked the plan output to match the root relation,
3223  * above.)
3224  */
3225  if (rootRelInfo->ri_RelationDesc->rd_rel->relkind ==
3226  RELKIND_PARTITIONED_TABLE)
3227  {
3228  if (mtstate->mt_partition_tuple_routing == NULL)
3229  {
3230  /*
3231  * Initialize planstate for routing if not already
3232  * done.
3233  *
3234  * Note that the slot is managed as a standalone
3235  * slot belonging to ModifyTableState, so we pass
3236  * NULL for the 2nd argument.
3237  */
3238  mtstate->mt_root_tuple_slot =
3239  table_slot_create(rootRelInfo->ri_RelationDesc,
3240  NULL);
3241  mtstate->mt_partition_tuple_routing =
3243  rootRelInfo->ri_RelationDesc);
3244  }
3245  tgtslot = mtstate->mt_root_tuple_slot;
3246  tgtdesc = RelationGetDescr(rootRelInfo->ri_RelationDesc);
3247  }
3248  else
3249  {
3250  /* not partitioned? use the stock relation and slot */
3251  tgtslot = resultRelInfo->ri_newTupleSlot;
3252  tgtdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
3253  }
3254 
3255  action_state->mas_proj =
3256  ExecBuildProjectionInfo(action->targetList, econtext,
3257  tgtslot,
3258  &mtstate->ps,
3259  tgtdesc);
3260 
3261  mtstate->mt_merge_subcommands |= MERGE_INSERT;
3262  break;
3263  case CMD_UPDATE:
3264  action_state->mas_proj =
3265  ExecBuildUpdateProjection(action->targetList,
3266  true,
3267  action->updateColnos,
3268  relationDesc,
3269  econtext,
3270  resultRelInfo->ri_newTupleSlot,
3271  &mtstate->ps);
3272  mtstate->mt_merge_subcommands |= MERGE_UPDATE;
3273  break;
3274  case CMD_DELETE:
3275  mtstate->mt_merge_subcommands |= MERGE_DELETE;
3276  break;
3277  case CMD_NOTHING:
3278  break;
3279  default:
3280  elog(ERROR, "unknown operation");
3281  break;
3282  }
3283  }
3284  }
3285 }
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:514
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:210
#define MERGE_UPDATE
Definition: execnodes.h:1247
#define MERGE_INSERT
Definition: execnodes.h:1246
#define MERGE_DELETE
Definition: execnodes.h:1248
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
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:165
MergeAction * mas_action
Definition: execnodes.h:417
ProjectionInfo * mas_proj
Definition: execnodes.h:418
ExprState * mas_whenqual
Definition: execnodes.h:420
int mt_merge_subcommands
Definition: execnodes.h:1301
List * mergeActionLists
Definition: plannodes.h:252

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, sort-test::list, makeNode, MergeActionState::mas_action, MergeActionState::mas_proj, MergeActionState::mas_whenqual, MergeAction::matched, MERGE_DELETE, MERGE_INSERT, MERGE_UPDATE, ModifyTable::mergeActionLists, 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 3294 of file nodeModifyTable.c.

3296 {
3297  EState *estate = mtstate->ps.state;
3298 
3299  Assert(!resultRelInfo->ri_projectNewInfoValid);
3300 
3301  resultRelInfo->ri_oldTupleSlot =
3302  table_slot_create(resultRelInfo->ri_RelationDesc,
3303  &estate->es_tupleTable);
3304  resultRelInfo->ri_newTupleSlot =
3305  table_slot_create(resultRelInfo->ri_RelationDesc,
3306  &estate->es_tupleTable);
3307  resultRelInfo->ri_projectNewInfoValid = true;
3308 }

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

3893 {
3894  ModifyTableState *mtstate;
3895  Plan *subplan = outerPlan(node);
3896  CmdType operation = node->operation;
3897  int nrels = list_length(node->resultRelations);
3898  ResultRelInfo *resultRelInfo;
3899  List *arowmarks;
3900  ListCell *l;
3901  int i;
3902  Relation rel;
3903 
3904  /* check for unsupported flags */
3905  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
3906 
3907  /*
3908  * create state structure
3909  */
3910  mtstate = makeNode(ModifyTableState);
3911  mtstate->ps.plan = (Plan *) node;
3912  mtstate->ps.state = estate;
3913  mtstate->ps.ExecProcNode = ExecModifyTable;
3914 
3915  mtstate->operation = operation;
3916  mtstate->canSetTag = node->canSetTag;
3917  mtstate->mt_done = false;
3918 
3919  mtstate->mt_nrels = nrels;
3920  mtstate->resultRelInfo = (ResultRelInfo *)
3921  palloc(nrels * sizeof(ResultRelInfo));
3922 
3923  mtstate->mt_merge_inserted = 0;
3924  mtstate->mt_merge_updated = 0;
3925  mtstate->mt_merge_deleted = 0;
3926 
3927  /*----------
3928  * Resolve the target relation. This is the same as:
3929  *
3930  * - the relation for which we will fire FOR STATEMENT triggers,
3931  * - the relation into whose tuple format all captured transition tuples
3932  * must be converted, and
3933  * - the root partitioned table used for tuple routing.
3934  *
3935  * If it's a partitioned table, the root partition doesn't appear
3936  * elsewhere in the plan and its RT index is given explicitly in
3937  * node->rootRelation. Otherwise (i.e. table inheritance) the target
3938  * relation is the first relation in the node->resultRelations list.
3939  *----------
3940  */
3941  if (node->rootRelation > 0)
3942  {
3944  ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
3945  node->rootRelation);
3946  }
3947  else
3948  {
3949  mtstate->rootResultRelInfo = mtstate->resultRelInfo;
3950  ExecInitResultRelation(estate, mtstate->resultRelInfo,
3951  linitial_int(node->resultRelations));
3952  }
3953 
3954  /* set up epqstate with dummy subplan data for the moment */
3955  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
3956  mtstate->fireBSTriggers = true;
3957 
3958  /*
3959  * Build state for collecting transition tuples. This requires having a
3960  * valid trigger query context, so skip it in explain-only mode.
3961  */
3962  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
3963  ExecSetupTransitionCaptureState(mtstate, estate);
3964 
3965  /*
3966  * Open all the result relations and initialize the ResultRelInfo structs.
3967  * (But root relation was initialized above, if it's part of the array.)
3968  * We must do this before initializing the subplan, because direct-modify
3969  * FDWs expect their ResultRelInfos to be available.
3970  */
3971  resultRelInfo = mtstate->resultRelInfo;
3972  i = 0;
3973  foreach(l, node->resultRelations)
3974  {
3975  Index resultRelation = lfirst_int(l);
3976 
3977  if (resultRelInfo != mtstate->rootResultRelInfo)
3978  {
3979  ExecInitResultRelation(estate, resultRelInfo, resultRelation);
3980 
3981  /*
3982  * For child result relations, store the root result relation
3983  * pointer. We do so for the convenience of places that want to
3984  * look at the query's original target relation but don't have the
3985  * mtstate handy.
3986  */
3987  resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
3988  }
3989 
3990  /* Initialize the usesFdwDirectModify flag */
3991  resultRelInfo->ri_usesFdwDirectModify =
3993 
3994  /*
3995  * Verify result relation is a valid target for the current operation
3996  */
3997  CheckValidResultRel(resultRelInfo, operation);
3998 
3999  resultRelInfo++;
4000  i++;
4001  }
4002 
4003  /*
4004  * Now we may initialize the subplan.
4005  */
4006  outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
4007 
4008  /*
4009  * Do additional per-result-relation initialization.
4010  */
4011  for (i = 0; i < nrels; i++)
4012  {
4013  resultRelInfo = &mtstate->resultRelInfo[i];
4014 
4015  /* Let FDWs init themselves for foreign-table result rels */
4016  if (!resultRelInfo->ri_usesFdwDirectModify &&
4017  resultRelInfo->ri_FdwRoutine != NULL &&
4018  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
4019  {
4020  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
4021 
4022  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
4023  resultRelInfo,
4024  fdw_private,
4025  i,
4026  eflags);
4027  }
4028 
4029  /*
4030  * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
4031  * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
4032  * tables, the FDW might have created additional junk attr(s), but
4033  * those are no concern of ours.
4034  */
4035  if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4036  operation == CMD_MERGE)
4037  {
4038  char relkind;
4039 
4040  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4041  if (relkind == RELKIND_RELATION ||
4042  relkind == RELKIND_MATVIEW ||
4043  relkind == RELKIND_PARTITIONED_TABLE)
4044  {
4045  resultRelInfo->ri_RowIdAttNo =
4046  ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
4047  if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4048  elog(ERROR, "could not find junk ctid column");
4049  }
4050  else if (relkind == RELKIND_FOREIGN_TABLE)
4051  {
4052  /*
4053  * We don't support MERGE with foreign tables for now. (It's
4054  * problematic because the implementation uses CTID.)
4055  */
4056  Assert(operation != CMD_MERGE);
4057 
4058  /*
4059  * When there is a row-level trigger, there should be a
4060  * wholerow attribute. We also require it to be present in
4061  * UPDATE and MERGE, so we can get the values of unchanged
4062  * columns.
4063  */
4064  resultRelInfo->ri_RowIdAttNo =
4066  "wholerow");
4067  if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
4068  !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4069  elog(ERROR, "could not find junk wholerow column");
4070  }
4071  else
4072  {
4073  /* No support for MERGE */
4074  Assert(operation != CMD_MERGE);
4075  /* Other valid target relkinds must provide wholerow */
4076  resultRelInfo->ri_RowIdAttNo =
4078  "wholerow");
4079  if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4080  elog(ERROR, "could not find junk wholerow column");
4081  }
4082  }
4083  }
4084 
4085  /*
4086  * If this is an inherited update/delete/merge, there will be a junk
4087  * attribute named "tableoid" present in the subplan's targetlist. It
4088  * will be used to identify the result relation for a given tuple to be
4089  * updated/deleted/merged.
4090  */
4091  mtstate->mt_resultOidAttno =
4092  ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
4093  Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || nrels == 1);
4094  mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
4095  mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
4096 
4097  /* Get the root target relation */
4098  rel = mtstate->rootResultRelInfo->ri_RelationDesc;
4099 
4100  /*
4101  * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
4102  * or MERGE might need this too, but only if it actually moves tuples
4103  * between partitions; in that case setup is done by
4104  * ExecCrossPartitionUpdate.
4105  */
4106  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
4107  operation == CMD_INSERT)
4108  mtstate->mt_partition_tuple_routing =
4109  ExecSetupPartitionTupleRouting(estate, rel);
4110 
4111  /*
4112  * Initialize any WITH CHECK OPTION constraints if needed.
4113  */
4114  resultRelInfo = mtstate->resultRelInfo;
4115  foreach(l, node->withCheckOptionLists)
4116  {
4117  List *wcoList = (List *) lfirst(l);
4118  List *wcoExprs = NIL;
4119  ListCell *ll;
4120 
4121  foreach(ll, wcoList)
4122  {
4123  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
4124  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
4125  &mtstate->ps);
4126 
4127  wcoExprs = lappend(wcoExprs, wcoExpr);
4128  }
4129 
4130  resultRelInfo->ri_WithCheckOptions = wcoList;
4131  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4132  resultRelInfo++;
4133  }
4134 
4135  /*
4136  * Initialize RETURNING projections if needed.
4137  */
4138  if (node->returningLists)
4139  {
4140  TupleTableSlot *slot;
4141  ExprContext *econtext;
4142 
4143  /*
4144  * Initialize result tuple slot and assign its rowtype using the first
4145  * RETURNING list. We assume the rest will look the same.
4146  */
4147  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
4148 
4149  /* Set up a slot for the output of the RETURNING projection(s) */
4151  slot = mtstate->ps.ps_ResultTupleSlot;
4152 
4153  /* Need an econtext too */
4154  if (mtstate->ps.ps_ExprContext == NULL)
4155  ExecAssignExprContext(estate, &mtstate->ps);
4156  econtext = mtstate->ps.ps_ExprContext;
4157 
4158  /*
4159  * Build a projection for each result rel.
4160  */
4161  resultRelInfo = mtstate->resultRelInfo;
4162  foreach(l, node->returningLists)
4163  {
4164  List *rlist = (List *) lfirst(l);
4165 
4166  resultRelInfo->ri_returningList = rlist;
4167  resultRelInfo->ri_projectReturning =
4168  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
4169  resultRelInfo->ri_RelationDesc->rd_att);
4170  resultRelInfo++;
4171  }
4172  }
4173  else
4174  {
4175  /*
4176  * We still must construct a dummy result tuple type, because InitPlan
4177  * expects one (maybe should change that?).
4178  */
4179  mtstate->ps.plan->targetlist = NIL;
4180  ExecInitResultTypeTL(&mtstate->ps);
4181 
4182  mtstate->ps.ps_ExprContext = NULL;
4183  }
4184 
4185  /* Set the list of arbiter indexes if needed for ON CONFLICT */
4186  resultRelInfo = mtstate->resultRelInfo;
4187  if (node->onConflictAction != ONCONFLICT_NONE)
4188  {
4189  /* insert may only have one relation, inheritance is not expanded */
4190  Assert(nrels == 1);
4191  resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
4192  }
4193 
4194  /*
4195  * If needed, Initialize target list, projection and qual for ON CONFLICT
4196  * DO UPDATE.
4197  */
4198  if (node->onConflictAction == ONCONFLICT_UPDATE)
4199  {
4201  ExprContext *econtext;
4202  TupleDesc relationDesc;
4203 
4204  /* already exists if created by RETURNING processing above */
4205  if (mtstate->ps.ps_ExprContext == NULL)
4206  ExecAssignExprContext(estate, &mtstate->ps);
4207 
4208  econtext = mtstate->ps.ps_ExprContext;
4209  relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
4210 
4211  /* create state for DO UPDATE SET operation */
4212  resultRelInfo->ri_onConflict = onconfl;
4213 
4214  /* initialize slot for the existing tuple */
4215  onconfl->oc_Existing =
4216  table_slot_create(resultRelInfo->ri_RelationDesc,
4217  &mtstate->ps.state->es_tupleTable);
4218 
4219  /*
4220  * Create the tuple slot for the UPDATE SET projection. We want a slot
4221  * of the table's type here, because the slot will be used to insert
4222  * into the table, and for RETURNING processing - which may access
4223  * system attributes.
4224  */
4225  onconfl->oc_ProjSlot =
4226  table_slot_create(resultRelInfo->ri_RelationDesc,
4227  &mtstate->ps.state->es_tupleTable);
4228 
4229  /* build UPDATE SET projection state */
4230  onconfl->oc_ProjInfo =
4232  true,
4233  node->onConflictCols,
4234  relationDesc,
4235  econtext,
4236  onconfl->oc_ProjSlot,
4237  &mtstate->ps);
4238 
4239  /* initialize state to evaluate the WHERE clause, if any */
4240  if (node->onConflictWhere)
4241  {
4242  ExprState *qualexpr;
4243 
4244  qualexpr = ExecInitQual((List *) node->onConflictWhere,
4245  &mtstate->ps);
4246  onconfl->oc_WhereClause = qualexpr;
4247  }
4248  }
4249 
4250  /*
4251  * If we have any secondary relations in an UPDATE or DELETE, they need to
4252  * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
4253  * EvalPlanQual mechanism needs to be told about them. Locate the
4254  * relevant ExecRowMarks.
4255  */
4256  arowmarks = NIL;
4257  foreach(l, node->rowMarks)
4258  {
4260  ExecRowMark *erm;
4261  ExecAuxRowMark *aerm;
4262 
4263  /* ignore "parent" rowmarks; they are irrelevant at runtime */
4264  if (rc->isParent)
4265  continue;
4266 
4267  /* Find ExecRowMark and build ExecAuxRowMark */
4268  erm = ExecFindRowMark(estate, rc->rti, false);
4269  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
4270  arowmarks = lappend(arowmarks, aerm);
4271  }
4272 
4273  /* For a MERGE command, initialize its state */
4274  if (mtstate->operation == CMD_MERGE)
4275  ExecInitMerge(mtstate, estate);
4276 
4277  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
4278 
4279  /*
4280  * If there are a lot of result relations, use a hash table to speed the
4281  * lookups. If there are not a lot, a simple linear search is faster.
4282  *
4283  * It's not clear where the threshold is, but try 64 for starters. In a
4284  * debugging build, use a small threshold so that we get some test
4285  * coverage of both code paths.
4286  */
4287 #ifdef USE_ASSERT_CHECKING
4288 #define MT_NRELS_HASH 4
4289 #else
4290 #define MT_NRELS_HASH 64
4291 #endif
4292  if (nrels >= MT_NRELS_HASH)
4293  {
4294  HASHCTL hash_ctl;
4295 
4296  hash_ctl.keysize = sizeof(Oid);
4297  hash_ctl.entrysize = sizeof(MTTargetRelLookup);
4298  hash_ctl.hcxt = CurrentMemoryContext;
4299  mtstate->mt_resultOidHash =
4300  hash_create("ModifyTable target hash",
4301  nrels, &hash_ctl,
4303  for (i = 0; i < nrels; i++)
4304  {
4305  Oid hashkey;
4306  MTTargetRelLookup *mtlookup;
4307  bool found;
4308 
4309  resultRelInfo = &mtstate->resultRelInfo[i];
4310  hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
4311  mtlookup = (MTTargetRelLookup *)
4312  hash_search(mtstate->mt_resultOidHash, &hashkey,
4313  HASH_ENTER, &found);
4314  Assert(!found);
4315  mtlookup->relationIndex = i;
4316  }
4317  }
4318  else
4319  mtstate->mt_resultOidHash = NULL;
4320 
4321  /*
4322  * Determine if the FDW supports batch insert and determine the batch size
4323  * (a FDW may support batching, but it may be disabled for the
4324  * server/table).
4325  *
4326  * We only do this for INSERT, so that for UPDATE/DELETE the batch size
4327  * remains set to 0.
4328  */
4329  if (operation == CMD_INSERT)
4330  {
4331  /* insert may only have one relation, inheritance is not expanded */
4332  Assert(nrels == 1);
4333  resultRelInfo = mtstate->resultRelInfo;
4334  if (!resultRelInfo->ri_usesFdwDirectModify &&
4335  resultRelInfo->ri_FdwRoutine != NULL &&
4336  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
4337  resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
4338  {
4339  resultRelInfo->ri_BatchSize =
4340  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
4341  Assert(resultRelInfo->ri_BatchSize >= 1);
4342  }
4343  else
4344  resultRelInfo->ri_BatchSize = 1;
4345 
4346  /*
4347  * If doing batch insert, setup back-link so we can easily find the
4348  * mtstate again.
4349  */
4350  if (resultRelInfo->ri_BatchSize > 1)
4351  resultRelInfo->ri_ModifyTableState = mtstate;
4352  }
4353 
4354  /*
4355  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
4356  * to estate->es_auxmodifytables so that it will be run to completion by
4357  * ExecPostprocessPlan. (It'd actually work fine to add the primary
4358  * ModifyTable node too, but there's no need.) Note the use of lcons not
4359  * lappend: we need later-initialized ModifyTable nodes to be shut down
4360  * before earlier ones. This ensures that we don't throw away RETURNING
4361  * rows that need to be seen by a later CTE subplan.
4362  */
4363  if (!mtstate->canSetTag)
4364  estate->es_auxmodifytables = lcons(mtstate,
4365  estate->es_auxmodifytables);
4366 
4367  return mtstate;
4368 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
unsigned int Index
Definition: c.h:550
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:953
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:350
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2394
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2557
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2518
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2371
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:995
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1755
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1799
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:836
#define EXEC_FLAG_BACKWARD
Definition: executor.h:58
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:56
#define EXEC_FLAG_MARK
Definition: executor.h:59
@ 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:494
MemoryContext CurrentMemoryContext
Definition: mcxt.c:124
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:415
CmdType
Definition: nodes.h:263
@ CMD_MERGE
Definition: nodes.h:269
#define lfirst_node(type, lc)
Definition: pg_list.h:174
static int list_length(const List *l)
Definition: pg_list.h:150
#define lfirst_int(lc)
Definition: pg_list.h:171
#define linitial_int(l)
Definition: pg_list.h:177
#define linitial(l)
Definition: pg_list.h:176
static void * list_nth(const List *list, int n)
Definition: pg_list.h:297
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
List * es_auxmodifytables
Definition: execnodes.h:669
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
double mt_merge_deleted
Definition: execnodes.h:1306
double mt_merge_inserted
Definition: execnodes.h:1304
double mt_merge_updated
Definition: execnodes.h:1305
HTAB * mt_resultOidHash
Definition: execnodes.h:1283
List * arbiterIndexes
Definition: plannodes.h:246
List * onConflictCols
Definition: plannodes.h:248
CmdType operation
Definition: plannodes.h:232
int epqParam
Definition: plannodes.h:244
List * resultRelations
Definition: plannodes.h:237
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:242
List * onConflictSet
Definition: plannodes.h:247
bool canSetTag
Definition: plannodes.h:233
List * fdwPrivLists
Definition: plannodes.h:241
List * returningLists
Definition: plannodes.h:240
List * withCheckOptionLists
Definition: plannodes.h:239
Index rootRelation
Definition: plannodes.h:235
Node * onConflictWhere
Definition: plannodes.h:249
List * rowMarks
Definition: plannodes.h:243
OnConflictAction onConflictAction
Definition: plannodes.h:245
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:402
TupleTableSlot * oc_Existing
Definition: execnodes.h:401
ExprState * oc_WhereClause
Definition: execnodes.h:404
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:403
bool isParent
Definition: plannodes.h:1385
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1035
List * targetlist
Definition: plannodes.h:153
TupleDesc rd_att
Definition: rel.h:111
Form_pg_class rd_rel
Definition: rel.h:110
OnConflictSetState * ri_onConflict
Definition: execnodes.h:532
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:529
struct ModifyTableState * ri_ModifyTableState
Definition: execnodes.h:580
List * ri_WithCheckOptionExprs
Definition: execnodes.h:511
List * ri_returningList
Definition: execnodes.h:523
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:463
int ri_BatchSize
Definition: execnodes.h:503

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, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, 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_ModifyTableState, 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().

◆ ExecInitUpdateProjection()

static void ExecInitUpdateProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 556 of file nodeModifyTable.c.

558 {
559  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
560  Plan *subplan = outerPlan(node);
561  EState *estate = mtstate->ps.state;
562  TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
563  int whichrel;
564  List *updateColnos;
565 
566  /*
567  * Usually, mt_lastResultIndex matches the target rel. If it happens not
568  * to, we can get the index the hard way with an integer division.
569  */
570  whichrel = mtstate->mt_lastResultIndex;
571  if (resultRelInfo != mtstate->resultRelInfo + whichrel)
572  {
573  whichrel = resultRelInfo - mtstate->resultRelInfo;
574  Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
575  }
576 
577  updateColnos = (List *) list_nth(node->updateColnosLists, whichrel);
578 
579  /*
580  * For UPDATE, we use the old tuple to fill up missing values in the tuple
581  * produced by the subplan to get the new tuple. We need two slots, both
582  * matching the table's desired format.
583  */
584  resultRelInfo->ri_oldTupleSlot =
585  table_slot_create(resultRelInfo->ri_RelationDesc,
586  &estate->es_tupleTable);
587  resultRelInfo->ri_newTupleSlot =
588  table_slot_create(resultRelInfo->ri_RelationDesc,
589  &estate->es_tupleTable);
590 
591  /* need an expression context to do the projection */
592  if (mtstate->ps.ps_ExprContext == NULL)
593  ExecAssignExprContext(estate, &mtstate->ps);
594 
595  resultRelInfo->ri_projectNew =
596  ExecBuildUpdateProjection(subplan->targetlist,
597  false, /* subplan did the evaluation */
598  updateColnos,
599  relDesc,
600  mtstate->ps.ps_ExprContext,
601  resultRelInfo->ri_newTupleSlot,
602  &mtstate->ps);
603 
604  resultRelInfo->ri_projectNewInfoValid = true;
605 }
List * updateColnosLists
Definition: plannodes.h:238

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

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

References Assert(), CHECK_FOR_INTERRUPTS, CMD_INSERT, CMD_MERGE, CMD_UPDATE, MergeAction::commandType, TupleDescData::constr, CreateTupleDescCopy(), 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, lappend(), list_free(), list_member_ptr(), MakeSingleTupleTableSlot(), MergeActionState::mas_action, MemoryContextSwitchTo(), 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, ModifyTableContext::relaction, 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 3843 of file nodeModifyTable.c.

3845 {
3846  if (node->mt_resultOidHash)
3847  {
3848  /* Use the pre-built hash table to locate the rel */
3849  MTTargetRelLookup *mtlookup;
3850 
3851  mtlookup = (MTTargetRelLookup *)
3852  hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
3853  if (mtlookup)
3854  {
3855  if (update_cache)
3856  {
3857  node->mt_lastResultOid = resultoid;
3858  node->mt_lastResultIndex = mtlookup->relationIndex;
3859  }
3860  return node->resultRelInfo + mtlookup->relationIndex;
3861  }
3862  }
3863  else
3864  {
3865  /* With few target rels, just search the ResultRelInfo array */
3866  for (int ndx = 0; ndx < node->mt_nrels; ndx++)
3867  {
3868  ResultRelInfo *rInfo = node->resultRelInfo + ndx;
3869 
3870  if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
3871  {
3872  if (update_cache)
3873  {
3874  node->mt_lastResultOid = resultoid;
3875  node->mt_lastResultIndex = ndx;
3876  }
3877  return rInfo;
3878  }
3879  }
3880  }
3881 
3882  if (!missing_ok)
3883  elog(ERROR, "incorrect result relation OID %u", resultoid);
3884  return NULL;
3885 }
@ 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,
bool  canSetTag 
)
static

Definition at line 2633 of file nodeModifyTable.c.

2635 {
2636  bool matched;
2637 
2638  /*-----
2639  * If we are dealing with a WHEN MATCHED case (tupleid is valid), we
2640  * execute the first action for which the additional WHEN MATCHED AND
2641  * quals pass. If an action without quals is found, that action is
2642  * executed.
2643  *
2644  * Similarly, if we are dealing with WHEN NOT MATCHED case, we look at
2645  * the given WHEN NOT MATCHED actions in sequence until one passes.
2646  *
2647  * Things get interesting in case of concurrent update/delete of the
2648  * target tuple. Such concurrent update/delete is detected while we are
2649  * executing a WHEN MATCHED action.
2650  *
2651  * A concurrent update can:
2652  *
2653  * 1. modify the target tuple so that it no longer satisfies the
2654  * additional quals attached to the current WHEN MATCHED action
2655  *
2656  * In this case, we are still dealing with a WHEN MATCHED case.
2657  * We recheck the list of WHEN MATCHED actions from the start and
2658  * choose the first one that satisfies the new target tuple.
2659  *
2660  * 2. modify the target tuple so that the join quals no longer pass and
2661  * hence the source tuple no longer has a match.
2662  *
2663  * In this case, the source tuple no longer matches the target tuple,
2664  * so we now instead find a qualifying WHEN NOT MATCHED action to
2665  * execute.
2666  *
2667  * XXX Hmmm, what if the updated tuple would now match one that was
2668  * considered NOT MATCHED so far?
2669  *
2670  * A concurrent delete changes a WHEN MATCHED case to WHEN NOT MATCHED.
2671  *
2672  * ExecMergeMatched takes care of following the update chain and
2673  * re-finding the qualifying WHEN MATCHED action, as long as the updated
2674  * target tuple still satisfies the join quals, i.e., it remains a WHEN
2675  * MATCHED case. If the tuple gets deleted or the join quals fail, it
2676  * returns and we try ExecMergeNotMatched. Given that ExecMergeMatched
2677  * always make progress by following the update chain and we never switch
2678  * from ExecMergeNotMatched to ExecMergeMatched, there is no risk of a
2679  * livelock.
2680  */
2681  matched = tupleid != NULL;
2682  if (matched)
2683  matched = ExecMergeMatched(context, resultRelInfo, tupleid, canSetTag);
2684 
2685  /*
2686  * Either we were dealing with a NOT MATCHED tuple or ExecMergeMatched()
2687  * returned "false", indicating the previously MATCHED tuple no longer
2688  * matches.
2689  */
2690  if (!matched)
2691  ExecMergeNotMatched(context, resultRelInfo, canSetTag);
2692 
2693  /* No RETURNING support yet */
2694  return NULL;
2695 }
static void ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool canSetTag)
static bool ExecMergeMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, bool canSetTag)

References ExecMergeMatched(), and ExecMergeNotMatched().

Referenced by ExecModifyTable().

◆ ExecMergeMatched()

static bool ExecMergeMatched ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
bool  canSetTag 
)
static

Definition at line 2719 of file nodeModifyTable.c.

2721 {
2722  ModifyTableState *mtstate = context->mtstate;
2723  TupleTableSlot *newslot;
2724  EState *estate = context->estate;
2725  ExprContext *econtext = mtstate->ps.ps_ExprContext;
2726  bool isNull;
2727  EPQState *epqstate = &mtstate->mt_epqstate;
2728  ListCell *l;
2729 
2730  /*
2731  * If there are no WHEN MATCHED actions, we are done.
2732  */
2733  if (resultRelInfo->ri_matchedMergeAction == NIL)
2734  return true;
2735 
2736  /*
2737  * Make tuple and any needed join variables available to ExecQual and
2738  * ExecProject. The target's existing tuple is installed in the scantuple.
2739  * Again, this target relation's slot is required only in the case of a
2740  * MATCHED tuple and UPDATE/DELETE actions.
2741  */
2742  econtext->ecxt_scantuple = resultRelInfo->ri_oldTupleSlot;
2743  econtext->ecxt_innertuple = context->planSlot;
2744  econtext->ecxt_outertuple = NULL;
2745 
2746 lmerge_matched:
2747 
2748  /*
2749  * This routine is only invoked for matched rows, and we must have found
2750  * the tupleid of the target row in that case; fetch that tuple.
2751  *
2752  * We use SnapshotAny for this because we might get called again after
2753  * EvalPlanQual returns us a new tuple, which may not be visible to our
2754  * MVCC snapshot.
2755  */
2756 
2757  if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
2758  tupleid,
2759  SnapshotAny,
2760  resultRelInfo->ri_oldTupleSlot))
2761  elog(ERROR, "failed to fetch the target tuple");
2762 
2763  foreach(l, resultRelInfo->ri_matchedMergeAction)
2764  {
2765  MergeActionState *relaction = (MergeActionState *) lfirst(l);
2766  CmdType commandType = relaction->mas_action->commandType;
2767  List *recheckIndexes = NIL;
2768  TM_Result result;
2769  UpdateContext updateCxt = {0};
2770 
2771  /*
2772  * Test condition, if any.
2773  *
2774  * In the absence of any condition, we perform the action
2775  * unconditionally (no need to check separately since ExecQual() will
2776  * return true if there are no conditions to evaluate).
2777  */
2778  if (!ExecQual(relaction->mas_whenqual, econtext))
2779  continue;
2780 
2781  /*
2782  * Check if the existing target tuple meets the USING checks of
2783  * UPDATE/DELETE RLS policies. If those checks fail, we throw an
2784  * error.
2785  *
2786  * The WITH CHECK quals are applied in ExecUpdate() and hence we need
2787  * not do anything special to handle them.
2788  *
2789  * NOTE: We must do this after WHEN quals are evaluated, so that we
2790  * check policies only when they matter.
2791  */
2792  if (resultRelInfo->ri_WithCheckOptions)
2793  {
2794  ExecWithCheckOptions(commandType == CMD_UPDATE ?
2796  resultRelInfo,
2797  resultRelInfo->ri_oldTupleSlot,
2798  context->mtstate->ps.state);
2799  }
2800 
2801  /* Perform stated action */
2802  switch (commandType)
2803  {
2804  case CMD_UPDATE:
2805 
2806  /*
2807  * Project the output tuple, and use that to update the table.
2808  * We don't need to filter out junk attributes, because the
2809  * UPDATE action's targetlist doesn't have any.
2810  */
2811  newslot = ExecProject(relaction->mas_proj);
2812 
2813  context->relaction = relaction;
2815  context->cpUpdateRetrySlot = NULL;
2816 
2817  if (!ExecUpdatePrologue(context, resultRelInfo,
2818  tupleid, NULL, newslot))
2819  {
2820  result = TM_Ok;
2821  break;
2822  }
2823  ExecUpdatePrepareSlot(resultRelInfo, newslot, context->estate);
2824  result = ExecUpdateAct(context, resultRelInfo, tupleid, NULL,
2825  newslot, mtstate->canSetTag, &updateCxt);
2826  if (result == TM_Ok && updateCxt.updated)
2827  {
2828  ExecUpdateEpilogue(context, &updateCxt, resultRelInfo,
2829  tupleid, NULL, newslot, recheckIndexes);
2830  mtstate->mt_merge_updated += 1;
2831  }
2832 
2833  break;
2834 
2835  case CMD_DELETE:
2836  context->relaction = relaction;
2837  if (!ExecDeletePrologue(context, resultRelInfo, tupleid,
2838  NULL, NULL))
2839  {
2840  result = TM_Ok;
2841  break;
2842  }
2843  result = ExecDeleteAct(context, resultRelInfo, tupleid, false);
2844  if (result == TM_Ok)
2845  {
2846  ExecDeleteEpilogue(context, resultRelInfo, tupleid, NULL,
2847  false);
2848  mtstate->mt_merge_deleted += 1;
2849  }
2850  break;
2851 
2852  case CMD_NOTHING:
2853  /* Doing nothing is always OK */
2854  result = TM_Ok;
2855  break;
2856 
2857  default:
2858  elog(ERROR, "unknown action in MERGE WHEN MATCHED clause");
2859  }
2860 
2861  switch (result)
2862  {
2863  case TM_Ok:
2864  /* all good; perform final actions */
2865  if (canSetTag && commandType != CMD_NOTHING)
2866  (estate->es_processed)++;
2867 
2868  break;
2869 
2870  case TM_SelfModified:
2871 
2872  /*
2873  * The SQL standard disallows this for MERGE.
2874  */
2876  ereport(ERROR,
2877  (errcode(ERRCODE_CARDINALITY_VIOLATION),
2878  /* translator: %s is a SQL command name */
2879  errmsg("%s command cannot affect row a second time",
2880  "MERGE"),
2881  errhint("Ensure that not more than one source row matches any one target row.")));
2882  /* This shouldn't happen */
2883  elog(ERROR, "attempted to update or delete invisible tuple");
2884  break;
2885 
2886  case TM_Deleted:
2888  ereport(ERROR,
2890  errmsg("could not serialize access due to concurrent delete")));
2891 
2892  /*
2893  * If the tuple was already deleted, return to let caller
2894  * handle it under NOT MATCHED clauses.
2895  */
2896  return false;
2897 
2898  case TM_Updated:
2899  {
2900  Relation resultRelationDesc;
2901  TupleTableSlot *epqslot,
2902  *inputslot;
2903  LockTupleMode lockmode;
2904 
2905  /*
2906  * The target tuple was concurrently updated by some other
2907  * transaction.
2908  */
2909 
2910  /*
2911  * If cpUpdateRetrySlot is set, ExecCrossPartitionUpdate()
2912  * must have detected that the tuple was concurrently
2913  * updated, so we restart the search for an appropriate
2914  * WHEN MATCHED clause to process the updated tuple.
2915  *
2916  * In this case, ExecDelete() would already have performed
2917  * EvalPlanQual() on the latest version of the tuple,
2918  * which in turn would already have been loaded into
2919  * ri_oldTupleSlot, so no need to do either of those
2920  * things.
2921  *
2922  * XXX why do we not check the WHEN NOT MATCHED list in
2923  * this case?
2924  */
2925  if (!TupIsNull(context->cpUpdateRetrySlot))
2926  goto lmerge_matched;
2927 
2928  /*
2929  * Otherwise, we run the EvalPlanQual() with the new
2930  * version of the tuple. If EvalPlanQual() does not return
2931  * a tuple, then we switch to the NOT MATCHED list of
2932  * actions. If it does return a tuple and the join qual is
2933  * still satisfied, then we just need to recheck the
2934  * MATCHED actions, starting from the top, and execute the
2935  * first qualifying action.
2936  */
2937  resultRelationDesc = resultRelInfo->ri_RelationDesc;
2938  lockmode = ExecUpdateLockMode(estate, resultRelInfo);
2939 
2940  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
2941  resultRelInfo->ri_RangeTableIndex);
2942 
2943  result = table_tuple_lock(resultRelationDesc, tupleid,
2944  estate->es_snapshot,
2945  inputslot, estate->es_output_cid,
2946  lockmode, LockWaitBlock,
2948  &context->tmfd);
2949  switch (result)
2950  {
2951  case TM_Ok:
2952  epqslot = EvalPlanQual(epqstate,
2953  resultRelationDesc,
2954  resultRelInfo->ri_RangeTableIndex,
2955  inputslot);
2956 
2957  /*
2958  * If we got no tuple, or the tuple we get has a
2959  * NULL ctid, go back to caller: this one is not a
2960  * MATCHED tuple anymore, so they can retry with
2961  * NOT MATCHED actions.
2962  */
2963  if (TupIsNull(epqslot))
2964  return false;
2965 
2966  (void) ExecGetJunkAttribute(epqslot,
2967  resultRelInfo->ri_RowIdAttNo,
2968  &isNull);
2969  if (isNull)
2970  return false;
2971 
2972  /*
2973  * When a tuple was updated and migrated to
2974  * another partition concurrently, the current
2975  * MERGE implementation can't follow. There's
2976  * probably a better way to handle this case, but
2977  * it'd require recognizing the relation to which
2978  * the tuple moved, and setting our current
2979  * resultRelInfo to that.
2980  */
2982  ereport(ERROR,
2984  errmsg("tuple to be deleted was already moved to another partition due to concurrent update")));
2985 
2986  /*
2987  * A non-NULL ctid means that we are still dealing
2988  * with MATCHED case. Restart the loop so that we
2989  * apply all the MATCHED rules again, to ensure
2990  * that the first qualifying WHEN MATCHED action
2991  * is executed.
2992  *
2993  * Update tupleid to that of the new tuple, for
2994  * the refetch we do at the top.
2995  */
2996  ItemPointerCopy(&context->tmfd.ctid, tupleid);
2997  goto lmerge_matched;
2998 
2999  case TM_Deleted:
3000 
3001  /*
3002  * tuple already deleted; tell caller to run NOT
3003  * MATCHED actions
3004  */
3005  return false;
3006 
3007  case TM_SelfModified:
3008 
3009  /*
3010  * This can be reached when following an update
3011  * chain from a tuple updated by another session,
3012  * reaching a tuple that was already updated in
3013  * this transaction. If previously modified by
3014  * this command, ignore the redundant update,
3015  * otherwise error out.
3016  *
3017  * See also response to TM_SelfModified in
3018  * ExecUpdate().
3019  */
3020  if (context->tmfd.cmax != estate->es_output_cid)
3021  ereport(ERROR,
3022  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
3023  errmsg("tuple to be updated or deleted was already modified by an operation triggered by the current command"),
3024  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
3025  return false;
3026 
3027  default:
3028  /* see table_tuple_lock call in ExecDelete() */
3029  elog(ERROR, "unexpected table_tuple_lock status: %u",
3030  result);
3031  return false;
3032  }
3033  }
3034 
3035  case TM_Invisible:
3036  case TM_WouldBlock:
3037  case TM_BeingModified:
3038  /* these should not occur */
3039  elog(ERROR, "unexpected tuple operation result: %d", result);
3040  break;
3041  }
3042 
3043  /*
3044  * We've activated one of the WHEN clauses, so we don't search
3045  * further. This is required behaviour, not an optimization.
3046  */
3047  break;
3048  }
3049 
3050  /*
3051  * Successfully executed an action or no qualifying action was found.
3052  */
3053  return true;
3054 }
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2345
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:398
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: executor.h:178
static bool ItemPointerIndicatesMovedPartitions(const ItemPointerData *pointer)
Definition: itemptr.h:197
static void ItemPointerCopy(const ItemPointerData *fromPointer, ItemPointerData *toPointer)
Definition: itemptr.h:172
LockTupleMode
Definition: lockoptions.h:50
static void ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
static TupleTableSlot * mergeGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot, MergeActionState *relaction)
static void ExecUpdateEpilogue(ModifyTableContext *context, UpdateContext *updateCxt, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, List *recheckIndexes)
static TM_Result ExecUpdateAct(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag, UpdateContext *updateCxt)
static bool ExecUpdatePrologue(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot)
@ WCO_RLS_MERGE_UPDATE_CHECK
Definition: parsenodes.h:1245
@ WCO_RLS_MERGE_DELETE_CHECK
Definition: parsenodes.h:1246
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:249
List * ri_matchedMergeAction
Definition: execnodes.h:535
TransactionId xmax
Definition: tableam.h:127
ItemPointerData ctid
Definition: tableam.h:126
@ TM_BeingModified
Definition: tableam.h:99
@ TM_WouldBlock
Definition: tableam.h:102
@ TM_Invisible
Definition: tableam.h:80

References ModifyTableState::canSetTag, TM_FailureData::cmax, CMD_DELETE, CMD_NOTHING, CMD_UPDATE, MergeAction::commandType, ModifyTableContext::cpUpdateRetrySlot, 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(), ExecGetJunkAttribute(), ExecProject(), ExecQual(), ExecUpdateAct(), ExecUpdateEpilogue(), ExecUpdateLockMode(), ExecUpdatePrepareSlot(), ExecUpdatePrologue(), ExecWithCheckOptions(), ModifyTableContext::GetUpdateNewTuple, IsolationUsesXactSnapshot, ItemPointerCopy(), ItemPointerIndicatesMovedPartitions(), lfirst, LockWaitBlock, MergeActionState::mas_action, MergeActionState::mas_proj, MergeActionState::mas_whenqual, mergeGetUpdateNewTuple(), ModifyTableState::mt_epqstate, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_updated, ModifyTableContext::mtstate, NIL, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, ModifyTableContext::relaction, ResultRelInfo::ri_matchedMergeAction, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RowIdAttNo, 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(), TupIsNull, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, UpdateContext::updated, WCO_RLS_MERGE_DELETE_CHECK, WCO_RLS_MERGE_UPDATE_CHECK, and TM_FailureData::xmax.

Referenced by ExecMerge().

◆ ExecMergeNotMatched()

static void ExecMergeNotMatched ( ModifyTableContext context,
ResultRelInfo resultRelInfo,
bool  canSetTag 
)
static

Definition at line 3060 of file nodeModifyTable.c.

3062 {
3063  ModifyTableState *mtstate = context->mtstate;
3064  ExprContext *econtext = mtstate->ps.ps_ExprContext;
3065  List *actionStates = NIL;
3066  ListCell *l;
3067 
3068  /*
3069  * For INSERT actions, the root relation's merge action is OK since the
3070  * INSERT's targetlist and the WHEN conditions can only refer to the
3071  * source relation and hence it does not matter which result relation we
3072  * work with.
3073  *
3074  * XXX does this mean that we can avoid creating copies of actionStates on
3075  * partitioned tables, for not-matched actions?
3076  */
3077  actionStates = resultRelInfo->ri_notMatchedMergeAction;
3078 
3079  /*
3080  * Make source tuple available to ExecQual and ExecProject. We don't need
3081  * the target tuple, since the WHEN quals and targetlist can't refer to
3082  * the target columns.
3083  */
3084  econtext->ecxt_scantuple = NULL;
3085  econtext->ecxt_innertuple = context->planSlot;
3086  econtext->ecxt_outertuple = NULL;
3087 
3088  foreach(l, actionStates)
3089  {
3091  CmdType commandType = action->mas_action->commandType;
3092  TupleTableSlot *newslot;
3093 
3094  /*
3095  * Test condition, if any.
3096  *
3097  * In the absence of any condition, we perform the action
3098  * unconditionally (no need to check separately since ExecQual() will
3099  * return true if there are no conditions to evaluate).
3100  */
3101  if (!ExecQual(action->mas_whenqual, econtext))
3102  continue;
3103 
3104  /* Perform stated action */
3105  switch (commandType)
3106  {
3107  case CMD_INSERT:
3108 
3109  /*
3110  * Project the tuple. In case of a partitioned table, the
3111  * projection was already built to use the root's descriptor,
3112  * so we don't need to map the tuple here.
3113  */
3114  newslot = ExecProject(action->mas_proj);
3115  context->relaction = action;
3116 
3117  (void) ExecInsert(context, mtstate->rootResultRelInfo, newslot,
3118  canSetTag, NULL, NULL);
3119  mtstate->mt_merge_inserted += 1;
3120  break;
3121  case CMD_NOTHING:
3122  /* Do nothing */
3123  break;
3124  default:
3125  elog(ERROR, "unknown action in MERGE WHEN NOT MATCHED clause");
3126  }
3127 
3128  /*
3129  * We've activated one of the WHEN clauses, so we don't search
3130  * further. This is required behaviour, not an optimization.
3131  */
3132  break;
3133  }
3134 }
List * ri_notMatchedMergeAction
Definition: execnodes.h:536

References generate_unaccent_rules::action, CMD_INSERT, CMD_NOTHING, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog(), ERROR, ExecInsert(), ExecProject(), ExecQual(), lfirst, ModifyTableState::mt_merge_inserted, ModifyTableContext::mtstate, NIL, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, ModifyTableContext::relaction, ResultRelInfo::ri_notMatchedMergeAction, and ModifyTableState::rootResultRelInfo.

Referenced by ExecMerge().

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 3504 of file nodeModifyTable.c.

3505 {
3506  ModifyTableState *node = castNode(ModifyTableState, pstate);
3507  ModifyTableContext context;
3508  EState *estate = node->ps.state;
3509  CmdType operation = node->operation;
3510  ResultRelInfo *resultRelInfo;
3511  PlanState *subplanstate;
3512  TupleTableSlot *slot;
3513  TupleTableSlot *oldSlot;
3514  ItemPointerData tuple_ctid;
3515  HeapTupleData oldtupdata;
3516  HeapTuple oldtuple;
3517  ItemPointer tupleid;
3518 
3520 
3521  /*
3522  * This should NOT get called during EvalPlanQual; we should have passed a
3523  * subplan tree to EvalPlanQual, instead. Use a runtime test not just
3524  * Assert because this condition is easy to miss in testing. (Note:
3525  * although ModifyTable should not get executed within an EvalPlanQual
3526  * operation, we do have to allow it to be initialized and shut down in
3527  * case it is within a CTE subplan. Hence this test must be here, not in
3528  * ExecInitModifyTable.)
3529  */
3530  if (estate->es_epq_active != NULL)
3531  elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
3532 
3533  /*
3534  * If we've already completed processing, don't try to do more. We need
3535  * this test because ExecPostprocessPlan might call us an extra time, and
3536  * our subplan's nodes aren't necessarily robust against being called
3537  * extra times.
3538  */
3539  if (node->mt_done)
3540  return NULL;
3541 
3542  /*
3543  * On first call, fire BEFORE STATEMENT triggers before proceeding.
3544  */
3545  if (node->fireBSTriggers)
3546  {
3547  fireBSTriggers(node);
3548  node->fireBSTriggers = false;
3549  }
3550 
3551  /* Preload local variables */
3552  resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
3553  subplanstate = outerPlanState(node);
3554 
3555  /* Set global context */
3556  context.mtstate = node;
3557  context.epqstate = &node->mt_epqstate;
3558  context.estate = estate;
3559 
3560  /*
3561  * Fetch rows from subplan, and execute the required table modification
3562  * for each row.
3563  */
3564  for (;;)
3565  {
3566  /*
3567  * Reset the per-output-tuple exprcontext. This is needed because
3568  * triggers expect to use that context as workspace. It's a bit ugly
3569  * to do this below the top level of the plan, however. We might need
3570  * to rethink this later.
3571  */
3572  ResetPerTupleExprContext(estate);
3573 
3574  /*
3575  * Reset per-tuple memory context used for processing on conflict and
3576  * returning clauses, to free any expression evaluation storage
3577  * allocated in the previous cycle.
3578  */
3579  if (pstate->ps_ExprContext)
3581 
3582  context.planSlot = ExecProcNode(subplanstate);
3583 
3584  /* No more tuples to process? */
3585  if (TupIsNull(context.planSlot))
3586  break;
3587 
3588  /*
3589  * When there are multiple result relations, each tuple contains a
3590  * junk column that gives the OID of the rel from which it came.
3591  * Extract it and select the correct result relation.
3592  */
3594  {
3595  Datum datum;
3596  bool isNull;
3597  Oid resultoid;
3598 
3599  datum = ExecGetJunkAttribute(context.planSlot, node->mt_resultOidAttno,
3600  &isNull);
3601  if (isNull)
3602  {
3603  /*
3604  * For commands other than MERGE, any tuples having InvalidOid
3605  * for tableoid are errors. For MERGE, we may need to handle
3606  * them as WHEN NOT MATCHED clauses if any, so do that.
3607  *
3608  * Note that we use the node's toplevel resultRelInfo, not any
3609  * specific partition's.
3610  */
3611  if (operation == CMD_MERGE)
3612  {
3613  EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3614 
3615  ExecMerge(&context, node->resultRelInfo, NULL, node->canSetTag);
3616  continue; /* no RETURNING support yet */
3617  }
3618 
3619  elog(ERROR, "tableoid is NULL");
3620  }
3621  resultoid = DatumGetObjectId(datum);
3622 
3623  /* If it's not the same as last time, we need to locate the rel */
3624  if (resultoid != node->mt_lastResultOid)
3625  resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
3626  false, true);
3627  }
3628 
3629  /*
3630  * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
3631  * here is compute the RETURNING expressions.
3632  */
3633  if (resultRelInfo->ri_usesFdwDirectModify)
3634  {
3635  Assert(resultRelInfo->ri_projectReturning);
3636 
3637  /*
3638  * A scan slot containing the data that was actually inserted,
3639  * updated or deleted has already been made available to
3640  * ExecProcessReturning by IterateDirectModify, so no need to
3641  * provide it here.
3642  */
3643  slot = ExecProcessReturning(resultRelInfo, NULL, context.planSlot);
3644 
3645  return slot;
3646  }
3647 
3648  EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3649  slot = context.planSlot;
3650 
3651  tupleid = NULL;
3652  oldtuple = NULL;
3653 
3654  /*
3655  * For UPDATE/DELETE/MERGE, fetch the row identity info for the tuple
3656  * to be updated/deleted/merged. For a heap relation, that's a TID;
3657  * otherwise we may have a wholerow junk attr that carries the old
3658  * tuple in toto. Keep this in step with the part of
3659  * ExecInitModifyTable that sets up ri_RowIdAttNo.
3660  */
3661  if (operation == CMD_UPDATE || operation == CMD_DELETE ||
3662  operation == CMD_MERGE)
3663  {
3664  char relkind;
3665  Datum datum;
3666  bool isNull;
3667 
3668  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
3669  if (relkind == RELKIND_RELATION ||
3670  relkind == RELKIND_MATVIEW ||
3671  relkind == RELKIND_PARTITIONED_TABLE)
3672  {
3673  /* ri_RowIdAttNo refers to a ctid attribute */
3674  Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo));
3675  datum = ExecGetJunkAttribute(slot,
3676  resultRelInfo->ri_RowIdAttNo,
3677  &isNull);
3678 
3679  /*
3680  * For commands other than MERGE, any tuples having a null row
3681  * identifier are errors. For MERGE, we may need to handle
3682  * them as WHEN NOT MATCHED clauses if any, so do that.
3683  *
3684  * Note that we use the node's toplevel resultRelInfo, not any
3685  * specific partition's.
3686  */
3687  if (isNull)
3688  {
3689  if (operation == CMD_MERGE)
3690  {
3691  EvalPlanQualSetSlot(&node->mt_epqstate, context.planSlot);
3692 
3693  ExecMerge(&context, node->resultRelInfo, NULL, node->canSetTag);
3694  continue; /* no RETURNING support yet */
3695  }
3696 
3697  elog(ERROR, "ctid is NULL");
3698  }
3699 
3700  tupleid = (ItemPointer) DatumGetPointer(datum);
3701  tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
3702  tupleid = &tuple_ctid;
3703  }
3704 
3705  /*
3706  * Use the wholerow attribute, when available, to reconstruct the
3707  * old relation tuple. The old tuple serves one or both of two
3708  * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
3709  * provides values for any unchanged columns for the NEW tuple of
3710  * an UPDATE, because the subplan does not produce all the columns
3711  * of the target table.
3712  *
3713  * Note that the wholerow attribute does not carry system columns,
3714  * so foreign table triggers miss seeing those, except that we
3715  * know enough here to set t_tableOid. Quite separately from
3716  * this, the FDW may fetch its own junk attrs to identify the row.
3717  *
3718  * Other relevant relkinds, currently limited to views, always
3719  * have a wholerow attribute.
3720  */
3721  else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
3722  {
3723  datum = ExecGetJunkAttribute(slot,
3724  resultRelInfo->ri_RowIdAttNo,
3725  &isNull);
3726  /* shouldn't ever get a null result... */
3727  if (isNull)
3728  elog(ERROR, "wholerow is NULL");
3729 
3730  oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
3731  oldtupdata.t_len =
3733  ItemPointerSetInvalid(&(oldtupdata.t_self));
3734  /* Historically, view triggers see invalid t_tableOid. */
3735  oldtupdata.t_tableOid =
3736  (relkind == RELKIND_VIEW) ? InvalidOid :
3737  RelationGetRelid(resultRelInfo->ri_RelationDesc);
3738 
3739  oldtuple = &oldtupdata;
3740  }
3741  else
3742  {
3743  /* Only foreign tables are allowed to omit a row-ID attr */
3744  Assert(relkind == RELKIND_FOREIGN_TABLE);
3745  }
3746  }
3747 
3748  switch (operation)
3749  {
3750  case CMD_INSERT:
3751  /* Initialize projection info if first time for this table */
3752  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3753  ExecInitInsertProjection(node, resultRelInfo);
3754  slot = ExecGetInsertNewTuple(resultRelInfo, context.planSlot);
3755  slot = ExecInsert(&context, resultRelInfo, slot,
3756  node->canSetTag, NULL, NULL);
3757  break;
3758 
3759  case CMD_UPDATE:
3760  /* Initialize projection info if first time for this table */
3761  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
3762  ExecInitUpdateProjection(node, resultRelInfo);
3763 
3764  /*
3765  * Make the new tuple by combining plan's output tuple with
3766  * the old tuple being updated.
3767  */
3768  oldSlot = resultRelInfo->ri_oldTupleSlot;
3769  if (oldtuple != NULL)
3770  {
3771  /* Use the wholerow junk attr as the old tuple. */
3772  ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
3773  }
3774  else
3775  {
3776  /* Fetch the most recent version of old tuple. */
3777  Relation relation = resultRelInfo->ri_RelationDesc;
3778 
3779  if (!table_tuple_fetch_row_version(relation, tupleid,
3780  SnapshotAny,
3781  oldSlot))
3782  elog(ERROR, "failed to fetch tuple being updated");
3783  }
3784  slot = internalGetUpdateNewTuple(resultRelInfo, context.planSlot,
3785  oldSlot, NULL);
3787  context.relaction = NULL;
3788 
3789  /* Now apply the update. */
3790  slot = ExecUpdate(&context, resultRelInfo, tupleid, oldtuple,
3791  slot, node->canSetTag);
3792  break;
3793 
3794  case CMD_DELETE:
3795  slot = ExecDelete(&context, resultRelInfo, tupleid, oldtuple,
3796  true, false, node->canSetTag, NULL, NULL);
3797  break;
3798 
3799  case CMD_MERGE:
3800  slot = ExecMerge(&context, resultRelInfo, tupleid, node->canSetTag);
3801  break;
3802 
3803  default:
3804  elog(ERROR, "unknown operation");
3805  break;
3806  }
3807 
3808  /*
3809  * If we got a RETURNING result, return it to caller. We'll continue
3810  * the work on next call.
3811  */
3812  if (slot)
3813  return slot;
3814  }
3815 
3816  /*
3817  * Insert remaining tuples for batch insert.
3818  */
3819  if (estate->es_insert_pending_result_relations != NIL)
3820  ExecPendingInserts(estate);
3821 
3822  /*
3823  * We're done, but fire AFTER STATEMENT triggers before exiting.
3824  */
3825  fireASTriggers(node);
3826 
3827  node->mt_done = true;
3828 
3829  return NULL;
3830 }
#define ResetPerTupleExprContext(estate)
Definition: executor.h:544
#define ResetExprContext(econtext)
Definition: executor.h:529
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:229
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:254
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:446
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
ItemPointerData * ItemPointer
Definition: itemptr.h:49
static void ExecInitInsertProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static TupleTableSlot * ExecMerge(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, bool canSetTag)
static TupleTableSlot * ExecGetInsertNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot)
static TupleTableSlot * ExecUpdate(ModifyTableContext *context, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, bool canSetTag)
ResultRelInfo * ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid, bool missing_ok, bool update_cache)
static void fireBSTriggers(ModifyTableState *node)
static void fireASTriggers(ModifyTableState *node)
#define castNode(_type_, nodeptr)
Definition: nodes.h:186
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:590
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:660
struct EPQState * es_epq_active
Definition: execnodes.h:684
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(), ExecInitInsertProjection(), ExecInitUpdateProjection(), ExecInsert(), ExecLookupResultRelByOid(), ExecMerge(), ExecPendingInserts(), ExecProcessReturning(), ExecProcNode(), ExecUpdate(), fireASTriggers(), fireBSTriggers(), ModifyTableState::fireBSTriggers, ModifyTableContext::GetUpdateNewTuple, HeapTupleHeaderGetDatumLength, internalGetUpdateNewTuple(), InvalidOid, ItemPointerSetInvalid(), ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_resultOidAttno, ModifyTableContext::mtstate, NIL, ModifyTableState::operation, outerPlanState, ModifyTableContext::planSlot, ModifyTableState::ps, PlanState::ps_ExprContext, RelationData::rd_rel, ModifyTableContext::relaction, RelationGetRelid, ResetExprContext, ResetPerTupleExprContext, ModifyTableState::resultRelInfo, 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, and unlikely.

Referenced by ExecInitModifyTable().

◆ ExecOnConflictUpdate()

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

Definition at line 2421 of file nodeModifyTable.c.

2427 {
2428  ModifyTableState *mtstate = context->mtstate;
2429  ExprContext *econtext = mtstate->ps.ps_ExprContext;
2430  Relation relation = resultRelInfo->ri_RelationDesc;
2431  ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
2432  TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
2433  TM_FailureData tmfd;
2434  LockTupleMode lockmode;
2435  TM_Result test;
2436  Datum xminDatum;
2437  TransactionId xmin;
2438  bool isnull;
2439 
2440  /* Determine lock mode to use */
2441  lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
2442 
2443  /*
2444  * Lock tuple for update. Don't follow updates when tuple cannot be
2445  * locked without doing so. A row locking conflict here means our
2446  * previous conclusion that the tuple is conclusively committed is not
2447  * true anymore.
2448  */
2449  test = table_tuple_lock(relation, conflictTid,
2450  context->estate->es_snapshot,
2451  existing, context->estate->es_output_cid,
2452  lockmode, LockWaitBlock, 0,
2453  &tmfd);
2454  switch (test)
2455  {
2456  case TM_Ok:
2457  /* success! */
2458  break;
2459 
2460  case TM_Invisible:
2461 
2462  /*
2463  * This can occur when a just inserted tuple is updated again in
2464  * the same command. E.g. because multiple rows with the same
2465  * conflicting key values are inserted.
2466  *
2467  * This is somewhat similar to the ExecUpdate() TM_SelfModified
2468  * case. We do not want to proceed because it would lead to the
2469  * same row being updated a second time in some unspecified order,
2470  * and in contrast to plain UPDATEs there's no historical behavior
2471  * to break.
2472  *
2473  * It is the user's responsibility to prevent this situation from
2474  * occurring. These problems are why the SQL standard similarly
2475  * specifies that for SQL MERGE, an exception must be raised in
2476  * the event of an attempt to update the same row twice.
2477  */
2478  xminDatum = slot_getsysattr(existing,
2480  &isnull);
2481  Assert(!isnull);
2482  xmin = DatumGetTransactionId(xminDatum);
2483 
2485  ereport(ERROR,
2486  (errcode(ERRCODE_CARDINALITY_VIOLATION),
2487  /* translator: %s is a SQL command name */
2488  errmsg("%s command cannot affect row a second time",
2489  "ON CONFLICT DO UPDATE"),
2490  errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
2491 
2492  /* This shouldn't happen */
2493  elog(ERROR, "attempted to lock invisible tuple");
2494  break;
2495 
2496  case TM_SelfModified:
2497 
2498  /*
2499  * This state should never be reached. As a dirty snapshot is used
2500  * to find conflicting tuples, speculative insertion wouldn't have
2501  * seen this row to conflict with.
2502  */
2503  elog(ERROR, "unexpected self-updated tuple");
2504  break;
2505 
2506  case TM_Updated:
2508  ereport(ERROR,
2510  errmsg("could not serialize access due to concurrent update")));
2511 
2512  /*
2513  * As long as we don't support an UPDATE of INSERT ON CONFLICT for
2514  * a partitioned table we shouldn't reach to a case where tuple to
2515  * be lock is moved to another partition due to concurrent update
2516  * of the partition key.
2517  */
2519 
2520  /*
2521  * Tell caller to try again from the very start.
2522  *
2523  * It does not make sense to use the usual EvalPlanQual() style
2524  * loop here, as the new version of the row might not conflict
2525  * anymore, or the conflicting tuple has actually been deleted.
2526  */
2527  ExecClearTuple(existing);
2528  return false;
2529 
2530  case TM_Deleted:
2532  ereport(ERROR,
2534  errmsg("could not serialize access due to concurrent delete")));
2535 
2536  /* see TM_Updated case */
2538  ExecClearTuple(existing);
2539  return false;
2540 
2541  default:
2542  elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
2543  }
2544 
2545  /* Success, the tuple is locked. */
2546 
2547  /*
2548  * Verify that the tuple is visible to our MVCC snapshot if the current
2549  * isolation level mandates that.
2550  *
2551  * It's not sufficient to rely on the check within ExecUpdate() as e.g.
2552  * CONFLICT ... WHERE clause may prevent us from reaching that.
2553  *
2554  * This means we only ever continue when a new command in the current
2555  * transaction could see the row, even though in READ COMMITTED mode the
2556  * tuple will not be visible according to the current statement's
2557  * snapshot. This is in line with the way UPDATE deals with newer tuple
2558  * versions.
2559  */
2560  ExecCheckTupleVisible(context->estate, relation, existing);
2561 
2562  /*
2563  * Make tuple and any needed join variables available to ExecQual and
2564  * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
2565  * the target's existing tuple is installed in the scantuple. EXCLUDED
2566  * has been made to reference INNER_VAR in setrefs.c, but there is no
2567  * other redirection.
2568  */
2569  econtext->ecxt_scantuple = existing;
2570  econtext->ecxt_innertuple = excludedSlot;
2571  econtext->ecxt_outertuple = NULL;
2572 
2573  if (!ExecQual(onConflictSetWhere, econtext))
2574  {
2575  ExecClearTuple(existing); /* see return below */
2576  InstrCountFiltered1(&mtstate->ps, 1);
2577  return true; /* done with the tuple */
2578  }
2579 
2580  if (resultRelInfo->ri_WithCheckOptions != NIL)
2581  {
2582  /*
2583  * Check target's existing tuple against UPDATE-applicable USING
2584  * security barrier quals (if any), enforced here as RLS checks/WCOs.
2585  *
2586  * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
2587  * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
2588  * but that's almost the extent of its special handling for ON
2589  * CONFLICT DO UPDATE.
2590  *
2591  * The rewriter will also have associated UPDATE applicable straight
2592  * RLS checks/WCOs for the benefit of the ExecUpdate() call that
2593  * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
2594  * kinds, so there is no danger of spurious over-enforcement in the
2595  * INSERT or UPDATE path.
2596  */
2598  existing,
2599  mtstate->ps.state);
2600  }
2601 
2602  /* Project the new tuple version */
2603  ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
2604 
2605  /*
2606  * Note that it is possible that the target tuple has been modified in
2607  * this session, after the above table_tuple_lock. We choose to not error
2608  * out in that case, in line with ExecUpdate's treatment of similar cases.
2609  * This can happen if an UPDATE is triggered from within ExecQual(),
2610  * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
2611  * wCTE in the ON CONFLICT's SET.
2612  */
2613 
2614  /* Execute UPDATE with projection */
2615  *returning = ExecUpdate(context, resultRelInfo,
2616  conflictTid, NULL,
2617  resultRelInfo->ri_onConflict->oc_ProjSlot,
2618  canSetTag);
2619 
2620  /*
2621  * Clear out existing tuple, as there might not be another conflict among
2622  * the next input rows. Don't want to hold resources till the end of the
2623  * query.
2624  */
2625  ExecClearTuple(existing);
2626  return true;
2627 }
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1133
@ WCO_RLS_CONFLICT_CHECK
Definition: parsenodes.h:1244
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_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 1220 of file nodeModifyTable.c.

1221 {
1222  ListCell *lc;
1223 
1224  foreach(lc, estate->es_insert_pending_result_relations)
1225  {
1226  ResultRelInfo *resultRelInfo = (ResultRelInfo *) lfirst(lc);
1227  ModifyTableState *mtstate = resultRelInfo->ri_ModifyTableState;
1228 
1229  Assert(mtstate);
1230  ExecBatchInsert(mtstate, resultRelInfo,
1231  resultRelInfo->ri_Slots,
1232  resultRelInfo->ri_PlanSlots,
1233  resultRelInfo->ri_NumSlots,
1234  estate, mtstate->canSetTag);
1235  resultRelInfo->ri_NumSlots = 0;
1236  }
1237 
1240 }

References Assert(), ModifyTableState::canSetTag, EState::es_insert_pending_result_relations, ExecBatchInsert(), lfirst, list_free(), NIL, ResultRelInfo::ri_ModifyTableState, 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 3444 of file nodeModifyTable.c.

3450 {
3451  ResultRelInfo *partrel;
3452  TupleConversionMap *map;
3453 
3454  /*
3455  * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
3456  * not find a valid partition for the tuple in 'slot' then an error is
3457  * raised. An error may also be raised if the found partition is not a
3458  * valid target for INSERTs. This is required since a partitioned table
3459  * UPDATE to another partition becomes a DELETE+INSERT.
3460  */
3461  partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
3462 
3463  /*
3464  * If we're capturing transition tuples, we might need to convert from the
3465  * partition rowtype to root partitioned table's rowtype. But if there
3466  * are no BEFORE triggers on the partition that could change the tuple, we
3467  * can just remember the original unconverted tuple to avoid a needless
3468  * round trip conversion.
3469  */
3470  if (mtstate->mt_transition_capture != NULL)
3471  {
3472  bool has_before_insert_row_trig;
3473 
3474  has_before_insert_row_trig = (partrel->ri_TrigDesc &&
3476 
3478  !has_before_insert_row_trig ? slot : NULL;
3479  }
3480 
3481  /*
3482  * Convert the tuple, if necessary.
3483  */
3484  map = ExecGetRootToChildMap(partrel, estate);
3485  if (map != NULL)
3486  {
3487  TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
3488 
3489  slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
3490  }
3491 
3492  *partRelInfo = partrel;
3493  return slot;
3494 }
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleConversionMap * ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate)
Definition: execUtils.c:1262
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:568

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

275 {
276  ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
277  ExprContext *econtext = projectReturning->pi_exprContext;
278 
279  /* Make tuple and any needed join variables available to ExecProject */
280  if (tupleSlot)
281  econtext->ecxt_scantuple = tupleSlot;
282  econtext->ecxt_outertuple = planSlot;
283 
284  /*
285  * RETURNING expressions might reference the tableoid column, so
286  * reinitialize tts_tableOid before evaluating them.
287  */
288  econtext->ecxt_scantuple->tts_tableOid =
289  RelationGetRelid(resultRelInfo->ri_RelationDesc);
290 
291  /* Compute the RETURNING expressions */
292  return ExecProject(projectReturning);
293 }

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(), ExecModifyTable(), and ExecUpdate().

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 4444 of file nodeModifyTable.c.

4445 {
4446  /*
4447  * Currently, we don't need to support rescan on ModifyTable nodes. The
4448  * semantics of that would be a bit debatable anyway.
4449  */
4450  elog(ERROR, "ExecReScanModifyTable is not implemented");
4451 }

References elog(), and ERROR.

Referenced by ExecReScan().

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 3415 of file nodeModifyTable.c.

3416 {
3417  ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
3418  ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
3419 
3420  /* Check for transition tables on the directly targeted relation. */
3421  mtstate->mt_transition_capture =
3422  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3423  RelationGetRelid(targetRelInfo->ri_RelationDesc),
3424  mtstate->operation);
3425  if (plan->operation == CMD_INSERT &&
3427  mtstate->mt_oc_transition_capture =
3428  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
3429  RelationGetRelid(targetRelInfo->ri_RelationDesc),
3430  CMD_UPDATE);
3431 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1298
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4818

References CMD_INSERT, CMD_UPDATE, MakeTransitionCaptureState(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, ModifyTable::operation, PlanState::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 2182 of file nodeModifyTable.c.

2185 {
2186  EState *estate = context->estate;
2187  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
2188  UpdateContext updateCxt = {0};
2189  List *recheckIndexes = NIL;
2190  TM_Result result;
2191 
2192  /*
2193  * abort the operation if not running transactions
2194  */
2196  elog(ERROR, "cannot UPDATE during bootstrap");
2197 
2198  /*
2199  * Prepare for the update. This includes BEFORE ROW triggers, so we're
2200  * done if it says we are.
2201  */
2202  if (!ExecUpdatePrologue(context, resultRelInfo, tupleid, oldtuple, slot))
2203  return NULL;
2204 
2205  /* INSTEAD OF ROW UPDATE Triggers */
2206  if (resultRelInfo->ri_TrigDesc &&
2207  resultRelInfo->ri_TrigDesc->trig_update_instead_row)
2208  {
2209  if (!ExecIRUpdateTriggers(estate, resultRelInfo,
2210  oldtuple, slot))
2211  return NULL; /* "do nothing" */
2212  }
2213  else if (resultRelInfo->ri_FdwRoutine)
2214  {
2215  ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2216 
2217  /*
2218  * update in foreign table: let the FDW do it
2219  */
2220  slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
2221  resultRelInfo,
2222  slot,
2223  context->planSlot);
2224 
2225  if (slot == NULL) /* "do nothing" */
2226  return NULL;
2227 
2228  /*
2229  * AFTER ROW Triggers or RETURNING expressions might reference the
2230  * tableoid column, so (re-)initialize tts_tableOid before evaluating
2231  * them. (This covers the case where the FDW replaced the slot.)
2232  */
2233  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
2234  }
2235  else
2236  {
2237  /* Fill in the slot appropriately */
2238  ExecUpdatePrepareSlot(resultRelInfo, slot, estate);
2239 
2240 redo_act:
2241  result = ExecUpdateAct(context, resultRelInfo, tupleid, oldtuple, slot,
2242  canSetTag, &updateCxt);
2243 
2244  /*
2245  * If ExecUpdateAct reports that a cross-partition update was done,
2246  * then the RETURNING tuple (if any) has been projected and there's
2247  * nothing else for us to do.
2248  */
2249  if (updateCxt.crossPartUpdate)
2250  return context->cpUpdateReturningSlot;
2251 
2252  switch (result)
2253  {
2254  case TM_SelfModified:
2255 
2256  /*
2257  * The target tuple was already updated or deleted by the
2258  * current command, or by a later command in the current
2259  * transaction. The former case is possible in a join UPDATE
2260  * where multiple tuples join to the same target tuple. This
2261  * is pretty questionable, but Postgres has always allowed it:
2262  * we just execute the first update action and ignore
2263  * additional update attempts.
2264  *
2265  * The latter case arises if the tuple is modified by a
2266  * command in a BEFORE trigger, or perhaps by a command in a
2267  * volatile function used in the query. In such situations we
2268  * should not ignore the update, but it is equally unsafe to
2269  * proceed. We don't want to discard the original UPDATE
2270  * while keeping the triggered actions based on it; and we
2271  * have no principled way to merge this update with the
2272  * previous ones. So throwing an error is the only safe
2273  * course.
2274  *
2275  * If a trigger actually intends this type of interaction, it
2276  * can re-execute the UPDATE (assuming it can figure out how)
2277  * and then return NULL to cancel the outer update.
2278  */
2279  if (context->tmfd.cmax != estate->es_output_cid)
2280  ereport(ERROR,
2281  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2282  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2283  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2284 
2285  /* Else, already updated by self; nothing to do */
2286  return NULL;
2287 
2288  case TM_Ok:
2289  break;
2290 
2291  case TM_Updated:
2292  {
2293  TupleTableSlot *inputslot;
2294  TupleTableSlot *epqslot;
2295  TupleTableSlot *oldSlot;
2296 
2298  ereport(ERROR,
2300  errmsg("could not serialize access due to concurrent update")));
2301 
2302  /*
2303  * Already know that we're going to need to do EPQ, so
2304  * fetch tuple directly into the right slot.
2305  */
2306  inputslot = EvalPlanQualSlot(context->epqstate, resultRelationDesc,
2307  resultRelInfo->ri_RangeTableIndex);
2308 
2309  result = table_tuple_lock(resultRelationDesc, tupleid,
2310  estate->es_snapshot,
2311  inputslot, estate->es_output_cid,
2312  updateCxt.lockmode, LockWaitBlock,
2314  &context->tmfd);
2315 
2316  switch (result)
2317  {
2318  case TM_Ok:
2319  Assert(context->tmfd.traversed);
2320 
2321  epqslot = EvalPlanQual(context->epqstate,
2322  resultRelationDesc,
2323  resultRelInfo->ri_RangeTableIndex,
2324  inputslot);
2325  if (TupIsNull(epqslot))
2326  /* Tuple not passing quals anymore, exiting... */
2327  return NULL;
2328 
2329  /* Make sure ri_oldTupleSlot is initialized. */
2330  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2332  resultRelInfo);
2333 
2334  /* Fetch the most recent version of old tuple. */
2335  oldSlot = resultRelInfo->ri_oldTupleSlot;
2336  if (!table_tuple_fetch_row_version(resultRelationDesc,
2337  tupleid,
2338  SnapshotAny,
2339  oldSlot))
2340  elog(ERROR, "failed to fetch tuple being updated");
2341  slot = ExecGetUpdateNewTuple(resultRelInfo,
2342  epqslot, oldSlot);
2343  goto redo_act;
2344 
2345  case TM_Deleted:
2346  /* tuple already deleted; nothing to do */
2347  return NULL;
2348 
2349  case TM_SelfModified:
2350 
2351  /*
2352  * This can be reached when following an update
2353  * chain from a tuple updated by another session,
2354  * reaching a tuple that was already updated in
2355  * this transaction. If previously modified by
2356  * this command, ignore the redundant update,
2357  * otherwise error out.
2358  *
2359  * See also TM_SelfModified response to
2360  * table_tuple_update() above.
2361  */
2362  if (context->tmfd.cmax != estate->es_output_cid)
2363  ereport(ERROR,
2364  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
2365  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
2366  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
2367  return NULL;
2368 
2369  default:
2370  /* see table_tuple_lock call in ExecDelete() */
2371  elog(ERROR, "unexpected table_tuple_lock status: %u",
2372  result);
2373  return NULL;
2374  }
2375  }
2376 
2377  break;
2378 
2379  case TM_Deleted:
2381  ereport(ERROR,
2383  errmsg("could not serialize access due to concurrent delete")));
2384  /* tuple already deleted; nothing to do */
2385  return NULL;
2386 
2387  default:
2388  elog(ERROR, "unrecognized table_tuple_update status: %u",
2389  result);
2390  return NULL;
2391  }
2392  }
2393 
2394  if (canSetTag)
2395  (estate->es_processed)++;
2396 
2397  ExecUpdateEpilogue(context, &updateCxt, resultRelInfo, tupleid, oldtuple,
2398  slot, recheckIndexes);
2399 
2400  list_free(recheckIndexes);
2401 
2402  /* Process RETURNING if present */
2403  if (resultRelInfo->ri_projectReturning)
2404  return ExecProcessReturning(resultRelInfo, slot, context->planSlot);
2405 
2406  return NULL;
2407 }
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:402
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:235
bool trig_update_instead_row
Definition: reltrigger.h:63
LockTupleMode lockmode
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:3138

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(), IsBootstrapProcessingMode, IsolationUsesXactSnapshot, list_free(), UpdateContext::lockmode, LockWaitBlock, ModifyTableContext::mtstate, NIL, ModifyTableContext::planSlot, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, 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, and unlikely.

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

1900 {
1901  EState *estate = context->estate;
1902  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1903  bool partition_constraint_failed;
1904  TM_Result result;
1905 
1906  updateCxt->crossPartUpdate = false;
1907 
1908  /*
1909  * If we generate a new candidate tuple after EvalPlanQual testing, we
1910  * must loop back here and recheck any RLS policies and constraints. (We
1911  * don't need to redo triggers, however. If there are any BEFORE triggers
1912  * then trigger.c will have done table_tuple_lock to lock the correct
1913  * tuple, so there's no need to do them again.)
1914  */
1915 lreplace:
1916 
1917  /* ensure slot is independent, consider e.g. EPQ */
1918  ExecMaterializeSlot(slot);
1919 
1920  /*
1921  * If partition constraint fails, this row might get moved to another
1922  * partition, in which case we should check the RLS CHECK policy just
1923  * before inserting into the new partition, rather than doing it here.
1924  * This is because a trigger on that partition might again change the row.
1925  * So skip the WCO checks if the partition constraint fails.
1926  */
1927  partition_constraint_failed =
1928  resultRelationDesc->rd_rel->relispartition &&
1929  !ExecPartitionCheck(resultRelInfo, slot, estate, false);
1930 
1931  /* Check any RLS UPDATE WITH CHECK policies */
1932  if (!partition_constraint_failed &&
1933  resultRelInfo->ri_WithCheckOptions != NIL)
1934  {
1935  /*
1936  * ExecWithCheckOptions() will skip any WCOs which are not of the kind
1937  * we are looking for at this point.
1938  */
1940  resultRelInfo, slot, estate);
1941  }
1942 
1943  /*
1944  * If a partition check failed, try to move the row into the right
1945  * partition.
1946  */
1947  if (partition_constraint_failed)
1948  {
1949  TupleTableSlot *inserted_tuple;
1950  ResultRelInfo *insert_destrel = NULL;
1951 
1952  /*
1953  * ExecCrossPartitionUpdate will first DELETE the row from the
1954  * partition it's currently in and then insert it back into the root
1955  * table, which will re-route it to the correct partition. However,
1956  * if the tuple has been concurrently updated, a retry is needed.
1957  */
1958  if (ExecCrossPartitionUpdate(context, resultRelInfo,
1959  tupleid, oldtuple, slot,
1960  canSetTag, updateCxt,
1961  &inserted_tuple,
1962  &insert_destrel))
1963  {
1964  /* success! */
1965  updateCxt->updated = true;
1966  updateCxt->crossPartUpdate = true;
1967 
1968  /*
1969  * If the partitioned table being updated is referenced in foreign
1970  * keys, queue up trigger events to check that none of them were
1971  * violated. No special treatment is needed in
1972  * non-cross-partition update situations, because the leaf
1973  * partition's AR update triggers will take care of that. During
1974  * cross-partition updates implemented as delete on the source
1975  * partition followed by insert on the destination partition,
1976  * AR-UPDATE triggers of the root table (that is, the table
1977  * mentioned in the query) must be fired.
1978  *
1979  * NULL insert_destrel means that the move failed to occur, that
1980  * is, the update failed, so no need to anything in that case.
1981  */
1982  if (insert_destrel &&
1983  resultRelInfo->ri_TrigDesc &&
1984  resultRelInfo->ri_TrigDesc->trig_update_after_row)
1986  resultRelInfo,
1987  insert_destrel,
1988  tupleid, slot,
1989  inserted_tuple);
1990 
1991  return TM_Ok;
1992  }
1993 
1994  /*
1995  * No luck, a retry is needed. If running MERGE, we do not do so
1996  * here; instead let it handle that on its own rules.
1997  */
1998  if (context->relaction != NULL)
1999  return TM_Updated;
2000 
2001  /*
2002  * ExecCrossPartitionUpdate installed an updated version of the new
2003  * tuple in the retry slot; start over.
2004  */
2005  slot = context->cpUpdateRetrySlot;
2006  goto lreplace;
2007  }
2008 
2009  /*
2010  * Check the constraints of the tuple. We've already checked the
2011  * partition constraint above; however, we must still ensure the tuple
2012  * passes all other constraints, so we will call ExecConstraints() and
2013  * have it validate all remaining checks.
2014  */
2015  if (resultRelationDesc->rd_att->constr)
2016  ExecConstraints(resultRelInfo, slot, estate);
2017 
2018  /*
2019  * replace the heap tuple
2020  *
2021  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
2022  * the row to be updated is visible to that snapshot, and throw a
2023  * can't-serialize error if not. This is a special-case behavior needed
2024  * for referential integrity updates in transaction-snapshot mode
2025  * transactions.
2026  */
2027  result = table_tuple_update(resultRelationDesc, tupleid, slot,
2028  estate->es_output_cid,
2029  estate->es_snapshot,
2030  estate->es_crosscheck_snapshot,
2031  true /* wait for commit */ ,
2032  &context->tmfd, &updateCxt->lockmode,
2033  &updateCxt->updateIndexes);
2034  if (result == TM_Ok)
2035  updateCxt->updated = true;
2036 
2037  return result;
2038 }
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, TupleTableSlot **inserted_tuple, ResultRelInfo **insert_destrel)
static TM_Result table_tuple_update(Relation rel, ItemPointer otid, TupleTableSlot *slot, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, bool *update_indexes)
Definition: tableam.h:1506

References TupleDescData::constr, ModifyTableContext::cpUpdateRetrySlot, UpdateContext::crossPartUpdate, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_snapshot, ModifyTableContext::estate, ExecConstraints(), ExecCrossPartitionUpdate(), ExecCrossPartitionUpdateForeignKey(), ExecMaterializeSlot(), ExecPartitionCheck(), ExecWithCheckOptions(), UpdateContext::lockmode, NIL, RelationData::rd_att, RelationData::rd_rel, ModifyTableContext::relaction, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, table_tuple_update(), TM_Ok, TM_Updated, ModifyTableContext::tmfd, TriggerDesc::trig_update_after_row, UpdateContext::updated, 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,
List recheckIndexes 
)
static

Definition at line 2047 of file nodeModifyTable.c.

2051 {
2052  ModifyTableState *mtstate = context->mtstate;
2053 
2054  /* insert index entries for tuple if necessary */
2055  if (resultRelInfo->ri_NumIndices > 0 && updateCxt->updateIndexes)
2056  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
2057  slot, context->estate,
2058  true, false,
2059  NULL, NIL);
2060 
2061  /* AFTER ROW UPDATE Triggers */
2062  ExecARUpdateTriggers(context->estate, resultRelInfo,
2063  NULL, NULL,
2064  tupleid, oldtuple, slot,
2065  recheckIndexes,
2066  mtstate->operation == CMD_INSERT ?
2067  mtstate->mt_oc_transition_capture :
2068  mtstate->mt_transition_capture,
2069  false);
2070 
2071  /*
2072  * Check any WITH CHECK OPTION constraints from parent views. We are
2073  * required to do this after testing all constraints and uniqueness
2074  * violations per the SQL spec, so we do it after actually updating the
2075  * record in the heap and all indexes.
2076  *
2077  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
2078  * are looking for at this point.
2079  */
2080  if (resultRelInfo->ri_WithCheckOptions != NIL)
2081  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo,
2082  slot, context->estate);
2083 }

References CMD_INSERT, ModifyTableContext::estate, ExecARUpdateTriggers(), ExecInsertIndexTuples(), ExecWithCheckOptions(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ModifyTableContext::mtstate, NIL, ModifyTableState::operation, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_WithCheckOptions, 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 1859 of file nodeModifyTable.c.

1862 {
1863  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1864 
1865  /*
1866  * Constraints and GENERATED expressions might reference the tableoid
1867  * column, so (re-)initialize tts_tableOid before evaluating them.
1868  */
1869  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1870 
1871  /*
1872  * Compute stored generated columns
1873  */
1874  if (resultRelationDesc->rd_att->constr &&
1875  resultRelationDesc->rd_att->constr->has_generated_stored)
1876  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1877  CMD_UPDATE);
1878 }

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

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ ExecUpdatePrologue()

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

Definition at line 1822 of file nodeModifyTable.c.

1824 {
1825  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1826 
1827  ExecMaterializeSlot(slot);
1828 
1829  /*
1830  * Open the table's indexes, if we have not done so already, so that we
1831  * can add new index entries for the updated tuple.
1832  */
1833  if (resultRelationDesc->rd_rel->relhasindex &&
1834  resultRelInfo->ri_IndexRelationDescs == NULL)
1835  ExecOpenIndices(resultRelInfo, false);
1836 
1837  /* BEFORE ROW UPDATE triggers */
1838  if (resultRelInfo->ri_TrigDesc &&
1839  resultRelInfo->ri_TrigDesc->trig_update_before_row)
1840  {
1841  /* Flush any pending inserts, so rows are visible to the triggers */
1843  ExecPendingInserts(context->estate);
1844 
1845  return ExecBRUpdateTriggers(context->estate, context->epqstate,
1846  resultRelInfo, tupleid, oldtuple, slot,
1847  &context->tmfd);
1848  }
1849 
1850  return true;
1851 }
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, TM_FailureData *tmfd)
Definition: trigger.c:2936

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, ModifyTableContext::tmfd, and TriggerDesc::trig_update_before_row.

Referenced by ExecMergeMatched(), and ExecUpdate().

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 3370 of file nodeModifyTable.c.

3371 {
3372  ModifyTable *plan = (ModifyTable *) node->ps.plan;
3373  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3374 
3375  switch (node->operation)
3376  {
3377  case CMD_INSERT:
3378  if (plan->onConflictAction == ONCONFLICT_UPDATE)
3380  resultRelInfo,
3381  node->mt_oc_transition_capture);
3382  ExecASInsertTriggers(node->ps.state, resultRelInfo,
3383  node->mt_transition_capture);
3384  break;
3385  case CMD_UPDATE:
3386  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3387  node->mt_transition_capture);
3388  break;
3389  case CMD_DELETE:
3390  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3391  node->mt_transition_capture);
3392  break;
3393  case CMD_MERGE:
3394  if (node->mt_merge_subcommands & MERGE_DELETE)
3395  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
3396  node->mt_transition_capture);
3397  if (node->mt_merge_subcommands & MERGE_UPDATE)
3398  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
3399  node->mt_transition_capture);
3400  if (node->mt_merge_subcommands & MERGE_INSERT)
3401  ExecASInsertTriggers(node->ps.state, resultRelInfo,
3402  node->mt_transition_capture);
3403  break;
3404  default:
3405  elog(ERROR, "unknown operation");
3406  break;
3407  }
3408 }
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2918
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2668
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2450

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

Referenced by ExecModifyTable().

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 3333 of file nodeModifyTable.c.

3334 {
3335  ModifyTable *plan = (ModifyTable *) node->ps.plan;
3336  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
3337 
3338  switch (node->operation)
3339  {
3340  case CMD_INSERT:
3341  ExecBSInsertTriggers(node->ps.state, resultRelInfo);
3342  if (plan->onConflictAction == ONCONFLICT_UPDATE)
3344  resultRelInfo);
3345  break;
3346  case CMD_UPDATE:
3347  ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
3348  break;
3349  case CMD_DELETE:
3350  ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
3351  break;
3352  case CMD_MERGE:
3353  if (node->