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.

Functions

static bool ExecOnConflictUpdate (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *planSlot, TupleTableSlot *excludedSlot, EState *estate, bool canSetTag, TupleTableSlot **returning)
 
static TupleTableSlotExecPrepareTupleRouting (ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot)
 
static ResultRelInfogetTargetResultRelInfo (ModifyTableState *node)
 
static void ExecSetupChildParentMapForSubplan (ModifyTableState *mtstate)
 
static TupleConversionMaptupconv_map_for_subplan (ModifyTableState *node, int whichplan)
 
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 (EState *estate, TupleTableSlot *slot, CmdType cmdtype)
 
static TupleTableSlotExecInsert (ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
 
static TupleTableSlotExecDelete (ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool processReturning, bool canSetTag, bool changingPart, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
 
static TupleTableSlotExecUpdate (ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
 
static void fireBSTriggers (ModifyTableState *node)
 
static void fireASTriggers (ModifyTableState *node)
 
static void ExecSetupTransitionCaptureState (ModifyTableState *mtstate, EState *estate)
 
static TupleTableSlotExecModifyTable (PlanState *pstate)
 
ModifyTableStateExecInitModifyTable (ModifyTable *node, EState *estate, int eflags)
 
void ExecEndModifyTable (ModifyTableState *node)
 
void ExecReScanModifyTable (ModifyTableState *node)
 

Function Documentation

◆ ExecCheckPlanOutput()

static void ExecCheckPlanOutput ( Relation  resultRel,
List targetList 
)
static

Definition at line 92 of file nodeModifyTable.c.

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

Referenced by ExecInitModifyTable().

93 {
94  TupleDesc resultDesc = RelationGetDescr(resultRel);
95  int attno = 0;
96  ListCell *lc;
97 
98  foreach(lc, targetList)
99  {
100  TargetEntry *tle = (TargetEntry *) lfirst(lc);
101  Form_pg_attribute attr;
102 
103  if (tle->resjunk)
104  continue; /* ignore junk tlist items */
105 
106  if (attno >= resultDesc->natts)
107  ereport(ERROR,
108  (errcode(ERRCODE_DATATYPE_MISMATCH),
109  errmsg("table row type and query-specified row type do not match"),
110  errdetail("Query has too many columns.")));
111  attr = TupleDescAttr(resultDesc, attno);
112  attno++;
113 
114  if (!attr->attisdropped)
115  {
116  /* Normal case: demand type match */
117  if (exprType((Node *) tle->expr) != attr->atttypid)
118  ereport(ERROR,
119  (errcode(ERRCODE_DATATYPE_MISMATCH),
120  errmsg("table row type and query-specified row type do not match"),
121  errdetail("Table has type %s at ordinal position %d, but query expects %s.",
122  format_type_be(attr->atttypid),
123  attno,
124  format_type_be(exprType((Node *) tle->expr)))));
125  }
126  else
127  {
128  /*
129  * For a dropped column, we can't check atttypid (it's likely 0).
130  * In any case the planner has most likely inserted an INT4 null.
131  * What we insist on is just *some* NULL constant.
132  */
133  if (!IsA(tle->expr, Const) ||
134  !((Const *) tle->expr)->constisnull)
135  ereport(ERROR,
136  (errcode(ERRCODE_DATATYPE_MISMATCH),
137  errmsg("table row type and query-specified row type do not match"),
138  errdetail("Query provides a value for a dropped column at ordinal position %d.",
139  attno)));
140  }
141  }
142  if (attno != resultDesc->natts)
143  ereport(ERROR,
144  (errcode(ERRCODE_DATATYPE_MISMATCH),
145  errmsg("table row type and query-specified row type do not match"),
146  errdetail("Query has too few columns.")));
147 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:576
#define RelationGetDescr(relation)
Definition: rel.h:454
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
Definition: nodes.h:525
int errcode(int sqlerrcode)
Definition: elog.c:608
char * format_type_be(Oid type_oid)
Definition: format_type.c:326
bool resjunk
Definition: primnodes.h:1414
#define ERROR
Definition: elog.h:43
int errdetail(const char *fmt,...)
Definition: elog.c:955
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
#define ereport(elevel, rest)
Definition: elog.h:141
#define lfirst(lc)
Definition: pg_list.h:190
Expr * expr
Definition: primnodes.h:1407
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
int errmsg(const char *fmt,...)
Definition: elog.c:822

◆ ExecCheckTIDVisible()

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

Definition at line 228 of file nodeModifyTable.c.

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

Referenced by ExecInsert().

232 {
233  Relation rel = relinfo->ri_RelationDesc;
234 
235  /* Redundantly check isolation level */
237  return;
238 
239  if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
240  elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
241  ExecCheckTupleVisible(estate, rel, tempSlot);
242  ExecClearTuple(tempSlot);
243 }
Relation ri_RelationDesc
Definition: execnodes.h:411
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
#define ERROR
Definition: elog.h:43
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1053
#define SnapshotAny
Definition: snapmgr.h:69
#define elog(elevel,...)
Definition: elog.h:228

◆ ExecCheckTupleVisible()

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

Definition at line 194 of file nodeModifyTable.c.

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

Referenced by ExecCheckTIDVisible(), and ExecOnConflictUpdate().

197 {
199  return;
200 
201  if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
202  {
203  Datum xminDatum;
204  TransactionId xmin;
205  bool isnull;
206 
207  xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
208  Assert(!isnull);
209  xmin = DatumGetTransactionId(xminDatum);
210 
211  /*
212  * We should not raise a serialization failure if the conflict is
213  * against a tuple inserted by our own transaction, even if it's not
214  * visible to our snapshot. (This would happen, for example, if
215  * conflicting keys are proposed for insertion in a single command.)
216  */
218  ereport(ERROR,
219  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
220  errmsg("could not serialize access due to concurrent update")));
221  }
222 }
uint32 TransactionId
Definition: c.h:513
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:853
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
int errcode(int sqlerrcode)
Definition: elog.c:608
Snapshot es_snapshot
Definition: execnodes.h:506
#define ERROR
Definition: elog.h:43
#define ereport(elevel, rest)
Definition: elog.h:141
uintptr_t Datum
Definition: postgres.h:367
#define Assert(condition)
Definition: c.h:738
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition: tableam.h:1091
#define DatumGetTransactionId(X)
Definition: postgres.h:514
int errmsg(const char *fmt,...)
Definition: elog.c:822
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:402
#define MinTransactionIdAttributeNumber
Definition: sysattr.h:22

◆ ExecComputeStoredGenerated()

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

Definition at line 249 of file nodeModifyTable.c.

References Assert, bms_is_member(), build_column_default(), CMD_UPDATE, TupleDescData::constr, datumCopy(), ExprContext::ecxt_scantuple, elog, ERROR, EState::es_query_cxt, EState::es_result_relation_info, exec_rt_fetch(), ExecClearTuple(), ExecEvalExpr(), ExecMaterializeSlot(), ExecPrepareExpr(), ExecStoreVirtualTuple(), RangeTblEntry::extraUpdatedCols, FirstLowInvalidHeapAttributeNumber, GetPerTupleExprContext, GetPerTupleMemoryContext, TupleConstr::has_generated_stored, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc(), RelationGetDescr, RelationGetRelationName, ResultRelInfo::ri_GeneratedExprs, ResultRelInfo::ri_NumGeneratedNeeded, ResultRelInfo::ri_RangeTableIndex, 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 ExecUpdate().

250 {
251  ResultRelInfo *resultRelInfo = estate->es_result_relation_info;
252  Relation rel = resultRelInfo->ri_RelationDesc;
253  TupleDesc tupdesc = RelationGetDescr(rel);
254  int natts = tupdesc->natts;
255  MemoryContext oldContext;
256  Datum *values;
257  bool *nulls;
258 
259  Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
260 
261  /*
262  * If first time through for this result relation, build expression
263  * nodetrees for rel's stored generation expressions. Keep them in the
264  * per-query memory context so they'll survive throughout the query.
265  */
266  if (resultRelInfo->ri_GeneratedExprs == NULL)
267  {
268  oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
269 
270  resultRelInfo->ri_GeneratedExprs =
271  (ExprState **) palloc(natts * sizeof(ExprState *));
272  resultRelInfo->ri_NumGeneratedNeeded = 0;
273 
274  for (int i = 0; i < natts; i++)
275  {
276  if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
277  {
278  Expr *expr;
279 
280  /*
281  * If it's an update and the current column was not marked as
282  * being updated, then we can skip the computation. But if
283  * there is a BEFORE ROW UPDATE trigger, we cannot skip
284  * because the trigger might affect additional columns.
285  */
286  if (cmdtype == CMD_UPDATE &&
287  !(rel->trigdesc && rel->trigdesc->trig_update_before_row) &&
289  exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, estate)->extraUpdatedCols))
290  {
291  resultRelInfo->ri_GeneratedExprs[i] = NULL;
292  continue;
293  }
294 
295  expr = (Expr *) build_column_default(rel, i + 1);
296  if (expr == NULL)
297  elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
298  i + 1, RelationGetRelationName(rel));
299 
300  resultRelInfo->ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
301  resultRelInfo->ri_NumGeneratedNeeded++;
302  }
303  }
304 
305  MemoryContextSwitchTo(oldContext);
306  }
307 
308  /*
309  * If no generated columns have been affected by this change, then skip
310  * the rest.
311  */
312  if (resultRelInfo->ri_NumGeneratedNeeded == 0)
313  return;
314 
315  oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
316 
317  values = palloc(sizeof(*values) * natts);
318  nulls = palloc(sizeof(*nulls) * natts);
319 
320  slot_getallattrs(slot);
321  memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
322 
323  for (int i = 0; i < natts; i++)
324  {
325  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
326 
327  if (attr->attgenerated == ATTRIBUTE_GENERATED_STORED &&
328  resultRelInfo->ri_GeneratedExprs[i])
329  {
330  ExprContext *econtext;
331  Datum val;
332  bool isnull;
333 
334  econtext = GetPerTupleExprContext(estate);
335  econtext->ecxt_scantuple = slot;
336 
337  val = ExecEvalExpr(resultRelInfo->ri_GeneratedExprs[i], econtext, &isnull);
338 
339  values[i] = val;
340  nulls[i] = isnull;
341  }
342  else
343  {
344  if (!nulls[i])
345  values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
346  }
347  }
348 
349  ExecClearTuple(slot);
350  memcpy(slot->tts_values, values, sizeof(*values) * natts);
351  memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
352  ExecStoreVirtualTuple(slot);
353  ExecMaterializeSlot(slot);
354 
355  MemoryContextSwitchTo(oldContext);
356 }
Relation ri_RelationDesc
Definition: execnodes.h:411
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define RelationGetDescr(relation)
Definition: rel.h:454
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Datum * tts_values
Definition: tuptable.h:126
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:490
Index ri_RangeTableIndex
Definition: execnodes.h:408
#define GetPerTupleExprContext(estate)
Definition: executor.h:506
bool has_generated_stored
Definition: tupdesc.h:45
Bitmapset * extraUpdatedCols
Definition: parsenodes.h:1122
MemoryContext es_query_cxt
Definition: execnodes.h:553
#define ERROR
Definition: elog.h:43
TriggerDesc * trigdesc
Definition: rel.h:90
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:354
bool * tts_isnull
Definition: tuptable.h:128
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:290
TupleConstr * constr
Definition: tupdesc.h:85
ExprState ** ri_GeneratedExprs
Definition: execnodes.h:458
bool trig_update_before_row
Definition: reltrigger.h:60
#define RelationGetRelationName(relation)
Definition: rel.h:462
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:542
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:130
int ri_NumGeneratedNeeded
Definition: execnodes.h:461
Node * build_column_default(Relation rel, int attrno)
uintptr_t Datum
Definition: postgres.h:367
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
#define Assert(condition)
Definition: c.h:738
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:224
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:511
static Datum values[MAXATTR]
Definition: bootstrap.c:167
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:228
int i
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
long val
Definition: informix.c:664
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1522
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:525

◆ ExecDelete()

static TupleTableSlot* ExecDelete ( ModifyTableState mtstate,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot planSlot,
EPQState epqstate,
EState estate,
bool  processReturning,
bool  canSetTag,
bool  changingPart,
bool tupleDeleted,
TupleTableSlot **  epqreturnslot 
)
static

Definition at line 702 of file nodeModifyTable.c.

References Assert, TM_FailureData::cmax, CMD_UPDATE, elog, ereport, errcode(), errhint(), errmsg(), ERROR, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, EState::es_snapshot, EvalPlanQual(), EvalPlanQualBegin(), EvalPlanQualSlot(), ExecARDeleteTriggers(), ExecARUpdateTriggers(), ExecBRDeleteTriggers(), ExecClearTuple(), ExecForceStoreHeapTuple(), FdwRoutine::ExecForeignDelete, ExecGetReturningSlot(), ExecIRDeleteTriggers(), ExecMaterializeSlot(), ExecProcessReturning(), ExecStoreAllNullTuple(), IsolationUsesXactSnapshot, LockTupleExclusive, LockWaitBlock, ModifyTableState::mt_transition_capture, ModifyTableState::operation, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, SnapshotAny, table_tuple_delete(), table_tuple_fetch_row_version(), table_tuple_lock(), TransitionCaptureState::tcs_update_old_table, TM_Deleted, TM_Ok, TM_SelfModified, TM_Updated, TM_FailureData::traversed, TriggerDesc::trig_delete_before_row, TriggerDesc::trig_delete_instead_row, TTS_EMPTY, TupleTableSlot::tts_tableOid, TupIsNull, and TUPLE_LOCK_FLAG_FIND_LAST_VERSION.

Referenced by ExecModifyTable(), and ExecUpdate().

713 {
714  ResultRelInfo *resultRelInfo;
715  Relation resultRelationDesc;
716  TM_Result result;
717  TM_FailureData tmfd;
718  TupleTableSlot *slot = NULL;
719  TransitionCaptureState *ar_delete_trig_tcs;
720 
721  if (tupleDeleted)
722  *tupleDeleted = false;
723 
724  /*
725  * get information on the (current) result relation
726  */
727  resultRelInfo = estate->es_result_relation_info;
728  resultRelationDesc = resultRelInfo->ri_RelationDesc;
729 
730  /* BEFORE ROW DELETE Triggers */
731  if (resultRelInfo->ri_TrigDesc &&
732  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
733  {
734  bool dodelete;
735 
736  dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
737  tupleid, oldtuple, epqreturnslot);
738 
739  if (!dodelete) /* "do nothing" */
740  return NULL;
741  }
742 
743  /* INSTEAD OF ROW DELETE Triggers */
744  if (resultRelInfo->ri_TrigDesc &&
745  resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
746  {
747  bool dodelete;
748 
749  Assert(oldtuple != NULL);
750  dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
751 
752  if (!dodelete) /* "do nothing" */
753  return NULL;
754  }
755  else if (resultRelInfo->ri_FdwRoutine)
756  {
757  /*
758  * delete from foreign table: let the FDW do it
759  *
760  * We offer the returning slot as a place to store RETURNING data,
761  * although the FDW can return some other slot if it wants.
762  */
763  slot = ExecGetReturningSlot(estate, resultRelInfo);
764  slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
765  resultRelInfo,
766  slot,
767  planSlot);
768 
769  if (slot == NULL) /* "do nothing" */
770  return NULL;
771 
772  /*
773  * RETURNING expressions might reference the tableoid column, so
774  * (re)initialize tts_tableOid before evaluating them.
775  */
776  if (TTS_EMPTY(slot))
777  ExecStoreAllNullTuple(slot);
778 
779  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
780  }
781  else
782  {
783  /*
784  * delete the tuple
785  *
786  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
787  * that the row to be deleted is visible to that snapshot, and throw a
788  * can't-serialize error if not. This is a special-case behavior
789  * needed for referential integrity updates in transaction-snapshot
790  * mode transactions.
791  */
792 ldelete:;
793  result = table_tuple_delete(resultRelationDesc, tupleid,
794  estate->es_output_cid,
795  estate->es_snapshot,
796  estate->es_crosscheck_snapshot,
797  true /* wait for commit */ ,
798  &tmfd,
799  changingPart);
800 
801  switch (result)
802  {
803  case TM_SelfModified:
804 
805  /*
806  * The target tuple was already updated or deleted by the
807  * current command, or by a later command in the current
808  * transaction. The former case is possible in a join DELETE
809  * where multiple tuples join to the same target tuple. This
810  * is somewhat questionable, but Postgres has always allowed
811  * it: we just ignore additional deletion attempts.
812  *
813  * The latter case arises if the tuple is modified by a
814  * command in a BEFORE trigger, or perhaps by a command in a
815  * volatile function used in the query. In such situations we
816  * should not ignore the deletion, but it is equally unsafe to
817  * proceed. We don't want to discard the original DELETE
818  * while keeping the triggered actions based on its deletion;
819  * and it would be no better to allow the original DELETE
820  * while discarding updates that it triggered. The row update
821  * carries some information that might be important according
822  * to business rules; so throwing an error is the only safe
823  * course.
824  *
825  * If a trigger actually intends this type of interaction, it
826  * can re-execute the DELETE and then return NULL to cancel
827  * the outer delete.
828  */
829  if (tmfd.cmax != estate->es_output_cid)
830  ereport(ERROR,
831  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
832  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
833  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
834 
835  /* Else, already deleted by self; nothing to do */
836  return NULL;
837 
838  case TM_Ok:
839  break;
840 
841  case TM_Updated:
842  {
843  TupleTableSlot *inputslot;
844  TupleTableSlot *epqslot;
845 
847  ereport(ERROR,
848  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
849  errmsg("could not serialize access due to concurrent update")));
850 
851  /*
852  * Already know that we're going to need to do EPQ, so
853  * fetch tuple directly into the right slot.
854  */
855  EvalPlanQualBegin(epqstate);
856  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
857  resultRelInfo->ri_RangeTableIndex);
858 
859  result = table_tuple_lock(resultRelationDesc, tupleid,
860  estate->es_snapshot,
861  inputslot, estate->es_output_cid,
864  &tmfd);
865 
866  switch (result)
867  {
868  case TM_Ok:
869  Assert(tmfd.traversed);
870  epqslot = EvalPlanQual(epqstate,
871  resultRelationDesc,
872  resultRelInfo->ri_RangeTableIndex,
873  inputslot);
874  if (TupIsNull(epqslot))
875  /* Tuple not passing quals anymore, exiting... */
876  return NULL;
877 
878  /*
879  * If requested, skip delete and pass back the
880  * updated row.
881  */
882  if (epqreturnslot)
883  {
884  *epqreturnslot = epqslot;
885  return NULL;
886  }
887  else
888  goto ldelete;
889 
890  case TM_SelfModified:
891 
892  /*
893  * This can be reached when following an update
894  * chain from a tuple updated by another session,
895  * reaching a tuple that was already updated in
896  * this transaction. If previously updated by this
897  * command, ignore the delete, otherwise error
898  * out.
899  *
900  * See also TM_SelfModified response to
901  * table_tuple_delete() above.
902  */
903  if (tmfd.cmax != estate->es_output_cid)
904  ereport(ERROR,
905  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
906  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
907  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
908  return NULL;
909 
910  case TM_Deleted:
911  /* tuple already deleted; nothing to do */
912  return NULL;
913 
914  default:
915 
916  /*
917  * TM_Invisible should be impossible because we're
918  * waiting for updated row versions, and would
919  * already have errored out if the first version
920  * is invisible.
921  *
922  * TM_Updated should be impossible, because we're
923  * locking the latest version via
924  * TUPLE_LOCK_FLAG_FIND_LAST_VERSION.
925  */
926  elog(ERROR, "unexpected table_tuple_lock status: %u",
927  result);
928  return NULL;
929  }
930 
931  Assert(false);
932  break;
933  }
934 
935  case TM_Deleted:
937  ereport(ERROR,
938  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
939  errmsg("could not serialize access due to concurrent delete")));
940  /* tuple already deleted; nothing to do */
941  return NULL;
942 
943  default:
944  elog(ERROR, "unrecognized table_tuple_delete status: %u",
945  result);
946  return NULL;
947  }
948 
949  /*
950  * Note: Normally one would think that we have to delete index tuples
951  * associated with the heap tuple now...
952  *
953  * ... but in POSTGRES, we have no need to do this because VACUUM will
954  * take care of it later. We can't delete index tuples immediately
955  * anyway, since the tuple is still visible to other transactions.
956  */
957  }
958 
959  if (canSetTag)
960  (estate->es_processed)++;
961 
962  /* Tell caller that the delete actually happened. */
963  if (tupleDeleted)
964  *tupleDeleted = true;
965 
966  /*
967  * If this delete is the result of a partition key update that moved the
968  * tuple to a new partition, put this row into the transition OLD TABLE,
969  * if there is one. We need to do this separately for DELETE and INSERT
970  * because they happen on different tables.
971  */
972  ar_delete_trig_tcs = mtstate->mt_transition_capture;
973  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
975  {
976  ExecARUpdateTriggers(estate, resultRelInfo,
977  tupleid,
978  oldtuple,
979  NULL,
980  NULL,
981  mtstate->mt_transition_capture);
982 
983  /*
984  * We've already captured the NEW TABLE row, so make sure any AR
985  * DELETE trigger fired below doesn't capture it again.
986  */
987  ar_delete_trig_tcs = NULL;
988  }
989 
990  /* AFTER ROW DELETE Triggers */
991  ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
992  ar_delete_trig_tcs);
993 
994  /* Process RETURNING if present and if requested */
995  if (processReturning && resultRelInfo->ri_projectReturning)
996  {
997  /*
998  * We have to put the target tuple into a slot, which means first we
999  * gotta fetch it. We can use the trigger tuple slot.
1000  */
1001  TupleTableSlot *rslot;
1002 
1003  if (resultRelInfo->ri_FdwRoutine)
1004  {
1005  /* FDW must have provided a slot containing the deleted row */
1006  Assert(!TupIsNull(slot));
1007  }
1008  else
1009  {
1010  slot = ExecGetReturningSlot(estate, resultRelInfo);
1011  if (oldtuple != NULL)
1012  {
1013  ExecForceStoreHeapTuple(oldtuple, slot, false);
1014  }
1015  else
1016  {
1017  if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
1018  SnapshotAny, slot))
1019  elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
1020  }
1021  }
1022 
1023  rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
1024 
1025  /*
1026  * Before releasing the target tuple again, make sure rslot has a
1027  * local copy of any pass-by-reference values.
1028  */
1029  ExecMaterializeSlot(rslot);
1030 
1031  ExecClearTuple(slot);
1032 
1033  return rslot;
1034  }
1035 
1036  return NULL;
1037 }
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:213
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:411
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition: trigger.c:2870
int errhint(const char *fmt,...)
Definition: elog.c:1069
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1148
CommandId es_output_cid
Definition: execnodes.h:520
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1546
#define TTS_EMPTY(slot)
Definition: tuptable.h:97
CommandId cmax
Definition: tableam.h:126
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:507
int errcode(int sqlerrcode)
Definition: elog.c:608
CmdType operation
Definition: execnodes.h:1164
Snapshot es_snapshot
Definition: execnodes.h:506
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1439
Index ri_RangeTableIndex
Definition: execnodes.h:408
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot)
Definition: trigger.c:2749
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
Definition: trigger.c:2840
#define ERROR
Definition: elog.h:43
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:1333
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition: execMain.c:2540
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1189
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2431
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:470
#define TupIsNull(slot)
Definition: tuptable.h:292
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:440
#define ereport(elevel, rest)
Definition: elog.h:141
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1053
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:3127
TM_Result
Definition: tableam.h:69
bool trig_delete_instead_row
Definition: reltrigger.h:67
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:1244
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
#define Assert(condition)
Definition: c.h:738
Definition: tableam.h:75
uint64 es_processed
Definition: execnodes.h:557
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:140
#define SnapshotAny
Definition: snapmgr.h:69
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
bool traversed
Definition: tableam.h:127
#define RelationGetRelid(relation)
Definition: rel.h:428
void EvalPlanQualBegin(EPQState *epqstate)
Definition: execMain.c:2693
bool trig_delete_before_row
Definition: reltrigger.h:65
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:525

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 2754 of file nodeModifyTable.c.

References FdwRoutine::EndForeignModify, EvalPlanQualEnd(), ExecCleanupTupleRouting(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecEndNode(), ExecFreeExprContext(), i, ModifyTableState::mt_epqstate, ModifyTableState::mt_nplans, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_plans, ModifyTableState::mt_root_tuple_slot, ModifyTableState::ps, PlanState::ps_ResultTupleSlot, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_usesFdwDirectModify, and PlanState::state.

Referenced by ExecEndNode().

2755 {
2756  int i;
2757 
2758  /*
2759  * Allow any FDWs to shut down
2760  */
2761  for (i = 0; i < node->mt_nplans; i++)
2762  {
2763  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
2764 
2765  if (!resultRelInfo->ri_usesFdwDirectModify &&
2766  resultRelInfo->ri_FdwRoutine != NULL &&
2767  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
2768  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
2769  resultRelInfo);
2770  }
2771 
2772  /*
2773  * Close all the partitioned tables, leaf partitions, and their indices
2774  * and release the slot used for tuple routing, if set.
2775  */
2776  if (node->mt_partition_tuple_routing)
2777  {
2779 
2780  if (node->mt_root_tuple_slot)
2782  }
2783 
2784  /*
2785  * Free the exprcontext
2786  */
2787  ExecFreeExprContext(&node->ps);
2788 
2789  /*
2790  * clean out the tuple table
2791  */
2792  if (node->ps.ps_ResultTupleSlot)
2794 
2795  /*
2796  * Terminate EPQ execution if active
2797  */
2798  EvalPlanQualEnd(&node->mt_epqstate);
2799 
2800  /*
2801  * shut down subplans
2802  */
2803  for (i = 0; i < node->mt_nplans; i++)
2804  ExecEndNode(node->mt_plans[i]);
2805 }
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1186
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:537
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1172
EState * state
Definition: execnodes.h:945
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:614
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2931
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:981
PlanState ps
Definition: execnodes.h:1163
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
bool ri_usesFdwDirectModify
Definition: execnodes.h:446
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
EPQState mt_epqstate
Definition: execnodes.h:1176
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:440
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:214
PlanState ** mt_plans
Definition: execnodes.h:1167
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1183
int i

◆ ExecInitModifyTable()

ModifyTableState* ExecInitModifyTable ( ModifyTable node,
EState estate,
int  eflags 
)

Definition at line 2289 of file nodeModifyTable.c.

References ModifyTable::arbiterIndexes, Assert, AttributeNumberIsValid, FdwRoutine::BeginForeignModify, bms_is_member(), ModifyTable::canSetTag, ModifyTableState::canSetTag, CheckValidResultRel(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ModifyTable::epqParam, ERROR, EState::es_auxmodifytables, EState::es_result_relation_info, EState::es_result_relations, EState::es_root_result_relations, EState::es_tupleTable, EvalPlanQualInit(), EvalPlanQualSetPlan(), EXEC_FLAG_BACKWARD, EXEC_FLAG_EXPLAIN_ONLY, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecBuildAuxRowMark(), ExecBuildProjectionInfo(), ExecCheckPlanOutput(), ExecFindJunkAttribute(), ExecFindRowMark(), ExecGetResultType(), ExecInitExtraTupleSlot(), ExecInitJunkFilter(), ExecInitNode(), ExecInitQual(), ExecInitResultTupleSlotTL(), ExecInitResultTypeTL(), ExecModifyTable(), ExecOpenIndices(), PlanState::ExecProcNode, ExecSetupChildParentMapForSubplan(), ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), ExecTypeFromTL(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, getTargetResultRelInfo(), i, PlanRowMark::isParent, JunkFilter::jf_junkAttNo, lappend(), lcons(), lfirst, lfirst_node, linitial, list_length(), list_nth(), makeNode, ModifyTableState::mt_arowmarks, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_nplans, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_plans, ModifyTableState::mt_root_tuple_slot, ModifyTableState::mt_scans, ModifyTableState::mt_whichplan, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTable::operation, ModifyTableState::operation, palloc0(), ModifyTable::partColsUpdated, PlanState::plan, ModifyTable::plans, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationData::rd_att, RelationData::rd_rel, TargetEntry::resjunk, ModifyTable::resultRelIndex, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_IndexRelationDescs, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_usesFdwDirectModify, ResultRelInfo::ri_WithCheckOptionExprs, ResultRelInfo::ri_WithCheckOptions, ModifyTable::rootResultRelIndex, ModifyTableState::rootResultRelInfo, ModifyTable::rowMarks, PlanRowMark::rti, PlanState::state, table_slot_callbacks(), table_slot_create(), Plan::targetlist, TriggerDesc::trig_update_before_row, TTSOpsVirtual, and ModifyTable::withCheckOptionLists.

Referenced by ExecInitNode().

2290 {
2291  ModifyTableState *mtstate;
2292  CmdType operation = node->operation;
2293  int nplans = list_length(node->plans);
2294  ResultRelInfo *saved_resultRelInfo;
2295  ResultRelInfo *resultRelInfo;
2296  Plan *subplan;
2297  ListCell *l;
2298  int i;
2299  Relation rel;
2300  bool update_tuple_routing_needed = node->partColsUpdated;
2301 
2302  /* check for unsupported flags */
2303  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2304 
2305  /*
2306  * create state structure
2307  */
2308  mtstate = makeNode(ModifyTableState);
2309  mtstate->ps.plan = (Plan *) node;
2310  mtstate->ps.state = estate;
2311  mtstate->ps.ExecProcNode = ExecModifyTable;
2312 
2313  mtstate->operation = operation;
2314  mtstate->canSetTag = node->canSetTag;
2315  mtstate->mt_done = false;
2316 
2317  mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
2318  mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
2319  mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
2320 
2321  /* If modifying a partitioned table, initialize the root table info */
2322  if (node->rootResultRelIndex >= 0)
2323  mtstate->rootResultRelInfo = estate->es_root_result_relations +
2324  node->rootResultRelIndex;
2325 
2326  mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
2327  mtstate->mt_nplans = nplans;
2328 
2329  /* set up epqstate with dummy subplan data for the moment */
2330  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
2331  mtstate->fireBSTriggers = true;
2332 
2333  /*
2334  * call ExecInitNode on each of the plans to be executed and save the
2335  * results into the array "mt_plans". This is also a convenient place to
2336  * verify that the proposed target relations are valid and open their
2337  * indexes for insertion of new index entries. Note we *must* set
2338  * estate->es_result_relation_info correctly while we initialize each
2339  * sub-plan; external modules such as FDWs may depend on that (see
2340  * contrib/postgres_fdw/postgres_fdw.c: postgresBeginDirectModify() as one
2341  * example).
2342  */
2343  saved_resultRelInfo = estate->es_result_relation_info;
2344 
2345  resultRelInfo = mtstate->resultRelInfo;
2346  i = 0;
2347  foreach(l, node->plans)
2348  {
2349  subplan = (Plan *) lfirst(l);
2350 
2351  /* Initialize the usesFdwDirectModify flag */
2352  resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
2353  node->fdwDirectModifyPlans);
2354 
2355  /*
2356  * Verify result relation is a valid target for the current operation
2357  */
2358  CheckValidResultRel(resultRelInfo, operation);
2359 
2360  /*
2361  * If there are indices on the result relation, open them and save
2362  * descriptors in the result relation info, so that we can add new
2363  * index entries for the tuples we add/update. We need not do this
2364  * for a DELETE, however, since deletion doesn't affect indexes. Also,
2365  * inside an EvalPlanQual operation, the indexes might be open
2366  * already, since we share the resultrel state with the original
2367  * query.
2368  */
2369  if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
2370  operation != CMD_DELETE &&
2371  resultRelInfo->ri_IndexRelationDescs == NULL)
2372  ExecOpenIndices(resultRelInfo,
2374 
2375  /*
2376  * If this is an UPDATE and a BEFORE UPDATE trigger is present, the
2377  * trigger itself might modify the partition-key values. So arrange
2378  * for tuple routing.
2379  */
2380  if (resultRelInfo->ri_TrigDesc &&
2381  resultRelInfo->ri_TrigDesc->trig_update_before_row &&
2382  operation == CMD_UPDATE)
2383  update_tuple_routing_needed = true;
2384 
2385  /* Now init the plan for this result rel */
2386  estate->es_result_relation_info = resultRelInfo;
2387  mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
2388  mtstate->mt_scans[i] =
2389  ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[i]),
2390  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2391 
2392  /* Also let FDWs init themselves for foreign-table result rels */
2393  if (!resultRelInfo->ri_usesFdwDirectModify &&
2394  resultRelInfo->ri_FdwRoutine != NULL &&
2395  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
2396  {
2397  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
2398 
2399  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
2400  resultRelInfo,
2401  fdw_private,
2402  i,
2403  eflags);
2404  }
2405 
2406  resultRelInfo++;
2407  i++;
2408  }
2409 
2410  estate->es_result_relation_info = saved_resultRelInfo;
2411 
2412  /* Get the target relation */
2413  rel = (getTargetResultRelInfo(mtstate))->ri_RelationDesc;
2414 
2415  /*
2416  * If it's not a partitioned table after all, UPDATE tuple routing should
2417  * not be attempted.
2418  */
2419  if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2420  update_tuple_routing_needed = false;
2421 
2422  /*
2423  * Build state for tuple routing if it's an INSERT or if it's an UPDATE of
2424  * partition key.
2425  */
2426  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2427  (operation == CMD_INSERT || update_tuple_routing_needed))
2428  mtstate->mt_partition_tuple_routing =
2429  ExecSetupPartitionTupleRouting(estate, mtstate, rel);
2430 
2431  /*
2432  * Build state for collecting transition tuples. This requires having a
2433  * valid trigger query context, so skip it in explain-only mode.
2434  */
2435  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
2436  ExecSetupTransitionCaptureState(mtstate, estate);
2437 
2438  /*
2439  * Construct mapping from each of the per-subplan partition attnos to the
2440  * root attno. This is required when during update row movement the tuple
2441  * descriptor of a source partition does not match the root partitioned
2442  * table descriptor. In such a case we need to convert tuples to the root
2443  * tuple descriptor, because the search for destination partition starts
2444  * from the root. We'll also need a slot to store these converted tuples.
2445  * We can skip this setup if it's not a partition key update.
2446  */
2447  if (update_tuple_routing_needed)
2448  {
2450  mtstate->mt_root_tuple_slot = table_slot_create(rel, NULL);
2451  }
2452 
2453  /*
2454  * Initialize any WITH CHECK OPTION constraints if needed.
2455  */
2456  resultRelInfo = mtstate->resultRelInfo;
2457  i = 0;
2458  foreach(l, node->withCheckOptionLists)
2459  {
2460  List *wcoList = (List *) lfirst(l);
2461  List *wcoExprs = NIL;
2462  ListCell *ll;
2463 
2464  foreach(ll, wcoList)
2465  {
2466  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
2467  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
2468  &mtstate->ps);
2469 
2470  wcoExprs = lappend(wcoExprs, wcoExpr);
2471  }
2472 
2473  resultRelInfo->ri_WithCheckOptions = wcoList;
2474  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
2475  resultRelInfo++;
2476  i++;
2477  }
2478 
2479  /*
2480  * Initialize RETURNING projections if needed.
2481  */
2482  if (node->returningLists)
2483  {
2484  TupleTableSlot *slot;
2485  ExprContext *econtext;
2486 
2487  /*
2488  * Initialize result tuple slot and assign its rowtype using the first
2489  * RETURNING list. We assume the rest will look the same.
2490  */
2491  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
2492 
2493  /* Set up a slot for the output of the RETURNING projection(s) */
2495  slot = mtstate->ps.ps_ResultTupleSlot;
2496 
2497  /* Need an econtext too */
2498  if (mtstate->ps.ps_ExprContext == NULL)
2499  ExecAssignExprContext(estate, &mtstate->ps);
2500  econtext = mtstate->ps.ps_ExprContext;
2501 
2502  /*
2503  * Build a projection for each result rel.
2504  */
2505  resultRelInfo = mtstate->resultRelInfo;
2506  foreach(l, node->returningLists)
2507  {
2508  List *rlist = (List *) lfirst(l);
2509 
2510  resultRelInfo->ri_returningList = rlist;
2511  resultRelInfo->ri_projectReturning =
2512  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
2513  resultRelInfo->ri_RelationDesc->rd_att);
2514  resultRelInfo++;
2515  }
2516  }
2517  else
2518  {
2519  /*
2520  * We still must construct a dummy result tuple type, because InitPlan
2521  * expects one (maybe should change that?).
2522  */
2523  mtstate->ps.plan->targetlist = NIL;
2524  ExecInitResultTypeTL(&mtstate->ps);
2525 
2526  mtstate->ps.ps_ExprContext = NULL;
2527  }
2528 
2529  /* Set the list of arbiter indexes if needed for ON CONFLICT */
2530  resultRelInfo = mtstate->resultRelInfo;
2531  if (node->onConflictAction != ONCONFLICT_NONE)
2532  resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
2533 
2534  /*
2535  * If needed, Initialize target list, projection and qual for ON CONFLICT
2536  * DO UPDATE.
2537  */
2538  if (node->onConflictAction == ONCONFLICT_UPDATE)
2539  {
2540  ExprContext *econtext;
2541  TupleDesc relationDesc;
2542  TupleDesc tupDesc;
2543 
2544  /* insert may only have one plan, inheritance is not expanded */
2545  Assert(nplans == 1);
2546 
2547  /* already exists if created by RETURNING processing above */
2548  if (mtstate->ps.ps_ExprContext == NULL)
2549  ExecAssignExprContext(estate, &mtstate->ps);
2550 
2551  econtext = mtstate->ps.ps_ExprContext;
2552  relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
2553 
2554  /* create state for DO UPDATE SET operation */
2555  resultRelInfo->ri_onConflict = makeNode(OnConflictSetState);
2556 
2557  /* initialize slot for the existing tuple */
2558  resultRelInfo->ri_onConflict->oc_Existing =
2559  table_slot_create(resultRelInfo->ri_RelationDesc,
2560  &mtstate->ps.state->es_tupleTable);
2561 
2562  /*
2563  * Create the tuple slot for the UPDATE SET projection. We want a slot
2564  * of the table's type here, because the slot will be used to insert
2565  * into the table, and for RETURNING processing - which may access
2566  * system attributes.
2567  */
2568  tupDesc = ExecTypeFromTL((List *) node->onConflictSet);
2569  resultRelInfo->ri_onConflict->oc_ProjSlot =
2570  ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
2571  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2572 
2573  /* build UPDATE SET projection state */
2574  resultRelInfo->ri_onConflict->oc_ProjInfo =
2575  ExecBuildProjectionInfo(node->onConflictSet, econtext,
2576  resultRelInfo->ri_onConflict->oc_ProjSlot,
2577  &mtstate->ps,
2578  relationDesc);
2579 
2580  /* initialize state to evaluate the WHERE clause, if any */
2581  if (node->onConflictWhere)
2582  {
2583  ExprState *qualexpr;
2584 
2585  qualexpr = ExecInitQual((List *) node->onConflictWhere,
2586  &mtstate->ps);
2587  resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
2588  }
2589  }
2590 
2591  /*
2592  * If we have any secondary relations in an UPDATE or DELETE, they need to
2593  * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
2594  * EvalPlanQual mechanism needs to be told about them. Locate the
2595  * relevant ExecRowMarks.
2596  */
2597  foreach(l, node->rowMarks)
2598  {
2600  ExecRowMark *erm;
2601 
2602  /* ignore "parent" rowmarks; they are irrelevant at runtime */
2603  if (rc->isParent)
2604  continue;
2605 
2606  /* find ExecRowMark (same for all subplans) */
2607  erm = ExecFindRowMark(estate, rc->rti, false);
2608 
2609  /* build ExecAuxRowMark for each subplan */
2610  for (i = 0; i < nplans; i++)
2611  {
2612  ExecAuxRowMark *aerm;
2613 
2614  subplan = mtstate->mt_plans[i]->plan;
2615  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
2616  mtstate->mt_arowmarks[i] = lappend(mtstate->mt_arowmarks[i], aerm);
2617  }
2618  }
2619 
2620  /* select first subplan */
2621  mtstate->mt_whichplan = 0;
2622  subplan = (Plan *) linitial(node->plans);
2623  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan,
2624  mtstate->mt_arowmarks[0]);
2625 
2626  /*
2627  * Initialize the junk filter(s) if needed. INSERT queries need a filter
2628  * if there are any junk attrs in the tlist. UPDATE and DELETE always
2629  * need a filter, since there's always at least one junk attribute present
2630  * --- no need to look first. Typically, this will be a 'ctid' or
2631  * 'wholerow' attribute, but in the case of a foreign data wrapper it
2632  * might be a set of junk attributes sufficient to identify the remote
2633  * row.
2634  *
2635  * If there are multiple result relations, each one needs its own junk
2636  * filter. Note multiple rels are only possible for UPDATE/DELETE, so we
2637  * can't be fooled by some needing a filter and some not.
2638  *
2639  * This section of code is also a convenient place to verify that the
2640  * output of an INSERT or UPDATE matches the target table(s).
2641  */
2642  {
2643  bool junk_filter_needed = false;
2644 
2645  switch (operation)
2646  {
2647  case CMD_INSERT:
2648  foreach(l, subplan->targetlist)
2649  {
2650  TargetEntry *tle = (TargetEntry *) lfirst(l);
2651 
2652  if (tle->resjunk)
2653  {
2654  junk_filter_needed = true;
2655  break;
2656  }
2657  }
2658  break;
2659  case CMD_UPDATE:
2660  case CMD_DELETE:
2661  junk_filter_needed = true;
2662  break;
2663  default:
2664  elog(ERROR, "unknown operation");
2665  break;
2666  }
2667 
2668  if (junk_filter_needed)
2669  {
2670  resultRelInfo = mtstate->resultRelInfo;
2671  for (i = 0; i < nplans; i++)
2672  {
2673  JunkFilter *j;
2674  TupleTableSlot *junkresslot;
2675 
2676  subplan = mtstate->mt_plans[i]->plan;
2677  if (operation == CMD_INSERT || operation == CMD_UPDATE)
2678  ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
2679  subplan->targetlist);
2680 
2681  junkresslot =
2682  ExecInitExtraTupleSlot(estate, NULL,
2683  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2684  j = ExecInitJunkFilter(subplan->targetlist,
2685  junkresslot);
2686 
2687  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2688  {
2689  /* For UPDATE/DELETE, find the appropriate junk attr now */
2690  char relkind;
2691 
2692  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2693  if (relkind == RELKIND_RELATION ||
2694  relkind == RELKIND_MATVIEW ||
2695  relkind == RELKIND_PARTITIONED_TABLE)
2696  {
2697  j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
2699  elog(ERROR, "could not find junk ctid column");
2700  }
2701  else if (relkind == RELKIND_FOREIGN_TABLE)
2702  {
2703  /*
2704  * When there is a row-level trigger, there should be
2705  * a wholerow attribute.
2706  */
2707  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2708  }
2709  else
2710  {
2711  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2713  elog(ERROR, "could not find junk wholerow column");
2714  }
2715  }
2716 
2717  resultRelInfo->ri_junkFilter = j;
2718  resultRelInfo++;
2719  }
2720  }
2721  else
2722  {
2723  if (operation == CMD_INSERT)
2725  subplan->targetlist);
2726  }
2727  }
2728 
2729  /*
2730  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
2731  * to estate->es_auxmodifytables so that it will be run to completion by
2732  * ExecPostprocessPlan. (It'd actually work fine to add the primary
2733  * ModifyTable node too, but there's no need.) Note the use of lcons not
2734  * lappend: we need later-initialized ModifyTable nodes to be shut down
2735  * before earlier ones. This ensures that we don't throw away RETURNING
2736  * rows that need to be seen by a later CTE subplan.
2737  */
2738  if (!mtstate->canSetTag)
2739  estate->es_auxmodifytables = lcons(mtstate,
2740  estate->es_auxmodifytables);
2741 
2742  return mtstate;
2743 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:77
AttrNumber jf_junkAttNo
Definition: execnodes.h:371
#define NIL
Definition: pg_list.h:65
JunkFilter * ri_junkFilter
Definition: execnodes.h:464
List * arbiterIndexes
Definition: plannodes.h:237
Relation ri_RelationDesc
Definition: execnodes.h:411
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:233
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1801
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1186
List * withCheckOptionLists
Definition: plannodes.h:230
int resultRelIndex
Definition: plannodes.h:227
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1172
AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
Definition: execJunk.c:208
ExprContext * ps_ExprContext
Definition: execnodes.h:982
TupleTableSlot ** mt_scans
Definition: execnodes.h:1170
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:44
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
bool partColsUpdated
Definition: plannodes.h:225
bool canSetTag
Definition: plannodes.h:222
CmdType operation
Definition: execnodes.h:1164
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1173
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2523
EState * state
Definition: execnodes.h:945
Form_pg_class rd_rel
Definition: rel.h:84
List * plans
Definition: plannodes.h:229
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:207
List * onConflictSet
Definition: plannodes.h:238
int rootResultRelIndex
Definition: plannodes.h:228
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:151
List * ri_WithCheckOptionExprs
Definition: execnodes.h:452
TupleTableSlot * oc_Existing
Definition: execnodes.h:383
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:981
static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
List * rowMarks
Definition: plannodes.h:234
bool resjunk
Definition: primnodes.h:1414
#define linitial(l)
Definition: pg_list.h:195
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1163
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:385
static void * list_nth(const List *list, int n)
Definition: pg_list.h:277
bool ri_usesFdwDirectModify
Definition: execnodes.h:446
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:1076
#define EXEC_FLAG_BACKWARD
Definition: executor.h:58
#define lfirst_node(type, lc)
Definition: pg_list.h:193
ResultRelInfo * es_result_relations
Definition: execnodes.h:523
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1725
List * fdwPrivLists
Definition: plannodes.h:232
EPQState mt_epqstate
Definition: execnodes.h:1176
bool trig_update_before_row
Definition: reltrigger.h:60
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:470
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:440
ExprState * oc_WhereClause
Definition: execnodes.h:386
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, ModifyTableState *mtstate, Relation rel)
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
List * lappend(List *list, void *datum)
Definition: list.c:322
PlanState ** mt_plans
Definition: execnodes.h:1167
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2485
OnConflictSetState * ri_onConflict
Definition: execnodes.h:476
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
List * es_tupleTable
Definition: execnodes.h:555
void * palloc0(Size size)
Definition: mcxt.c:980
List * es_auxmodifytables
Definition: execnodes.h:567
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:949
List * ri_WithCheckOptions
Definition: execnodes.h:449
TupleDesc rd_att
Definition: rel.h:85
Plan * plan
Definition: execnodes.h:943
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:384
List * lcons(void *datum, List *list)
Definition: list.c:454
#define makeNode(_type_)
Definition: nodes.h:573
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define Assert(condition)
Definition: c.h:738
#define lfirst(lc)
Definition: pg_list.h:190
#define EXEC_FLAG_MARK
Definition: executor.h:59
OnConflictAction onConflictAction
Definition: plannodes.h:236
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:444
static int list_length(const List *l)
Definition: pg_list.h:169
TupleDesc ExecTypeFromTL(List *targetList)
Definition: execTuples.c:1908
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1769
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1183
TupleDesc ExecGetResultType(PlanState *planstate)
Definition: execUtils.c:454
List * targetlist
Definition: plannodes.h:142
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:351
CmdType operation
Definition: plannodes.h:221
ResultRelInfo * es_root_result_relations
Definition: execnodes.h:533
#define elog(elevel,...)
Definition: elog.h:228
int i
List * returningLists
Definition: plannodes.h:231
bool isParent
Definition: plannodes.h:1068
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:210
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:138
Definition: pg_list.h:50
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:56
JunkFilter * ExecInitJunkFilter(List *targetList, TupleTableSlot *slot)
Definition: execJunk.c:60
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:473
CmdType
Definition: nodes.h:668
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:417
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2361
List * ri_returningList
Definition: execnodes.h:467
List ** mt_arowmarks
Definition: execnodes.h:1175
int epqParam
Definition: plannodes.h:235
Node * onConflictWhere
Definition: plannodes.h:239
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2338
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:525

◆ ExecInsert()

static TupleTableSlot* ExecInsert ( ModifyTableState mtstate,
TupleTableSlot slot,
TupleTableSlot planSlot,
EState estate,
bool  canSetTag 
)
static

Definition at line 368 of file nodeModifyTable.c.

References Assert, CMD_INSERT, CMD_UPDATE, TupleDescData::constr, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, ExecARInsertTriggers(), ExecARUpdateTriggers(), ExecBRInsertTriggers(), ExecCheckIndexConstraints(), ExecCheckTIDVisible(), ExecComputeStoredGenerated(), ExecConstraints(), FdwRoutine::ExecForeignInsert, ExecGetReturningSlot(), ExecInsertIndexTuples(), ExecIRInsertTriggers(), ExecMaterializeSlot(), ExecOnConflictUpdate(), ExecPartitionCheck(), ExecProcessReturning(), ExecWithCheckOptions(), GetCurrentTransactionId(), TupleConstr::has_generated_stored, InstrCountTuples2, list_free(), ModifyTableState::mt_transition_capture, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationData::rd_att, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_PartitionCheck, ResultRelInfo::ri_PartitionRoot, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, setLastTid(), 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_tableOid, TupleTableSlot::tts_tid, WCO_RLS_INSERT_CHECK, WCO_RLS_UPDATE_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecModifyTable(), and ExecUpdate().

373 {
374  ResultRelInfo *resultRelInfo;
375  Relation resultRelationDesc;
376  List *recheckIndexes = NIL;
377  TupleTableSlot *result = NULL;
378  TransitionCaptureState *ar_insert_trig_tcs;
379  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
380  OnConflictAction onconflict = node->onConflictAction;
381 
382  ExecMaterializeSlot(slot);
383 
384  /*
385  * get information on the (current) result relation
386  */
387  resultRelInfo = estate->es_result_relation_info;
388  resultRelationDesc = resultRelInfo->ri_RelationDesc;
389 
390  /*
391  * BEFORE ROW INSERT Triggers.
392  *
393  * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
394  * INSERT ... ON CONFLICT statement. We cannot check for constraint
395  * violations before firing these triggers, because they can change the
396  * values to insert. Also, they can run arbitrary user-defined code with
397  * side-effects that we can't cancel by just not inserting the tuple.
398  */
399  if (resultRelInfo->ri_TrigDesc &&
400  resultRelInfo->ri_TrigDesc->trig_insert_before_row)
401  {
402  if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
403  return NULL; /* "do nothing" */
404  }
405 
406  /* INSTEAD OF ROW INSERT Triggers */
407  if (resultRelInfo->ri_TrigDesc &&
408  resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
409  {
410  if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
411  return NULL; /* "do nothing" */
412  }
413  else if (resultRelInfo->ri_FdwRoutine)
414  {
415  /*
416  * Compute stored generated columns
417  */
418  if (resultRelationDesc->rd_att->constr &&
419  resultRelationDesc->rd_att->constr->has_generated_stored)
420  ExecComputeStoredGenerated(estate, slot, CMD_INSERT);
421 
422  /*
423  * insert into foreign table: let the FDW do it
424  */
425  slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
426  resultRelInfo,
427  slot,
428  planSlot);
429 
430  if (slot == NULL) /* "do nothing" */
431  return NULL;
432 
433  /*
434  * AFTER ROW Triggers or RETURNING expressions might reference the
435  * tableoid column, so (re-)initialize tts_tableOid before evaluating
436  * them.
437  */
438  slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
439  }
440  else
441  {
442  WCOKind wco_kind;
443 
444  /*
445  * Constraints might reference the tableoid column, so (re-)initialize
446  * tts_tableOid before evaluating them.
447  */
448  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
449 
450  /*
451  * Compute stored generated columns
452  */
453  if (resultRelationDesc->rd_att->constr &&
454  resultRelationDesc->rd_att->constr->has_generated_stored)
455  ExecComputeStoredGenerated(estate, slot, CMD_INSERT);
456 
457  /*
458  * Check any RLS WITH CHECK policies.
459  *
460  * Normally we should check INSERT policies. But if the insert is the
461  * result of a partition key update that moved the tuple to a new
462  * partition, we should instead check UPDATE policies, because we are
463  * executing policies defined on the target table, and not those
464  * defined on the child partitions.
465  */
466  wco_kind = (mtstate->operation == CMD_UPDATE) ?
468 
469  /*
470  * ExecWithCheckOptions() will skip any WCOs which are not of the kind
471  * we are looking for at this point.
472  */
473  if (resultRelInfo->ri_WithCheckOptions != NIL)
474  ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
475 
476  /*
477  * Check the constraints of the tuple.
478  */
479  if (resultRelationDesc->rd_att->constr)
480  ExecConstraints(resultRelInfo, slot, estate);
481 
482  /*
483  * Also check the tuple against the partition constraint, if there is
484  * one; except that if we got here via tuple-routing, we don't need to
485  * if there's no BR trigger defined on the partition.
486  */
487  if (resultRelInfo->ri_PartitionCheck &&
488  (resultRelInfo->ri_PartitionRoot == NULL ||
489  (resultRelInfo->ri_TrigDesc &&
490  resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
491  ExecPartitionCheck(resultRelInfo, slot, estate, true);
492 
493  if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
494  {
495  /* Perform a speculative insertion. */
496  uint32 specToken;
497  ItemPointerData conflictTid;
498  bool specConflict;
499  List *arbiterIndexes;
500 
501  arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
502 
503  /*
504  * Do a non-conclusive check for conflicts first.
505  *
506  * We're not holding any locks yet, so this doesn't guarantee that
507  * the later insert won't conflict. But it avoids leaving behind
508  * a lot of canceled speculative insertions, if you run a lot of
509  * INSERT ON CONFLICT statements that do conflict.
510  *
511  * We loop back here if we find a conflict below, either during
512  * the pre-check, or when we re-check after inserting the tuple
513  * speculatively.
514  */
515  vlock:
516  specConflict = false;
517  if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
518  arbiterIndexes))
519  {
520  /* committed conflict tuple found */
521  if (onconflict == ONCONFLICT_UPDATE)
522  {
523  /*
524  * In case of ON CONFLICT DO UPDATE, execute the UPDATE
525  * part. Be prepared to retry if the UPDATE fails because
526  * of another concurrent UPDATE/DELETE to the conflict
527  * tuple.
528  */
529  TupleTableSlot *returning = NULL;
530 
531  if (ExecOnConflictUpdate(mtstate, resultRelInfo,
532  &conflictTid, planSlot, slot,
533  estate, canSetTag, &returning))
534  {
535  InstrCountTuples2(&mtstate->ps, 1);
536  return returning;
537  }
538  else
539  goto vlock;
540  }
541  else
542  {
543  /*
544  * In case of ON CONFLICT DO NOTHING, do nothing. However,
545  * verify that the tuple is visible to the executor's MVCC
546  * snapshot at higher isolation levels.
547  *
548  * Using ExecGetReturningSlot() to store the tuple for the
549  * recheck isn't that pretty, but we can't trivially use
550  * the input slot, because it might not be of a compatible
551  * type. As there's no conflicting usage of
552  * ExecGetReturningSlot() in the DO NOTHING case...
553  */
554  Assert(onconflict == ONCONFLICT_NOTHING);
555  ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
556  ExecGetReturningSlot(estate, resultRelInfo));
557  InstrCountTuples2(&mtstate->ps, 1);
558  return NULL;
559  }
560  }
561 
562  /*
563  * Before we start insertion proper, acquire our "speculative
564  * insertion lock". Others can use that to wait for us to decide
565  * if we're going to go ahead with the insertion, instead of
566  * waiting for the whole transaction to complete.
567  */
569 
570  /* insert the tuple, with the speculative token */
571  table_tuple_insert_speculative(resultRelationDesc, slot,
572  estate->es_output_cid,
573  0,
574  NULL,
575  specToken);
576 
577  /* insert index entries for tuple */
578  recheckIndexes = ExecInsertIndexTuples(slot, estate, true,
579  &specConflict,
580  arbiterIndexes);
581 
582  /* adjust the tuple's state accordingly */
583  table_tuple_complete_speculative(resultRelationDesc, slot,
584  specToken, !specConflict);
585 
586  /*
587  * Wake up anyone waiting for our decision. They will re-check
588  * the tuple, see that it's no longer speculative, and wait on our
589  * XID as if this was a regularly inserted tuple all along. Or if
590  * we killed the tuple, they will see it's dead, and proceed as if
591  * the tuple never existed.
592  */
594 
595  /*
596  * If there was a conflict, start from the beginning. We'll do
597  * the pre-check again, which will now find the conflicting tuple
598  * (unless it aborts before we get there).
599  */
600  if (specConflict)
601  {
602  list_free(recheckIndexes);
603  goto vlock;
604  }
605 
606  /* Since there was no insertion conflict, we're done */
607  }
608  else
609  {
610  /* insert the tuple normally */
611  table_tuple_insert(resultRelationDesc, slot,
612  estate->es_output_cid,
613  0, NULL);
614 
615  /* insert index entries for tuple */
616  if (resultRelInfo->ri_NumIndices > 0)
617  recheckIndexes = ExecInsertIndexTuples(slot, estate, false, NULL,
618  NIL);
619  }
620  }
621 
622  if (canSetTag)
623  {
624  (estate->es_processed)++;
625  setLastTid(&slot->tts_tid);
626  }
627 
628  /*
629  * If this insert is the result of a partition key update that moved the
630  * tuple to a new partition, put this row into the transition NEW TABLE,
631  * if there is one. We need to do this separately for DELETE and INSERT
632  * because they happen on different tables.
633  */
634  ar_insert_trig_tcs = mtstate->mt_transition_capture;
635  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
637  {
638  ExecARUpdateTriggers(estate, resultRelInfo, NULL,
639  NULL,
640  slot,
641  NULL,
642  mtstate->mt_transition_capture);
643 
644  /*
645  * We've already captured the NEW TABLE row, so make sure any AR
646  * INSERT trigger fired below doesn't capture it again.
647  */
648  ar_insert_trig_tcs = NULL;
649  }
650 
651  /* AFTER ROW INSERT Triggers */
652  ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
653  ar_insert_trig_tcs);
654 
655  list_free(recheckIndexes);
656 
657  /*
658  * Check any WITH CHECK OPTION constraints from parent views. We are
659  * required to do this after testing all constraints and uniqueness
660  * violations per the SQL spec, so we do it after actually inserting the
661  * record into the heap and all indexes.
662  *
663  * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
664  * tuple will never be seen, if it violates the WITH CHECK OPTION.
665  *
666  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
667  * are looking for at this point.
668  */
669  if (resultRelInfo->ri_WithCheckOptions != NIL)
670  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
671 
672  /* Process RETURNING if present */
673  if (resultRelInfo->ri_projectReturning)
674  result = ExecProcessReturning(resultRelInfo, slot, planSlot);
675 
676  return result;
677 }
int ri_NumIndices
Definition: execnodes.h:414
#define NIL
Definition: pg_list.h:65
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:411
void SpeculativeInsertionLockRelease(TransactionId xid)
Definition: lmgr.c:765
static bool ExecOnConflictUpdate(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *planSlot, TupleTableSlot *excludedSlot, EState *estate, bool canSetTag, TupleTableSlot **returning)
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2592
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2027
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1148
CommandId es_output_cid
Definition: execnodes.h:520
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:211
Relation ri_PartitionRoot
Definition: execnodes.h:485
void ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype)
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1896
uint32 SpeculativeInsertionLockAcquire(TransactionId xid)
Definition: lmgr.c:739
CmdType operation
Definition: execnodes.h:1164
static void table_tuple_complete_speculative(Relation rel, TupleTableSlot *slot, uint32 specToken, bool succeeded)
Definition: tableam.h:1188
bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, List *arbiterIndexes)
Definition: execIndexing.c:482
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate)
Definition: tableam.h:1155
bool trig_insert_instead_row
Definition: reltrigger.h:57
bool has_generated_stored
Definition: tupdesc.h:45
PlanState ps
Definition: execnodes.h:1163
bool ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2525
static void ExecCheckTIDVisible(EState *estate, ResultRelInfo *relinfo, ItemPointer tid, TupleTableSlot *tempSlot)
List * ExecInsertIndexTuples(TupleTableSlot *slot, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:273
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:422
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1189
TupleConstr * constr
Definition: tupdesc.h:85
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:470
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:440
unsigned int uint32
Definition: c.h:367
#define InstrCountTuples2(node, delta)
Definition: execnodes.h:1040
void setLastTid(const ItemPointer tid)
Definition: tid.c:280
WCOKind
Definition: parsenodes.h:1176
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:3127
bool trig_insert_before_row
Definition: reltrigger.h:55
static void table_tuple_insert_speculative(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate, uint32 specToken)
Definition: tableam.h:1174
List * ri_WithCheckOptions
Definition: execnodes.h:449
List * ri_PartitionCheck
Definition: execnodes.h:479
TupleDesc rd_att
Definition: rel.h:85
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
Plan * plan
Definition: execnodes.h:943
#define Assert(condition)
Definition: c.h:738
bool ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2607
OnConflictAction onConflictAction
Definition: plannodes.h:236
uint64 es_processed
Definition: execnodes.h:557
void list_free(List *list)
Definition: list.c:1377
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1783
Definition: pg_list.h:50
ItemPointerData tts_tid
Definition: tuptable.h:130
OnConflictAction
Definition: nodes.h:818
#define RelationGetRelid(relation)
Definition: rel.h:428
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:473
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:525

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 2009 of file nodeModifyTable.c.

References Assert, AttributeNumberIsValid, ModifyTableState::canSetTag, castNode, CHECK_FOR_INTERRUPTS, CMD_DELETE, CMD_INSERT, CMD_UPDATE, DatumGetHeapTupleHeader, DatumGetPointer, elog, ERROR, EState::es_epq_active, EState::es_result_relation_info, EvalPlanQualSetPlan(), EvalPlanQualSetSlot, ExecCopySlot(), ExecDelete(), ExecFilterJunk(), ExecGetJunkAttribute(), ExecInsert(), ExecPrepareTupleRouting(), ExecProcessReturning(), ExecProcNode(), ExecUpdate(), fireASTriggers(), ModifyTableState::fireBSTriggers, fireBSTriggers(), HeapTupleHeaderGetDatumLength, InvalidOid, ItemPointerSetInvalid, JunkFilter::jf_junkAttNo, ModifyTableState::mt_arowmarks, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_nplans, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_plans, ModifyTableState::mt_scans, ModifyTableState::mt_transition_capture, ModifyTableState::mt_whichplan, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, RelationData::rd_rel, RelationGetRelid, ResetExprContext, ResetPerTupleExprContext, ModifyTableState::resultRelInfo, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_usesFdwDirectModify, PlanState::state, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransitionCaptureState::tcs_map, TupleTableSlot::tts_ops, tupconv_map_for_subplan(), and TupIsNull.

Referenced by ExecInitModifyTable().

2010 {
2011  ModifyTableState *node = castNode(ModifyTableState, pstate);
2013  EState *estate = node->ps.state;
2014  CmdType operation = node->operation;
2015  ResultRelInfo *saved_resultRelInfo;
2016  ResultRelInfo *resultRelInfo;
2017  PlanState *subplanstate;
2018  JunkFilter *junkfilter;
2019  TupleTableSlot *slot;
2020  TupleTableSlot *planSlot;
2021  ItemPointer tupleid;
2022  ItemPointerData tuple_ctid;
2023  HeapTupleData oldtupdata;
2024  HeapTuple oldtuple;
2025 
2027 
2028  /*
2029  * This should NOT get called during EvalPlanQual; we should have passed a
2030  * subplan tree to EvalPlanQual, instead. Use a runtime test not just
2031  * Assert because this condition is easy to miss in testing. (Note:
2032  * although ModifyTable should not get executed within an EvalPlanQual
2033  * operation, we do have to allow it to be initialized and shut down in
2034  * case it is within a CTE subplan. Hence this test must be here, not in
2035  * ExecInitModifyTable.)
2036  */
2037  if (estate->es_epq_active != NULL)
2038  elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
2039 
2040  /*
2041  * If we've already completed processing, don't try to do more. We need
2042  * this test because ExecPostprocessPlan might call us an extra time, and
2043  * our subplan's nodes aren't necessarily robust against being called
2044  * extra times.
2045  */
2046  if (node->mt_done)
2047  return NULL;
2048 
2049  /*
2050  * On first call, fire BEFORE STATEMENT triggers before proceeding.
2051  */
2052  if (node->fireBSTriggers)
2053  {
2054  fireBSTriggers(node);
2055  node->fireBSTriggers = false;
2056  }
2057 
2058  /* Preload local variables */
2059  resultRelInfo = node->resultRelInfo + node->mt_whichplan;
2060  subplanstate = node->mt_plans[node->mt_whichplan];
2061  junkfilter = resultRelInfo->ri_junkFilter;
2062 
2063  /*
2064  * es_result_relation_info must point to the currently active result
2065  * relation while we are within this ModifyTable node. Even though
2066  * ModifyTable nodes can't be nested statically, they can be nested
2067  * dynamically (since our subplan could include a reference to a modifying
2068  * CTE). So we have to save and restore the caller's value.
2069  */
2070  saved_resultRelInfo = estate->es_result_relation_info;
2071 
2072  estate->es_result_relation_info = resultRelInfo;
2073 
2074  /*
2075  * Fetch rows from subplan(s), and execute the required table modification
2076  * for each row.
2077  */
2078  for (;;)
2079  {
2080  /*
2081  * Reset the per-output-tuple exprcontext. This is needed because
2082  * triggers expect to use that context as workspace. It's a bit ugly
2083  * to do this below the top level of the plan, however. We might need
2084  * to rethink this later.
2085  */
2086  ResetPerTupleExprContext(estate);
2087 
2088  /*
2089  * Reset per-tuple memory context used for processing on conflict and
2090  * returning clauses, to free any expression evaluation storage
2091  * allocated in the previous cycle.
2092  */
2093  if (pstate->ps_ExprContext)
2095 
2096  planSlot = ExecProcNode(subplanstate);
2097 
2098  if (TupIsNull(planSlot))
2099  {
2100  /* advance to next subplan if any */
2101  node->mt_whichplan++;
2102  if (node->mt_whichplan < node->mt_nplans)
2103  {
2104  resultRelInfo++;
2105  subplanstate = node->mt_plans[node->mt_whichplan];
2106  junkfilter = resultRelInfo->ri_junkFilter;
2107  estate->es_result_relation_info = resultRelInfo;
2108  EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
2109  node->mt_arowmarks[node->mt_whichplan]);
2110  /* Prepare to convert transition tuples from this child. */
2111  if (node->mt_transition_capture != NULL)
2112  {
2115  }
2116  if (node->mt_oc_transition_capture != NULL)
2117  {
2120  }
2121  continue;
2122  }
2123  else
2124  break;
2125  }
2126 
2127  /*
2128  * Ensure input tuple is the right format for the target relation.
2129  */
2130  if (node->mt_scans[node->mt_whichplan]->tts_ops != planSlot->tts_ops)
2131  {
2132  ExecCopySlot(node->mt_scans[node->mt_whichplan], planSlot);
2133  planSlot = node->mt_scans[node->mt_whichplan];
2134  }
2135 
2136  /*
2137  * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
2138  * here is compute the RETURNING expressions.
2139  */
2140  if (resultRelInfo->ri_usesFdwDirectModify)
2141  {
2142  Assert(resultRelInfo->ri_projectReturning);
2143 
2144  /*
2145  * A scan slot containing the data that was actually inserted,
2146  * updated or deleted has already been made available to
2147  * ExecProcessReturning by IterateDirectModify, so no need to
2148  * provide it here.
2149  */
2150  slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
2151 
2152  estate->es_result_relation_info = saved_resultRelInfo;
2153  return slot;
2154  }
2155 
2156  EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
2157  slot = planSlot;
2158 
2159  tupleid = NULL;
2160  oldtuple = NULL;
2161  if (junkfilter != NULL)
2162  {
2163  /*
2164  * extract the 'ctid' or 'wholerow' junk attribute.
2165  */
2166  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2167  {
2168  char relkind;
2169  Datum datum;
2170  bool isNull;
2171 
2172  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2173  if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW)
2174  {
2175  datum = ExecGetJunkAttribute(slot,
2176  junkfilter->jf_junkAttNo,
2177  &isNull);
2178  /* shouldn't ever get a null result... */
2179  if (isNull)
2180  elog(ERROR, "ctid is NULL");
2181 
2182  tupleid = (ItemPointer) DatumGetPointer(datum);
2183  tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
2184  tupleid = &tuple_ctid;
2185  }
2186 
2187  /*
2188  * Use the wholerow attribute, when available, to reconstruct
2189  * the old relation tuple.
2190  *
2191  * Foreign table updates have a wholerow attribute when the
2192  * relation has a row-level trigger. Note that the wholerow
2193  * attribute does not carry system columns. Foreign table
2194  * triggers miss seeing those, except that we know enough here
2195  * to set t_tableOid. Quite separately from this, the FDW may
2196  * fetch its own junk attrs to identify the row.
2197  *
2198  * Other relevant relkinds, currently limited to views, always
2199  * have a wholerow attribute.
2200  */
2201  else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo))
2202  {
2203  datum = ExecGetJunkAttribute(slot,
2204  junkfilter->jf_junkAttNo,
2205  &isNull);
2206  /* shouldn't ever get a null result... */
2207  if (isNull)
2208  elog(ERROR, "wholerow is NULL");
2209 
2210  oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
2211  oldtupdata.t_len =
2213  ItemPointerSetInvalid(&(oldtupdata.t_self));
2214  /* Historically, view triggers see invalid t_tableOid. */
2215  oldtupdata.t_tableOid =
2216  (relkind == RELKIND_VIEW) ? InvalidOid :
2217  RelationGetRelid(resultRelInfo->ri_RelationDesc);
2218 
2219  oldtuple = &oldtupdata;
2220  }
2221  else
2222  Assert(relkind == RELKIND_FOREIGN_TABLE);
2223  }
2224 
2225  /*
2226  * apply the junkfilter if needed.
2227  */
2228  if (operation != CMD_DELETE)
2229  slot = ExecFilterJunk(junkfilter, slot);
2230  }
2231 
2232  switch (operation)
2233  {
2234  case CMD_INSERT:
2235  /* Prepare for tuple routing if needed. */
2236  if (proute)
2237  slot = ExecPrepareTupleRouting(node, estate, proute,
2238  resultRelInfo, slot);
2239  slot = ExecInsert(node, slot, planSlot,
2240  estate, node->canSetTag);
2241  /* Revert ExecPrepareTupleRouting's state change. */
2242  if (proute)
2243  estate->es_result_relation_info = resultRelInfo;
2244  break;
2245  case CMD_UPDATE:
2246  slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot,
2247  &node->mt_epqstate, estate, node->canSetTag);
2248  break;
2249  case CMD_DELETE:
2250  slot = ExecDelete(node, tupleid, oldtuple, planSlot,
2251  &node->mt_epqstate, estate,
2252  true, node->canSetTag,
2253  false /* changingPart */ , NULL, NULL);
2254  break;
2255  default:
2256  elog(ERROR, "unknown operation");
2257  break;
2258  }
2259 
2260  /*
2261  * If we got a RETURNING result, return it to caller. We'll continue
2262  * the work on next call.
2263  */
2264  if (slot)
2265  {
2266  estate->es_result_relation_info = saved_resultRelInfo;
2267  return slot;
2268  }
2269  }
2270 
2271  /* Restore es_result_relation_info before exiting */
2272  estate->es_result_relation_info = saved_resultRelInfo;
2273 
2274  /*
2275  * We're done, but fire AFTER STATEMENT triggers before exiting.
2276  */
2277  fireASTriggers(node);
2278 
2279  node->mt_done = true;
2280 
2281  return NULL;
2282 }
AttrNumber jf_junkAttNo
Definition: execnodes.h:371
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
JunkFilter * ri_junkFilter
Definition: execnodes.h:464
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1192
static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool processReturning, bool canSetTag, bool changingPart, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
Relation ri_RelationDesc
Definition: execnodes.h:411
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
#define ResetPerTupleExprContext(estate)
Definition: executor.h:515
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1186
#define castNode(_type_, nodeptr)
Definition: nodes.h:594
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1172
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
ExprContext * ps_ExprContext
Definition: execnodes.h:982
TupleTableSlot ** mt_scans
Definition: execnodes.h:1170
static void fireBSTriggers(ModifyTableState *node)
static TupleConversionMap * tupconv_map_for_subplan(ModifyTableState *node, int whichplan)
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:122
CmdType operation
Definition: execnodes.h:1164
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2523
EState * state
Definition: execnodes.h:945
Form_pg_class rd_rel
Definition: rel.h:84
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:289
struct EPQState * es_epq_active
Definition: execnodes.h:582
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1163
TupleConversionMap * tcs_map
Definition: trigger.h:73
ItemPointerData t_self
Definition: htup.h:65
bool ri_usesFdwDirectModify
Definition: execnodes.h:446
uint32 t_len
Definition: htup.h:64
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1189
EPQState mt_epqstate
Definition: execnodes.h:1176
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:470
#define TupIsNull(slot)
Definition: tuptable.h:292
Oid t_tableOid
Definition: htup.h:66
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
PlanState ** mt_plans
Definition: execnodes.h:1167
static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot)
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static void fireASTriggers(ModifyTableState *node)
uintptr_t Datum
Definition: postgres.h:367
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:261
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:240
Plan * plan
Definition: execnodes.h:943
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:738
#define DatumGetPointer(X)
Definition: postgres.h:549
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: execJunk.c:247
#define elog(elevel,...)
Definition: elog.h:228
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
#define RelationGetRelid(relation)
Definition: rel.h:428
CmdType
Definition: nodes.h:668
#define ResetExprContext(econtext)
Definition: executor.h:500
List ** mt_arowmarks
Definition: execnodes.h:1175
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:215
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:452
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:525

◆ ExecOnConflictUpdate()

static bool ExecOnConflictUpdate ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo,
ItemPointer  conflictTid,
TupleTableSlot planSlot,
TupleTableSlot excludedSlot,
EState estate,
bool  canSetTag,
TupleTableSlot **  returning 
)
static

Definition at line 1518 of file nodeModifyTable.c.

References Assert, TM_FailureData::ctid, DatumGetTransactionId, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ereport, errcode(), errhint(), errmsg(), ERROR, EState::es_output_cid, EState::es_snapshot, ExecCheckTupleVisible(), ExecClearTuple(), ExecProject(), ExecQual(), ExecUpdate(), ExecUpdateLockMode(), ExecWithCheckOptions(), InstrCountFiltered1, IsolationUsesXactSnapshot, ItemPointerIndicatesMovedPartitions, LockWaitBlock, MinTransactionIdAttributeNumber, ModifyTableState::mt_epqstate, 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().

1526 {
1527  ExprContext *econtext = mtstate->ps.ps_ExprContext;
1528  Relation relation = resultRelInfo->ri_RelationDesc;
1529  ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
1530  TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
1531  TM_FailureData tmfd;
1532  LockTupleMode lockmode;
1533  TM_Result test;
1534  Datum xminDatum;
1535  TransactionId xmin;
1536  bool isnull;
1537 
1538  /* Determine lock mode to use */
1539  lockmode = ExecUpdateLockMode(estate, resultRelInfo);
1540 
1541  /*
1542  * Lock tuple for update. Don't follow updates when tuple cannot be
1543  * locked without doing so. A row locking conflict here means our
1544  * previous conclusion that the tuple is conclusively committed is not
1545  * true anymore.
1546  */
1547  test = table_tuple_lock(relation, conflictTid,
1548  estate->es_snapshot,
1549  existing, estate->es_output_cid,
1550  lockmode, LockWaitBlock, 0,
1551  &tmfd);
1552  switch (test)
1553  {
1554  case TM_Ok:
1555  /* success! */
1556  break;
1557 
1558  case TM_Invisible:
1559 
1560  /*
1561  * This can occur when a just inserted tuple is updated again in
1562  * the same command. E.g. because multiple rows with the same
1563  * conflicting key values are inserted.
1564  *
1565  * This is somewhat similar to the ExecUpdate() TM_SelfModified
1566  * case. We do not want to proceed because it would lead to the
1567  * same row being updated a second time in some unspecified order,
1568  * and in contrast to plain UPDATEs there's no historical behavior
1569  * to break.
1570  *
1571  * It is the user's responsibility to prevent this situation from
1572  * occurring. These problems are why SQL-2003 similarly specifies
1573  * that for SQL MERGE, an exception must be raised in the event of
1574  * an attempt to update the same row twice.
1575  */
1576  xminDatum = slot_getsysattr(existing,
1578  &isnull);
1579  Assert(!isnull);
1580  xmin = DatumGetTransactionId(xminDatum);
1581 
1583  ereport(ERROR,
1584  (errcode(ERRCODE_CARDINALITY_VIOLATION),
1585  errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
1586  errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
1587 
1588  /* This shouldn't happen */
1589  elog(ERROR, "attempted to lock invisible tuple");
1590  break;
1591 
1592  case TM_SelfModified:
1593 
1594  /*
1595  * This state should never be reached. As a dirty snapshot is used
1596  * to find conflicting tuples, speculative insertion wouldn't have
1597  * seen this row to conflict with.
1598  */
1599  elog(ERROR, "unexpected self-updated tuple");
1600  break;
1601 
1602  case TM_Updated:
1604  ereport(ERROR,
1605  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1606  errmsg("could not serialize access due to concurrent update")));
1607 
1608  /*
1609  * As long as we don't support an UPDATE of INSERT ON CONFLICT for
1610  * a partitioned table we shouldn't reach to a case where tuple to
1611  * be lock is moved to another partition due to concurrent update
1612  * of the partition key.
1613  */
1615 
1616  /*
1617  * Tell caller to try again from the very start.
1618  *
1619  * It does not make sense to use the usual EvalPlanQual() style
1620  * loop here, as the new version of the row might not conflict
1621  * anymore, or the conflicting tuple has actually been deleted.
1622  */
1623  ExecClearTuple(existing);
1624  return false;
1625 
1626  case TM_Deleted:
1628  ereport(ERROR,
1629  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1630  errmsg("could not serialize access due to concurrent delete")));
1631 
1632  /* see TM_Updated case */
1634  ExecClearTuple(existing);
1635  return false;
1636 
1637  default:
1638  elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
1639  }
1640 
1641  /* Success, the tuple is locked. */
1642 
1643  /*
1644  * Verify that the tuple is visible to our MVCC snapshot if the current
1645  * isolation level mandates that.
1646  *
1647  * It's not sufficient to rely on the check within ExecUpdate() as e.g.
1648  * CONFLICT ... WHERE clause may prevent us from reaching that.
1649  *
1650  * This means we only ever continue when a new command in the current
1651  * transaction could see the row, even though in READ COMMITTED mode the
1652  * tuple will not be visible according to the current statement's
1653  * snapshot. This is in line with the way UPDATE deals with newer tuple
1654  * versions.
1655  */
1656  ExecCheckTupleVisible(estate, relation, existing);
1657 
1658  /*
1659  * Make tuple and any needed join variables available to ExecQual and
1660  * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
1661  * the target's existing tuple is installed in the scantuple. EXCLUDED
1662  * has been made to reference INNER_VAR in setrefs.c, but there is no
1663  * other redirection.
1664  */
1665  econtext->ecxt_scantuple = existing;
1666  econtext->ecxt_innertuple = excludedSlot;
1667  econtext->ecxt_outertuple = NULL;
1668 
1669  if (!ExecQual(onConflictSetWhere, econtext))
1670  {
1671  ExecClearTuple(existing); /* see return below */
1672  InstrCountFiltered1(&mtstate->ps, 1);
1673  return true; /* done with the tuple */
1674  }
1675 
1676  if (resultRelInfo->ri_WithCheckOptions != NIL)
1677  {
1678  /*
1679  * Check target's existing tuple against UPDATE-applicable USING
1680  * security barrier quals (if any), enforced here as RLS checks/WCOs.
1681  *
1682  * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
1683  * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
1684  * but that's almost the extent of its special handling for ON
1685  * CONFLICT DO UPDATE.
1686  *
1687  * The rewriter will also have associated UPDATE applicable straight
1688  * RLS checks/WCOs for the benefit of the ExecUpdate() call that
1689  * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
1690  * kinds, so there is no danger of spurious over-enforcement in the
1691  * INSERT or UPDATE path.
1692  */
1694  existing,
1695  mtstate->ps.state);
1696  }
1697 
1698  /* Project the new tuple version */
1699  ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
1700 
1701  /*
1702  * Note that it is possible that the target tuple has been modified in
1703  * this session, after the above table_tuple_lock. We choose to not error
1704  * out in that case, in line with ExecUpdate's treatment of similar cases.
1705  * This can happen if an UPDATE is triggered from within ExecQual(),
1706  * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
1707  * wCTE in the ON CONFLICT's SET.
1708  */
1709 
1710  /* Execute UPDATE with projection */
1711  *returning = ExecUpdate(mtstate, conflictTid, NULL,
1712  resultRelInfo->ri_onConflict->oc_ProjSlot,
1713  planSlot,
1714  &mtstate->mt_epqstate, mtstate->ps.state,
1715  canSetTag);
1716 
1717  /*
1718  * Clear out existing tuple, as there might not be another conflict among
1719  * the next input rows. Don't want to hold resources till the end of the
1720  * query.
1721  */
1722  ExecClearTuple(existing);
1723  return true;
1724 }
#define NIL
Definition: pg_list.h:65
ItemPointerData ctid
Definition: tableam.h:124
Relation ri_RelationDesc
Definition: execnodes.h:411
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1069
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2027
CommandId es_output_cid
Definition: execnodes.h:520
static void test(void)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
uint32 TransactionId
Definition: c.h:513
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:853
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
ExprContext * ps_ExprContext
Definition: execnodes.h:982
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
int errcode(int sqlerrcode)
Definition: elog.c:608
Snapshot es_snapshot
Definition: execnodes.h:506
EState * state
Definition: execnodes.h:945
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:370
TupleTableSlot * oc_Existing
Definition: execnodes.h:383
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1163
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:385
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:1333
EPQState mt_epqstate
Definition: execnodes.h:1176
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:226
ExprState * oc_WhereClause
Definition: execnodes.h:386
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1045
#define ereport(elevel, rest)
Definition: elog.h:141
OnConflictSetState * ri_onConflict
Definition: execnodes.h:476
TM_Result
Definition: tableam.h:69
uintptr_t Datum
Definition: postgres.h:367
List * ri_WithCheckOptions
Definition: execnodes.h:449
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:384
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:228
#define Assert(condition)
Definition: c.h:738
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2312
Definition: tableam.h:75
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:224
#define DatumGetTransactionId(X)
Definition: postgres.h:514
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
static Datum slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:402
#define MinTransactionIdAttributeNumber
Definition: sysattr.h:22
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:333

◆ ExecPrepareTupleRouting()

static TupleTableSlot * ExecPrepareTupleRouting ( ModifyTableState mtstate,
EState estate,
PartitionTupleRouting proute,
ResultRelInfo targetRelInfo,
TupleTableSlot slot 
)
static

Definition at line 1876 of file nodeModifyTable.c.

References Assert, TupleConversionMap::attrMap, EState::es_result_relation_info, ExecFindPartition(), execute_attr_map_slot(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, PartitionRoutingInfo::pi_PartitionToRootMap, PartitionRoutingInfo::pi_PartitionTupleSlot, PartitionRoutingInfo::pi_RootToPartitionMap, ResultRelInfo::ri_PartitionInfo, ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, and TriggerDesc::trig_insert_before_row.

Referenced by ExecModifyTable(), and ExecUpdate().

1881 {
1882  ResultRelInfo *partrel;
1883  PartitionRoutingInfo *partrouteinfo;
1884  TupleConversionMap *map;
1885 
1886  /*
1887  * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
1888  * not find a valid partition for the tuple in 'slot' then an error is
1889  * raised. An error may also be raised if the found partition is not a
1890  * valid target for INSERTs. This is required since a partitioned table
1891  * UPDATE to another partition becomes a DELETE+INSERT.
1892  */
1893  partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
1894  partrouteinfo = partrel->ri_PartitionInfo;
1895  Assert(partrouteinfo != NULL);
1896 
1897  /*
1898  * Make it look like we are inserting into the partition.
1899  */
1900  estate->es_result_relation_info = partrel;
1901 
1902  /*
1903  * If we're capturing transition tuples, we might need to convert from the
1904  * partition rowtype to root partitioned table's rowtype.
1905  */
1906  if (mtstate->mt_transition_capture != NULL)
1907  {
1908  if (partrel->ri_TrigDesc &&
1910  {
1911  /*
1912  * If there are any BEFORE triggers on the partition, we'll have
1913  * to be ready to convert their result back to tuplestore format.
1914  */
1916  mtstate->mt_transition_capture->tcs_map =
1917  partrouteinfo->pi_PartitionToRootMap;
1918  }
1919  else
1920  {
1921  /*
1922  * Otherwise, just remember the original unconverted tuple, to
1923  * avoid a needless round trip conversion.
1924  */
1926  mtstate->mt_transition_capture->tcs_map = NULL;
1927  }
1928  }
1929  if (mtstate->mt_oc_transition_capture != NULL)
1930  {
1931  mtstate->mt_oc_transition_capture->tcs_map =
1932  partrouteinfo->pi_PartitionToRootMap;
1933  }
1934 
1935  /*
1936  * Convert the tuple, if necessary.
1937  */
1938  map = partrouteinfo->pi_RootToPartitionMap;
1939  if (map != NULL)
1940  {
1941  TupleTableSlot *new_slot = partrouteinfo->pi_PartitionTupleSlot;
1942 
1943  slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
1944  }
1945 
1946  return slot;
1947 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1192
struct PartitionRoutingInfo * ri_PartitionInfo
Definition: execnodes.h:488
TupleConversionMap * pi_RootToPartitionMap
Definition: execPartition.h:37
TupleConversionMap * tcs_map
Definition: trigger.h:73
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1189
AttrMap * attrMap
Definition: tupconvert.h:27
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
bool trig_insert_before_row
Definition: reltrigger.h:55
TupleConversionMap * pi_PartitionToRootMap
Definition: execPartition.h:43
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
#define Assert(condition)
Definition: c.h:738
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleTableSlot * pi_PartitionTupleSlot
Definition: execPartition.h:49
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:82
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:525

◆ ExecProcessReturning()

static TupleTableSlot* ExecProcessReturning ( ResultRelInfo resultRelInfo,
TupleTableSlot tupleSlot,
TupleTableSlot planSlot 
)
static

Definition at line 162 of file nodeModifyTable.c.

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

165 {
166  ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
167  ExprContext *econtext = projectReturning->pi_exprContext;
168 
169  /* Make tuple and any needed join variables available to ExecProject */
170  if (tupleSlot)
171  econtext->ecxt_scantuple = tupleSlot;
172  econtext->ecxt_outertuple = planSlot;
173 
174  /*
175  * RETURNING expressions might reference the tableoid column, so
176  * reinitialize tts_tableOid before evaluating them.
177  */
178  econtext->ecxt_scantuple->tts_tableOid =
179  RelationGetRelid(resultRelInfo->ri_RelationDesc);
180 
181  /* Compute the RETURNING expressions */
182  return ExecProject(projectReturning);
183 }
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:411
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:470
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:228
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:224
ExprContext * pi_exprContext
Definition: execnodes.h:333
#define RelationGetRelid(relation)
Definition: rel.h:428
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:333

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 2808 of file nodeModifyTable.c.

References elog, and ERROR.

Referenced by ExecReScan().

2809 {
2810  /*
2811  * Currently, we don't need to support rescan on ModifyTable nodes. The
2812  * semantics of that would be a bit debatable anyway.
2813  */
2814  elog(ERROR, "ExecReScanModifyTable is not implemented");
2815 }
#define ERROR
Definition: elog.h:43
#define elog(elevel,...)
Definition: elog.h:228

◆ ExecSetupChildParentMapForSubplan()

static void ExecSetupChildParentMapForSubplan ( ModifyTableState mtstate)
static

Definition at line 1959 of file nodeModifyTable.c.

References convert_tuples_by_name(), getTargetResultRelInfo(), i, ModifyTableState::mt_nplans, ModifyTableState::mt_per_subplan_tupconv_maps, palloc(), RelationGetDescr, ModifyTableState::resultRelInfo, and ResultRelInfo::ri_RelationDesc.

Referenced by ExecInitModifyTable(), ExecSetupTransitionCaptureState(), and tupconv_map_for_subplan().

1960 {
1961  ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate);
1962  ResultRelInfo *resultRelInfos = mtstate->resultRelInfo;
1963  TupleDesc outdesc;
1964  int numResultRelInfos = mtstate->mt_nplans;
1965  int i;
1966 
1967  /*
1968  * Build array of conversion maps from each child's TupleDesc to the one
1969  * used in the target relation. The map pointers may be NULL when no
1970  * conversion is necessary, which is hopefully a common case.
1971  */
1972 
1973  /* Get tuple descriptor of the target rel. */
1974  outdesc = RelationGetDescr(targetRelInfo->ri_RelationDesc);
1975 
1977  palloc(sizeof(TupleConversionMap *) * numResultRelInfos);
1978 
1979  for (i = 0; i < numResultRelInfos; ++i)
1980  {
1981  mtstate->mt_per_subplan_tupconv_maps[i] =
1982  convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc),
1983  outdesc);
1984  }
1985 }
Relation ri_RelationDesc
Definition: execnodes.h:411
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
#define RelationGetDescr(relation)
Definition: rel.h:454
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1172
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:102
TupleConversionMap ** mt_per_subplan_tupconv_maps
Definition: execnodes.h:1195
void * palloc(Size size)
Definition: mcxt.c:949
int i

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 1824 of file nodeModifyTable.c.

References CMD_INSERT, CMD_UPDATE, ExecSetupChildParentMapForSubplan(), getTargetResultRelInfo(), MakeTransitionCaptureState(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::operation, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationGetRelid, TransitionCaptureState::tcs_map, and tupconv_map_for_subplan().

Referenced by ExecInitModifyTable().

1825 {
1826  ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
1827  ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate);
1828 
1829  /* Check for transition tables on the directly targeted relation. */
1830  mtstate->mt_transition_capture =
1831  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1832  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1833  mtstate->operation);
1834  if (plan->operation == CMD_INSERT &&
1836  mtstate->mt_oc_transition_capture =
1837  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1838  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1839  CMD_UPDATE);
1840 
1841  /*
1842  * If we found that we need to collect transition tuples then we may also
1843  * need tuple conversion maps for any children that have TupleDescs that
1844  * aren't compatible with the tuplestores. (We can share these maps
1845  * between the regular and ON CONFLICT cases.)
1846  */
1847  if (mtstate->mt_transition_capture != NULL ||
1848  mtstate->mt_oc_transition_capture != NULL)
1849  {
1851 
1852  /*
1853  * Install the conversion map for the first plan for UPDATE and DELETE
1854  * operations. It will be advanced each time we switch to the next
1855  * plan. (INSERT operations set it every time, so we need not update
1856  * mtstate->mt_oc_transition_capture here.)
1857  */
1858  if (mtstate->mt_transition_capture && mtstate->operation != CMD_INSERT)
1859  mtstate->mt_transition_capture->tcs_map =
1860  tupconv_map_for_subplan(mtstate, 0);
1861  }
1862 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1192
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
static TupleConversionMap * tupconv_map_for_subplan(ModifyTableState *node, int whichplan)
CmdType operation
Definition: execnodes.h:1164
static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
PlanState ps
Definition: execnodes.h:1163
TupleConversionMap * tcs_map
Definition: trigger.h:73
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1189
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4663
Plan * plan
Definition: execnodes.h:943
OnConflictAction onConflictAction
Definition: plannodes.h:236
CmdType operation
Definition: plannodes.h:221
#define RelationGetRelid(relation)
Definition: rel.h:428

◆ ExecUpdate()

static TupleTableSlot* ExecUpdate ( ModifyTableState mtstate,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot slot,
TupleTableSlot planSlot,
EPQState epqstate,
EState estate,
bool  canSetTag 
)
static

Definition at line 1062 of file nodeModifyTable.c.

References Assert, TupleConversionMap::attrMap, TM_FailureData::cmax, CMD_INSERT, CMD_UPDATE, TupleDescData::constr, elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, EState::es_snapshot, EvalPlanQual(), EvalPlanQualSlot(), ExecARUpdateTriggers(), ExecBRUpdateTriggers(), ExecComputeStoredGenerated(), ExecConstraints(), ExecDelete(), ExecFilterJunk(), FdwRoutine::ExecForeignUpdate, ExecInsert(), ExecInsertIndexTuples(), ExecIRUpdateTriggers(), ExecMaterializeSlot(), ExecPartitionCheck(), ExecPartitionCheckEmitError(), ExecPrepareTupleRouting(), ExecProcessReturning(), execute_attr_map_slot(), ExecWithCheckOptions(), TupleConstr::has_generated_stored, IsBootstrapProcessingMode, IsolationUsesXactSnapshot, list_free(), LockWaitBlock, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, ModifyTableState::mt_transition_capture, NIL, ONCONFLICT_UPDATE, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationData::rd_att, RelationGetRelid, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_PartitionCheck, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, ModifyTableState::rootResultRelInfo, table_tuple_lock(), table_tuple_update(), TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, TM_Deleted, TM_Ok, TM_SelfModified, TM_Updated, TM_FailureData::traversed, TriggerDesc::trig_update_before_row, TriggerDesc::trig_update_instead_row, TupleTableSlot::tts_tableOid, tupconv_map_for_subplan(), TupIsNull, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, WCO_RLS_UPDATE_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

1070 {
1071  ResultRelInfo *resultRelInfo;
1072  Relation resultRelationDesc;
1073  TM_Result result;
1074  TM_FailureData tmfd;
1075  List *recheckIndexes = NIL;
1076  TupleConversionMap *saved_tcs_map = NULL;
1077 
1078  /*
1079  * abort the operation if not running transactions
1080  */
1082  elog(ERROR, "cannot UPDATE during bootstrap");
1083 
1084  ExecMaterializeSlot(slot);
1085 
1086  /*
1087  * get information on the (current) result relation
1088  */
1089  resultRelInfo = estate->es_result_relation_info;
1090  resultRelationDesc = resultRelInfo->ri_RelationDesc;
1091 
1092  /* BEFORE ROW UPDATE Triggers */
1093  if (resultRelInfo->ri_TrigDesc &&
1094  resultRelInfo->ri_TrigDesc->trig_update_before_row)
1095  {
1096  if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
1097  tupleid, oldtuple, slot))
1098  return NULL; /* "do nothing" */
1099  }
1100 
1101  /* INSTEAD OF ROW UPDATE Triggers */
1102  if (resultRelInfo->ri_TrigDesc &&
1103  resultRelInfo->ri_TrigDesc->trig_update_instead_row)
1104  {
1105  if (!ExecIRUpdateTriggers(estate, resultRelInfo,
1106  oldtuple, slot))
1107  return NULL; /* "do nothing" */
1108  }
1109  else if (resultRelInfo->ri_FdwRoutine)
1110  {
1111  /*
1112  * Compute stored generated columns
1113  */
1114  if (resultRelationDesc->rd_att->constr &&
1115  resultRelationDesc->rd_att->constr->has_generated_stored)
1116  ExecComputeStoredGenerated(estate, slot, CMD_UPDATE);
1117 
1118  /*
1119  * update in foreign table: let the FDW do it
1120  */
1121  slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
1122  resultRelInfo,
1123  slot,
1124  planSlot);
1125 
1126  if (slot == NULL) /* "do nothing" */
1127  return NULL;
1128 
1129  /*
1130  * AFTER ROW Triggers or RETURNING expressions might reference the
1131  * tableoid column, so (re-)initialize tts_tableOid before evaluating
1132  * them.
1133  */
1134  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1135  }
1136  else
1137  {
1138  LockTupleMode lockmode;
1139  bool partition_constraint_failed;
1140  bool update_indexes;
1141 
1142  /*
1143  * Constraints might reference the tableoid column, so (re-)initialize
1144  * tts_tableOid before evaluating them.
1145  */
1146  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1147 
1148  /*
1149  * Compute stored generated columns
1150  */
1151  if (resultRelationDesc->rd_att->constr &&
1152  resultRelationDesc->rd_att->constr->has_generated_stored)
1153  ExecComputeStoredGenerated(estate, slot, CMD_UPDATE);
1154 
1155  /*
1156  * Check any RLS UPDATE WITH CHECK policies
1157  *
1158  * If we generate a new candidate tuple after EvalPlanQual testing, we
1159  * must loop back here and recheck any RLS policies and constraints.
1160  * (We don't need to redo triggers, however. If there are any BEFORE
1161  * triggers then trigger.c will have done table_tuple_lock to lock the
1162  * correct tuple, so there's no need to do them again.)
1163  */
1164 lreplace:;
1165 
1166  /* ensure slot is independent, consider e.g. EPQ */
1167  ExecMaterializeSlot(slot);
1168 
1169  /*
1170  * If partition constraint fails, this row might get moved to another
1171  * partition, in which case we should check the RLS CHECK policy just
1172  * before inserting into the new partition, rather than doing it here.
1173  * This is because a trigger on that partition might again change the
1174  * row. So skip the WCO checks if the partition constraint fails.
1175  */
1176  partition_constraint_failed =
1177  resultRelInfo->ri_PartitionCheck &&
1178  !ExecPartitionCheck(resultRelInfo, slot, estate, false);
1179 
1180  if (!partition_constraint_failed &&
1181  resultRelInfo->ri_WithCheckOptions != NIL)
1182  {
1183  /*
1184  * ExecWithCheckOptions() will skip any WCOs which are not of the
1185  * kind we are looking for at this point.
1186  */
1188  resultRelInfo, slot, estate);
1189  }
1190 
1191  /*
1192  * If a partition check failed, try to move the row into the right
1193  * partition.
1194  */
1195  if (partition_constraint_failed)
1196  {
1197  bool tuple_deleted;
1198  TupleTableSlot *ret_slot;
1199  TupleTableSlot *epqslot = NULL;
1201  int map_index;
1202  TupleConversionMap *tupconv_map;
1203 
1204  /*
1205  * Disallow an INSERT ON CONFLICT DO UPDATE that causes the
1206  * original row to migrate to a different partition. Maybe this
1207  * can be implemented some day, but it seems a fringe feature with
1208  * little redeeming value.
1209  */
1210  if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1211  ereport(ERROR,
1212  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1213  errmsg("invalid ON UPDATE specification"),
1214  errdetail("The result tuple would appear in a different partition than the original tuple.")));
1215 
1216  /*
1217  * When an UPDATE is run on a leaf partition, we will not have
1218  * partition tuple routing set up. In that case, fail with
1219  * partition constraint violation error.
1220  */
1221  if (proute == NULL)
1222  ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1223 
1224  /*
1225  * Row movement, part 1. Delete the tuple, but skip RETURNING
1226  * processing. We want to return rows from INSERT.
1227  */
1228  ExecDelete(mtstate, tupleid, oldtuple, planSlot, epqstate,
1229  estate, false, false /* canSetTag */ ,
1230  true /* changingPart */ , &tuple_deleted, &epqslot);
1231 
1232  /*
1233  * For some reason if DELETE didn't happen (e.g. trigger prevented
1234  * it, or it was already deleted by self, or it was concurrently
1235  * deleted by another transaction), then we should skip the insert
1236  * as well; otherwise, an UPDATE could cause an increase in the
1237  * total number of rows across all partitions, which is clearly
1238  * wrong.
1239  *
1240  * For a normal UPDATE, the case where the tuple has been the
1241  * subject of a concurrent UPDATE or DELETE would be handled by
1242  * the EvalPlanQual machinery, but for an UPDATE that we've
1243  * translated into a DELETE from this partition and an INSERT into
1244  * some other partition, that's not available, because CTID chains
1245  * can't span relation boundaries. We mimic the semantics to a
1246  * limited extent by skipping the INSERT if the DELETE fails to
1247  * find a tuple. This ensures that two concurrent attempts to
1248  * UPDATE the same tuple at the same time can't turn one tuple
1249  * into two, and that an UPDATE of a just-deleted tuple can't
1250  * resurrect it.
1251  */
1252  if (!tuple_deleted)
1253  {
1254  /*
1255  * epqslot will be typically NULL. But when ExecDelete()
1256  * finds that another transaction has concurrently updated the
1257  * same row, it re-fetches the row, skips the delete, and
1258  * epqslot is set to the re-fetched tuple slot. In that case,
1259  * we need to do all the checks again.
1260  */
1261  if (TupIsNull(epqslot))
1262  return NULL;
1263  else
1264  {
1265  slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1266  goto lreplace;
1267  }
1268  }
1269 
1270  /*
1271  * Updates set the transition capture map only when a new subplan
1272  * is chosen. But for inserts, it is set for each row. So after
1273  * INSERT, we need to revert back to the map created for UPDATE;
1274  * otherwise the next UPDATE will incorrectly use the one created
1275  * for INSERT. So first save the one created for UPDATE.
1276  */
1277  if (mtstate->mt_transition_capture)
1278  saved_tcs_map = mtstate->mt_transition_capture->tcs_map;
1279 
1280  /*
1281  * resultRelInfo is one of the per-subplan resultRelInfos. So we
1282  * should convert the tuple into root's tuple descriptor, since
1283  * ExecInsert() starts the search from root. The tuple conversion
1284  * map list is in the order of mtstate->resultRelInfo[], so to
1285  * retrieve the one for this resultRel, we need to know the
1286  * position of the resultRel in mtstate->resultRelInfo[].
1287  */
1288  map_index = resultRelInfo - mtstate->resultRelInfo;
1289  Assert(map_index >= 0 && map_index < mtstate->mt_nplans);
1290  tupconv_map = tupconv_map_for_subplan(mtstate, map_index);
1291  if (tupconv_map != NULL)
1292  slot = execute_attr_map_slot(tupconv_map->attrMap,
1293  slot,
1294  mtstate->mt_root_tuple_slot);
1295 
1296  /*
1297  * Prepare for tuple routing, making it look like we're inserting
1298  * into the root.
1299  */
1300  Assert(mtstate->rootResultRelInfo != NULL);
1301  slot = ExecPrepareTupleRouting(mtstate, estate, proute,
1302  mtstate->rootResultRelInfo, slot);
1303 
1304  ret_slot = ExecInsert(mtstate, slot, planSlot,
1305  estate, canSetTag);
1306 
1307  /* Revert ExecPrepareTupleRouting's node change. */
1308  estate->es_result_relation_info = resultRelInfo;
1309  if (mtstate->mt_transition_capture)
1310  {
1312  mtstate->mt_transition_capture->tcs_map = saved_tcs_map;
1313  }
1314 
1315  return ret_slot;
1316  }
1317 
1318  /*
1319  * Check the constraints of the tuple. We've already checked the
1320  * partition constraint above; however, we must still ensure the tuple
1321  * passes all other constraints, so we will call ExecConstraints() and
1322  * have it validate all remaining checks.
1323  */
1324  if (resultRelationDesc->rd_att->constr)
1325  ExecConstraints(resultRelInfo, slot, estate);
1326 
1327  /*
1328  * replace the heap tuple
1329  *
1330  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
1331  * that the row to be updated is visible to that snapshot, and throw a
1332  * can't-serialize error if not. This is a special-case behavior
1333  * needed for referential integrity updates in transaction-snapshot
1334  * mode transactions.
1335  */
1336  result = table_tuple_update(resultRelationDesc, tupleid, slot,
1337  estate->es_output_cid,
1338  estate->es_snapshot,
1339  estate->es_crosscheck_snapshot,
1340  true /* wait for commit */ ,
1341  &tmfd, &lockmode, &update_indexes);
1342 
1343  switch (result)
1344  {
1345  case TM_SelfModified:
1346 
1347  /*
1348  * The target tuple was already updated or deleted by the
1349  * current command, or by a later command in the current
1350  * transaction. The former case is possible in a join UPDATE
1351  * where multiple tuples join to the same target tuple. This
1352  * is pretty questionable, but Postgres has always allowed it:
1353  * we just execute the first update action and ignore
1354  * additional update attempts.
1355  *
1356  * The latter case arises if the tuple is modified by a
1357  * command in a BEFORE trigger, or perhaps by a command in a
1358  * volatile function used in the query. In such situations we
1359  * should not ignore the update, but it is equally unsafe to
1360  * proceed. We don't want to discard the original UPDATE
1361  * while keeping the triggered actions based on it; and we
1362  * have no principled way to merge this update with the
1363  * previous ones. So throwing an error is the only safe
1364  * course.
1365  *
1366  * If a trigger actually intends this type of interaction, it
1367  * can re-execute the UPDATE (assuming it can figure out how)
1368  * and then return NULL to cancel the outer update.
1369  */
1370  if (tmfd.cmax != estate->es_output_cid)
1371  ereport(ERROR,
1372  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1373  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1374  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1375 
1376  /* Else, already updated by self; nothing to do */
1377  return NULL;
1378 
1379  case TM_Ok:
1380  break;
1381 
1382  case TM_Updated:
1383  {
1384  TupleTableSlot *inputslot;
1385  TupleTableSlot *epqslot;
1386 
1388  ereport(ERROR,
1389  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1390  errmsg("could not serialize access due to concurrent update")));
1391 
1392  /*
1393  * Already know that we're going to need to do EPQ, so
1394  * fetch tuple directly into the right slot.
1395  */
1396  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
1397  resultRelInfo->ri_RangeTableIndex);
1398 
1399  result = table_tuple_lock(resultRelationDesc, tupleid,
1400  estate->es_snapshot,
1401  inputslot, estate->es_output_cid,
1402  lockmode, LockWaitBlock,
1404  &tmfd);
1405 
1406  switch (result)
1407  {
1408  case TM_Ok:
1409  Assert(tmfd.traversed);
1410 
1411  epqslot = EvalPlanQual(epqstate,
1412  resultRelationDesc,
1413  resultRelInfo->ri_RangeTableIndex,
1414  inputslot);
1415  if (TupIsNull(epqslot))
1416  /* Tuple not passing quals anymore, exiting... */
1417  return NULL;
1418 
1419  slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1420  goto lreplace;
1421 
1422  case TM_Deleted:
1423  /* tuple already deleted; nothing to do */
1424  return NULL;
1425 
1426  case TM_SelfModified:
1427 
1428  /*
1429  * This can be reached when following an update
1430  * chain from a tuple updated by another session,
1431  * reaching a tuple that was already updated in
1432  * this transaction. If previously modified by
1433  * this command, ignore the redundant update,
1434  * otherwise error out.
1435  *
1436  * See also TM_SelfModified response to
1437  * table_tuple_update() above.
1438  */
1439  if (tmfd.cmax != estate->es_output_cid)
1440  ereport(ERROR,
1441  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1442  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1443  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1444  return NULL;
1445 
1446  default:
1447  /* see table_tuple_lock call in ExecDelete() */
1448  elog(ERROR, "unexpected table_tuple_lock status: %u",
1449  result);
1450  return NULL;
1451  }
1452  }
1453 
1454  break;
1455 
1456  case TM_Deleted:
1458  ereport(ERROR,
1459  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1460  errmsg("could not serialize access due to concurrent delete")));
1461  /* tuple already deleted; nothing to do */
1462  return NULL;
1463 
1464  default:
1465  elog(ERROR, "unrecognized table_tuple_update status: %u",
1466  result);
1467  return NULL;
1468  }
1469 
1470  /* insert index entries for tuple if necessary */
1471  if (resultRelInfo->ri_NumIndices > 0 && update_indexes)
1472  recheckIndexes = ExecInsertIndexTuples(slot, estate, false, NULL, NIL);
1473  }
1474 
1475  if (canSetTag)
1476  (estate->es_processed)++;
1477 
1478  /* AFTER ROW UPDATE Triggers */
1479  ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, slot,
1480  recheckIndexes,
1481  mtstate->operation == CMD_INSERT ?
1482  mtstate->mt_oc_transition_capture :
1483  mtstate->mt_transition_capture);
1484 
1485  list_free(recheckIndexes);
1486 
1487  /*
1488  * Check any WITH CHECK OPTION constraints from parent views. We are
1489  * required to do this after testing all constraints and uniqueness
1490  * violations per the SQL spec, so we do it after actually updating the
1491  * record in the heap and all indexes.
1492  *
1493  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1494  * are looking for at this point.
1495  */
1496  if (resultRelInfo->ri_WithCheckOptions != NIL)
1497  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1498 
1499  /* Process RETURNING if present */
1500  if (resultRelInfo->ri_projectReturning)
1501  return ExecProcessReturning(resultRelInfo, slot, planSlot);
1502 
1503  return NULL;
1504 }
int ri_NumIndices
Definition: execnodes.h:414
#define NIL
Definition: pg_list.h:65
Oid tts_tableOid
Definition: tuptable.h:131
JunkFilter * ri_junkFilter
Definition: execnodes.h:464
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1192
static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool processReturning, bool canSetTag, bool changingPart, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
Relation ri_RelationDesc
Definition: execnodes.h:411
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1069
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2027
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
CommandId es_output_cid
Definition: execnodes.h:520
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1186
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1172
void ExecComputeStoredGenerated(EState *estate, TupleTableSlot *slot, CmdType cmdtype)
CommandId cmax
Definition: tableam.h:126
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1896
static TupleConversionMap * tupconv_map_for_subplan(ModifyTableState *node, int whichplan)
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:507
int errcode(int sqlerrcode)
Definition: elog.c:608
CmdType operation
Definition: execnodes.h:1164
Snapshot es_snapshot
Definition: execnodes.h:506
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1173
Index ri_RangeTableIndex
Definition: execnodes.h:408
bool has_generated_stored
Definition: tupdesc.h:45
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1163
TupleConversionMap * tcs_map
Definition: trigger.h:73
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:1333
List * ExecInsertIndexTuples(TupleTableSlot *slot, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:273
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition: execMain.c:2540
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1189
TupleConstr * constr
Definition: tupdesc.h:85
int errdetail(const char *fmt,...)
Definition: elog.c:955
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1828
bool trig_update_before_row
Definition: reltrigger.h:60
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2431
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:470
#define TupIsNull(slot)
Definition: tuptable.h:292
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:440
#define ereport(elevel, rest)
Definition: elog.h:141
AttrMap * attrMap
Definition: tupconvert.h:27
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:423
bool trig_update_instead_row
Definition: reltrigger.h:62
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:3127
static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot)
TM_Result
Definition: tableam.h:69
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:261
List * ri_WithCheckOptions
Definition: execnodes.h:449
List * ri_PartitionCheck
Definition: execnodes.h:479
TupleDesc rd_att
Definition: rel.h:85
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
Plan * plan
Definition: execnodes.h:943
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:212
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
#define Assert(condition)
Definition: c.h:738
Definition: tableam.h:75
uint64 es_processed
Definition: execnodes.h:557
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:140
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1183
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:372
int errmsg(const char *fmt,...)
Definition: elog.c:822
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:3169
void list_free(List *list)
Definition: list.c:1377
#define elog(elevel,...)
Definition: elog.h:228
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2996
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1783
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:1288
bool traversed
Definition: tableam.h:127
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:428
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:82
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:525

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 1790 of file nodeModifyTable.c.

References CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, ExecASDeleteTriggers(), ExecASInsertTriggers(), ExecASUpdateTriggers(), getTargetResultRelInfo(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, and PlanState::state.

Referenced by ExecModifyTable().

1791 {
1792  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1793  ResultRelInfo *resultRelInfo = getTargetResultRelInfo(node);
1794 
1795  switch (node->operation)
1796  {
1797  case CMD_INSERT:
1798  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1800  resultRelInfo,
1801  node->mt_oc_transition_capture);
1802  ExecASInsertTriggers(node->ps.state, resultRelInfo,
1803  node->mt_transition_capture);
1804  break;
1805  case CMD_UPDATE:
1806  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
1807  node->mt_transition_capture);
1808  break;
1809  case CMD_DELETE:
1810  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
1811  node->mt_transition_capture);
1812  break;
1813  default:
1814  elog(ERROR, "unknown operation");
1815  break;
1816  }
1817 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1192
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
CmdType operation
Definition: execnodes.h:1164
EState * state
Definition: execnodes.h:945
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2731
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1163
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1189
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2983
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2514
Plan * plan
Definition: execnodes.h:943
OnConflictAction onConflictAction
Definition: plannodes.h:236
#define elog(elevel,...)
Definition: elog.h:228

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 1731 of file nodeModifyTable.c.

References CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, ExecBSDeleteTriggers(), ExecBSInsertTriggers(), ExecBSUpdateTriggers(), ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, ModifyTableState::resultRelInfo, ModifyTableState::rootResultRelInfo, and PlanState::state.

Referenced by ExecModifyTable().

1732 {
1733  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1734  ResultRelInfo *resultRelInfo = node->resultRelInfo;
1735 
1736  /*
1737  * If the node modifies a partitioned table, we must fire its triggers.
1738  * Note that in that case, node->resultRelInfo points to the first leaf
1739  * partition, not the root table.
1740  */
1741  if (node->rootResultRelInfo != NULL)
1742  resultRelInfo = node->rootResultRelInfo;
1743 
1744  switch (node->operation)
1745  {
1746  case CMD_INSERT:
1747  ExecBSInsertTriggers(node->ps.state, resultRelInfo);
1748  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1750  resultRelInfo);
1751  break;
1752  case CMD_UPDATE:
1753  ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
1754  break;
1755  case CMD_DELETE:
1756  ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
1757  break;
1758  default:
1759  elog(ERROR, "unknown operation");
1760  break;
1761  }
1762 }
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2674
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1172
CmdType operation
Definition: execnodes.h:1164
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1173
EState * state
Definition: execnodes.h:945
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1163
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2457
Plan * plan
Definition: execnodes.h:943
OnConflictAction onConflictAction
Definition: plannodes.h:236
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2923
#define elog(elevel,...)
Definition: elog.h:228

◆ getTargetResultRelInfo()

static ResultRelInfo * getTargetResultRelInfo ( ModifyTableState node)
static

Definition at line 1774 of file nodeModifyTable.c.

References ModifyTableState::resultRelInfo, and ModifyTableState::rootResultRelInfo.

Referenced by ExecInitModifyTable(), ExecSetupChildParentMapForSubplan(), ExecSetupTransitionCaptureState(), and fireASTriggers().

1775 {
1776  /*
1777  * Note that if the node modifies a partitioned table, node->resultRelInfo
1778  * points to the first leaf partition, not the root table.
1779  */
1780  if (node->rootResultRelInfo != NULL)
1781  return node->rootResultRelInfo;
1782  else
1783  return node->resultRelInfo;
1784 }
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1172
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1173

◆ tupconv_map_for_subplan()

static TupleConversionMap * tupconv_map_for_subplan ( ModifyTableState node,
int  whichplan 
)
static

Definition at line 1991 of file nodeModifyTable.c.

References Assert, ExecSetupChildParentMapForSubplan(), and ModifyTableState::mt_per_subplan_tupconv_maps.

Referenced by ExecModifyTable(), ExecSetupTransitionCaptureState(), and ExecUpdate().

1992 {
1993  /* If nobody else set the per-subplan array of maps, do so ourselves. */
1994  if (mtstate->mt_per_subplan_tupconv_maps == NULL)
1996 
1997  Assert(whichplan >= 0 && whichplan < mtstate->mt_nplans);
1998  return mtstate->mt_per_subplan_tupconv_maps[whichplan];
1999 }
static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
#define Assert(condition)
Definition: c.h:738