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 "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 TupleTableSlotExecPrepareTupleRouting (ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot)
 
static ResultRelInfogetTargetResultRelInfo (ModifyTableState *node)
 
static void ExecSetupChildParentMapForTcs (ModifyTableState *mtstate)
 
static void ExecSetupChildParentMapForSubplan (ModifyTableState *mtstate)
 
static TupleConversionMaptupconv_map_for_subplan (ModifyTableState *node, int whichplan)
 
static void ExecCheckPlanOutput (Relation resultRel, List *targetList)
 
static TupleTableSlotExecProcessReturning (ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
 
static void ExecCheckHeapTupleVisible (EState *estate, HeapTuple tuple, Buffer buffer)
 
static void ExecCheckTIDVisible (EState *estate, ResultRelInfo *relinfo, ItemPointer tid)
 
static TupleTableSlotExecInsert (ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
 
static TupleTableSlotExecDelete (ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool *tupleDeleted, bool processReturning, bool canSetTag, bool changingPart)
 
static TupleTableSlotExecUpdate (ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
 
static void fireBSTriggers (ModifyTableState *node)
 
static void fireASTriggers (ModifyTableState *node)
 
static void ExecSetupTransitionCaptureState (ModifyTableState *mtstate, EState *estate)
 
static TupleTableSlotExecModifyTable (PlanState *pstate)
 
ModifyTableStateExecInitModifyTable (ModifyTable *node, EState *estate, int eflags)
 
void ExecEndModifyTable (ModifyTableState *node)
 
void ExecReScanModifyTable (ModifyTableState *node)
 

Function Documentation

◆ ExecCheckHeapTupleVisible()

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

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

205 {
207  return;
208 
209  /*
210  * We need buffer pin and lock to call HeapTupleSatisfiesVisibility.
211  * Caller should be holding pin, but not lock.
212  */
214  if (!HeapTupleSatisfiesVisibility(tuple, estate->es_snapshot, buffer))
215  {
216  /*
217  * We should not raise a serialization failure if the conflict is
218  * against a tuple inserted by our own transaction, even if it's not
219  * visible to our snapshot. (This would happen, for example, if
220  * conflicting keys are proposed for insertion in a single command.)
221  */
223  ereport(ERROR,
224  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
225  errmsg("could not serialize access due to concurrent update")));
226  }
228 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:765
#define IsolationUsesXactSnapshot()
Definition: xact.h:50
int errcode(int sqlerrcode)
Definition: elog.c:575
Snapshot es_snapshot
Definition: execnodes.h:478
#define HeapTupleSatisfiesVisibility(tuple, snapshot, buffer)
Definition: tqual.h:45
HeapTupleHeader t_data
Definition: htup.h:68
#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 89 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().

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

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

Referenced by ExecInsert().

237 {
238  Relation rel = relinfo->ri_RelationDesc;
239  Buffer buffer;
240  HeapTupleData tuple;
241 
242  /* Redundantly check isolation level */
244  return;
245 
246  tuple.t_self = *tid;
247  if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL))
248  elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
249  ExecCheckHeapTupleVisible(estate, &tuple, buffer);
250  ReleaseBuffer(buffer);
251 }
Relation ri_RelationDesc
Definition: execnodes.h:397
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation)
Definition: heapam.c:1903
#define IsolationUsesXactSnapshot()
Definition: xact.h:50
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 tupleDeleted,
bool  processReturning,
bool  canSetTag,
bool  changingPart 
)
static

Definition at line 624 of file nodeModifyTable.c.

References Assert, BufferIsValid, HeapUpdateFailureData::cmax, CMD_UPDATE, 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(), ExecARUpdateTriggers(), ExecBRDeleteTriggers(), ExecClearTuple(), FdwRoutine::ExecForeignDelete, ExecIRDeleteTriggers(), ExecMaterializeSlot(), ExecProcessReturning(), ExecSetSlotDescriptor(), ExecStoreAllNullTuple(), ExecStoreTuple(), heap_delete(), heap_fetch(), HeapTupleMayBeUpdated, HeapTupleSelfUpdated, HeapTupleUpdated, InvalidBuffer, IsolationUsesXactSnapshot, ItemPointerEquals(), ItemPointerIndicatesMovedPartitions, LockTupleExclusive, ModifyTableState::mt_transition_capture, ModifyTableState::operation, 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, TransitionCaptureState::tcs_update_old_table, TriggerDesc::trig_delete_before_row, TriggerDesc::trig_delete_instead_row, TupleTableSlot::tts_isempty, TupleTableSlot::tts_tupleDescriptor, TupIsNull, and HeapUpdateFailureData::xmax.

Referenced by ExecModifyTable(), and ExecUpdate().

634 {
635  ResultRelInfo *resultRelInfo;
636  Relation resultRelationDesc;
637  HTSU_Result result;
639  TupleTableSlot *slot = NULL;
640  TransitionCaptureState *ar_delete_trig_tcs;
641 
642  if (tupleDeleted)
643  *tupleDeleted = false;
644 
645  /*
646  * get information on the (current) result relation
647  */
648  resultRelInfo = estate->es_result_relation_info;
649  resultRelationDesc = resultRelInfo->ri_RelationDesc;
650 
651  /* BEFORE ROW DELETE Triggers */
652  if (resultRelInfo->ri_TrigDesc &&
653  resultRelInfo->ri_TrigDesc->trig_delete_before_row)
654  {
655  bool dodelete;
656 
657  dodelete = ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
658  tupleid, oldtuple);
659 
660  if (!dodelete) /* "do nothing" */
661  return NULL;
662  }
663 
664  /* INSTEAD OF ROW DELETE Triggers */
665  if (resultRelInfo->ri_TrigDesc &&
666  resultRelInfo->ri_TrigDesc->trig_delete_instead_row)
667  {
668  bool dodelete;
669 
670  Assert(oldtuple != NULL);
671  dodelete = ExecIRDeleteTriggers(estate, resultRelInfo, oldtuple);
672 
673  if (!dodelete) /* "do nothing" */
674  return NULL;
675  }
676  else if (resultRelInfo->ri_FdwRoutine)
677  {
678  HeapTuple tuple;
679 
680  /*
681  * delete from foreign table: let the FDW do it
682  *
683  * We offer the trigger tuple slot as a place to store RETURNING data,
684  * although the FDW can return some other slot if it wants. Set up
685  * the slot's tupdesc so the FDW doesn't need to do that for itself.
686  */
687  slot = estate->es_trig_tuple_slot;
688  if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
689  ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
690 
691  slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
692  resultRelInfo,
693  slot,
694  planSlot);
695 
696  if (slot == NULL) /* "do nothing" */
697  return NULL;
698 
699  /*
700  * RETURNING expressions might reference the tableoid column, so
701  * initialize t_tableOid before evaluating them.
702  */
703  if (slot->tts_isempty)
704  ExecStoreAllNullTuple(slot);
705  tuple = ExecMaterializeSlot(slot);
706  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
707  }
708  else
709  {
710  /*
711  * delete the tuple
712  *
713  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
714  * that the row to be deleted is visible to that snapshot, and throw a
715  * can't-serialize error if not. This is a special-case behavior
716  * needed for referential integrity updates in transaction-snapshot
717  * mode transactions.
718  */
719 ldelete:;
720  result = heap_delete(resultRelationDesc, tupleid,
721  estate->es_output_cid,
722  estate->es_crosscheck_snapshot,
723  true /* wait for commit */ ,
724  &hufd,
725  changingPart);
726  switch (result)
727  {
729 
730  /*
731  * The target tuple was already updated or deleted by the
732  * current command, or by a later command in the current
733  * transaction. The former case is possible in a join DELETE
734  * where multiple tuples join to the same target tuple. This
735  * is somewhat questionable, but Postgres has always allowed
736  * it: we just ignore additional deletion attempts.
737  *
738  * The latter case arises if the tuple is modified by a
739  * command in a BEFORE trigger, or perhaps by a command in a
740  * volatile function used in the query. In such situations we
741  * should not ignore the deletion, but it is equally unsafe to
742  * proceed. We don't want to discard the original DELETE
743  * while keeping the triggered actions based on its deletion;
744  * and it would be no better to allow the original DELETE
745  * while discarding updates that it triggered. The row update
746  * carries some information that might be important according
747  * to business rules; so throwing an error is the only safe
748  * course.
749  *
750  * If a trigger actually intends this type of interaction, it
751  * can re-execute the DELETE and then return NULL to cancel
752  * the outer delete.
753  */
754  if (hufd.cmax != estate->es_output_cid)
755  ereport(ERROR,
756  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
757  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
758  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
759 
760  /* Else, already deleted by self; nothing to do */
761  return NULL;
762 
764  break;
765 
766  case HeapTupleUpdated:
768  ereport(ERROR,
769  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
770  errmsg("could not serialize access due to concurrent update")));
772  ereport(ERROR,
773  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
774  errmsg("tuple to be deleted was already moved to another partition due to concurrent update")));
775 
776  if (!ItemPointerEquals(tupleid, &hufd.ctid))
777  {
778  TupleTableSlot *epqslot;
779 
780  epqslot = EvalPlanQual(estate,
781  epqstate,
782  resultRelationDesc,
783  resultRelInfo->ri_RangeTableIndex,
785  &hufd.ctid,
786  hufd.xmax);
787  if (!TupIsNull(epqslot))
788  {
789  *tupleid = hufd.ctid;
790  goto ldelete;
791  }
792  }
793  /* tuple already deleted; nothing to do */
794  return NULL;
795 
796  default:
797  elog(ERROR, "unrecognized heap_delete status: %u", result);
798  return NULL;
799  }
800 
801  /*
802  * Note: Normally one would think that we have to delete index tuples
803  * associated with the heap tuple now...
804  *
805  * ... but in POSTGRES, we have no need to do this because VACUUM will
806  * take care of it later. We can't delete index tuples immediately
807  * anyway, since the tuple is still visible to other transactions.
808  */
809  }
810 
811  if (canSetTag)
812  (estate->es_processed)++;
813 
814  /* Tell caller that the delete actually happened. */
815  if (tupleDeleted)
816  *tupleDeleted = true;
817 
818  /*
819  * If this delete is the result of a partition key update that moved the
820  * tuple to a new partition, put this row into the transition OLD TABLE,
821  * if there is one. We need to do this separately for DELETE and INSERT
822  * because they happen on different tables.
823  */
824  ar_delete_trig_tcs = mtstate->mt_transition_capture;
825  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
827  {
828  ExecARUpdateTriggers(estate, resultRelInfo,
829  tupleid,
830  oldtuple,
831  NULL,
832  NULL,
833  mtstate->mt_transition_capture);
834 
835  /*
836  * We've already captured the NEW TABLE row, so make sure any AR
837  * DELETE trigger fired below doesn't capture it again.
838  */
839  ar_delete_trig_tcs = NULL;
840  }
841 
842  /* AFTER ROW DELETE Triggers */
843  ExecARDeleteTriggers(estate, resultRelInfo, tupleid, oldtuple,
844  ar_delete_trig_tcs);
845 
846  /* Process RETURNING if present and if requested */
847  if (processReturning && resultRelInfo->ri_projectReturning)
848  {
849  /*
850  * We have to put the target tuple into a slot, which means first we
851  * gotta fetch it. We can use the trigger tuple slot.
852  */
853  TupleTableSlot *rslot;
854  HeapTupleData deltuple;
855  Buffer delbuffer;
856 
857  if (resultRelInfo->ri_FdwRoutine)
858  {
859  /* FDW must have provided a slot containing the deleted row */
860  Assert(!TupIsNull(slot));
861  delbuffer = InvalidBuffer;
862  }
863  else
864  {
865  slot = estate->es_trig_tuple_slot;
866  if (oldtuple != NULL)
867  {
868  deltuple = *oldtuple;
869  delbuffer = InvalidBuffer;
870  }
871  else
872  {
873  deltuple.t_self = *tupleid;
874  if (!heap_fetch(resultRelationDesc, SnapshotAny,
875  &deltuple, &delbuffer, false, NULL))
876  elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
877  }
878 
879  if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
880  ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
881  ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
882  }
883 
884  rslot = ExecProcessReturning(resultRelInfo, slot, planSlot);
885 
886  /*
887  * Before releasing the target tuple again, make sure rslot has a
888  * local copy of any pass-by-reference values.
889  */
890  ExecMaterializeSlot(rslot);
891 
892  ExecClearTuple(slot);
893  if (BufferIsValid(delbuffer))
894  ReleaseBuffer(delbuffer);
895 
896  return rslot;
897  }
898 
899  return NULL;
900 }
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:212
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:356
Relation ri_RelationDesc
Definition: execnodes.h:397
bool tts_isempty
Definition: tuptable.h:116
bool ExecIRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple)
Definition: trigger.c:2831
int errhint(const char *fmt,...)
Definition: elog.c:987
CommandId es_output_cid
Definition: execnodes.h:487
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:548
#define RelationGetDescr(relation)
Definition: rel.h:433
bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation)
Definition: heapam.c:1903
HTSU_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, HeapUpdateFailureData *hufd, bool changingPart)
Definition: heapam.c:3060
TupleTableSlot * EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, int lockmode, ItemPointer tid, TransactionId priorXmax)
Definition: execMain.c:2501
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
#define IsolationUsesXactSnapshot()
Definition: xact.h:50
#define InvalidBuffer
Definition: buf.h:25
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:479
int errcode(int sqlerrcode)
Definition: elog.c:575
CmdType operation
Definition: execnodes.h:1037
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
Index ri_RangeTableIndex
Definition: execnodes.h:394
void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TransitionCaptureState *transition_capture)
Definition: trigger.c:2799
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1057
CommandId cmax
Definition: heapam.h:72
HTSU_Result
Definition: snapshot.h:121
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:445
#define TupIsNull(slot)
Definition: tuptable.h:146
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:421
Oid t_tableOid
Definition: htup.h:66
TupleTableSlot * es_trig_tuple_slot
Definition: execnodes.h:512
#define ereport(elevel, rest)
Definition: elog.h:122
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, HeapTuple newtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:3068
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
TransactionId xmax
Definition: heapam.h:71
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
bool trig_delete_instead_row
Definition: reltrigger.h:67
void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc)
Definition: execTuples.c:281
#define SnapshotAny
Definition: tqual.h:28
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
bool ExecBRDeleteTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple)
Definition: trigger.c:2730
#define Assert(condition)
Definition: c.h:699
uint64 es_processed
Definition: execnodes.h:529
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:781
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:407
bool trig_delete_before_row
Definition: reltrigger.h:65
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:492

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

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

2660 {
2661  int i;
2662 
2663  /*
2664  * Allow any FDWs to shut down
2665  */
2666  for (i = 0; i < node->mt_nplans; i++)
2667  {
2668  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
2669 
2670  if (!resultRelInfo->ri_usesFdwDirectModify &&
2671  resultRelInfo->ri_FdwRoutine != NULL &&
2672  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
2673  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
2674  resultRelInfo);
2675  }
2676 
2677  /* Close all the partitioned tables, leaf partitions, and their indices */
2678  if (node->mt_partition_tuple_routing)
2680 
2681  /*
2682  * Free the exprcontext
2683  */
2684  ExecFreeExprContext(&node->ps);
2685 
2686  /*
2687  * clean out the tuple table
2688  */
2690 
2691  /*
2692  * Terminate EPQ execution if active
2693  */
2694  EvalPlanQualEnd(&node->mt_epqstate);
2695 
2696  /*
2697  * shut down subplans
2698  */
2699  for (i = 0; i < node->mt_nplans; i++)
2700  ExecEndNode(node->mt_plans[i]);
2701 }
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1054
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:538
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1043
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
EState * state
Definition: execnodes.h:914
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:566
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:3253
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:946
PlanState ps
Definition: execnodes.h:1036
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
bool ri_usesFdwDirectModify
Definition: execnodes.h:427
EPQState mt_epqstate
Definition: execnodes.h:1047
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:421
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:213
PlanState ** mt_plans
Definition: execnodes.h:1040
int i

◆ ExecInitModifyTable()

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

Definition at line 2185 of file nodeModifyTable.c.

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

Referenced by ExecInitNode().

2186 {
2187  ModifyTableState *mtstate;
2188  CmdType operation = node->operation;
2189  int nplans = list_length(node->plans);
2190  ResultRelInfo *saved_resultRelInfo;
2191  ResultRelInfo *resultRelInfo;
2192  Plan *subplan;
2193  ListCell *l;
2194  int i;
2195  Relation rel;
2196  bool update_tuple_routing_needed = node->partColsUpdated;
2197 
2198  /* check for unsupported flags */
2199  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2200 
2201  /*
2202  * create state structure
2203  */
2204  mtstate = makeNode(ModifyTableState);
2205  mtstate->ps.plan = (Plan *) node;
2206  mtstate->ps.state = estate;
2207  mtstate->ps.ExecProcNode = ExecModifyTable;
2208 
2209  mtstate->operation = operation;
2210  mtstate->canSetTag = node->canSetTag;
2211  mtstate->mt_done = false;
2212 
2213  mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * nplans);
2214  mtstate->resultRelInfo = estate->es_result_relations + node->resultRelIndex;
2215 
2216  /* If modifying a partitioned table, initialize the root table info */
2217  if (node->rootResultRelIndex >= 0)
2218  mtstate->rootResultRelInfo = estate->es_root_result_relations +
2219  node->rootResultRelIndex;
2220 
2221  mtstate->mt_arowmarks = (List **) palloc0(sizeof(List *) * nplans);
2222  mtstate->mt_nplans = nplans;
2223 
2224  /* set up epqstate with dummy subplan data for the moment */
2225  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
2226  mtstate->fireBSTriggers = true;
2227 
2228  /*
2229  * call ExecInitNode on each of the plans to be executed and save the
2230  * results into the array "mt_plans". This is also a convenient place to
2231  * verify that the proposed target relations are valid and open their
2232  * indexes for insertion of new index entries. Note we *must* set
2233  * estate->es_result_relation_info correctly while we initialize each
2234  * sub-plan; ExecContextForcesOids depends on that!
2235  */
2236  saved_resultRelInfo = estate->es_result_relation_info;
2237 
2238  resultRelInfo = mtstate->resultRelInfo;
2239  i = 0;
2240  foreach(l, node->plans)
2241  {
2242  subplan = (Plan *) lfirst(l);
2243 
2244  /* Initialize the usesFdwDirectModify flag */
2245  resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
2246  node->fdwDirectModifyPlans);
2247 
2248  /*
2249  * Verify result relation is a valid target for the current operation
2250  */
2251  CheckValidResultRel(resultRelInfo, operation);
2252 
2253  /*
2254  * If there are indices on the result relation, open them and save
2255  * descriptors in the result relation info, so that we can add new
2256  * index entries for the tuples we add/update. We need not do this
2257  * for a DELETE, however, since deletion doesn't affect indexes. Also,
2258  * inside an EvalPlanQual operation, the indexes might be open
2259  * already, since we share the resultrel state with the original
2260  * query.
2261  */
2262  if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
2263  operation != CMD_DELETE &&
2264  resultRelInfo->ri_IndexRelationDescs == NULL)
2265  ExecOpenIndices(resultRelInfo,
2267 
2268  /*
2269  * If this is an UPDATE and a BEFORE UPDATE trigger is present, the
2270  * trigger itself might modify the partition-key values. So arrange
2271  * for tuple routing.
2272  */
2273  if (resultRelInfo->ri_TrigDesc &&
2274  resultRelInfo->ri_TrigDesc->trig_update_before_row &&
2275  operation == CMD_UPDATE)
2276  update_tuple_routing_needed = true;
2277 
2278  /* Now init the plan for this result rel */
2279  estate->es_result_relation_info = resultRelInfo;
2280  mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
2281 
2282  /* Also let FDWs init themselves for foreign-table result rels */
2283  if (!resultRelInfo->ri_usesFdwDirectModify &&
2284  resultRelInfo->ri_FdwRoutine != NULL &&
2285  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
2286  {
2287  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
2288 
2289  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
2290  resultRelInfo,
2291  fdw_private,
2292  i,
2293  eflags);
2294  }
2295 
2296  resultRelInfo++;
2297  i++;
2298  }
2299 
2300  estate->es_result_relation_info = saved_resultRelInfo;
2301 
2302  /* Get the target relation */
2303  rel = (getTargetResultRelInfo(mtstate))->ri_RelationDesc;
2304 
2305  /*
2306  * If it's not a partitioned table after all, UPDATE tuple routing should
2307  * not be attempted.
2308  */
2309  if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2310  update_tuple_routing_needed = false;
2311 
2312  /*
2313  * Build state for tuple routing if it's an INSERT or if it's an UPDATE of
2314  * partition key.
2315  */
2316  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2317  (operation == CMD_INSERT || update_tuple_routing_needed))
2318  mtstate->mt_partition_tuple_routing =
2319  ExecSetupPartitionTupleRouting(mtstate, rel);
2320 
2321  /*
2322  * Build state for collecting transition tuples. This requires having a
2323  * valid trigger query context, so skip it in explain-only mode.
2324  */
2325  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
2326  ExecSetupTransitionCaptureState(mtstate, estate);
2327 
2328  /*
2329  * Construct mapping from each of the per-subplan partition attnos to the
2330  * root attno. This is required when during update row movement the tuple
2331  * descriptor of a source partition does not match the root partitioned
2332  * table descriptor. In such a case we need to convert tuples to the root
2333  * tuple descriptor, because the search for destination partition starts
2334  * from the root. Skip this setup if it's not a partition key update.
2335  */
2336  if (update_tuple_routing_needed)
2338 
2339  /*
2340  * Initialize any WITH CHECK OPTION constraints if needed.
2341  */
2342  resultRelInfo = mtstate->resultRelInfo;
2343  i = 0;
2344  foreach(l, node->withCheckOptionLists)
2345  {
2346  List *wcoList = (List *) lfirst(l);
2347  List *wcoExprs = NIL;
2348  ListCell *ll;
2349 
2350  foreach(ll, wcoList)
2351  {
2352  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
2353  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
2354  mtstate->mt_plans[i]);
2355 
2356  wcoExprs = lappend(wcoExprs, wcoExpr);
2357  }
2358 
2359  resultRelInfo->ri_WithCheckOptions = wcoList;
2360  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
2361  resultRelInfo++;
2362  i++;
2363  }
2364 
2365  /*
2366  * Initialize RETURNING projections if needed.
2367  */
2368  if (node->returningLists)
2369  {
2370  TupleTableSlot *slot;
2371  ExprContext *econtext;
2372 
2373  /*
2374  * Initialize result tuple slot and assign its rowtype using the first
2375  * RETURNING list. We assume the rest will look the same.
2376  */
2377  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
2378 
2379  /* Set up a slot for the output of the RETURNING projection(s) */
2380  ExecInitResultTupleSlotTL(estate, &mtstate->ps);
2381  slot = mtstate->ps.ps_ResultTupleSlot;
2382 
2383  /* Need an econtext too */
2384  if (mtstate->ps.ps_ExprContext == NULL)
2385  ExecAssignExprContext(estate, &mtstate->ps);
2386  econtext = mtstate->ps.ps_ExprContext;
2387 
2388  /*
2389  * Build a projection for each result rel.
2390  */
2391  resultRelInfo = mtstate->resultRelInfo;
2392  foreach(l, node->returningLists)
2393  {
2394  List *rlist = (List *) lfirst(l);
2395 
2396  resultRelInfo->ri_returningList = rlist;
2397  resultRelInfo->ri_projectReturning =
2398  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
2399  resultRelInfo->ri_RelationDesc->rd_att);
2400  resultRelInfo++;
2401  }
2402  }
2403  else
2404  {
2405  /*
2406  * We still must construct a dummy result tuple type, because InitPlan
2407  * expects one (maybe should change that?).
2408  */
2409  mtstate->ps.plan->targetlist = NIL;
2410  ExecInitResultTupleSlotTL(estate, &mtstate->ps);
2411 
2412  mtstate->ps.ps_ExprContext = NULL;
2413  }
2414 
2415  /* Set the list of arbiter indexes if needed for ON CONFLICT */
2416  resultRelInfo = mtstate->resultRelInfo;
2417  if (node->onConflictAction != ONCONFLICT_NONE)
2418  resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
2419 
2420  /*
2421  * If needed, Initialize target list, projection and qual for ON CONFLICT
2422  * DO UPDATE.
2423  */
2424  if (node->onConflictAction == ONCONFLICT_UPDATE)
2425  {
2426  ExprContext *econtext;
2427  TupleDesc relationDesc;
2428  TupleDesc tupDesc;
2429 
2430  /* insert may only have one plan, inheritance is not expanded */
2431  Assert(nplans == 1);
2432 
2433  /* already exists if created by RETURNING processing above */
2434  if (mtstate->ps.ps_ExprContext == NULL)
2435  ExecAssignExprContext(estate, &mtstate->ps);
2436 
2437  econtext = mtstate->ps.ps_ExprContext;
2438  relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
2439 
2440  /*
2441  * Initialize slot for the existing tuple. If we'll be performing
2442  * tuple routing, the tuple descriptor to use for this will be
2443  * determined based on which relation the update is actually applied
2444  * to, so we don't set its tuple descriptor here.
2445  */
2446  mtstate->mt_existing =
2447  ExecInitExtraTupleSlot(mtstate->ps.state,
2448  mtstate->mt_partition_tuple_routing ?
2449  NULL : relationDesc);
2450 
2451  /* carried forward solely for the benefit of explain */
2452  mtstate->mt_excludedtlist = node->exclRelTlist;
2453 
2454  /* create state for DO UPDATE SET operation */
2455  resultRelInfo->ri_onConflict = makeNode(OnConflictSetState);
2456 
2457  /*
2458  * Create the tuple slot for the UPDATE SET projection.
2459  *
2460  * Just like mt_existing above, we leave it without a tuple descriptor
2461  * in the case of partitioning tuple routing, so that it can be
2462  * changed by ExecPrepareTupleRouting. In that case, we still save
2463  * the tupdesc in the parent's state: it can be reused by partitions
2464  * with an identical descriptor to the parent.
2465  */
2466  tupDesc = ExecTypeFromTL((List *) node->onConflictSet,
2467  relationDesc->tdhasoid);
2468  mtstate->mt_conflproj =
2469  ExecInitExtraTupleSlot(mtstate->ps.state,
2470  mtstate->mt_partition_tuple_routing ?
2471  NULL : tupDesc);
2472  resultRelInfo->ri_onConflict->oc_ProjTupdesc = tupDesc;
2473 
2474  /* build UPDATE SET projection state */
2475  resultRelInfo->ri_onConflict->oc_ProjInfo =
2476  ExecBuildProjectionInfo(node->onConflictSet, econtext,
2477  mtstate->mt_conflproj, &mtstate->ps,
2478  relationDesc);
2479 
2480  /* initialize state to evaluate the WHERE clause, if any */
2481  if (node->onConflictWhere)
2482  {
2483  ExprState *qualexpr;
2484 
2485  qualexpr = ExecInitQual((List *) node->onConflictWhere,
2486  &mtstate->ps);
2487  resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
2488  }
2489  }
2490 
2491  /*
2492  * If we have any secondary relations in an UPDATE or DELETE, they need to
2493  * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
2494  * EvalPlanQual mechanism needs to be told about them. Locate the
2495  * relevant ExecRowMarks.
2496  */
2497  foreach(l, node->rowMarks)
2498  {
2500  ExecRowMark *erm;
2501 
2502  /* ignore "parent" rowmarks; they are irrelevant at runtime */
2503  if (rc->isParent)
2504  continue;
2505 
2506  /* find ExecRowMark (same for all subplans) */
2507  erm = ExecFindRowMark(estate, rc->rti, false);
2508 
2509  /* build ExecAuxRowMark for each subplan */
2510  for (i = 0; i < nplans; i++)
2511  {
2512  ExecAuxRowMark *aerm;
2513 
2514  subplan = mtstate->mt_plans[i]->plan;
2515  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
2516  mtstate->mt_arowmarks[i] = lappend(mtstate->mt_arowmarks[i], aerm);
2517  }
2518  }
2519 
2520  /* select first subplan */
2521  mtstate->mt_whichplan = 0;
2522  subplan = (Plan *) linitial(node->plans);
2523  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan,
2524  mtstate->mt_arowmarks[0]);
2525 
2526  /*
2527  * Initialize the junk filter(s) if needed. INSERT queries need a filter
2528  * if there are any junk attrs in the tlist. UPDATE and DELETE always
2529  * need a filter, since there's always at least one junk attribute present
2530  * --- no need to look first. Typically, this will be a 'ctid' or
2531  * 'wholerow' attribute, but in the case of a foreign data wrapper it
2532  * might be a set of junk attributes sufficient to identify the remote
2533  * row.
2534  *
2535  * If there are multiple result relations, each one needs its own junk
2536  * filter. Note multiple rels are only possible for UPDATE/DELETE, so we
2537  * can't be fooled by some needing a filter and some not.
2538  *
2539  * This section of code is also a convenient place to verify that the
2540  * output of an INSERT or UPDATE matches the target table(s).
2541  */
2542  {
2543  bool junk_filter_needed = false;
2544 
2545  switch (operation)
2546  {
2547  case CMD_INSERT:
2548  foreach(l, subplan->targetlist)
2549  {
2550  TargetEntry *tle = (TargetEntry *) lfirst(l);
2551 
2552  if (tle->resjunk)
2553  {
2554  junk_filter_needed = true;
2555  break;
2556  }
2557  }
2558  break;
2559  case CMD_UPDATE:
2560  case CMD_DELETE:
2561  junk_filter_needed = true;
2562  break;
2563  default:
2564  elog(ERROR, "unknown operation");
2565  break;
2566  }
2567 
2568  if (junk_filter_needed)
2569  {
2570  resultRelInfo = mtstate->resultRelInfo;
2571  for (i = 0; i < nplans; i++)
2572  {
2573  JunkFilter *j;
2574 
2575  subplan = mtstate->mt_plans[i]->plan;
2576  if (operation == CMD_INSERT || operation == CMD_UPDATE)
2577  ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
2578  subplan->targetlist);
2579 
2580  j = ExecInitJunkFilter(subplan->targetlist,
2581  resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
2582  ExecInitExtraTupleSlot(estate, NULL));
2583 
2584  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2585  {
2586  /* For UPDATE/DELETE, find the appropriate junk attr now */
2587  char relkind;
2588 
2589  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2590  if (relkind == RELKIND_RELATION ||
2591  relkind == RELKIND_MATVIEW ||
2592  relkind == RELKIND_PARTITIONED_TABLE)
2593  {
2594  j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
2596  elog(ERROR, "could not find junk ctid column");
2597  }
2598  else if (relkind == RELKIND_FOREIGN_TABLE)
2599  {
2600  /*
2601  * When there is a row-level trigger, there should be
2602  * a wholerow attribute.
2603  */
2604  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2605  }
2606  else
2607  {
2608  j->jf_junkAttNo = ExecFindJunkAttribute(j, "wholerow");
2610  elog(ERROR, "could not find junk wholerow column");
2611  }
2612  }
2613 
2614  resultRelInfo->ri_junkFilter = j;
2615  resultRelInfo++;
2616  }
2617  }
2618  else
2619  {
2620  if (operation == CMD_INSERT)
2622  subplan->targetlist);
2623  }
2624  }
2625 
2626  /*
2627  * Set up a tuple table slot for use for trigger output tuples. In a plan
2628  * containing multiple ModifyTable nodes, all can share one such slot, so
2629  * we keep it in the estate.
2630  */
2631  if (estate->es_trig_tuple_slot == NULL)
2632  estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL);
2633 
2634  /*
2635  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
2636  * to estate->es_auxmodifytables so that it will be run to completion by
2637  * ExecPostprocessPlan. (It'd actually work fine to add the primary
2638  * ModifyTable node too, but there's no need.) Note the use of lcons not
2639  * lappend: we need later-initialized ModifyTable nodes to be shut down
2640  * before earlier ones. This ensures that we don't throw away RETURNING
2641  * rows that need to be seen by a later CTE subplan.
2642  */
2643  if (!mtstate->canSetTag)
2644  estate->es_auxmodifytables = lcons(mtstate,
2645  estate->es_auxmodifytables);
2646 
2647  return mtstate;
2648 }
AttrNumber jf_junkAttNo
Definition: execnodes.h:365
#define NIL
Definition: pg_list.h:69
JunkFilter * ri_junkFilter
Definition: execnodes.h:439
List * arbiterIndexes
Definition: plannodes.h:236
Relation ri_RelationDesc
Definition: execnodes.h:397
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:232
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
bool tdhasoid
Definition: tupdesc.h:85
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1054
List * withCheckOptionLists
Definition: plannodes.h:229
int resultRelIndex
Definition: plannodes.h:226
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1043
AttrNumber ExecFindJunkAttribute(JunkFilter *junkfilter, const char *attrName)
Definition: execJunk.c:209
ExprContext * ps_ExprContext
Definition: execnodes.h:947
TupleTableSlot * mt_conflproj
Definition: execnodes.h:1051
bool partColsUpdated
Definition: plannodes.h:224
bool canSetTag
Definition: plannodes.h:220
CmdType operation
Definition: execnodes.h:1037
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1044
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2849
EState * state
Definition: execnodes.h:914
Form_pg_class rd_rel
Definition: rel.h:84
List * plans
Definition: plannodes.h:228
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc)
Definition: execTuples.c:931
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:205
char relkind
Definition: pg_class.h:51
List * onConflictSet
Definition: plannodes.h:237
int rootResultRelIndex
Definition: plannodes.h:227
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
Definition: execIndexing.c:149
TupleTableSlot * mt_existing
Definition: execnodes.h:1049
PartitionTupleRouting * ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel)
Definition: execPartition.c:76
List * ri_WithCheckOptionExprs
Definition: execnodes.h:433
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:946
static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
List * rowMarks
Definition: plannodes.h:233
bool resjunk
Definition: primnodes.h:1383
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1036
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:377
bool ri_usesFdwDirectModify
Definition: execnodes.h:427
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:1104
#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
ResultRelInfo * es_result_relations
Definition: execnodes.h:490
JunkFilter * ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
Definition: execJunk.c:61
List * fdwPrivLists
Definition: plannodes.h:231
EPQState mt_epqstate
Definition: execnodes.h:1047
bool trig_update_before_row
Definition: reltrigger.h:60
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:445
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:421
ExprState * oc_WhereClause
Definition: execnodes.h:379
TupleTableSlot * es_trig_tuple_slot
Definition: execnodes.h:512
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
TupleDesc ExecTypeFromTL(List *targetList, bool hasoid)
Definition: execTuples.c:965
List * lappend(List *list, void *datum)
Definition: list.c:128
PlanState ** mt_plans
Definition: execnodes.h:1040
OnConflictSetState * ri_onConflict
Definition: execnodes.h:451
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
void * palloc0(Size size)
Definition: mcxt.c:955
List * es_auxmodifytables
Definition: execnodes.h:540
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:918
List * ri_WithCheckOptions
Definition: execnodes.h:430
void ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
Definition: execTuples.c:890
TupleDesc rd_att
Definition: rel.h:85
void EvalPlanQualInit(EPQState *epqstate, EState *estate, Plan *subplan, List *auxrowmarks, int epqParam)
Definition: execMain.c:2830
Plan * plan
Definition: execnodes.h:912
List * lcons(void *datum, List *list)
Definition: list.c:259
#define makeNode(_type_)
Definition: nodes.h:565
static void ExecCheckPlanOutput(Relation resultRel, List *targetList)
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
#define EXEC_FLAG_MARK
Definition: executor.h:61
OnConflictAction onConflictAction
Definition: plannodes.h:235
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:428
static int list_length(const List *l)
Definition: pg_list.h:89
List * targetlist
Definition: plannodes.h:146
List * mt_excludedtlist
Definition: execnodes.h:1050
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:349
CmdType operation
Definition: plannodes.h:219
ResultRelInfo * es_root_result_relations
Definition: execnodes.h:501
int i
List * returningLists
Definition: plannodes.h:230
bool isParent
Definition: plannodes.h:1045
#define elog
Definition: elog.h:219
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:209
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:486
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:58
TupleDesc oc_ProjTupdesc
Definition: execnodes.h:378
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:448
CmdType
Definition: nodes.h:657
RelationPtr ri_IndexRelationDescs
Definition: execnodes.h:403
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2430
List * exclRelTlist
Definition: plannodes.h:240
List * ri_returningList
Definition: execnodes.h:442
List ** mt_arowmarks
Definition: execnodes.h:1046
int epqParam
Definition: plannodes.h:234
Node * onConflictWhere
Definition: plannodes.h:238
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2406
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:492

◆ ExecInsert()

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

Definition at line 263 of file nodeModifyTable.c.

References Assert, CMD_UPDATE, tupleDesc::constr, EState::es_lastoid, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, ExecARInsertTriggers(), ExecARUpdateTriggers(), ExecBRInsertTriggers(), ExecCheckIndexConstraints(), ExecCheckTIDVisible(), ExecConstraints(), FdwRoutine::ExecForeignInsert, ExecInsertIndexTuples(), ExecIRInsertTriggers(), ExecMaterializeSlot(), ExecOnConflictUpdate(), ExecProcessReturning(), ExecWithCheckOptions(), GetCurrentTransactionId(), heap_abort_speculative(), heap_finish_speculative(), heap_insert(), HEAP_INSERT_SPECULATIVE, HeapTupleHeaderSetSpeculativeToken, HeapTupleSetOid, InstrCountTuples2, InvalidOid, list_free(), ModifyTableState::mt_transition_capture, NIL, ONCONFLICT_NONE, ONCONFLICT_NOTHING, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_PartitionCheck, ResultRelInfo::ri_PartitionRoot, 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_update_new_table, TriggerDesc::trig_insert_before_row, TriggerDesc::trig_insert_instead_row, WCO_RLS_INSERT_CHECK, WCO_RLS_UPDATE_CHECK, and WCO_VIEW_CHECK.

Referenced by ExecModifyTable(), and ExecUpdate().

268 {
269  HeapTuple tuple;
270  ResultRelInfo *resultRelInfo;
271  Relation resultRelationDesc;
272  Oid newId;
273  List *recheckIndexes = NIL;
274  TupleTableSlot *result = NULL;
275  TransitionCaptureState *ar_insert_trig_tcs;
276  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
277  OnConflictAction onconflict = node->onConflictAction;
278 
279  /*
280  * get the heap tuple out of the tuple table slot, making sure we have a
281  * writable copy
282  */
283  tuple = ExecMaterializeSlot(slot);
284 
285  /*
286  * get information on the (current) result relation
287  */
288  resultRelInfo = estate->es_result_relation_info;
289  resultRelationDesc = resultRelInfo->ri_RelationDesc;
290 
291  /*
292  * If the result relation has OIDs, force the tuple's OID to zero so that
293  * heap_insert will assign a fresh OID. Usually the OID already will be
294  * zero at this point, but there are corner cases where the plan tree can
295  * return a tuple extracted literally from some table with the same
296  * rowtype.
297  *
298  * XXX if we ever wanted to allow users to assign their own OIDs to new
299  * rows, this'd be the place to do it. For the moment, we make a point of
300  * doing this before calling triggers, so that a user-supplied trigger
301  * could hack the OID if desired.
302  */
303  if (resultRelationDesc->rd_rel->relhasoids)
304  HeapTupleSetOid(tuple, InvalidOid);
305 
306  /*
307  * BEFORE ROW INSERT Triggers.
308  *
309  * Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
310  * INSERT ... ON CONFLICT statement. We cannot check for constraint
311  * violations before firing these triggers, because they can change the
312  * values to insert. Also, they can run arbitrary user-defined code with
313  * side-effects that we can't cancel by just not inserting the tuple.
314  */
315  if (resultRelInfo->ri_TrigDesc &&
316  resultRelInfo->ri_TrigDesc->trig_insert_before_row)
317  {
318  slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
319 
320  if (slot == NULL) /* "do nothing" */
321  return NULL;
322 
323  /* trigger might have changed tuple */
324  tuple = ExecMaterializeSlot(slot);
325  }
326 
327  /* INSTEAD OF ROW INSERT Triggers */
328  if (resultRelInfo->ri_TrigDesc &&
329  resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
330  {
331  slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);
332 
333  if (slot == NULL) /* "do nothing" */
334  return NULL;
335 
336  /* trigger might have changed tuple */
337  tuple = ExecMaterializeSlot(slot);
338 
339  newId = InvalidOid;
340  }
341  else if (resultRelInfo->ri_FdwRoutine)
342  {
343  /*
344  * insert into foreign table: let the FDW do it
345  */
346  slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
347  resultRelInfo,
348  slot,
349  planSlot);
350 
351  if (slot == NULL) /* "do nothing" */
352  return NULL;
353 
354  /* FDW might have changed tuple */
355  tuple = ExecMaterializeSlot(slot);
356 
357  /*
358  * AFTER ROW Triggers or RETURNING expressions might reference the
359  * tableoid column, so initialize t_tableOid before evaluating them.
360  */
361  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
362 
363  newId = InvalidOid;
364  }
365  else
366  {
367  WCOKind wco_kind;
368  bool check_partition_constr;
369 
370  /*
371  * We always check the partition constraint, including when the tuple
372  * got here via tuple-routing. However we don't need to in the latter
373  * case if no BR trigger is defined on the partition. Note that a BR
374  * trigger might modify the tuple such that the partition constraint
375  * is no longer satisfied, so we need to check in that case.
376  */
377  check_partition_constr = (resultRelInfo->ri_PartitionCheck != NIL);
378 
379  /*
380  * Constraints might reference the tableoid column, so initialize
381  * t_tableOid before evaluating them.
382  */
383  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
384 
385  /*
386  * Check any RLS WITH CHECK policies.
387  *
388  * Normally we should check INSERT policies. But if the insert is the
389  * result of a partition key update that moved the tuple to a new
390  * partition, we should instead check UPDATE policies, because we are
391  * executing policies defined on the target table, and not those
392  * defined on the child partitions.
393  */
394  wco_kind = (mtstate->operation == CMD_UPDATE) ?
396 
397  /*
398  * ExecWithCheckOptions() will skip any WCOs which are not of the kind
399  * we are looking for at this point.
400  */
401  if (resultRelInfo->ri_WithCheckOptions != NIL)
402  ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
403 
404  /*
405  * No need though if the tuple has been routed, and a BR trigger
406  * doesn't exist.
407  */
408  if (resultRelInfo->ri_PartitionRoot != NULL &&
409  !(resultRelInfo->ri_TrigDesc &&
410  resultRelInfo->ri_TrigDesc->trig_insert_before_row))
411  check_partition_constr = false;
412 
413  /* Check the constraints of the tuple */
414  if (resultRelationDesc->rd_att->constr || check_partition_constr)
415  ExecConstraints(resultRelInfo, slot, estate, true);
416 
417  if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
418  {
419  /* Perform a speculative insertion. */
420  uint32 specToken;
421  ItemPointerData conflictTid;
422  bool specConflict;
423  List *arbiterIndexes;
424 
425  arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
426 
427  /*
428  * Do a non-conclusive check for conflicts first.
429  *
430  * We're not holding any locks yet, so this doesn't guarantee that
431  * the later insert won't conflict. But it avoids leaving behind
432  * a lot of canceled speculative insertions, if you run a lot of
433  * INSERT ON CONFLICT statements that do conflict.
434  *
435  * We loop back here if we find a conflict below, either during
436  * the pre-check, or when we re-check after inserting the tuple
437  * speculatively.
438  */
439  vlock:
440  specConflict = false;
441  if (!ExecCheckIndexConstraints(slot, estate, &conflictTid,
442  arbiterIndexes))
443  {
444  /* committed conflict tuple found */
445  if (onconflict == ONCONFLICT_UPDATE)
446  {
447  /*
448  * In case of ON CONFLICT DO UPDATE, execute the UPDATE
449  * part. Be prepared to retry if the UPDATE fails because
450  * of another concurrent UPDATE/DELETE to the conflict
451  * tuple.
452  */
453  TupleTableSlot *returning = NULL;
454 
455  if (ExecOnConflictUpdate(mtstate, resultRelInfo,
456  &conflictTid, planSlot, slot,
457  estate, canSetTag, &returning))
458  {
459  InstrCountTuples2(&mtstate->ps, 1);
460  return returning;
461  }
462  else
463  goto vlock;
464  }
465  else
466  {
467  /*
468  * In case of ON CONFLICT DO NOTHING, do nothing. However,
469  * verify that the tuple is visible to the executor's MVCC
470  * snapshot at higher isolation levels.
471  */
472  Assert(onconflict == ONCONFLICT_NOTHING);
473  ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid);
474  InstrCountTuples2(&mtstate->ps, 1);
475  return NULL;
476  }
477  }
478 
479  /*
480  * Before we start insertion proper, acquire our "speculative
481  * insertion lock". Others can use that to wait for us to decide
482  * if we're going to go ahead with the insertion, instead of
483  * waiting for the whole transaction to complete.
484  */
486  HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken);
487 
488  /* insert the tuple, with the speculative token */
489  newId = heap_insert(resultRelationDesc, tuple,
490  estate->es_output_cid,
492  NULL);
493 
494  /* insert index entries for tuple */
495  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
496  estate, true, &specConflict,
497  arbiterIndexes);
498 
499  /* adjust the tuple's state accordingly */
500  if (!specConflict)
501  heap_finish_speculative(resultRelationDesc, tuple);
502  else
503  heap_abort_speculative(resultRelationDesc, tuple);
504 
505  /*
506  * Wake up anyone waiting for our decision. They will re-check
507  * the tuple, see that it's no longer speculative, and wait on our
508  * XID as if this was a regularly inserted tuple all along. Or if
509  * we killed the tuple, they will see it's dead, and proceed as if
510  * the tuple never existed.
511  */
513 
514  /*
515  * If there was a conflict, start from the beginning. We'll do
516  * the pre-check again, which will now find the conflicting tuple
517  * (unless it aborts before we get there).
518  */
519  if (specConflict)
520  {
521  list_free(recheckIndexes);
522  goto vlock;
523  }
524 
525  /* Since there was no insertion conflict, we're done */
526  }
527  else
528  {
529  /*
530  * insert the tuple normally.
531  *
532  * Note: heap_insert returns the tid (location) of the new tuple
533  * in the t_self field.
534  */
535  newId = heap_insert(resultRelationDesc, tuple,
536  estate->es_output_cid,
537  0, NULL);
538 
539  /* insert index entries for tuple */
540  if (resultRelInfo->ri_NumIndices > 0)
541  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
542  estate, false, NULL,
543  NIL);
544  }
545  }
546 
547  if (canSetTag)
548  {
549  (estate->es_processed)++;
550  estate->es_lastoid = newId;
551  setLastTid(&(tuple->t_self));
552  }
553 
554  /*
555  * If this insert is the result of a partition key update that moved the
556  * tuple to a new partition, put this row into the transition NEW TABLE,
557  * if there is one. We need to do this separately for DELETE and INSERT
558  * because they happen on different tables.
559  */
560  ar_insert_trig_tcs = mtstate->mt_transition_capture;
561  if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
563  {
564  ExecARUpdateTriggers(estate, resultRelInfo, NULL,
565  NULL,
566  tuple,
567  NULL,
568  mtstate->mt_transition_capture);
569 
570  /*
571  * We've already captured the NEW TABLE row, so make sure any AR
572  * INSERT trigger fired below doesn't capture it again.
573  */
574  ar_insert_trig_tcs = NULL;
575  }
576 
577  /* AFTER ROW INSERT Triggers */
578  ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes,
579  ar_insert_trig_tcs);
580 
581  list_free(recheckIndexes);
582 
583  /*
584  * Check any WITH CHECK OPTION constraints from parent views. We are
585  * required to do this after testing all constraints and uniqueness
586  * violations per the SQL spec, so we do it after actually inserting the
587  * record into the heap and all indexes.
588  *
589  * ExecWithCheckOptions will elog(ERROR) if a violation is found, so the
590  * tuple will never be seen, if it violates the WITH CHECK OPTION.
591  *
592  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
593  * are looking for at this point.
594  */
595  if (resultRelInfo->ri_WithCheckOptions != NIL)
596  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
597 
598  /* Process RETURNING if present */
599  if (resultRelInfo->ri_projectReturning)
600  result = ExecProcessReturning(resultRelInfo, slot, planSlot);
601 
602  return result;
603 }
int ri_NumIndices
Definition: execnodes.h:400
#define NIL
Definition: pg_list.h:69
Relation ri_RelationDesc
Definition: execnodes.h:397
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:2096
void heap_abort_speculative(Relation relation, HeapTuple tuple)
Definition: heapam.c:6249
CommandId es_output_cid
Definition: execnodes.h:487
List * ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes)
Definition: execIndexing.c:271
Oid es_lastoid
Definition: execnodes.h:530
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:210
Relation ri_PartitionRoot
Definition: execnodes.h:460
TupleTableSlot * ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2596
#define HeapTupleHeaderSetSpeculativeToken(tup, token)
Definition: htup_details.h:439
uint32 SpeculativeInsertionLockAcquire(TransactionId xid)
Definition: lmgr.c:669
CmdType operation
Definition: execnodes.h:1037
void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:2581
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool check_partition_constraint)
Definition: execMain.c:1962
bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, List *arbiterIndexes)
Definition: execIndexing.c:475
#define HEAP_INSERT_SPECULATIVE
Definition: heapam.h:31
HeapTupleHeader t_data
Definition: htup.h:68
#define HeapTupleSetOid(tuple, oid)
Definition: htup_details.h:710
bool trig_insert_instead_row
Definition: reltrigger.h:57
PlanState ps
Definition: execnodes.h:1036
static void ExecCheckTIDVisible(EState *estate, ResultRelInfo *relinfo, ItemPointer tid)
ItemPointerData t_self
Definition: htup.h:65
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:417
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1057
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:445
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:421
unsigned int uint32
Definition: c.h:325
Oid t_tableOid
Definition: htup.h:66
#define InstrCountTuples2(node, delta)
Definition: execnodes.h:969
void setLastTid(const ItemPointer tid)
Definition: tid.c:252
WCOKind
Definition: parsenodes.h:1128
Oid heap_insert(Relation relation, HeapTuple tup, CommandId cid, int options, BulkInsertState bistate)
Definition: heapam.c:2441
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, HeapTuple newtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:3068
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
bool trig_insert_before_row
Definition: reltrigger.h:55
List * ri_WithCheckOptions
Definition: execnodes.h:430
List * ri_PartitionCheck
Definition: execnodes.h:454
TupleDesc rd_att
Definition: rel.h:85
Plan * plan
Definition: execnodes.h:912
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:699
OnConflictAction onConflictAction
Definition: plannodes.h:235
void heap_finish_speculative(Relation relation, HeapTuple tuple)
Definition: heapam.c:6158
uint64 es_processed
Definition: execnodes.h:529
TupleConstr * constr
Definition: tupdesc.h:87
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:781
void list_free(List *list)
Definition: list.c:1133
TupleTableSlot * ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, TupleTableSlot *slot)
Definition: trigger.c:2515
Definition: pg_list.h:45
OnConflictAction
Definition: nodes.h:807
#define RelationGetRelid(relation)
Definition: rel.h:407
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:448
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:492

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 1922 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(), ExecPrepareTupleRouting(), ExecProcessReturning(), ExecProcNode(), ExecUpdate(), fireASTriggers(), ModifyTableState::fireBSTriggers, fireBSTriggers(), HeapTupleHeaderGetDatumLength, InvalidOid, ItemPointerSetInvalid, JunkFilter::jf_junkAttNo, ModifyTableState::mt_arowmarks, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_nplans, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_plans, ModifyTableState::mt_transition_capture, ModifyTableState::mt_whichplan, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationData::rd_rel, RelationGetRelid, relkind, 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, tupconv_map_for_subplan(), and TupIsNull.

Referenced by ExecInitModifyTable().

1923 {
1924  ModifyTableState *node = castNode(ModifyTableState, pstate);
1926  EState *estate = node->ps.state;
1927  CmdType operation = node->operation;
1928  ResultRelInfo *saved_resultRelInfo;
1929  ResultRelInfo *resultRelInfo;
1930  PlanState *subplanstate;
1931  JunkFilter *junkfilter;
1932  TupleTableSlot *slot;
1933  TupleTableSlot *planSlot;
1934  ItemPointer tupleid;
1935  ItemPointerData tuple_ctid;
1936  HeapTupleData oldtupdata;
1937  HeapTuple oldtuple;
1938 
1940 
1941  /*
1942  * This should NOT get called during EvalPlanQual; we should have passed a
1943  * subplan tree to EvalPlanQual, instead. Use a runtime test not just
1944  * Assert because this condition is easy to miss in testing. (Note:
1945  * although ModifyTable should not get executed within an EvalPlanQual
1946  * operation, we do have to allow it to be initialized and shut down in
1947  * case it is within a CTE subplan. Hence this test must be here, not in
1948  * ExecInitModifyTable.)
1949  */
1950  if (estate->es_epqTuple != NULL)
1951  elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
1952 
1953  /*
1954  * If we've already completed processing, don't try to do more. We need
1955  * this test because ExecPostprocessPlan might call us an extra time, and
1956  * our subplan's nodes aren't necessarily robust against being called
1957  * extra times.
1958  */
1959  if (node->mt_done)
1960  return NULL;
1961 
1962  /*
1963  * On first call, fire BEFORE STATEMENT triggers before proceeding.
1964  */
1965  if (node->fireBSTriggers)
1966  {
1967  fireBSTriggers(node);
1968  node->fireBSTriggers = false;
1969  }
1970 
1971  /* Preload local variables */
1972  resultRelInfo = node->resultRelInfo + node->mt_whichplan;
1973  subplanstate = node->mt_plans[node->mt_whichplan];
1974  junkfilter = resultRelInfo->ri_junkFilter;
1975 
1976  /*
1977  * es_result_relation_info must point to the currently active result
1978  * relation while we are within this ModifyTable node. Even though
1979  * ModifyTable nodes can't be nested statically, they can be nested
1980  * dynamically (since our subplan could include a reference to a modifying
1981  * CTE). So we have to save and restore the caller's value.
1982  */
1983  saved_resultRelInfo = estate->es_result_relation_info;
1984 
1985  estate->es_result_relation_info = resultRelInfo;
1986 
1987  /*
1988  * Fetch rows from subplan(s), and execute the required table modification
1989  * for each row.
1990  */
1991  for (;;)
1992  {
1993  /*
1994  * Reset the per-output-tuple exprcontext. This is needed because
1995  * triggers expect to use that context as workspace. It's a bit ugly
1996  * to do this below the top level of the plan, however. We might need
1997  * to rethink this later.
1998  */
1999  ResetPerTupleExprContext(estate);
2000 
2001  planSlot = ExecProcNode(subplanstate);
2002 
2003  if (TupIsNull(planSlot))
2004  {
2005  /* advance to next subplan if any */
2006  node->mt_whichplan++;
2007  if (node->mt_whichplan < node->mt_nplans)
2008  {
2009  resultRelInfo++;
2010  subplanstate = node->mt_plans[node->mt_whichplan];
2011  junkfilter = resultRelInfo->ri_junkFilter;
2012  estate->es_result_relation_info = resultRelInfo;
2013  EvalPlanQualSetPlan(&node->mt_epqstate, subplanstate->plan,
2014  node->mt_arowmarks[node->mt_whichplan]);
2015  /* Prepare to convert transition tuples from this child. */
2016  if (node->mt_transition_capture != NULL)
2017  {
2020  }
2021  if (node->mt_oc_transition_capture != NULL)
2022  {
2025  }
2026  continue;
2027  }
2028  else
2029  break;
2030  }
2031 
2032  /*
2033  * If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
2034  * here is compute the RETURNING expressions.
2035  */
2036  if (resultRelInfo->ri_usesFdwDirectModify)
2037  {
2038  Assert(resultRelInfo->ri_projectReturning);
2039 
2040  /*
2041  * A scan slot containing the data that was actually inserted,
2042  * updated or deleted has already been made available to
2043  * ExecProcessReturning by IterateDirectModify, so no need to
2044  * provide it here.
2045  */
2046  slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
2047 
2048  estate->es_result_relation_info = saved_resultRelInfo;
2049  return slot;
2050  }
2051 
2052  EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
2053  slot = planSlot;
2054 
2055  tupleid = NULL;
2056  oldtuple = NULL;
2057  if (junkfilter != NULL)
2058  {
2059  /*
2060  * extract the 'ctid' or 'wholerow' junk attribute.
2061  */
2062  if (operation == CMD_UPDATE || operation == CMD_DELETE)
2063  {
2064  char relkind;
2065  Datum datum;
2066  bool isNull;
2067 
2068  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
2069  if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW)
2070  {
2071  datum = ExecGetJunkAttribute(slot,
2072  junkfilter->jf_junkAttNo,
2073  &isNull);
2074  /* shouldn't ever get a null result... */
2075  if (isNull)
2076  elog(ERROR, "ctid is NULL");
2077 
2078  tupleid = (ItemPointer) DatumGetPointer(datum);
2079  tuple_ctid = *tupleid; /* be sure we don't free ctid!! */
2080  tupleid = &tuple_ctid;
2081  }
2082 
2083  /*
2084  * Use the wholerow attribute, when available, to reconstruct
2085  * the old relation tuple.
2086  *
2087  * Foreign table updates have a wholerow attribute when the
2088  * relation has a row-level trigger. Note that the wholerow
2089  * attribute does not carry system columns. Foreign table
2090  * triggers miss seeing those, except that we know enough here
2091  * to set t_tableOid. Quite separately from this, the FDW may
2092  * fetch its own junk attrs to identify the row.
2093  *
2094  * Other relevant relkinds, currently limited to views, always
2095  * have a wholerow attribute.
2096  */
2097  else if (AttributeNumberIsValid(junkfilter->jf_junkAttNo))
2098  {
2099  datum = ExecGetJunkAttribute(slot,
2100  junkfilter->jf_junkAttNo,
2101  &isNull);
2102  /* shouldn't ever get a null result... */
2103  if (isNull)
2104  elog(ERROR, "wholerow is NULL");
2105 
2106  oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
2107  oldtupdata.t_len =
2109  ItemPointerSetInvalid(&(oldtupdata.t_self));
2110  /* Historically, view triggers see invalid t_tableOid. */
2111  oldtupdata.t_tableOid =
2112  (relkind == RELKIND_VIEW) ? InvalidOid :
2113  RelationGetRelid(resultRelInfo->ri_RelationDesc);
2114 
2115  oldtuple = &oldtupdata;
2116  }
2117  else
2118  Assert(relkind == RELKIND_FOREIGN_TABLE);
2119  }
2120 
2121  /*
2122  * apply the junkfilter if needed.
2123  */
2124  if (operation != CMD_DELETE)
2125  slot = ExecFilterJunk(junkfilter, slot);
2126  }
2127 
2128  switch (operation)
2129  {
2130  case CMD_INSERT:
2131  /* Prepare for tuple routing if needed. */
2132  if (proute)
2133  slot = ExecPrepareTupleRouting(node, estate, proute,
2134  resultRelInfo, slot);
2135  slot = ExecInsert(node, slot, planSlot,
2136  estate, node->canSetTag);
2137  /* Revert ExecPrepareTupleRouting's state change. */
2138  if (proute)
2139  estate->es_result_relation_info = resultRelInfo;
2140  break;
2141  case CMD_UPDATE:
2142  slot = ExecUpdate(node, tupleid, oldtuple, slot, planSlot,
2143  &node->mt_epqstate, estate, node->canSetTag);
2144  break;
2145  case CMD_DELETE:
2146  slot = ExecDelete(node, tupleid, oldtuple, planSlot,
2147  &node->mt_epqstate, estate,
2148  NULL, true, node->canSetTag,
2149  false /* changingPart */ );
2150  break;
2151  default:
2152  elog(ERROR, "unknown operation");
2153  break;
2154  }
2155 
2156  /*
2157  * If we got a RETURNING result, return it to caller. We'll continue
2158  * the work on next call.
2159  */
2160  if (slot)
2161  {
2162  estate->es_result_relation_info = saved_resultRelInfo;
2163  return slot;
2164  }
2165  }
2166 
2167  /* Restore es_result_relation_info before exiting */
2168  estate->es_result_relation_info = saved_resultRelInfo;
2169 
2170  /*
2171  * We're done, but fire AFTER STATEMENT triggers before exiting.
2172  */
2173  fireASTriggers(node);
2174 
2175  node->mt_done = true;
2176 
2177  return NULL;
2178 }
AttrNumber jf_junkAttNo
Definition: execnodes.h:365
JunkFilter * ri_junkFilter
Definition: execnodes.h:439
HeapTuple * es_epqTuple
Definition: execnodes.h:558
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1060
Relation ri_RelationDesc
Definition: execnodes.h:397
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
#define ResetPerTupleExprContext(estate)
Definition: executor.h:498
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1054
#define castNode(_type_, nodeptr)
Definition: nodes.h:586
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1043
static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag)
static void fireBSTriggers(ModifyTableState *node)
static TupleConversionMap * tupconv_map_for_subplan(ModifyTableState *node, int whichplan)
CmdType operation
Definition: execnodes.h:1037
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2849
EState * state
Definition: execnodes.h:914
Form_pg_class rd_rel
Definition: rel.h:84
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:264
char relkind
Definition: pg_class.h:51
ItemPointerData * ItemPointer
Definition: itemptr.h:49
HeapTupleHeader t_data
Definition: htup.h:68
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1036
TupleConversionMap * tcs_map
Definition: trigger.h:73
ItemPointerData t_self
Definition: htup.h:65
bool ri_usesFdwDirectModify
Definition: execnodes.h:427
uint32 t_len
Definition: htup.h:64
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1057
EPQState mt_epqstate
Definition: execnodes.h:1047
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:445
#define TupIsNull(slot)
Definition: tuptable.h:146
Oid t_tableOid
Definition: htup.h:66
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
PlanState ** mt_plans
Definition: execnodes.h:1040
static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot)
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
static void fireASTriggers(ModifyTableState *node)
uintptr_t Datum
Definition: postgres.h:367
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:262
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:233
Plan * plan
Definition: execnodes.h:912
#define InvalidOid
Definition: postgres_ext.h:36
static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool *tupleDeleted, bool processReturning, bool canSetTag, bool changingPart)
#define Assert(condition)
Definition: c.h:699
#define DatumGetPointer(X)
Definition: postgres.h:534
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
Datum ExecGetJunkAttribute(TupleTableSlot *slot, AttrNumber attno, bool *isNull)
Definition: execJunk.c:248
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define elog
Definition: elog.h:219
#define RelationGetRelid(relation)
Definition: rel.h:407
CmdType
Definition: nodes.h:657
List ** mt_arowmarks
Definition: execnodes.h:1046
#define EvalPlanQualSetSlot(epqstate, slot)
Definition: executor.h:208
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:451
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:492

◆ ExecOnConflictUpdate()

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

Definition at line 1330 of file nodeModifyTable.c.

References Assert, buffer, HeapUpdateFailureData::ctid, 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, ItemPointerIndicatesMovedPartitions, LockWaitBlock, ModifyTableState::mt_conflproj, ModifyTableState::mt_epqstate, ModifyTableState::mt_existing, NIL, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_WhereClause, ModifyTableState::ps, PlanState::ps_ExprContext, ReleaseBuffer(), ResetExprContext, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_WithCheckOptions, PlanState::state, HeapTupleData::t_data, HeapTupleData::t_self, test(), TransactionIdIsCurrentTransactionId(), and WCO_RLS_CONFLICT_CHECK.

Referenced by ExecInsert().

1338 {
1339  ExprContext *econtext = mtstate->ps.ps_ExprContext;
1340  Relation relation = resultRelInfo->ri_RelationDesc;
1341  ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
1342  HeapTupleData tuple;
1343  HeapUpdateFailureData hufd;
1344  LockTupleMode lockmode;
1345  HTSU_Result test;
1346  Buffer buffer;
1347 
1348  /* Determine lock mode to use */
1349  lockmode = ExecUpdateLockMode(estate, resultRelInfo);
1350 
1351  /*
1352  * Lock tuple for update. Don't follow updates when tuple cannot be
1353  * locked without doing so. A row locking conflict here means our
1354  * previous conclusion that the tuple is conclusively committed is not
1355  * true anymore.
1356  */
1357  tuple.t_self = *conflictTid;
1358  test = heap_lock_tuple(relation, &tuple, estate->es_output_cid,
1359  lockmode, LockWaitBlock, false, &buffer,
1360  &hufd);
1361  switch (test)
1362  {
1363  case HeapTupleMayBeUpdated:
1364  /* success! */
1365  break;
1366 
1367  case HeapTupleInvisible:
1368 
1369  /*
1370  * This can occur when a just inserted tuple is updated again in
1371  * the same command. E.g. because multiple rows with the same
1372  * conflicting key values are inserted.
1373  *
1374  * This is somewhat similar to the ExecUpdate()
1375  * HeapTupleSelfUpdated case. We do not want to proceed because
1376  * it would lead to the same row being updated a second time in
1377  * some unspecified order, and in contrast to plain UPDATEs
1378  * there's no historical behavior to break.
1379  *
1380  * It is the user's responsibility to prevent this situation from
1381  * occurring. These problems are why SQL-2003 similarly specifies
1382  * that for SQL MERGE, an exception must be raised in the event of
1383  * an attempt to update the same row twice.
1384  */
1386  ereport(ERROR,
1387  (errcode(ERRCODE_CARDINALITY_VIOLATION),
1388  errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"),
1389  errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
1390 
1391  /* This shouldn't happen */
1392  elog(ERROR, "attempted to lock invisible tuple");
1393  break;
1394 
1395  case HeapTupleSelfUpdated:
1396 
1397  /*
1398  * This state should never be reached. As a dirty snapshot is used
1399  * to find conflicting tuples, speculative insertion wouldn't have
1400  * seen this row to conflict with.
1401  */
1402  elog(ERROR, "unexpected self-updated tuple");
1403  break;
1404 
1405  case HeapTupleUpdated:
1407  ereport(ERROR,
1408  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1409  errmsg("could not serialize access due to concurrent update")));
1410 
1411  /*
1412  * As long as we don't support an UPDATE of INSERT ON CONFLICT for
1413  * a partitioned table we shouldn't reach to a case where tuple to
1414  * be lock is moved to another partition due to concurrent update
1415  * of the partition key.
1416  */
1418 
1419  /*
1420  * Tell caller to try again from the very start.
1421  *
1422  * It does not make sense to use the usual EvalPlanQual() style
1423  * loop here, as the new version of the row might not conflict
1424  * anymore, or the conflicting tuple has actually been deleted.
1425  */
1426  ReleaseBuffer(buffer);
1427  return false;
1428 
1429  default:
1430  elog(ERROR, "unrecognized heap_lock_tuple status: %u", test);
1431  }
1432 
1433  /*
1434  * Success, the tuple is locked.
1435  *
1436  * Reset per-tuple memory context to free any expression evaluation
1437  * storage allocated in the previous cycle.
1438  */
1439  ResetExprContext(econtext);
1440 
1441  /*
1442  * Verify that the tuple is visible to our MVCC snapshot if the current
1443  * isolation level mandates that.
1444  *
1445  * It's not sufficient to rely on the check within ExecUpdate() as e.g.
1446  * CONFLICT ... WHERE clause may prevent us from reaching that.
1447  *
1448  * This means we only ever continue when a new command in the current
1449  * transaction could see the row, even though in READ COMMITTED mode the
1450  * tuple will not be visible according to the current statement's
1451  * snapshot. This is in line with the way UPDATE deals with newer tuple
1452  * versions.
1453  */
1454  ExecCheckHeapTupleVisible(estate, &tuple, buffer);
1455 
1456  /* Store target's existing tuple in the state's dedicated slot */
1457  ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false);
1458 
1459  /*
1460  * Make tuple and any needed join variables available to ExecQual and
1461  * ExecProject. The EXCLUDED tuple is installed in ecxt_innertuple, while
1462  * the target's existing tuple is installed in the scantuple. EXCLUDED
1463  * has been made to reference INNER_VAR in setrefs.c, but there is no
1464  * other redirection.
1465  */
1466  econtext->ecxt_scantuple = mtstate->mt_existing;
1467  econtext->ecxt_innertuple = excludedSlot;
1468  econtext->ecxt_outertuple = NULL;
1469 
1470  if (!ExecQual(onConflictSetWhere, econtext))
1471  {
1472  ReleaseBuffer(buffer);
1473  InstrCountFiltered1(&mtstate->ps, 1);
1474  return true; /* done with the tuple */
1475  }
1476 
1477  if (resultRelInfo->ri_WithCheckOptions != NIL)
1478  {
1479  /*
1480  * Check target's existing tuple against UPDATE-applicable USING
1481  * security barrier quals (if any), enforced here as RLS checks/WCOs.
1482  *
1483  * The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
1484  * quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
1485  * but that's almost the extent of its special handling for ON
1486  * CONFLICT DO UPDATE.
1487  *
1488  * The rewriter will also have associated UPDATE applicable straight
1489  * RLS checks/WCOs for the benefit of the ExecUpdate() call that
1490  * follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
1491  * kinds, so there is no danger of spurious over-enforcement in the
1492  * INSERT or UPDATE path.
1493  */
1495  mtstate->mt_existing,
1496  mtstate->ps.state);
1497  }
1498 
1499  /* Project the new tuple version */
1500  ExecProject(resultRelInfo->ri_onConflict->oc_ProjInfo);
1501 
1502  /*
1503  * Note that it is possible that the target tuple has been modified in
1504  * this session, after the above heap_lock_tuple. We choose to not error
1505  * out in that case, in line with ExecUpdate's treatment of similar cases.
1506  * This can happen if an UPDATE is triggered from within ExecQual(),
1507  * ExecWithCheckOptions() or ExecProject() above, e.g. by selecting from a
1508  * wCTE in the ON CONFLICT's SET.
1509  */
1510 
1511  /* Execute UPDATE with projection */
1512  *returning = ExecUpdate(mtstate, &tuple.t_self, NULL,
1513  mtstate->mt_conflproj, planSlot,
1514  &mtstate->mt_epqstate, mtstate->ps.state,
1515  canSetTag);
1516 
1517  ReleaseBuffer(buffer);
1518  return true;
1519 }
#define NIL
Definition: pg_list.h:69
TupleTableSlot * ExecStoreTuple(HeapTuple tuple, TupleTableSlot *slot, Buffer buffer, bool shouldFree)
Definition: execTuples.c:356
Relation ri_RelationDesc
Definition: execnodes.h:397
int errhint(const char *fmt,...)
Definition: elog.c:987
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2096
CommandId es_output_cid
Definition: execnodes.h:487
static void test(void)
bool TransactionIdIsCurrentTransactionId(TransactionId xid)
Definition: xact.c:765
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:4688
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:947
TupleTableSlot * mt_conflproj
Definition: execnodes.h:1051
#define IsolationUsesXactSnapshot()
Definition: xact.h:50
int errcode(int sqlerrcode)
Definition: elog.c:575
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
EState * state
Definition: execnodes.h:914
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:361
TupleTableSlot * mt_existing
Definition: execnodes.h:1049
HeapTupleHeader t_data
Definition: htup.h:68
LockTupleMode
Definition: heapam.h:38
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1036
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:377
ItemPointerData t_self
Definition: htup.h:65
static void ExecCheckHeapTupleVisible(EState *estate, HeapTuple tuple, Buffer buffer)
EPQState mt_epqstate
Definition: execnodes.h:1047
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:220
HTSU_Result
Definition: snapshot.h:121
ExprState * oc_WhereClause
Definition: execnodes.h:379
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:974
#define ereport(elevel, rest)
Definition: elog.h:122
OnConflictSetState * ri_onConflict
Definition: execnodes.h:451
List * ri_WithCheckOptions
Definition: execnodes.h:430
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define Assert(condition)
Definition: c.h:699
LockTupleMode ExecUpdateLockMode(EState *estate, ResultRelInfo *relinfo)
Definition: execMain.c:2380
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define HeapTupleHeaderGetXmin(tup)
Definition: htup_details.h:312
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:218
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
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:324
#define ResetExprContext(econtext)
Definition: executor.h:483

◆ ExecPrepareTupleRouting()

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

Definition at line 1671 of file nodeModifyTable.c.

References Assert, CheckValidResultRel(), CMD_INSERT, ConvertPartitionTupleSlot(), EState::es_result_relation_info, ExecFindPartition(), ExecInitPartitionInfo(), ExecInitRoutingInfo(), ExecMaterializeSlot(), ExecSetSlotDescriptor(), ModifyTableState::mt_conflproj, ModifyTableState::mt_existing, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_transition_capture, OnConflictSetState::oc_ProjTupdesc, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, PartitionTupleRouting::parent_child_tupconv_maps, PartitionTupleRouting::partition_dispatch_info, PartitionTupleRouting::partition_tuple_slot, PartitionTupleRouting::partitions, PlanState::plan, ModifyTableState::ps, RelationGetDescr, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_PartitionReadyForRouting, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, TriggerDesc::trig_insert_before_row, and TupConvMapForLeaf().

Referenced by ExecModifyTable(), and ExecUpdate().

1676 {
1677  ModifyTable *node;
1678  int partidx;
1679  ResultRelInfo *partrel;
1680  HeapTuple tuple;
1681 
1682  /*
1683  * Determine the target partition. If ExecFindPartition does not find a
1684  * partition after all, it doesn't return here; otherwise, the returned
1685  * value is to be used as an index into the arrays for the ResultRelInfo
1686  * and TupleConversionMap for the partition.
1687  */
1688  partidx = ExecFindPartition(targetRelInfo,
1689  proute->partition_dispatch_info,
1690  slot,
1691  estate);
1692  Assert(partidx >= 0 && partidx < proute->num_partitions);
1693 
1694  /*
1695  * Get the ResultRelInfo corresponding to the selected partition; if not
1696  * yet there, initialize it.
1697  */
1698  partrel = proute->partitions[partidx];
1699  if (partrel == NULL)
1700  partrel = ExecInitPartitionInfo(mtstate, targetRelInfo,
1701  proute, estate,
1702  partidx);
1703 
1704  /*
1705  * Check whether the partition is routable if we didn't yet
1706  *
1707  * Note: an UPDATE of a partition key invokes an INSERT that moves the
1708  * tuple to a new partition. This check would be applied to a subplan
1709  * partition of such an UPDATE that is chosen as the partition to route
1710  * the tuple to. The reason we do this check here rather than in
1711  * ExecSetupPartitionTupleRouting is to avoid aborting such an UPDATE
1712  * unnecessarily due to non-routable subplan partitions that may not be
1713  * chosen for update tuple movement after all.
1714  */
1715  if (!partrel->ri_PartitionReadyForRouting)
1716  {
1717  /* Verify the partition is a valid target for INSERT. */
1718  CheckValidResultRel(partrel, CMD_INSERT);
1719 
1720  /* Set up information needed for routing tuples to the partition. */
1721  ExecInitRoutingInfo(mtstate, estate, proute, partrel, partidx);
1722  }
1723 
1724  /*
1725  * Make it look like we are inserting into the partition.
1726  */
1727  estate->es_result_relation_info = partrel;
1728 
1729  /* Get the heap tuple out of the given slot. */
1730  tuple = ExecMaterializeSlot(slot);
1731 
1732  /*
1733  * If we're capturing transition tuples, we might need to convert from the
1734  * partition rowtype to parent rowtype.
1735  */
1736  if (mtstate->mt_transition_capture != NULL)
1737  {
1738  if (partrel->ri_TrigDesc &&
1740  {
1741  /*
1742  * If there are any BEFORE triggers on the partition, we'll have
1743  * to be ready to convert their result back to tuplestore format.
1744  */
1746  mtstate->mt_transition_capture->tcs_map =
1747  TupConvMapForLeaf(proute, targetRelInfo, partidx);
1748  }
1749  else
1750  {
1751  /*
1752  * Otherwise, just remember the original unconverted tuple, to
1753  * avoid a needless round trip conversion.
1754  */
1756  mtstate->mt_transition_capture->tcs_map = NULL;
1757  }
1758  }
1759  if (mtstate->mt_oc_transition_capture != NULL)
1760  {
1761  mtstate->mt_oc_transition_capture->tcs_map =
1762  TupConvMapForLeaf(proute, targetRelInfo, partidx);
1763  }
1764 
1765  /*
1766  * Convert the tuple, if necessary.
1767  */
1769  tuple,
1770  proute->partition_tuple_slot,
1771  &slot);
1772 
1773  /* Initialize information needed to handle ON CONFLICT DO UPDATE. */
1774  Assert(mtstate != NULL);
1775  node = (ModifyTable *) mtstate->ps.plan;
1776  if (node->onConflictAction == ONCONFLICT_UPDATE)
1777  {
1778  Assert(mtstate->mt_existing != NULL);
1780  RelationGetDescr(partrel->ri_RelationDesc));
1781  Assert(mtstate->mt_conflproj != NULL);
1783  partrel->ri_onConflict->oc_ProjTupdesc);
1784  }
1785 
1786  return slot;
1787 }
int ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, TupleTableSlot *slot, EState *estate)
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1060
Relation ri_RelationDesc
Definition: execnodes.h:397
TupleConversionMap * TupConvMapForLeaf(PartitionTupleRouting *proute, ResultRelInfo *rootRelInfo, int leaf_index)
#define RelationGetDescr(relation)
Definition: rel.h:433
TupleTableSlot * mt_conflproj
Definition: execnodes.h:1051
bool ri_PartitionReadyForRouting
Definition: execnodes.h:463
TupleTableSlot * partition_tuple_slot
HeapTuple tcs_original_insert_tuple
Definition: trigger.h:82
ResultRelInfo ** partitions
TupleTableSlot * mt_existing
Definition: execnodes.h:1049
PlanState ps
Definition: execnodes.h:1036
TupleConversionMap * tcs_map
Definition: trigger.h:73
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:1104
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1057
PartitionDispatch * partition_dispatch_info
void ExecInitRoutingInfo(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *partRelInfo, int partidx)
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
OnConflictSetState * ri_onConflict
Definition: execnodes.h:451
bool trig_insert_before_row
Definition: reltrigger.h:55
void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc)
Definition: execTuples.c:281
HeapTuple ConvertPartitionTupleSlot(TupleConversionMap *map, HeapTuple tuple, TupleTableSlot *new_slot, TupleTableSlot **p_my_slot)
Plan * plan
Definition: execnodes.h:912
#define Assert(condition)
Definition: c.h:699
OnConflictAction onConflictAction
Definition: plannodes.h:235
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:781
TupleConversionMap ** parent_child_tupconv_maps
ResultRelInfo * ExecInitPartitionInfo(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, PartitionTupleRouting *proute, EState *estate, int partidx)
TupleDesc oc_ProjTupdesc
Definition: execnodes.h:378
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:492

◆ ExecProcessReturning()

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

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

162 {
163  ProjectionInfo *projectReturning = resultRelInfo->ri_projectReturning;
164  ExprContext *econtext = projectReturning->pi_exprContext;
165 
166  /*
167  * Reset per-tuple memory context to free any expression evaluation
168  * storage allocated in the previous cycle.
169  */
170  ResetExprContext(econtext);
171 
172  /* Make tuple and any needed join variables available to ExecProject */
173  if (tupleSlot)
174  econtext->ecxt_scantuple = tupleSlot;
175  else
176  {
177  HeapTuple tuple;
178 
179  /*
180  * RETURNING expressions might reference the tableoid column, so
181  * initialize t_tableOid before evaluating them.
182  */
183  Assert(!TupIsNull(econtext->ecxt_scantuple));
184  tuple = ExecMaterializeSlot(econtext->ecxt_scantuple);
185  tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
186  }
187  econtext->ecxt_outertuple = planSlot;
188 
189  /* Compute the RETURNING expressions */
190  return ExecProject(projectReturning);
191 }
Relation ri_RelationDesc
Definition: execnodes.h:397
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:445
#define TupIsNull(slot)
Definition: tuptable.h:146
Oid t_tableOid
Definition: htup.h:66
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define Assert(condition)
Definition: c.h:699
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:218
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:781
ExprContext * pi_exprContext
Definition: execnodes.h:327
#define RelationGetRelid(relation)
Definition: rel.h:407
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:324
#define ResetExprContext(econtext)
Definition: executor.h:483

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 2704 of file nodeModifyTable.c.

References elog, and ERROR.

Referenced by ExecReScan().

2705 {
2706  /*
2707  * Currently, we don't need to support rescan on ModifyTable nodes. The
2708  * semantics of that would be a bit debatable anyway.
2709  */
2710  elog(ERROR, "ExecReScanModifyTable is not implemented");
2711 }
#define ERROR
Definition: elog.h:43
#define elog
Definition: elog.h:219

◆ ExecSetupChildParentMapForSubplan()

static void ExecSetupChildParentMapForSubplan ( ModifyTableState mtstate)
static

Definition at line 1799 of file nodeModifyTable.c.

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

Referenced by ExecInitModifyTable(), and ExecSetupChildParentMapForTcs().

1800 {
1801  ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate);
1802  ResultRelInfo *resultRelInfos = mtstate->resultRelInfo;
1803  TupleDesc outdesc;
1804  int numResultRelInfos = mtstate->mt_nplans;
1805  int i;
1806 
1807  /*
1808  * First check if there is already a per-subplan array allocated. Even if
1809  * there is already a per-leaf map array, we won't require a per-subplan
1810  * one, since we will use the subplan offset array to convert the subplan
1811  * index to per-leaf index.
1812  */
1813  if (mtstate->mt_per_subplan_tupconv_maps ||
1814  (mtstate->mt_partition_tuple_routing &&
1816  return;
1817 
1818  /*
1819  * Build array of conversion maps from each child's TupleDesc to the one
1820  * used in the target relation. The map pointers may be NULL when no
1821  * conversion is necessary, which is hopefully a common case.
1822  */
1823 
1824  /* Get tuple descriptor of the target rel. */
1825  outdesc = RelationGetDescr(targetRelInfo->ri_RelationDesc);
1826 
1828  palloc(sizeof(TupleConversionMap *) * numResultRelInfos);
1829 
1830  for (i = 0; i < numResultRelInfos; ++i)
1831  {
1832  mtstate->mt_per_subplan_tupconv_maps[i] =
1833  convert_tuples_by_name(RelationGetDescr(resultRelInfos[i].ri_RelationDesc),
1834  outdesc,
1835  gettext_noop("could not convert row type"));
1836  }
1837 }
Relation ri_RelationDesc
Definition: execnodes.h:397
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1054
#define RelationGetDescr(relation)
Definition: rel.h:433
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1043
#define gettext_noop(x)
Definition: c.h:1036
TupleConversionMap ** child_parent_tupconv_maps
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc, const char *msg)
Definition: tupconvert.c:210
TupleConversionMap ** mt_per_subplan_tupconv_maps
Definition: execnodes.h:1063
void * palloc(Size size)
Definition: mcxt.c:924
int i

◆ ExecSetupChildParentMapForTcs()

static void ExecSetupChildParentMapForTcs ( ModifyTableState mtstate)
static

Definition at line 1848 of file nodeModifyTable.c.

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

Referenced by ExecSetupTransitionCaptureState().

1849 {
1851 
1852  /*
1853  * If partition tuple routing is set up, we will require partition-indexed
1854  * access. In that case, create the map array indexed by partition; we
1855  * will still be able to access the maps using a subplan index by
1856  * converting the subplan index to a partition index using
1857  * subplan_partition_offsets. If tuple routing is not set up, it means we
1858  * don't require partition-indexed access. In that case, create just a
1859  * subplan-indexed map.
1860  */
1861  if (proute)
1862  {
1863  /*
1864  * If a partition-indexed map array is to be created, the subplan map
1865  * array has to be NULL. If the subplan map array is already created,
1866  * we won't be able to access the map using a partition index.
1867  */
1868  Assert(mtstate->mt_per_subplan_tupconv_maps == NULL);
1869 
1871  }
1872  else
1874 }
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1054
void ExecSetupChildParentMapForLeaf(PartitionTupleRouting *proute)
static void ExecSetupChildParentMapForSubplan(ModifyTableState *mtstate)
TupleConversionMap ** mt_per_subplan_tupconv_maps
Definition: execnodes.h:1063
#define Assert(condition)
Definition: c.h:699

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 1619 of file nodeModifyTable.c.

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

Referenced by ExecInitModifyTable().

1620 {
1621  ModifyTable *plan = (ModifyTable *) mtstate->ps.plan;
1622  ResultRelInfo *targetRelInfo = getTargetResultRelInfo(mtstate);
1623 
1624  /* Check for transition tables on the directly targeted relation. */
1625  mtstate->mt_transition_capture =
1626  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1627  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1628  mtstate->operation);
1629  if (plan->operation == CMD_INSERT &&
1631  mtstate->mt_oc_transition_capture =
1632  MakeTransitionCaptureState(targetRelInfo->ri_TrigDesc,
1633  RelationGetRelid(targetRelInfo->ri_RelationDesc),
1634  CMD_UPDATE);
1635 
1636  /*
1637  * If we found that we need to collect transition tuples then we may also
1638  * need tuple conversion maps for any children that have TupleDescs that
1639  * aren't compatible with the tuplestores. (We can share these maps
1640  * between the regular and ON CONFLICT cases.)
1641  */
1642  if (mtstate->mt_transition_capture != NULL ||
1643  mtstate->mt_oc_transition_capture != NULL)
1644  {
1646 
1647  /*
1648  * Install the conversion map for the first plan for UPDATE and DELETE
1649  * operations. It will be advanced each time we switch to the next
1650  * plan. (INSERT operations set it every time, so we need not update
1651  * mtstate->mt_oc_transition_capture here.)
1652  */
1653  if (mtstate->mt_transition_capture && mtstate->operation != CMD_INSERT)
1654  mtstate->mt_transition_capture->tcs_map =
1655  tupconv_map_for_subplan(mtstate, 0);
1656  }
1657 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1060
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
static TupleConversionMap * tupconv_map_for_subplan(ModifyTableState *node, int whichplan)
CmdType operation
Definition: execnodes.h:1037
PlanState ps
Definition: execnodes.h:1036
TupleConversionMap * tcs_map
Definition: trigger.h:73
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1057
TransitionCaptureState * MakeTransitionCaptureState(TriggerDesc *trigdesc, Oid relid, CmdType cmdType)
Definition: trigger.c:4644
Plan * plan
Definition: execnodes.h:912
OnConflictAction onConflictAction
Definition: plannodes.h:235
CmdType operation
Definition: plannodes.h:219
#define RelationGetRelid(relation)
Definition: rel.h:407
static void ExecSetupChildParentMapForTcs(ModifyTableState *mtstate)

◆ ExecUpdate()

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

Definition at line 925 of file nodeModifyTable.c.

References Assert, HeapUpdateFailureData::cmax, CMD_INSERT, tupleDesc::constr, ConvertPartitionTupleSlot(), HeapUpdateFailureData::ctid, elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, EState::es_crosscheck_snapshot, EState::es_output_cid, EState::es_processed, EState::es_result_relation_info, EvalPlanQual(), ExecARUpdateTriggers(), ExecBRUpdateTriggers(), ExecConstraints(), ExecDelete(), ExecFilterJunk(), FdwRoutine::ExecForeignUpdate, ExecInsert(), ExecInsertIndexTuples(), ExecIRUpdateTriggers(), ExecMaterializeSlot(), ExecPartitionCheck(), ExecPartitionCheckEmitError(), ExecPrepareTupleRouting(), ExecProcessReturning(), ExecWithCheckOptions(), heap_update(), HeapTupleIsHeapOnly, HeapTupleMayBeUpdated, HeapTupleSelfUpdated, HeapTupleUpdated, IsBootstrapProcessingMode, IsolationUsesXactSnapshot, ItemPointerEquals(), ItemPointerIndicatesMovedPartitions, list_free(), ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_transition_capture, NIL, ONCONFLICT_UPDATE, ModifyTableState::operation, PlanState::plan, ModifyTableState::ps, RelationData::rd_att, RelationGetRelid, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_junkFilter, ResultRelInfo::ri_NumIndices, ResultRelInfo::ri_PartitionCheck, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RangeTableIndex, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, ResultRelInfo::ri_WithCheckOptions, PartitionTupleRouting::root_tuple_slot, ModifyTableState::rootResultRelInfo, HeapTupleData::t_self, HeapTupleData::t_tableOid, TransitionCaptureState::tcs_map, TransitionCaptureState::tcs_original_insert_tuple, TriggerDesc::trig_update_before_row, TriggerDesc::trig_update_instead_row, tupconv_map_for_subplan(), TupIsNull, WCO_RLS_UPDATE_CHECK, WCO_VIEW_CHECK, and HeapUpdateFailureData::xmax.

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

933 {
934  HeapTuple tuple;
935  ResultRelInfo *resultRelInfo;
936  Relation resultRelationDesc;
937  HTSU_Result result;
939  List *recheckIndexes = NIL;
940  TupleConversionMap *saved_tcs_map = NULL;
941 
942  /*
943  * abort the operation if not running transactions
944  */
946  elog(ERROR, "cannot UPDATE during bootstrap");
947 
948  /*
949  * get the heap tuple out of the tuple table slot, making sure we have a
950  * writable copy
951  */
952  tuple = ExecMaterializeSlot(slot);
953 
954  /*
955  * get information on the (current) result relation
956  */
957  resultRelInfo = estate->es_result_relation_info;
958  resultRelationDesc = resultRelInfo->ri_RelationDesc;
959 
960  /* BEFORE ROW UPDATE Triggers */
961  if (resultRelInfo->ri_TrigDesc &&
962  resultRelInfo->ri_TrigDesc->trig_update_before_row)
963  {
964  slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
965  tupleid, oldtuple, slot);
966 
967  if (slot == NULL) /* "do nothing" */
968  return NULL;
969 
970  /* trigger might have changed tuple */
971  tuple = ExecMaterializeSlot(slot);
972  }
973 
974  /* INSTEAD OF ROW UPDATE Triggers */
975  if (resultRelInfo->ri_TrigDesc &&
976  resultRelInfo->ri_TrigDesc->trig_update_instead_row)
977  {
978  slot = ExecIRUpdateTriggers(estate, resultRelInfo,
979  oldtuple, slot);
980 
981  if (slot == NULL) /* "do nothing" */
982  return NULL;
983 
984  /* trigger might have changed tuple */
985  tuple = ExecMaterializeSlot(slot);
986  }
987  else if (resultRelInfo->ri_FdwRoutine)
988  {
989  /*
990  * update in foreign table: let the FDW do it
991  */
992  slot = resultRelInfo->ri_FdwRoutine->ExecForeignUpdate(estate,
993  resultRelInfo,
994  slot,
995  planSlot);
996 
997  if (slot == NULL) /* "do nothing" */
998  return NULL;
999 
1000  /* FDW might have changed tuple */
1001  tuple = ExecMaterializeSlot(slot);
1002 
1003  /*
1004  * AFTER ROW Triggers or RETURNING expressions might reference the
1005  * tableoid column, so initialize t_tableOid before evaluating them.
1006  */
1007  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
1008  }
1009  else
1010  {
1011  LockTupleMode lockmode;
1012  bool partition_constraint_failed;
1013 
1014  /*
1015  * Constraints might reference the tableoid column, so initialize
1016  * t_tableOid before evaluating them.
1017  */
1018  tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
1019 
1020  /*
1021  * Check any RLS UPDATE WITH CHECK policies
1022  *
1023  * If we generate a new candidate tuple after EvalPlanQual testing, we
1024  * must loop back here and recheck any RLS policies and constraints.
1025  * (We don't need to redo triggers, however. If there are any BEFORE
1026  * triggers then trigger.c will have done heap_lock_tuple to lock the
1027  * correct tuple, so there's no need to do them again.)
1028  */
1029 lreplace:;
1030 
1031  /*
1032  * If partition constraint fails, this row might get moved to another
1033  * partition, in which case we should check the RLS CHECK policy just
1034  * before inserting into the new partition, rather than doing it here.
1035  * This is because a trigger on that partition might again change the
1036  * row. So skip the WCO checks if the partition constraint fails.
1037  */
1038  partition_constraint_failed =
1039  resultRelInfo->ri_PartitionCheck &&
1040  !ExecPartitionCheck(resultRelInfo, slot, estate);
1041 
1042  if (!partition_constraint_failed &&
1043  resultRelInfo->ri_WithCheckOptions != NIL)
1044  {
1045  /*
1046  * ExecWithCheckOptions() will skip any WCOs which are not of the
1047  * kind we are looking for at this point.
1048  */
1050  resultRelInfo, slot, estate);
1051  }
1052 
1053  /*
1054  * If a partition check failed, try to move the row into the right
1055  * partition.
1056  */
1057  if (partition_constraint_failed)
1058  {
1059  bool tuple_deleted;
1060  TupleTableSlot *ret_slot;
1062  int map_index;
1063  TupleConversionMap *tupconv_map;
1064 
1065  /*
1066  * Disallow an INSERT ON CONFLICT DO UPDATE that causes the
1067  * original row to migrate to a different partition. Maybe this
1068  * can be implemented some day, but it seems a fringe feature with
1069  * little redeeming value.
1070  */
1071  if (((ModifyTable *) mtstate->ps.plan)->onConflictAction == ONCONFLICT_UPDATE)
1072  ereport(ERROR,
1073  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1074  errmsg("invalid ON UPDATE specification"),
1075  errdetail("The result tuple would appear in a different partition than the original tuple.")));
1076 
1077  /*
1078  * When an UPDATE is run on a leaf partition, we will not have
1079  * partition tuple routing set up. In that case, fail with
1080  * partition constraint violation error.
1081  */
1082  if (proute == NULL)
1083  ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
1084 
1085  /*
1086  * Row movement, part 1. Delete the tuple, but skip RETURNING
1087  * processing. We want to return rows from INSERT.
1088  */
1089  ExecDelete(mtstate, tupleid, oldtuple, planSlot, epqstate,
1090  estate, &tuple_deleted, false,
1091  false /* canSetTag */ , true /* changingPart */ );
1092 
1093  /*
1094  * For some reason if DELETE didn't happen (e.g. trigger prevented
1095  * it, or it was already deleted by self, or it was concurrently
1096  * deleted by another transaction), then we should skip the insert
1097  * as well; otherwise, an UPDATE could cause an increase in the
1098  * total number of rows across all partitions, which is clearly
1099  * wrong.
1100  *
1101  * For a normal UPDATE, the case where the tuple has been the
1102  * subject of a concurrent UPDATE or DELETE would be handled by
1103  * the EvalPlanQual machinery, but for an UPDATE that we've
1104  * translated into a DELETE from this partition and an INSERT into
1105  * some other partition, that's not available, because CTID chains
1106  * can't span relation boundaries. We mimic the semantics to a
1107  * limited extent by skipping the INSERT if the DELETE fails to
1108  * find a tuple. This ensures that two concurrent attempts to
1109  * UPDATE the same tuple at the same time can't turn one tuple
1110  * into two, and that an UPDATE of a just-deleted tuple can't
1111  * resurrect it.
1112  */
1113  if (!tuple_deleted)
1114  return NULL;
1115 
1116  /*
1117  * Updates set the transition capture map only when a new subplan
1118  * is chosen. But for inserts, it is set for each row. So after
1119  * INSERT, we need to revert back to the map created for UPDATE;
1120  * otherwise the next UPDATE will incorrectly use the one created
1121  * for INSERT. So first save the one created for UPDATE.
1122  */
1123  if (mtstate->mt_transition_capture)
1124  saved_tcs_map = mtstate->mt_transition_capture->tcs_map;
1125 
1126  /*
1127  * resultRelInfo is one of the per-subplan resultRelInfos. So we
1128  * should convert the tuple into root's tuple descriptor, since
1129  * ExecInsert() starts the search from root. The tuple conversion
1130  * map list is in the order of mtstate->resultRelInfo[], so to
1131  * retrieve the one for this resultRel, we need to know the
1132  * position of the resultRel in mtstate->resultRelInfo[].
1133  */
1134  map_index = resultRelInfo - mtstate->resultRelInfo;
1135  Assert(map_index >= 0 && map_index < mtstate->mt_nplans);
1136  tupconv_map = tupconv_map_for_subplan(mtstate, map_index);
1137  tuple = ConvertPartitionTupleSlot(tupconv_map,
1138  tuple,
1139  proute->root_tuple_slot,
1140  &slot);
1141 
1142  /*
1143  * Prepare for tuple routing, making it look like we're inserting
1144  * into the root.
1145  */
1146  Assert(mtstate->rootResultRelInfo != NULL);
1147  slot = ExecPrepareTupleRouting(mtstate, estate, proute,
1148  mtstate->rootResultRelInfo, slot);
1149 
1150  ret_slot = ExecInsert(mtstate, slot, planSlot,
1151  estate, canSetTag);
1152 
1153  /* Revert ExecPrepareTupleRouting's node change. */
1154  estate->es_result_relation_info = resultRelInfo;
1155  if (mtstate->mt_transition_capture)
1156  {
1158  mtstate->mt_transition_capture->tcs_map = saved_tcs_map;
1159  }
1160 
1161  return ret_slot;
1162  }
1163 
1164  /*
1165  * Check the constraints of the tuple. Note that we pass the same
1166  * slot for the orig_slot argument, because unlike ExecInsert(), no
1167  * tuple-routing is performed here, hence the slot remains unchanged.
1168  * We've already checked the partition constraint above; however, we
1169  * must still ensure the tuple passes all other constraints, so we
1170  * will call ExecConstraints() and have it validate all remaining
1171  * checks.
1172  */
1173  if (resultRelationDesc->rd_att->constr)
1174  ExecConstraints(resultRelInfo, slot, estate, false);
1175 
1176  /*
1177  * replace the heap tuple
1178  *
1179  * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check
1180  * that the row to be updated is visible to that snapshot, and throw a
1181  * can't-serialize error if not. This is a special-case behavior
1182  * needed for referential integrity updates in transaction-snapshot
1183  * mode transactions.
1184  */
1185  result = heap_update(resultRelationDesc, tupleid, tuple,
1186  estate->es_output_cid,
1187  estate->es_crosscheck_snapshot,
1188  true /* wait for commit */ ,
1189  &hufd, &lockmode);
1190  switch (result)
1191  {
1192  case HeapTupleSelfUpdated:
1193 
1194  /*
1195  * The target tuple was already updated or deleted by the
1196  * current command, or by a later command in the current
1197  * transaction. The former case is possible in a join UPDATE
1198  * where multiple tuples join to the same target tuple. This
1199  * is pretty questionable, but Postgres has always allowed it:
1200  * we just execute the first update action and ignore
1201  * additional update attempts.
1202  *
1203  * The latter case arises if the tuple is modified by a
1204  * command in a BEFORE trigger, or perhaps by a command in a
1205  * volatile function used in the query. In such situations we
1206  * should not ignore the update, but it is equally unsafe to
1207  * proceed. We don't want to discard the original UPDATE
1208  * while keeping the triggered actions based on it; and we
1209  * have no principled way to merge this update with the
1210  * previous ones. So throwing an error is the only safe
1211  * course.
1212  *
1213  * If a trigger actually intends this type of interaction, it
1214  * can re-execute the UPDATE (assuming it can figure out how)
1215  * and then return NULL to cancel the outer update.
1216  */
1217  if (hufd.cmax != estate->es_output_cid)
1218  ereport(ERROR,
1219  (errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
1220  errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
1221  errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
1222 
1223  /* Else, already updated by self; nothing to do */
1224  return NULL;
1225 
1226  case HeapTupleMayBeUpdated:
1227  break;
1228 
1229  case HeapTupleUpdated:
1231  ereport(ERROR,
1232  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1233  errmsg("could not serialize access due to concurrent update")));
1235  ereport(ERROR,
1236  (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
1237  errmsg("tuple to be updated was already moved to another partition due to concurrent update")));
1238 
1239  if (!ItemPointerEquals(tupleid, &hufd.ctid))
1240  {
1241  TupleTableSlot *epqslot;
1242 
1243  epqslot = EvalPlanQual(estate,
1244  epqstate,
1245  resultRelationDesc,
1246  resultRelInfo->ri_RangeTableIndex,
1247  lockmode,
1248  &hufd.ctid,
1249  hufd.xmax);
1250  if (!TupIsNull(epqslot))
1251  {
1252  *tupleid = hufd.ctid;
1253  slot = ExecFilterJunk(resultRelInfo->ri_junkFilter, epqslot);
1254  tuple = ExecMaterializeSlot(slot);
1255  goto lreplace;
1256  }
1257  }
1258  /* tuple already deleted; nothing to do */
1259  return NULL;
1260 
1261  default:
1262  elog(ERROR, "unrecognized heap_update status: %u", result);
1263  return NULL;
1264  }
1265 
1266  /*
1267  * Note: instead of having to update the old index tuples associated
1268  * with the heap tuple, all we do is form and insert new index tuples.
1269  * This is because UPDATEs are actually DELETEs and INSERTs, and index
1270  * tuple deletion is done later by VACUUM (see notes in ExecDelete).
1271  * All we do here is insert new index tuples. -cim 9/27/89
1272  */
1273 
1274  /*
1275  * insert index entries for tuple
1276  *
1277  * Note: heap_update returns the tid (location) of the new tuple in
1278  * the t_self field.
1279  *
1280  * If it's a HOT update, we mustn't insert new index entries.
1281  */
1282  if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
1283  recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
1284  estate, false, NULL, NIL);
1285  }
1286 
1287  if (canSetTag)
1288  (estate->es_processed)++;
1289 
1290  /* AFTER ROW UPDATE Triggers */
1291  ExecARUpdateTriggers(estate, resultRelInfo, tupleid, oldtuple, tuple,
1292  recheckIndexes,
1293  mtstate->operation == CMD_INSERT ?
1294  mtstate->mt_oc_transition_capture :
1295  mtstate->mt_transition_capture);
1296 
1297  list_free(recheckIndexes);
1298 
1299  /*
1300  * Check any WITH CHECK OPTION constraints from parent views. We are
1301  * required to do this after testing all constraints and uniqueness
1302  * violations per the SQL spec, so we do it after actually updating the
1303  * record in the heap and all indexes.
1304  *
1305  * ExecWithCheckOptions() will skip any WCOs which are not of the kind we
1306  * are looking for at this point.
1307  */
1308  if (resultRelInfo->ri_WithCheckOptions != NIL)
1309  ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
1310 
1311  /* Process RETURNING if present */
1312  if (resultRelInfo->ri_projectReturning)
1313  return ExecProcessReturning(resultRelInfo, slot, planSlot);
1314 
1315  return NULL;
1316 }
int ri_NumIndices
Definition: execnodes.h:400
#define NIL
Definition: pg_list.h:69
JunkFilter * ri_junkFilter
Definition: execnodes.h:439
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1060
Relation ri_RelationDesc
Definition: execnodes.h:397
int errhint(const char *fmt,...)
Definition: elog.c:987
void ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:2096
static TupleTableSlot * ExecInsert(ModifyTableState *mtstate, TupleTableSlot *slot, TupleTableSlot *planSlot, EState *estate, bool canSetTag)
CommandId es_output_cid
Definition: execnodes.h:487
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:1054
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1043
TupleTableSlot * EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, int lockmode, ItemPointer tid, TransactionId priorXmax)
Definition: execMain.c:2501
TupleTableSlot * ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, TupleTableSlot *slot)
Definition: trigger.c:2951
static TupleConversionMap * tupconv_map_for_subplan(ModifyTableState *node, int whichplan)
#define IsolationUsesXactSnapshot()
Definition: xact.h:50
Snapshot es_crosscheck_snapshot
Definition: execnodes.h:479
int errcode(int sqlerrcode)
Definition: elog.c:575
CmdType operation
Definition: execnodes.h:1037
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1044
void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, bool check_partition_constraint)
Definition: execMain.c:1962
HeapTuple tcs_original_insert_tuple
Definition: trigger.h:82
Index ri_RangeTableIndex
Definition: execnodes.h:394
TupleTableSlot * ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, TupleTableSlot *slot)
Definition: trigger.c:3110
LockTupleMode
Definition: heapam.h:38
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1036
TupleConversionMap * tcs_map
Definition: trigger.h:73
ItemPointerData t_self
Definition: htup.h:65
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1057
int errdetail(const char *fmt,...)
Definition: elog.c:873
void ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1901
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:445
#define TupIsNull(slot)
Definition: tuptable.h:146
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:421
Oid t_tableOid
Definition: htup.h:66
#define ereport(elevel, rest)
Definition: elog.h:122
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:409
void ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple fdw_trigtuple, HeapTuple newtuple, List *recheckIndexes, TransitionCaptureState *transition_capture)
Definition: trigger.c:3068
bool trig_update_instead_row
Definition: reltrigger.h:62
static TupleTableSlot * ExecProcessReturning(ResultRelInfo *resultRelInfo, TupleTableSlot *tupleSlot, TupleTableSlot *planSlot)
static TupleTableSlot * ExecPrepareTupleRouting(ModifyTableState *mtstate, EState *estate, PartitionTupleRouting *proute, ResultRelInfo *targetRelInfo, TupleTableSlot *slot)
TransactionId xmax
Definition: heapam.h:71
TupleTableSlot * ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
Definition: execJunk.c:262
List * ri_WithCheckOptions
Definition: execnodes.h:430
HeapTuple ConvertPartitionTupleSlot(TupleConversionMap *map, HeapTuple tuple, TupleTableSlot *new_slot, TupleTableSlot **p_my_slot)
#define ItemPointerIndicatesMovedPartitions(pointer)
Definition: itemptr.h:184
List * ri_PartitionCheck
Definition: execnodes.h:454
TupleDesc rd_att
Definition: rel.h:85
Plan * plan
Definition: execnodes.h:912
HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode)
Definition: heapam.c:3519
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:211
static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool *tupleDeleted, bool processReturning, bool canSetTag, bool changingPart)
#define HeapTupleIsHeapOnly(tuple)
Definition: htup_details.h:698
#define Assert(condition)
Definition: c.h:699
uint64 es_processed
Definition: execnodes.h:529
TupleConstr * constr
Definition: tupdesc.h:87
TupleTableSlot * root_tuple_slot
HeapTuple ExecMaterializeSlot(TupleTableSlot *slot)
Definition: execTuples.c:781
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:372
int errmsg(const char *fmt,...)
Definition: elog.c:797
void list_free(List *list)
Definition: list.c:1133
bool ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate)
Definition: execMain.c:1863
ItemPointerData ctid
Definition: heapam.h:70
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:407
ResultRelInfo * es_result_relation_info
Definition: execnodes.h:492

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 1585 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

1586 {
1587  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1588  ResultRelInfo *resultRelInfo = getTargetResultRelInfo(node);
1589 
1590  switch (node->operation)
1591  {
1592  case CMD_INSERT:
1593  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1595  resultRelInfo,
1596  node->mt_oc_transition_capture);
1597  ExecASInsertTriggers(node->ps.state, resultRelInfo,
1598  node->mt_transition_capture);
1599  break;
1600  case CMD_UPDATE:
1601  ExecASUpdateTriggers(node->ps.state, resultRelInfo,
1602  node->mt_transition_capture);
1603  break;
1604  case CMD_DELETE:
1605  ExecASDeleteTriggers(node->ps.state, resultRelInfo,
1606  node->mt_transition_capture);
1607  break;
1608  default:
1609  elog(ERROR, "unknown operation");
1610  break;
1611  }
1612 }
struct TransitionCaptureState * mt_oc_transition_capture
Definition: execnodes.h:1060
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
CmdType operation
Definition: execnodes.h:1037
EState * state
Definition: execnodes.h:914
void ExecASDeleteTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2719
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1036
struct TransitionCaptureState * mt_transition_capture
Definition: execnodes.h:1057
void ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2938
void ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo, TransitionCaptureState *transition_capture)
Definition: trigger.c:2504
Plan * plan
Definition: execnodes.h:912
OnConflictAction onConflictAction
Definition: plannodes.h:235
#define elog
Definition: elog.h:219

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 1526 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

1527 {
1528  ModifyTable *plan = (ModifyTable *) node->ps.plan;
1529  ResultRelInfo *resultRelInfo = node->resultRelInfo;
1530 
1531  /*
1532  * If the node modifies a partitioned table, we must fire its triggers.
1533  * Note that in that case, node->resultRelInfo points to the first leaf
1534  * partition, not the root table.
1535  */
1536  if (node->rootResultRelInfo != NULL)
1537  resultRelInfo = node->rootResultRelInfo;
1538 
1539  switch (node->operation)
1540  {
1541  case CMD_INSERT:
1542  ExecBSInsertTriggers(node->ps.state, resultRelInfo);
1543  if (plan->onConflictAction == ONCONFLICT_UPDATE)
1545  resultRelInfo);
1546  break;
1547  case CMD_UPDATE:
1548  ExecBSUpdateTriggers(node->ps.state, resultRelInfo);
1549  break;
1550  case CMD_DELETE:
1551  ExecBSDeleteTriggers(node->ps.state, resultRelInfo);
1552  break;
1553  default:
1554  elog(ERROR, "unknown operation");
1555  break;
1556  }
1557 }
void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2662
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1043
CmdType operation
Definition: execnodes.h:1037
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1044
EState * state
Definition: execnodes.h:914
#define ERROR
Definition: elog.h:43
PlanState ps
Definition: execnodes.h:1036
void ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2447
Plan * plan
Definition: execnodes.h:912
OnConflictAction onConflictAction
Definition: plannodes.h:235
void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:2878
#define elog
Definition: elog.h:219

◆ getTargetResultRelInfo()

static ResultRelInfo * getTargetResultRelInfo ( ModifyTableState node)
static

Definition at line 1569 of file nodeModifyTable.c.

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

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

1570 {
1571  /*
1572  * Note that if the node modifies a partitioned table, node->resultRelInfo
1573  * points to the first leaf partition, not the root table.
1574  */
1575  if (node->rootResultRelInfo != NULL)
1576  return node->rootResultRelInfo;
1577  else
1578  return node->resultRelInfo;
1579 }
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1043
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1044

◆ tupconv_map_for_subplan()

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

Definition at line 1880 of file nodeModifyTable.c.

References Assert, getTargetResultRelInfo(), ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_per_subplan_tupconv_maps, PartitionTupleRouting::subplan_partition_offsets, and TupConvMapForLeaf().

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

1881 {
1882  /*
1883  * If a partition-index tuple conversion map array is allocated, we need
1884  * to first get the index into the partition array. Exactly *one* of the
1885  * two arrays is allocated. This is because if there is a partition array
1886  * required, we don't require subplan-indexed array since we can translate
1887  * subplan index into partition index. And, we create a subplan-indexed
1888  * array *only* if partition-indexed array is not required.
1889  */
1890  if (mtstate->mt_per_subplan_tupconv_maps == NULL)
1891  {
1892  int leaf_index;
1893  PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
1894 
1895  /*
1896  * If subplan-indexed array is NULL, things should have been arranged
1897  * to convert the subplan index to partition index.
1898  */
1899  Assert(proute && proute->subplan_partition_offsets != NULL &&
1900  whichplan < proute->num_subplan_partition_offsets);
1901 
1902  leaf_index = proute->subplan_partition_offsets[whichplan];
1903 
1904  return TupConvMapForLeaf(proute, getTargetResultRelInfo(mtstate),
1905  leaf_index);
1906  }
1907  else
1908  {
1909  Assert(whichplan >= 0 && whichplan < mtstate->mt_nplans);
1910  return mtstate->mt_per_subplan_tupconv_maps[whichplan];
1911  }
1912 }
static ResultRelInfo * getTargetResultRelInfo(ModifyTableState *node)
TupleConversionMap * TupConvMapForLeaf(PartitionTupleRouting *proute, ResultRelInfo *rootRelInfo, int leaf_index)
#define Assert(condition)
Definition: c.h:699