PostgreSQL Source Code  git master
nodeModifyTable.c File Reference
#include "postgres.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "commands/trigger.h"
#include "executor/execPartition.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for nodeModifyTable.c:

Go to the source code of this file.

Data Structures

struct  MTTargetRelLookup
 

Macros

#define MT_NRELS_HASH   64
 

Typedefs

typedef struct MTTargetRelLookup MTTargetRelLookup
 

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 void ExecInitInsertProjection (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static void ExecInitUpdateProjection (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 
static TupleTableSlotExecGetInsertNewTuple (ResultRelInfo *relinfo, TupleTableSlot *planSlot)
 
TupleTableSlotExecGetUpdateNewTuple (ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
 
static TupleTableSlotExecInsert (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)
 
ResultRelInfoExecLookupResultRelByOid (ModifyTableState *node, Oid resultoid, bool missing_ok, bool update_cache)
 
ModifyTableStateExecInitModifyTable (ModifyTable *node, EState *estate, int eflags)
 
void ExecEndModifyTable (ModifyTableState *node)
 
void ExecReScanModifyTable (ModifyTableState *node)
 

Macro Definition Documentation

◆ MT_NRELS_HASH

#define MT_NRELS_HASH   64

Referenced by ExecInitModifyTable().

Typedef Documentation

◆ MTTargetRelLookup

Function Documentation

◆ ExecBatchInsert()

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

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

990 {
991  int i;
992  int numInserted = numSlots;
993  TupleTableSlot *slot = NULL;
994  TupleTableSlot **rslots;
995 
996  /*
997  * insert into foreign table: let the FDW do it
998  */
999  rslots = resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert(estate,
1000  resultRelInfo,
1001  slots,
1002  planSlots,
1003  &numInserted);
1004 
1005  for (i = 0; i < numInserted; i++)
1006  {
1007  slot = rslots[i];
1008 
1009  /*
1010  * AFTER ROW Triggers or RETURNING expressions might reference the
1011  * tableoid column, so (re-)initialize tts_tableOid before evaluating
1012  * them.
1013  */
1014  slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
1015 
1016  /* AFTER ROW INSERT Triggers */
1017  ExecARInsertTriggers(estate, resultRelInfo, slot, NIL,
1018  mtstate->mt_transition_capture);
1019 
1020  /*
1021  * Check any WITH CHECK OPTION constraints from parent views. See the
1022  * comment in ExecInsert.
1023  */
1024  if (resultRelInfo->ri_WithCheckOptions != NIL)
1025  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1026  }
1027 
1028  if (canSetTag && numInserted > 0)
1029  estate->es_processed += numInserted;
1030 
1031  for (i = 0; i < numSlots; i++)
1032  {
1033  ExecDropSingleTupleTableSlot(slots[i]);
1034  ExecDropSingleTupleTableSlot(planSlots[i]);
1035  }
1036 }
#define NIL
Definition: pg_list.h:65
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:411
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:1954
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
List * ri_WithCheckOptions
Definition: execnodes.h:470
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
uint64 es_processed
Definition: execnodes.h:602
int i
#define RelationGetRelid(relation)
Definition: rel.h:472

◆ ExecCheckPlanOutput()

static void ExecCheckPlanOutput ( Relation  resultRel,
List targetList 
)
static

Definition at line 101 of file nodeModifyTable.c.

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

Referenced by ExecInitInsertProjection().

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

◆ ExecCheckTIDVisible()

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

Definition at line 236 of file nodeModifyTable.c.

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

Referenced by ExecInsert().

240 {
241  Relation rel = relinfo->ri_RelationDesc;
242 
243  /* Redundantly check isolation level */
245  return;
246 
247  if (!table_tuple_fetch_row_version(rel, tid, SnapshotAny, tempSlot))
248  elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
249  ExecCheckTupleVisible(estate, rel, tempSlot);
250  ExecClearTuple(tempSlot);
251 }
Relation ri_RelationDesc
Definition: execnodes.h:411
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
#define ERROR
Definition: elog.h:46
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1257
#define SnapshotAny
Definition: snapmgr.h:67
#define elog(elevel,...)
Definition: elog.h:232

◆ ExecCheckTupleVisible()

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

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

205 {
207  return;
208 
209  if (!table_tuple_satisfies_snapshot(rel, slot, estate->es_snapshot))
210  {
211  Datum xminDatum;
212  TransactionId xmin;
213  bool isnull;
214 
215  xminDatum = slot_getsysattr(slot, MinTransactionIdAttributeNumber, &isnull);
216  Assert(!isnull);
217  xmin = DatumGetTransactionId(xminDatum);
218 
219  /*
220  * We should not raise a serialization failure if the conflict is
221  * against a tuple inserted by our own transaction, even if it's not
222  * visible to our snapshot. (This would happen, for example, if
223  * conflicting keys are proposed for insertion in a single command.)
224  */
226  ereport(ERROR,
227  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
228  errmsg("could not serialize access due to concurrent update")));
229  }
230 }
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:698
Snapshot es_snapshot
Definition: execnodes.h:556
#define ERROR
Definition: elog.h:46
uintptr_t Datum
Definition: postgres.h:411
#define ereport(elevel,...)
Definition: elog.h:157
#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:558
int errmsg(const char *fmt,...)
Definition: elog.c:909
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 257 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().

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

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

References Assert, TupleConversionMap::attrMap, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, EState::es_query_cxt, ExecDelete(), ExecGetChildToRootMap(), ExecGetUpdateNewTuple(), ExecInitUpdateProjection(), ExecInsert(), ExecPartitionCheckEmitError(), ExecSetupPartitionTupleRouting(), execute_attr_map_slot(), MemoryContextSwitchTo(), ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, PlanState::plan, ModifyTableState::ps, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, ModifyTableState::rootResultRelInfo, SnapshotAny, PlanState::state, table_slot_create(), table_tuple_fetch_row_version(), TransitionCaptureState::tcs_original_insert_tuple, TupIsNull, and unlikely.

Referenced by ExecUpdate().

1417 {
1418  EState *estate = mtstate->ps.state;
1419  TupleConversionMap *tupconv_map;
1420  bool tuple_deleted;
1421  TupleTableSlot *epqslot = NULL;
1422 
1423  *inserted_tuple = NULL;
1424  *retry_slot = NULL;
1425 
1426  /*
1427  * Disallow an INSERT ON CONFLICT DO UPDATE that causes the original row
1428  * to migrate to a different partition. Maybe this can be implemented
1429  * some day, but it seems a fringe feature with little redeeming value.
1430  */
1431  if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1432  ereport(ERROR,
1433  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1434  errmsg("invalid ON UPDATE specification"),
1435  errdetail("The result tuple would appear in a different partition than the original tuple.")));
1436 
1437  /*
1438  * When an UPDATE is run directly on a leaf partition, simply fail with a
1439  * partition constraint violation error.
1440  */
1441  if (resultRelInfo == mtstate->rootResultRelInfo)
1442  ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1443 
1444  /* Initialize tuple routing info if not already done. */
1445  if (mtstate->mt_partition_tuple_routing == NULL)
1446  {
1447  Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
1448  MemoryContext oldcxt;
1449 
1450  /* Things built here have to last for the query duration. */
1451  oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
1452 
1453  mtstate->mt_partition_tuple_routing =
1454  ExecSetupPartitionTupleRouting(estate, rootRel);
1455 
1456  /*
1457  * Before a partition's tuple can be re-routed, it must first be
1458  * converted to the root's format, so we'll need a slot for storing
1459  * such tuples.
1460  */
1461  Assert(mtstate->mt_root_tuple_slot == NULL);
1462  mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
1463 
1464  MemoryContextSwitchTo(oldcxt);
1465  }
1466 
1467  /*
1468  * Row movement, part 1. Delete the tuple, but skip RETURNING processing.
1469  * We want to return rows from INSERT.
1470  */
1471  ExecDelete(mtstate, resultRelInfo, tupleid, oldtuple, planSlot,
1472  epqstate, estate,
1473  false, /* processReturning */
1474  false, /* canSetTag */
1475  true, /* changingPart */
1476  &tuple_deleted, &epqslot);
1477 
1478  /*
1479  * For some reason if DELETE didn't happen (e.g. trigger prevented it, or
1480  * it was already deleted by self, or it was concurrently deleted by
1481  * another transaction), then we should skip the insert as well;
1482  * otherwise, an UPDATE could cause an increase in the total number of
1483  * rows across all partitions, which is clearly wrong.
1484  *
1485  * For a normal UPDATE, the case where the tuple has been the subject of a
1486  * concurrent UPDATE or DELETE would be handled by the EvalPlanQual
1487  * machinery, but for an UPDATE that we've translated into a DELETE from
1488  * this partition and an INSERT into some other partition, that's not
1489  * available, because CTID chains can't span relation boundaries. We
1490  * mimic the semantics to a limited extent by skipping the INSERT if the
1491  * DELETE fails to find a tuple. This ensures that two concurrent
1492  * attempts to UPDATE the same tuple at the same time can't turn one tuple
1493  * into two, and that an UPDATE of a just-deleted tuple can't resurrect
1494  * it.
1495  */
1496  if (!tuple_deleted)
1497  {
1498  /*
1499  * epqslot will be typically NULL. But when ExecDelete() finds that
1500  * another transaction has concurrently updated the same row, it
1501  * re-fetches the row, skips the delete, and epqslot is set to the
1502  * re-fetched tuple slot. In that case, we need to do all the checks
1503  * again.
1504  */
1505  if (TupIsNull(epqslot))
1506  return true;
1507  else
1508  {
1509  /* Fetch the most recent version of old tuple. */
1510  TupleTableSlot *oldSlot;
1511 
1512  /* ... but first, make sure ri_oldTupleSlot is initialized. */
1513  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
1514  ExecInitUpdateProjection(mtstate, resultRelInfo);
1515  oldSlot = resultRelInfo->ri_oldTupleSlot;
1516  if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc,
1517  tupleid,
1518  SnapshotAny,
1519  oldSlot))
1520  elog(ERROR, "failed to fetch tuple being updated");
1521  *retry_slot = ExecGetUpdateNewTuple(resultRelInfo, epqslot,
1522  oldSlot);
1523  return false;
1524  }
1525  }
1526 
1527  /*
1528  * resultRelInfo is one of the per-relation resultRelInfos. So we should
1529  * convert the tuple into root's tuple descriptor if needed, since
1530  * ExecInsert() starts the search from root.
1531  */
1532  tupconv_map = ExecGetChildToRootMap(resultRelInfo);
1533  if (tupconv_map != NULL)
1534  slot = execute_attr_map_slot(tupconv_map->attrMap,
1535  slot,
1536  mtstate->mt_root_tuple_slot);
1537 
1538  /* Tuple routing starts from the root table. */
1539  *inserted_tuple = ExecInsert(mtstate, mtstate->rootResultRelInfo, slot,
1540  planSlot, estate, canSetTag);
1541 
1542  /*
1543  * Reset the transition state that may possibly have been written by
1544  * INSERT.
1545  */
1546  if (mtstate->mt_transition_capture)
1548 
1549  /* We're done moving. */
1550  return true;
1551 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
Relation ri_RelationDesc
Definition: execnodes.h:411
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1219
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecInitUpdateProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:698
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1196
EState * state
Definition: execnodes.h:966
bool ri_projectNewInfoValid
Definition: execnodes.h:435
MemoryContext es_query_cxt
Definition: execnodes.h:598
#define ERROR
Definition: elog.h:46
PlanState ps
Definition: execnodes.h:1183
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
int errdetail(const char *fmt,...)
Definition: elog.c:1042
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1750
TupleConversionMap * ExecGetChildToRootMap(ResultRelInfo *resultRelInfo)
Definition: execUtils.c:1234
#define TupIsNull(slot)
Definition: tuptable.h:292
AttrMap * attrMap
Definition: tupconvert.h:28
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1257
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
Plan * plan
Definition: execnodes.h:964
#define ereport(elevel,...)
Definition: elog.h:157
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:177
#define Assert(condition)
Definition: c.h:804
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:433
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1216
#define SnapshotAny
Definition: snapmgr.h:67
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
#define unlikely(x)
Definition: c.h:273
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
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)

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

1073 {
1074  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1075  TM_Result result;
1076  TM_FailureData tmfd;
1077  TupleTableSlot *slot = NULL;
1078  TransitionCaptureState *ar_delete_trig_tcs;
1079 
1080  if (tupleDeleted)
1081  *tupleDeleted = false;
1082 
1083  /* BEFORE ROW DELETE Triggers */
1084  if (resultRelInfo->ri_TrigDesc &&
1085  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
1086  {
1087  bool dodelete;
1088 
1089  dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
1090  tupleid, oldtuple, epqreturnslot);
1091 
1092  if (!dodelete) /* "do nothing" */
1093  return NULL;
1094  }
1095 
1096  /* INSTEAD OF ROW DELETE Triggers */
1097  if (resultRelInfo->ri_TrigDesc &&
1098  resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
1099  {
1100  bool dodelete;
1101 
1102  Assert(oldtuple != NULL);
1103  dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
1104 
1105  if (!dodelete) /* "do nothing" */
1106  return NULL;
1107  }
1108  else if (resultRelInfo->ri_FdwRoutine)
1109  {
1110  /*
1111  * delete from foreign table: let the FDW do it
1112  *
1113  * We offer the returning slot as a place to store RETURNING data,
1114  * although the FDW can return some other slot if it wants.
1115  */
1116  slot = ExecGetReturningSlot(estate, resultRelInfo);
1117  slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
1118  resultRelInfo,
1119  slot,
1120  planSlot);
1121 
1122  if (slot == NULL) /* "do nothing" */
1123  return NULL;
1124 
1125  /*
1126  * RETURNING expressions might reference the tableoid column, so
1127  * (re)initialize tts_tableOid before evaluating them.
1128  */
1129  if (TTS_EMPTY(slot))
1130  ExecStoreAllNullTuple(slot);
1131 
1132  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1133  }
1134  else
1135  {
1136  /*
1137  * delete the tuple
1138  *
1139  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
1140  * that the row to be deleted is visible to that snapshot, and throw a
1141  * can't-serialize error if not. This is a special-case behavior
1142  * needed for referential integrity updates in transaction-snapshot
1143  * mode transactions.
1144  */
1145 ldelete:;
1146  result = table_tuple_delete(resultRelationDesc, tupleid,
1147  estate->es_output_cid,
1148  estate->es_snapshot,
1149  estate->es_crosscheck_snapshot,
1150  true /* wait for commit */ ,
1151  &tmfd,
1152  changingPart);
1153 
1154  switch (result)
1155  {
1156  case TM_SelfModified:
1157 
1158  /*
1159  * The target tuple was already updated or deleted by the
1160  * current command, or by a later command in the current
1161  * transaction. The former case is possible in a join DELETE
1162  * where multiple tuples join to the same target tuple. This
1163  * is somewhat questionable, but Postgres has always allowed
1164  * it: we just ignore additional deletion attempts.
1165  *
1166  * The latter case arises if the tuple is modified by a
1167  * command in a BEFORE trigger, or perhaps by a command in a
1168  * volatile function used in the query. In such situations we
1169  * should not ignore the deletion, but it is equally unsafe to
1170  * proceed. We don't want to discard the original DELETE
1171  * while keeping the triggered actions based on its deletion;
1172  * and it would be no better to allow the original DELETE
1173  * while discarding updates that it triggered. The row update
1174  * carries some information that might be important according
1175  * to business rules; so throwing an error is the only safe
1176  * course.
1177  *
1178  * If a trigger actually intends this type of interaction, it
1179  * can re-execute the DELETE and then return NULL to cancel
1180  * the outer delete.
1181  */
1182  if (tmfd.cmax != estate->es_output_cid)
1183  ereport(ERROR,
1184  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1185  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1186  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1187 
1188  /* Else, already deleted by self; nothing to do */
1189  return NULL;
1190 
1191  case TM_Ok:
1192  break;
1193 
1194  case TM_Updated:
1195  {
1196  TupleTableSlot *inputslot;
1197  TupleTableSlot *epqslot;
1198 
1200  ereport(ERROR,
1201  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1202  errmsg("could not serialize access due to concurrent update")));
1203 
1204  /*
1205  * Already know that we're going to need to do EPQ, so
1206  * fetch tuple directly into the right slot.
1207  */
1208  EvalPlanQualBegin(epqstate);
1209  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
1210  resultRelInfo->ri_RangeTableIndex);
1211 
1212  result = table_tuple_lock(resultRelationDesc, tupleid,
1213  estate->es_snapshot,
1214  inputslot, estate->es_output_cid,
1217  &tmfd);
1218 
1219  switch (result)
1220  {
1221  case TM_Ok:
1222  Assert(tmfd.traversed);
1223  epqslot = EvalPlanQual(epqstate,
1224  resultRelationDesc,
1225  resultRelInfo->ri_RangeTableIndex,
1226  inputslot);
1227  if (TupIsNull(epqslot))
1228  /* Tuple not passing quals anymore, exiting... */
1229  return NULL;
1230 
1231  /*
1232  * If requested, skip delete and pass back the
1233  * updated row.
1234  */
1235  if (epqreturnslot)
1236  {
1237  *epqreturnslot = epqslot;
1238  return NULL;
1239  }
1240  else
1241  goto ldelete;
1242 
1243  case TM_SelfModified:
1244 
1245  /*
1246  * This can be reached when following an update
1247  * chain from a tuple updated by another session,
1248  * reaching a tuple that was already updated in
1249  * this transaction. If previously updated by this
1250  * command, ignore the delete, otherwise error
1251  * out.
1252  *
1253  * See also TM_SelfModified response to
1254  * table_tuple_delete() above.
1255  */
1256  if (tmfd.cmax != estate->es_output_cid)
1257  ereport(ERROR,
1258  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1259  errmsg("tuple to be deleted was already modified by an operation triggered by the current command"),
1260  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1261  return NULL;
1262 
1263  case TM_Deleted:
1264  /* tuple already deleted; nothing to do */
1265  return NULL;
1266 
1267  default:
1268 
1269  /*
1270  * TM_Invisible should be impossible because we're
1271  * waiting for updated row versions, and would
1272  * already have errored out if the first version
1273  * is invisible.
1274  *
1275  * TM_Updated should be impossible, because we're
1276  * locking the latest version via
1277  * TUPLE_LOCK_FLAG_FIND_LAST_VERSION.
1278  */
1279  elog(ERROR, "unexpected table_tuple_lock status: %u",
1280  result);
1281  return NULL;
1282  }
1283 
1284  Assert(false);
1285  break;
1286  }
1287 
1288  case TM_Deleted:
1290  ereport(ERROR,
1291  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1292  errmsg("could not serialize access due to concurrent delete")));
1293  /* tuple already deleted; nothing to do */
1294  return NULL;
1295 
1296  default:
1297  elog(ERROR, "unrecognized table_tuple_delete status: %u",
1298  result);
1299  return NULL;
1300  }
1301 
1302  /*
1303  * Note: Normally one would think that we have to delete index tuples
1304  * associated with the heap tuple now...
1305  *
1306  * ... but in POSTGRES, we have no need to do this because VACUUM will
1307  * take care of it later. We can't delete index tuples immediately
1308  * anyway, since the tuple is still visible to other transactions.
1309  */
1310  }
1311 
1312  if (canSetTag)
1313  (estate->es_processed)++;
1314 
1315  /* Tell caller that the delete actually happened. */
1316  if (tupleDeleted)
1317  *tupleDeleted = true;
1318 
1319  /*
1320  * If this delete is the result of a partition key update that moved the
1321  * tuple to a new partition, put this row into the transition OLD TABLE,
1322  * if there is one. We need to do this separately for DELETE and INSERT
1323  * because they happen on different tables.
1324  */
1325  ar_delete_trig_tcs = mtstate->mt_transition_capture;
1326  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
1328  {
1329  ExecARUpdateTriggers(estate, resultRelInfo,
1330  tupleid,
1331  oldtuple,
1332  NULL,
1333  NULL,
1334  mtstate->mt_transition_capture);
1335 
1336  /*
1337  * We've already captured the NEW TABLE row, so make sure any AR
1338  * DELETE trigger fired below doesn't capture it again.
1339  */
1340  ar_delete_trig_tcs = NULL;
1341  }
1342 
1343  /* AFTER ROW DELETE Triggers */
1344  ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
1345  ar_delete_trig_tcs);
1346 
1347  /* Process RETURNING if present and if requested */
1348  if (processReturning && resultRelInfo->ri_projectReturning)
1349  {
1350  /*
1351  * We have to put the target tuple into a slot, which means first we
1352  * gotta fetch it. We can use the trigger tuple slot.
1353  */
1354  TupleTableSlot *rslot;
1355 
1356  if (resultRelInfo->ri_FdwRoutine)
1357  {
1358  /* FDW must have provided a slot containing the deleted row */
1359  Assert(!TupIsNull(slot));
1360  }
1361  else
1362  {
1363  slot = ExecGetReturningSlot(estate, resultRelInfo);
1364  if (oldtuple != NULL)
1365  {
1366  ExecForceStoreHeapTuple(oldtuple, slot, false);
1367  }
1368  else
1369  {
1370  if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
1371  SnapshotAny, slot))
1372  elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
1373  }
1374  }
1375 
1376  rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
1377 
1378  /*
1379  * Before releasing the target tuple again, make sure rslot has a
1380  * local copy of any pass-by-reference values.
1381  */
1382  ExecMaterializeSlot(rslot);
1383 
1384  ExecClearTuple(slot);
1385 
1386  return rslot;
1387  }
1388 
1389  return NULL;
1390 }
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:236
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:411
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition: trigger.c:2573
int errhint(const char *fmt,...)
Definition: elog.c:1156
TupleTableSlot * ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo)
Definition: execUtils.c:1210
CommandId es_output_cid
Definition: execnodes.h:570
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1576
#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:557
int errcode(int sqlerrcode)
Definition: elog.c:698
CmdType operation
Definition: execnodes.h:1184
Snapshot es_snapshot
Definition: execnodes.h:556
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1469
Index ri_RangeTableIndex
Definition: execnodes.h:408
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot **epqslot)
Definition: trigger.c:2457
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
Definition: trigger.c:2542
#define ERROR
Definition: elog.h:46
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:2469
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
TupleTableSlot * EvalPlanQual(EPQState *epqstate, Relation relation, Index rti, TupleTableSlot *inputslot)
Definition: execMain.c:2359
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:488
#define TupIsNull(slot)
Definition: tuptable.h:292
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:438
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:2826
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:157
#define Assert(condition)
Definition: c.h:804
Definition: tableam.h:77
uint64 es_processed
Definition: execnodes.h:602
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:242
#define SnapshotAny
Definition: snapmgr.h:67
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
bool traversed
Definition: tableam.h:129
#define RelationGetRelid(relation)
Definition: rel.h:472
void EvalPlanQualBegin(EPQState *epqstate)
Definition: execMain.c:2622
bool trig_delete_before_row
Definition: reltrigger.h:66

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 3141 of file nodeModifyTable.c.

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

Referenced by ExecEndNode().

3142 {
3143  int i;
3144 
3145  /*
3146  * Allow any FDWs to shut down
3147  */
3148  for (i = 0; i < node->mt_nrels; i++)
3149  {
3150  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
3151 
3152  if (!resultRelInfo->ri_usesFdwDirectModify &&
3153  resultRelInfo->ri_FdwRoutine != NULL &&
3154  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
3155  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
3156  resultRelInfo);
3157  }
3158 
3159  /*
3160  * Close all the partitioned tables, leaf partitions, and their indices
3161  * and release the slot used for tuple routing, if set.
3162  */
3163  if (node->mt_partition_tuple_routing)
3164  {
3166 
3167  if (node->mt_root_tuple_slot)
3169  }
3170 
3171  /*
3172  * Free the exprcontext
3173  */
3174  ExecFreeExprContext(&node->ps);
3175 
3176  /*
3177  * clean out the tuple table
3178  */
3179  if (node->ps.ps_ResultTupleSlot)
3181 
3182  /*
3183  * Terminate EPQ execution if active
3184  */
3185  EvalPlanQualEnd(&node->mt_epqstate);
3186 
3187  /*
3188  * shut down subplan
3189  */
3190  ExecEndNode(outerPlanState(node));
3191 }
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1219
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:555
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1188
EState * state
Definition: execnodes.h:966
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:650
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2834
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1002
PlanState ps
Definition: execnodes.h:1183
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
bool ri_usesFdwDirectModify
Definition: execnodes.h:461
#define outerPlanState(node)
Definition: execnodes.h:1058
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
EPQState mt_epqstate
Definition: execnodes.h:1198
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1216
int i

◆ ExecGetInsertNewTuple()

static TupleTableSlot* ExecGetInsertNewTuple ( ResultRelInfo relinfo,
TupleTableSlot planSlot 
)
static

Definition at line 511 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

513 {
514  ProjectionInfo *newProj = relinfo->ri_projectNew;
515  ExprContext *econtext;
516 
517  /*
518  * If there's no projection to be done, just make sure the slot is of the
519  * right type for the target rel. If the planSlot is the right type we
520  * can use it as-is, else copy the data into ri_newTupleSlot.
521  */
522  if (newProj == NULL)
523  {
524  if (relinfo->ri_newTupleSlot->tts_ops != planSlot->tts_ops)
525  {
526  ExecCopySlot(relinfo->ri_newTupleSlot, planSlot);
527  return relinfo->ri_newTupleSlot;
528  }
529  else
530  return planSlot;
531  }
532 
533  /*
534  * Else project; since the projection output slot is ri_newTupleSlot, this
535  * will also fix any slot-type problem.
536  *
537  * Note: currently, this is dead code, because INSERT cases don't receive
538  * any junk columns so there's never a projection to be done.
539  */
540  econtext = newProj->pi_exprContext;
541  econtext->ecxt_outertuple = planSlot;
542  return ExecProject(newProj);
543 }
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
ProjectionInfo * ri_projectNew
Definition: execnodes.h:429
const TupleTableSlotOps *const tts_ops
Definition: tuptable.h:122
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:431
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:230
ExprContext * pi_exprContext
Definition: execnodes.h:335
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:358

◆ ExecGetUpdateNewTuple()

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

Definition at line 555 of file nodeModifyTable.c.

References Assert, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, ExecProject(), ProjectionInfo::pi_exprContext, ResultRelInfo::ri_projectNew, ResultRelInfo::ri_projectNewInfoValid, and TTS_EMPTY.

Referenced by exec_rt_fetch(), ExecBRUpdateTriggers(), ExecCrossPartitionUpdate(), ExecModifyTable(), and ExecUpdate().

558 {
559  ProjectionInfo *newProj = relinfo->ri_projectNew;
560  ExprContext *econtext;
561 
562  /* Use a few extra Asserts to protect against outside callers */
563  Assert(relinfo->ri_projectNewInfoValid);
564  Assert(planSlot != NULL && !TTS_EMPTY(planSlot));
565  Assert(oldSlot != NULL && !TTS_EMPTY(oldSlot));
566 
567  econtext = newProj->pi_exprContext;
568  econtext->ecxt_outertuple = planSlot;
569  econtext->ecxt_scantuple = oldSlot;
570  return ExecProject(newProj);
571 }
#define TTS_EMPTY(slot)
Definition: tuptable.h:97
ProjectionInfo * ri_projectNew
Definition: execnodes.h:429
bool ri_projectNewInfoValid
Definition: execnodes.h:435
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:230
#define Assert(condition)
Definition: c.h:804
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:226
ExprContext * pi_exprContext
Definition: execnodes.h:335
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:358

◆ ExecInitInsertProjection()

static void ExecInitInsertProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 384 of file nodeModifyTable.c.

References EState::es_tupleTable, ExecAssignExprContext(), ExecBuildProjectionInfo(), ExecCheckPlanOutput(), lappend(), lfirst, NIL, outerPlan, PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, RelationGetDescr, TargetEntry::resjunk, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_projectNew, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, PlanState::state, and table_slot_create().

Referenced by ExecModifyTable().

386 {
387  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
388  Plan *subplan = outerPlan(node);
389  EState *estate = mtstate->ps.state;
390  List *insertTargetList = NIL;
391  bool need_projection = false;
392  ListCell *l;
393 
394  /* Extract non-junk columns of the subplan's result tlist. */
395  foreach(l, subplan->targetlist)
396  {
397  TargetEntry *tle = (TargetEntry *) lfirst(l);
398 
399  if (!tle->resjunk)
400  insertTargetList = lappend(insertTargetList, tle);
401  else
402  need_projection = true;
403  }
404 
405  /*
406  * The junk-free list must produce a tuple suitable for the result
407  * relation.
408  */
409  ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, insertTargetList);
410 
411  /* We'll need a slot matching the table's format. */
412  resultRelInfo->ri_newTupleSlot =
413  table_slot_create(resultRelInfo->ri_RelationDesc,
414  &estate->es_tupleTable);
415 
416  /* Build ProjectionInfo if needed (it probably isn't). */
417  if (need_projection)
418  {
419  TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
420 
421  /* need an expression context to do the projection */
422  if (mtstate->ps.ps_ExprContext == NULL)
423  ExecAssignExprContext(estate, &mtstate->ps);
424 
425  resultRelInfo->ri_projectNew =
426  ExecBuildProjectionInfo(insertTargetList,
427  mtstate->ps.ps_ExprContext,
428  resultRelInfo->ri_newTupleSlot,
429  &mtstate->ps,
430  relDesc);
431  }
432 
433  resultRelInfo->ri_projectNewInfoValid = true;
434 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
#define NIL
Definition: pg_list.h:65
Relation ri_RelationDesc
Definition: execnodes.h:411
#define RelationGetDescr(relation)
Definition: rel.h:498
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
ProjectionInfo * ri_projectNew
Definition: execnodes.h:429
EState * state
Definition: execnodes.h:966
bool ri_projectNewInfoValid
Definition: execnodes.h:435
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:431
bool resjunk
Definition: primnodes.h:1451
PlanState ps
Definition: execnodes.h:1183
#define outerPlan(node)
Definition: plannodes.h:171
List * lappend(List *list, void *datum)
Definition: list.c:336
List * es_tupleTable
Definition: execnodes.h:600
Plan * plan
Definition: execnodes.h:964
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define lfirst(lc)
Definition: pg_list.h:169
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:480
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:353
Definition: pg_list.h:50

◆ ExecInitModifyTable()

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

Definition at line 2682 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, CurrentMemoryContext, elog, HASHCTL::entrysize, ModifyTable::epqParam, ERROR, EState::es_auxmodifytables, EState::es_tupleTable, EvalPlanQualInit(), EvalPlanQualSetPlan(), EXEC_FLAG_BACKWARD, EXEC_FLAG_EXPLAIN_ONLY, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecBuildAuxRowMark(), ExecBuildProjectionInfo(), ExecFindJunkAttributeInTlist(), ExecFindRowMark(), FdwRoutine::ExecForeignBatchInsert, ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitQual(), ExecInitResultRelation(), ExecInitResultTupleSlotTL(), ExecInitResultTypeTL(), ExecModifyTable(), PlanState::ExecProcNode, ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), ExecTypeFromTL(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, FdwRoutine::GetForeignModifyBatchSize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_ENTER, hash_search(), HASHCTL::hcxt, i, InvalidOid, PlanRowMark::isParent, HASHCTL::keysize, lappend(), lcons(), lfirst, lfirst_int, lfirst_node, linitial, linitial_int, list_length(), list_nth(), makeNode, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_nrels, MT_NRELS_HASH, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_resultOidAttno, ModifyTableState::mt_resultOidHash, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTable::operation, ModifyTableState::operation, outerPlan, outerPlanState, palloc(), PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, MTTargetRelLookup::relationIndex, ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_usesFdwDirectModify, ResultRelInfo::ri_WithCheckOptionExprs, ResultRelInfo::ri_WithCheckOptions, ModifyTable::rootRelation, ModifyTableState::rootResultRelInfo, ModifyTable::rowMarks, PlanRowMark::rti, PlanState::state, table_slot_callbacks(), table_slot_create(), Plan::targetlist, TTSOpsVirtual, and ModifyTable::withCheckOptionLists.

Referenced by ExecInitNode().

2683 {
2684  ModifyTableState *mtstate;
2685  Plan *subplan = outerPlan(node);
2686  CmdType operation = node->operation;
2687  int nrels = list_length(node->resultRelations);
2688  ResultRelInfo *resultRelInfo;
2689  List *arowmarks;
2690  ListCell *l;
2691  int i;
2692  Relation rel;
2693 
2694  /* check for unsupported flags */
2695  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2696 
2697  /*
2698  * create state structure
2699  */
2700  mtstate = makeNode(ModifyTableState);
2701  mtstate->ps.plan = (Plan *) node;
2702  mtstate->ps.state = estate;
2703  mtstate->ps.ExecProcNode = ExecModifyTable;
2704 
2705  mtstate->operation = operation;
2706  mtstate->canSetTag = node->canSetTag;
2707  mtstate->mt_done = false;
2708 
2709  mtstate->mt_nrels = nrels;
2710  mtstate->resultRelInfo = (ResultRelInfo *)
2711  palloc(nrels * sizeof(ResultRelInfo));
2712 
2713  /*----------
2714  * Resolve the target relation. This is the same as:
2715  *
2716  * - the relation for which we will fire FOR STATEMENT triggers,
2717  * - the relation into whose tuple format all captured transition tuples
2718  * must be converted, and
2719  * - the root partitioned table used for tuple routing.
2720  *
2721  * If it's a partitioned table, the root partition doesn't appear
2722  * elsewhere in the plan and its RT index is given explicitly in
2723  * node->rootRelation. Otherwise (i.e. table inheritance) the target
2724  * relation is the first relation in the node->resultRelations list.
2725  *----------
2726  */
2727  if (node->rootRelation > 0)
2728  {
2730  ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
2731  node->rootRelation);
2732  }
2733  else
2734  {
2735  mtstate->rootResultRelInfo = mtstate->resultRelInfo;
2736  ExecInitResultRelation(estate, mtstate->resultRelInfo,
2737  linitial_int(node->resultRelations));
2738  }
2739 
2740  /* set up epqstate with dummy subplan data for the moment */
2741  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
2742  mtstate->fireBSTriggers = true;
2743 
2744  /*
2745  * Build state for collecting transition tuples. This requires having a
2746  * valid trigger query context, so skip it in explain-only mode.
2747  */
2748  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
2749  ExecSetupTransitionCaptureState(mtstate, estate);
2750 
2751  /*
2752  * Open all the result relations and initialize the ResultRelInfo structs.
2753  * (But root relation was initialized above, if it's part of the array.)
2754  * We must do this before initializing the subplan, because direct-modify
2755  * FDWs expect their ResultRelInfos to be available.
2756  */
2757  resultRelInfo = mtstate->resultRelInfo;
2758  i = 0;
2759  foreach(l, node->resultRelations)
2760  {
2761  Index resultRelation = lfirst_int(l);
2762 
2763  if (resultRelInfo != mtstate->rootResultRelInfo)
2764  {
2765  ExecInitResultRelation(estate, resultRelInfo, resultRelation);
2766 
2767  /*
2768  * For child result relations, store the root result relation
2769  * pointer. We do so for the convenience of places that want to
2770  * look at the query's original target relation but don't have the
2771  * mtstate handy.
2772  */
2773  resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
2774  }
2775 
2776  /* Initialize the usesFdwDirectModify flag */
2777  resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
2778  node->fdwDirectModifyPlans);
2779 
2780  /*
2781  * Verify result relation is a valid target for the current operation
2782  */
2783  CheckValidResultRel(resultRelInfo, operation);
2784 
2785  resultRelInfo++;
2786  i++;
2787  }
2788 
2789  /*
2790  * Now we may initialize the subplan.
2791  */
2792  outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
2793 
2794  /*
2795  * Do additional per-result-relation initialization.
2796  */
2797  for (i = 0; i < nrels; i++)
2798  {
2799  resultRelInfo = &mtstate->resultRelInfo[i];
2800 
2801  /* Let FDWs init themselves for foreign-table result rels */
2802  if (!resultRelInfo->ri_usesFdwDirectModify &&
2803  resultRelInfo->ri_FdwRoutine != NULL &&
2804  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
2805  {
2806  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
2807 
2808  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
2809  resultRelInfo,
2810  fdw_private,
2811  i,
2812  eflags);
2813  }
2814 
2815  /*
2816  * For UPDATE/DELETE, find the appropriate junk attr now, either a
2817  * 'ctid' or 'wholerow' attribute depending on relkind. For foreign
2818  * tables, the FDW might have created additional junk attr(s), but
2819  * those are no concern of ours.
2820  */
2821  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2822  {
2823  char relkind;
2824 
2825  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2826  if (relkind == RELKIND_RELATION ||
2827  relkind == RELKIND_MATVIEW ||
2828  relkind == RELKIND_PARTITIONED_TABLE)
2829  {
2830  resultRelInfo->ri_RowIdAttNo =
2831  ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
2832  if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
2833  elog(ERROR, "could not find junk ctid column");
2834  }
2835  else if (relkind == RELKIND_FOREIGN_TABLE)
2836  {
2837  /*
2838  * When there is a row-level trigger, there should be a
2839  * wholerow attribute. We also require it to be present in
2840  * UPDATE, so we can get the values of unchanged columns.
2841  */
2842  resultRelInfo->ri_RowIdAttNo =
2844  "wholerow");
2845  if (mtstate->operation == CMD_UPDATE &&
2846  !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
2847  elog(ERROR, "could not find junk wholerow column");
2848  }
2849  else
2850  {
2851  /* Other valid target relkinds must provide wholerow */
2852  resultRelInfo->ri_RowIdAttNo =
2854  "wholerow");
2855  if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
2856  elog(ERROR, "could not find junk wholerow column");
2857  }
2858  }
2859  }
2860 
2861  /*
2862  * If this is an inherited update/delete, there will be a junk attribute
2863  * named "tableoid" present in the subplan's targetlist. It will be used
2864  * to identify the result relation for a given tuple to be
2865  * updated/deleted.
2866  */
2867  mtstate->mt_resultOidAttno =
2868  ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
2869  Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || nrels == 1);
2870  mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
2871  mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
2872 
2873  /* Get the root target relation */
2874  rel = mtstate->rootResultRelInfo->ri_RelationDesc;
2875 
2876  /*
2877  * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
2878  * might need this too, but only if it actually moves tuples between
2879  * partitions; in that case setup is done by ExecCrossPartitionUpdate.
2880  */
2881  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2882  operation == CMD_INSERT)
2883  mtstate->mt_partition_tuple_routing =
2884  ExecSetupPartitionTupleRouting(estate, rel);
2885 
2886  /*
2887  * Initialize any WITH CHECK OPTION constraints if needed.
2888  */
2889  resultRelInfo = mtstate->resultRelInfo;
2890  foreach(l, node->withCheckOptionLists)
2891  {
2892  List *wcoList = (List *) lfirst(l);
2893  List *wcoExprs = NIL;
2894  ListCell *ll;
2895 
2896  foreach(ll, wcoList)
2897  {
2898  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
2899  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
2900  &mtstate->ps);
2901 
2902  wcoExprs = lappend(wcoExprs, wcoExpr);
2903  }
2904 
2905  resultRelInfo->ri_WithCheckOptions = wcoList;
2906  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
2907  resultRelInfo++;
2908  }
2909 
2910  /*
2911  * Initialize RETURNING projections if needed.
2912  */
2913  if (node->returningLists)
2914  {
2915  TupleTableSlot *slot;
2916  ExprContext *econtext;
2917 
2918  /*
2919  * Initialize result tuple slot and assign its rowtype using the first
2920  * RETURNING list. We assume the rest will look the same.
2921  */
2922  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
2923 
2924  /* Set up a slot for the output of the RETURNING projection(s) */
2926  slot = mtstate->ps.ps_ResultTupleSlot;
2927 
2928  /* Need an econtext too */
2929  if (mtstate->ps.ps_ExprContext == NULL)
2930  ExecAssignExprContext(estate, &mtstate->ps);
2931  econtext = mtstate->ps.ps_ExprContext;
2932 
2933  /*
2934  * Build a projection for each result rel.
2935  */
2936  resultRelInfo = mtstate->resultRelInfo;
2937  foreach(l, node->returningLists)
2938  {
2939  List *rlist = (List *) lfirst(l);
2940 
2941  resultRelInfo->ri_returningList = rlist;
2942  resultRelInfo->ri_projectReturning =
2943  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
2944  resultRelInfo->ri_RelationDesc->rd_att);
2945  resultRelInfo++;
2946  }
2947  }
2948  else
2949  {
2950  /*
2951  * We still must construct a dummy result tuple type, because InitPlan
2952  * expects one (maybe should change that?).
2953  */
2954  mtstate->ps.plan->targetlist = NIL;
2955  ExecInitResultTypeTL(&mtstate->ps);
2956 
2957  mtstate->ps.ps_ExprContext = NULL;
2958  }
2959 
2960  /* Set the list of arbiter indexes if needed for ON CONFLICT */
2961  resultRelInfo = mtstate->resultRelInfo;
2962  if (node->onConflictAction != ONCONFLICT_NONE)
2963  {
2964  /* insert may only have one relation, inheritance is not expanded */
2965  Assert(nrels == 1);
2966  resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
2967  }
2968 
2969  /*
2970  * If needed, Initialize target list, projection and qual for ON CONFLICT
2971  * DO UPDATE.
2972  */
2973  if (node->onConflictAction == ONCONFLICT_UPDATE)
2974  {
2975  ExprContext *econtext;
2976  TupleDesc relationDesc;
2977  TupleDesc tupDesc;
2978 
2979  /* already exists if created by RETURNING processing above */
2980  if (mtstate->ps.ps_ExprContext == NULL)
2981  ExecAssignExprContext(estate, &mtstate->ps);
2982 
2983  econtext = mtstate->ps.ps_ExprContext;
2984  relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
2985 
2986  /* create state for DO UPDATE SET operation */
2987  resultRelInfo->ri_onConflict = makeNode(OnConflictSetState);
2988 
2989  /* initialize slot for the existing tuple */
2990  resultRelInfo->ri_onConflict->oc_Existing =
2991  table_slot_create(resultRelInfo->ri_RelationDesc,
2992  &mtstate->ps.state->es_tupleTable);
2993 
2994  /*
2995  * Create the tuple slot for the UPDATE SET projection. We want a slot
2996  * of the table's type here, because the slot will be used to insert
2997  * into the table, and for RETURNING processing - which may access
2998  * system attributes.
2999  */
3000  tupDesc = ExecTypeFromTL((List *) node->onConflictSet);
3001  resultRelInfo->ri_onConflict->oc_ProjSlot =
3002  ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
3003  table_slot_callbacks(resultRelInfo->ri_RelationDesc));
3004 
3005  /* build UPDATE SET projection state */
3006  resultRelInfo->ri_onConflict->oc_ProjInfo =
3007  ExecBuildProjectionInfo(node->onConflictSet, econtext,
3008  resultRelInfo->ri_onConflict->oc_ProjSlot,
3009  &mtstate->ps,
3010  relationDesc);
3011 
3012  /* initialize state to evaluate the WHERE clause, if any */
3013  if (node->onConflictWhere)
3014  {
3015  ExprState *qualexpr;
3016 
3017  qualexpr = ExecInitQual((List *) node->onConflictWhere,
3018  &mtstate->ps);
3019  resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
3020  }
3021  }
3022 
3023  /*
3024  * If we have any secondary relations in an UPDATE or DELETE, they need to
3025  * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
3026  * EvalPlanQual mechanism needs to be told about them. Locate the
3027  * relevant ExecRowMarks.
3028  */
3029  arowmarks = NIL;
3030  foreach(l, node->rowMarks)
3031  {
3033  ExecRowMark *erm;
3034  ExecAuxRowMark *aerm;
3035 
3036  /* ignore "parent" rowmarks; they are irrelevant at runtime */
3037  if (rc->isParent)
3038  continue;
3039 
3040  /* Find ExecRowMark and build ExecAuxRowMark */
3041  erm = ExecFindRowMark(estate, rc->rti, false);
3042  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
3043  arowmarks = lappend(arowmarks, aerm);
3044  }
3045 
3046  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
3047 
3048  /*
3049  * If there are a lot of result relations, use a hash table to speed the
3050  * lookups. If there are not a lot, a simple linear search is faster.
3051  *
3052  * It's not clear where the threshold is, but try 64 for starters. In a
3053  * debugging build, use a small threshold so that we get some test
3054  * coverage of both code paths.
3055  */
3056 #ifdef USE_ASSERT_CHECKING
3057 #define MT_NRELS_HASH 4
3058 #else
3059 #define MT_NRELS_HASH 64
3060 #endif
3061  if (nrels >= MT_NRELS_HASH)
3062  {
3063  HASHCTL hash_ctl;
3064 
3065  hash_ctl.keysize = sizeof(Oid);
3066  hash_ctl.entrysize = sizeof(MTTargetRelLookup);
3067  hash_ctl.hcxt = CurrentMemoryContext;
3068  mtstate->mt_resultOidHash =
3069  hash_create("ModifyTable target hash",
3070  nrels, &hash_ctl,
3072  for (i = 0; i < nrels; i++)
3073  {
3074  Oid hashkey;
3075  MTTargetRelLookup *mtlookup;
3076  bool found;
3077 
3078  resultRelInfo = &mtstate->resultRelInfo[i];
3079  hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
3080  mtlookup = (MTTargetRelLookup *)
3081  hash_search(mtstate->mt_resultOidHash, &hashkey,
3082  HASH_ENTER, &found);
3083  Assert(!found);
3084  mtlookup->relationIndex = i;
3085  }
3086  }
3087  else
3088  mtstate->mt_resultOidHash = NULL;
3089 
3090  /*
3091  * Determine if the FDW supports batch insert and determine the batch
3092  * size (a FDW may support batching, but it may be disabled for the
3093  * server/table).
3094  *
3095  * We only do this for INSERT, so that for UPDATE/DELETE the batch
3096  * size remains set to 0.
3097  */
3098  if (operation == CMD_INSERT)
3099  {
3100  /* insert may only have one relation, inheritance is not expanded */
3101  Assert(nrels == 1);
3102  resultRelInfo = mtstate->resultRelInfo;
3103  if (!resultRelInfo->ri_usesFdwDirectModify &&
3104  resultRelInfo->ri_FdwRoutine != NULL &&
3105  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
3106  resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
3107  {
3108  resultRelInfo->ri_BatchSize =
3109  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
3110  Assert(resultRelInfo->ri_BatchSize >= 1);
3111  }
3112  else
3113  resultRelInfo->ri_BatchSize = 1;
3114  }
3115 
3116  /*
3117  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
3118  * to estate->es_auxmodifytables so that it will be run to completion by
3119  * ExecPostprocessPlan. (It'd actually work fine to add the primary
3120  * ModifyTable node too, but there's no need.) Note the use of lcons not
3121  * lappend: we need later-initialized ModifyTable nodes to be shut down
3122  * before earlier ones. This ensures that we don't throw away RETURNING
3123  * rows that need to be seen by a later CTE subplan.
3124  */
3125  if (!mtstate->canSetTag)
3126  estate->es_auxmodifytables = lcons(mtstate,
3127  estate->es_auxmodifytables);
3128 
3129  return mtstate;
3130 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
#define NIL
Definition: pg_list.h:65
List * arbiterIndexes
Definition: plannodes.h:234
Relation ri_RelationDesc
Definition: execnodes.h:411
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:230
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1831
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
MemoryContext hcxt
Definition: hsearch.h:86
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1219
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
List * withCheckOptionLists
Definition: plannodes.h:227
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:834
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1188
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:58
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
Size entrysize
Definition: hsearch.h:76
bool canSetTag
Definition: plannodes.h:221
CmdType operation
Definition: execnodes.h:1184
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1196
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2452
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
EState * state
Definition: execnodes.h:966
Form_pg_class rd_rel
Definition: rel.h:110
unsigned int Oid
Definition: postgres_ext.h:31
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:209
List * onConflictSet
Definition: plannodes.h:235
Index rootRelation
Definition: plannodes.h:223
List * resultRelations
Definition: plannodes.h:225
List * ri_WithCheckOptionExprs
Definition: execnodes.h:473
#define linitial_int(l)
Definition: pg_list.h:175
TupleTableSlot * oc_Existing
Definition: execnodes.h:380
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1002
List * rowMarks
Definition: plannodes.h:231
#define linitial(l)
Definition: pg_list.h:174
#define ERROR
Definition: elog.h:46
PlanState ps
Definition: execnodes.h:1183
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:382
#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:461
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:511
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:992
#define EXEC_FLAG_BACKWARD
Definition: executor.h:58
#define lfirst_node(type, lc)
Definition: pg_list.h:172
#define outerPlanState(node)
Definition: execnodes.h:1058
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1755
List * fdwPrivLists
Definition: plannodes.h:229
EPQState mt_epqstate
Definition: execnodes.h:1198
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:349
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:488
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
ExprState * oc_WhereClause
Definition: execnodes.h:383
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:234
#define outerPlan(node)
Definition: plannodes.h:171
List * lappend(List *list, void *datum)
Definition: list.c:336
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2413
OnConflictSetState * ri_onConflict
Definition: execnodes.h:494
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
#define HASH_BLOBS
Definition: hsearch.h:97
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
List * es_tupleTable
Definition: execnodes.h:600
List * es_auxmodifytables
Definition: execnodes.h:612
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:970
List * ri_WithCheckOptions
Definition: execnodes.h:470
Size keysize
Definition: hsearch.h:75
unsigned int Index
Definition: c.h:549
TupleDesc rd_att
Definition: rel.h:111
Plan * plan
Definition: execnodes.h:964
#define InvalidOid
Definition: postgres_ext.h:36
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:381
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:426
List * lcons(void *datum, List *list)
Definition: list.c:468
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
#define makeNode(_type_)
Definition: nodes.h:587
int ri_BatchSize
Definition: execnodes.h:465
#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:233
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:480
static int list_length(const List *l)
Definition: pg_list.h:149
#define MT_NRELS_HASH
TupleDesc ExecTypeFromTL(List *targetList)
Definition: execTuples.c:1938
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1799
struct MTTargetRelLookup MTTargetRelLookup
List * targetlist
Definition: plannodes.h:141
void * palloc(Size size)
Definition: mcxt.c:1062
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:353
CmdType operation
Definition: plannodes.h:220
#define elog(elevel,...)
Definition: elog.h:232
int i
List * returningLists
Definition: plannodes.h:228
bool isParent
Definition: plannodes.h:1124
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:231
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:141
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
#define RelationGetRelid(relation)
Definition: rel.h:472
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:491
CmdType
Definition: nodes.h:682
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2289
List * ri_returningList
Definition: execnodes.h:485
int epqParam
Definition: plannodes.h:232
Node * onConflictWhere
Definition: plannodes.h:236
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2266
HTAB * mt_resultOidHash
Definition: execnodes.h:1210
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)

◆ ExecInitUpdateProjection()

static void ExecInitUpdateProjection ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)
static

Definition at line 454 of file nodeModifyTable.c.

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

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

456 {
457  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
458  Plan *subplan = outerPlan(node);
459  EState *estate = mtstate->ps.state;
460  TupleDesc relDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
461  int whichrel;
462  List *updateColnos;
463 
464  /*
465  * Usually, mt_lastResultIndex matches the target rel. If it happens not
466  * to, we can get the index the hard way with an integer division.
467  */
468  whichrel = mtstate->mt_lastResultIndex;
469  if (resultRelInfo != mtstate->resultRelInfo + whichrel)
470  {
471  whichrel = resultRelInfo - mtstate->resultRelInfo;
472  Assert(whichrel >= 0 && whichrel < mtstate->mt_nrels);
473  }
474 
475  updateColnos = (List *) list_nth(node->updateColnosLists, whichrel);
476 
477  /*
478  * For UPDATE, we use the old tuple to fill up missing values in the tuple
479  * produced by the subplan to get the new tuple. We need two slots, both
480  * matching the table's desired format.
481  */
482  resultRelInfo->ri_oldTupleSlot =
483  table_slot_create(resultRelInfo->ri_RelationDesc,
484  &estate->es_tupleTable);
485  resultRelInfo->ri_newTupleSlot =
486  table_slot_create(resultRelInfo->ri_RelationDesc,
487  &estate->es_tupleTable);
488 
489  /* need an expression context to do the projection */
490  if (mtstate->ps.ps_ExprContext == NULL)
491  ExecAssignExprContext(estate, &mtstate->ps);
492 
493  resultRelInfo->ri_projectNew =
494  ExecBuildUpdateProjection(subplan->targetlist,
495  updateColnos,
496  relDesc,
497  mtstate->ps.ps_ExprContext,
498  resultRelInfo->ri_newTupleSlot,
499  &mtstate->ps);
500 
501  resultRelInfo->ri_projectNewInfoValid = true;
502 }
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
Relation ri_RelationDesc
Definition: execnodes.h:411
#define RelationGetDescr(relation)
Definition: rel.h:498
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1188
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
ProjectionInfo * ri_projectNew
Definition: execnodes.h:429
List * updateColnosLists
Definition: plannodes.h:226
EState * state
Definition: execnodes.h:966
bool ri_projectNewInfoValid
Definition: execnodes.h:435
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:431
PlanState ps
Definition: execnodes.h:1183
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
#define outerPlan(node)
Definition: plannodes.h:171
List * es_tupleTable
Definition: execnodes.h:600
Plan * plan
Definition: execnodes.h:964
#define Assert(condition)
Definition: c.h:804
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:480
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:433
ProjectionInfo * ExecBuildUpdateProjection(List *subTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:506
Definition: pg_list.h:50

◆ ExecInsert()

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

Definition at line 593 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(), ExecOpenIndices(), 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_IndexRelationDescs, 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().

599 {
600  Relation resultRelationDesc;
601  List *recheckIndexes = NIL;
602  TupleTableSlot *result = NULL;
603  TransitionCaptureState *ar_insert_trig_tcs;
604  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
605  OnConflictAction onconflict = node->onConflictAction;
607  MemoryContext oldContext;
608 
609  /*
610  * If the input result relation is a partitioned table, find the leaf
611  * partition to insert the tuple into.
612  */
613  if (proute)
614  {
615  ResultRelInfo *partRelInfo;
616 
617  slot = ExecPrepareTupleRouting(mtstate, estate, proute,
618  resultRelInfo, slot,
619  &partRelInfo);
620  resultRelInfo = partRelInfo;
621  }
622 
623  ExecMaterializeSlot(slot);
624 
625  resultRelationDesc = resultRelInfo->ri_RelationDesc;
626 
627  /*
628  * Open the table's indexes, if we have not done so already, so that we
629  * can add new index entries for the inserted tuple.
630  */
631  if (resultRelationDesc->rd_rel->relhasindex &&
632  resultRelInfo->ri_IndexRelationDescs == NULL)
633  ExecOpenIndices(resultRelInfo, onconflict != ONCONFLICT_NONE);
634 
635  /*
636  * BEFORE ROW INSERT Triggers.
637  *
638  * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
639  * INSERT ... ON CONFLICT statement. We cannot check for constraint
640  * violations before firing these triggers, because they can change the
641  * values to insert. Also, they can run arbitrary user-defined code with
642  * side-effects that we can't cancel by just not inserting the tuple.
643  */
644  if (resultRelInfo->ri_TrigDesc &&
645  resultRelInfo->ri_TrigDesc->trig_insert_before_row)
646  {
647  if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
648  return NULL; /* "do nothing" */
649  }
650 
651  /* INSTEAD OF ROW INSERT Triggers */
652  if (resultRelInfo->ri_TrigDesc &&
653  resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
654  {
655  if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
656  return NULL; /* "do nothing" */
657  }
658  else if (resultRelInfo->ri_FdwRoutine)
659  {
660  /*
661  * Compute stored generated columns
662  */
663  if (resultRelationDesc->rd_att->constr &&
664  resultRelationDesc->rd_att->constr->has_generated_stored)
665  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
666  CMD_INSERT);
667 
668  /*
669  * If the FDW supports batching, and batching is requested, accumulate
670  * rows and insert them in batches. Otherwise use the per-row inserts.
671  */
672  if (resultRelInfo->ri_BatchSize > 1)
673  {
674  /*
675  * If a certain number of tuples have already been accumulated,
676  * or a tuple has come for a different relation than that for
677  * the accumulated tuples, perform the batch insert
678  */
679  if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize)
680  {
681  ExecBatchInsert(mtstate, resultRelInfo,
682  resultRelInfo->ri_Slots,
683  resultRelInfo->ri_PlanSlots,
684  resultRelInfo->ri_NumSlots,
685  estate, canSetTag);
686  resultRelInfo->ri_NumSlots = 0;
687  }
688 
689  oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
690 
691  if (resultRelInfo->ri_Slots == NULL)
692  {
693  resultRelInfo->ri_Slots = palloc(sizeof(TupleTableSlot *) *
694  resultRelInfo->ri_BatchSize);
695  resultRelInfo->ri_PlanSlots = palloc(sizeof(TupleTableSlot *) *
696  resultRelInfo->ri_BatchSize);
697  }
698 
699  resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
701  slot->tts_ops);
702  ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
703  slot);
704  resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
706  planSlot->tts_ops);
707  ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
708  planSlot);
709 
710  resultRelInfo->ri_NumSlots++;
711 
712  MemoryContextSwitchTo(oldContext);
713 
714  return NULL;
715  }
716 
717  /*
718  * insert into foreign table: let the FDW do it
719  */
720  slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
721  resultRelInfo,
722  slot,
723  planSlot);
724 
725  if (slot == NULL) /* "do nothing" */
726  return NULL;
727 
728  /*
729  * AFTER ROW Triggers or RETURNING expressions might reference the
730  * tableoid column, so (re-)initialize tts_tableOid before evaluating
731  * them.
732  */
733  slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
734  }
735  else
736  {
737  WCOKind wco_kind;
738 
739  /*
740  * Constraints might reference the tableoid column, so (re-)initialize
741  * tts_tableOid before evaluating them.
742  */
743  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
744 
745  /*
746  * Compute stored generated columns
747  */
748  if (resultRelationDesc->rd_att->constr &&
749  resultRelationDesc->rd_att->constr->has_generated_stored)
750  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
751  CMD_INSERT);
752 
753  /*
754  * Check any RLS WITH CHECK policies.
755  *
756  * Normally we should check INSERT policies. But if the insert is the
757  * result of a partition key update that moved the tuple to a new
758  * partition, we should instead check UPDATE policies, because we are
759  * executing policies defined on the target table, and not those
760  * defined on the child partitions.
761  */
762  wco_kind = (mtstate->operation == CMD_UPDATE) ?
764 
765  /*
766  * ExecWithCheckOptions() will skip any WCOs which are not of the kind
767  * we are looking for at this point.
768  */
769  if (resultRelInfo->ri_WithCheckOptions != NIL)
770  ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
771 
772  /*
773  * Check the constraints of the tuple.
774  */
775  if (resultRelationDesc->rd_att->constr)
776  ExecConstraints(resultRelInfo, slot, estate);
777 
778  /*
779  * Also check the tuple against the partition constraint, if there is
780  * one; except that if we got here via tuple-routing, we don't need to
781  * if there's no BR trigger defined on the partition.
782  */
783  if (resultRelationDesc->rd_rel->relispartition &&
784  (resultRelInfo->ri_RootResultRelInfo == NULL ||
785  (resultRelInfo->ri_TrigDesc &&
786  resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
787  ExecPartitionCheck(resultRelInfo, slot, estate, true);
788 
789  if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
790  {
791  /* Perform a speculative insertion. */
792  uint32 specToken;
793  ItemPointerData conflictTid;
794  bool specConflict;
795  List *arbiterIndexes;
796 
797  arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
798 
799  /*
800  * Do a non-conclusive check for conflicts first.
801  *
802  * We're not holding any locks yet, so this doesn't guarantee that
803  * the later insert won't conflict. But it avoids leaving behind
804  * a lot of canceled speculative insertions, if you run a lot of
805  * INSERT ON CONFLICT statements that do conflict.
806  *
807  * We loop back here if we find a conflict below, either during
808  * the pre-check, or when we re-check after inserting the tuple
809  * speculatively.
810  */
811  vlock:
812  specConflict = false;
813  if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
814  &conflictTid, arbiterIndexes))
815  {
816  /* committed conflict tuple found */
817  if (onconflict == ONCONFLICT_UPDATE)
818  {
819  /*
820  * In case of ON CONFLICT DO UPDATE, execute the UPDATE
821  * part. Be prepared to retry if the UPDATE fails because
822  * of another concurrent UPDATE/DELETE to the conflict
823  * tuple.
824  */
825  TupleTableSlot *returning = NULL;
826 
827  if (ExecOnConflictUpdate(mtstate, resultRelInfo,
828  &conflictTid, planSlot, slot,
829  estate, canSetTag, &returning))
830  {
831  InstrCountTuples2(&mtstate->ps, 1);
832  return returning;
833  }
834  else
835  goto vlock;
836  }
837  else
838  {
839  /*
840  * In case of ON CONFLICT DO NOTHING, do nothing. However,
841  * verify that the tuple is visible to the executor's MVCC
842  * snapshot at higher isolation levels.
843  *
844  * Using ExecGetReturningSlot() to store the tuple for the
845  * recheck isn't that pretty, but we can't trivially use
846  * the input slot, because it might not be of a compatible
847  * type. As there's no conflicting usage of
848  * ExecGetReturningSlot() in the DO NOTHING case...
849  */
850  Assert(onconflict == ONCONFLICT_NOTHING);
851  ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
852  ExecGetReturningSlot(estate, resultRelInfo));
853  InstrCountTuples2(&mtstate->ps, 1);
854  return NULL;
855  }
856  }
857 
858  /*
859  * Before we start insertion proper, acquire our "speculative
860  * insertion lock". Others can use that to wait for us to decide
861  * if we're going to go ahead with the insertion, instead of
862  * waiting for the whole transaction to complete.
863  */
865 
866  /* insert the tuple, with the speculative token */
867  table_tuple_insert_speculative(resultRelationDesc, slot,
868  estate->es_output_cid,
869  0,
870  NULL,
871  specToken);
872 
873  /* insert index entries for tuple */
874  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
875  slot, estate, false, true,
876  &specConflict,
877  arbiterIndexes);
878 
879  /* adjust the tuple's state accordingly */
880  table_tuple_complete_speculative(resultRelationDesc, slot,
881  specToken, !specConflict);
882 
883  /*
884  * Wake up anyone waiting for our decision. They will re-check
885  * the tuple, see that it's no longer speculative, and wait on our
886  * XID as if this was a regularly inserted tuple all along. Or if
887  * we killed the tuple, they will see it's dead, and proceed as if
888  * the tuple never existed.
889  */
891 
892  /*
893  * If there was a conflict, start from the beginning. We'll do
894  * the pre-check again, which will now find the conflicting tuple
895  * (unless it aborts before we get there).
896  */
897  if (specConflict)
898  {
899  list_free(recheckIndexes);
900  goto vlock;
901  }
902 
903  /* Since there was no insertion conflict, we're done */
904  }
905  else
906  {
907  /* insert the tuple normally */
908  table_tuple_insert(resultRelationDesc, slot,
909  estate->es_output_cid,
910  0, NULL);
911 
912  /* insert index entries for tuple */
913  if (resultRelInfo->ri_NumIndices > 0)
914  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
915  slot, estate, false,
916  false, NULL, NIL);
917  }
918  }
919 
920  if (canSetTag)
921  (estate->es_processed)++;
922 
923  /*
924  * If this insert is the result of a partition key update that moved the
925  * tuple to a new partition, put this row into the transition NEW TABLE,
926  * if there is one. We need to do this separately for DELETE and INSERT
927  * because they happen on different tables.
928  */
929  ar_insert_trig_tcs = mtstate->mt_transition_capture;
930  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
932  {
933  ExecARUpdateTriggers(estate, resultRelInfo, NULL,
934  NULL,
935  slot,
936  NULL,
937  mtstate->mt_transition_capture);
938 
939  /*
940  * We've already captured the NEW TABLE row, so make sure any AR
941  * INSERT trigger fired below doesn't capture it again.
942  */
943  ar_insert_trig_tcs = NULL;
944  }
945 
946  /* AFTER ROW INSERT Triggers */
947  ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
948  ar_insert_trig_tcs);
949 
950  list_free(recheckIndexes);
951 
952  /*
953  * Check any WITH CHECK OPTION constraints from parent views. We are
954  * required to do this after testing all constraints and uniqueness
955  * violations per the SQL spec, so we do it after actually inserting the
956  * record into the heap and all indexes.
957  *
958  * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
959  * tuple will never be seen, if it violates the WITH CHECK OPTION.
960  *
961  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
962  * are looking for at this point.
963  */
964  if (resultRelInfo->ri_WithCheckOptions != NIL)
965  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
966 
967  /* Process RETURNING if present */
968  if (resultRelInfo->ri_projectReturning)
969  result = ExecProcessReturning(resultRelInfo, slot, planSlot);
970 
971  return result;
972 }
int ri_NumIndices
Definition: execnodes.h:414
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:411
void SpeculativeInsertionLockRelease(TransactionId xid)
Definition: lmgr.c:781
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:1954
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:570
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:467
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1219
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1238
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:232
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1821
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:755
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
CmdType operation
Definition: execnodes.h:1184
TupleTableSlot ** ri_Slots
Definition: execnodes.h:466
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
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:156
bool trig_insert_instead_row
Definition: reltrigger.h:58
bool has_generated_stored
Definition: tupdesc.h:45
MemoryContext es_query_cxt
Definition: execnodes.h:598
PlanState ps
Definition: execnodes.h:1183
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:511
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
TupleConstr * constr
Definition: tupdesc.h:85
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:488
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
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:1061
WCOKind
Definition: parsenodes.h:1204
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:438
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:2826
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:470
TupleDesc rd_att
Definition: rel.h:111
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
Plan * plan
Definition: execnodes.h:964
int ri_BatchSize
Definition: execnodes.h:465
#define Assert(condition)
Definition: c.h:804
bool ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2327
OnConflictAction onConflictAction
Definition: plannodes.h:233
uint64 es_processed
Definition: execnodes.h:602
bool ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, List *arbiterIndexes)
Definition: execIndexing.c:505
void * palloc(Size size)
Definition: mcxt.c:1062
void list_free(List *list)
Definition: list.c:1391
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1697
Definition: pg_list.h:50
OnConflictAction
Definition: nodes.h:832
#define RelationGetRelid(relation)
Definition: rel.h:472
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:491
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:417

◆ ExecLookupResultRelByOid()

ResultRelInfo* ExecLookupResultRelByOid ( ModifyTableState node,
Oid  resultoid,
bool  missing_ok,
bool  update_cache 
)

Definition at line 2633 of file nodeModifyTable.c.

References elog, ERROR, HASH_FIND, hash_search(), ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_nrels, ModifyTableState::mt_resultOidHash, RelationGetRelid, MTTargetRelLookup::relationIndex, ModifyTableState::resultRelInfo, and ResultRelInfo::ri_RelationDesc.

Referenced by exec_rt_fetch(), ExecFindPartition(), and ExecModifyTable().

2635 {
2636  if (node->mt_resultOidHash)
2637  {
2638  /* Use the pre-built hash table to locate the rel */
2639  MTTargetRelLookup *mtlookup;
2640 
2641  mtlookup = (MTTargetRelLookup *)
2642  hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
2643  if (mtlookup)
2644  {
2645  if (update_cache)
2646  {
2647  node->mt_lastResultOid = resultoid;
2648  node->mt_lastResultIndex = mtlookup->relationIndex;
2649  }
2650  return node->resultRelInfo + mtlookup->relationIndex;
2651  }
2652  }
2653  else
2654  {
2655  /* With few target rels, just search the ResultRelInfo array */
2656  for (int ndx = 0; ndx < node->mt_nrels; ndx++)
2657  {
2658  ResultRelInfo *rInfo = node->resultRelInfo + ndx;
2659 
2660  if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
2661  {
2662  if (update_cache)
2663  {
2664  node->mt_lastResultOid = resultoid;
2665  node->mt_lastResultIndex = ndx;
2666  }
2667  return rInfo;
2668  }
2669  }
2670  }
2671 
2672  if (!missing_ok)
2673  elog(ERROR, "incorrect result relation OID %u", resultoid);
2674  return NULL;
2675 }
Relation ri_RelationDesc
Definition: execnodes.h:411
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1188
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
#define ERROR
Definition: elog.h:46
#define elog(elevel,...)
Definition: elog.h:232
#define RelationGetRelid(relation)
Definition: rel.h:472
HTAB * mt_resultOidHash
Definition: execnodes.h:1210

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 2323 of file nodeModifyTable.c.

References Assert, AttributeNumberIsValid, ModifyTableState::canSetTag, castNode, CHECK_FOR_INTERRUPTS, CMD_DELETE, CMD_INSERT, CMD_UPDATE, DatumGetHeapTupleHeader, DatumGetObjectId, DatumGetPointer, elog, ERROR, EState::es_epq_active, EState::es_opened_result_relations, EState::es_tuple_routing_result_relations, EvalPlanQualSetSlot, ExecBatchInsert(), ExecDelete(), ExecForceStoreHeapTuple(), ExecGetInsertNewTuple(), ExecGetJunkAttribute(), ExecGetUpdateNewTuple(), ExecInitInsertProjection(), ExecInitUpdateProjection(), ExecInsert(), ExecLookupResultRelByOid(), ExecProcessReturning(), ExecProcNode(), ExecUpdate(), fireASTriggers(), ModifyTableState::fireBSTriggers, fireBSTriggers(), HeapTupleHeaderGetDatumLength, InvalidOid, ItemPointerSetInvalid, lfirst, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_resultOidAttno, NIL, ModifyTableState::operation, outerPlanState, ModifyTableState::ps, PlanState::ps_ExprContext, RelationData::rd_rel, RelationGetRelid, ResetExprContext, ResetPerTupleExprContext, ModifyTableState::resultRelInfo, ResultRelInfo::ri_NumSlots, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_PlanSlots, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_Slots, ResultRelInfo::ri_usesFdwDirectModify, SnapshotAny, PlanState::state, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, table_tuple_fetch_row_version(), TupIsNull, and unlikely.

Referenced by ExecInitModifyTable().

2324 {
2325  ModifyTableState *node = castNode(ModifyTableState, pstate);
2326  EState *estate = node->ps.state;
2327  CmdType operation = node->operation;
2328  ResultRelInfo *resultRelInfo;
2329  PlanState *subplanstate;
2330  TupleTableSlot *slot;
2331  TupleTableSlot *planSlot;
2332  TupleTableSlot *oldSlot;
2333  ItemPointer tupleid;
2334  ItemPointerData tuple_ctid;
2335  HeapTupleData oldtupdata;
2336  HeapTuple oldtuple;
2338  List *relinfos = NIL;
2339  ListCell *lc;
2340 
2342 
2343  /*
2344  * This should NOT get called during EvalPlanQual; we should have passed a
2345  * subplan tree to EvalPlanQual, instead. Use a runtime test not just
2346  * Assert because this condition is easy to miss in testing. (Note:
2347  * although ModifyTable should not get executed within an EvalPlanQual
2348  * operation, we do have to allow it to be initialized and shut down in
2349  * case it is within a CTE subplan. Hence this test must be here, not in
2350  * ExecInitModifyTable.)
2351  */
2352  if (estate->es_epq_active != NULL)
2353  elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
2354 
2355  /*
2356  * If we've already completed processing, don't try to do more. We need
2357  * this test because ExecPostprocessPlan might call us an extra time, and
2358  * our subplan's nodes aren't necessarily robust against being called
2359  * extra times.
2360  */
2361  if (node->mt_done)
2362  return NULL;
2363 
2364  /*
2365  * On first call, fire BEFORE STATEMENT triggers before proceeding.
2366  */
2367  if (node->fireBSTriggers)
2368  {
2369  fireBSTriggers(node);
2370  node->fireBSTriggers = false;
2371  }
2372 
2373  /* Preload local variables */
2374  resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
2375  subplanstate = outerPlanState(node);
2376 
2377  /*
2378  * Fetch rows from subplan, and execute the required table modification
2379  * for each row.
2380  */
2381  for (;;)
2382  {
2383  /*
2384  * Reset the per-output-tuple exprcontext. This is needed because
2385  * triggers expect to use that context as workspace. It's a bit ugly
2386  * to do this below the top level of the plan, however. We might need
2387  * to rethink this later.
2388  */
2389  ResetPerTupleExprContext(estate);
2390 
2391  /*
2392  * Reset per-tuple memory context used for processing on conflict and
2393  * returning clauses, to free any expression evaluation storage
2394  * allocated in the previous cycle.
2395  */
2396  if (pstate->ps_ExprContext)
2398 
2399  planSlot = ExecProcNode(subplanstate);
2400 
2401  /* No more tuples to process? */
2402  if (TupIsNull(planSlot))
2403  break;
2404 
2405  /*
2406  * When there are multiple result relations, each tuple contains a
2407  * junk column that gives the OID of the rel from which it came.
2408  * Extract it and select the correct result relation.
2409  */
2411  {
2412  Datum datum;
2413  bool isNull;
2414  Oid resultoid;
2415 
2416  datum = ExecGetJunkAttribute(planSlot, node->mt_resultOidAttno,
2417  &isNull);
2418  if (isNull)
2419  elog(ERROR, "tableoid is NULL");
2420  resultoid = DatumGetObjectId(datum);
2421 
2422  /* If it's not the same as last time, we need to locate the rel */
2423  if (resultoid != node->mt_lastResultOid)
2424  resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
2425  false, true);
2426  }
2427 
2428  /*
2429  * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
2430  * here is compute the RETURNING expressions.
2431  */
2432  if (resultRelInfo->ri_usesFdwDirectModify)
2433  {
2434  Assert(resultRelInfo->ri_projectReturning);
2435 
2436  /*
2437  * A scan slot containing the data that was actually inserted,
2438  * updated or deleted has already been made available to
2439  * ExecProcessReturning by IterateDirectModify, so no need to
2440  * provide it here.
2441  */
2442  slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
2443 
2444  return slot;
2445  }
2446 
2447  EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
2448  slot = planSlot;
2449 
2450  tupleid = NULL;
2451  oldtuple = NULL;
2452 
2453  /*
2454  * For UPDATE/DELETE, fetch the row identity info for the tuple to be
2455  * updated/deleted. For a heap relation, that's a TID; otherwise we
2456  * may have a wholerow junk attr that carries the old tuple in toto.
2457  * Keep this in step with the part of ExecInitModifyTable that sets up
2458  * ri_RowIdAttNo.
2459  */
2460  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2461  {
2462  char relkind;
2463  Datum datum;
2464  bool isNull;
2465 
2466  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2467  if (relkind == RELKIND_RELATION ||
2468  relkind == RELKIND_MATVIEW ||
2469  relkind == RELKIND_PARTITIONED_TABLE)
2470  {
2471  /* ri_RowIdAttNo refers to a ctid attribute */
2472  Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo));
2473  datum = ExecGetJunkAttribute(slot,
2474  resultRelInfo->ri_RowIdAttNo,
2475  &isNull);
2476  /* shouldn't ever get a null result... */
2477  if (isNull)
2478  elog(ERROR, "ctid is NULL");
2479 
2480  tupleid = (ItemPointer) DatumGetPointer(datum);
2481  tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
2482  tupleid = &tuple_ctid;
2483  }
2484 
2485  /*
2486  * Use the wholerow attribute, when available, to reconstruct the
2487  * old relation tuple. The old tuple serves one or both of two
2488  * purposes: 1) it serves as the OLD tuple for row triggers, 2) it
2489  * provides values for any unchanged columns for the NEW tuple of
2490  * an UPDATE, because the subplan does not produce all the columns
2491  * of the target table.
2492  *
2493  * Note that the wholerow attribute does not carry system columns,
2494  * so foreign table triggers miss seeing those, except that we
2495  * know enough here to set t_tableOid. Quite separately from
2496  * this, the FDW may fetch its own junk attrs to identify the row.
2497  *
2498  * Other relevant relkinds, currently limited to views, always
2499  * have a wholerow attribute.
2500  */
2501  else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
2502  {
2503  datum = ExecGetJunkAttribute(slot,
2504  resultRelInfo->ri_RowIdAttNo,
2505  &isNull);
2506  /* shouldn't ever get a null result... */
2507  if (isNull)
2508  elog(ERROR, "wholerow is NULL");
2509 
2510  oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
2511  oldtupdata.t_len =
2513  ItemPointerSetInvalid(&(oldtupdata.t_self));
2514  /* Historically, view triggers see invalid t_tableOid. */
2515  oldtupdata.t_tableOid =
2516  (relkind == RELKIND_VIEW) ? InvalidOid :
2517  RelationGetRelid(resultRelInfo->ri_RelationDesc);
2518 
2519  oldtuple = &oldtupdata;
2520  }
2521  else
2522  {
2523  /* Only foreign tables are allowed to omit a row-ID attr */
2524  Assert(relkind == RELKIND_FOREIGN_TABLE);
2525  }
2526  }
2527 
2528  switch (operation)
2529  {
2530  case CMD_INSERT:
2531  /* Initialize projection info if first time for this table */
2532  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2533  ExecInitInsertProjection(node, resultRelInfo);
2534  slot = ExecGetInsertNewTuple(resultRelInfo, planSlot);
2535  slot = ExecInsert(node, resultRelInfo, slot, planSlot,
2536  estate, node->canSetTag);
2537  break;
2538  case CMD_UPDATE:
2539  /* Initialize projection info if first time for this table */
2540  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
2541  ExecInitUpdateProjection(node, resultRelInfo);
2542 
2543  /*
2544  * Make the new tuple by combining plan's output tuple with
2545  * the old tuple being updated.
2546  */
2547  oldSlot = resultRelInfo->ri_oldTupleSlot;
2548  if (oldtuple != NULL)
2549  {
2550  /* Use the wholerow junk attr as the old tuple. */
2551  ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
2552  }
2553  else
2554  {
2555  /* Fetch the most recent version of old tuple. */
2556  Relation relation = resultRelInfo->ri_RelationDesc;
2557 
2558  Assert(tupleid != NULL);
2559  if (!table_tuple_fetch_row_version(relation, tupleid,
2560  SnapshotAny,
2561  oldSlot))
2562  elog(ERROR, "failed to fetch tuple being updated");
2563  }
2564  slot = ExecGetUpdateNewTuple(resultRelInfo, planSlot,
2565  oldSlot);
2566 
2567  /* Now apply the update. */
2568  slot = ExecUpdate(node, resultRelInfo, tupleid, oldtuple, slot,
2569  planSlot, &node->mt_epqstate, estate,
2570  node->canSetTag);
2571  break;
2572  case CMD_DELETE:
2573  slot = ExecDelete(node, resultRelInfo, tupleid, oldtuple,
2574  planSlot, &node->mt_epqstate, estate,
2575  true, /* processReturning */
2576  node->canSetTag,
2577  false, /* changingPart */
2578  NULL, NULL);
2579  break;
2580  default:
2581  elog(ERROR, "unknown operation");
2582  break;
2583  }
2584 
2585  /*
2586  * If we got a RETURNING result, return it to caller. We'll continue
2587  * the work on next call.
2588  */
2589  if (slot)
2590  return slot;
2591  }
2592 
2593  /*
2594  * Insert remaining tuples for batch insert.
2595  */
2596  if (proute)
2597  relinfos = estate->es_tuple_routing_result_relations;
2598  else
2599  relinfos = estate->es_opened_result_relations;
2600 
2601  foreach(lc, relinfos)
2602  {
2603  resultRelInfo = lfirst(lc);
2604  if (resultRelInfo->ri_NumSlots > 0)
2605  ExecBatchInsert(node, resultRelInfo,
2606  resultRelInfo->ri_Slots,
2607  resultRelInfo->ri_PlanSlots,
2608  resultRelInfo->ri_NumSlots,
2609  estate, node->canSetTag);
2610  }
2611 
2612  /*
2613  * We're done, but fire AFTER STATEMENT triggers before exiting.
2614  */
2615  fireASTriggers(node);
2616 
2617  node->mt_done = true;
2618 
2619  return NULL;
2620 }
#define NIL
Definition: pg_list.h:65
Relation ri_RelationDesc
Definition: execnodes.h:411
static void ExecBatchInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, TupleTableSlot **slots, TupleTableSlot **planSlots, int numSlots, EState *estate, bool canSetTag)
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:467
#define ResetPerTupleExprContext(estate)
Definition: executor.h:541
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1219
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1188
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
#define DatumGetObjectId(X)
Definition: postgres.h:544
ExprContext * ps_ExprContext
Definition: execnodes.h:1003
static TupleTableSlot * ExecGetInsertNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot)
static void ExecInitUpdateProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
static void fireBSTriggers(ModifyTableState *node)
static void ExecInitInsertProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
CmdType operation
Definition: execnodes.h:1184
TupleTableSlot ** ri_Slots
Definition: execnodes.h:466
EState * state
Definition: execnodes.h:966
Form_pg_class rd_rel
Definition: rel.h:110
unsigned int Oid
Definition: postgres_ext.h:31
bool ri_projectNewInfoValid
Definition: execnodes.h:435
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1469
struct EPQState * es_epq_active
Definition: execnodes.h:627
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
#define ERROR
Definition: elog.h:46
PlanState ps
Definition: execnodes.h:1183
ItemPointerData t_self
Definition: htup.h:65
bool ri_usesFdwDirectModify
Definition: execnodes.h:461
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
#define outerPlanState(node)
Definition: execnodes.h:1058
uint32 t_len
Definition: htup.h:64
List * es_opened_result_relations
Definition: execnodes.h:576
EPQState mt_epqstate
Definition: execnodes.h:1198
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:488
#define TupIsNull(slot)
Definition: tuptable.h:292
Oid t_tableOid
Definition: htup.h:66
ResultRelInfo * ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid, bool missing_ok, bool update_cache)
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)
#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)
static Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: executor.h:178
uintptr_t Datum
Definition: postgres.h:411
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:252
#define InvalidOid
Definition: postgres_ext.h:36
List * es_tuple_routing_result_relations
Definition: execnodes.h:586
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:426
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:433
#define SnapshotAny
Definition: snapmgr.h:67
#define DatumGetPointer(X)
Definition: postgres.h:593
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
#define elog(elevel,...)
Definition: elog.h:232
#define unlikely(x)
Definition: c.h:273
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:102
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:472
CmdType
Definition: nodes.h:682
#define ResetExprContext(econtext)
Definition: executor.h:526
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:227
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451

◆ ExecOnConflictUpdate()

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

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

1966 {
1967  ExprContext *econtext = mtstate->ps.ps_ExprContext;
1968  Relation relation = resultRelInfo->ri_RelationDesc;
1969  ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
1970  TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
1971  TM_FailureData tmfd;
1972  LockTupleMode lockmode;
1973  TM_Result test;
1974  Datum xminDatum;
1975  TransactionId xmin;
1976  bool isnull;
1977 
1978  /* Determine lock mode to use */
1979  lockmode = ExecUpdateLockMode(estate, resultRelInfo);
1980 
1981  /*
1982  * Lock tuple for update. Don't follow updates when tuple cannot be
1983  * locked without doing so. A row locking conflict here means our
1984  * previous conclusion that the tuple is conclusively committed is not
1985  * true anymore.
1986  */
1987  test = table_tuple_lock(relation, conflictTid,
1988  estate->es_snapshot,
1989  existing, estate->es_output_cid,
1990  lockmode, LockWaitBlock, 0,
1991  &tmfd);
1992  switch (test)
1993  {
1994  case TM_Ok:
1995  /* success! */
1996  break;
1997 
1998  case TM_Invisible:
1999 
2000  /*
2001  * This can occur when a just inserted tuple is updated again in
2002  * the same command. E.g. because multiple rows with the same
2003  * conflicting key values are inserted.
2004  *
2005  * This is somewhat similar to the ExecUpdate() TM_SelfModified
2006  * case. We do not want to proceed because it would lead to the
2007  * same row being updated a second time in some unspecified order,
2008  * and in contrast to plain UPDATEs there's no historical behavior
2009  * to break.
2010  *
2011  * It is the user's responsibility to prevent this situation from
2012  * occurring. These problems are why SQL-2003 similarly specifies
2013  * that for SQL MERGE, an exception must be raised in the event of
2014  * an attempt to update the same row twice.
2015  */
2016  xminDatum = slot_getsysattr(existing,
2018  &isnull);
2019  Assert(!isnull);
2020  xmin = DatumGetTransactionId(xminDatum);
2021 
2023  ereport(ERROR,
2024  (errcode(ERRCODE_CARDINALITY_VIOLATION),
2025  errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
2026  errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
2027 
2028  /* This shouldn't happen */
2029  elog(ERROR, "attempted to lock invisible tuple");
2030  break;
2031 
2032  case TM_SelfModified:
2033 
2034  /*
2035  * This state should never be reached. As a dirty snapshot is used
2036  * to find conflicting tuples, speculative insertion wouldn't have
2037  * seen this row to conflict with.
2038  */
2039  elog(ERROR, "unexpected self-updated tuple");
2040  break;
2041 
2042  case TM_Updated:
2044  ereport(ERROR,
2045  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2046  errmsg("could not serialize access due to concurrent update")));
2047 
2048  /*
2049  * As long as we don't support an UPDATE of INSERT ON CONFLICT for
2050  * a partitioned table we shouldn't reach to a case where tuple to
2051  * be lock is moved to another partition due to concurrent update
2052  * of the partition key.
2053  */
2055 
2056  /*
2057  * Tell caller to try again from the very start.
2058  *
2059  * It does not make sense to use the usual EvalPlanQual() style
2060  * loop here, as the new version of the row might not conflict
2061  * anymore, or the conflicting tuple has actually been deleted.
2062  */
2063  ExecClearTuple(existing);
2064  return false;
2065 
2066  case TM_Deleted:
2068  ereport(ERROR,
2069  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
2070  errmsg("could not serialize access due to concurrent delete")));
2071 
2072  /* see TM_Updated case */
2074  ExecClearTuple(existing);
2075  return false;
2076 
2077  default:
2078  elog(ERROR, "unrecognized table_tuple_lock status: %u", test);
2079  }
2080 
2081  /* Success, the tuple is locked. */
2082 
2083  /*
2084  * Verify that the tuple is visible to our MVCC snapshot if the current
2085  * isolation level mandates that.
2086  *
2087  * It's not sufficient to rely on the check within ExecUpdate() as e.g.
2088  * CONFLICT ... WHERE clause may prevent us from reaching that.
2089  *
2090  * This means we only ever continue when a new command in the current
2091  * transaction could see the row, even though in READ COMMITTED mode the
2092  * tuple will not be visible according to the current statement's
2093  * snapshot. This is in line with the way UPDATE deals with newer tuple
2094  * versions.
2095  */
2096  ExecCheckTupleVisible(estate, relation, existing);
2097 
2098  /*
2099  * Make tuple and any needed join variables available to ExecQual and
2100  * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
2101  * the target's existing tuple is installed in the scantuple. EXCLUDED
2102  * has been made to reference INNER_VAR in setrefs.c, but there is no
2103  * other redirection.
2104  */
2105  econtext->ecxt_scantuple = existing;
2106  econtext->ecxt_innertuple = excludedSlot;
2107  econtext->ecxt_outertuple = NULL;
2108 
2109  if (!ExecQual(onConflictSetWhere, econtext))
2110  {
2111  ExecClearTuple(existing); /* see return below */
2112  InstrCountFiltered1(&mtstate->ps, 1);
2113  return true; /* done with the tuple */
2114  }
2115 
2116  if (resultRelInfo->ri_WithCheckOptions != NIL)
2117  {
2118  /*
2119  * Check target's existing tuple against UPDATE-applicable USING
2120  * security barrier quals (if any), enforced here as RLS checks/WCOs.
2121  *
2122  * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
2123  * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
2124  * but that's almost the extent of its special handling for ON
2125  * CONFLICT DO UPDATE.
2126  *
2127  * The rewriter will also have associated UPDATE applicable straight
2128  * RLS checks/WCOs for the benefit of the ExecUpdate() call that
2129  * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
2130  * kinds, so there is no danger of spurious over-enforcement in the
2131  * INSERT or UPDATE path.
2132  */
2134  existing,
2135  mtstate->ps.state);
2136  }
2137 
2138  /* Project the new tuple version */
2139  ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
2140 
2141  /*
2142  * Note that it is possible that the target tuple has been modified in
2143  * this session, after the above table_tuple_lock. We choose to not error
2144  * out in that case, in line with ExecUpdate's treatment of similar cases.
2145  * This can happen if an UPDATE is triggered from within ExecQual(),
2146  * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
2147  * wCTE in the ON CONFLICT's SET.
2148  */
2149 
2150  /* Execute UPDATE with projection */
2151  *returning = ExecUpdate(mtstate, resultRelInfo, conflictTid, NULL,
2152  resultRelInfo->ri_onConflict->oc_ProjSlot,
2153  planSlot,
2154  &mtstate->mt_epqstate, mtstate->ps.state,
2155  canSetTag);
2156 
2157  /*
2158  * Clear out existing tuple, as there might not be another conflict among
2159  * the next input rows. Don't want to hold resources till the end of the
2160  * query.
2161  */
2162  ExecClearTuple(existing);
2163  return true;
2164 }
#define NIL
Definition: pg_list.h:65
ItemPointerData ctid
Definition: tableam.h:126
Relation ri_RelationDesc
Definition: execnodes.h:411
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1156
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1954
CommandId es_output_cid
Definition: execnodes.h:570
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:1003
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
int errcode(int sqlerrcode)
Definition: elog.c:698
Snapshot es_snapshot
Definition: execnodes.h:556
EState * state
Definition: execnodes.h:966
static void ExecCheckTupleVisible(EState *estate, Relation rel, TupleTableSlot *slot)
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:395
TupleTableSlot * oc_Existing
Definition: execnodes.h:380
#define ERROR
Definition: elog.h:46
PlanState ps
Definition: execnodes.h:1183
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:382
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:1198
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:228
ExprState * oc_WhereClause
Definition: execnodes.h:383
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1066
OnConflictSetState * ri_onConflict
Definition: execnodes.h:494
TM_Result
Definition: tableam.h:71
uintptr_t Datum
Definition: postgres.h:411
List * ri_WithCheckOptions
Definition: execnodes.h:470
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
#define ereport(elevel,...)
Definition: elog.h:157
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:381
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:230
#define Assert(condition)
Definition: c.h:804
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2240
Definition: tableam.h:77
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:226
#define DatumGetTransactionId(X)
Definition: postgres.h:558
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
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:358

◆ ExecPrepareTupleRouting()

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

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

2269 {
2270  ResultRelInfo *partrel;
2271  TupleConversionMap *map;
2272 
2273  /*
2274  * Lookup the target partition's ResultRelInfo. If ExecFindPartition does
2275  * not find a valid partition for the tuple in 'slot' then an error is
2276  * raised. An error may also be raised if the found partition is not a
2277  * valid target for INSERTs. This is required since a partitioned table
2278  * UPDATE to another partition becomes a DELETE+INSERT.
2279  */
2280  partrel = ExecFindPartition(mtstate, targetRelInfo, proute, slot, estate);
2281 
2282  /*
2283  * If we're capturing transition tuples, we might need to convert from the
2284  * partition rowtype to root partitioned table's rowtype. But if there
2285  * are no BEFORE triggers on the partition that could change the tuple, we
2286  * can just remember the original unconverted tuple to avoid a needless
2287  * round trip conversion.
2288  */
2289  if (mtstate->mt_transition_capture != NULL)
2290  {
2291  bool has_before_insert_row_trig;
2292 
2293  has_before_insert_row_trig = (partrel->ri_TrigDesc &&
2295 
2297  !has_before_insert_row_trig ? slot : NULL;
2298  }
2299 
2300  /*
2301  * Convert the tuple, if necessary.
2302  */
2303  map = partrel->ri_RootToPartitionMap;
2304  if (map != NULL)
2305  {
2306  TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot;
2307 
2308  slot = execute_attr_map_slot(map->attrMap, slot, new_slot);
2309  }
2310 
2311  *partRelInfo = partrel;
2312  return slot;
2313 }
TupleTableSlot * ri_PartitionTupleSlot
Definition: execnodes.h:513
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
AttrMap * attrMap
Definition: tupconvert.h:28
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:438
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:512
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 170 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().

173 {
174  ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
175  ExprContext *econtext = projectReturning->pi_exprContext;
176 
177  /* Make tuple and any needed join variables available to ExecProject */
178  if (tupleSlot)
179  econtext->ecxt_scantuple = tupleSlot;
180  econtext->ecxt_outertuple = planSlot;
181 
182  /*
183  * RETURNING expressions might reference the tableoid column, so
184  * reinitialize tts_tableOid before evaluating them.
185  */
186  econtext->ecxt_scantuple->tts_tableOid =
187  RelationGetRelid(resultRelInfo->ri_RelationDesc);
188 
189  /* Compute the RETURNING expressions */
190  return ExecProject(projectReturning);
191 }
Oid tts_tableOid
Definition: tuptable.h:131
Relation ri_RelationDesc
Definition: execnodes.h:411
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:488
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:230
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:226
ExprContext * pi_exprContext
Definition: execnodes.h:335
#define RelationGetRelid(relation)
Definition: rel.h:472
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:358

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 3194 of file nodeModifyTable.c.

References elog, and ERROR.

Referenced by ExecReScan().

3195 {
3196  /*
3197  * Currently, we don't need to support rescan on ModifyTable nodes. The
3198  * semantics of that would be a bit debatable anyway.
3199  */
3200  elog(ERROR, "ExecReScanModifyTable is not implemented");
3201 }
#define ERROR
Definition: elog.h:46
#define elog(elevel,...)
Definition: elog.h:232

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

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

2235 {
2236  ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
2237  ResultRelInfo *targetRelInfo = mtstate->rootResultRelInfo;
2238 
2239  /* Check for transition tables on the directly targeted relation. */
2240  mtstate->mt_transition_capture =
2241  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
2242  RelationGetRelid(targetRelInfo->ri_RelationDesc),
2243  mtstate->operation);
2244  if (plan->operation == CMD_INSERT &&
2246  mtstate->mt_oc_transition_capture =
2247  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
2248  RelationGetRelid(targetRelInfo->ri_RelationDesc),
2249  CMD_UPDATE);
2250 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1225
CmdType operation
Definition: execnodes.h:1184
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1196
PlanState ps
Definition: execnodes.h:1183
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4397
Plan * plan
Definition: execnodes.h:964
OnConflictAction onConflictAction
Definition: plannodes.h:233
CmdType operation
Definition: plannodes.h:220
#define RelationGetRelid(relation)
Definition: rel.h:472

◆ 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 1581 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(), FdwRoutine::ExecForeignUpdate, ExecGetUpdateNewTuple(), ExecInitUpdateProjection(), ExecInsertIndexTuples(), ExecIRUpdateTriggers(), ExecMaterializeSlot(), ExecOpenIndices(), 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_IndexRelationDescs, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, SnapshotAny, table_tuple_fetch_row_version(), 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, unlikely, WCO_RLS_UPDATE_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

1590 {
1591  Relation resultRelationDesc = resultRelInfo->ri_RelationDesc;
1592  TM_Result result;
1593  TM_FailureData tmfd;
1594  List *recheckIndexes = NIL;
1595 
1596  /*
1597  * abort the operation if not running transactions
1598  */
1600  elog(ERROR, "cannot UPDATE during bootstrap");
1601 
1602  ExecMaterializeSlot(slot);
1603 
1604  /*
1605  * Open the table's indexes, if we have not done so already, so that we
1606  * can add new index entries for the updated tuple.
1607  */
1608  if (resultRelationDesc->rd_rel->relhasindex &&
1609  resultRelInfo->ri_IndexRelationDescs == NULL)
1610  ExecOpenIndices(resultRelInfo, false);
1611 
1612  /* BEFORE ROW UPDATE Triggers */
1613  if (resultRelInfo->ri_TrigDesc &&
1614  resultRelInfo->ri_TrigDesc->trig_update_before_row)
1615  {
1616  if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
1617  tupleid, oldtuple, slot))
1618  return NULL; /* "do nothing" */
1619  }
1620 
1621  /* INSTEAD OF ROW UPDATE Triggers */
1622  if (resultRelInfo->ri_TrigDesc &&
1623  resultRelInfo->ri_TrigDesc->trig_update_instead_row)
1624  {
1625  if (!ExecIRUpdateTriggers(estate, resultRelInfo,
1626  oldtuple, slot))
1627  return NULL; /* "do nothing" */
1628  }
1629  else if (resultRelInfo->ri_FdwRoutine)
1630  {
1631  /*
1632  * Compute stored generated columns
1633  */
1634  if (resultRelationDesc->rd_att->constr &&
1635  resultRelationDesc->rd_att->constr->has_generated_stored)
1636  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1637  CMD_UPDATE);
1638 
1639  /*
1640  * update in foreign table: let the FDW do it
1641  */
1642  slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
1643  resultRelInfo,
1644  slot,
1645  planSlot);
1646 
1647  if (slot == NULL) /* "do nothing" */
1648  return NULL;
1649 
1650  /*
1651  * AFTER ROW Triggers or RETURNING expressions might reference the
1652  * tableoid column, so (re-)initialize tts_tableOid before evaluating
1653  * them.
1654  */
1655  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1656  }
1657  else
1658  {
1659  LockTupleMode lockmode;
1660  bool partition_constraint_failed;
1661  bool update_indexes;
1662 
1663  /*
1664  * Constraints might reference the tableoid column, so (re-)initialize
1665  * tts_tableOid before evaluating them.
1666  */
1667  slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
1668 
1669  /*
1670  * Compute stored generated columns
1671  */
1672  if (resultRelationDesc->rd_att->constr &&
1673  resultRelationDesc->rd_att->constr->has_generated_stored)
1674  ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1675  CMD_UPDATE);
1676 
1677  /*
1678  * Check any RLS UPDATE WITH CHECK policies
1679  *
1680  * If we generate a new candidate tuple after EvalPlanQual testing, we
1681  * must loop back here and recheck any RLS policies and constraints.
1682  * (We don't need to redo triggers, however. If there are any BEFORE
1683  * triggers then trigger.c will have done table_tuple_lock to lock the
1684  * correct tuple, so there's no need to do them again.)
1685  */
1686 lreplace:;
1687 
1688  /* ensure slot is independent, consider e.g. EPQ */
1689  ExecMaterializeSlot(slot);
1690 
1691  /*
1692  * If partition constraint fails, this row might get moved to another
1693  * partition, in which case we should check the RLS CHECK policy just
1694  * before inserting into the new partition, rather than doing it here.
1695  * This is because a trigger on that partition might again change the
1696  * row. So skip the WCO checks if the partition constraint fails.
1697  */
1698  partition_constraint_failed =
1699  resultRelationDesc->rd_rel->relispartition &&
1700  !ExecPartitionCheck(resultRelInfo, slot, estate, false);
1701 
1702  if (!partition_constraint_failed &&
1703  resultRelInfo->ri_WithCheckOptions != NIL)
1704  {
1705  /*
1706  * ExecWithCheckOptions() will skip any WCOs which are not of the
1707  * kind we are looking for at this point.
1708  */
1710  resultRelInfo, slot, estate);
1711  }
1712 
1713  /*
1714  * If a partition check failed, try to move the row into the right
1715  * partition.
1716  */
1717  if (partition_constraint_failed)
1718  {
1719  TupleTableSlot *inserted_tuple,
1720  *retry_slot;
1721  bool retry;
1722 
1723  /*
1724  * ExecCrossPartitionUpdate will first DELETE the row from the
1725  * partition it's currently in and then insert it back into the
1726  * root table, which will re-route it to the correct partition.
1727  * The first part may have to be repeated if it is detected that
1728  * the tuple we're trying to move has been concurrently updated.
1729  */
1730  retry = !ExecCrossPartitionUpdate(mtstate, resultRelInfo, tupleid,
1731  oldtuple, slot, planSlot,
1732  epqstate, canSetTag,
1733  &retry_slot, &inserted_tuple);
1734  if (retry)
1735  {
1736  slot = retry_slot;
1737  goto lreplace;
1738  }
1739 
1740  return inserted_tuple;
1741  }
1742 
1743  /*
1744  * Check the constraints of the tuple. We've already checked the
1745  * partition constraint above; however, we must still ensure the tuple
1746  * passes all other constraints, so we will call ExecConstraints() and
1747  * have it validate all remaining checks.
1748  */
1749  if (resultRelationDesc->rd_att->constr)
1750  ExecConstraints(resultRelInfo, slot, estate);
1751 
1752  /*
1753  * replace the heap tuple
1754  *
1755  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
1756  * that the row to be updated is visible to that snapshot, and throw a
1757  * can't-serialize error if not. This is a special-case behavior
1758  * needed for referential integrity updates in transaction-snapshot
1759  * mode transactions.
1760  */
1761  result = table_tuple_update(resultRelationDesc, tupleid, slot,
1762  estate->es_output_cid,
1763  estate->es_snapshot,
1764  estate->es_crosscheck_snapshot,
1765  true /* wait for commit */ ,
1766  &tmfd, &lockmode, &update_indexes);
1767 
1768  switch (result)
1769  {
1770  case TM_SelfModified:
1771 
1772  /*
1773  * The target tuple was already updated or deleted by the
1774  * current command, or by a later command in the current
1775  * transaction. The former case is possible in a join UPDATE
1776  * where multiple tuples join to the same target tuple. This
1777  * is pretty questionable, but Postgres has always allowed it:
1778  * we just execute the first update action and ignore
1779  * additional update attempts.
1780  *
1781  * The latter case arises if the tuple is modified by a
1782  * command in a BEFORE trigger, or perhaps by a command in a
1783  * volatile function used in the query. In such situations we
1784  * should not ignore the update, but it is equally unsafe to
1785  * proceed. We don't want to discard the original UPDATE
1786  * while keeping the triggered actions based on it; and we
1787  * have no principled way to merge this update with the
1788  * previous ones. So throwing an error is the only safe
1789  * course.
1790  *
1791  * If a trigger actually intends this type of interaction, it
1792  * can re-execute the UPDATE (assuming it can figure out how)
1793  * and then return NULL to cancel the outer update.
1794  */
1795  if (tmfd.cmax != estate->es_output_cid)
1796  ereport(ERROR,
1797  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1798  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1799  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1800 
1801  /* Else, already updated by self; nothing to do */
1802  return NULL;
1803 
1804  case TM_Ok:
1805  break;
1806 
1807  case TM_Updated:
1808  {
1809  TupleTableSlot *inputslot;
1810  TupleTableSlot *epqslot;
1811  TupleTableSlot *oldSlot;
1812 
1814  ereport(ERROR,
1815  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1816  errmsg("could not serialize access due to concurrent update")));
1817 
1818  /*
1819  * Already know that we're going to need to do EPQ, so
1820  * fetch tuple directly into the right slot.
1821  */
1822  inputslot = EvalPlanQualSlot(epqstate, resultRelationDesc,
1823  resultRelInfo->ri_RangeTableIndex);
1824 
1825  result = table_tuple_lock(resultRelationDesc, tupleid,
1826  estate->es_snapshot,
1827  inputslot, estate->es_output_cid,
1828  lockmode, LockWaitBlock,
1830  &tmfd);
1831 
1832  switch (result)
1833  {
1834  case TM_Ok:
1835  Assert(tmfd.traversed);
1836 
1837  epqslot = EvalPlanQual(epqstate,
1838  resultRelationDesc,
1839  resultRelInfo->ri_RangeTableIndex,
1840  inputslot);
1841  if (TupIsNull(epqslot))
1842  /* Tuple not passing quals anymore, exiting... */
1843  return NULL;
1844 
1845  /* Make sure ri_oldTupleSlot is initialized. */
1846  if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
1847  ExecInitUpdateProjection(mtstate, resultRelInfo);
1848 
1849  /* Fetch the most recent version of old tuple. */
1850  oldSlot = resultRelInfo->ri_oldTupleSlot;
1851  if (!table_tuple_fetch_row_version(resultRelationDesc,
1852  tupleid,
1853  SnapshotAny,
1854  oldSlot))
1855  elog(ERROR, "failed to fetch tuple being updated");
1856  slot = ExecGetUpdateNewTuple(resultRelInfo,
1857  epqslot, oldSlot);
1858  goto lreplace;
1859 
1860  case TM_Deleted:
1861  /* tuple already deleted; nothing to do */
1862  return NULL;
1863 
1864  case TM_SelfModified:
1865 
1866  /*
1867  * This can be reached when following an update
1868  * chain from a tuple updated by another session,
1869  * reaching a tuple that was already updated in
1870  * this transaction. If previously modified by
1871  * this command, ignore the redundant update,
1872  * otherwise error out.
1873  *
1874  * See also TM_SelfModified response to
1875  * table_tuple_update() above.
1876  */
1877  if (tmfd.cmax != estate->es_output_cid)
1878  ereport(ERROR,
1879  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1880  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1881  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1882  return NULL;
1883 
1884  default:
1885  /* see table_tuple_lock call in ExecDelete() */
1886  elog(ERROR, "unexpected table_tuple_lock status: %u",
1887  result);
1888  return NULL;
1889  }
1890  }
1891 
1892  break;
1893 
1894  case TM_Deleted:
1896  ereport(ERROR,
1897  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1898  errmsg("could not serialize access due to concurrent delete")));
1899  /* tuple already deleted; nothing to do */
1900  return NULL;
1901 
1902  default:
1903  elog(ERROR, "unrecognized table_tuple_update status: %u",
1904  result);
1905  return NULL;
1906  }
1907 
1908  /* insert index entries for tuple if necessary */
1909  if (resultRelInfo->ri_NumIndices > 0 && update_indexes)
1910  recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
1911  slot, estate, true, false,
1912  NULL, NIL);
1913  }
1914 
1915  if (canSetTag)
1916  (estate->es_processed)++;
1917 
1918  /* AFTER ROW UPDATE Triggers */
1919  ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, slot,
1920  recheckIndexes,
1921  mtstate->operation == CMD_INSERT ?
1922  mtstate->mt_oc_transition_capture :
1923  mtstate->mt_transition_capture);
1924 
1925  list_free(recheckIndexes);
1926 
1927  /*
1928  * Check any WITH CHECK OPTION constraints from parent views. We are
1929  * required to do this after testing all constraints and uniqueness
1930  * violations per the SQL spec, so we do it after actually updating the
1931  * record in the heap and all indexes.
1932  *
1933  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1934  * are looking for at this point.
1935  */
1936  if (resultRelInfo->ri_WithCheckOptions != NIL)
1937  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1938 
1939  /* Process RETURNING if present */
1940  if (resultRelInfo->ri_projectReturning)
1941  return ExecProcessReturning(resultRelInfo, slot, planSlot);
1942 
1943  return NULL;
1944 }
int ri_NumIndices
Definition: execnodes.h:414
#define NIL
Definition: pg_list.h:65
Oid tts_tableOid
Definition: tuptable.h:131
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1225
Relation ri_RelationDesc
Definition: execnodes.h:411
LockTupleMode
Definition: lockoptions.h:49
int errhint(const char *fmt,...)
Definition: elog.c:1156
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1954
CommandId es_output_cid
Definition: execnodes.h:570
TupleTableSlot * ExecGetUpdateNewTuple(ResultRelInfo *relinfo, TupleTableSlot *planSlot, TupleTableSlot *oldSlot)
static void ExecInitUpdateProjection(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
CommandId cmax
Definition: tableam.h:128
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1821
#define IsolationUsesXactSnapshot()
Definition: xact.h:51
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:557
int errcode(int sqlerrcode)
Definition: elog.c:698
void ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
CmdType operation
Definition: execnodes.h:1184
Snapshot es_snapshot
Definition: execnodes.h:556
Form_pg_class rd_rel
Definition: rel.h:110
bool ri_projectNewInfoValid
Definition: execnodes.h:435
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:408
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:156
bool has_generated_stored
Definition: tupdesc.h:45
#define ERROR
Definition: elog.h:46
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:2469
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
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:2359
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:488
#define TupIsNull(slot)
Definition: tuptable.h:292
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:455
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:438
static bool table_tuple_fetch_row_version(Relation rel, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot)
Definition: tableam.h:1257
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:2826
TM_Result
Definition: tableam.h:71
List * ri_WithCheckOptions
Definition: execnodes.h:470
TupleDesc rd_att
Definition: rel.h:111
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:443
#define ereport(elevel,...)
Definition: elog.h:157
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:235
#define Assert(condition)
Definition: c.h:804
Definition: tableam.h:77
uint64 es_processed
Definition: execnodes.h:602
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:433
#define TUPLE_LOCK_FLAG_FIND_LAST_VERSION
Definition: tableam.h:242
#define SnapshotAny
Definition: snapmgr.h:67
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:394
int errmsg(const char *fmt,...)
Definition: elog.c:909
bool ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2869
void list_free(List *list)
Definition: list.c:1391
#define elog(elevel,...)
Definition: elog.h:232
bool ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *newslot)
Definition: trigger.c:2694
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool emitError)
Definition: execMain.c:1697
#define unlikely(x)
Definition: c.h:273
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:472
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)
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:417

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

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

2201 {
2202  ModifyTable *plan = (ModifyTable *) node->ps.plan;
2203  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
2204 
2205  switch (node->operation)
2206  {
2207  case CMD_INSERT:
2208  if (plan->onConflictAction == ONCONFLICT_UPDATE)
2210  resultRelInfo,
2211  node->mt_oc_transition_capture);
2212  ExecASInsertTriggers(node->ps.state, resultRelInfo,
2213  node->mt_transition_capture);
2214  break;
2215  case CMD_UPDATE:
2216  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
2217  node->mt_transition_capture);
2218  break;
2219  case CMD_DELETE:
2220  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
2221  node->mt_transition_capture);
2222  break;
2223  default:
2224  elog(ERROR, "unknown operation");
2225  break;
2226  }
2227 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1225
CmdType operation
Definition: execnodes.h:1184
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1196
EState * state
Definition: execnodes.h:966
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2439
#define ERROR
Definition: elog.h:46
PlanState ps
Definition: execnodes.h:1183
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1222
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2678
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2225
Plan * plan
Definition: execnodes.h:964
OnConflictAction onConflictAction
Definition: plannodes.h:233
#define elog(elevel,...)
Definition: elog.h:232

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

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

2172 {
2173  ModifyTable *plan = (ModifyTable *) node->ps.plan;
2174  ResultRelInfo *resultRelInfo = node->rootResultRelInfo;
2175 
2176  switch (node->operation)
2177  {
2178  case CMD_INSERT:
2179  ExecBSInsertTriggers(node->ps.state, resultRelInfo);
2180  if (plan->onConflictAction == ONCONFLICT_UPDATE)
2182  resultRelInfo);
2183  break;
2184  case CMD_UPDATE:
2185  ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
2186  break;
2187  case CMD_DELETE:
2188  ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
2189  break;
2190  default:
2191  elog(ERROR, "unknown operation");
2192  break;
2193  }
2194 }
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2388
CmdType operation
Definition: execnodes.h:1184
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1196
EState * state
Definition: execnodes.h:966
#define ERROR
Definition: elog.h:46
PlanState ps
Definition: execnodes.h:1183
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2174
Plan * plan
Definition: execnodes.h:964
OnConflictAction onConflictAction
Definition: plannodes.h:233
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2620
#define elog(elevel,...)
Definition: elog.h:232