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 void ExecBatchInsert (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
 
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

◆ ExecBatchInsert()

static void ExecBatchInsert ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo,
TupleTableSlot **  slots,
TupleTableSlot **  planSlots,
int  numSlots,
EState estate,
bool  canSetTag 
)
static

Definition at line 767 of file nodeModifyTable.c.

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

Referenced by ExecInsert(), and ExecModifyTable().

774 {
775  int i;
776  int numInserted = numSlots;
777  TupleTableSlot *slot = NULL;
778  TupleTableSlot **rslots;
779 
780  /*
781  * insert into foreign table: let the FDW do it
782  */
783  rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
784  resultRelInfo,
785  slots,
786  planSlots,
787  &numInserted);
788 
789  for (i = 0; i < numInserted; i++)
790  {
791  slot = rslots[i];
792 
793  /*
794  * AFTER ROW Triggers or RETURNING expressions might reference the
795  * tableoid column, so (re-)initialize tts_tableOid before evaluating
796  * them.
797  */
798  slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
799 
800  /* AFTER ROW INSERT Triggers */
801  ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
802  mtstate->mt_transition_capture);
803 
804  /*
805  * Check any WITH CHECK OPTION constraints from parent views. See the
806  * comment in ExecInsert.
807  */
808  if (resultRelInfo->ri_WithCheckOptions != NIL)
809  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
810  }
811 
812  if (canSetTag && numInserted > 0)
813  estate->es_processed += numInserted;
814 
815  for (i = 0; i < numSlots; i++)
816  {
818  ExecDropSingleTupleTableSlot(planSlots[i]);
819  }
820 }
#define NIL
Definition: pg_list.h:65
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:415
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2312
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1922
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1192
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:444
List * ri_WithCheckOptions
Definition: execnodes.h:459
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:220
uint64 es_processed
Definition: execnodes.h:576
int i
#define RelationGetRelid(relation)
Definition: rel.h:457

◆ ExecCheckPlanOutput()

static void ExecCheckPlanOutput ( Relation  resultRel,
List targetList 
)
static

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

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

◆ ExecCheckTIDVisible()

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

Definition at line 232 of file nodeModifyTable.c.

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

Referenced by ExecInsert().

236 {
237  Relation rel = relinfo->ri_RelationDesc;
238 
239  /* Redundantly check isolation level */
241  return;
242 
243  if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
244  elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
245  ExecCheckTupleVisible(estate, rel, tempSlot);
246  ExecClearTuple(tempSlot);
247 }
Relation ri_RelationDesc
Definition: execnodes.h:415
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:45
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1257
#define SnapshotAny
Definition: snapmgr.h:68
#define elog(elevel,...)
Definition: elog.h:227

◆ ExecCheckTupleVisible()

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

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

201 {
203  return;
204 
205  if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
206  {
207  Datum xminDatum;
208  TransactionId xmin;
209  bool isnull;
210 
211  xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
212  Assert(!isnull);
213  xmin = DatumGetTransactionId(xminDatum);
214 
215  /*
216  * We should not raise a serialization failure if the conflict is
217  * against a tuple inserted by our own transaction, even if it's not
218  * visible to our snapshot. (This would happen, for example, if
219  * conflicting keys are proposed for insertion in a single command.)
220  */
222  ereport(ERROR,
223  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
224  errmsg("could not serialize access due to concurrent update")));
225  }
226 }
uint32 TransactionId
Definition: c.h:587
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
int errcode(int sqlerrcode)
Definition: elog.c:694
Snapshot es_snapshot
Definition: execnodes.h:530
#define ERROR
Definition: elog.h:45
uintptr_t Datum
Definition: postgres.h:367
#define ereport(elevel,...)
Definition: elog.h:155
#define Assert(condition)
Definition: c.h:804
static bool table_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot)
Definition: tableam.h:1304
#define DatumGetTransactionId(X)
Definition: postgres.h:514
int errmsg(const char *fmt,...)
Definition: elog.c:905
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 253 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, ExecClearTuple(), ExecEvalExpr(), ExecGetExtraUpdatedCols(), ExecMaterializeSlot(), ExecPrepareExpr(), ExecStoreVirtualTuple(), FirstLowInvalidHeapAttributeNumber, GetPerTupleExprContext, GetPerTupleMemoryContext, TupleConstr::has_generated_stored, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc(), RelationGetDescr, RelationGetRelationName, ResultRelInfo::ri_GeneratedExprs, ResultRelInfo::ri_NumGeneratedNeeded, ResultRelInfo::ri_RelationDesc, slot_getallattrs(), TriggerDesc::trig_update_before_row, RelationData::trigdesc, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TupleDescAttr, val, and values.

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

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

1201 {
1202  EState *estate = mtstate->ps.state;
1204  TupleConversionMap *tupconv_map;
1205  bool tuple_deleted;
1206  TupleTableSlot *epqslot = NULL;
1207 
1208  *inserted_tuple = NULL;
1209  *retry_slot = NULL;
1210 
1211  /*
1212  * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1213  * to migrate to a different partition. Maybe this can be implemented
1214  * some day, but it seems a fringe feature with little redeeming value.
1215  */
1216  if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1217  ereport(ERROR,
1218  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1219  errmsg("invalid ON UPDATE specification"),
1220  errdetail("The result tuple would appear in a different partition than the original tuple.")));
1221 
1222  /*
1223  * When an UPDATE is run on a leaf partition, we will not have partition
1224  * tuple routing set up. In that case, fail with partition constraint
1225  * violation error.
1226  */
1227  if (proute == NULL)
1228  ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1229 
1230  /*
1231  * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
1232  * We want to return rows from INSERT.
1233  */
1234  ExecDelete(mtstate, resultRelInfo, tupleid, oldtuple, planSlot,
1235  epqstate, estate,
1236  false, /* processReturning */
1237  false, /* canSetTag */
1238  true, /* changingPart */
1239  &tuple_deleted, &epqslot);
1240 
1241  /*
1242  * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
1243  * it was already deleted by self, or it was concurrently deleted by
1244  * another transaction), then we should skip the insert as well;
1245  * otherwise, an UPDATE could cause an increase in the total number of
1246  * rows across all partitions, which is clearly wrong.
1247  *
1248  * For a normal UPDATE, the case where the tuple has been the subject of a
1249  * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
1250  * machinery, but for an UPDATE that we've translated into a DELETE from
1251  * this partition and an INSERT into some other partition, that's not
1252  * available, because CTID chains can't span relation boundaries. We
1253  * mimic the semantics to a limited extent by skipping the INSERT if the
1254  * DELETE fails to find a tuple. This ensures that two concurrent
1255  * attempts to UPDATE the same tuple at the same time can't turn one tuple
1256  * into two, and that an UPDATE of a just-deleted tuple can't resurrect
1257  * it.
1258  */
1259  if (!tuple_deleted)
1260  {
1261  /*
1262  * epqslot will be typically NULL. But when ExecDelete() finds that
1263  * another transaction has concurrently updated the same row, it
1264  * re-fetches the row, skips the delete, and epqslot is set to the
1265  * re-fetched tuple slot. In that case, we need to do all the checks
1266  * again.
1267  */
1268  if (TupIsNull(epqslot))
1269  return true;
1270  else
1271  {
1272  *retry_slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1273  return false;
1274  }
1275  }
1276 
1277  /*
1278  * resultRelInfo is one of the per-subplan resultRelInfos. So we should
1279  * convert the tuple into root's tuple descriptor if needed, since
1280  * ExecInsert() starts the search from root.
1281  */
1282  tupconv_map = resultRelInfo->ri_ChildToRootMap;
1283  if (tupconv_map != NULL)
1284  slot = execute_attr_map_slot(tupconv_map->attrMap,
1285  slot,
1286  mtstate->mt_root_tuple_slot);
1287 
1288  /* Tuple routing starts from the root table. */
1289  *inserted_tuple = ExecInsert(mtstate, mtstate->rootResultRelInfo, slot,
1290  planSlot, estate, canSetTag);
1291 
1292  /*
1293  * Reset the transition state that may possibly have been written by
1294  * INSERT.
1295  */
1296  if (mtstate->mt_transition_capture)
1298 
1299  /* We're done moving. */
1300  return true;
1301 }
JunkFilter * ri_junkFilter
Definition: execnodes.h:474
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1189
TupleConversionMap * ri_ChildToRootMap
Definition: execnodes.h:512
int errcode(int sqlerrcode)
Definition: elog.c:694
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1176
EState * state
Definition: execnodes.h:943
#define ERROR
Definition: elog.h:45
PlanState ps
Definition: execnodes.h:1161
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1192
int errdetail(const char *fmt,...)
Definition: elog.c:1038
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1718
#define TupIsNull(slot)
Definition: tuptable.h:292
AttrMap * attrMap
Definition: tupconvert.h:28
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:287
Plan * plan
Definition: execnodes.h:941
#define ereport(elevel,...)
Definition: elog.h:155
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:1186
int errmsg(const char *fmt,...)
Definition: elog.c:905
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 845 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().

857 {
858  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
859  TM_Result result;
860  TM_FailureData tmfd;
861  TupleTableSlot *slot = NULL;
862  TransitionCaptureState *ar_delete_trig_tcs;
863 
864  if (tupleDeleted)
865  *tupleDeleted = false;
866 
867  /* BEFORE ROW DELETE Triggers */
868  if (resultRelInfo->ri_TrigDesc &&
869  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
870  {
871  bool dodelete;
872 
873  dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
874  tupleid, oldtuple, epqreturnslot);
875 
876  if (!dodelete) /* "do nothing" */
877  return NULL;
878  }
879 
880  /* INSTEAD OF ROW DELETE Triggers */
881  if (resultRelInfo->ri_TrigDesc &&
882  resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
883  {
884  bool dodelete;
885 
886  Assert(oldtuple != NULL);
887  dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
888 
889  if (!dodelete) /* "do nothing" */
890  return NULL;
891  }
892  else if (resultRelInfo->ri_FdwRoutine)
893  {
894  /*
895  * delete from foreign table: let the FDW do it
896  *
897  * We offer the returning slot as a place to store RETURNING data,
898  * although the FDW can return some other slot if it wants.
899  */
900  slot = ExecGetReturningSlot(estate, resultRelInfo);
901  slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
902  resultRelInfo,
903  slot,
904  planSlot);
905 
906  if (slot == NULL) /* "do nothing" */
907  return NULL;
908 
909  /*
910  * RETURNING expressions might reference the tableoid column, so
911  * (re)initialize tts_tableOid before evaluating them.
912  */
913  if (TTS_EMPTY(slot))
914  ExecStoreAllNullTuple(slot);
915 
916  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
917  }
918  else
919  {
920  /*
921  * delete the tuple
922  *
923  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
924  * that the row to be deleted is visible to that snapshot, and throw a
925  * can't-serialize error if not. This is a special-case behavior
926  * needed for referential integrity updates in transaction-snapshot
927  * mode transactions.
928  */
929 ldelete:;
930  result = table_tuple_delete(resultRelationDesc, tupleid,
931  estate->es_output_cid,
932  estate->es_snapshot,
933  estate->es_crosscheck_snapshot,
934  true /* wait for commit */ ,
935  &tmfd,
936  changingPart);
937 
938  switch (result)
939  {
940  case TM_SelfModified:
941 
942  /*
943  * The target tuple was already updated or deleted by the
944  * current command, or by a later command in the current
945  * transaction. The former case is possible in a join DELETE
946  * where multiple tuples join to the same target tuple. This
947  * is somewhat questionable, but Postgres has always allowed
948  * it: we just ignore additional deletion attempts.
949  *
950  * The latter case arises if the tuple is modified by a
951  * command in a BEFORE trigger, or perhaps by a command in a
952  * volatile function used in the query. In such situations we
953  * should not ignore the deletion, but it is equally unsafe to
954  * proceed. We don't want to discard the original DELETE
955  * while keeping the triggered actions based on its deletion;
956  * and it would be no better to allow the original DELETE
957  * while discarding updates that it triggered. The row update
958  * carries some information that might be important according
959  * to business rules; so throwing an error is the only safe
960  * course.
961  *
962  * If a trigger actually intends this type of interaction, it
963  * can re-execute the DELETE and then return NULL to cancel
964  * the outer delete.
965  */
966  if (tmfd.cmax != estate->es_output_cid)
967  ereport(ERROR,
968  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
969  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
970  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
971 
972  /* Else, already deleted by self; nothing to do */
973  return NULL;
974 
975  case TM_Ok:
976  break;
977 
978  case TM_Updated:
979  {
980  TupleTableSlot *inputslot;
981  TupleTableSlot *epqslot;
982 
984  ereport(ERROR,
985  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
986  errmsg("could not serialize access due to concurrent update")));
987 
988  /*
989  * Already know that we're going to need to do EPQ, so
990  * fetch tuple directly into the right slot.
991  */
992  EvalPlanQualBegin(epqstate);
993  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
994  resultRelInfo->ri_RangeTableIndex);
995 
996  result = table_tuple_lock(resultRelationDesc, tupleid,
997  estate->es_snapshot,
998  inputslot, estate->es_output_cid,
1001  &tmfd);
1002 
1003  switch (result)
1004  {
1005  case TM_Ok:
1006  Assert(tmfd.traversed);
1007  epqslot = EvalPlanQual(epqstate,
1008  resultRelationDesc,
1009  resultRelInfo->ri_RangeTableIndex,
1010  inputslot);
1011  if (TupIsNull(epqslot))
1012  /* Tuple not passing quals anymore, exiting... */
1013  return NULL;
1014 
1015  /*
1016  * If requested, skip delete and pass back the
1017  * updated row.
1018  */
1019  if (epqreturnslot)
1020  {
1021  *epqreturnslot = epqslot;
1022  return NULL;
1023  }
1024  else
1025  goto ldelete;
1026 
1027  case TM_SelfModified:
1028 
1029  /*
1030  * This can be reached when following an update
1031  * chain from a tuple updated by another session,
1032  * reaching a tuple that was already updated in
1033  * this transaction. If previously updated by this
1034  * command, ignore the delete, otherwise error
1035  * out.
1036  *
1037  * See also TM_SelfModified response to
1038  * table_tuple_delete() above.
1039  */
1040  if (tmfd.cmax != estate->es_output_cid)
1041  ereport(ERROR,
1042  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1043  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1044  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1045  return NULL;
1046 
1047  case TM_Deleted:
1048  /* tuple already deleted; nothing to do */
1049  return NULL;
1050 
1051  default:
1052 
1053  /*
1054  * TM_Invisible should be impossible because we're
1055  * waiting for updated row versions, and would
1056  * already have errored out if the first version
1057  * is invisible.
1058  *
1059  * TM_Updated should be impossible, because we're
1060  * locking the latest version via
1061  * TUPLE_LOCK_FLAG_FIND_LAST_VERSION.
1062  */
1063  elog(ERROR, "unexpected table_tuple_lock status: %u",
1064  result);
1065  return NULL;
1066  }
1067 
1068  Assert(false);
1069  break;
1070  }
1071 
1072  case TM_Deleted:
1074  ereport(ERROR,
1075  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1076  errmsg("could not serialize access due to concurrent delete")));
1077  /* tuple already deleted; nothing to do */
1078  return NULL;
1079 
1080  default:
1081  elog(ERROR, "unrecognized table_tuple_delete status: %u",
1082  result);
1083  return NULL;
1084  }
1085 
1086  /*
1087  * Note: Normally one would think that we have to delete index tuples
1088  * associated with the heap tuple now...
1089  *
1090  * ... but in POSTGRES, we have no need to do this because VACUUM will
1091  * take care of it later. We can't delete index tuples immediately
1092  * anyway, since the tuple is still visible to other transactions.
1093  */
1094  }
1095 
1096  if (canSetTag)
1097  (estate->es_processed)++;
1098 
1099  /* Tell caller that the delete actually happened. */
1100  if (tupleDeleted)
1101  *tupleDeleted = true;
1102 
1103  /*
1104  * If this delete is the result of a partition key update that moved the
1105  * tuple to a new partition, put this row into the transition OLD TABLE,
1106  * if there is one. We need to do this separately for DELETE and INSERT
1107  * because they happen on different tables.
1108  */
1109  ar_delete_trig_tcs = mtstate->mt_transition_capture;
1110  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
1112  {
1113  ExecARUpdateTriggers(estate, resultRelInfo,
1114  tupleid,
1115  oldtuple,
1116  NULL,
1117  NULL,
1118  mtstate->mt_transition_capture);
1119 
1120  /*
1121  * We've already captured the NEW TABLE row, so make sure any AR
1122  * DELETE trigger fired below doesn't capture it again.
1123  */
1124  ar_delete_trig_tcs = NULL;
1125  }
1126 
1127  /* AFTER ROW DELETE Triggers */
1128  ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1129  ar_delete_trig_tcs);
1130 
1131  /* Process RETURNING if present and if requested */
1132  if (processReturning && resultRelInfo->ri_projectReturning)
1133  {
1134  /*
1135  * We have to put the target tuple into a slot, which means first we
1136  * gotta fetch it. We can use the trigger tuple slot.
1137  */
1138  TupleTableSlot *rslot;
1139 
1140  if (resultRelInfo->ri_FdwRoutine)
1141  {
1142  /* FDW must have provided a slot containing the deleted row */
1143  Assert(!TupIsNull(slot));
1144  }
1145  else
1146  {
1147  slot = ExecGetReturningSlot(estate, resultRelInfo);
1148  if (oldtuple != NULL)
1149  {
1150  ExecForceStoreHeapTuple(oldtuple, slot, false);
1151  }
1152  else
1153  {
1154  if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
1155  SnapshotAny, slot))
1156  elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
1157  }
1158  }
1159 
1160  rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
1161 
1162  /*
1163  * Before releasing the target tuple again, make sure rslot has a
1164  * local copy of any pass-by-reference values.
1165  */
1166  ExecMaterializeSlot(rslot);
1167 
1168  ExecClearTuple(slot);
1169 
1170  return rslot;
1171  }
1172 
1173  return NULL;
1174 }
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:223
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:415
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition: trigger.c:2572
int errhint(const char *fmt,...)
Definition: elog.c:1152
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1210
CommandId es_output_cid
Definition: execnodes.h:544
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:128
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:531
int errcode(int sqlerrcode)
Definition: elog.c:694
CmdType operation
Definition: execnodes.h:1162
Snapshot es_snapshot
Definition: execnodes.h:530
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1439
Index ri_RangeTableIndex
Definition: execnodes.h:412
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot)
Definition: trigger.c:2457
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
Definition: trigger.c:2542
#define ERROR
Definition: elog.h:45
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:1549
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition: execMain.c:2436
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1192
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2327
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:480
#define TupIsNull(slot)
Definition: tuptable.h:292
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:444
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:427
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1257
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:2823
TM_Result
Definition: tableam.h:71
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:1460
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
#define ereport(elevel,...)
Definition: elog.h:155
#define Assert(condition)
Definition: c.h:804
Definition: tableam.h:77
uint64 es_processed
Definition: execnodes.h:576
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:242
#define SnapshotAny
Definition: snapmgr.h:68
int errmsg(const char *fmt,...)
Definition: elog.c:905
#define elog(elevel,...)
Definition: elog.h:227
bool traversed
Definition: tableam.h:129
#define RelationGetRelid(relation)
Definition: rel.h:457
void EvalPlanQualBegin(EPQState *epqstate)
Definition: execMain.c:2589
bool trig_delete_before_row
Definition: reltrigger.h:66

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

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

2850 {
2851  int i;
2852 
2853  /*
2854  * Allow any FDWs to shut down
2855  */
2856  for (i = 0; i < node->mt_nplans; i++)
2857  {
2858  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
2859 
2860  if (!resultRelInfo->ri_usesFdwDirectModify &&
2861  resultRelInfo->ri_FdwRoutine != NULL &&
2862  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
2863  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
2864  resultRelInfo);
2865  }
2866 
2867  /*
2868  * Close all the partitioned tables, leaf partitions, and their indices
2869  * and release the slot used for tuple routing, if set.
2870  */
2871  if (node->mt_partition_tuple_routing)
2872  {
2874 
2875  if (node->mt_root_tuple_slot)
2877  }
2878 
2879  /*
2880  * Free the exprcontext
2881  */
2882  ExecFreeExprContext(&node->ps);
2883 
2884  /*
2885  * clean out the tuple table
2886  */
2887  if (node->ps.ps_ResultTupleSlot)
2889 
2890  /*
2891  * Terminate EPQ execution if active
2892  */
2893  EvalPlanQualEnd(&node->mt_epqstate);
2894 
2895  /*
2896  * shut down subplans
2897  */
2898  for (i = 0; i < node->mt_nplans; i++)
2899  ExecEndNode(node->mt_plans[i]);
2900 }
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1189
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:549
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1170
EState * state
Definition: execnodes.h:943
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:650
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2801
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:979
PlanState ps
Definition: execnodes.h:1161
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
bool ri_usesFdwDirectModify
Definition: execnodes.h:450
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1224
EPQState mt_epqstate
Definition: execnodes.h:1179
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:444
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:224
PlanState ** mt_plans
Definition: execnodes.h:1165
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1186
int i

◆ ExecInitModifyTable()

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

Definition at line 2313 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(), FdwRoutine::ExecForeignBatchInsert, ExecGetResultType(), ExecInitExtraTupleSlot(), ExecInitJunkFilter(), ExecInitJunkFilterInsertion(), ExecInitNode(), ExecInitQual(), ExecInitResultRelation(), ExecInitResultTupleSlotTL(), ExecInitResultTypeTL(), ExecModifyTable(), ExecOpenIndices(), PlanState::ExecProcNode, ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), ExecTypeFromTL(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, forboth, FdwRoutine::GetForeignModifyBatchSize, 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_BatchSize, 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().

2314 {
2315  ModifyTableState *mtstate;
2316  CmdType operation = node->operation;
2317  int nplans = list_length(node->plans);
2318  ResultRelInfo *resultRelInfo;
2319  Plan *subplan;
2320  ListCell *l,
2321  *l1;
2322  int i;
2323  Relation rel;
2324  bool update_tuple_routing_needed = node->partColsUpdated;
2325 
2326  /* check for unsupported flags */
2327  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2328 
2329  /*
2330  * create state structure
2331  */
2332  mtstate = makeNode(ModifyTableState);
2333  mtstate->ps.plan = (Plan *) node;
2334  mtstate->ps.state = estate;
2335  mtstate->ps.ExecProcNode = ExecModifyTable;
2336 
2337  mtstate->operation = operation;
2338  mtstate->canSetTag = node->canSetTag;
2339  mtstate->mt_done = false;
2340 
2341  mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
2342  mtstate->resultRelInfo = (ResultRelInfo *)
2343  palloc(nplans * sizeof(ResultRelInfo));
2344  mtstate->mt_scans = (TupleTableSlot **) palloc0(sizeof(TupleTableSlot *) * nplans);
2345 
2346  /*----------
2347  * Resolve the target relation. This is the same as:
2348  *
2349  * - the relation for which we will fire FOR STATEMENT triggers,
2350  * - the relation into whose tuple format all captured transition tuples
2351  * must be converted, and
2352  * - the root partitioned table used for tuple routing.
2353  *
2354  * If it's a partitioned table, the root partition doesn't appear
2355  * elsewhere in the plan and its RT index is given explicitly in
2356  * node->rootRelation. Otherwise (i.e. table inheritance) the target
2357  * relation is the first relation in the node->resultRelations list.
2358  *----------
2359  */
2360  if (node->rootRelation > 0)
2361  {
2363  ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
2364  node->rootRelation);
2365  }
2366  else
2367  {
2368  mtstate->rootResultRelInfo = mtstate->resultRelInfo;
2369  ExecInitResultRelation(estate, mtstate->resultRelInfo,
2370  linitial_int(node->resultRelations));
2371  }
2372 
2373  mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
2374  mtstate->mt_nplans = nplans;
2375 
2376  /* set up epqstate with dummy subplan data for the moment */
2377  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
2378  mtstate->fireBSTriggers = true;
2379 
2380  /*
2381  * Build state for collecting transition tuples. This requires having a
2382  * valid trigger query context, so skip it in explain-only mode.
2383  */
2384  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
2385  ExecSetupTransitionCaptureState(mtstate, estate);
2386 
2387  /*
2388  * call ExecInitNode on each of the plans to be executed and save the
2389  * results into the array "mt_plans". This is also a convenient place to
2390  * verify that the proposed target relations are valid and open their
2391  * indexes for insertion of new index entries.
2392  */
2393  resultRelInfo = mtstate->resultRelInfo;
2394  i = 0;
2395  forboth(l, node->resultRelations, l1, node->plans)
2396  {
2397  Index resultRelation = lfirst_int(l);
2398 
2399  subplan = (Plan *) lfirst(l1);
2400 
2401  /*
2402  * This opens result relation and fills ResultRelInfo. (root relation
2403  * was initialized already.)
2404  */
2405  if (resultRelInfo != mtstate->rootResultRelInfo)
2406  ExecInitResultRelation(estate, resultRelInfo, resultRelation);
2407 
2408  /* Initialize the usesFdwDirectModify flag */
2409  resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
2410  node->fdwDirectModifyPlans);
2411 
2412  /*
2413  * Verify result relation is a valid target for the current operation
2414  */
2415  CheckValidResultRel(resultRelInfo, operation);
2416 
2417  /*
2418  * If there are indices on the result relation, open them and save
2419  * descriptors in the result relation info, so that we can add new
2420  * index entries for the tuples we add/update. We need not do this
2421  * for a DELETE, however, since deletion doesn't affect indexes. Also,
2422  * inside an EvalPlanQual operation, the indexes might be open
2423  * already, since we share the resultrel state with the original
2424  * query.
2425  */
2426  if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
2427  operation != CMD_DELETE &&
2428  resultRelInfo->ri_IndexRelationDescs == NULL)
2429  ExecOpenIndices(resultRelInfo,
2431 
2432  /*
2433  * If this is an UPDATE and a BEFORE UPDATE trigger is present, the
2434  * trigger itself might modify the partition-key values. So arrange
2435  * for tuple routing.
2436  */
2437  if (resultRelInfo->ri_TrigDesc &&
2438  resultRelInfo->ri_TrigDesc->trig_update_before_row &&
2439  operation == CMD_UPDATE)
2440  update_tuple_routing_needed = true;
2441 
2442  /* Now init the plan for this result rel */
2443  mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
2444  mtstate->mt_scans[i] =
2445  ExecInitExtraTupleSlot(mtstate->ps.state, ExecGetResultType(mtstate->mt_plans[i]),
2446  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2447 
2448  /* Also let FDWs init themselves for foreign-table result rels */
2449  if (!resultRelInfo->ri_usesFdwDirectModify &&
2450  resultRelInfo->ri_FdwRoutine != NULL &&
2451  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
2452  {
2453  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
2454 
2455  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
2456  resultRelInfo,
2457  fdw_private,
2458  i,
2459  eflags);
2460  }
2461 
2462  /*
2463  * If needed, initialize a map to convert tuples in the child format
2464  * to the format of the table mentioned in the query (root relation).
2465  * It's needed for update tuple routing, because the routing starts
2466  * from the root relation. It's also needed for capturing transition
2467  * tuples, because the transition tuple store can only store tuples in
2468  * the root table format.
2469  *
2470  * For INSERT, the map is only initialized for a given partition when
2471  * the partition itself is first initialized by ExecFindPartition().
2472  */
2473  if (update_tuple_routing_needed ||
2474  (mtstate->mt_transition_capture &&
2475  mtstate->operation != CMD_INSERT))
2476  resultRelInfo->ri_ChildToRootMap =
2479  resultRelInfo++;
2480  i++;
2481  }
2482 
2483  /* Get the target relation */
2484  rel = mtstate->rootResultRelInfo->ri_RelationDesc;
2485 
2486  /*
2487  * If it's not a partitioned table after all, UPDATE tuple routing should
2488  * not be attempted.
2489  */
2490  if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2491  update_tuple_routing_needed = false;
2492 
2493  /*
2494  * Build state for tuple routing if it's an INSERT or if it's an UPDATE of
2495  * partition key.
2496  */
2497  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2498  (operation == CMD_INSERT || update_tuple_routing_needed))
2499  mtstate->mt_partition_tuple_routing =
2500  ExecSetupPartitionTupleRouting(estate, mtstate, rel);
2501 
2502  /*
2503  * For update row movement we'll need a dedicated slot to store the tuples
2504  * that have been converted from partition format to the root table
2505  * format.
2506  */
2507  if (update_tuple_routing_needed)
2508  mtstate->mt_root_tuple_slot = table_slot_create(rel, NULL);
2509 
2510  /*
2511  * Initialize any WITH CHECK OPTION constraints if needed.
2512  */
2513  resultRelInfo = mtstate->resultRelInfo;
2514  foreach(l, node->withCheckOptionLists)
2515  {
2516  List *wcoList = (List *) lfirst(l);
2517  List *wcoExprs = NIL;
2518  ListCell *ll;
2519 
2520  foreach(ll, wcoList)
2521  {
2522  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
2523  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
2524  &mtstate->ps);
2525 
2526  wcoExprs = lappend(wcoExprs, wcoExpr);
2527  }
2528 
2529  resultRelInfo->ri_WithCheckOptions = wcoList;
2530  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
2531  resultRelInfo++;
2532  }
2533 
2534  /*
2535  * Initialize RETURNING projections if needed.
2536  */
2537  if (node->returningLists)
2538  {
2539  TupleTableSlot *slot;
2540  ExprContext *econtext;
2541 
2542  /*
2543  * Initialize result tuple slot and assign its rowtype using the first
2544  * RETURNING list. We assume the rest will look the same.
2545  */
2546  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
2547 
2548  /* Set up a slot for the output of the RETURNING projection(s) */
2550  slot = mtstate->ps.ps_ResultTupleSlot;
2551 
2552  /* Need an econtext too */
2553  if (mtstate->ps.ps_ExprContext == NULL)
2554  ExecAssignExprContext(estate, &mtstate->ps);
2555  econtext = mtstate->ps.ps_ExprContext;
2556 
2557  /*
2558  * Build a projection for each result rel.
2559  */
2560  resultRelInfo = mtstate->resultRelInfo;
2561  foreach(l, node->returningLists)
2562  {
2563  List *rlist = (List *) lfirst(l);
2564 
2565  resultRelInfo->ri_returningList = rlist;
2566  resultRelInfo->ri_projectReturning =
2567  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
2568  resultRelInfo->ri_RelationDesc->rd_att);
2569  resultRelInfo++;
2570  }
2571  }
2572  else
2573  {
2574  /*
2575  * We still must construct a dummy result tuple type, because InitPlan
2576  * expects one (maybe should change that?).
2577  */
2578  mtstate->ps.plan->targetlist = NIL;
2579  ExecInitResultTypeTL(&mtstate->ps);
2580 
2581  mtstate->ps.ps_ExprContext = NULL;
2582  }
2583 
2584  /* Set the list of arbiter indexes if needed for ON CONFLICT */
2585  resultRelInfo = mtstate->resultRelInfo;
2586  if (node->onConflictAction != ONCONFLICT_NONE)
2587  resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
2588 
2589  /*
2590  * If needed, Initialize target list, projection and qual for ON CONFLICT
2591  * DO UPDATE.
2592  */
2593  if (node->onConflictAction == ONCONFLICT_UPDATE)
2594  {
2595  ExprContext *econtext;
2596  TupleDesc relationDesc;
2597  TupleDesc tupDesc;
2598 
2599  /* insert may only have one plan, inheritance is not expanded */
2600  Assert(nplans == 1);
2601 
2602  /* already exists if created by RETURNING processing above */
2603  if (mtstate->ps.ps_ExprContext == NULL)
2604  ExecAssignExprContext(estate, &mtstate->ps);
2605 
2606  econtext = mtstate->ps.ps_ExprContext;
2607  relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
2608 
2609  /* create state for DO UPDATE SET operation */
2610  resultRelInfo->ri_onConflict = makeNode(OnConflictSetState);
2611 
2612  /* initialize slot for the existing tuple */
2613  resultRelInfo->ri_onConflict->oc_Existing =
2614  table_slot_create(resultRelInfo->ri_RelationDesc,
2615  &mtstate->ps.state->es_tupleTable);
2616 
2617  /*
2618  * Create the tuple slot for the UPDATE SET projection. We want a slot
2619  * of the table's type here, because the slot will be used to insert
2620  * into the table, and for RETURNING processing - which may access
2621  * system attributes.
2622  */
2623  tupDesc = ExecTypeFromTL((List *) node->onConflictSet);
2624  resultRelInfo->ri_onConflict->oc_ProjSlot =
2625  ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
2626  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2627 
2628  /* build UPDATE SET projection state */
2629  resultRelInfo->ri_onConflict->oc_ProjInfo =
2630  ExecBuildProjectionInfo(node->onConflictSet, econtext,
2631  resultRelInfo->ri_onConflict->oc_ProjSlot,
2632  &mtstate->ps,
2633  relationDesc);
2634 
2635  /* initialize state to evaluate the WHERE clause, if any */
2636  if (node->onConflictWhere)
2637  {
2638  ExprState *qualexpr;
2639 
2640  qualexpr = ExecInitQual((List *) node->onConflictWhere,
2641  &mtstate->ps);
2642  resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
2643  }
2644  }
2645 
2646  /*
2647  * If we have any secondary relations in an UPDATE or DELETE, they need to
2648  * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
2649  * EvalPlanQual mechanism needs to be told about them. Locate the
2650  * relevant ExecRowMarks.
2651  */
2652  foreach(l, node->rowMarks)
2653  {
2655  ExecRowMark *erm;
2656 
2657  /* ignore "parent" rowmarks; they are irrelevant at runtime */
2658  if (rc->isParent)
2659  continue;
2660 
2661  /* find ExecRowMark (same for all subplans) */
2662  erm = ExecFindRowMark(estate, rc->rti, false);
2663 
2664  /* build ExecAuxRowMark for each subplan */
2665  for (i = 0; i < nplans; i++)
2666  {
2667  ExecAuxRowMark *aerm;
2668 
2669  subplan = mtstate->mt_plans[i]->plan;
2670  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
2671  mtstate->mt_arowmarks[i] = lappend(mtstate->mt_arowmarks[i], aerm);
2672  }
2673  }
2674 
2675  /* select first subplan */
2676  mtstate->mt_whichplan = 0;
2677  subplan = (Plan *) linitial(node->plans);
2678  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan,
2679  mtstate->mt_arowmarks[0]);
2680 
2681  /*
2682  * Initialize the junk filter(s) if needed. INSERT queries need a filter
2683  * if there are any junk attrs in the tlist. UPDATE and DELETE always
2684  * need a filter, since there's always at least one junk attribute present
2685  * --- no need to look first. Typically, this will be a 'ctid' or
2686  * 'wholerow' attribute, but in the case of a foreign data wrapper it
2687  * might be a set of junk attributes sufficient to identify the remote
2688  * row.
2689  *
2690  * If there are multiple result relations, each one needs its own junk
2691  * filter. Note multiple rels are only possible for UPDATE/DELETE, so we
2692  * can't be fooled by some needing a filter and some not.
2693  *
2694  * This section of code is also a convenient place to verify that the
2695  * output of an INSERT or UPDATE matches the target table(s).
2696  */
2697  {
2698  bool junk_filter_needed = false;
2699 
2700  switch (operation)
2701  {
2702  case CMD_INSERT:
2703  foreach(l, subplan->targetlist)
2704  {
2705  TargetEntry *tle = (TargetEntry *) lfirst(l);
2706 
2707  if (tle->resjunk)
2708  {
2709  junk_filter_needed = true;
2710  break;
2711  }
2712  }
2713  break;
2714  case CMD_UPDATE:
2715  case CMD_DELETE:
2716  junk_filter_needed = true;
2717  break;
2718  default:
2719  elog(ERROR, "unknown operation");
2720  break;
2721  }
2722 
2723  if (junk_filter_needed)
2724  {
2725  resultRelInfo = mtstate->resultRelInfo;
2726  for (i = 0; i < nplans; i++)
2727  {
2728  JunkFilter *j;
2729  TupleTableSlot *junkresslot;
2730 
2731  subplan = mtstate->mt_plans[i]->plan;
2732 
2733  junkresslot =
2734  ExecInitExtraTupleSlot(estate, NULL,
2735  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
2736 
2737  /*
2738  * For an INSERT or UPDATE, the result tuple must always match
2739  * the target table's descriptor. For a DELETE, it won't
2740  * (indeed, there's probably no non-junk output columns).
2741  */
2742  if (operation == CMD_INSERT || operation == CMD_UPDATE)
2743  {
2744  ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
2745  subplan->targetlist);
2747  RelationGetDescr(resultRelInfo->ri_RelationDesc),
2748  junkresslot);
2749  }
2750  else
2751  j = ExecInitJunkFilter(subplan->targetlist,
2752  junkresslot);
2753 
2754  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2755  {
2756  /* For UPDATE/DELETE, find the appropriate junk attr now */
2757  char relkind;
2758 
2759  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2760  if (relkind == RELKIND_RELATION ||
2761  relkind == RELKIND_MATVIEW ||
2762  relkind == RELKIND_PARTITIONED_TABLE)
2763  {
2764  j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
2766  elog(ERROR, "could not find junk ctid column");
2767  }
2768  else if (relkind == RELKIND_FOREIGN_TABLE)
2769  {
2770  /*
2771  * When there is a row-level trigger, there should be
2772  * a wholerow attribute.
2773  */
2774  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2775  }
2776  else
2777  {
2778  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2780  elog(ERROR, "could not find junk wholerow column");
2781  }
2782  }
2783 
2784  resultRelInfo->ri_junkFilter = j;
2785  resultRelInfo++;
2786  }
2787  }
2788  else
2789  {
2790  if (operation == CMD_INSERT)
2792  subplan->targetlist);
2793  }
2794  }
2795 
2796  /*
2797  * Determine if the FDW supports batch insert and determine the batch
2798  * size (a FDW may support batching, but it may be disabled for the
2799  * server/table).
2800  *
2801  * We only do this for INSERT, so that for UPDATE/DELETE the batch
2802  * size remains set to 0.
2803  */
2804  if (operation == CMD_INSERT)
2805  {
2806  resultRelInfo = mtstate->resultRelInfo;
2807  for (i = 0; i < nplans; i++)
2808  {
2809  if (!resultRelInfo->ri_usesFdwDirectModify &&
2810  resultRelInfo->ri_FdwRoutine != NULL &&
2811  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
2812  resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
2813  resultRelInfo->ri_BatchSize =
2814  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
2815  else
2816  resultRelInfo->ri_BatchSize = 1;
2817 
2818  Assert(resultRelInfo->ri_BatchSize >= 1);
2819 
2820  resultRelInfo++;
2821  }
2822  }
2823 
2824  /*
2825  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
2826  * to estate->es_auxmodifytables so that it will be run to completion by
2827  * ExecPostprocessPlan. (It'd actually work fine to add the primary
2828  * ModifyTable node too, but there's no need.) Note the use of lcons not
2829  * lappend: we need later-initialized ModifyTable nodes to be shut down
2830  * before earlier ones. This ensures that we don't throw away RETURNING
2831  * rows that need to be seen by a later CTE subplan.
2832  */
2833  if (!mtstate->canSetTag)
2834  estate->es_auxmodifytables = lcons(mtstate,
2835  estate->es_auxmodifytables);
2836 
2837  return mtstate;
2838 }
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:474
List * arbiterIndexes
Definition: plannodes.h:229
Relation ri_RelationDesc
Definition: execnodes.h:415
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:225
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:446
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1801
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1189
#define RelationGetDescr(relation)
Definition: rel.h:483
List * withCheckOptionLists
Definition: plannodes.h:222
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:834
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1170
AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
Definition: execJunk.c:234
ExprContext * ps_ExprContext
Definition: execnodes.h:980
TupleTableSlot ** mt_scans
Definition: execnodes.h:1168
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:58
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
TupleConversionMap * ri_ChildToRootMap
Definition: execnodes.h:512
JunkFilter * ExecInitJunkFilterInsertion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot)
Definition: execJunk.c:89
bool partColsUpdated
Definition: plannodes.h:219
bool canSetTag
Definition: plannodes.h:216
CmdType operation
Definition: execnodes.h:1162
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1176
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2419
EState * state
Definition: execnodes.h:943
Form_pg_class rd_rel
Definition: rel.h:110
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:156
List * ri_WithCheckOptionExprs
Definition: execnodes.h:462
#define linitial_int(l)
Definition: pg_list.h:175
TupleTableSlot * oc_Existing
Definition: execnodes.h:384
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:979
List * rowMarks
Definition: plannodes.h:226
bool resjunk
Definition: primnodes.h:1438
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:45
PlanState ps
Definition: execnodes.h:1161
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:278
bool ri_usesFdwDirectModify
Definition: execnodes.h:450
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:981
#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:1192
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1725
List * fdwPrivLists
Definition: plannodes.h:224
EPQState mt_epqstate
Definition: execnodes.h:1179
bool trig_update_before_row
Definition: reltrigger.h:61
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:480
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:444
ExprState * oc_WhereClause
Definition: execnodes.h:387
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, ModifyTableState *mtstate, Relation rel)
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:221
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:427
List * lappend(List *list, void *datum)
Definition: list.c:336
PlanState ** mt_plans
Definition: execnodes.h:1165
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2381
OnConflictSetState * ri_onConflict
Definition: execnodes.h:486
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
List * es_tupleTable
Definition: execnodes.h:574
void * palloc0(Size size)
Definition: mcxt.c:981
List * es_auxmodifytables
Definition: execnodes.h:586
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:947
List * ri_WithCheckOptions
Definition: execnodes.h:459
unsigned int Index
Definition: c.h:549
TupleDesc rd_att
Definition: rel.h:111
Plan * plan
Definition: execnodes.h:941
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:385
List * lcons(void *datum, List *list)
Definition: list.c:468
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:220
#define makeNode(_type_)
Definition: nodes.h:581
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
int ri_BatchSize
Definition: execnodes.h:454
#define Assert(condition)
Definition: c.h:804
#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:480
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:1186
TupleDesc ExecGetResultType(PlanState *planstate)
Definition: execUtils.c:490
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:227
int i
List * returningLists
Definition: plannodes.h:223
bool isParent
Definition: plannodes.h:1097
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:218
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:140
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:483
CmdType
Definition: nodes.h:676
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:421
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2257
List * ri_returningList
Definition: execnodes.h:477
List ** mt_arowmarks
Definition: execnodes.h:1178
int epqParam
Definition: plannodes.h:227
Node * onConflictWhere
Definition: plannodes.h:231
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2234

◆ ExecInsert()

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

Definition at line 385 of file nodeModifyTable.c.

References Assert, CMD_INSERT, CMD_UPDATE, TupleDescData::constr, EState::es_output_cid, EState::es_processed, EState::es_query_cxt, ExecARInsertTriggers(), ExecARUpdateTriggers(), ExecBatchInsert(), ExecBRInsertTriggers(), ExecCheckIndexConstraints(), ExecCheckTIDVisible(), ExecComputeStoredGenerated(), ExecConstraints(), ExecCopySlot(), FdwRoutine::ExecForeignInsert, ExecGetReturningSlot(), ExecInsertIndexTuples(), ExecIRInsertTriggers(), ExecMaterializeSlot(), ExecOnConflictUpdate(), ExecPartitionCheck(), ExecPrepareTupleRouting(), ExecProcessReturning(), ExecWithCheckOptions(), GetCurrentTransactionId(), TupleConstr::has_generated_stored, InstrCountTuples2, list_free(), MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, palloc(), PlanState::plan, ModifyTableState::ps, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_NumSlots, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_PlanSlots, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_Slots, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, SpeculativeInsertionLockAcquire(), SpeculativeInsertionLockRelease(), table_tuple_complete_speculative(), table_tuple_insert(), table_tuple_insert_speculative(), TransitionCaptureState::tcs_update_new_table, TriggerDesc::trig_insert_before_row, TriggerDesc::trig_insert_instead_row, TupleTableSlot::tts_ops, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_tupleDescriptor, WCO_RLS_INSERT_CHECK, WCO_RLS_UPDATE_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecCrossPartitionUpdate(), and ExecModifyTable().

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

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 2047 of file nodeModifyTable.c.

References Assert, AttributeNumberIsValid, ModifyTableState::canSetTag, castNode, CHECK_FOR_INTERRUPTS, CMD_DELETE, CMD_INSERT, CMD_UPDATE, DatumGetHeapTupleHeader, DatumGetPointer, elog, ERROR, EState::es_epq_active, EState::es_opened_result_relations, EState::es_tuple_routing_result_relations, EvalPlanQualSetPlan(), EvalPlanQualSetSlot, ExecBatchInsert(), ExecCopySlot(), ExecDelete(), ExecFilterJunk(), ExecGetJunkAttribute(), ExecInsert(), ExecProcessReturning(), ExecProcNode(), ExecUpdate(), fireASTriggers(), ModifyTableState::fireBSTriggers, fireBSTriggers(), HeapTupleHeaderGetDatumLength, InvalidOid, ItemPointerSetInvalid, JunkFilter::jf_junkAttNo, lfirst, ModifyTableState::mt_arowmarks, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_nplans, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_plans, ModifyTableState::mt_scans, ModifyTableState::mt_whichplan, NIL, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, RelationData::rd_rel, RelationGetRelid, ResetExprContext, ResetPerTupleExprContext, ModifyTableState::resultRelInfo, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_NumSlots, ResultRelInfo::ri_PlanSlots, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_Slots, 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().

2048 {
2049  ModifyTableState *node = castNode(ModifyTableState, pstate);
2050  EState *estate = node->ps.state;
2051  CmdType operation = node->operation;
2052  ResultRelInfo *resultRelInfo;
2053  PlanState *subplanstate;
2054  JunkFilter *junkfilter;
2055  TupleTableSlot *slot;
2056  TupleTableSlot *planSlot;
2057  ItemPointer tupleid;
2058  ItemPointerData tuple_ctid;
2059  HeapTupleData oldtupdata;
2060  HeapTuple oldtuple;
2062  List *relinfos = NIL;
2063  ListCell *lc;
2064 
2066 
2067  /*
2068  * This should NOT get called during EvalPlanQual; we should have passed a
2069  * subplan tree to EvalPlanQual, instead. Use a runtime test not just
2070  * Assert because this condition is easy to miss in testing. (Note:
2071  * although ModifyTable should not get executed within an EvalPlanQual
2072  * operation, we do have to allow it to be initialized and shut down in
2073  * case it is within a CTE subplan. Hence this test must be here, not in
2074  * ExecInitModifyTable.)
2075  */
2076  if (estate->es_epq_active != NULL)
2077  elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
2078 
2079  /*
2080  * If we've already completed processing, don't try to do more. We need
2081  * this test because ExecPostprocessPlan might call us an extra time, and
2082  * our subplan's nodes aren't necessarily robust against being called
2083  * extra times.
2084  */
2085  if (node->mt_done)
2086  return NULL;
2087 
2088  /*
2089  * On first call, fire BEFORE STATEMENT triggers before proceeding.
2090  */
2091  if (node->fireBSTriggers)
2092  {
2093  fireBSTriggers(node);
2094  node->fireBSTriggers = false;
2095  }
2096 
2097  /* Preload local variables */
2098  resultRelInfo = node->resultRelInfo + node->mt_whichplan;
2099  subplanstate = node->mt_plans[node->mt_whichplan];
2100  junkfilter = resultRelInfo->ri_junkFilter;
2101 
2102  /*
2103  * Fetch rows from subplan(s), and execute the required table modification
2104  * for each row.
2105  */
2106  for (;;)
2107  {
2108  /*
2109  * Reset the per-output-tuple exprcontext. This is needed because
2110  * triggers expect to use that context as workspace. It's a bit ugly
2111  * to do this below the top level of the plan, however. We might need
2112  * to rethink this later.
2113  */
2114  ResetPerTupleExprContext(estate);
2115 
2116  /*
2117  * Reset per-tuple memory context used for processing on conflict and
2118  * returning clauses, to free any expression evaluation storage
2119  * allocated in the previous cycle.
2120  */
2121  if (pstate->ps_ExprContext)
2123 
2124  planSlot = ExecProcNode(subplanstate);
2125 
2126  if (TupIsNull(planSlot))
2127  {
2128  /* advance to next subplan if any */
2129  node->mt_whichplan++;
2130  if (node->mt_whichplan < node->mt_nplans)
2131  {
2132  resultRelInfo++;
2133  subplanstate = node->mt_plans[node->mt_whichplan];
2134  junkfilter = resultRelInfo->ri_junkFilter;
2135  EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
2136  node->mt_arowmarks[node->mt_whichplan]);
2137  continue;
2138  }
2139  else
2140  break;
2141  }
2142 
2143  /*
2144  * Ensure input tuple is the right format for the target relation.
2145  */
2146  if (node->mt_scans[node->mt_whichplan]->tts_ops != planSlot->tts_ops)
2147  {
2148  ExecCopySlot(node->mt_scans[node->mt_whichplan], planSlot);
2149  planSlot = node->mt_scans[node->mt_whichplan];
2150  }
2151 
2152  /*
2153  * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
2154  * here is compute the RETURNING expressions.
2155  */
2156  if (resultRelInfo->ri_usesFdwDirectModify)
2157  {
2158  Assert(resultRelInfo->ri_projectReturning);
2159 
2160  /*
2161  * A scan slot containing the data that was actually inserted,
2162  * updated or deleted has already been made available to
2163  * ExecProcessReturning by IterateDirectModify, so no need to
2164  * provide it here.
2165  */
2166  slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
2167 
2168  return slot;
2169  }
2170 
2171  EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
2172  slot = planSlot;
2173 
2174  tupleid = NULL;
2175  oldtuple = NULL;
2176  if (junkfilter != NULL)
2177  {
2178  /*
2179  * extract the 'ctid' or 'wholerow' junk attribute.
2180  */
2181  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2182  {
2183  char relkind;
2184  Datum datum;
2185  bool isNull;
2186 
2187  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2188  if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW)
2189  {
2190  datum = ExecGetJunkAttribute(slot,
2191  junkfilter->jf_junkAttNo,
2192  &isNull);
2193  /* shouldn't ever get a null result... */
2194  if (isNull)
2195  elog(ERROR, "ctid is NULL");
2196 
2197  tupleid = (ItemPointer) DatumGetPointer(datum);
2198  tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
2199  tupleid = &tuple_ctid;
2200  }
2201 
2202  /*
2203  * Use the wholerow attribute, when available, to reconstruct
2204  * the old relation tuple.
2205  *
2206  * Foreign table updates have a wholerow attribute when the
2207  * relation has a row-level trigger. Note that the wholerow
2208  * attribute does not carry system columns. Foreign table
2209  * triggers miss seeing those, except that we know enough here
2210  * to set t_tableOid. Quite separately from this, the FDW may
2211  * fetch its own junk attrs to identify the row.
2212  *
2213  * Other relevant relkinds, currently limited to views, always
2214  * have a wholerow attribute.
2215  */
2216  else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo))
2217  {
2218  datum = ExecGetJunkAttribute(slot,
2219  junkfilter->jf_junkAttNo,
2220  &isNull);
2221  /* shouldn't ever get a null result... */
2222  if (isNull)
2223  elog(ERROR, "wholerow is NULL");
2224 
2225  oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
2226  oldtupdata.t_len =
2228  ItemPointerSetInvalid(&(oldtupdata.t_self));
2229  /* Historically, view triggers see invalid t_tableOid. */
2230  oldtupdata.t_tableOid =
2231  (relkind == RELKIND_VIEW) ? InvalidOid :
2232  RelationGetRelid(resultRelInfo->ri_RelationDesc);
2233 
2234  oldtuple = &oldtupdata;
2235  }
2236  else
2237  Assert(relkind == RELKIND_FOREIGN_TABLE);
2238  }
2239 
2240  /*
2241  * apply the junkfilter if needed.
2242  */
2243  if (operation != CMD_DELETE)
2244  slot = ExecFilterJunk(junkfilter, slot);
2245  }
2246 
2247  switch (operation)
2248  {
2249  case CMD_INSERT:
2250  slot = ExecInsert(node, resultRelInfo, slot, planSlot,
2251  estate, node->canSetTag);
2252  break;
2253  case CMD_UPDATE:
2254  slot = ExecUpdate(node, resultRelInfo, tupleid, oldtuple, slot,
2255  planSlot, &node->mt_epqstate, estate,
2256  node->canSetTag);
2257  break;
2258  case CMD_DELETE:
2259  slot = ExecDelete(node, resultRelInfo, tupleid, oldtuple,
2260  planSlot, &node->mt_epqstate, estate,
2261  true, /* processReturning */
2262  node->canSetTag,
2263  false, /* changingPart */
2264  NULL, NULL);
2265  break;
2266  default:
2267  elog(ERROR, "unknown operation");
2268  break;
2269  }
2270 
2271  /*
2272  * If we got a RETURNING result, return it to caller. We'll continue
2273  * the work on next call.
2274  */
2275  if (slot)
2276  return slot;
2277  }
2278 
2279  /*
2280  * Insert remaining tuples for batch insert.
2281  */
2282  if (proute)
2283  relinfos = estate->es_tuple_routing_result_relations;
2284  else
2285  relinfos = estate->es_opened_result_relations;
2286 
2287  foreach(lc, relinfos)
2288  {
2289  resultRelInfo = lfirst(lc);
2290  if (resultRelInfo->ri_NumSlots > 0)
2291  ExecBatchInsert(node, resultRelInfo,
2292  resultRelInfo->ri_Slots,
2293  resultRelInfo->ri_PlanSlots,
2294  resultRelInfo->ri_NumSlots,
2295  estate, node->canSetTag);
2296  }
2297 
2298  /*
2299  * We're done, but fire AFTER STATEMENT triggers before exiting.
2300  */
2301  fireASTriggers(node);
2302 
2303  node->mt_done = true;
2304 
2305  return NULL;
2306 }
AttrNumber jf_junkAttNo
Definition: execnodes.h:372
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
#define NIL
Definition: pg_list.h:65
JunkFilter * ri_junkFilter
Definition: execnodes.h:474
Relation ri_RelationDesc
Definition: execnodes.h:415
static void ExecBatchInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:456
#define ResetPerTupleExprContext(estate)
Definition: executor.h:518
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1189
#define castNode(_type_, nodeptr)
Definition: nodes.h:602
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1170
ExprContext * ps_ExprContext
Definition: execnodes.h:980
TupleTableSlot ** mt_scans
Definition: execnodes.h:1168
static void fireBSTriggers(ModifyTableState *node)
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:122
CmdType operation
Definition: execnodes.h:1162
TupleTableSlot ** ri_Slots
Definition: execnodes.h:455
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2419
EState * state
Definition: execnodes.h:943
Form_pg_class rd_rel
Definition: rel.h:110
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
struct EPQState * es_epq_active
Definition: execnodes.h:601
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
#define ERROR
Definition: elog.h:45
PlanState ps
Definition: execnodes.h:1161
ItemPointerData t_self
Definition: htup.h:65
bool ri_usesFdwDirectModify
Definition: execnodes.h:450
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
List * es_opened_result_relations
Definition: execnodes.h:550
EPQState mt_epqstate
Definition: execnodes.h:1179
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:480
#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:1165
#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:287
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:242
Plan * plan
Definition: execnodes.h:941
#define InvalidOid
Definition: postgres_ext.h:36
List * es_tuple_routing_result_relations
Definition: execnodes.h:560
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
#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:273
#define elog(elevel,...)
Definition: elog.h:227
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:100
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)
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:457
CmdType
Definition: nodes.h:676
#define ResetExprContext(econtext)
Definition: executor.h:503
List ** mt_arowmarks
Definition: execnodes.h:1178
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:217
#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 1682 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().

1690 {
1691  ExprContext *econtext = mtstate->ps.ps_ExprContext;
1692  Relation relation = resultRelInfo->ri_RelationDesc;
1693  ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
1694  TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
1695  TM_FailureData tmfd;
1696  LockTupleMode lockmode;
1697  TM_Result test;
1698  Datum xminDatum;
1699  TransactionId xmin;
1700  bool isnull;
1701 
1702  /* Determine lock mode to use */
1703  lockmode = ExecUpdateLockMode(estate, resultRelInfo);
1704 
1705  /*
1706  * Lock tuple for update. Don't follow updates when tuple cannot be
1707  * locked without doing so. A row locking conflict here means our
1708  * previous conclusion that the tuple is conclusively committed is not
1709  * true anymore.
1710  */
1711  test = table_tuple_lock(relation, conflictTid,
1712  estate->es_snapshot,
1713  existing, estate->es_output_cid,
1714  lockmode, LockWaitBlock, 0,
1715  &tmfd);
1716  switch (test)
1717  {
1718  case TM_Ok:
1719  /* success! */
1720  break;
1721 
1722  case TM_Invisible:
1723 
1724  /*
1725  * This can occur when a just inserted tuple is updated again in
1726  * the same command. E.g. because multiple rows with the same
1727  * conflicting key values are inserted.
1728  *
1729  * This is somewhat similar to the ExecUpdate() TM_SelfModified
1730  * case. We do not want to proceed because it would lead to the
1731  * same row being updated a second time in some unspecified order,
1732  * and in contrast to plain UPDATEs there's no historical behavior
1733  * to break.
1734  *
1735  * It is the user's responsibility to prevent this situation from
1736  * occurring. These problems are why SQL-2003 similarly specifies
1737  * that for SQL MERGE, an exception must be raised in the event of
1738  * an attempt to update the same row twice.
1739  */
1740  xminDatum = slot_getsysattr(existing,
1742  &isnull);
1743  Assert(!isnull);
1744  xmin = DatumGetTransactionId(xminDatum);
1745 
1747  ereport(ERROR,
1748  (errcode(ERRCODE_CARDINALITY_VIOLATION),
1749  errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
1750  errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
1751 
1752  /* This shouldn't happen */
1753  elog(ERROR, "attempted to lock invisible tuple");
1754  break;
1755 
1756  case TM_SelfModified:
1757 
1758  /*
1759  * This state should never be reached. As a dirty snapshot is used
1760  * to find conflicting tuples, speculative insertion wouldn't have
1761  * seen this row to conflict with.
1762  */
1763  elog(ERROR, "unexpected self-updated tuple");
1764  break;
1765 
1766  case TM_Updated:
1768  ereport(ERROR,
1769  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1770  errmsg("could not serialize access due to concurrent update")));
1771 
1772  /*
1773  * As long as we don't support an UPDATE of INSERT ON CONFLICT for
1774  * a partitioned table we shouldn't reach to a case where tuple to
1775  * be lock is moved to another partition due to concurrent update
1776  * of the partition key.
1777  */
1779 
1780  /*
1781  * Tell caller to try again from the very start.
1782  *
1783  * It does not make sense to use the usual EvalPlanQual() style
1784  * loop here, as the new version of the row might not conflict
1785  * anymore, or the conflicting tuple has actually been deleted.
1786  */
1787  ExecClearTuple(existing);
1788  return false;
1789 
1790  case TM_Deleted:
1792  ereport(ERROR,
1793  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1794  errmsg("could not serialize access due to concurrent delete")));
1795 
1796  /* see TM_Updated case */
1798  ExecClearTuple(existing);
1799  return false;
1800 
1801  default:
1802  elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
1803  }
1804 
1805  /* Success, the tuple is locked. */
1806 
1807  /*
1808  * Verify that the tuple is visible to our MVCC snapshot if the current
1809  * isolation level mandates that.
1810  *
1811  * It's not sufficient to rely on the check within ExecUpdate() as e.g.
1812  * CONFLICT ... WHERE clause may prevent us from reaching that.
1813  *
1814  * This means we only ever continue when a new command in the current
1815  * transaction could see the row, even though in READ COMMITTED mode the
1816  * tuple will not be visible according to the current statement's
1817  * snapshot. This is in line with the way UPDATE deals with newer tuple
1818  * versions.
1819  */
1820  ExecCheckTupleVisible(estate, relation, existing);
1821 
1822  /*
1823  * Make tuple and any needed join variables available to ExecQual and
1824  * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
1825  * the target's existing tuple is installed in the scantuple. EXCLUDED
1826  * has been made to reference INNER_VAR in setrefs.c, but there is no
1827  * other redirection.
1828  */
1829  econtext->ecxt_scantuple = existing;
1830  econtext->ecxt_innertuple = excludedSlot;
1831  econtext->ecxt_outertuple = NULL;
1832 
1833  if (!ExecQual(onConflictSetWhere, econtext))
1834  {
1835  ExecClearTuple(existing); /* see return below */
1836  InstrCountFiltered1(&mtstate->ps, 1);
1837  return true; /* done with the tuple */
1838  }
1839 
1840  if (resultRelInfo->ri_WithCheckOptions != NIL)
1841  {
1842  /*
1843  * Check target's existing tuple against UPDATE-applicable USING
1844  * security barrier quals (if any), enforced here as RLS checks/WCOs.
1845  *
1846  * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
1847  * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
1848  * but that's almost the extent of its special handling for ON
1849  * CONFLICT DO UPDATE.
1850  *
1851  * The rewriter will also have associated UPDATE applicable straight
1852  * RLS checks/WCOs for the benefit of the ExecUpdate() call that
1853  * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
1854  * kinds, so there is no danger of spurious over-enforcement in the
1855  * INSERT or UPDATE path.
1856  */
1858  existing,
1859  mtstate->ps.state);
1860  }
1861 
1862  /* Project the new tuple version */
1863  ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
1864 
1865  /*
1866  * Note that it is possible that the target tuple has been modified in
1867  * this session, after the above table_tuple_lock. We choose to not error
1868  * out in that case, in line with ExecUpdate's treatment of similar cases.
1869  * This can happen if an UPDATE is triggered from within ExecQual(),
1870  * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
1871  * wCTE in the ON CONFLICT's SET.
1872  */
1873 
1874  /* Execute UPDATE with projection */
1875  *returning = ExecUpdate(mtstate, resultRelInfo, conflictTid, NULL,
1876  resultRelInfo->ri_onConflict->oc_ProjSlot,
1877  planSlot,
1878  &mtstate->mt_epqstate, mtstate->ps.state,
1879  canSetTag);
1880 
1881  /*
1882  * Clear out existing tuple, as there might not be another conflict among
1883  * the next input rows. Don't want to hold resources till the end of the
1884  * query.
1885  */
1886  ExecClearTuple(existing);
1887  return true;
1888 }
#define NIL
Definition: pg_list.h:65
ItemPointerData ctid
Definition: tableam.h:126
Relation ri_RelationDesc
Definition: execnodes.h:415
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1152
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1922
CommandId es_output_cid
Definition: execnodes.h:544
static void test(void)
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
uint32 TransactionId
Definition: c.h:587
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:869
ExprContext * ps_ExprContext
Definition: execnodes.h:980
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
int errcode(int sqlerrcode)
Definition: elog.c:694
Snapshot es_snapshot
Definition: execnodes.h:530
EState * state
Definition: execnodes.h:943
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:372
TupleTableSlot * oc_Existing
Definition: execnodes.h:384
#define ERROR
Definition: elog.h:45
PlanState ps
Definition: execnodes.h:1161
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:1549
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:1179
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:227
ExprState * oc_WhereClause
Definition: execnodes.h:387
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1043
OnConflictSetState * ri_onConflict
Definition: execnodes.h:486
TM_Result
Definition: tableam.h:71
uintptr_t Datum
Definition: postgres.h:367
List * ri_WithCheckOptions
Definition: execnodes.h:459
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
#define ereport(elevel,...)
Definition: elog.h:155
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:385
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:229
#define Assert(condition)
Definition: c.h:804
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2208
Definition: tableam.h:77
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:225
#define DatumGetTransactionId(X)
Definition: postgres.h:514
int errmsg(const char *fmt,...)
Definition: elog.c:905
#define elog(elevel,...)
Definition: elog.h:227
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:335

◆ ExecPrepareTupleRouting()

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

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

1993 {
1994  ResultRelInfo *partrel;
1995  TupleConversionMap *map;
1996 
1997  /*
1998  * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
1999  * not find a valid partition for the tuple in 'slot' then an error is
2000  * raised. An error may also be raised if the found partition is not a
2001  * valid target for INSERTs. This is required since a partitioned table
2002  * UPDATE to another partition becomes a DELETE+INSERT.
2003  */
2004  partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
2005 
2006  /*
2007  * If we're capturing transition tuples, we might need to convert from the
2008  * partition rowtype to root partitioned table's rowtype. But if there
2009  * are no BEFORE triggers on the partition that could change the tuple, we
2010  * can just remember the original unconverted tuple to avoid a needless
2011  * round trip conversion.
2012  */
2013  if (mtstate->mt_transition_capture != NULL)
2014  {
2015  bool has_before_insert_row_trig;
2016 
2017  has_before_insert_row_trig = (partrel->ri_TrigDesc &&
2019 
2021  !has_before_insert_row_trig ? slot : NULL;
2022  }
2023 
2024  /*
2025  * Convert the tuple, if necessary.
2026  */
2027  map = partrel->ri_RootToPartitionMap;
2028  if (map != NULL)
2029  {
2030  TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
2031 
2032  slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
2033  }
2034 
2035  *partRelInfo = partrel;
2036  return slot;
2037 }
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:505
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1192
AttrMap * attrMap
Definition: tupconvert.h:28
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:427
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:504
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 166 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().

169 {
170  ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
171  ExprContext *econtext = projectReturning->pi_exprContext;
172 
173  /* Make tuple and any needed join variables available to ExecProject */
174  if (tupleSlot)
175  econtext->ecxt_scantuple = tupleSlot;
176  econtext->ecxt_outertuple = planSlot;
177 
178  /*
179  * RETURNING expressions might reference the tableoid column, so
180  * reinitialize tts_tableOid before evaluating them.
181  */
182  econtext->ecxt_scantuple->tts_tableOid =
183  RelationGetRelid(resultRelInfo->ri_RelationDesc);
184 
185  /* Compute the RETURNING expressions */
186  return ExecProject(projectReturning);
187 }
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:415
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:480
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:457
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:335

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 2903 of file nodeModifyTable.c.

References elog, and ERROR.

Referenced by ExecReScan().

2904 {
2905  /*
2906  * Currently, we don't need to support rescan on ModifyTable nodes. The
2907  * semantics of that would be a bit debatable anyway.
2908  */
2909  elog(ERROR, "ExecReScanModifyTable is not implemented");
2910 }
#define ERROR
Definition: elog.h:45
#define elog(elevel,...)
Definition: elog.h:227

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

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

1959 {
1960  ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
1961  ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
1962 
1963  /* Check for transition tables on the directly targeted relation. */
1964  mtstate->mt_transition_capture =
1965  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1966  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1967  mtstate->operation);
1968  if (plan->operation == CMD_INSERT &&
1970  mtstate->mt_oc_transition_capture =
1971  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1972  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1973  CMD_UPDATE);
1974 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1195
CmdType operation
Definition: execnodes.h:1162
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1176
PlanState ps
Definition: execnodes.h:1161
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1192
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4393
Plan * plan
Definition: execnodes.h:941
OnConflictAction onConflictAction
Definition: plannodes.h:228
CmdType operation
Definition: plannodes.h:215
#define RelationGetRelid(relation)
Definition: rel.h:457

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

1335 {
1336  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1337  TM_Result result;
1338  TM_FailureData tmfd;
1339  List *recheckIndexes = NIL;
1340 
1341  /*
1342  * abort the operation if not running transactions
1343  */
1345  elog(ERROR, "cannot UPDATE during bootstrap");
1346 
1347  ExecMaterializeSlot(slot);
1348 
1349  /* BEFORE ROW UPDATE Triggers */
1350  if (resultRelInfo->ri_TrigDesc &&
1351  resultRelInfo->ri_TrigDesc->trig_update_before_row)
1352  {
1353  if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
1354  tupleid, oldtuple, slot))
1355  return NULL; /* "do nothing" */
1356  }
1357 
1358  /* INSTEAD OF ROW UPDATE Triggers */
1359  if (resultRelInfo->ri_TrigDesc &&
1360  resultRelInfo->ri_TrigDesc->trig_update_instead_row)
1361  {
1362  if (!ExecIRUpdateTriggers(estate, resultRelInfo,
1363  oldtuple, slot))
1364  return NULL; /* "do nothing" */
1365  }
1366  else if (resultRelInfo->ri_FdwRoutine)
1367  {
1368  /*
1369  * Compute stored generated columns
1370  */
1371  if (resultRelationDesc->rd_att->constr &&
1372  resultRelationDesc->rd_att->constr->has_generated_stored)
1373  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1374  CMD_UPDATE);
1375 
1376  /*
1377  * update in foreign table: let the FDW do it
1378  */
1379  slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
1380  resultRelInfo,
1381  slot,
1382  planSlot);
1383 
1384  if (slot == NULL) /* "do nothing" */
1385  return NULL;
1386 
1387  /*
1388  * AFTER ROW Triggers or RETURNING expressions might reference the
1389  * tableoid column, so (re-)initialize tts_tableOid before evaluating
1390  * them.
1391  */
1392  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1393  }
1394  else
1395  {
1396  LockTupleMode lockmode;
1397  bool partition_constraint_failed;
1398  bool update_indexes;
1399 
1400  /*
1401  * Constraints might reference the tableoid column, so (re-)initialize
1402  * tts_tableOid before evaluating them.
1403  */
1404  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1405 
1406  /*
1407  * Compute stored generated columns
1408  */
1409  if (resultRelationDesc->rd_att->constr &&
1410  resultRelationDesc->rd_att->constr->has_generated_stored)
1411  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1412  CMD_UPDATE);
1413 
1414  /*
1415  * Check any RLS UPDATE WITH CHECK policies
1416  *
1417  * If we generate a new candidate tuple after EvalPlanQual testing, we
1418  * must loop back here and recheck any RLS policies and constraints.
1419  * (We don't need to redo triggers, however. If there are any BEFORE
1420  * triggers then trigger.c will have done table_tuple_lock to lock the
1421  * correct tuple, so there's no need to do them again.)
1422  */
1423 lreplace:;
1424 
1425  /* ensure slot is independent, consider e.g. EPQ */
1426  ExecMaterializeSlot(slot);
1427 
1428  /*
1429  * If partition constraint fails, this row might get moved to another
1430  * partition, in which case we should check the RLS CHECK policy just
1431  * before inserting into the new partition, rather than doing it here.
1432  * This is because a trigger on that partition might again change the
1433  * row. So skip the WCO checks if the partition constraint fails.
1434  */
1435  partition_constraint_failed =
1436  resultRelationDesc->rd_rel->relispartition &&
1437  !ExecPartitionCheck(resultRelInfo, slot, estate, false);
1438 
1439  if (!partition_constraint_failed &&
1440  resultRelInfo->ri_WithCheckOptions != NIL)
1441  {
1442  /*
1443  * ExecWithCheckOptions() will skip any WCOs which are not of the
1444  * kind we are looking for at this point.
1445  */
1447  resultRelInfo, slot, estate);
1448  }
1449 
1450  /*
1451  * If a partition check failed, try to move the row into the right
1452  * partition.
1453  */
1454  if (partition_constraint_failed)
1455  {
1456  TupleTableSlot *inserted_tuple,
1457  *retry_slot;
1458  bool retry;
1459 
1460  /*
1461  * ExecCrossPartitionUpdate will first DELETE the row from the
1462  * partition it's currently in and then insert it back into the
1463  * root table, which will re-route it to the correct partition.
1464  * The first part may have to be repeated if it is detected that
1465  * the tuple we're trying to move has been concurrently updated.
1466  */
1467  retry = !ExecCrossPartitionUpdate(mtstate, resultRelInfo, tupleid,
1468  oldtuple, slot, planSlot,
1469  epqstate, canSetTag,
1470  &retry_slot, &inserted_tuple);
1471  if (retry)
1472  {
1473  slot = retry_slot;
1474  goto lreplace;
1475  }
1476 
1477  return inserted_tuple;
1478  }
1479 
1480  /*
1481  * Check the constraints of the tuple. We've already checked the
1482  * partition constraint above; however, we must still ensure the tuple
1483  * passes all other constraints, so we will call ExecConstraints() and
1484  * have it validate all remaining checks.
1485  */
1486  if (resultRelationDesc->rd_att->constr)
1487  ExecConstraints(resultRelInfo, slot, estate);
1488 
1489  /*
1490  * replace the heap tuple
1491  *
1492  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
1493  * that the row to be updated is visible to that snapshot, and throw a
1494  * can't-serialize error if not. This is a special-case behavior
1495  * needed for referential integrity updates in transaction-snapshot
1496  * mode transactions.
1497  */
1498  result = table_tuple_update(resultRelationDesc, tupleid, slot,
1499  estate->es_output_cid,
1500  estate->es_snapshot,
1501  estate->es_crosscheck_snapshot,
1502  true /* wait for commit */ ,
1503  &tmfd, &lockmode, &update_indexes);
1504 
1505  switch (result)
1506  {
1507  case TM_SelfModified:
1508 
1509  /*
1510  * The target tuple was already updated or deleted by the
1511  * current command, or by a later command in the current
1512  * transaction. The former case is possible in a join UPDATE
1513  * where multiple tuples join to the same target tuple. This
1514  * is pretty questionable, but Postgres has always allowed it:
1515  * we just execute the first update action and ignore
1516  * additional update attempts.
1517  *
1518  * The latter case arises if the tuple is modified by a
1519  * command in a BEFORE trigger, or perhaps by a command in a
1520  * volatile function used in the query. In such situations we
1521  * should not ignore the update, but it is equally unsafe to
1522  * proceed. We don't want to discard the original UPDATE
1523  * while keeping the triggered actions based on it; and we
1524  * have no principled way to merge this update with the
1525  * previous ones. So throwing an error is the only safe
1526  * course.
1527  *
1528  * If a trigger actually intends this type of interaction, it
1529  * can re-execute the UPDATE (assuming it can figure out how)
1530  * and then return NULL to cancel the outer update.
1531  */
1532  if (tmfd.cmax != estate->es_output_cid)
1533  ereport(ERROR,
1534  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1535  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1536  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1537 
1538  /* Else, already updated by self; nothing to do */
1539  return NULL;
1540 
1541  case TM_Ok:
1542  break;
1543 
1544  case TM_Updated:
1545  {
1546  TupleTableSlot *inputslot;
1547  TupleTableSlot *epqslot;
1548 
1550  ereport(ERROR,
1551  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1552  errmsg("could not serialize access due to concurrent update")));
1553 
1554  /*
1555  * Already know that we're going to need to do EPQ, so
1556  * fetch tuple directly into the right slot.
1557  */
1558  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
1559  resultRelInfo->ri_RangeTableIndex);
1560 
1561  result = table_tuple_lock(resultRelationDesc, tupleid,
1562  estate->es_snapshot,
1563  inputslot, estate->es_output_cid,
1564  lockmode, LockWaitBlock,
1566  &tmfd);
1567 
1568  switch (result)
1569  {
1570  case TM_Ok:
1571  Assert(tmfd.traversed);
1572 
1573  epqslot = EvalPlanQual(epqstate,
1574  resultRelationDesc,
1575  resultRelInfo->ri_RangeTableIndex,
1576  inputslot);
1577  if (TupIsNull(epqslot))
1578  /* Tuple not passing quals anymore, exiting... */
1579  return NULL;
1580 
1581  slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1582  goto lreplace;
1583 
1584  case TM_Deleted:
1585  /* tuple already deleted; nothing to do */
1586  return NULL;
1587 
1588  case TM_SelfModified:
1589 
1590  /*
1591  * This can be reached when following an update
1592  * chain from a tuple updated by another session,
1593  * reaching a tuple that was already updated in
1594  * this transaction. If previously modified by
1595  * this command, ignore the redundant update,
1596  * otherwise error out.
1597  *
1598  * See also TM_SelfModified response to
1599  * table_tuple_update() above.
1600  */
1601  if (tmfd.cmax != estate->es_output_cid)
1602  ereport(ERROR,
1603  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1604  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1605  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1606  return NULL;
1607 
1608  default:
1609  /* see table_tuple_lock call in ExecDelete() */
1610  elog(ERROR, "unexpected table_tuple_lock status: %u",
1611  result);
1612  return NULL;
1613  }
1614  }
1615 
1616  break;
1617 
1618  case TM_Deleted:
1620  ereport(ERROR,
1621  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1622  errmsg("could not serialize access due to concurrent delete")));
1623  /* tuple already deleted; nothing to do */
1624  return NULL;
1625 
1626  default:
1627  elog(ERROR, "unrecognized table_tuple_update status: %u",
1628  result);
1629  return NULL;
1630  }
1631 
1632  /* insert index entries for tuple if necessary */
1633  if (resultRelInfo->ri_NumIndices > 0 && update_indexes)
1634  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1635  slot, estate, true, false,
1636  NULL, NIL);
1637  }
1638 
1639  if (canSetTag)
1640  (estate->es_processed)++;
1641 
1642  /* AFTER ROW UPDATE Triggers */
1643  ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, slot,
1644  recheckIndexes,
1645  mtstate->operation == CMD_INSERT ?
1646  mtstate->mt_oc_transition_capture :
1647  mtstate->mt_transition_capture);
1648 
1649  list_free(recheckIndexes);
1650 
1651  /*
1652  * Check any WITH CHECK OPTION constraints from parent views. We are
1653  * required to do this after testing all constraints and uniqueness
1654  * violations per the SQL spec, so we do it after actually updating the
1655  * record in the heap and all indexes.
1656  *
1657  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1658  * are looking for at this point.
1659  */
1660  if (resultRelInfo->ri_WithCheckOptions != NIL)
1661  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1662 
1663  /* Process RETURNING if present */
1664  if (resultRelInfo->ri_projectReturning)
1665  return ExecProcessReturning(resultRelInfo, slot, planSlot);
1666 
1667  return NULL;
1668 }
int ri_NumIndices
Definition: execnodes.h:418
#define NIL
Definition: pg_list.h:65
Oid tts_tableOid
Definition: tuptable.h:131
JunkFilter * ri_junkFilter
Definition: execnodes.h:474
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1195
Relation ri_RelationDesc
Definition: execnodes.h:415
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1152
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1922
CommandId es_output_cid
Definition: execnodes.h:544
CommandId cmax
Definition: tableam.h:128
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1789
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:531
int errcode(int sqlerrcode)
Definition: elog.c:694
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
CmdType operation
Definition: execnodes.h:1162
Snapshot es_snapshot
Definition: execnodes.h:530
Form_pg_class rd_rel
Definition: rel.h:110
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool update, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:284
Index ri_RangeTableIndex
Definition: execnodes.h:412
bool has_generated_stored
Definition: tupdesc.h:45
#define ERROR
Definition: elog.h:45
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:1549
TupleTableSlot * EvalPlanQualSlot(EPQState *epqstate, Relation relation, Index rti)
Definition: execMain.c:2436
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1192
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:2327
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:480
#define TupIsNull(slot)
Definition: tuptable.h:292
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:444
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:427
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:2823
TM_Result
Definition: tableam.h:71
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:287
List * ri_WithCheckOptions
Definition: execnodes.h:459
TupleDesc rd_att
Definition: rel.h:111
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
#define ereport(elevel,...)
Definition: elog.h:155
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:222
#define Assert(condition)
Definition: c.h:804
Definition: tableam.h:77
uint64 es_processed
Definition: execnodes.h:576
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:242
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:394
int errmsg(const char *fmt,...)
Definition: elog.c:905
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2865
void list_free(List *list)
Definition: list.c:1391
#define elog(elevel,...)
Definition: elog.h:227
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2693
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1665
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:1504
bool traversed
Definition: tableam.h:129
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:457
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 1924 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().

1925 {
1926  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1927  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
1928 
1929  switch (node->operation)
1930  {
1931  case CMD_INSERT:
1932  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1934  resultRelInfo,
1935  node->mt_oc_transition_capture);
1936  ExecASInsertTriggers(node->ps.state, resultRelInfo,
1937  node->mt_transition_capture);
1938  break;
1939  case CMD_UPDATE:
1940  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
1941  node->mt_transition_capture);
1942  break;
1943  case CMD_DELETE:
1944  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
1945  node->mt_transition_capture);
1946  break;
1947  default:
1948  elog(ERROR, "unknown operation");
1949  break;
1950  }
1951 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1195
CmdType operation
Definition: execnodes.h:1162
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1176
EState * state
Definition: execnodes.h:943
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2439
#define ERROR
Definition: elog.h:45
PlanState ps
Definition: execnodes.h:1161
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1192
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2677
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2225
Plan * plan
Definition: execnodes.h:941
OnConflictAction onConflictAction
Definition: plannodes.h:228
#define elog(elevel,...)
Definition: elog.h:227

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

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

1896 {
1897  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1898  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
1899 
1900  switch (node->operation)
1901  {
1902  case CMD_INSERT:
1903  ExecBSInsertTriggers(node->ps.state, resultRelInfo);
1904  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1906  resultRelInfo);
1907  break;
1908  case CMD_UPDATE:
1909  ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
1910  break;
1911  case CMD_DELETE:
1912  ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
1913  break;
1914  default:
1915  elog(ERROR, "unknown operation");
1916  break;
1917  }
1918 }
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2388
CmdType operation
Definition: execnodes.h:1162
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1176
EState * state
Definition: execnodes.h:943
#define ERROR
Definition: elog.h:45
PlanState ps
Definition: execnodes.h:1161
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2174
Plan * plan
Definition: execnodes.h:941
OnConflictAction onConflictAction
Definition: plannodes.h:228
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2619
#define elog(elevel,...)
Definition: elog.h:227