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

Go to the source code of this file.

Functions

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

Function Documentation

◆ ExecCheckHeapTupleVisible()

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

Definition at line 193 of file nodeModifyTable.c.

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

Referenced by ExecCheckTIDVisible(), and ExecOnConflictUpdate().

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

◆ ExecCheckPlanOutput()

static void ExecCheckPlanOutput ( Relation  resultRel,
List targetList 
)
static

Definition at line 80 of file nodeModifyTable.c.

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

Referenced by ExecInitModifyTable().

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

◆ ExecCheckTIDVisible()

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

Definition at line 225 of file nodeModifyTable.c.

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

Referenced by ExecInsert().

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

◆ ExecDelete()

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

Definition at line 675 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

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

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 2357 of file nodeModifyTable.c.

References FdwRoutine::EndForeignModify, EvalPlanQualEnd(), ExecClearTuple(), ExecCloseIndices(), ExecDropSingleTupleTableSlot(), ExecEndNode(), ExecFreeExprContext(), heap_close, i, ModifyTableState::mt_epqstate, ModifyTableState::mt_nplans, ModifyTableState::mt_num_dispatch, ModifyTableState::mt_num_partitions, ModifyTableState::mt_partition_dispatch_info, ModifyTableState::mt_partition_tuple_slot, ModifyTableState::mt_partitions, ModifyTableState::mt_plans, NoLock, ModifyTableState::ps, PlanState::ps_ResultTupleSlot, PartitionDispatchData::reldesc, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_usesFdwDirectModify, PlanState::state, and PartitionDispatchData::tupslot.

Referenced by ExecEndNode().

2358 {
2359  int i;
2360 
2361  /*
2362  * Allow any FDWs to shut down
2363  */
2364  for (i = 0; i < node->mt_nplans; i++)
2365  {
2366  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
2367 
2368  if (!resultRelInfo->ri_usesFdwDirectModify &&
2369  resultRelInfo->ri_FdwRoutine != NULL &&
2370  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
2371  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
2372  resultRelInfo);
2373  }
2374 
2375  /*
2376  * Close all the partitioned tables, leaf partitions, and their indices
2377  *
2378  * Remember node->mt_partition_dispatch_info[0] corresponds to the root
2379  * partitioned table, which we must not try to close, because it is the
2380  * main target table of the query that will be closed by ExecEndPlan().
2381  * Also, tupslot is NULL for the root partitioned table.
2382  */
2383  for (i = 1; i < node->mt_num_dispatch; i++)
2384  {
2386 
2387  heap_close(pd->reldesc, NoLock);
2389  }
2390  for (i = 0; i < node->mt_num_partitions; i++)
2391  {
2392  ResultRelInfo *resultRelInfo = node->mt_partitions[i];
2393 
2394  ExecCloseIndices(resultRelInfo);
2395  heap_close(resultRelInfo->ri_RelationDesc, NoLock);
2396  }
2397 
2398  /* Release the standalone partition tuple descriptor, if any */
2399  if (node->mt_partition_tuple_slot)
2401 
2402  /*
2403  * Free the exprcontext
2404  */
2405  ExecFreeExprContext(&node->ps);
2406 
2407  /*
2408  * clean out the tuple table
2409  */
2411 
2412  /*
2413  * Terminate EPQ execution if active
2414  */
2415  EvalPlanQualEnd(&node->mt_epqstate);
2416 
2417  /*
2418  * shut down subplans
2419  */
2420  for (i = 0; i < node->mt_nplans; i++)
2421  ExecEndNode(node->mt_plans[i]);
2422 }
Relation ri_RelationDesc
Definition: execnodes.h:354
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:523
ResultRelInfo * resultRelInfo
Definition: execnodes.h:967
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:439
#define heap_close(r, l)
Definition: heapam.h:97
EState * state
Definition: execnodes.h:851
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:523
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:3208
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:882
PlanState ps
Definition: execnodes.h:960
bool ri_usesFdwDirectModify
Definition: execnodes.h:384
#define NoLock
Definition: lockdefs.h:34
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:216
EPQState mt_epqstate
Definition: execnodes.h:971
ResultRelInfo ** mt_partitions
Definition: execnodes.h:984
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:378
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:206
PlanState ** mt_plans
Definition: execnodes.h:964
TupleTableSlot * tupslot
Definition: execPartition.h:45
TupleTableSlot * mt_partition_tuple_slot
Definition: execnodes.h:987
int i
struct PartitionDispatchData ** mt_partition_dispatch_info
Definition: execnodes.h:979
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
Definition: execIndexing.c:224

◆ ExecInitModifyTable()

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

Definition at line 1823 of file nodeModifyTable.c.

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

Referenced by ExecInitNode().

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

◆ ExecInsert()

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

Definition at line 254 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

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

◆ ExecModifyTable()

static TupleTableSlot* ExecModifyTable ( PlanState pstate)
static

Definition at line 1568 of file nodeModifyTable.c.

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

Referenced by ExecInitModifyTable().

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

◆ ExecOnConflictUpdate()

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

Definition at line 1202 of file nodeModifyTable.c.

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

Referenced by ExecInsert().

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

◆ ExecProcessReturning()

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

Definition at line 150 of file nodeModifyTable.c.

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

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

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

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 2425 of file nodeModifyTable.c.

References elog, and ERROR.

Referenced by ExecReScan().

2426 {
2427  /*
2428  * Currently, we don't need to support rescan on ModifyTable nodes. The
2429  * semantics of that would be a bit debatable anyway.
2430  */
2431  elog(ERROR, "ExecReScanModifyTable is not implemented");
2432 }
#define ERROR
Definition: elog.h:43
#define elog
Definition: elog.h:219

◆ ExecSetupTransitionCaptureState()

static void ExecSetupTransitionCaptureState ( ModifyTableState mtstate,
EState estate 
)
static

Definition at line 1476 of file nodeModifyTable.c.

References CMD_INSERT, CMD_UPDATE, convert_tuples_by_name(), getASTriggerResultRelInfo(), gettext_noop, i, MakeTransitionCaptureState(), ModifyTableState::mt_nplans, ModifyTableState::mt_num_partitions, ModifyTableState::mt_oc_transition_capture, ModifyTableState::mt_onconflict, ModifyTableState::mt_partition_dispatch_info, ModifyTableState::mt_partition_tuple_slot, ModifyTableState::mt_partitions, ModifyTableState::mt_transition_capture, ModifyTableState::mt_transition_tupconv_maps, ONCONFLICT_UPDATE, ModifyTableState::operation, palloc0(), RelationGetDescr, RelationGetRelid, ModifyTableState::resultRelInfo, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_TrigDesc, and TransitionCaptureState::tcs_map.

Referenced by ExecInitModifyTable().

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

◆ ExecUpdate()

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

Definition at line 935 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable(), and ExecOnConflictUpdate().

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

◆ fireASTriggers()

static void fireASTriggers ( ModifyTableState node)
static

Definition at line 1443 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

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

◆ fireBSTriggers()

static void fireBSTriggers ( ModifyTableState node)
static

Definition at line 1388 of file nodeModifyTable.c.

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

Referenced by ExecModifyTable().

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

◆ getASTriggerResultRelInfo()

static ResultRelInfo* getASTriggerResultRelInfo ( ModifyTableState node)
static

Definition at line 1426 of file nodeModifyTable.c.

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

Referenced by ExecSetupTransitionCaptureState(), and fireASTriggers().

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