PostgreSQL Source Code  git master
nodeModifyTable.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "access/xact.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 "parser/parsetree.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
Include dependency graph for nodeModifyTable.c:

Go to the source code of this file.

Functions

static bool ExecOnConflictUpdate (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *planSlot, TupleTableSlot *excludedSlot, EState *estate, bool canSetTag, TupleTableSlot **returning)
 
static void ExecCheckPlanOutput (Relation resultRel, List *targetList)
 
static TupleTableSlotExecProcessReturning (ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
 
static void ExecCheckHeapTupleVisible (EState *estate, HeapTuple tuple, Buffer buffer)
 
static void ExecCheckTIDVisible (EState *estate, ResultRelInfo *relinfo, ItemPointer tid)
 
static TupleTableSlotExecInsert (ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, List *arbiterIndexes, OnConflictAction onconflict, EState *estate, bool canSetTag)
 
static TupleTableSlotExecDelete (ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
 
static TupleTableSlotExecUpdate (ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
 
static void fireBSTriggers (ModifyTableState *node)
 
static ResultRelInfogetASTriggerResultRelInfo (ModifyTableState *node)
 
static void fireASTriggers (ModifyTableState *node)
 
static void ExecSetupTransitionCaptureState (ModifyTableState *mtstate, EState *estate)
 
static TupleTableSlotExecModifyTable (PlanState *pstate)
 
ModifyTableStateExecInitModifyTable (ModifyTable *node, EState *estate, int eflags)
 
void ExecEndModifyTable (ModifyTableState *node)
 
void ExecReScanModifyTable (ModifyTableState *node)
 

Function Documentation

◆ ExecCheckHeapTupleVisible()

static void ExecCheckHeapTupleVisible ( EState estate,
HeapTuple  tuple,
Buffer  buffer 
)
static

Definition at line 193 of file nodeModifyTable.c.

References BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, ereport, errcode(), errmsg(), ERROR, EState::es_snapshot, HeapTupleHeaderGetXmin, HeapTupleSatisfiesVisibility, IsolationUsesXactSnapshot, LockBuffer(), HeapTupleData::t_data, and TransactionIdIsCurrentTransactionId().

Referenced by ExecCheckTIDVisible(), and ExecOnConflictUpdate().

196 {
198  return;
199 
200  /*
201  * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
202  * Caller should be holding pin, but not lock.
203  */
205  if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
206  {
207  /*
208  * We should not raise a serialization failure if the conflict is
209  * against a tuple inserted by our own transaction, even if it's not
210  * visible to our snapshot. (This would happen, for example, if
211  * conflicting keys are proposed for insertion in a single command.)
212  */
214  ereport(ERROR,
215  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
216  errmsg("could not serialize access due to concurrent update")));
217  }
219 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:766
#define IsolationUsesXactSnapshot()
Definition: xact.h:43
int errcode(int sqlerrcode)
Definition: elog.c:575
Snapshot es_snapshot
Definition: execnodes.h:440
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer)
Definition: tqual.h:45
HeapTupleHeader t_data
Definition: htup.h:67
#define ERROR
Definition: elog.h:43
#define ereport(elevel, rest)
Definition: elog.h:122
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:312
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:88

◆ ExecCheckPlanOutput()

static void ExecCheckPlanOutput ( Relation  resultRel,
List targetList 
)
static

Definition at line 80 of file nodeModifyTable.c.

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

Referenced by ExecInitModifyTable().

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

◆ ExecCheckTIDVisible()

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

Definition at line 225 of file nodeModifyTable.c.

References buffer, elog, ERROR, ExecCheckHeapTupleVisible(), heap_fetch(), IsolationUsesXactSnapshot, ReleaseBuffer(), ResultRelInfo::ri_RelationDesc, SnapshotAny, and HeapTupleData::t_self.

Referenced by ExecInsert().

228 {
229  Relation rel = relinfo->ri_RelationDesc;
230  Buffer buffer;
231  HeapTupleData tuple;
232 
233  /* Redundantly check isolation level */
235  return;
236 
237  tuple.t_self = *tid;
238  if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
239  elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
240  ExecCheckHeapTupleVisible(estate, &tuple, buffer);
241  ReleaseBuffer(buffer);
242 }
Relation ri_RelationDesc
Definition: execnodes.h:365
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation)
Definition: heapam.c:1876
#define IsolationUsesXactSnapshot()
Definition: xact.h:43
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
static void ExecCheckHeapTupleVisible(EState *estate, HeapTuple tuple, Buffer buffer)
#define SnapshotAny
Definition: tqual.h:28
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define elog
Definition: elog.h:219
int Buffer
Definition: buf.h:23

◆ ExecDelete()

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

Definition at line 676 of file nodeModifyTable.c.

References Assert, BufferIsValid, HeapUpdateFailureData::cmax, HeapUpdateFailureData::ctid, elog, ereport, errcode(), errhint(), errmsg(), ERROR, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, EState::es_trig_tuple_slot, EvalPlanQual(), ExecARDeleteTriggers(), ExecBRDeleteTriggers(), ExecClearTuple(), FdwRoutine::ExecForeignDelete, ExecIRDeleteTriggers(), ExecMaterializeSlot(), ExecProcessReturning(), ExecSetSlotDescriptor(), ExecStoreAllNullTuple(), ExecStoreTuple(), heap_delete(), heap_fetch(), HeapTupleMayBeUpdated, HeapTupleSelfUpdated, HeapTupleUpdated, InvalidBuffer, IsolationUsesXactSnapshot, ItemPointerEquals(), LockTupleExclusive, ModifyTableState::mt_transition_capture, RelationGetDescr, RelationGetRelid, ReleaseBuffer(), ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, SnapshotAny, HeapTupleData::t_self, HeapTupleData::t_tableOid, TriggerDesc::trig_delete_before_row, TriggerDesc::trig_delete_instead_row, TupleTableSlot::tts_isempty, TupleTableSlot::tts_tupleDescriptor, TupIsNull, and HeapUpdateFailureData::xmax.

Referenced by ExecModifyTable().

683 {
684  ResultRelInfo *resultRelInfo;
685  Relation resultRelationDesc;
686  HTSU_Result result;
688  TupleTableSlot *slot = NULL;
689 
690  /*
691  * get information on the (current) result relation
692  */
693  resultRelInfo = estate->es_result_relation_info;
694  resultRelationDesc = resultRelInfo->ri_RelationDesc;
695 
696  /* BEFORE ROW DELETE Triggers */
697  if (resultRelInfo->ri_TrigDesc &&
698  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
699  {
700  bool dodelete;
701 
702  dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
703  tupleid, oldtuple);
704 
705  if (!dodelete) /* "do nothing" */
706  return NULL;
707  }
708 
709  /* INSTEAD OF ROW DELETE Triggers */
710  if (resultRelInfo->ri_TrigDesc &&
711  resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
712  {
713  bool dodelete;
714 
715  Assert(oldtuple != NULL);
716  dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
717 
718  if (!dodelete) /* "do nothing" */
719  return NULL;
720  }
721  else if (resultRelInfo->ri_FdwRoutine)
722  {
723  HeapTuple tuple;
724 
725  /*
726  * delete from foreign table: let the FDW do it
727  *
728  * We offer the trigger tuple slot as a place to store RETURNING data,
729  * although the FDW can return some other slot if it wants. Set up
730  * the slot's tupdesc so the FDW doesn't need to do that for itself.
731  */
732  slot = estate->es_trig_tuple_slot;
733  if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
734  ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
735 
736  slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
737  resultRelInfo,
738  slot,
739  planSlot);
740 
741  if (slot == NULL) /* "do nothing" */
742  return NULL;
743 
744  /*
745  * RETURNING expressions might reference the tableoid column, so
746  * initialize t_tableOid before evaluating them.
747  */
748  if (slot->tts_isempty)
749  ExecStoreAllNullTuple(slot);
750  tuple = ExecMaterializeSlot(slot);
751  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
752  }
753  else
754  {
755  /*
756  * delete the tuple
757  *
758  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
759  * that the row to be deleted is visible to that snapshot, and throw a
760  * can't-serialize error if not. This is a special-case behavior
761  * needed for referential integrity updates in transaction-snapshot
762  * mode transactions.
763  */
764 ldelete:;
765  result = heap_delete(resultRelationDesc, tupleid,
766  estate->es_output_cid,
767  estate->es_crosscheck_snapshot,
768  true /* wait for commit */ ,
769  &hufd);
770  switch (result)
771  {
773 
774  /*
775  * The target tuple was already updated or deleted by the
776  * current command, or by a later command in the current
777  * transaction. The former case is possible in a join DELETE
778  * where multiple tuples join to the same target tuple. This
779  * is somewhat questionable, but Postgres has always allowed
780  * it: we just ignore additional deletion attempts.
781  *
782  * The latter case arises if the tuple is modified by a
783  * command in a BEFORE trigger, or perhaps by a command in a
784  * volatile function used in the query. In such situations we
785  * should not ignore the deletion, but it is equally unsafe to
786  * proceed. We don't want to discard the original DELETE
787  * while keeping the triggered actions based on its deletion;
788  * and it would be no better to allow the original DELETE
789  * while discarding updates that it triggered. The row update
790  * carries some information that might be important according
791  * to business rules; so throwing an error is the only safe
792  * course.
793  *
794  * If a trigger actually intends this type of interaction, it
795  * can re-execute the DELETE and then return NULL to cancel
796  * the outer delete.
797  */
798  if (hufd.cmax != estate->es_output_cid)
799  ereport(ERROR,
800  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
801  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
802  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
803 
804  /* Else, already deleted by self; nothing to do */
805  return NULL;
806 
808  break;
809 
810  case HeapTupleUpdated:
812  ereport(ERROR,
813  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
814  errmsg("could not serialize access due to concurrent update")));
815  if (!ItemPointerEquals(tupleid, &hufd.ctid))
816  {
817  TupleTableSlot *epqslot;
818 
819  epqslot = EvalPlanQual(estate,
820  epqstate,
821  resultRelationDesc,
822  resultRelInfo->ri_RangeTableIndex,
824  &hufd.ctid,
825  hufd.xmax);
826  if (!TupIsNull(epqslot))
827  {
828  *tupleid = hufd.ctid;
829  goto ldelete;
830  }
831  }
832  /* tuple already deleted; nothing to do */
833  return NULL;
834 
835  default:
836  elog(ERROR, "unrecognized heap_delete status: %u", result);
837  return NULL;
838  }
839 
840  /*
841  * Note: Normally one would think that we have to delete index tuples
842  * associated with the heap tuple now...
843  *
844  * ... but in POSTGRES, we have no need to do this because VACUUM will
845  * take care of it later. We can't delete index tuples immediately
846  * anyway, since the tuple is still visible to other transactions.
847  */
848  }
849 
850  if (canSetTag)
851  (estate->es_processed)++;
852 
853  /* AFTER ROW DELETE Triggers */
854  ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
855  mtstate->mt_transition_capture);
856 
857  /* Process RETURNING if present */
858  if (resultRelInfo->ri_projectReturning)
859  {
860  /*
861  * We have to put the target tuple into a slot, which means first we
862  * gotta fetch it. We can use the trigger tuple slot.
863  */
864  TupleTableSlot *rslot;
865  HeapTupleData deltuple;
866  Buffer delbuffer;
867 
868  if (resultRelInfo->ri_FdwRoutine)
869  {
870  /* FDW must have provided a slot containing the deleted row */
871  Assert(!TupIsNull(slot));
872  delbuffer = InvalidBuffer;
873  }
874  else
875  {
876  slot = estate->es_trig_tuple_slot;
877  if (oldtuple != NULL)
878  {
879  deltuple = *oldtuple;
880  delbuffer = InvalidBuffer;
881  }
882  else
883  {
884  deltuple.t_self = *tupleid;
885  if (!heap_fetch(resultRelationDesc, SnapshotAny,
886  &deltuple, &delbuffer, false, NULL))
887  elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
888  }
889 
890  if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
891  ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
892  ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
893  }
894 
895  rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
896 
897  /*
898  * Before releasing the target tuple again, make sure rslot has a
899  * local copy of any pass-by-reference values.
900  */
901  ExecMaterializeSlot(rslot);
902 
903  ExecClearTuple(slot);
904  if (BufferIsValid(delbuffer))
905  ReleaseBuffer(delbuffer);
906 
907  return rslot;
908  }
909 
910  return NULL;
911 }
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:205
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:320
Relation ri_RelationDesc
Definition: execnodes.h:365
bool tts_isempty
Definition: tuptable.h:116
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition: trigger.c:2604
int errhint(const char *fmt,...)
Definition: elog.c:987
CommandId es_output_cid
Definition: execnodes.h:449
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:512
#define RelationGetDescr(relation)
Definition: rel.h:437
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation)
Definition: heapam.c:1876
TupleTableSlot * EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, int lockmode, ItemPointer tid, TransactionId priorXmax)
Definition: execMain.c:2493
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:439
#define IsolationUsesXactSnapshot()
Definition: xact.h:43
#define InvalidBuffer
Definition: buf.h:25
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:441
int errcode(int sqlerrcode)
Definition: elog.c:575
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
Index ri_RangeTableIndex
Definition: execnodes.h:362
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
Definition: trigger.c:2572
HTSU_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, HeapUpdateFailureData *hufd)
Definition: heapam.c:3030
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:990
CommandId cmax
Definition: heapam.h:72
HTSU_Result
Definition: snapshot.h:121
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:410
#define TupIsNull(slot)
Definition: tuptable.h:138
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:389
Oid t_tableOid
Definition: htup.h:66
TupleTableSlot * es_trig_tuple_slot
Definition: execnodes.h:471
#define ereport(elevel, rest)
Definition: elog.h:122
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:377
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
TransactionId xmax
Definition: heapam.h:71
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:121
bool trig_delete_instead_row
Definition: reltrigger.h:67
void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc)
Definition: execTuples.c:247
#define SnapshotAny
Definition: tqual.h:28
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple)
Definition: trigger.c:2503
#define Assert(condition)
Definition: c.h:680
uint64 es_processed
Definition: execnodes.h:488
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:725
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
int errmsg(const char *fmt,...)
Definition: elog.c:797
ItemPointerData ctid
Definition: heapam.h:70
#define elog
Definition: elog.h:219
int Buffer
Definition: buf.h:23
#define RelationGetRelid(relation)
Definition: rel.h:425
bool trig_delete_before_row
Definition: reltrigger.h:65
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:454

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 2344 of file nodeModifyTable.c.

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

Referenced by ExecEndNode().

2345 {
2346  int i;
2347 
2348  /*
2349  * Allow any FDWs to shut down
2350  */
2351  for (i = 0; i < node->mt_nplans; i++)
2352  {
2353  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
2354 
2355  if (!resultRelInfo->ri_usesFdwDirectModify &&
2356  resultRelInfo->ri_FdwRoutine != NULL &&
2357  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
2358  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
2359  resultRelInfo);
2360  }
2361 
2362  /* Close all the partitioned tables, leaf partitions, and their indices */
2363  if (node->mt_partition_tuple_routing)
2365 
2366  /*
2367  * Free the exprcontext
2368  */
2369  ExecFreeExprContext(&node->ps);
2370 
2371  /*
2372  * clean out the tuple table
2373  */
2375 
2376  /*
2377  * Terminate EPQ execution if active
2378  */
2379  EvalPlanQualEnd(&node->mt_epqstate);
2380 
2381  /*
2382  * shut down subplans
2383  */
2384  for (i = 0; i < node->mt_nplans; i++)
2385  ExecEndNode(node->mt_plans[i]);
2386 }
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:988
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:539
ResultRelInfo * resultRelInfo
Definition: execnodes.h:976
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:439
EState * state
Definition: execnodes.h:860
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:603
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:3223
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:891
PlanState ps
Definition: execnodes.h:969
bool ri_usesFdwDirectModify
Definition: execnodes.h:395
EPQState mt_epqstate
Definition: execnodes.h:980
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:389
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:206
PlanState ** mt_plans
Definition: execnodes.h:973
void ExecCleanupTupleRouting(PartitionTupleRouting *proute)
int i

◆ ExecInitModifyTable()

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

Definition at line 1826 of file nodeModifyTable.c.

References ModifyTable::arbiterIndexes, Assert, AttributeNumberIsValid, FdwRoutine::BeginForeignModify, bms_is_member(), ModifyTable::canSetTag, ModifyTableState::canSetTag, castNode, CheckValidResultRel(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ModifyTable::epqParam, ERROR, EState::es_auxmodifytables, EState::es_range_table, EState::es_result_relation_info, EState::es_result_relations, EState::es_root_result_relations, EState::es_trig_tuple_slot, EvalPlanQualInit(), EvalPlanQualSetPlan(), ModifyTable::exclRelTlist, EXEC_FLAG_BACKWARD, EXEC_FLAG_EXPLAIN_ONLY, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignResultTypeFromTL(), ExecBuildAuxRowMark(), ExecBuildProjectionInfo(), ExecCheckPlanOutput(), ExecFindJunkAttribute(), ExecFindRowMark(), ExecInitExtraTupleSlot(), ExecInitJunkFilter(), ExecInitNode(), ExecInitQual(), ExecInitResultTupleSlot(), ExecModifyTable(), ExecOpenIndices(), PlanState::ExecProcNode, ExecSetSlotDescriptor(), ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), ExecTypeFromTL(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, getrelid, heap_close, heap_open(), i, PlanRowMark::isParent, JunkFilter::jf_junkAttNo, lappend(), lcons(), lfirst, lfirst_node, linitial, linitial_int, list_length(), list_nth(), makeNode, map_partition_varattnos(), ModifyTableState::mt_arbiterindexes, ModifyTableState::mt_arowmarks, ModifyTableState::mt_conflproj, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_excludedtlist, ModifyTableState::mt_existing, ModifyTableState::mt_nplans, ModifyTableState::mt_onconflict, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_plans, ModifyTableState::mt_whichplan, NIL, NoLock, ModifyTable::nominalRelation, PartitionTupleRouting::num_partitions, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTable::operation, ModifyTableState::operation, palloc0(), ModifyTable::partitioned_rels, PartitionTupleRouting::partitions, PlanState::plan, ModifyTable::plans, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationData::rd_att, RelationData::rd_rel, RELKIND_FOREIGN_TABLE, RELKIND_MATVIEW, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, TargetEntry::resjunk, ModifyTable::resultRelIndex, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_IndexRelationDescs, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_onConflictSetProj, ResultRelInfo::ri_onConflictSetWhere, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_usesFdwDirectModify, ResultRelInfo::ri_WithCheckOptionExprs, ResultRelInfo::ri_WithCheckOptions, ModifyTable::rootResultRelIndex, ModifyTableState::rootResultRelInfo, ModifyTable::rowMarks, PlanRowMark::rti, PlanState::state, Plan::targetlist, tupleDesc::tdhasoid, and ModifyTable::withCheckOptionLists.

Referenced by ExecInitNode().

1827 {
1828  ModifyTableState *mtstate;
1829  CmdType operation = node->operation;
1830  int nplans = list_length(node->plans);
1831  ResultRelInfo *saved_resultRelInfo;
1832  ResultRelInfo *resultRelInfo;
1833  Plan *subplan;
1834  ListCell *l;
1835  int i;
1836  Relation rel;
1837  PartitionTupleRouting *proute = NULL;
1838  int num_partitions = 0;
1839 
1840  /* check for unsupported flags */
1841  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
1842 
1843  /*
1844  * create state structure
1845  */
1846  mtstate = makeNode(ModifyTableState);
1847  mtstate->ps.plan = (Plan *) node;
1848  mtstate->ps.state = estate;
1849  mtstate->ps.ExecProcNode = ExecModifyTable;
1850 
1851  mtstate->operation = operation;
1852  mtstate->canSetTag = node->canSetTag;
1853  mtstate->mt_done = false;
1854 
1855  mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
1856  mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
1857 
1858  /* If modifying a partitioned table, initialize the root table info */
1859  if (node->rootResultRelIndex >= 0)
1860  mtstate->rootResultRelInfo = estate->es_root_result_relations +
1861  node->rootResultRelIndex;
1862 
1863  mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
1864  mtstate->mt_nplans = nplans;
1865  mtstate->mt_onconflict = node->onConflictAction;
1866  mtstate->mt_arbiterindexes = node->arbiterIndexes;
1867 
1868  /* set up epqstate with dummy subplan data for the moment */
1869  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
1870  mtstate->fireBSTriggers = true;
1871 
1872  /*
1873  * call ExecInitNode on each of the plans to be executed and save the
1874  * results into the array "mt_plans". This is also a convenient place to
1875  * verify that the proposed target relations are valid and open their
1876  * indexes for insertion of new index entries. Note we *must* set
1877  * estate->es_result_relation_info correctly while we initialize each
1878  * sub-plan; ExecContextForcesOids depends on that!
1879  */
1880  saved_resultRelInfo = estate->es_result_relation_info;
1881 
1882  resultRelInfo = mtstate->resultRelInfo;
1883  i = 0;
1884  foreach(l, node->plans)
1885  {
1886  subplan = (Plan *) lfirst(l);
1887 
1888  /* Initialize the usesFdwDirectModify flag */
1889  resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
1890  node->fdwDirectModifyPlans);
1891 
1892  /*
1893  * Verify result relation is a valid target for the current operation
1894  */
1895  CheckValidResultRel(resultRelInfo, operation);
1896 
1897  /*
1898  * If there are indices on the result relation, open them and save
1899  * descriptors in the result relation info, so that we can add new
1900  * index entries for the tuples we add/update. We need not do this
1901  * for a DELETE, however, since deletion doesn't affect indexes. Also,
1902  * inside an EvalPlanQual operation, the indexes might be open
1903  * already, since we share the resultrel state with the original
1904  * query.
1905  */
1906  if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
1907  operation != CMD_DELETE &&
1908  resultRelInfo->ri_IndexRelationDescs == NULL)
1909  ExecOpenIndices(resultRelInfo, mtstate->mt_onconflict != ONCONFLICT_NONE);
1910 
1911  /* Now init the plan for this result rel */
1912  estate->es_result_relation_info = resultRelInfo;
1913  mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
1914 
1915  /* Also let FDWs init themselves for foreign-table result rels */
1916  if (!resultRelInfo->ri_usesFdwDirectModify &&
1917  resultRelInfo->ri_FdwRoutine != NULL &&
1918  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
1919  {
1920  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
1921 
1922  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
1923  resultRelInfo,
1924  fdw_private,
1925  i,
1926  eflags);
1927  }
1928 
1929  resultRelInfo++;
1930  i++;
1931  }
1932 
1933  estate->es_result_relation_info = saved_resultRelInfo;
1934 
1935  /* The root table RT index is at the head of the partitioned_rels list */
1936  if (node->partitioned_rels)
1937  {
1938  Index root_rti;
1939  Oid root_oid;
1940 
1941  root_rti = linitial_int(node->partitioned_rels);
1942  root_oid = getrelid(root_rti, estate->es_range_table);
1943  rel = heap_open(root_oid, NoLock); /* locked by InitPlan */
1944  }
1945  else
1946  rel = mtstate->resultRelInfo->ri_RelationDesc;
1947 
1948  /* Build state for INSERT tuple routing */
1949  if (operation == CMD_INSERT &&
1950  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1951  {
1952  proute = mtstate->mt_partition_tuple_routing =
1954  rel, node->nominalRelation,
1955  estate);
1956  num_partitions = proute->num_partitions;
1957  }
1958 
1959  /*
1960  * Build state for collecting transition tuples. This requires having a
1961  * valid trigger query context, so skip it in explain-only mode.
1962  */
1963  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
1964  ExecSetupTransitionCaptureState(mtstate, estate);
1965 
1966  /*
1967  * Initialize any WITH CHECK OPTION constraints if needed.
1968  */
1969  resultRelInfo = mtstate->resultRelInfo;
1970  i = 0;
1971  foreach(l, node->withCheckOptionLists)
1972  {
1973  List *wcoList = (List *) lfirst(l);
1974  List *wcoExprs = NIL;
1975  ListCell *ll;
1976 
1977  foreach(ll, wcoList)
1978  {
1979  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
1980  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
1981  mtstate->mt_plans[i]);
1982 
1983  wcoExprs = lappend(wcoExprs, wcoExpr);
1984  }
1985 
1986  resultRelInfo->ri_WithCheckOptions = wcoList;
1987  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
1988  resultRelInfo++;
1989  i++;
1990  }
1991 
1992  /*
1993  * Build WITH CHECK OPTION constraints for each leaf partition rel. Note
1994  * that we didn't build the withCheckOptionList for each partition within
1995  * the planner, but simple translation of the varattnos for each partition
1996  * will suffice. This only occurs for the INSERT case; UPDATE/DELETE
1997  * cases are handled above.
1998  */
1999  if (node->withCheckOptionLists != NIL && num_partitions > 0)
2000  {
2001  List *wcoList;
2002  PlanState *plan;
2003 
2004  /*
2005  * In case of INSERT on partitioned tables, there is only one plan.
2006  * Likewise, there is only one WITH CHECK OPTIONS list, not one per
2007  * partition. We make a copy of the WCO qual for each partition; note
2008  * that, if there are SubPlans in there, they all end up attached to
2009  * the one parent Plan node.
2010  */
2011  Assert(operation == CMD_INSERT &&
2012  list_length(node->withCheckOptionLists) == 1 &&
2013  mtstate->mt_nplans == 1);
2014  wcoList = linitial(node->withCheckOptionLists);
2015  plan = mtstate->mt_plans[0];
2016  for (i = 0; i < num_partitions; i++)
2017  {
2018  Relation partrel;
2019  List *mapped_wcoList;
2020  List *wcoExprs = NIL;
2021  ListCell *ll;
2022 
2023  resultRelInfo = proute->partitions[i];
2024  partrel = resultRelInfo->ri_RelationDesc;
2025 
2026  /* varno = node->nominalRelation */
2027  mapped_wcoList = map_partition_varattnos(wcoList,
2028  node->nominalRelation,
2029  partrel, rel, NULL);
2030  foreach(ll, mapped_wcoList)
2031  {
2033  ExprState *wcoExpr = ExecInitQual(castNode(List, wco->qual),
2034  plan);
2035 
2036  wcoExprs = lappend(wcoExprs, wcoExpr);
2037  }
2038 
2039  resultRelInfo->ri_WithCheckOptions = mapped_wcoList;
2040  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
2041  }
2042  }
2043 
2044  /*
2045  * Initialize RETURNING projections if needed.
2046  */
2047  if (node->returningLists)
2048  {
2049  TupleTableSlot *slot;
2050  ExprContext *econtext;
2051  List *returningList;
2052 
2053  /*
2054  * Initialize result tuple slot and assign its rowtype using the first
2055  * RETURNING list. We assume the rest will look the same.
2056  */
2057  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
2058 
2059  /* Set up a slot for the output of the RETURNING projection(s) */
2060  ExecInitResultTupleSlot(estate, &mtstate->ps);
2061  ExecAssignResultTypeFromTL(&mtstate->ps);
2062  slot = mtstate->ps.ps_ResultTupleSlot;
2063 
2064  /* Need an econtext too */
2065  if (mtstate->ps.ps_ExprContext == NULL)
2066  ExecAssignExprContext(estate, &mtstate->ps);
2067  econtext = mtstate->ps.ps_ExprContext;
2068 
2069  /*
2070  * Build a projection for each result rel.
2071  */
2072  resultRelInfo = mtstate->resultRelInfo;
2073  foreach(l, node->returningLists)
2074  {
2075  List *rlist = (List *) lfirst(l);
2076 
2077  resultRelInfo->ri_projectReturning =
2078  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
2079  resultRelInfo->ri_RelationDesc->rd_att);
2080  resultRelInfo++;
2081  }
2082 
2083  /*
2084  * Build a projection for each leaf partition rel. Note that we
2085  * didn't build the returningList for each partition within the
2086  * planner, but simple translation of the varattnos for each partition
2087  * will suffice. This only occurs for the INSERT case; UPDATE/DELETE
2088  * are handled above.
2089  */
2090  returningList = linitial(node->returningLists);
2091  for (i = 0; i < num_partitions; i++)
2092  {
2093  Relation partrel;
2094  List *rlist;
2095 
2096  resultRelInfo = proute->partitions[i];
2097  partrel = resultRelInfo->ri_RelationDesc;
2098 
2099  /* varno = node->nominalRelation */
2100  rlist = map_partition_varattnos(returningList,
2101  node->nominalRelation,
2102  partrel, rel, NULL);
2103  resultRelInfo->ri_projectReturning =
2104  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
2105  resultRelInfo->ri_RelationDesc->rd_att);
2106  }
2107  }
2108  else
2109  {
2110  /*
2111  * We still must construct a dummy result tuple type, because InitPlan
2112  * expects one (maybe should change that?).
2113  */
2114  mtstate->ps.plan->targetlist = NIL;
2115  ExecInitResultTupleSlot(estate, &mtstate->ps);
2116  ExecAssignResultTypeFromTL(&mtstate->ps);
2117 
2118  mtstate->ps.ps_ExprContext = NULL;
2119  }
2120 
2121  /* Close the root partitioned rel if we opened it above. */
2122  if (rel != mtstate->resultRelInfo->ri_RelationDesc)
2123  heap_close(rel, NoLock);
2124 
2125  /*
2126  * If needed, Initialize target list, projection and qual for ON CONFLICT
2127  * DO UPDATE.
2128  */
2129  resultRelInfo = mtstate->resultRelInfo;
2130  if (node->onConflictAction == ONCONFLICT_UPDATE)
2131  {
2132  ExprContext *econtext;
2133  TupleDesc tupDesc;
2134 
2135  /* insert may only have one plan, inheritance is not expanded */
2136  Assert(nplans == 1);
2137 
2138  /* already exists if created by RETURNING processing above */
2139  if (mtstate->ps.ps_ExprContext == NULL)
2140  ExecAssignExprContext(estate, &mtstate->ps);
2141 
2142  econtext = mtstate->ps.ps_ExprContext;
2143 
2144  /* initialize slot for the existing tuple */
2145  mtstate->mt_existing = ExecInitExtraTupleSlot(mtstate->ps.state);
2147  resultRelInfo->ri_RelationDesc->rd_att);
2148 
2149  /* carried forward solely for the benefit of explain */
2150  mtstate->mt_excludedtlist = node->exclRelTlist;
2151 
2152  /* create target slot for UPDATE SET projection */
2153  tupDesc = ExecTypeFromTL((List *) node->onConflictSet,
2154  resultRelInfo->ri_RelationDesc->rd_rel->relhasoids);
2155  mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
2156  ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
2157 
2158  /* build UPDATE SET projection state */
2159  resultRelInfo->ri_onConflictSetProj =
2160  ExecBuildProjectionInfo(node->onConflictSet, econtext,
2161  mtstate->mt_conflproj, &mtstate->ps,
2162  resultRelInfo->ri_RelationDesc->rd_att);
2163 
2164  /* build DO UPDATE WHERE clause expression */
2165  if (node->onConflictWhere)
2166  {
2167  ExprState *qualexpr;
2168 
2169  qualexpr = ExecInitQual((List *) node->onConflictWhere,
2170  &mtstate->ps);
2171 
2172  resultRelInfo->ri_onConflictSetWhere = qualexpr;
2173  }
2174  }
2175 
2176  /*
2177  * If we have any secondary relations in an UPDATE or DELETE, they need to
2178  * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
2179  * EvalPlanQual mechanism needs to be told about them. Locate the
2180  * relevant ExecRowMarks.
2181  */
2182  foreach(l, node->rowMarks)
2183  {
2185  ExecRowMark *erm;
2186 
2187  /* ignore "parent" rowmarks; they are irrelevant at runtime */
2188  if (rc->isParent)
2189  continue;
2190 
2191  /* find ExecRowMark (same for all subplans) */
2192  erm = ExecFindRowMark(estate, rc->rti, false);
2193 
2194  /* build ExecAuxRowMark for each subplan */
2195  for (i = 0; i < nplans; i++)
2196  {
2197  ExecAuxRowMark *aerm;
2198 
2199  subplan = mtstate->mt_plans[i]->plan;
2200  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
2201  mtstate->mt_arowmarks[i] = lappend(mtstate->mt_arowmarks[i], aerm);
2202  }
2203  }
2204 
2205  /* select first subplan */
2206  mtstate->mt_whichplan = 0;
2207  subplan = (Plan *) linitial(node->plans);
2208  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan,
2209  mtstate->mt_arowmarks[0]);
2210 
2211  /*
2212  * Initialize the junk filter(s) if needed. INSERT queries need a filter
2213  * if there are any junk attrs in the tlist. UPDATE and DELETE always
2214  * need a filter, since there's always at least one junk attribute present
2215  * --- no need to look first. Typically, this will be a 'ctid' or
2216  * 'wholerow' attribute, but in the case of a foreign data wrapper it
2217  * might be a set of junk attributes sufficient to identify the remote
2218  * row.
2219  *
2220  * If there are multiple result relations, each one needs its own junk
2221  * filter. Note multiple rels are only possible for UPDATE/DELETE, so we
2222  * can't be fooled by some needing a filter and some not.
2223  *
2224  * This section of code is also a convenient place to verify that the
2225  * output of an INSERT or UPDATE matches the target table(s).
2226  */
2227  {
2228  bool junk_filter_needed = false;
2229 
2230  switch (operation)
2231  {
2232  case CMD_INSERT:
2233  foreach(l, subplan->targetlist)
2234  {
2235  TargetEntry *tle = (TargetEntry *) lfirst(l);
2236 
2237  if (tle->resjunk)
2238  {
2239  junk_filter_needed = true;
2240  break;
2241  }
2242  }
2243  break;
2244  case CMD_UPDATE:
2245  case CMD_DELETE:
2246  junk_filter_needed = true;
2247  break;
2248  default:
2249  elog(ERROR, "unknown operation");
2250  break;
2251  }
2252 
2253  if (junk_filter_needed)
2254  {
2255  resultRelInfo = mtstate->resultRelInfo;
2256  for (i = 0; i < nplans; i++)
2257  {
2258  JunkFilter *j;
2259 
2260  subplan = mtstate->mt_plans[i]->plan;
2261  if (operation == CMD_INSERT || operation == CMD_UPDATE)
2262  ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
2263  subplan->targetlist);
2264 
2265  j = ExecInitJunkFilter(subplan->targetlist,
2266  resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
2267  ExecInitExtraTupleSlot(estate));
2268 
2269  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2270  {
2271  /* For UPDATE/DELETE, find the appropriate junk attr now */
2272  char relkind;
2273 
2274  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2275  if (relkind == RELKIND_RELATION ||
2276  relkind == RELKIND_MATVIEW ||
2277  relkind == RELKIND_PARTITIONED_TABLE)
2278  {
2279  j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
2281  elog(ERROR, "could not find junk ctid column");
2282  }
2283  else if (relkind == RELKIND_FOREIGN_TABLE)
2284  {
2285  /*
2286  * When there is a row-level trigger, there should be
2287  * a wholerow attribute.
2288  */
2289  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2290  }
2291  else
2292  {
2293  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2295  elog(ERROR, "could not find junk wholerow column");
2296  }
2297  }
2298 
2299  resultRelInfo->ri_junkFilter = j;
2300  resultRelInfo++;
2301  }
2302  }
2303  else
2304  {
2305  if (operation == CMD_INSERT)
2307  subplan->targetlist);
2308  }
2309  }
2310 
2311  /*
2312  * Set up a tuple table slot for use for trigger output tuples. In a plan
2313  * containing multiple ModifyTable nodes, all can share one such slot, so
2314  * we keep it in the estate.
2315  */
2316  if (estate->es_trig_tuple_slot == NULL)
2317  estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate);
2318 
2319  /*
2320  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
2321  * to estate->es_auxmodifytables so that it will be run to completion by
2322  * ExecPostprocessPlan. (It'd actually work fine to add the primary
2323  * ModifyTable node too, but there's no need.) Note the use of lcons not
2324  * lappend: we need later-initialized ModifyTable nodes to be shut down
2325  * before earlier ones. This ensures that we don't throw away RETURNING
2326  * rows that need to be seen by a later CTE subplan.
2327  */
2328  if (!mtstate->canSetTag)
2329  estate->es_auxmodifytables = lcons(mtstate,
2330  estate->es_auxmodifytables);
2331 
2332  return mtstate;
2333 }
AttrNumber jf_junkAttNo
Definition: execnodes.h:347
#define NIL
Definition: pg_list.h:69
JunkFilter * ri_junkFilter
Definition: execnodes.h:407
List * arbiterIndexes
Definition: plannodes.h:233
Relation ri_RelationDesc
Definition: execnodes.h:365
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:229
ExprState * ri_onConflictSetWhere
Definition: execnodes.h:416
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate)
Definition: execTuples.c:852
Index nominalRelation
Definition: plannodes.h:219
bool tdhasoid
Definition: tupdesc.h:82
ProjectionInfo * ri_onConflictSetProj
Definition: execnodes.h:413
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:988
List * withCheckOptionLists
Definition: plannodes.h:226
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
int resultRelIndex
Definition: plannodes.h:223
ResultRelInfo * resultRelInfo
Definition: execnodes.h:976
AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
Definition: execJunk.c:209
ExprContext * ps_ExprContext
Definition: execnodes.h:892
TupleTableSlot * mt_conflproj
Definition: execnodes.h:987
#define RELKIND_MATVIEW
Definition: pg_class.h:165
PartitionTupleRouting * ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel, Index resultRTindex, EState *estate)
Definition: execPartition.c:49
bool canSetTag
Definition: plannodes.h:218
CmdType operation
Definition: execnodes.h:970
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:977
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2828
#define heap_close(r, l)
Definition: heapam.h:97
EState * state
Definition: execnodes.h:860
List * es_range_table
Definition: execnodes.h:442
Form_pg_class rd_rel
Definition: rel.h:114
unsigned int Oid
Definition: postgres_ext.h:31
List * plans
Definition: plannodes.h:225
ResultRelInfo ** partitions
Definition: execPartition.h:80
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:204
void ExecAssignResultTypeFromTL(PlanState *planstate)
Definition: execUtils.c:448
List * onConflictSet
Definition: plannodes.h:234
int rootResultRelIndex
Definition: plannodes.h:224
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:149
TupleTableSlot * mt_existing
Definition: execnodes.h:985
List * ri_WithCheckOptionExprs
Definition: execnodes.h:401
#define linitial_int(l)
Definition: pg_list.h:112
OnConflictAction mt_onconflict
Definition: execnodes.h:982
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:891
List * rowMarks
Definition: plannodes.h:230
bool resjunk
Definition: primnodes.h:1382
#define linitial(l)
Definition: pg_list.h:111
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel, bool *found_whole_row)
Definition: partition.c:1465
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:969
void ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
Definition: execTuples.c:832
bool ri_usesFdwDirectModify
Definition: execnodes.h:395
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:1098
#define EXEC_FLAG_BACKWARD
Definition: executor.h:60
#define lfirst_node(type, lc)
Definition: pg_list.h:109
void * list_nth(const List *list, int n)
Definition: list.c:410
#define NoLock
Definition: lockdefs.h:34
ResultRelInfo * es_result_relations
Definition: execnodes.h:452
JunkFilter * ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
Definition: execJunk.c:61
List * fdwPrivLists
Definition: plannodes.h:228
EPQState mt_epqstate
Definition: execnodes.h:980
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:410
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:389
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
List * partitioned_rels
Definition: plannodes.h:221
TupleTableSlot * es_trig_tuple_slot
Definition: execnodes.h:471
TupleDesc ExecTypeFromTL(List *targetList, bool hasoid)
Definition: execTuples.c:888
List * lappend(List *list, void *datum)
Definition: list.c:128
PlanState ** mt_plans
Definition: execnodes.h:973
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
void * palloc0(Size size)
Definition: mcxt.c:864
List * es_auxmodifytables
Definition: execnodes.h:499
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:864
void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc)
Definition: execTuples.c:247
List * ri_WithCheckOptions
Definition: execnodes.h:398
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
unsigned int Index
Definition: c.h:423
TupleDesc rd_att
Definition: rel.h:115
void EvalPlanQualInit(EPQState *epqstate, EState *estate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2809
Plan * plan
Definition: execnodes.h:858
List * lcons(void *datum, List *list)
Definition: list.c:259
#define makeNode(_type_)
Definition: nodes.h:560
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
#define EXEC_FLAG_MARK
Definition: executor.h:61
OnConflictAction onConflictAction
Definition: plannodes.h:232
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:426
static int list_length(const List *l)
Definition: pg_list.h:89
List * targetlist
Definition: plannodes.h:144
List * mt_arbiterindexes
Definition: execnodes.h:983
List * mt_excludedtlist
Definition: execnodes.h:986
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:348
#define getrelid(rangeindex, rangetable)
Definition: parsetree.h:41
CmdType operation
Definition: plannodes.h:217
ResultRelInfo * es_root_result_relations
Definition: execnodes.h:463
int i
List * returningLists
Definition: plannodes.h:227
bool isParent
Definition: plannodes.h:1026
#define elog
Definition: elog.h:219
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:202
#define RELKIND_RELATION
Definition: pg_class.h:160
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139
Definition: pg_list.h:45
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:464
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:58
CmdType
Definition: nodes.h:652
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:371
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2422
List * exclRelTlist
Definition: plannodes.h:237
List ** mt_arowmarks
Definition: execnodes.h:979
int epqParam
Definition: plannodes.h:231
Node * onConflictWhere
Definition: plannodes.h:235
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2398
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:454

◆ ExecInsert()

static TupleTableSlot* ExecInsert ( ModifyTableState mtstate,
TupleTableSlot slot,
TupleTableSlot planSlot,
List arbiterIndexes,
OnConflictAction  onconflict,
EState estate,
bool  canSetTag 
)
static

Definition at line 254 of file nodeModifyTable.c.

References Assert, tupleDesc::constr, do_convert_tuple(), ereport, errcode(), errmsg(), ERROR, EState::es_lastoid, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, ExecARInsertTriggers(), ExecBRInsertTriggers(), ExecCheckIndexConstraints(), ExecCheckTIDVisible(), ExecConstraints(), ExecFindPartition(), FdwRoutine::ExecForeignInsert, ExecInsertIndexTuples(), ExecIRInsertTriggers(), ExecMaterializeSlot(), ExecOnConflictUpdate(), ExecProcessReturning(), ExecSetSlotDescriptor(), ExecStoreTuple(), ExecWithCheckOptions(), GetCurrentTransactionId(), heap_abort_speculative(), heap_finish_speculative(), heap_insert(), HEAP_INSERT_SPECULATIVE, HeapTupleHeaderSetSpeculativeToken, HeapTupleSetOid, InstrCountFiltered2, InvalidBuffer, InvalidOid, list_free(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, ModifyTableState::mt_transition_tupconv_maps, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_UPDATE, PartitionTupleRouting::partition_dispatch_info, PartitionTupleRouting::partition_tupconv_maps, PartitionTupleRouting::partition_tuple_slot, PartitionTupleRouting::partitions, ModifyTableState::ps, RelationData::rd_att, RelationData::rd_rel, RelationGetDescr, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_PartitionCheck, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, setLastTid(), SpeculativeInsertionLockAcquire(), SpeculativeInsertionLockRelease(), HeapTupleData::t_data, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, TriggerDesc::trig_insert_before_row, TriggerDesc::trig_insert_instead_row, WCO_RLS_INSERT_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecModifyTable().

261 {
262  HeapTuple tuple;
263  ResultRelInfo *resultRelInfo;
264  ResultRelInfo *saved_resultRelInfo = NULL;
265  Relation resultRelationDesc;
266  Oid newId;
267  List *recheckIndexes = NIL;
268  TupleTableSlot *result = NULL;
269 
270  /*
271  * get the heap tuple out of the tuple table slot, making sure we have a
272  * writable copy
273  */
274  tuple = ExecMaterializeSlot(slot);
275 
276  /*
277  * get information on the (current) result relation
278  */
279  resultRelInfo = estate->es_result_relation_info;
280 
281  /* Determine the partition to heap_insert the tuple into */
282  if (mtstate->mt_partition_tuple_routing)
283  {
284  int leaf_part_index;
286  TupleConversionMap *map;
287 
288  /*
289  * Away we go ... If we end up not finding a partition after all,
290  * ExecFindPartition() does not return and errors out instead.
291  * Otherwise, the returned value is to be used as an index into arrays
292  * proute->partitions[] and proute->partition_tupconv_maps[] that will
293  * get us the ResultRelInfo and TupleConversionMap for the partition,
294  * respectively.
295  */
296  leaf_part_index = ExecFindPartition(resultRelInfo,
297  proute->partition_dispatch_info,
298  slot,
299  estate);
300  Assert(leaf_part_index >= 0 &&
301  leaf_part_index < proute->num_partitions);
302 
303  /*
304  * Save the old ResultRelInfo and switch to the one corresponding to
305  * the selected partition.
306  */
307  saved_resultRelInfo = resultRelInfo;
308  resultRelInfo = proute->partitions[leaf_part_index];
309 
310  /* We do not yet have a way to insert into a foreign partition */
311  if (resultRelInfo->ri_FdwRoutine)
312  ereport(ERROR,
313  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
314  errmsg("cannot route inserted tuples to a foreign table")));
315 
316  /* For ExecInsertIndexTuples() to work on the partition's indexes */
317  estate->es_result_relation_info = resultRelInfo;
318 
319  /*
320  * If we're capturing transition tuples, we might need to convert from
321  * the partition rowtype to parent rowtype.
322  */
323  if (mtstate->mt_transition_capture != NULL)
324  {
325  if (resultRelInfo->ri_TrigDesc &&
326  (resultRelInfo->ri_TrigDesc->trig_insert_before_row ||
327  resultRelInfo->ri_TrigDesc->trig_insert_instead_row))
328  {
329  /*
330  * If there are any BEFORE or INSTEAD triggers on the
331  * partition, we'll have to be ready to convert their result
332  * back to tuplestore format.
333  */
335  mtstate->mt_transition_capture->tcs_map =
336  mtstate->mt_transition_tupconv_maps[leaf_part_index];
337  }
338  else
339  {
340  /*
341  * Otherwise, just remember the original unconverted tuple, to
342  * avoid a needless round trip conversion.
343  */
345  mtstate->mt_transition_capture->tcs_map = NULL;
346  }
347  }
348  if (mtstate->mt_oc_transition_capture != NULL)
350  mtstate->mt_transition_tupconv_maps[leaf_part_index];
351 
352  /*
353  * We might need to convert from the parent rowtype to the partition
354  * rowtype.
355  */
356  map = proute->partition_tupconv_maps[leaf_part_index];
357  if (map)
358  {
359  Relation partrel = resultRelInfo->ri_RelationDesc;
360 
361  tuple = do_convert_tuple(tuple, map);
362 
363  /*
364  * We must use the partition's tuple descriptor from this point
365  * on, until we're finished dealing with the partition. Use the
366  * dedicated slot for that.
367  */
368  slot = proute->partition_tuple_slot;
369  Assert(slot != NULL);
370  ExecSetSlotDescriptor(slot, RelationGetDescr(partrel));
371  ExecStoreTuple(tuple, slot, InvalidBuffer, true);
372  }
373  }
374 
375  resultRelationDesc = resultRelInfo->ri_RelationDesc;
376 
377  /*
378  * If the result relation has OIDs, force the tuple's OID to zero so that
379  * heap_insert will assign a fresh OID. Usually the OID already will be
380  * zero at this point, but there are corner cases where the plan tree can
381  * return a tuple extracted literally from some table with the same
382  * rowtype.
383  *
384  * XXX if we ever wanted to allow users to assign their own OIDs to new
385  * rows, this'd be the place to do it. For the moment, we make a point of
386  * doing this before calling triggers, so that a user-supplied trigger
387  * could hack the OID if desired.
388  */
389  if (resultRelationDesc->rd_rel->relhasoids)
390  HeapTupleSetOid(tuple, InvalidOid);
391 
392  /*
393  * BEFORE ROW INSERT Triggers.
394  *
395  * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
396  * INSERT ... ON CONFLICT statement. We cannot check for constraint
397  * violations before firing these triggers, because they can change the
398  * values to insert. Also, they can run arbitrary user-defined code with
399  * side-effects that we can't cancel by just not inserting the tuple.
400  */
401  if (resultRelInfo->ri_TrigDesc &&
402  resultRelInfo->ri_TrigDesc->trig_insert_before_row)
403  {
404  slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
405 
406  if (slot == NULL) /* "do nothing" */
407  return NULL;
408 
409  /* trigger might have changed tuple */
410  tuple = ExecMaterializeSlot(slot);
411  }
412 
413  /* INSTEAD OF ROW INSERT Triggers */
414  if (resultRelInfo->ri_TrigDesc &&
415  resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
416  {
417  slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);
418 
419  if (slot == NULL) /* "do nothing" */
420  return NULL;
421 
422  /* trigger might have changed tuple */
423  tuple = ExecMaterializeSlot(slot);
424 
425  newId = InvalidOid;
426  }
427  else if (resultRelInfo->ri_FdwRoutine)
428  {
429  /*
430  * insert into foreign table: let the FDW do it
431  */
432  slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
433  resultRelInfo,
434  slot,
435  planSlot);
436 
437  if (slot == NULL) /* "do nothing" */
438  return NULL;
439 
440  /* FDW might have changed tuple */
441  tuple = ExecMaterializeSlot(slot);
442 
443  /*
444  * AFTER ROW Triggers or RETURNING expressions might reference the
445  * tableoid column, so initialize t_tableOid before evaluating them.
446  */
447  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
448 
449  newId = InvalidOid;
450  }
451  else
452  {
453  /*
454  * We always check the partition constraint, including when the tuple
455  * got here via tuple-routing. However we don't need to in the latter
456  * case if no BR trigger is defined on the partition. Note that a BR
457  * trigger might modify the tuple such that the partition constraint
458  * is no longer satisfied, so we need to check in that case.
459  */
460  bool check_partition_constr =
461  (resultRelInfo->ri_PartitionCheck != NIL);
462 
463  /*
464  * Constraints might reference the tableoid column, so initialize
465  * t_tableOid before evaluating them.
466  */
467  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
468 
469  /*
470  * Check any RLS INSERT WITH CHECK policies
471  *
472  * ExecWithCheckOptions() will skip any WCOs which are not of the kind
473  * we are looking for at this point.
474  */
475  if (resultRelInfo->ri_WithCheckOptions != NIL)
477  resultRelInfo, slot, estate);
478 
479  /*
480  * No need though if the tuple has been routed, and a BR trigger
481  * doesn't exist.
482  */
483  if (saved_resultRelInfo != NULL &&
484  !(resultRelInfo->ri_TrigDesc &&
485  resultRelInfo->ri_TrigDesc->trig_insert_before_row))
486  check_partition_constr = false;
487 
488  /* Check the constraints of the tuple */
489  if (resultRelationDesc->rd_att->constr || check_partition_constr)
490  ExecConstraints(resultRelInfo, slot, estate, true);
491 
492  if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
493  {
494  /* Perform a speculative insertion. */
495  uint32 specToken;
496  ItemPointerData conflictTid;
497  bool specConflict;
498 
499  /*
500  * Do a non-conclusive check for conflicts first.
501  *
502  * We're not holding any locks yet, so this doesn't guarantee that
503  * the later insert won't conflict. But it avoids leaving behind
504  * a lot of canceled speculative insertions, if you run a lot of
505  * INSERT ON CONFLICT statements that do conflict.
506  *
507  * We loop back here if we find a conflict below, either during
508  * the pre-check, or when we re-check after inserting the tuple
509  * speculatively.
510  */
511  vlock:
512  specConflict = false;
513  if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
514  arbiterIndexes))
515  {
516  /* committed conflict tuple found */
517  if (onconflict == ONCONFLICT_UPDATE)
518  {
519  /*
520  * In case of ON CONFLICT DO UPDATE, execute the UPDATE
521  * part. Be prepared to retry if the UPDATE fails because
522  * of another concurrent UPDATE/DELETE to the conflict
523  * tuple.
524  */
525  TupleTableSlot *returning = NULL;
526 
527  if (ExecOnConflictUpdate(mtstate, resultRelInfo,
528  &conflictTid, planSlot, slot,
529  estate, canSetTag, &returning))
530  {
531  InstrCountFiltered2(&mtstate->ps, 1);
532  return returning;
533  }
534  else
535  goto vlock;
536  }
537  else
538  {
539  /*
540  * In case of ON CONFLICT DO NOTHING, do nothing. However,
541  * verify that the tuple is visible to the executor's MVCC
542  * snapshot at higher isolation levels.
543  */
544  Assert(onconflict == ONCONFLICT_NOTHING);
545  ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid);
546  InstrCountFiltered2(&mtstate->ps, 1);
547  return NULL;
548  }
549  }
550 
551  /*
552  * Before we start insertion proper, acquire our "speculative
553  * insertion lock". Others can use that to wait for us to decide
554  * if we're going to go ahead with the insertion, instead of
555  * waiting for the whole transaction to complete.
556  */
558  HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
559 
560  /* insert the tuple, with the speculative token */
561  newId = heap_insert(resultRelationDesc, tuple,
562  estate->es_output_cid,
564  NULL);
565 
566  /* insert index entries for tuple */
567  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
568  estate, true, &specConflict,
569  arbiterIndexes);
570 
571  /* adjust the tuple's state accordingly */
572  if (!specConflict)
573  heap_finish_speculative(resultRelationDesc, tuple);
574  else
575  heap_abort_speculative(resultRelationDesc, tuple);
576 
577  /*
578  * Wake up anyone waiting for our decision. They will re-check
579  * the tuple, see that it's no longer speculative, and wait on our
580  * XID as if this was a regularly inserted tuple all along. Or if
581  * we killed the tuple, they will see it's dead, and proceed as if
582  * the tuple never existed.
583  */
585 
586  /*
587  * If there was a conflict, start from the beginning. We'll do
588  * the pre-check again, which will now find the conflicting tuple
589  * (unless it aborts before we get there).
590  */
591  if (specConflict)
592  {
593  list_free(recheckIndexes);
594  goto vlock;
595  }
596 
597  /* Since there was no insertion conflict, we're done */
598  }
599  else
600  {
601  /*
602  * insert the tuple normally.
603  *
604  * Note: heap_insert returns the tid (location) of the new tuple
605  * in the t_self field.
606  */
607  newId = heap_insert(resultRelationDesc, tuple,
608  estate->es_output_cid,
609  0, NULL);
610 
611  /* insert index entries for tuple */
612  if (resultRelInfo->ri_NumIndices > 0)
613  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
614  estate, false, NULL,
615  arbiterIndexes);
616  }
617  }
618 
619  if (canSetTag)
620  {
621  (estate->es_processed)++;
622  estate->es_lastoid = newId;
623  setLastTid(&(tuple->t_self));
624  }
625 
626  /* AFTER ROW INSERT Triggers */
627  ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
628  mtstate->mt_transition_capture);
629 
630  list_free(recheckIndexes);
631 
632  /*
633  * Check any WITH CHECK OPTION constraints from parent views. We are
634  * required to do this after testing all constraints and uniqueness
635  * violations per the SQL spec, so we do it after actually inserting the
636  * record into the heap and all indexes.
637  *
638  * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
639  * tuple will never be seen, if it violates the WITH CHECK OPTION.
640  *
641  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
642  * are looking for at this point.
643  */
644  if (resultRelInfo->ri_WithCheckOptions != NIL)
645  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
646 
647  /* Process RETURNING if present */
648  if (resultRelInfo->ri_projectReturning)
649  result = ExecProcessReturning(resultRelInfo, slot, planSlot);
650 
651  if (saved_resultRelInfo)
652  estate->es_result_relation_info = saved_resultRelInfo;
653 
654  return result;
655 }
int ri_NumIndices
Definition: execnodes.h:368
#define NIL
Definition: pg_list.h:69
int ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, TupleTableSlot *slot, EState *estate)
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:320
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:992
Relation ri_RelationDesc
Definition: execnodes.h:365
void SpeculativeInsertionLockRelease(TransactionId xid)
Definition: lmgr.c:695
static bool ExecOnConflictUpdate(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, ItemPointer conflictTid, TupleTableSlot *planSlot, TupleTableSlot *excludedSlot, EState *estate, bool canSetTag, TupleTableSlot **returning)
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2088
void heap_abort_speculative(Relation relation, HeapTuple tuple)
Definition: heapam.c:6107
CommandId es_output_cid
Definition: execnodes.h:449
List * ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:271
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:988
#define RelationGetDescr(relation)
Definition: rel.h:437
Oid es_lastoid
Definition: execnodes.h:489
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:203
TupleConversionMap ** partition_tupconv_maps
Definition: execPartition.h:82
TupleTableSlot * ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2369
#define HeapTupleHeaderSetSpeculativeToken(tup, token)
Definition: htup_details.h:439
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:575
uint32 SpeculativeInsertionLockAcquire(TransactionId xid)
Definition: lmgr.c:669
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2354
TupleTableSlot * partition_tuple_slot
Definition: execPartition.h:83
Form_pg_class rd_rel
Definition: rel.h:114
unsigned int Oid
Definition: postgres_ext.h:31
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool check_partition_constraint)
Definition: execMain.c:1954
bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, List *arbiterIndexes)
Definition: execIndexing.c:475
HeapTuple tcs_original_insert_tuple
Definition: trigger.h:82
ResultRelInfo ** partitions
Definition: execPartition.h:80
#define HEAP_INSERT_SPECULATIVE
Definition: heapam.h:31
HeapTupleHeader t_data
Definition: htup.h:67
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:703
bool trig_insert_instead_row
Definition: reltrigger.h:57
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:969
static void ExecCheckTIDVisible(EState *estate, ResultRelInfo *relinfo, ItemPointer tid)
TupleConversionMap * tcs_map
Definition: trigger.h:73
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:418
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:990
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:410
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:389
unsigned int uint32
Definition: c.h:306
Oid t_tableOid
Definition: htup.h:66
PartitionDispatch * partition_dispatch_info
Definition: execPartition.h:78
void setLastTid(const ItemPointer tid)
Definition: tid.c:252
#define ereport(elevel, rest)
Definition: elog.h:122
Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
Definition: heapam.c:2413
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:377
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
bool trig_insert_before_row
Definition: reltrigger.h:55
void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc)
Definition: execTuples.c:247
List * ri_WithCheckOptions
Definition: execnodes.h:398
List * ri_PartitionCheck
Definition: execnodes.h:419
TupleDesc rd_att
Definition: rel.h:115
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:680
#define InstrCountFiltered2(node, delta)
Definition: execnodes.h:912
void heap_finish_speculative(Relation relation, HeapTuple tuple)
Definition: heapam.c:6016
uint64 es_processed
Definition: execnodes.h:488
TupleConstr * constr
Definition: tupdesc.h:84
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:725
HeapTuple do_convert_tuple(HeapTuple tuple, TupleConversionMap *map)
Definition: tupconvert.c:354
TupleConversionMap ** mt_transition_tupconv_maps
Definition: execnodes.h:994
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
TupleTableSlot * ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2288
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:454

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 1570 of file nodeModifyTable.c.

References Assert, AttributeNumberIsValid, ModifyTableState::canSetTag, castNode, CHECK_FOR_INTERRUPTS, CMD_DELETE, CMD_INSERT, CMD_UPDATE, DatumGetHeapTupleHeader, DatumGetPointer, elog, ERROR, EState::es_epqTuple, EState::es_result_relation_info, EvalPlanQualSetPlan(), EvalPlanQualSetSlot, ExecDelete(), ExecFilterJunk(), ExecGetJunkAttribute(), ExecInsert(), ExecProcessReturning(), ExecProcNode(), ExecUpdate(), fireASTriggers(), ModifyTableState::fireBSTriggers, fireBSTriggers(), HeapTupleHeaderGetDatumLength, InvalidOid, ItemPointerSetInvalid, JunkFilter::jf_junkAttNo, ModifyTableState::mt_arbiterindexes, ModifyTableState::mt_arowmarks, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_nplans, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_onconflict, ModifyTableState::mt_plans, ModifyTableState::mt_transition_capture, ModifyTableState::mt_transition_tupconv_maps, ModifyTableState::mt_whichplan, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationData::rd_rel, RelationGetRelid, RELKIND_FOREIGN_TABLE, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_VIEW, ResetPerTupleExprContext, ModifyTableState::resultRelInfo, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_usesFdwDirectModify, PlanState::state, HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransitionCaptureState::tcs_map, and TupIsNull.

Referenced by ExecInitModifyTable().

1571 {
1572  ModifyTableState *node = castNode(ModifyTableState, pstate);
1573  EState *estate = node->ps.state;
1574  CmdType operation = node->operation;
1575  ResultRelInfo *saved_resultRelInfo;
1576  ResultRelInfo *resultRelInfo;
1577  PlanState *subplanstate;
1578  JunkFilter *junkfilter;
1579  TupleTableSlot *slot;
1580  TupleTableSlot *planSlot;
1581  ItemPointer tupleid;
1582  ItemPointerData tuple_ctid;
1583  HeapTupleData oldtupdata;
1584  HeapTuple oldtuple;
1585 
1587 
1588  /*
1589  * This should NOT get called during EvalPlanQual; we should have passed a
1590  * subplan tree to EvalPlanQual, instead. Use a runtime test not just
1591  * Assert because this condition is easy to miss in testing. (Note:
1592  * although ModifyTable should not get executed within an EvalPlanQual
1593  * operation, we do have to allow it to be initialized and shut down in
1594  * case it is within a CTE subplan. Hence this test must be here, not in
1595  * ExecInitModifyTable.)
1596  */
1597  if (estate->es_epqTuple != NULL)
1598  elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
1599 
1600  /*
1601  * If we've already completed processing, don't try to do more. We need
1602  * this test because ExecPostprocessPlan might call us an extra time, and
1603  * our subplan's nodes aren't necessarily robust against being called
1604  * extra times.
1605  */
1606  if (node->mt_done)
1607  return NULL;
1608 
1609  /*
1610  * On first call, fire BEFORE STATEMENT triggers before proceeding.
1611  */
1612  if (node->fireBSTriggers)
1613  {
1614  fireBSTriggers(node);
1615  node->fireBSTriggers = false;
1616  }
1617 
1618  /* Preload local variables */
1619  resultRelInfo = node->resultRelInfo + node->mt_whichplan;
1620  subplanstate = node->mt_plans[node->mt_whichplan];
1621  junkfilter = resultRelInfo->ri_junkFilter;
1622 
1623  /*
1624  * es_result_relation_info must point to the currently active result
1625  * relation while we are within this ModifyTable node. Even though
1626  * ModifyTable nodes can't be nested statically, they can be nested
1627  * dynamically (since our subplan could include a reference to a modifying
1628  * CTE). So we have to save and restore the caller's value.
1629  */
1630  saved_resultRelInfo = estate->es_result_relation_info;
1631 
1632  estate->es_result_relation_info = resultRelInfo;
1633 
1634  /*
1635  * Fetch rows from subplan(s), and execute the required table modification
1636  * for each row.
1637  */
1638  for (;;)
1639  {
1640  /*
1641  * Reset the per-output-tuple exprcontext. This is needed because
1642  * triggers expect to use that context as workspace. It's a bit ugly
1643  * to do this below the top level of the plan, however. We might need
1644  * to rethink this later.
1645  */
1646  ResetPerTupleExprContext(estate);
1647 
1648  planSlot = ExecProcNode(subplanstate);
1649 
1650  if (TupIsNull(planSlot))
1651  {
1652  /* advance to next subplan if any */
1653  node->mt_whichplan++;
1654  if (node->mt_whichplan < node->mt_nplans)
1655  {
1656  resultRelInfo++;
1657  subplanstate = node->mt_plans[node->mt_whichplan];
1658  junkfilter = resultRelInfo->ri_junkFilter;
1659  estate->es_result_relation_info = resultRelInfo;
1660  EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
1661  node->mt_arowmarks[node->mt_whichplan]);
1662  /* Prepare to convert transition tuples from this child. */
1663  if (node->mt_transition_capture != NULL)
1664  {
1665  Assert(node->mt_transition_tupconv_maps != NULL);
1668  }
1669  if (node->mt_oc_transition_capture != NULL)
1670  {
1671  Assert(node->mt_transition_tupconv_maps != NULL);
1674  }
1675  continue;
1676  }
1677  else
1678  break;
1679  }
1680 
1681  /*
1682  * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
1683  * here is compute the RETURNING expressions.
1684  */
1685  if (resultRelInfo->ri_usesFdwDirectModify)
1686  {
1687  Assert(resultRelInfo->ri_projectReturning);
1688 
1689  /*
1690  * A scan slot containing the data that was actually inserted,
1691  * updated or deleted has already been made available to
1692  * ExecProcessReturning by IterateDirectModify, so no need to
1693  * provide it here.
1694  */
1695  slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
1696 
1697  estate->es_result_relation_info = saved_resultRelInfo;
1698  return slot;
1699  }
1700 
1701  EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
1702  slot = planSlot;
1703 
1704  tupleid = NULL;
1705  oldtuple = NULL;
1706  if (junkfilter != NULL)
1707  {
1708  /*
1709  * extract the 'ctid' or 'wholerow' junk attribute.
1710  */
1711  if (operation == CMD_UPDATE || operation == CMD_DELETE)
1712  {
1713  char relkind;
1714  Datum datum;
1715  bool isNull;
1716 
1717  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
1718  if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW)
1719  {
1720  datum = ExecGetJunkAttribute(slot,
1721  junkfilter->jf_junkAttNo,
1722  &isNull);
1723  /* shouldn't ever get a null result... */
1724  if (isNull)
1725  elog(ERROR, "ctid is NULL");
1726 
1727  tupleid = (ItemPointer) DatumGetPointer(datum);
1728  tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
1729  tupleid = &tuple_ctid;
1730  }
1731 
1732  /*
1733  * Use the wholerow attribute, when available, to reconstruct
1734  * the old relation tuple.
1735  *
1736  * Foreign table updates have a wholerow attribute when the
1737  * relation has a row-level trigger. Note that the wholerow
1738  * attribute does not carry system columns. Foreign table
1739  * triggers miss seeing those, except that we know enough here
1740  * to set t_tableOid. Quite separately from this, the FDW may
1741  * fetch its own junk attrs to identify the row.
1742  *
1743  * Other relevant relkinds, currently limited to views, always
1744  * have a wholerow attribute.
1745  */
1746  else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo))
1747  {
1748  datum = ExecGetJunkAttribute(slot,
1749  junkfilter->jf_junkAttNo,
1750  &isNull);
1751  /* shouldn't ever get a null result... */
1752  if (isNull)
1753  elog(ERROR, "wholerow is NULL");
1754 
1755  oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
1756  oldtupdata.t_len =
1758  ItemPointerSetInvalid(&(oldtupdata.t_self));
1759  /* Historically, view triggers see invalid t_tableOid. */
1760  oldtupdata.t_tableOid =
1761  (relkind == RELKIND_VIEW) ? InvalidOid :
1762  RelationGetRelid(resultRelInfo->ri_RelationDesc);
1763 
1764  oldtuple = &oldtupdata;
1765  }
1766  else
1767  Assert(relkind == RELKIND_FOREIGN_TABLE);
1768  }
1769 
1770  /*
1771  * apply the junkfilter if needed.
1772  */
1773  if (operation != CMD_DELETE)
1774  slot = ExecFilterJunk(junkfilter, slot);
1775  }
1776 
1777  switch (operation)
1778  {
1779  case CMD_INSERT:
1780  slot = ExecInsert(node, slot, planSlot,
1781  node->mt_arbiterindexes, node->mt_onconflict,
1782  estate, node->canSetTag);
1783  break;
1784  case CMD_UPDATE:
1785  slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot,
1786  &node->mt_epqstate, estate, node->canSetTag);
1787  break;
1788  case CMD_DELETE:
1789  slot = ExecDelete(node, tupleid, oldtuple, planSlot,
1790  &node->mt_epqstate, estate, node->canSetTag);
1791  break;
1792  default:
1793  elog(ERROR, "unknown operation");
1794  break;
1795  }
1796 
1797  /*
1798  * If we got a RETURNING result, return it to caller. We'll continue
1799  * the work on next call.
1800  */
1801  if (slot)
1802  {
1803  estate->es_result_relation_info = saved_resultRelInfo;
1804  return slot;
1805  }
1806  }
1807 
1808  /* Restore es_result_relation_info before exiting */
1809  estate->es_result_relation_info = saved_resultRelInfo;
1810 
1811  /*
1812  * We're done, but fire AFTER STATEMENT triggers before exiting.
1813  */
1814  fireASTriggers(node);
1815 
1816  node->mt_done = true;
1817 
1818  return NULL;
1819 }
AttrNumber jf_junkAttNo
Definition: execnodes.h:347
JunkFilter * ri_junkFilter
Definition: execnodes.h:407
HeapTuple * es_epqTuple
Definition: execnodes.h:517
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:992
Relation ri_RelationDesc
Definition: execnodes.h:365
#define ResetPerTupleExprContext(estate)
Definition: executor.h:483
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
ResultRelInfo * resultRelInfo
Definition: execnodes.h:976
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
static void fireBSTriggers(ModifyTableState *node)
#define RELKIND_MATVIEW
Definition: pg_class.h:165
CmdType operation
Definition: execnodes.h:970
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2828
EState * state
Definition: execnodes.h:860
Form_pg_class rd_rel
Definition: rel.h:114
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:259
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:67
OnConflictAction mt_onconflict
Definition: execnodes.h:982
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:969
TupleConversionMap * tcs_map
Definition: trigger.h:73
ItemPointerData t_self
Definition: htup.h:65
bool ri_usesFdwDirectModify
Definition: execnodes.h:395
uint32 t_len
Definition: htup.h:64
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:990
EPQState mt_epqstate
Definition: execnodes.h:980
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:410
#define TupIsNull(slot)
Definition: tuptable.h:138
Oid t_tableOid
Definition: htup.h:66
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
PlanState ** mt_plans
Definition: execnodes.h:973
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static void fireASTriggers(ModifyTableState *node)
static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
uintptr_t Datum
Definition: postgres.h:372
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:262
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:240
Plan * plan
Definition: execnodes.h:858
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:680
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, List *arbiterIndexes, OnConflictAction onconflict, EState *estate, bool canSetTag)
#define DatumGetPointer(X)
Definition: postgres.h:555
TupleConversionMap ** mt_transition_tupconv_maps
Definition: execnodes.h:994
List * mt_arbiterindexes
Definition: execnodes.h:983
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: execJunk.c:248
#define RELKIND_VIEW
Definition: pg_class.h:164
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define elog
Definition: elog.h:219
#define RELKIND_RELATION
Definition: pg_class.h:160
#define RelationGetRelid(relation)
Definition: rel.h:425
CmdType
Definition: nodes.h:652
List ** mt_arowmarks
Definition: execnodes.h:979
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:215
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:444
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:454

◆ ExecOnConflictUpdate()

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

Definition at line 1203 of file nodeModifyTable.c.

References buffer, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ereport, errcode(), errhint(), errmsg(), ERROR, EState::es_output_cid, ExecCheckHeapTupleVisible(), ExecProject(), ExecQual(), ExecStoreTuple(), ExecUpdate(), ExecUpdateLockMode(), ExecWithCheckOptions(), heap_lock_tuple(), HeapTupleHeaderGetXmin, HeapTupleInvisible, HeapTupleMayBeUpdated, HeapTupleSelfUpdated, HeapTupleUpdated, InstrCountFiltered1, IsolationUsesXactSnapshot, LockWaitBlock, ModifyTableState::mt_conflproj, ModifyTableState::mt_epqstate, ModifyTableState::mt_existing, NIL, ModifyTableState::ps, PlanState::ps_ExprContext, ReleaseBuffer(), ResetExprContext, ResultRelInfo::ri_onConflictSetProj, ResultRelInfo::ri_onConflictSetWhere, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_WithCheckOptions, PlanState::state, HeapTupleData::t_data, HeapTupleData::t_self, test(), TransactionIdIsCurrentTransactionId(), and WCO_RLS_CONFLICT_CHECK.

Referenced by ExecInsert().

1211 {
1212  ExprContext *econtext = mtstate->ps.ps_ExprContext;
1213  Relation relation = resultRelInfo->ri_RelationDesc;
1214  ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
1215  HeapTupleData tuple;
1216  HeapUpdateFailureData hufd;
1217  LockTupleMode lockmode;
1218  HTSU_Result test;
1219  Buffer buffer;
1220 
1221  /* Determine lock mode to use */
1222  lockmode = ExecUpdateLockMode(estate, resultRelInfo);
1223 
1224  /*
1225  * Lock tuple for update. Don't follow updates when tuple cannot be
1226  * locked without doing so. A row locking conflict here means our
1227  * previous conclusion that the tuple is conclusively committed is not
1228  * true anymore.
1229  */
1230  tuple.t_self = *conflictTid;
1231  test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
1232  lockmode, LockWaitBlock, false, &buffer,
1233  &hufd);
1234  switch (test)
1235  {
1236  case HeapTupleMayBeUpdated:
1237  /* success! */
1238  break;
1239 
1240  case HeapTupleInvisible:
1241 
1242  /*
1243  * This can occur when a just inserted tuple is updated again in
1244  * the same command. E.g. because multiple rows with the same
1245  * conflicting key values are inserted.
1246  *
1247  * This is somewhat similar to the ExecUpdate()
1248  * HeapTupleSelfUpdated case. We do not want to proceed because
1249  * it would lead to the same row being updated a second time in
1250  * some unspecified order, and in contrast to plain UPDATEs
1251  * there's no historical behavior to break.
1252  *
1253  * It is the user's responsibility to prevent this situation from
1254  * occurring. These problems are why SQL-2003 similarly specifies
1255  * that for SQL MERGE, an exception must be raised in the event of
1256  * an attempt to update the same row twice.
1257  */
1259  ereport(ERROR,
1260  (errcode(ERRCODE_CARDINALITY_VIOLATION),
1261  errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
1262  errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
1263 
1264  /* This shouldn't happen */
1265  elog(ERROR, "attempted to lock invisible tuple");
1266 
1267  case HeapTupleSelfUpdated:
1268 
1269  /*
1270  * This state should never be reached. As a dirty snapshot is used
1271  * to find conflicting tuples, speculative insertion wouldn't have
1272  * seen this row to conflict with.
1273  */
1274  elog(ERROR, "unexpected self-updated tuple");
1275 
1276  case HeapTupleUpdated:
1278  ereport(ERROR,
1279  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1280  errmsg("could not serialize access due to concurrent update")));
1281 
1282  /*
1283  * Tell caller to try again from the very start.
1284  *
1285  * It does not make sense to use the usual EvalPlanQual() style
1286  * loop here, as the new version of the row might not conflict
1287  * anymore, or the conflicting tuple has actually been deleted.
1288  */
1289  ReleaseBuffer(buffer);
1290  return false;
1291 
1292  default:
1293  elog(ERROR, "unrecognized heap_lock_tuple status: %u", test);
1294  }
1295 
1296  /*
1297  * Success, the tuple is locked.
1298  *
1299  * Reset per-tuple memory context to free any expression evaluation
1300  * storage allocated in the previous cycle.
1301  */
1302  ResetExprContext(econtext);
1303 
1304  /*
1305  * Verify that the tuple is visible to our MVCC snapshot if the current
1306  * isolation level mandates that.
1307  *
1308  * It's not sufficient to rely on the check within ExecUpdate() as e.g.
1309  * CONFLICT ... WHERE clause may prevent us from reaching that.
1310  *
1311  * This means we only ever continue when a new command in the current
1312  * transaction could see the row, even though in READ COMMITTED mode the
1313  * tuple will not be visible according to the current statement's
1314  * snapshot. This is in line with the way UPDATE deals with newer tuple
1315  * versions.
1316  */
1317  ExecCheckHeapTupleVisible(estate, &tuple, buffer);
1318 
1319  /* Store target's existing tuple in the state's dedicated slot */
1320  ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
1321 
1322  /*
1323  * Make tuple and any needed join variables available to ExecQual and
1324  * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
1325  * the target's existing tuple is installed in the scantuple. EXCLUDED
1326  * has been made to reference INNER_VAR in setrefs.c, but there is no
1327  * other redirection.
1328  */
1329  econtext->ecxt_scantuple = mtstate->mt_existing;
1330  econtext->ecxt_innertuple = excludedSlot;
1331  econtext->ecxt_outertuple = NULL;
1332 
1333  if (!ExecQual(onConflictSetWhere, econtext))
1334  {
1335  ReleaseBuffer(buffer);
1336  InstrCountFiltered1(&mtstate->ps, 1);
1337  return true; /* done with the tuple */
1338  }
1339 
1340  if (resultRelInfo->ri_WithCheckOptions != NIL)
1341  {
1342  /*
1343  * Check target's existing tuple against UPDATE-applicable USING
1344  * security barrier quals (if any), enforced here as RLS checks/WCOs.
1345  *
1346  * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
1347  * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
1348  * but that's almost the extent of its special handling for ON
1349  * CONFLICT DO UPDATE.
1350  *
1351  * The rewriter will also have associated UPDATE applicable straight
1352  * RLS checks/WCOs for the benefit of the ExecUpdate() call that
1353  * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
1354  * kinds, so there is no danger of spurious over-enforcement in the
1355  * INSERT or UPDATE path.
1356  */
1358  mtstate->mt_existing,
1359  mtstate->ps.state);
1360  }
1361 
1362  /* Project the new tuple version */
1363  ExecProject(resultRelInfo->ri_onConflictSetProj);
1364 
1365  /*
1366  * Note that it is possible that the target tuple has been modified in
1367  * this session, after the above heap_lock_tuple. We choose to not error
1368  * out in that case, in line with ExecUpdate's treatment of similar cases.
1369  * This can happen if an UPDATE is triggered from within ExecQual(),
1370  * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
1371  * wCTE in the ON CONFLICT's SET.
1372  */
1373 
1374  /* Execute UPDATE with projection */
1375  *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
1376  mtstate->mt_conflproj, planSlot,
1377  &mtstate->mt_epqstate, mtstate->ps.state,
1378  canSetTag);
1379 
1380  ReleaseBuffer(buffer);
1381  return true;
1382 }
#define NIL
Definition: pg_list.h:69
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:320
Relation ri_RelationDesc
Definition: execnodes.h:365
ExprState * ri_onConflictSetWhere
Definition: execnodes.h:416
int errhint(const char *fmt,...)
Definition: elog.c:987
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2088
CommandId es_output_cid
Definition: execnodes.h:449
ProjectionInfo * ri_onConflictSetProj
Definition: execnodes.h:413
static void test(void)
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:766
HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd)
Definition: heapam.c:4560
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
ExprContext * ps_ExprContext
Definition: execnodes.h:892
TupleTableSlot * mt_conflproj
Definition: execnodes.h:987
#define IsolationUsesXactSnapshot()
Definition: xact.h:43
int errcode(int sqlerrcode)
Definition: elog.c:575
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
EState * state
Definition: execnodes.h:860
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:363
TupleTableSlot * mt_existing
Definition: execnodes.h:985
HeapTupleHeader t_data
Definition: htup.h:67
LockTupleMode
Definition: heapam.h:38
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:969
ItemPointerData t_self
Definition: htup.h:65
static void ExecCheckHeapTupleVisible(EState *estate, HeapTuple tuple, Buffer buffer)
EPQState mt_epqstate
Definition: execnodes.h:980
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:209
HTSU_Result
Definition: snapshot.h:121
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:907
#define ereport(elevel, rest)
Definition: elog.h:122
List * ri_WithCheckOptions
Definition: execnodes.h:398
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:210
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2372
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:312
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:208
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define elog
Definition: elog.h:219
int Buffer
Definition: buf.h:23
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:326
#define ResetExprContext(econtext)
Definition: executor.h:468

◆ ExecProcessReturning()

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

Definition at line 150 of file nodeModifyTable.c.

References Assert, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, ExecMaterializeSlot(), ExecProject(), ProjectionInfo::pi_exprContext, RelationGetRelid, ResetExprContext, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, HeapTupleData::t_tableOid, and TupIsNull.

Referenced by ExecDelete(), ExecInsert(), ExecModifyTable(), and ExecUpdate().

153 {
154  ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
155  ExprContext *econtext = projectReturning->pi_exprContext;
156 
157  /*
158  * Reset per-tuple memory context to free any expression evaluation
159  * storage allocated in the previous cycle.
160  */
161  ResetExprContext(econtext);
162 
163  /* Make tuple and any needed join variables available to ExecProject */
164  if (tupleSlot)
165  econtext->ecxt_scantuple = tupleSlot;
166  else
167  {
168  HeapTuple tuple;
169 
170  /*
171  * RETURNING expressions might reference the tableoid column, so
172  * initialize t_tableOid before evaluating them.
173  */
174  Assert(!TupIsNull(econtext->ecxt_scantuple));
175  tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
176  tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
177  }
178  econtext->ecxt_outertuple = planSlot;
179 
180  /* Compute the RETURNING expressions */
181  return ExecProject(projectReturning);
182 }
Relation ri_RelationDesc
Definition: execnodes.h:365
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:410
#define TupIsNull(slot)
Definition: tuptable.h:138
Oid t_tableOid
Definition: htup.h:66
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:210
#define Assert(condition)
Definition: c.h:680
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:208
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:725
ExprContext * pi_exprContext
Definition: execnodes.h:309
#define RelationGetRelid(relation)
Definition: rel.h:425
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:326
#define ResetExprContext(econtext)
Definition: executor.h:468

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 2389 of file nodeModifyTable.c.

References elog, and ERROR.

Referenced by ExecReScan().

2390 {
2391  /*
2392  * Currently, we don't need to support rescan on ModifyTable nodes. The
2393  * semantics of that would be a bit debatable anyway.
2394  */
2395  elog(ERROR, "ExecReScanModifyTable is not implemented");
2396 }
#define ERROR
Definition: elog.h:43
#define elog
Definition: elog.h:219

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 1477 of file nodeModifyTable.c.

References CMD_INSERT, CMD_UPDATE, convert_tuples_by_name(), getASTriggerResultRelInfo(), gettext_noop, i, MakeTransitionCaptureState(), ModifyTableState::mt_nplans, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_onconflict, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, ModifyTableState::mt_transition_tupconv_maps, PartitionTupleRouting::num_partitions, ONCONFLICT_UPDATE, ModifyTableState::operation, palloc0(), PartitionTupleRouting::partitions, RelationGetDescr, RelationGetRelid, ModifyTableState::resultRelInfo, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, and TransitionCaptureState::tcs_map.

Referenced by ExecInitModifyTable().

1478 {
1479  ResultRelInfo *targetRelInfo = getASTriggerResultRelInfo(mtstate);
1480  int i;
1481 
1482  /* Check for transition tables on the directly targeted relation. */
1483  mtstate->mt_transition_capture =
1484  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1485  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1486  mtstate->operation);
1487  if (mtstate->operation == CMD_INSERT &&
1488  mtstate->mt_onconflict == ONCONFLICT_UPDATE)
1489  mtstate->mt_oc_transition_capture =
1490  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1491  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1492  CMD_UPDATE);
1493 
1494  /*
1495  * If we found that we need to collect transition tuples then we may also
1496  * need tuple conversion maps for any children that have TupleDescs that
1497  * aren't compatible with the tuplestores. (We can share these maps
1498  * between the regular and ON CONFLICT cases.)
1499  */
1500  if (mtstate->mt_transition_capture != NULL ||
1501  mtstate->mt_oc_transition_capture != NULL)
1502  {
1503  int numResultRelInfos;
1505 
1506  numResultRelInfos = (proute != NULL ?
1507  proute->num_partitions :
1508  mtstate->mt_nplans);
1509 
1510  /*
1511  * Build array of conversion maps from each child's TupleDesc to the
1512  * one used in the tuplestore. The map pointers may be NULL when no
1513  * conversion is necessary, which is hopefully a common case for
1514  * partitions.
1515  */
1517  palloc0(sizeof(TupleConversionMap *) * numResultRelInfos);
1518 
1519  /* Choose the right set of partitions */
1520  if (proute != NULL)
1521  {
1522  /*
1523  * For tuple routing among partitions, we need TupleDescs based on
1524  * the partition routing table.
1525  */
1526  ResultRelInfo **resultRelInfos = proute->partitions;
1527 
1528  for (i = 0; i < numResultRelInfos; ++i)
1529  {
1530  mtstate->mt_transition_tupconv_maps[i] =
1531  convert_tuples_by_name(RelationGetDescr(resultRelInfos[i]->ri_RelationDesc),
1532  RelationGetDescr(targetRelInfo->ri_RelationDesc),
1533  gettext_noop("could not convert row type"));
1534  }
1535  }
1536  else
1537  {
1538  /* Otherwise we need the ResultRelInfo for each subplan. */
1539  ResultRelInfo *resultRelInfos = mtstate->resultRelInfo;
1540 
1541  for (i = 0; i < numResultRelInfos; ++i)
1542  {
1543  mtstate->mt_transition_tupconv_maps[i] =
1544  convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc),
1545  RelationGetDescr(targetRelInfo->ri_RelationDesc),
1546  gettext_noop("could not convert row type"));
1547  }
1548  }
1549 
1550  /*
1551  * Install the conversion map for the first plan for UPDATE and DELETE
1552  * operations. It will be advanced each time we switch to the next
1553  * plan. (INSERT operations set it every time, so we need not update
1554  * mtstate->mt_oc_transition_capture here.)
1555  */
1556  if (mtstate->mt_transition_capture)
1557  mtstate->mt_transition_capture->tcs_map =
1558  mtstate->mt_transition_tupconv_maps[0];
1559  }
1560 }
static ResultRelInfo * getASTriggerResultRelInfo(ModifyTableState *node)
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:992
Relation ri_RelationDesc
Definition: execnodes.h:365
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:988
#define RelationGetDescr(relation)
Definition: rel.h:437
ResultRelInfo * resultRelInfo
Definition: execnodes.h:976
#define gettext_noop(x)
Definition: c.h:1005
CmdType operation
Definition: execnodes.h:970
ResultRelInfo ** partitions
Definition: execPartition.h:80
OnConflictAction mt_onconflict
Definition: execnodes.h:982
TupleConversionMap * tcs_map
Definition: trigger.h:73
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:990
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:377
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:210
void * palloc0(Size size)
Definition: mcxt.c:864
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4404
TupleConversionMap ** mt_transition_tupconv_maps
Definition: execnodes.h:994
int i
#define RelationGetRelid(relation)
Definition: rel.h:425

◆ ExecUpdate()

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

Definition at line 936 of file nodeModifyTable.c.

References HeapUpdateFailureData::cmax, CMD_INSERT, tupleDesc::constr, HeapUpdateFailureData::ctid, elog, ereport, errcode(), errhint(), errmsg(), ERROR, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, EvalPlanQual(), ExecARUpdateTriggers(), ExecBRUpdateTriggers(), ExecConstraints(), ExecFilterJunk(), FdwRoutine::ExecForeignUpdate, ExecInsertIndexTuples(), ExecIRUpdateTriggers(), ExecMaterializeSlot(), ExecProcessReturning(), ExecWithCheckOptions(), heap_update(), HeapTupleIsHeapOnly, HeapTupleMayBeUpdated, HeapTupleSelfUpdated, HeapTupleUpdated, IsBootstrapProcessingMode, IsolationUsesXactSnapshot, ItemPointerEquals(), list_free(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, NIL, ModifyTableState::operation, RelationData::rd_att, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_PartitionCheck, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, HeapTupleData::t_self, HeapTupleData::t_tableOid, TriggerDesc::trig_update_before_row, TriggerDesc::trig_update_instead_row, TupIsNull, WCO_RLS_UPDATE_CHECK, WCO_VIEW_CHECK, and HeapUpdateFailureData::xmax.

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

944 {
945  HeapTuple tuple;
946  ResultRelInfo *resultRelInfo;
947  Relation resultRelationDesc;
948  HTSU_Result result;
950  List *recheckIndexes = NIL;
951 
952  /*
953  * abort the operation if not running transactions
954  */
956  elog(ERROR, "cannot UPDATE during bootstrap");
957 
958  /*
959  * get the heap tuple out of the tuple table slot, making sure we have a
960  * writable copy
961  */
962  tuple = ExecMaterializeSlot(slot);
963 
964  /*
965  * get information on the (current) result relation
966  */
967  resultRelInfo = estate->es_result_relation_info;
968  resultRelationDesc = resultRelInfo->ri_RelationDesc;
969 
970  /* BEFORE ROW UPDATE Triggers */
971  if (resultRelInfo->ri_TrigDesc &&
972  resultRelInfo->ri_TrigDesc->trig_update_before_row)
973  {
974  slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
975  tupleid, oldtuple, slot);
976 
977  if (slot == NULL) /* "do nothing" */
978  return NULL;
979 
980  /* trigger might have changed tuple */
981  tuple = ExecMaterializeSlot(slot);
982  }
983 
984  /* INSTEAD OF ROW UPDATE Triggers */
985  if (resultRelInfo->ri_TrigDesc &&
986  resultRelInfo->ri_TrigDesc->trig_update_instead_row)
987  {
988  slot = ExecIRUpdateTriggers(estate, resultRelInfo,
989  oldtuple, slot);
990 
991  if (slot == NULL) /* "do nothing" */
992  return NULL;
993 
994  /* trigger might have changed tuple */
995  tuple = ExecMaterializeSlot(slot);
996  }
997  else if (resultRelInfo->ri_FdwRoutine)
998  {
999  /*
1000  * update in foreign table: let the FDW do it
1001  */
1002  slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
1003  resultRelInfo,
1004  slot,
1005  planSlot);
1006 
1007  if (slot == NULL) /* "do nothing" */
1008  return NULL;
1009 
1010  /* FDW might have changed tuple */
1011  tuple = ExecMaterializeSlot(slot);
1012 
1013  /*
1014  * AFTER ROW Triggers or RETURNING expressions might reference the
1015  * tableoid column, so initialize t_tableOid before evaluating them.
1016  */
1017  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
1018  }
1019  else
1020  {
1021  LockTupleMode lockmode;
1022 
1023  /*
1024  * Constraints might reference the tableoid column, so initialize
1025  * t_tableOid before evaluating them.
1026  */
1027  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
1028 
1029  /*
1030  * Check any RLS UPDATE WITH CHECK policies
1031  *
1032  * If we generate a new candidate tuple after EvalPlanQual testing, we
1033  * must loop back here and recheck any RLS policies and constraints.
1034  * (We don't need to redo triggers, however. If there are any BEFORE
1035  * triggers then trigger.c will have done heap_lock_tuple to lock the
1036  * correct tuple, so there's no need to do them again.)
1037  *
1038  * ExecWithCheckOptions() will skip any WCOs which are not of the kind
1039  * we are looking for at this point.
1040  */
1041 lreplace:;
1042  if (resultRelInfo->ri_WithCheckOptions != NIL)
1044  resultRelInfo, slot, estate);
1045 
1046  /*
1047  * Check the constraints of the tuple. Note that we pass the same
1048  * slot for the orig_slot argument, because unlike ExecInsert(), no
1049  * tuple-routing is performed here, hence the slot remains unchanged.
1050  */
1051  if (resultRelationDesc->rd_att->constr || resultRelInfo->ri_PartitionCheck)
1052  ExecConstraints(resultRelInfo, slot, estate, true);
1053 
1054  /*
1055  * replace the heap tuple
1056  *
1057  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
1058  * that the row to be updated is visible to that snapshot, and throw a
1059  * can't-serialize error if not. This is a special-case behavior
1060  * needed for referential integrity updates in transaction-snapshot
1061  * mode transactions.
1062  */
1063  result = heap_update(resultRelationDesc, tupleid, tuple,
1064  estate->es_output_cid,
1065  estate->es_crosscheck_snapshot,
1066  true /* wait for commit */ ,
1067  &hufd, &lockmode);
1068  switch (result)
1069  {
1070  case HeapTupleSelfUpdated:
1071 
1072  /*
1073  * The target tuple was already updated or deleted by the
1074  * current command, or by a later command in the current
1075  * transaction. The former case is possible in a join UPDATE
1076  * where multiple tuples join to the same target tuple. This
1077  * is pretty questionable, but Postgres has always allowed it:
1078  * we just execute the first update action and ignore
1079  * additional update attempts.
1080  *
1081  * The latter case arises if the tuple is modified by a
1082  * command in a BEFORE trigger, or perhaps by a command in a
1083  * volatile function used in the query. In such situations we
1084  * should not ignore the update, but it is equally unsafe to
1085  * proceed. We don't want to discard the original UPDATE
1086  * while keeping the triggered actions based on it; and we
1087  * have no principled way to merge this update with the
1088  * previous ones. So throwing an error is the only safe
1089  * course.
1090  *
1091  * If a trigger actually intends this type of interaction, it
1092  * can re-execute the UPDATE (assuming it can figure out how)
1093  * and then return NULL to cancel the outer update.
1094  */
1095  if (hufd.cmax != estate->es_output_cid)
1096  ereport(ERROR,
1097  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1098  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1099  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1100 
1101  /* Else, already updated by self; nothing to do */
1102  return NULL;
1103 
1104  case HeapTupleMayBeUpdated:
1105  break;
1106 
1107  case HeapTupleUpdated:
1109  ereport(ERROR,
1110  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1111  errmsg("could not serialize access due to concurrent update")));
1112  if (!ItemPointerEquals(tupleid, &hufd.ctid))
1113  {
1114  TupleTableSlot *epqslot;
1115 
1116  epqslot = EvalPlanQual(estate,
1117  epqstate,
1118  resultRelationDesc,
1119  resultRelInfo->ri_RangeTableIndex,
1120  lockmode,
1121  &hufd.ctid,
1122  hufd.xmax);
1123  if (!TupIsNull(epqslot))
1124  {
1125  *tupleid = hufd.ctid;
1126  slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1127  tuple = ExecMaterializeSlot(slot);
1128  goto lreplace;
1129  }
1130  }
1131  /* tuple already deleted; nothing to do */
1132  return NULL;
1133 
1134  default:
1135  elog(ERROR, "unrecognized heap_update status: %u", result);
1136  return NULL;
1137  }
1138 
1139  /*
1140  * Note: instead of having to update the old index tuples associated
1141  * with the heap tuple, all we do is form and insert new index tuples.
1142  * This is because UPDATEs are actually DELETEs and INSERTs, and index
1143  * tuple deletion is done later by VACUUM (see notes in ExecDelete).
1144  * All we do here is insert new index tuples. -cim 9/27/89
1145  */
1146 
1147  /*
1148  * insert index entries for tuple
1149  *
1150  * Note: heap_update returns the tid (location) of the new tuple in
1151  * the t_self field.
1152  *
1153  * If it's a HOT update, we mustn't insert new index entries.
1154  */
1155  if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
1156  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
1157  estate, false, NULL, NIL);
1158  }
1159 
1160  if (canSetTag)
1161  (estate->es_processed)++;
1162 
1163  /* AFTER ROW UPDATE Triggers */
1164  ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple,
1165  recheckIndexes,
1166  mtstate->operation == CMD_INSERT ?
1167  mtstate->mt_oc_transition_capture :
1168  mtstate->mt_transition_capture);
1169 
1170  list_free(recheckIndexes);
1171 
1172  /*
1173  * Check any WITH CHECK OPTION constraints from parent views. We are
1174  * required to do this after testing all constraints and uniqueness
1175  * violations per the SQL spec, so we do it after actually updating the
1176  * record in the heap and all indexes.
1177  *
1178  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1179  * are looking for at this point.
1180  */
1181  if (resultRelInfo->ri_WithCheckOptions != NIL)
1182  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1183 
1184  /* Process RETURNING if present */
1185  if (resultRelInfo->ri_projectReturning)
1186  return ExecProcessReturning(resultRelInfo, slot, planSlot);
1187 
1188  return NULL;
1189 }
int ri_NumIndices
Definition: execnodes.h:368
#define NIL
Definition: pg_list.h:69
JunkFilter * ri_junkFilter
Definition: execnodes.h:407
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:992
Relation ri_RelationDesc
Definition: execnodes.h:365
int errhint(const char *fmt,...)
Definition: elog.c:987
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2088
CommandId es_output_cid
Definition: execnodes.h:449
List * ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:271
TupleTableSlot * EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, int lockmode, ItemPointer tid, TransactionId priorXmax)
Definition: execMain.c:2493
TupleTableSlot * ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *slot)
Definition: trigger.c:2724
#define IsolationUsesXactSnapshot()
Definition: xact.h:43
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:441
int errcode(int sqlerrcode)
Definition: elog.c:575
CmdType operation
Definition: execnodes.h:970
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool check_partition_constraint)
Definition: execMain.c:1954
Index ri_RangeTableIndex
Definition: execnodes.h:362
TupleTableSlot * ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *slot)
Definition: trigger.c:2878
LockTupleMode
Definition: heapam.h:38
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:990
CommandId cmax
Definition: heapam.h:72
bool trig_update_before_row
Definition: reltrigger.h:60
HTSU_Result
Definition: snapshot.h:121
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:410
#define TupIsNull(slot)
Definition: tuptable.h:138
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:389
Oid t_tableOid
Definition: htup.h:66
#define ereport(elevel, rest)
Definition: elog.h:122
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:377
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, HeapTuple newtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2841
bool trig_update_instead_row
Definition: reltrigger.h:62
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
TransactionId xmax
Definition: heapam.h:71
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:262
List * ri_WithCheckOptions
Definition: execnodes.h:398
List * ri_PartitionCheck
Definition: execnodes.h:419
TupleDesc rd_att
Definition: rel.h:115
HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
Definition: heapam.c:3481
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:204
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:691
uint64 es_processed
Definition: execnodes.h:488
TupleConstr * constr
Definition: tupdesc.h:84
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:725
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:367
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
ItemPointerData ctid
Definition: heapam.h:70
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:454

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 1444 of file nodeModifyTable.c.

References CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, ExecASDeleteTriggers(), ExecASInsertTriggers(), ExecASUpdateTriggers(), getASTriggerResultRelInfo(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_onconflict, ModifyTableState::mt_transition_capture, ONCONFLICT_UPDATE, ModifyTableState::operation, ModifyTableState::ps, and PlanState::state.

Referenced by ExecModifyTable().

1445 {
1446  ResultRelInfo *resultRelInfo = getASTriggerResultRelInfo(node);
1447 
1448  switch (node->operation)
1449  {
1450  case CMD_INSERT:
1451  if (node->mt_onconflict == ONCONFLICT_UPDATE)
1453  resultRelInfo,
1454  node->mt_oc_transition_capture);
1455  ExecASInsertTriggers(node->ps.state, resultRelInfo,
1456  node->mt_transition_capture);
1457  break;
1458  case CMD_UPDATE:
1459  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
1460  node->mt_transition_capture);
1461  break;
1462  case CMD_DELETE:
1463  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
1464  node->mt_transition_capture);
1465  break;
1466  default:
1467  elog(ERROR, "unknown operation");
1468  break;
1469  }
1470 }
static ResultRelInfo * getASTriggerResultRelInfo(ModifyTableState *node)
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:992
CmdType operation
Definition: execnodes.h:970
EState * state
Definition: execnodes.h:860
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2492
OnConflictAction mt_onconflict
Definition: execnodes.h:982
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:969
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:990
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2711
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2277
#define elog
Definition: elog.h:219

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 1389 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

1390 {
1391  ResultRelInfo *resultRelInfo = node->resultRelInfo;
1392 
1393  /*
1394  * If the node modifies a partitioned table, we must fire its triggers.
1395  * Note that in that case, node->resultRelInfo points to the first leaf
1396  * partition, not the root table.
1397  */
1398  if (node->rootResultRelInfo != NULL)
1399  resultRelInfo = node->rootResultRelInfo;
1400 
1401  switch (node->operation)
1402  {
1403  case CMD_INSERT:
1404  ExecBSInsertTriggers(node->ps.state, resultRelInfo);
1405  if (node->mt_onconflict == ONCONFLICT_UPDATE)
1407  resultRelInfo);
1408  break;
1409  case CMD_UPDATE:
1410  ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
1411  break;
1412  case CMD_DELETE:
1413  ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
1414  break;
1415  default:
1416  elog(ERROR, "unknown operation");
1417  break;
1418  }
1419 }
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2435
ResultRelInfo * resultRelInfo
Definition: execnodes.h:976
CmdType operation
Definition: execnodes.h:970
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:977
EState * state
Definition: execnodes.h:860
OnConflictAction mt_onconflict
Definition: execnodes.h:982
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:969
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2220
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2651
#define elog
Definition: elog.h:219

◆ getASTriggerResultRelInfo()

static ResultRelInfo* getASTriggerResultRelInfo ( ModifyTableState node)
static

Definition at line 1427 of file nodeModifyTable.c.

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

Referenced by ExecSetupTransitionCaptureState(), and fireASTriggers().

1428 {
1429  /*
1430  * If the node modifies a partitioned table, we must fire its triggers.
1431  * Note that in that case, node->resultRelInfo points to the first leaf
1432  * partition, not the root table.
1433  */
1434  if (node->rootResultRelInfo != NULL)
1435  return node->rootResultRelInfo;
1436  else
1437  return node->resultRelInfo;
1438 }
ResultRelInfo * resultRelInfo
Definition: execnodes.h:976
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:977