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, ResultRelInfo **partRelInfo)
 
static void ExecCheckPlanOutput (Relation resultRel, List *targetList)
 
static TupleTableSlotExecProcessReturning (ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
 
static void ExecCheckTupleVisible (EState *estate, Relation rel, TupleTableSlot *slot)
 
static void ExecCheckTIDVisible (EState *estate, ResultRelInfo *relinfo, ItemPointer tid, TupleTableSlot *tempSlot)
 
void ExecComputeStoredGenerated (ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
 
static TupleTableSlotExecInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
 
static TupleTableSlotExecDelete (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool processReturning, bool canSetTag, bool changingPart, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
 
static bool ExecCrossPartitionUpdate (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, bool canSetTag, TupleTableSlot **retry_slot, TupleTableSlot **inserted_tuple)
 
static TupleTableSlotExecUpdate (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, 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 89 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().

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

◆ ExecCheckTIDVisible()

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

Definition at line 225 of file nodeModifyTable.c.

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

Referenced by ExecInsert().

229 {
230  Relation rel = relinfo->ri_RelationDesc;
231 
232  /* Redundantly check isolation level */
234  return;
235 
236  if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
237  elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
238  ExecCheckTupleVisible(estate, rel, tempSlot);
239  ExecClearTuple(tempSlot);
240 }
Relation ri_RelationDesc
Definition: execnodes.h:412
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:1071
#define SnapshotAny
Definition: snapmgr.h:68
#define elog(elevel,...)
Definition: elog.h:214

◆ ExecCheckTupleVisible()

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

Definition at line 191 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().

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

◆ ExecComputeStoredGenerated()

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

Definition at line 246 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, 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().

249 {
250  Relation rel = resultRelInfo->ri_RelationDesc;
251  TupleDesc tupdesc = RelationGetDescr(rel);
252  int natts = tupdesc->natts;
253  MemoryContext oldContext;
254  Datum *values;
255  bool *nulls;
256 
257  Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
258 
259  /*
260  * If first time through for this result relation, build expression
261  * nodetrees for rel's stored generation expressions. Keep them in the
262  * per-query memory context so they'll survive throughout the query.
263  */
264  if (resultRelInfo->ri_GeneratedExprs == NULL)
265  {
266  oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
267 
268  resultRelInfo->ri_GeneratedExprs =
269  (ExprState **) palloc(natts * sizeof(ExprState *));
270  resultRelInfo->ri_NumGeneratedNeeded = 0;
271 
272  for (int i = 0; i < natts; i++)
273  {
274  if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
275  {
276  Expr *expr;
277 
278  /*
279  * If it's an update and the current column was not marked as
280  * being updated, then we can skip the computation. But if
281  * there is a BEFORE ROW UPDATE trigger, we cannot skip
282  * because the trigger might affect additional columns.
283  */
284  if (cmdtype == CMD_UPDATE &&
285  !(rel->trigdesc && rel->trigdesc->trig_update_before_row) &&
287  exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, estate)->extraUpdatedCols))
288  {
289  resultRelInfo->ri_GeneratedExprs[i] = NULL;
290  continue;
291  }
292 
293  expr = (Expr *) build_column_default(rel, i + 1);
294  if (expr == NULL)
295  elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
296  i + 1, RelationGetRelationName(rel));
297 
298  resultRelInfo->ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
299  resultRelInfo->ri_NumGeneratedNeeded++;
300  }
301  }
302 
303  MemoryContextSwitchTo(oldContext);
304  }
305 
306  /*
307  * If no generated columns have been affected by this change, then skip
308  * the rest.
309  */
310  if (resultRelInfo->ri_NumGeneratedNeeded == 0)
311  return;
312 
313  oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
314 
315  values = palloc(sizeof(*values) * natts);
316  nulls = palloc(sizeof(*nulls) * natts);
317 
318  slot_getallattrs(slot);
319  memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
320 
321  for (int i = 0; i < natts; i++)
322  {
323  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
324 
325  if (attr->attgenerated == ATTRIBUTE_GENERATED_STORED &&
326  resultRelInfo->ri_GeneratedExprs[i])
327  {
328  ExprContext *econtext;
329  Datum val;
330  bool isnull;
331 
332  econtext = GetPerTupleExprContext(estate);
333  econtext->ecxt_scantuple = slot;
334 
335  val = ExecEvalExpr(resultRelInfo->ri_GeneratedExprs[i], econtext, &isnull);
336 
337  /*
338  * We must make a copy of val as we have no guarantees about where
339  * memory for a pass-by-reference Datum is located.
340  */
341  if (!isnull)
342  val = datumCopy(val, attr->attbyval, attr->attlen);
343 
344  values[i] = val;
345  nulls[i] = isnull;
346  }
347  else
348  {
349  if (!nulls[i])
350  values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
351  }
352  }
353 
354  ExecClearTuple(slot);
355  memcpy(slot->tts_values, values, sizeof(*values) * natts);
356  memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
357  ExecStoreVirtualTuple(slot);
358  ExecMaterializeSlot(slot);
359 
360  MemoryContextSwitchTo(oldContext);
361 }
Relation ri_RelationDesc
Definition: execnodes.h:412
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define RelationGetDescr(relation)
Definition: rel.h:482
#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:492
Index ri_RangeTableIndex
Definition: execnodes.h:409
#define GetPerTupleExprContext(estate)
Definition: executor.h:506
bool has_generated_stored
Definition: tupdesc.h:45
Bitmapset * extraUpdatedCols
Definition: parsenodes.h:1125
MemoryContext es_query_cxt
Definition: execnodes.h:559
#define ERROR
Definition: elog.h:43
TriggerDesc * trigdesc
Definition: rel.h:115
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:289
TupleConstr * constr
Definition: tupdesc.h:85
ExprState ** ri_GeneratedExprs
Definition: execnodes.h:459
bool trig_update_before_row
Definition: reltrigger.h:61
#define RelationGetRelationName(relation)
Definition: rel.h:490
static RangeTblEntry * exec_rt_fetch(Index rti, EState *estate)
Definition: executor.h:544
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:193
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:131
int ri_NumGeneratedNeeded
Definition: execnodes.h:462
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:746
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:225
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:511
static Datum values[MAXATTR]
Definition: bootstrap.c:165
void * palloc(Size size)
Definition: mcxt.c:950
#define elog(elevel,...)
Definition: elog.h:214
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

◆ ExecCrossPartitionUpdate()

static bool ExecCrossPartitionUpdate ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo,
ItemPointer  tupleid,
HeapTuple  oldtuple,
TupleTableSlot slot,
TupleTableSlot planSlot,
EPQState epqstate,
bool  canSetTag,
TupleTableSlot **  retry_slot,
TupleTableSlot **  inserted_tuple 
)
static

Definition at line 1076 of file nodeModifyTable.c.

References TupleConversionMap::attrMap, ereport, errcode(), errdetail(), errmsg(), ERROR, ExecDelete(), ExecFilterJunk(), ExecInsert(), ExecPartitionCheckEmitError(), execute_attr_map_slot(), ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, PlanState::plan, ModifyTableState::ps, ResultRelInfo::ri_ChildToRootMap, ResultRelInfo::ri_junkFilter, ModifyTableState::rootResultRelInfo, PlanState::state, TransitionCaptureState::tcs_original_insert_tuple, and TupIsNull.

Referenced by ExecUpdate().

1083 {
1084  EState *estate = mtstate->ps.state;
1086  TupleConversionMap *tupconv_map;
1087  bool tuple_deleted;
1088  TupleTableSlot *epqslot = NULL;
1089 
1090  *inserted_tuple = NULL;
1091  *retry_slot = NULL;
1092 
1093  /*
1094  * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1095  * to migrate to a different partition. Maybe this can be implemented
1096  * some day, but it seems a fringe feature with little redeeming value.
1097  */
1098  if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1099  ereport(ERROR,
1100  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1101  errmsg("invalid ON UPDATE specification"),
1102  errdetail("The result tuple would appear in a different partition than the original tuple.")));
1103 
1104  /*
1105  * When an UPDATE is run on a leaf partition, we will not have partition
1106  * tuple routing set up. In that case, fail with partition constraint
1107  * violation error.
1108  */
1109  if (proute == NULL)
1110  ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1111 
1112  /*
1113  * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
1114  * We want to return rows from INSERT.
1115  */
1116  ExecDelete(mtstate, resultRelInfo, tupleid, oldtuple, planSlot,
1117  epqstate, estate,
1118  false, /* processReturning */
1119  false, /* canSetTag */
1120  true, /* changingPart */
1121  &tuple_deleted, &epqslot);
1122 
1123  /*
1124  * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
1125  * it was already deleted by self, or it was concurrently deleted by
1126  * another transaction), then we should skip the insert as well;
1127  * otherwise, an UPDATE could cause an increase in the total number of
1128  * rows across all partitions, which is clearly wrong.
1129  *
1130  * For a normal UPDATE, the case where the tuple has been the subject of a
1131  * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
1132  * machinery, but for an UPDATE that we've translated into a DELETE from
1133  * this partition and an INSERT into some other partition, that's not
1134  * available, because CTID chains can't span relation boundaries. We
1135  * mimic the semantics to a limited extent by skipping the INSERT if the
1136  * DELETE fails to find a tuple. This ensures that two concurrent
1137  * attempts to UPDATE the same tuple at the same time can't turn one tuple
1138  * into two, and that an UPDATE of a just-deleted tuple can't resurrect
1139  * it.
1140  */
1141  if (!tuple_deleted)
1142  {
1143  /*
1144  * epqslot will be typically NULL. But when ExecDelete() finds that
1145  * another transaction has concurrently updated the same row, it
1146  * re-fetches the row, skips the delete, and epqslot is set to the
1147  * re-fetched tuple slot. In that case, we need to do all the checks
1148  * again.
1149  */
1150  if (TupIsNull(epqslot))
1151  return true;
1152  else
1153  {
1154  *retry_slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1155  return false;
1156  }
1157  }
1158 
1159  /*
1160  * resultRelInfo is one of the per-subplan resultRelInfos. So we should
1161  * convert the tuple into root's tuple descriptor if needed, since
1162  * ExecInsert() starts the search from root.
1163  */
1164  tupconv_map = resultRelInfo->ri_ChildToRootMap;
1165  if (tupconv_map != NULL)
1166  slot = execute_attr_map_slot(tupconv_map->attrMap,
1167  slot,
1168  mtstate->mt_root_tuple_slot);
1169 
1170  /* Tuple routing starts from the root table. */
1171  *inserted_tuple = ExecInsert(mtstate, mtstate->rootResultRelInfo, slot,
1172  planSlot, estate, canSetTag);
1173 
1174  /*
1175  * Reset the transition state that may possibly have been written by
1176  * INSERT.
1177  */
1178  if (mtstate->mt_transition_capture)
1180 
1181  /* We're done moving. */
1182  return true;
1183 }
JunkFilter * ri_junkFilter
Definition: execnodes.h:465
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1187
TupleConversionMap * ri_ChildToRootMap
Definition: execnodes.h:499
int errcode(int sqlerrcode)
Definition: elog.c:610
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1174
EState * state
Definition: execnodes.h:941
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1159
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1190
int errdetail(const char *fmt,...)
Definition: elog.c:954
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1732
#define TupIsNull(slot)
Definition: tuptable.h:292
AttrMap * attrMap
Definition: tupconvert.h:27
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:261
Plan * plan
Definition: execnodes.h:939
#define ereport(elevel,...)
Definition: elog.h:144
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1184
int errmsg(const char *fmt,...)
Definition: elog.c:821
static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool processReturning, bool canSetTag, bool changingPart, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:75

◆ ExecDelete()

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

Definition at line 727 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_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 ExecCrossPartitionUpdate(), and ExecModifyTable().

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

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 2671 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().

2672 {
2673  int i;
2674 
2675  /*
2676  * Allow any FDWs to shut down
2677  */
2678  for (i = 0; i < node->mt_nplans; i++)
2679  {
2680  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
2681 
2682  if (!resultRelInfo->ri_usesFdwDirectModify &&
2683  resultRelInfo->ri_FdwRoutine != NULL &&
2684  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
2685  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
2686  resultRelInfo);
2687  }
2688 
2689  /*
2690  * Close all the partitioned tables, leaf partitions, and their indices
2691  * and release the slot used for tuple routing, if set.
2692  */
2693  if (node->mt_partition_tuple_routing)
2694  {
2696 
2697  if (node->mt_root_tuple_slot)
2699  }
2700 
2701  /*
2702  * Free the exprcontext
2703  */
2704  ExecFreeExprContext(&node->ps);
2705 
2706  /*
2707  * clean out the tuple table
2708  */
2709  if (node->ps.ps_ResultTupleSlot)
2711 
2712  /*
2713  * Terminate EPQ execution if active
2714  */
2715  EvalPlanQualEnd(&node->mt_epqstate);
2716 
2717  /*
2718  * shut down subplans
2719  */
2720  for (i = 0; i < node->mt_nplans; i++)
2721  ExecEndNode(node->mt_plans[i]);
2722 }
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1187
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:543
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1168
EState * state
Definition: execnodes.h:941
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:649
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2810
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:977
PlanState ps
Definition: execnodes.h:1159
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
bool ri_usesFdwDirectModify
Definition: execnodes.h:447
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
EPQState mt_epqstate
Definition: execnodes.h:1177
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:441
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:214
PlanState ** mt_plans
Definition: execnodes.h:1163
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1184
int i

◆ ExecInitModifyTable()

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

Definition at line 2173 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, convert_tuples_by_name(), elog, ModifyTable::epqParam, ERROR, EState::es_auxmodifytables, 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(), ExecInitResultRelation(), ExecInitResultTupleSlotTL(), ExecInitResultTypeTL(), ExecModifyTable(), ExecOpenIndices(), PlanState::ExecProcNode, ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), ExecTypeFromTL(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, forboth, i, PlanRowMark::isParent, JunkFilter::jf_junkAttNo, lappend(), lcons(), lfirst, lfirst_int, lfirst_node, linitial, linitial_int, 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_transition_capture, 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, palloc(), palloc0(), ModifyTable::partColsUpdated, PlanState::plan, ModifyTable::plans, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationData::rd_att, RelationData::rd_rel, RelationGetDescr, TargetEntry::resjunk, ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_ChildToRootMap, 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::rootRelation, 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().

2174 {
2175  ModifyTableState *mtstate;
2176  CmdType operation = node->operation;
2177  int nplans = list_length(node->plans);
2178  ResultRelInfo *resultRelInfo;
2179  Plan *subplan;
2180  ListCell *l,
2181  *l1;
2182  int i;
2183  Relation rel;
2184  bool update_tuple_routing_needed = node->partColsUpdated;
2185 
2186  /* check for unsupported flags */
2187  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2188 
2189  /*
2190  * create state structure
2191  */
2192  mtstate = makeNode(ModifyTableState);
2193  mtstate->ps.plan = (Plan *) node;
2194  mtstate->ps.state = estate;
2195  mtstate->ps.ExecProcNode = ExecModifyTable;
2196 
2197  mtstate->operation = operation;
2198  mtstate->canSetTag = node->canSetTag;
2199  mtstate->mt_done = false;
2200 
2201  mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
2202  mtstate->resultRelInfo = (ResultRelInfo *)
2203  palloc(nplans * sizeof(ResultRelInfo));
2204  mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
2205 
2206  /*----------
2207  * Resolve the target relation. This is the same as:
2208  *
2209  * - the relation for which we will fire FOR STATEMENT triggers,
2210  * - the relation into whose tuple format all captured transition tuples
2211  * must be converted, and
2212  * - the root partitioned table used for tuple routing.
2213  *
2214  * If it's a partitioned table, the root partition doesn't appear
2215  * elsewhere in the plan and its RT index is given explicitly in
2216  * node->rootRelation. Otherwise (i.e. table inheritance) the target
2217  * relation is the first relation in the node->resultRelations list.
2218  *----------
2219  */
2220  if (node->rootRelation > 0)
2221  {
2223  ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
2224  node->rootRelation);
2225  }
2226  else
2227  {
2228  mtstate->rootResultRelInfo = mtstate->resultRelInfo;
2229  ExecInitResultRelation(estate, mtstate->resultRelInfo,
2230  linitial_int(node->resultRelations));
2231  }
2232 
2233  mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
2234  mtstate->mt_nplans = nplans;
2235 
2236  /* set up epqstate with dummy subplan data for the moment */
2237  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
2238  mtstate->fireBSTriggers = true;
2239 
2240  /*
2241  * Build state for collecting transition tuples. This requires having a
2242  * valid trigger query context, so skip it in explain-only mode.
2243  */
2244  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
2245  ExecSetupTransitionCaptureState(mtstate, estate);
2246 
2247  /*
2248  * call ExecInitNode on each of the plans to be executed and save the
2249  * results into the array "mt_plans". This is also a convenient place to
2250  * verify that the proposed target relations are valid and open their
2251  * indexes for insertion of new index entries.
2252  */
2253  resultRelInfo = mtstate->resultRelInfo;
2254  i = 0;
2255  forboth(l, node->resultRelations, l1, node->plans)
2256  {
2257  Index resultRelation = lfirst_int(l);
2258 
2259  subplan = (Plan *) lfirst(l1);
2260 
2261  /*
2262  * This opens result relation and fills ResultRelInfo. (root relation
2263  * was initialized already.)
2264  */
2265  if (resultRelInfo != mtstate->rootResultRelInfo)
2266  ExecInitResultRelation(estate, resultRelInfo, resultRelation);
2267 
2268  /* Initialize the usesFdwDirectModify flag */
2269  resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
2270  node->fdwDirectModifyPlans);
2271 
2272  /*
2273  * Verify result relation is a valid target for the current operation
2274  */
2275  CheckValidResultRel(resultRelInfo, operation);
2276 
2277  /*
2278  * If there are indices on the result relation, open them and save
2279  * descriptors in the result relation info, so that we can add new
2280  * index entries for the tuples we add/update. We need not do this
2281  * for a DELETE, however, since deletion doesn't affect indexes. Also,
2282  * inside an EvalPlanQual operation, the indexes might be open
2283  * already, since we share the resultrel state with the original
2284  * query.
2285  */
2286  if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
2287  operation != CMD_DELETE &&
2288  resultRelInfo->ri_IndexRelationDescs == NULL)
2289  ExecOpenIndices(resultRelInfo,
2291 
2292  /*
2293  * If this is an UPDATE and a BEFORE UPDATE trigger is present, the
2294  * trigger itself might modify the partition-key values. So arrange
2295  * for tuple routing.
2296  */
2297  if (resultRelInfo->ri_TrigDesc &&
2298  resultRelInfo->ri_TrigDesc->trig_update_before_row &&
2299  operation == CMD_UPDATE)
2300  update_tuple_routing_needed = true;
2301 
2302  /* Now init the plan for this result rel */
2303  mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
2304  mtstate->mt_scans[i] =
2305  ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[i]),
2306  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2307 
2308  /* Also let FDWs init themselves for foreign-table result rels */
2309  if (!resultRelInfo->ri_usesFdwDirectModify &&
2310  resultRelInfo->ri_FdwRoutine != NULL &&
2311  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
2312  {
2313  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
2314 
2315  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
2316  resultRelInfo,
2317  fdw_private,
2318  i,
2319  eflags);
2320  }
2321 
2322  /*
2323  * If needed, initialize a map to convert tuples in the child format
2324  * to the format of the table mentioned in the query (root relation).
2325  * It's needed for update tuple routing, because the routing starts
2326  * from the root relation. It's also needed for capturing transition
2327  * tuples, because the transition tuple store can only store tuples in
2328  * the root table format.
2329  *
2330  * For INSERT, the map is only initialized for a given partition when
2331  * the partition itself is first initialized by ExecFindPartition().
2332  */
2333  if (update_tuple_routing_needed ||
2334  (mtstate->mt_transition_capture &&
2335  mtstate->operation != CMD_INSERT))
2336  resultRelInfo->ri_ChildToRootMap =
2339  resultRelInfo++;
2340  i++;
2341  }
2342 
2343  /* Get the target relation */
2344  rel = mtstate->rootResultRelInfo->ri_RelationDesc;
2345 
2346  /*
2347  * If it's not a partitioned table after all, UPDATE tuple routing should
2348  * not be attempted.
2349  */
2350  if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2351  update_tuple_routing_needed = false;
2352 
2353  /*
2354  * Build state for tuple routing if it's an INSERT or if it's an UPDATE of
2355  * partition key.
2356  */
2357  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2358  (operation == CMD_INSERT || update_tuple_routing_needed))
2359  mtstate->mt_partition_tuple_routing =
2360  ExecSetupPartitionTupleRouting(estate, mtstate, rel);
2361 
2362  /*
2363  * For update row movement we'll need a dedicated slot to store the tuples
2364  * that have been converted from partition format to the root table
2365  * format.
2366  */
2367  if (update_tuple_routing_needed)
2368  mtstate->mt_root_tuple_slot = table_slot_create(rel, NULL);
2369 
2370  /*
2371  * Initialize any WITH CHECK OPTION constraints if needed.
2372  */
2373  resultRelInfo = mtstate->resultRelInfo;
2374  i = 0;
2375  foreach(l, node->withCheckOptionLists)
2376  {
2377  List *wcoList = (List *) lfirst(l);
2378  List *wcoExprs = NIL;
2379  ListCell *ll;
2380 
2381  foreach(ll, wcoList)
2382  {
2383  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
2384  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
2385  &mtstate->ps);
2386 
2387  wcoExprs = lappend(wcoExprs, wcoExpr);
2388  }
2389 
2390  resultRelInfo->ri_WithCheckOptions = wcoList;
2391  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
2392  resultRelInfo++;
2393  i++;
2394  }
2395 
2396  /*
2397  * Initialize RETURNING projections if needed.
2398  */
2399  if (node->returningLists)
2400  {
2401  TupleTableSlot *slot;
2402  ExprContext *econtext;
2403 
2404  /*
2405  * Initialize result tuple slot and assign its rowtype using the first
2406  * RETURNING list. We assume the rest will look the same.
2407  */
2408  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
2409 
2410  /* Set up a slot for the output of the RETURNING projection(s) */
2412  slot = mtstate->ps.ps_ResultTupleSlot;
2413 
2414  /* Need an econtext too */
2415  if (mtstate->ps.ps_ExprContext == NULL)
2416  ExecAssignExprContext(estate, &mtstate->ps);
2417  econtext = mtstate->ps.ps_ExprContext;
2418 
2419  /*
2420  * Build a projection for each result rel.
2421  */
2422  resultRelInfo = mtstate->resultRelInfo;
2423  foreach(l, node->returningLists)
2424  {
2425  List *rlist = (List *) lfirst(l);
2426 
2427  resultRelInfo->ri_returningList = rlist;
2428  resultRelInfo->ri_projectReturning =
2429  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
2430  resultRelInfo->ri_RelationDesc->rd_att);
2431  resultRelInfo++;
2432  }
2433  }
2434  else
2435  {
2436  /*
2437  * We still must construct a dummy result tuple type, because InitPlan
2438  * expects one (maybe should change that?).
2439  */
2440  mtstate->ps.plan->targetlist = NIL;
2441  ExecInitResultTypeTL(&mtstate->ps);
2442 
2443  mtstate->ps.ps_ExprContext = NULL;
2444  }
2445 
2446  /* Set the list of arbiter indexes if needed for ON CONFLICT */
2447  resultRelInfo = mtstate->resultRelInfo;
2448  if (node->onConflictAction != ONCONFLICT_NONE)
2449  resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
2450 
2451  /*
2452  * If needed, Initialize target list, projection and qual for ON CONFLICT
2453  * DO UPDATE.
2454  */
2455  if (node->onConflictAction == ONCONFLICT_UPDATE)
2456  {
2457  ExprContext *econtext;
2458  TupleDesc relationDesc;
2459  TupleDesc tupDesc;
2460 
2461  /* insert may only have one plan, inheritance is not expanded */
2462  Assert(nplans == 1);
2463 
2464  /* already exists if created by RETURNING processing above */
2465  if (mtstate->ps.ps_ExprContext == NULL)
2466  ExecAssignExprContext(estate, &mtstate->ps);
2467 
2468  econtext = mtstate->ps.ps_ExprContext;
2469  relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
2470 
2471  /* create state for DO UPDATE SET operation */
2472  resultRelInfo->ri_onConflict = makeNode(OnConflictSetState);
2473 
2474  /* initialize slot for the existing tuple */
2475  resultRelInfo->ri_onConflict->oc_Existing =
2476  table_slot_create(resultRelInfo->ri_RelationDesc,
2477  &mtstate->ps.state->es_tupleTable);
2478 
2479  /*
2480  * Create the tuple slot for the UPDATE SET projection. We want a slot
2481  * of the table's type here, because the slot will be used to insert
2482  * into the table, and for RETURNING processing - which may access
2483  * system attributes.
2484  */
2485  tupDesc = ExecTypeFromTL((List *) node->onConflictSet);
2486  resultRelInfo->ri_onConflict->oc_ProjSlot =
2487  ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
2488  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2489 
2490  /* build UPDATE SET projection state */
2491  resultRelInfo->ri_onConflict->oc_ProjInfo =
2492  ExecBuildProjectionInfo(node->onConflictSet, econtext,
2493  resultRelInfo->ri_onConflict->oc_ProjSlot,
2494  &mtstate->ps,
2495  relationDesc);
2496 
2497  /* initialize state to evaluate the WHERE clause, if any */
2498  if (node->onConflictWhere)
2499  {
2500  ExprState *qualexpr;
2501 
2502  qualexpr = ExecInitQual((List *) node->onConflictWhere,
2503  &mtstate->ps);
2504  resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
2505  }
2506  }
2507 
2508  /*
2509  * If we have any secondary relations in an UPDATE or DELETE, they need to
2510  * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
2511  * EvalPlanQual mechanism needs to be told about them. Locate the
2512  * relevant ExecRowMarks.
2513  */
2514  foreach(l, node->rowMarks)
2515  {
2517  ExecRowMark *erm;
2518 
2519  /* ignore "parent" rowmarks; they are irrelevant at runtime */
2520  if (rc->isParent)
2521  continue;
2522 
2523  /* find ExecRowMark (same for all subplans) */
2524  erm = ExecFindRowMark(estate, rc->rti, false);
2525 
2526  /* build ExecAuxRowMark for each subplan */
2527  for (i = 0; i < nplans; i++)
2528  {
2529  ExecAuxRowMark *aerm;
2530 
2531  subplan = mtstate->mt_plans[i]->plan;
2532  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
2533  mtstate->mt_arowmarks[i] = lappend(mtstate->mt_arowmarks[i], aerm);
2534  }
2535  }
2536 
2537  /* select first subplan */
2538  mtstate->mt_whichplan = 0;
2539  subplan = (Plan *) linitial(node->plans);
2540  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan,
2541  mtstate->mt_arowmarks[0]);
2542 
2543  /*
2544  * Initialize the junk filter(s) if needed. INSERT queries need a filter
2545  * if there are any junk attrs in the tlist. UPDATE and DELETE always
2546  * need a filter, since there's always at least one junk attribute present
2547  * --- no need to look first. Typically, this will be a 'ctid' or
2548  * 'wholerow' attribute, but in the case of a foreign data wrapper it
2549  * might be a set of junk attributes sufficient to identify the remote
2550  * row.
2551  *
2552  * If there are multiple result relations, each one needs its own junk
2553  * filter. Note multiple rels are only possible for UPDATE/DELETE, so we
2554  * can't be fooled by some needing a filter and some not.
2555  *
2556  * This section of code is also a convenient place to verify that the
2557  * output of an INSERT or UPDATE matches the target table(s).
2558  */
2559  {
2560  bool junk_filter_needed = false;
2561 
2562  switch (operation)
2563  {
2564  case CMD_INSERT:
2565  foreach(l, subplan->targetlist)
2566  {
2567  TargetEntry *tle = (TargetEntry *) lfirst(l);
2568 
2569  if (tle->resjunk)
2570  {
2571  junk_filter_needed = true;
2572  break;
2573  }
2574  }
2575  break;
2576  case CMD_UPDATE:
2577  case CMD_DELETE:
2578  junk_filter_needed = true;
2579  break;
2580  default:
2581  elog(ERROR, "unknown operation");
2582  break;
2583  }
2584 
2585  if (junk_filter_needed)
2586  {
2587  resultRelInfo = mtstate->resultRelInfo;
2588  for (i = 0; i < nplans; i++)
2589  {
2590  JunkFilter *j;
2591  TupleTableSlot *junkresslot;
2592 
2593  subplan = mtstate->mt_plans[i]->plan;
2594  if (operation == CMD_INSERT || operation == CMD_UPDATE)
2595  ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
2596  subplan->targetlist);
2597 
2598  junkresslot =
2599  ExecInitExtraTupleSlot(estate, NULL,
2600  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2601  j = ExecInitJunkFilter(subplan->targetlist,
2602  junkresslot);
2603 
2604  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2605  {
2606  /* For UPDATE/DELETE, find the appropriate junk attr now */
2607  char relkind;
2608 
2609  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2610  if (relkind == RELKIND_RELATION ||
2611  relkind == RELKIND_MATVIEW ||
2612  relkind == RELKIND_PARTITIONED_TABLE)
2613  {
2614  j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
2616  elog(ERROR, "could not find junk ctid column");
2617  }
2618  else if (relkind == RELKIND_FOREIGN_TABLE)
2619  {
2620  /*
2621  * When there is a row-level trigger, there should be
2622  * a wholerow attribute.
2623  */
2624  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2625  }
2626  else
2627  {
2628  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2630  elog(ERROR, "could not find junk wholerow column");
2631  }
2632  }
2633 
2634  resultRelInfo->ri_junkFilter = j;
2635  resultRelInfo++;
2636  }
2637  }
2638  else
2639  {
2640  if (operation == CMD_INSERT)
2642  subplan->targetlist);
2643  }
2644  }
2645 
2646  /*
2647  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
2648  * to estate->es_auxmodifytables so that it will be run to completion by
2649  * ExecPostprocessPlan. (It'd actually work fine to add the primary
2650  * ModifyTable node too, but there's no need.) Note the use of lcons not
2651  * lappend: we need later-initialized ModifyTable nodes to be shut down
2652  * before earlier ones. This ensures that we don't throw away RETURNING
2653  * rows that need to be seen by a later CTE subplan.
2654  */
2655  if (!mtstate->canSetTag)
2656  estate->es_auxmodifytables = lcons(mtstate,
2657  estate->es_auxmodifytables);
2658 
2659  return mtstate;
2660 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
AttrNumber jf_junkAttNo
Definition: execnodes.h:372
#define NIL
Definition: pg_list.h:65
JunkFilter * ri_junkFilter
Definition: execnodes.h:465
List * arbiterIndexes
Definition: plannodes.h:229
Relation ri_RelationDesc
Definition: execnodes.h:412
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:225
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:434
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1801
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1187
#define RelationGetDescr(relation)
Definition: rel.h:482
List * withCheckOptionLists
Definition: plannodes.h:222
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:833
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1168
AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
Definition: execJunk.c:208
ExprContext * ps_ExprContext
Definition: execnodes.h:978
TupleTableSlot ** mt_scans
Definition: execnodes.h:1166
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:58
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
TupleConversionMap * ri_ChildToRootMap
Definition: execnodes.h:499
bool partColsUpdated
Definition: plannodes.h:219
bool canSetTag
Definition: plannodes.h:216
CmdType operation
Definition: execnodes.h:1160
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1174
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2428
EState * state
Definition: execnodes.h:941
Form_pg_class rd_rel
Definition: rel.h:109
List * plans
Definition: plannodes.h:221
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:209
List * onConflictSet
Definition: plannodes.h:230
Index rootRelation
Definition: plannodes.h:218
List * resultRelations
Definition: plannodes.h:220
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:151
List * ri_WithCheckOptionExprs
Definition: execnodes.h:453
#define linitial_int(l)
Definition: pg_list.h:175
TupleTableSlot * oc_Existing
Definition: execnodes.h:384
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:977
List * rowMarks
Definition: plannodes.h:226
bool resjunk
Definition: primnodes.h:1417
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1159
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:386
#define lfirst_int(lc)
Definition: pg_list.h:170
static void * list_nth(const List *list, int n)
Definition: pg_list.h:266
bool ri_usesFdwDirectModify
Definition: execnodes.h:447
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:995
#define EXEC_FLAG_BACKWARD
Definition: executor.h:58
#define lfirst_node(type, lc)
Definition: pg_list.h:172
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:102
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1190
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1725
List * fdwPrivLists
Definition: plannodes.h:224
EPQState mt_epqstate
Definition: execnodes.h:1177
bool trig_update_before_row
Definition: reltrigger.h:61
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:471
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:441
ExprState * oc_WhereClause
Definition: execnodes.h:387
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, ModifyTableState *mtstate, Relation rel)
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:424
List * lappend(List *list, void *datum)
Definition: list.c:321
PlanState ** mt_plans
Definition: execnodes.h:1163
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2390
OnConflictSetState * ri_onConflict
Definition: execnodes.h:477
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
List * es_tupleTable
Definition: execnodes.h:561
void * palloc0(Size size)
Definition: mcxt.c:981
List * es_auxmodifytables
Definition: execnodes.h:573
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:945
List * ri_WithCheckOptions
Definition: execnodes.h:450
unsigned int Index
Definition: c.h:483
TupleDesc rd_att
Definition: rel.h:110
Plan * plan
Definition: execnodes.h:939
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:385
List * lcons(void *datum, List *list)
Definition: list.c:453
#define makeNode(_type_)
Definition: nodes.h:576
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define Assert(condition)
Definition: c.h:746
#define lfirst(lc)
Definition: pg_list.h:169
#define EXEC_FLAG_MARK
Definition: executor.h:59
OnConflictAction onConflictAction
Definition: plannodes.h:228
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:479
static int list_length(const List *l)
Definition: pg_list.h:149
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:1184
TupleDesc ExecGetResultType(PlanState *planstate)
Definition: execUtils.c:489
List * targetlist
Definition: plannodes.h:136
void * palloc(Size size)
Definition: mcxt.c:950
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:353
CmdType operation
Definition: plannodes.h:215
#define elog(elevel,...)
Definition: elog.h:214
int i
List * returningLists
Definition: plannodes.h:223
bool isParent
Definition: plannodes.h:1084
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:210
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139
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:474
CmdType
Definition: nodes.h:671
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:418
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2266
List * ri_returningList
Definition: execnodes.h:468
List ** mt_arowmarks
Definition: execnodes.h:1176
int epqParam
Definition: plannodes.h:227
Node * onConflictWhere
Definition: plannodes.h:231
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2243

◆ ExecInsert()

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

Definition at line 378 of file nodeModifyTable.c.

References Assert, CMD_INSERT, CMD_UPDATE, TupleDescData::constr, EState::es_output_cid, EState::es_processed, ExecARInsertTriggers(), ExecARUpdateTriggers(), ExecBRInsertTriggers(), ExecCheckIndexConstraints(), ExecCheckTIDVisible(), ExecComputeStoredGenerated(), ExecConstraints(), FdwRoutine::ExecForeignInsert, ExecGetReturningSlot(), ExecInsertIndexTuples(), ExecIRInsertTriggers(), ExecMaterializeSlot(), ExecOnConflictUpdate(), ExecPartitionCheck(), ExecPrepareTupleRouting(), ExecProcessReturning(), ExecWithCheckOptions(), GetCurrentTransactionId(), TupleConstr::has_generated_stored, InstrCountTuples2, list_free(), ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_onConflictArbiterIndexes, 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 ExecCrossPartitionUpdate(), and ExecModifyTable().

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

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 1929 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, EvalPlanQualSetPlan(), EvalPlanQualSetSlot, ExecCopySlot(), ExecDelete(), ExecFilterJunk(), ExecGetJunkAttribute(), ExecInsert(), 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_plans, ModifyTableState::mt_scans, 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, TupleTableSlot::tts_ops, and TupIsNull.

Referenced by ExecInitModifyTable().

1930 {
1931  ModifyTableState *node = castNode(ModifyTableState, pstate);
1932  EState *estate = node->ps.state;
1933  CmdType operation = node->operation;
1934  ResultRelInfo *resultRelInfo;
1935  PlanState *subplanstate;
1936  JunkFilter *junkfilter;
1937  TupleTableSlot *slot;
1938  TupleTableSlot *planSlot;
1939  ItemPointer tupleid;
1940  ItemPointerData tuple_ctid;
1941  HeapTupleData oldtupdata;
1942  HeapTuple oldtuple;
1943 
1945 
1946  /*
1947  * This should NOT get called during EvalPlanQual; we should have passed a
1948  * subplan tree to EvalPlanQual, instead. Use a runtime test not just
1949  * Assert because this condition is easy to miss in testing. (Note:
1950  * although ModifyTable should not get executed within an EvalPlanQual
1951  * operation, we do have to allow it to be initialized and shut down in
1952  * case it is within a CTE subplan. Hence this test must be here, not in
1953  * ExecInitModifyTable.)
1954  */
1955  if (estate->es_epq_active != NULL)
1956  elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
1957 
1958  /*
1959  * If we've already completed processing, don't try to do more. We need
1960  * this test because ExecPostprocessPlan might call us an extra time, and
1961  * our subplan's nodes aren't necessarily robust against being called
1962  * extra times.
1963  */
1964  if (node->mt_done)
1965  return NULL;
1966 
1967  /*
1968  * On first call, fire BEFORE STATEMENT triggers before proceeding.
1969  */
1970  if (node->fireBSTriggers)
1971  {
1972  fireBSTriggers(node);
1973  node->fireBSTriggers = false;
1974  }
1975 
1976  /* Preload local variables */
1977  resultRelInfo = node->resultRelInfo + node->mt_whichplan;
1978  subplanstate = node->mt_plans[node->mt_whichplan];
1979  junkfilter = resultRelInfo->ri_junkFilter;
1980 
1981  /*
1982  * Fetch rows from subplan(s), and execute the required table modification
1983  * for each row.
1984  */
1985  for (;;)
1986  {
1987  /*
1988  * Reset the per-output-tuple exprcontext. This is needed because
1989  * triggers expect to use that context as workspace. It's a bit ugly
1990  * to do this below the top level of the plan, however. We might need
1991  * to rethink this later.
1992  */
1993  ResetPerTupleExprContext(estate);
1994 
1995  /*
1996  * Reset per-tuple memory context used for processing on conflict and
1997  * returning clauses, to free any expression evaluation storage
1998  * allocated in the previous cycle.
1999  */
2000  if (pstate->ps_ExprContext)
2002 
2003  planSlot = ExecProcNode(subplanstate);
2004 
2005  if (TupIsNull(planSlot))
2006  {
2007  /* advance to next subplan if any */
2008  node->mt_whichplan++;
2009  if (node->mt_whichplan < node->mt_nplans)
2010  {
2011  resultRelInfo++;
2012  subplanstate = node->mt_plans[node->mt_whichplan];
2013  junkfilter = resultRelInfo->ri_junkFilter;
2014  EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
2015  node->mt_arowmarks[node->mt_whichplan]);
2016  continue;
2017  }
2018  else
2019  break;
2020  }
2021 
2022  /*
2023  * Ensure input tuple is the right format for the target relation.
2024  */
2025  if (node->mt_scans[node->mt_whichplan]->tts_ops != planSlot->tts_ops)
2026  {
2027  ExecCopySlot(node->mt_scans[node->mt_whichplan], planSlot);
2028  planSlot = node->mt_scans[node->mt_whichplan];
2029  }
2030 
2031  /*
2032  * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
2033  * here is compute the RETURNING expressions.
2034  */
2035  if (resultRelInfo->ri_usesFdwDirectModify)
2036  {
2037  Assert(resultRelInfo->ri_projectReturning);
2038 
2039  /*
2040  * A scan slot containing the data that was actually inserted,
2041  * updated or deleted has already been made available to
2042  * ExecProcessReturning by IterateDirectModify, so no need to
2043  * provide it here.
2044  */
2045  slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
2046 
2047  return slot;
2048  }
2049 
2050  EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
2051  slot = planSlot;
2052 
2053  tupleid = NULL;
2054  oldtuple = NULL;
2055  if (junkfilter != NULL)
2056  {
2057  /*
2058  * extract the 'ctid' or 'wholerow' junk attribute.
2059  */
2060  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2061  {
2062  char relkind;
2063  Datum datum;
2064  bool isNull;
2065 
2066  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2067  if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW)
2068  {
2069  datum = ExecGetJunkAttribute(slot,
2070  junkfilter->jf_junkAttNo,
2071  &isNull);
2072  /* shouldn't ever get a null result... */
2073  if (isNull)
2074  elog(ERROR, "ctid is NULL");
2075 
2076  tupleid = (ItemPointer) DatumGetPointer(datum);
2077  tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
2078  tupleid = &tuple_ctid;
2079  }
2080 
2081  /*
2082  * Use the wholerow attribute, when available, to reconstruct
2083  * the old relation tuple.
2084  *
2085  * Foreign table updates have a wholerow attribute when the
2086  * relation has a row-level trigger. Note that the wholerow
2087  * attribute does not carry system columns. Foreign table
2088  * triggers miss seeing those, except that we know enough here
2089  * to set t_tableOid. Quite separately from this, the FDW may
2090  * fetch its own junk attrs to identify the row.
2091  *
2092  * Other relevant relkinds, currently limited to views, always
2093  * have a wholerow attribute.
2094  */
2095  else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo))
2096  {
2097  datum = ExecGetJunkAttribute(slot,
2098  junkfilter->jf_junkAttNo,
2099  &isNull);
2100  /* shouldn't ever get a null result... */
2101  if (isNull)
2102  elog(ERROR, "wholerow is NULL");
2103 
2104  oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
2105  oldtupdata.t_len =
2107  ItemPointerSetInvalid(&(oldtupdata.t_self));
2108  /* Historically, view triggers see invalid t_tableOid. */
2109  oldtupdata.t_tableOid =
2110  (relkind == RELKIND_VIEW) ? InvalidOid :
2111  RelationGetRelid(resultRelInfo->ri_RelationDesc);
2112 
2113  oldtuple = &oldtupdata;
2114  }
2115  else
2116  Assert(relkind == RELKIND_FOREIGN_TABLE);
2117  }
2118 
2119  /*
2120  * apply the junkfilter if needed.
2121  */
2122  if (operation != CMD_DELETE)
2123  slot = ExecFilterJunk(junkfilter, slot);
2124  }
2125 
2126  switch (operation)
2127  {
2128  case CMD_INSERT:
2129  slot = ExecInsert(node, resultRelInfo, slot, planSlot,
2130  estate, node->canSetTag);
2131  break;
2132  case CMD_UPDATE:
2133  slot = ExecUpdate(node, resultRelInfo, tupleid, oldtuple, slot,
2134  planSlot, &node->mt_epqstate, estate,
2135  node->canSetTag);
2136  break;
2137  case CMD_DELETE:
2138  slot = ExecDelete(node, resultRelInfo, tupleid, oldtuple,
2139  planSlot, &node->mt_epqstate, estate,
2140  true, /* processReturning */
2141  node->canSetTag,
2142  false, /* changingPart */
2143  NULL, NULL);
2144  break;
2145  default:
2146  elog(ERROR, "unknown operation");
2147  break;
2148  }
2149 
2150  /*
2151  * If we got a RETURNING result, return it to caller. We'll continue
2152  * the work on next call.
2153  */
2154  if (slot)
2155  return slot;
2156  }
2157 
2158  /*
2159  * We're done, but fire AFTER STATEMENT triggers before exiting.
2160  */
2161  fireASTriggers(node);
2162 
2163  node->mt_done = true;
2164 
2165  return NULL;
2166 }
AttrNumber jf_junkAttNo
Definition: execnodes.h:372
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
JunkFilter * ri_junkFilter
Definition: execnodes.h:465
Relation ri_RelationDesc
Definition: execnodes.h:412
#define ResetPerTupleExprContext(estate)
Definition: executor.h:515
#define castNode(_type_, nodeptr)
Definition: nodes.h:597
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1168
ExprContext * ps_ExprContext
Definition: execnodes.h:978
TupleTableSlot ** mt_scans
Definition: execnodes.h:1166
static void fireBSTriggers(ModifyTableState *node)
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:122
CmdType operation
Definition: execnodes.h:1160
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2428
EState * state
Definition: execnodes.h:941
Form_pg_class rd_rel
Definition: rel.h:109
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:294
struct EPQState * es_epq_active
Definition: execnodes.h:588
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1159
ItemPointerData t_self
Definition: htup.h:65
bool ri_usesFdwDirectModify
Definition: execnodes.h:447
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
uint32 t_len
Definition: htup.h:64
EPQState mt_epqstate
Definition: execnodes.h:1177
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:471
#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:1163
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
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:239
Plan * plan
Definition: execnodes.h:939
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:746
#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:214
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool processReturning, bool canSetTag, bool changingPart, bool *tupleDeleted, TupleTableSlot **epqreturnslot)
#define RelationGetRelid(relation)
Definition: rel.h:456
CmdType
Definition: nodes.h:671
#define ResetExprContext(econtext)
Definition: executor.h:500
List ** mt_arowmarks
Definition: execnodes.h:1176
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:214
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:452

◆ ExecOnConflictUpdate()

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

Definition at line 1564 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().

1572 {
1573  ExprContext *econtext = mtstate->ps.ps_ExprContext;
1574  Relation relation = resultRelInfo->ri_RelationDesc;
1575  ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
1576  TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
1577  TM_FailureData tmfd;
1578  LockTupleMode lockmode;
1579  TM_Result test;
1580  Datum xminDatum;
1581  TransactionId xmin;
1582  bool isnull;
1583 
1584  /* Determine lock mode to use */
1585  lockmode = ExecUpdateLockMode(estate, resultRelInfo);
1586 
1587  /*
1588  * Lock tuple for update. Don't follow updates when tuple cannot be
1589  * locked without doing so. A row locking conflict here means our
1590  * previous conclusion that the tuple is conclusively committed is not
1591  * true anymore.
1592  */
1593  test = table_tuple_lock(relation, conflictTid,
1594  estate->es_snapshot,
1595  existing, estate->es_output_cid,
1596  lockmode, LockWaitBlock, 0,
1597  &tmfd);
1598  switch (test)
1599  {
1600  case TM_Ok:
1601  /* success! */
1602  break;
1603 
1604  case TM_Invisible:
1605 
1606  /*
1607  * This can occur when a just inserted tuple is updated again in
1608  * the same command. E.g. because multiple rows with the same
1609  * conflicting key values are inserted.
1610  *
1611  * This is somewhat similar to the ExecUpdate() TM_SelfModified
1612  * case. We do not want to proceed because it would lead to the
1613  * same row being updated a second time in some unspecified order,
1614  * and in contrast to plain UPDATEs there's no historical behavior
1615  * to break.
1616  *
1617  * It is the user's responsibility to prevent this situation from
1618  * occurring. These problems are why SQL-2003 similarly specifies
1619  * that for SQL MERGE, an exception must be raised in the event of
1620  * an attempt to update the same row twice.
1621  */
1622  xminDatum = slot_getsysattr(existing,
1624  &isnull);
1625  Assert(!isnull);
1626  xmin = DatumGetTransactionId(xminDatum);
1627 
1629  ereport(ERROR,
1630  (errcode(ERRCODE_CARDINALITY_VIOLATION),
1631  errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
1632  errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
1633 
1634  /* This shouldn't happen */
1635  elog(ERROR, "attempted to lock invisible tuple");
1636  break;
1637 
1638  case TM_SelfModified:
1639 
1640  /*
1641  * This state should never be reached. As a dirty snapshot is used
1642  * to find conflicting tuples, speculative insertion wouldn't have
1643  * seen this row to conflict with.
1644  */
1645  elog(ERROR, "unexpected self-updated tuple");
1646  break;
1647 
1648  case TM_Updated:
1650  ereport(ERROR,
1651  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1652  errmsg("could not serialize access due to concurrent update")));
1653 
1654  /*
1655  * As long as we don't support an UPDATE of INSERT ON CONFLICT for
1656  * a partitioned table we shouldn't reach to a case where tuple to
1657  * be lock is moved to another partition due to concurrent update
1658  * of the partition key.
1659  */
1661 
1662  /*
1663  * Tell caller to try again from the very start.
1664  *
1665  * It does not make sense to use the usual EvalPlanQual() style
1666  * loop here, as the new version of the row might not conflict
1667  * anymore, or the conflicting tuple has actually been deleted.
1668  */
1669  ExecClearTuple(existing);
1670  return false;
1671 
1672  case TM_Deleted:
1674  ereport(ERROR,
1675  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1676  errmsg("could not serialize access due to concurrent delete")));
1677 
1678  /* see TM_Updated case */
1680  ExecClearTuple(existing);
1681  return false;
1682 
1683  default:
1684  elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
1685  }
1686 
1687  /* Success, the tuple is locked. */
1688 
1689  /*
1690  * Verify that the tuple is visible to our MVCC snapshot if the current
1691  * isolation level mandates that.
1692  *
1693  * It's not sufficient to rely on the check within ExecUpdate() as e.g.
1694  * CONFLICT ... WHERE clause may prevent us from reaching that.
1695  *
1696  * This means we only ever continue when a new command in the current
1697  * transaction could see the row, even though in READ COMMITTED mode the
1698  * tuple will not be visible according to the current statement's
1699  * snapshot. This is in line with the way UPDATE deals with newer tuple
1700  * versions.
1701  */
1702  ExecCheckTupleVisible(estate, relation, existing);
1703 
1704  /*
1705  * Make tuple and any needed join variables available to ExecQual and
1706  * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
1707  * the target's existing tuple is installed in the scantuple. EXCLUDED
1708  * has been made to reference INNER_VAR in setrefs.c, but there is no
1709  * other redirection.
1710  */
1711  econtext->ecxt_scantuple = existing;
1712  econtext->ecxt_innertuple = excludedSlot;
1713  econtext->ecxt_outertuple = NULL;
1714 
1715  if (!ExecQual(onConflictSetWhere, econtext))
1716  {
1717  ExecClearTuple(existing); /* see return below */
1718  InstrCountFiltered1(&mtstate->ps, 1);
1719  return true; /* done with the tuple */
1720  }
1721 
1722  if (resultRelInfo->ri_WithCheckOptions != NIL)
1723  {
1724  /*
1725  * Check target's existing tuple against UPDATE-applicable USING
1726  * security barrier quals (if any), enforced here as RLS checks/WCOs.
1727  *
1728  * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
1729  * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
1730  * but that's almost the extent of its special handling for ON
1731  * CONFLICT DO UPDATE.
1732  *
1733  * The rewriter will also have associated UPDATE applicable straight
1734  * RLS checks/WCOs for the benefit of the ExecUpdate() call that
1735  * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
1736  * kinds, so there is no danger of spurious over-enforcement in the
1737  * INSERT or UPDATE path.
1738  */
1740  existing,
1741  mtstate->ps.state);
1742  }
1743 
1744  /* Project the new tuple version */
1745  ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
1746 
1747  /*
1748  * Note that it is possible that the target tuple has been modified in
1749  * this session, after the above table_tuple_lock. We choose to not error
1750  * out in that case, in line with ExecUpdate's treatment of similar cases.
1751  * This can happen if an UPDATE is triggered from within ExecQual(),
1752  * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
1753  * wCTE in the ON CONFLICT's SET.
1754  */
1755 
1756  /* Execute UPDATE with projection */
1757  *returning = ExecUpdate(mtstate, resultRelInfo, conflictTid, NULL,
1758  resultRelInfo->ri_onConflict->oc_ProjSlot,
1759  planSlot,
1760  &mtstate->mt_epqstate, mtstate->ps.state,
1761  canSetTag);
1762 
1763  /*
1764  * Clear out existing tuple, as there might not be another conflict among
1765  * the next input rows. Don't want to hold resources till the end of the
1766  * query.
1767  */
1768  ExecClearTuple(existing);
1769  return true;
1770 }
#define NIL
Definition: pg_list.h:65
ItemPointerData ctid
Definition: tableam.h:125
Relation ri_RelationDesc
Definition: execnodes.h:412
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1068
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1932
CommandId es_output_cid
Definition: execnodes.h:531
static void test(void)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
uint32 TransactionId
Definition: c.h:521
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
ExprContext * ps_ExprContext
Definition: execnodes.h:978
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
int errcode(int sqlerrcode)
Definition: elog.c:610
Snapshot es_snapshot
Definition: execnodes.h:517
EState * state
Definition: execnodes.h:941
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:369
TupleTableSlot * oc_Existing
Definition: execnodes.h:384
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1159
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:386
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:1356
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
EPQState mt_epqstate
Definition: execnodes.h:1177
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:227
ExprState * oc_WhereClause
Definition: execnodes.h:387
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1041
OnConflictSetState * ri_onConflict
Definition: execnodes.h:477
TM_Result
Definition: tableam.h:70
uintptr_t Datum
Definition: postgres.h:367
List * ri_WithCheckOptions
Definition: execnodes.h:450
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
#define ereport(elevel,...)
Definition: elog.h:144
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:385
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:229
#define Assert(condition)
Definition: c.h:746
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2217
Definition: tableam.h:76
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:225
#define DatumGetTransactionId(X)
Definition: postgres.h:514
int errmsg(const char *fmt,...)
Definition: elog.c:821
#define elog(elevel,...)
Definition: elog.h:214
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:332

◆ ExecPrepareTupleRouting()

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

Definition at line 1869 of file nodeModifyTable.c.

References TupleConversionMap::attrMap, ExecFindPartition(), execute_attr_map_slot(), ModifyTableState::mt_transition_capture, ResultRelInfo::ri_PartitionTupleSlot, ResultRelInfo::ri_RootToPartitionMap, ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_original_insert_tuple, and TriggerDesc::trig_insert_before_row.

Referenced by ExecInsert().

1875 {
1876  ResultRelInfo *partrel;
1877  TupleConversionMap *map;
1878 
1879  /*
1880  * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
1881  * not find a valid partition for the tuple in 'slot' then an error is
1882  * raised. An error may also be raised if the found partition is not a
1883  * valid target for INSERTs. This is required since a partitioned table
1884  * UPDATE to another partition becomes a DELETE+INSERT.
1885  */
1886  partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
1887 
1888  /*
1889  * If we're capturing transition tuples, we might need to convert from the
1890  * partition rowtype to root partitioned table's rowtype. But if there
1891  * are no BEFORE triggers on the partition that could change the tuple, we
1892  * can just remember the original unconverted tuple to avoid a needless
1893  * round trip conversion.
1894  */
1895  if (mtstate->mt_transition_capture != NULL)
1896  {
1897  bool has_before_insert_row_trig;
1898 
1899  has_before_insert_row_trig = (partrel->ri_TrigDesc &&
1901 
1903  !has_before_insert_row_trig ? slot : NULL;
1904  }
1905 
1906  /*
1907  * Convert the tuple, if necessary.
1908  */
1909  map = partrel->ri_RootToPartitionMap;
1910  if (map != NULL)
1911  {
1912  TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
1913 
1914  slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
1915  }
1916 
1917  *partRelInfo = partrel;
1918  return slot;
1919 }
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:492
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1190
AttrMap * attrMap
Definition: tupconvert.h:27
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:424
bool trig_insert_before_row
Definition: reltrigger.h:56
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
TupleConversionMap * ri_RootToPartitionMap
Definition: execnodes.h:491
ResultRelInfo * ExecFindPartition(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, TupleTableSlot *slot, EState *estate)
TupleTableSlot * tcs_original_insert_tuple
Definition: trigger.h:75

◆ ExecProcessReturning()

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

Definition at line 159 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().

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

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 2725 of file nodeModifyTable.c.

References elog, and ERROR.

Referenced by ExecReScan().

2726 {
2727  /*
2728  * Currently, we don't need to support rescan on ModifyTable nodes. The
2729  * semantics of that would be a bit debatable anyway.
2730  */
2731  elog(ERROR, "ExecReScanModifyTable is not implemented");
2732 }
#define ERROR
Definition: elog.h:43
#define elog(elevel,...)
Definition: elog.h:214

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 1840 of file nodeModifyTable.c.

References CMD_INSERT, CMD_UPDATE, MakeTransitionCaptureState(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::operation, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationGetRelid, and ModifyTableState::rootResultRelInfo.

Referenced by ExecInitModifyTable().

1841 {
1842  ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
1843  ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
1844 
1845  /* Check for transition tables on the directly targeted relation. */
1846  mtstate->mt_transition_capture =
1847  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1848  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1849  mtstate->operation);
1850  if (plan->operation == CMD_INSERT &&
1852  mtstate->mt_oc_transition_capture =
1853  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1854  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1855  CMD_UPDATE);
1856 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1193
CmdType operation
Definition: execnodes.h:1160
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1174
PlanState ps
Definition: execnodes.h:1159
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1190
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4291
Plan * plan
Definition: execnodes.h:939
OnConflictAction onConflictAction
Definition: plannodes.h:228
CmdType operation
Definition: plannodes.h:215
#define RelationGetRelid(relation)
Definition: rel.h:456

◆ ExecUpdate()

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

Definition at line 1208 of file nodeModifyTable.c.

References Assert, TM_FailureData::cmax, CMD_INSERT, CMD_UPDATE, TupleDescData::constr, elog, ereport, errcode(), errhint(), errmsg(), ERROR, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_processed, EState::es_snapshot, EvalPlanQual(), EvalPlanQualSlot(), ExecARUpdateTriggers(), ExecBRUpdateTriggers(), ExecComputeStoredGenerated(), ExecConstraints(), ExecCrossPartitionUpdate(), ExecFilterJunk(), FdwRoutine::ExecForeignUpdate, ExecInsertIndexTuples(), ExecIRUpdateTriggers(), ExecMaterializeSlot(), ExecPartitionCheck(), ExecProcessReturning(), ExecWithCheckOptions(), TupleConstr::has_generated_stored, IsBootstrapProcessingMode, IsolationUsesXactSnapshot, list_free(), LockWaitBlock, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, NIL, ModifyTableState::operation, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, table_tuple_lock(), table_tuple_update(), TM_Deleted, TM_Ok, TM_SelfModified, TM_Updated, TM_FailureData::traversed, TriggerDesc::trig_update_before_row, TriggerDesc::trig_update_instead_row, TupleTableSlot::tts_tableOid, TupIsNull, TUPLE_LOCK_FLAG_FIND_LAST_VERSION, WCO_RLS_UPDATE_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

1217 {
1218  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1219  TM_Result result;
1220  TM_FailureData tmfd;
1221  List *recheckIndexes = NIL;
1222 
1223  /*
1224  * abort the operation if not running transactions
1225  */
1227  elog(ERROR, "cannot UPDATE during bootstrap");
1228 
1229  ExecMaterializeSlot(slot);
1230 
1231  /* BEFORE ROW UPDATE Triggers */
1232  if (resultRelInfo->ri_TrigDesc &&
1233  resultRelInfo->ri_TrigDesc->trig_update_before_row)
1234  {
1235  if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
1236  tupleid, oldtuple, slot))
1237  return NULL; /* "do nothing" */
1238  }
1239 
1240  /* INSTEAD OF ROW UPDATE Triggers */
1241  if (resultRelInfo->ri_TrigDesc &&
1242  resultRelInfo->ri_TrigDesc->trig_update_instead_row)
1243  {
1244  if (!ExecIRUpdateTriggers(estate, resultRelInfo,
1245  oldtuple, slot))
1246  return NULL; /* "do nothing" */
1247  }
1248  else if (resultRelInfo->ri_FdwRoutine)
1249  {
1250  /*
1251  * Compute stored generated columns
1252  */
1253  if (resultRelationDesc->rd_att->constr &&
1254  resultRelationDesc->rd_att->constr->has_generated_stored)
1255  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1256  CMD_UPDATE);
1257 
1258  /*
1259  * update in foreign table: let the FDW do it
1260  */
1261  slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
1262  resultRelInfo,
1263  slot,
1264  planSlot);
1265 
1266  if (slot == NULL) /* "do nothing" */
1267  return NULL;
1268 
1269  /*
1270  * AFTER ROW Triggers or RETURNING expressions might reference the
1271  * tableoid column, so (re-)initialize tts_tableOid before evaluating
1272  * them.
1273  */
1274  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1275  }
1276  else
1277  {
1278  LockTupleMode lockmode;
1279  bool partition_constraint_failed;
1280  bool update_indexes;
1281 
1282  /*
1283  * Constraints might reference the tableoid column, so (re-)initialize
1284  * tts_tableOid before evaluating them.
1285  */
1286  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1287 
1288  /*
1289  * Compute stored generated columns
1290  */
1291  if (resultRelationDesc->rd_att->constr &&
1292  resultRelationDesc->rd_att->constr->has_generated_stored)
1293  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1294  CMD_UPDATE);
1295 
1296  /*
1297  * Check any RLS UPDATE WITH CHECK policies
1298  *
1299  * If we generate a new candidate tuple after EvalPlanQual testing, we
1300  * must loop back here and recheck any RLS policies and constraints.
1301  * (We don't need to redo triggers, however. If there are any BEFORE
1302  * triggers then trigger.c will have done table_tuple_lock to lock the
1303  * correct tuple, so there's no need to do them again.)
1304  */
1305 lreplace:;
1306 
1307  /* ensure slot is independent, consider e.g. EPQ */
1308  ExecMaterializeSlot(slot);
1309 
1310  /*
1311  * If partition constraint fails, this row might get moved to another
1312  * partition, in which case we should check the RLS CHECK policy just
1313  * before inserting into the new partition, rather than doing it here.
1314  * This is because a trigger on that partition might again change the
1315  * row. So skip the WCO checks if the partition constraint fails.
1316  */
1317  partition_constraint_failed =
1318  resultRelationDesc->rd_rel->relispartition &&
1319  !ExecPartitionCheck(resultRelInfo, slot, estate, false);
1320 
1321  if (!partition_constraint_failed &&
1322  resultRelInfo->ri_WithCheckOptions != NIL)
1323  {
1324  /*
1325  * ExecWithCheckOptions() will skip any WCOs which are not of the
1326  * kind we are looking for at this point.
1327  */
1329  resultRelInfo, slot, estate);
1330  }
1331 
1332  /*
1333  * If a partition check failed, try to move the row into the right
1334  * partition.
1335  */
1336  if (partition_constraint_failed)
1337  {
1338  TupleTableSlot *inserted_tuple,
1339  *retry_slot;
1340  bool retry;
1341 
1342  /*
1343  * ExecCrossPartitionUpdate will first DELETE the row from the
1344  * partition it's currently in and then insert it back into the
1345  * root table, which will re-route it to the correct partition.
1346  * The first part may have to be repeated if it is detected that
1347  * the tuple we're trying to move has been concurrently updated.
1348  */
1349  retry = !ExecCrossPartitionUpdate(mtstate, resultRelInfo, tupleid,
1350  oldtuple, slot, planSlot,
1351  epqstate, canSetTag,
1352  &retry_slot, &inserted_tuple);
1353  if (retry)
1354  {
1355  slot = retry_slot;
1356  goto lreplace;
1357  }
1358 
1359  return inserted_tuple;
1360  }
1361 
1362  /*
1363  * Check the constraints of the tuple. We've already checked the
1364  * partition constraint above; however, we must still ensure the tuple
1365  * passes all other constraints, so we will call ExecConstraints() and
1366  * have it validate all remaining checks.
1367  */
1368  if (resultRelationDesc->rd_att->constr)
1369  ExecConstraints(resultRelInfo, slot, estate);
1370 
1371  /*
1372  * replace the heap tuple
1373  *
1374  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
1375  * that the row to be updated is visible to that snapshot, and throw a
1376  * can't-serialize error if not. This is a special-case behavior
1377  * needed for referential integrity updates in transaction-snapshot
1378  * mode transactions.
1379  */
1380  result = table_tuple_update(resultRelationDesc, tupleid, slot,
1381  estate->es_output_cid,
1382  estate->es_snapshot,
1383  estate->es_crosscheck_snapshot,
1384  true /* wait for commit */ ,
1385  &tmfd, &lockmode, &update_indexes);
1386 
1387  switch (result)
1388  {
1389  case TM_SelfModified:
1390 
1391  /*
1392  * The target tuple was already updated or deleted by the
1393  * current command, or by a later command in the current
1394  * transaction. The former case is possible in a join UPDATE
1395  * where multiple tuples join to the same target tuple. This
1396  * is pretty questionable, but Postgres has always allowed it:
1397  * we just execute the first update action and ignore
1398  * additional update attempts.
1399  *
1400  * The latter case arises if the tuple is modified by a
1401  * command in a BEFORE trigger, or perhaps by a command in a
1402  * volatile function used in the query. In such situations we
1403  * should not ignore the update, but it is equally unsafe to
1404  * proceed. We don't want to discard the original UPDATE
1405  * while keeping the triggered actions based on it; and we
1406  * have no principled way to merge this update with the
1407  * previous ones. So throwing an error is the only safe
1408  * course.
1409  *
1410  * If a trigger actually intends this type of interaction, it
1411  * can re-execute the UPDATE (assuming it can figure out how)
1412  * and then return NULL to cancel the outer update.
1413  */
1414  if (tmfd.cmax != estate->es_output_cid)
1415  ereport(ERROR,
1416  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1417  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1418  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1419 
1420  /* Else, already updated by self; nothing to do */
1421  return NULL;
1422 
1423  case TM_Ok:
1424  break;
1425 
1426  case TM_Updated:
1427  {
1428  TupleTableSlot *inputslot;
1429  TupleTableSlot *epqslot;
1430 
1432  ereport(ERROR,
1433  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1434  errmsg("could not serialize access due to concurrent update")));
1435 
1436  /*
1437  * Already know that we're going to need to do EPQ, so
1438  * fetch tuple directly into the right slot.
1439  */
1440  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
1441  resultRelInfo->ri_RangeTableIndex);
1442 
1443  result = table_tuple_lock(resultRelationDesc, tupleid,
1444  estate->es_snapshot,
1445  inputslot, estate->es_output_cid,
1446  lockmode, LockWaitBlock,
1448  &tmfd);
1449 
1450  switch (result)
1451  {
1452  case TM_Ok:
1453  Assert(tmfd.traversed);
1454 
1455  epqslot = EvalPlanQual(epqstate,
1456  resultRelationDesc,
1457  resultRelInfo->ri_RangeTableIndex,
1458  inputslot);
1459  if (TupIsNull(epqslot))
1460  /* Tuple not passing quals anymore, exiting... */
1461  return NULL;
1462 
1463  slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1464  goto lreplace;
1465 
1466  case TM_Deleted:
1467  /* tuple already deleted; nothing to do */
1468  return NULL;
1469 
1470  case TM_SelfModified:
1471 
1472  /*
1473  * This can be reached when following an update
1474  * chain from a tuple updated by another session,
1475  * reaching a tuple that was already updated in
1476  * this transaction. If previously modified by
1477  * this command, ignore the redundant update,
1478  * otherwise error out.
1479  *
1480  * See also TM_SelfModified response to
1481  * table_tuple_update() above.
1482  */
1483  if (tmfd.cmax != estate->es_output_cid)
1484  ereport(ERROR,
1485  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1486  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1487  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1488  return NULL;
1489 
1490  default:
1491  /* see table_tuple_lock call in ExecDelete() */
1492  elog(ERROR, "unexpected table_tuple_lock status: %u",
1493  result);
1494  return NULL;
1495  }
1496  }
1497 
1498  break;
1499 
1500  case TM_Deleted:
1502  ereport(ERROR,
1503  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1504  errmsg("could not serialize access due to concurrent delete")));
1505  /* tuple already deleted; nothing to do */
1506  return NULL;
1507 
1508  default:
1509  elog(ERROR, "unrecognized table_tuple_update status: %u",
1510  result);
1511  return NULL;
1512  }
1513 
1514  /* insert index entries for tuple if necessary */
1515  if (resultRelInfo->ri_NumIndices > 0 && update_indexes)
1516  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1517  slot, estate, false,
1518  NULL, NIL);
1519  }
1520 
1521  if (canSetTag)
1522  (estate->es_processed)++;
1523 
1524  /* AFTER ROW UPDATE Triggers */
1525  ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, slot,
1526  recheckIndexes,
1527  mtstate->operation == CMD_INSERT ?
1528  mtstate->mt_oc_transition_capture :
1529  mtstate->mt_transition_capture);
1530 
1531  list_free(recheckIndexes);
1532 
1533  /*
1534  * Check any WITH CHECK OPTION constraints from parent views. We are
1535  * required to do this after testing all constraints and uniqueness
1536  * violations per the SQL spec, so we do it after actually updating the
1537  * record in the heap and all indexes.
1538  *
1539  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1540  * are looking for at this point.
1541  */
1542  if (resultRelInfo->ri_WithCheckOptions != NIL)
1543  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1544 
1545  /* Process RETURNING if present */
1546  if (resultRelInfo->ri_projectReturning)
1547  return ExecProcessReturning(resultRelInfo, slot, planSlot);
1548 
1549  return NULL;
1550 }
int ri_NumIndices
Definition: execnodes.h:415
#define NIL
Definition: pg_list.h:65
Oid tts_tableOid
Definition: tuptable.h:131
JunkFilter * ri_junkFilter
Definition: execnodes.h:465
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1193
Relation ri_RelationDesc
Definition: execnodes.h:412
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1068
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1932
CommandId es_output_cid
Definition: execnodes.h:531
CommandId cmax
Definition: tableam.h:127
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1801
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:518
int errcode(int sqlerrcode)
Definition: elog.c:610
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
CmdType operation
Definition: execnodes.h:1160
Snapshot es_snapshot
Definition: execnodes.h:517
Form_pg_class rd_rel
Definition: rel.h:109
Index ri_RangeTableIndex
Definition: execnodes.h:409
bool has_generated_stored
Definition: tupdesc.h:45
#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:1356
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, 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:2445
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1190
TupleConstr * constr
Definition: tupdesc.h:85
bool trig_update_before_row
Definition: reltrigger.h:61
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2336
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:471
#define TupIsNull(slot)
Definition: tuptable.h:292
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:441
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:424
bool trig_update_instead_row
Definition: reltrigger.h:63
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:2763
TM_Result
Definition: tableam.h:70
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:261
List * ri_WithCheckOptions
Definition: execnodes.h:450
TupleDesc rd_att
Definition: rel.h:110
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
#define ereport(elevel,...)
Definition: elog.h:144
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:212
#define Assert(condition)
Definition: c.h:746
Definition: tableam.h:76
uint64 es_processed
Definition: execnodes.h:563
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:141
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:393
int errmsg(const char *fmt,...)
Definition: elog.c:821
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2805
void list_free(List *list)
Definition: list.c:1376
#define elog(elevel,...)
Definition: elog.h:214
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2623
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1679
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:1311
bool traversed
Definition: tableam.h:128
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:456
static bool ExecCrossPartitionUpdate(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, bool canSetTag, TupleTableSlot **retry_slot, TupleTableSlot **inserted_tuple)

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 1806 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

1807 {
1808  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1809  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
1810 
1811  switch (node->operation)
1812  {
1813  case CMD_INSERT:
1814  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1816  resultRelInfo,
1817  node->mt_oc_transition_capture);
1818  ExecASInsertTriggers(node->ps.state, resultRelInfo,
1819  node->mt_transition_capture);
1820  break;
1821  case CMD_UPDATE:
1822  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
1823  node->mt_transition_capture);
1824  break;
1825  case CMD_DELETE:
1826  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
1827  node->mt_transition_capture);
1828  break;
1829  default:
1830  elog(ERROR, "unknown operation");
1831  break;
1832  }
1833 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1193
CmdType operation
Definition: execnodes.h:1160
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1174
EState * state
Definition: execnodes.h:941
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2375
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1159
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1190
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2610
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2161
Plan * plan
Definition: execnodes.h:939
OnConflictAction onConflictAction
Definition: plannodes.h:228
#define elog(elevel,...)
Definition: elog.h:214

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 1777 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::rootResultRelInfo, and PlanState::state.

Referenced by ExecModifyTable().

1778 {
1779  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1780  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
1781 
1782  switch (node->operation)
1783  {
1784  case CMD_INSERT:
1785  ExecBSInsertTriggers(node->ps.state, resultRelInfo);
1786  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1788  resultRelInfo);
1789  break;
1790  case CMD_UPDATE:
1791  ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
1792  break;
1793  case CMD_DELETE:
1794  ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
1795  break;
1796  default:
1797  elog(ERROR, "unknown operation");
1798  break;
1799  }
1800 }
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2324
CmdType operation
Definition: execnodes.h:1160
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1174
EState * state
Definition: execnodes.h:941
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1159
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2110
Plan * plan
Definition: execnodes.h:939
OnConflictAction onConflictAction
Definition: plannodes.h:228
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2555
#define elog(elevel,...)
Definition: elog.h:214