PostgreSQL Source Code  git master
nodeModifyTable.h File Reference
#include "nodes/execnodes.h"
Include dependency graph for nodeModifyTable.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

void ExecInitStoredGenerated (ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
 
void ExecComputeStoredGenerated (ResultRelInfo *resultRelInfo, EState *estate, TupleTableSlot *slot, CmdType cmdtype)
 
ModifyTableStateExecInitModifyTable (ModifyTable *node, EState *estate, int eflags)
 
void ExecEndModifyTable (ModifyTableState *node)
 
void ExecReScanModifyTable (ModifyTableState *node)
 
void ExecInitMergeTupleSlots (ModifyTableState *mtstate, ResultRelInfo *resultRelInfo)
 

Function Documentation

◆ ExecComputeStoredGenerated()

void ExecComputeStoredGenerated ( ResultRelInfo resultRelInfo,
EState estate,
TupleTableSlot slot,
CmdType  cmdtype 
)

Definition at line 446 of file nodeModifyTable.c.

449 {
450  Relation rel = resultRelInfo->ri_RelationDesc;
451  TupleDesc tupdesc = RelationGetDescr(rel);
452  int natts = tupdesc->natts;
453  ExprContext *econtext = GetPerTupleExprContext(estate);
454  ExprState **ri_GeneratedExprs;
455  MemoryContext oldContext;
456  Datum *values;
457  bool *nulls;
458 
459  /* We should not be called unless this is true */
460  Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
461 
462  /*
463  * Initialize the expressions if we didn't already, and check whether we
464  * can exit early because nothing needs to be computed.
465  */
466  if (cmdtype == CMD_UPDATE)
467  {
468  if (resultRelInfo->ri_GeneratedExprsU == NULL)
469  ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
470  if (resultRelInfo->ri_NumGeneratedNeededU == 0)
471  return;
472  ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
473  }
474  else
475  {
476  if (resultRelInfo->ri_GeneratedExprsI == NULL)
477  ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
478  /* Early exit is impossible given the prior Assert */
479  Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
480  ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
481  }
482 
483  oldContext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
484 
485  values = palloc(sizeof(*values) * natts);
486  nulls = palloc(sizeof(*nulls) * natts);
487 
488  slot_getallattrs(slot);
489  memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
490 
491  for (int i = 0; i < natts; i++)
492  {
493  Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
494 
495  if (ri_GeneratedExprs[i])
496  {
497  Datum val;
498  bool isnull;
499 
500  Assert(attr->attgenerated == ATTRIBUTE_GENERATED_STORED);
501 
502  econtext->ecxt_scantuple = slot;
503 
504  val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
505 
506  /*
507  * We must make a copy of val as we have no guarantees about where
508  * memory for a pass-by-reference Datum is located.
509  */
510  if (!isnull)
511  val = datumCopy(val, attr->attbyval, attr->attlen);
512 
513  values[i] = val;
514  nulls[i] = isnull;
515  }
516  else
517  {
518  if (!nulls[i])
519  values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
520  }
521  }
522 
523  ExecClearTuple(slot);
524  memcpy(slot->tts_values, values, sizeof(*values) * natts);
525  memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
526  ExecStoreVirtualTuple(slot);
527  ExecMaterializeSlot(slot);
528 
529  MemoryContextSwitchTo(oldContext);
530 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1553
#define GetPerTupleExprContext(estate)
Definition: executor.h:549
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:554
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:332
long val
Definition: informix.c:664
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
void * palloc(Size size)
Definition: mcxt.c:1226
void ExecInitStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
@ CMD_UPDATE
Definition: nodes.h:277
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
uintptr_t Datum
Definition: postgres.h:64
#define RelationGetDescr(relation)
Definition: rel.h:530
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:248
Relation ri_RelationDesc
Definition: execnodes.h:449
ExprState ** ri_GeneratedExprsI
Definition: execnodes.h:521
int ri_NumGeneratedNeededU
Definition: execnodes.h:526
ExprState ** ri_GeneratedExprsU
Definition: execnodes.h:522
int ri_NumGeneratedNeededI
Definition: execnodes.h:525
bool has_generated_stored
Definition: tupdesc.h:45
TupleConstr * constr
Definition: tupdesc.h:85
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:432
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:361
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:450

References Assert(), CMD_UPDATE, TupleDescData::constr, datumCopy(), ExprContext::ecxt_scantuple, ExecClearTuple(), ExecEvalExpr(), ExecInitStoredGenerated(), ExecMaterializeSlot(), ExecStoreVirtualTuple(), GetPerTupleExprContext, GetPerTupleMemoryContext, TupleConstr::has_generated_stored, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc(), RelationGetDescr, ResultRelInfo::ri_GeneratedExprsI, ResultRelInfo::ri_GeneratedExprsU, ResultRelInfo::ri_NumGeneratedNeededI, ResultRelInfo::ri_NumGeneratedNeededU, ResultRelInfo::ri_RelationDesc, slot_getallattrs(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, TupleDescAttr, val, and values.

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

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 4424 of file nodeModifyTable.c.

4425 {
4426  int i;
4427 
4428  /*
4429  * Allow any FDWs to shut down
4430  */
4431  for (i = 0; i < node->mt_nrels; i++)
4432  {
4433  int j;
4434  ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
4435 
4436  if (!resultRelInfo->ri_usesFdwDirectModify &&
4437  resultRelInfo->ri_FdwRoutine != NULL &&
4438  resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
4439  resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
4440  resultRelInfo);
4441 
4442  /*
4443  * Cleanup the initialized batch slots. This only matters for FDWs
4444  * with batching, but the other cases will have ri_NumSlotsInitialized
4445  * == 0.
4446  */
4447  for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
4448  {
4449  ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
4450  ExecDropSingleTupleTableSlot(resultRelInfo->ri_PlanSlots[j]);
4451  }
4452  }
4453 
4454  /*
4455  * Close all the partitioned tables, leaf partitions, and their indices
4456  * and release the slot used for tuple routing, if set.
4457  */
4458  if (node->mt_partition_tuple_routing)
4459  {
4461 
4462  if (node->mt_root_tuple_slot)
4464  }
4465 
4466  /*
4467  * Terminate EPQ execution if active
4468  */
4469  EvalPlanQualEnd(&node->mt_epqstate);
4470 
4471  /*
4472  * shut down subplan
4473  */
4474  ExecEndNode(outerPlanState(node));
4475 }
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:3006
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:557
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1255
#define outerPlanState(node)
Definition: execnodes.h:1132
int j
Definition: isn.c:74
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1279
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1310
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1307
EPQState mt_epqstate
Definition: execnodes.h:1289
PlanState ps
Definition: execnodes.h:1274
EState * state
Definition: execnodes.h:1038
TupleTableSlot ** ri_Slots
Definition: execnodes.h:508
int ri_NumSlotsInitialized
Definition: execnodes.h:506
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:496
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:509
bool ri_usesFdwDirectModify
Definition: execnodes.h:502

References FdwRoutine::EndForeignModify, EvalPlanQualEnd(), ExecCleanupTupleRouting(), ExecDropSingleTupleTableSlot(), ExecEndNode(), i, j, ModifyTableState::mt_epqstate, ModifyTableState::mt_nrels, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_root_tuple_slot, outerPlanState, ModifyTableState::ps, ModifyTableState::resultRelInfo, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_NumSlotsInitialized, ResultRelInfo::ri_PlanSlots, ResultRelInfo::ri_Slots, ResultRelInfo::ri_usesFdwDirectModify, and PlanState::state.

Referenced by ExecEndNode().

◆ ExecInitMergeTupleSlots()

void ExecInitMergeTupleSlots ( ModifyTableState mtstate,
ResultRelInfo resultRelInfo 
)

Definition at line 3364 of file nodeModifyTable.c.

3366 {
3367  EState *estate = mtstate->ps.state;
3368 
3369  Assert(!resultRelInfo->ri_projectNewInfoValid);
3370 
3371  resultRelInfo->ri_oldTupleSlot =
3372  table_slot_create(resultRelInfo->ri_RelationDesc,
3373  &estate->es_tupleTable);
3374  resultRelInfo->ri_newTupleSlot =
3375  table_slot_create(resultRelInfo->ri_RelationDesc,
3376  &estate->es_tupleTable);
3377  resultRelInfo->ri_projectNewInfoValid = true;
3378 }
List * es_tupleTable
Definition: execnodes.h:660
bool ri_projectNewInfoValid
Definition: execnodes.h:476
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:474
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:472
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91

References Assert(), EState::es_tupleTable, ModifyTableState::ps, ResultRelInfo::ri_newTupleSlot, ResultRelInfo::ri_oldTupleSlot, ResultRelInfo::ri_projectNewInfoValid, ResultRelInfo::ri_RelationDesc, PlanState::state, and table_slot_create().

Referenced by ExecInitMerge(), and ExecInitPartitionInfo().

◆ ExecInitModifyTable()

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

Definition at line 3942 of file nodeModifyTable.c.

3943 {
3944  ModifyTableState *mtstate;
3945  Plan *subplan = outerPlan(node);
3946  CmdType operation = node->operation;
3947  int nrels = list_length(node->resultRelations);
3948  ResultRelInfo *resultRelInfo;
3949  List *arowmarks;
3950  ListCell *l;
3951  int i;
3952  Relation rel;
3953 
3954  /* check for unsupported flags */
3955  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
3956 
3957  /*
3958  * create state structure
3959  */
3960  mtstate = makeNode(ModifyTableState);
3961  mtstate->ps.plan = (Plan *) node;
3962  mtstate->ps.state = estate;
3963  mtstate->ps.ExecProcNode = ExecModifyTable;
3964 
3965  mtstate->operation = operation;
3966  mtstate->canSetTag = node->canSetTag;
3967  mtstate->mt_done = false;
3968 
3969  mtstate->mt_nrels = nrels;
3970  mtstate->resultRelInfo = (ResultRelInfo *)
3971  palloc(nrels * sizeof(ResultRelInfo));
3972 
3973  mtstate->mt_merge_inserted = 0;
3974  mtstate->mt_merge_updated = 0;
3975  mtstate->mt_merge_deleted = 0;
3976 
3977  /*----------
3978  * Resolve the target relation. This is the same as:
3979  *
3980  * - the relation for which we will fire FOR STATEMENT triggers,
3981  * - the relation into whose tuple format all captured transition tuples
3982  * must be converted, and
3983  * - the root partitioned table used for tuple routing.
3984  *
3985  * If it's a partitioned or inherited table, the root partition or
3986  * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
3987  * given explicitly in node->rootRelation. Otherwise, the target relation
3988  * is the sole relation in the node->resultRelations list.
3989  *----------
3990  */
3991  if (node->rootRelation > 0)
3992  {
3994  ExecInitResultRelation(estate, mtstate->rootResultRelInfo,
3995  node->rootRelation);
3996  }
3997  else
3998  {
3999  Assert(list_length(node->resultRelations) == 1);
4000  mtstate->rootResultRelInfo = mtstate->resultRelInfo;
4001  ExecInitResultRelation(estate, mtstate->resultRelInfo,
4002  linitial_int(node->resultRelations));
4003  }
4004 
4005  /* set up epqstate with dummy subplan data for the moment */
4006  EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
4007  node->epqParam, node->resultRelations);
4008  mtstate->fireBSTriggers = true;
4009 
4010  /*
4011  * Build state for collecting transition tuples. This requires having a
4012  * valid trigger query context, so skip it in explain-only mode.
4013  */
4014  if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
4015  ExecSetupTransitionCaptureState(mtstate, estate);
4016 
4017  /*
4018  * Open all the result relations and initialize the ResultRelInfo structs.
4019  * (But root relation was initialized above, if it's part of the array.)
4020  * We must do this before initializing the subplan, because direct-modify
4021  * FDWs expect their ResultRelInfos to be available.
4022  */
4023  resultRelInfo = mtstate->resultRelInfo;
4024  i = 0;
4025  foreach(l, node->resultRelations)
4026  {
4027  Index resultRelation = lfirst_int(l);
4028 
4029  if (resultRelInfo != mtstate->rootResultRelInfo)
4030  {
4031  ExecInitResultRelation(estate, resultRelInfo, resultRelation);
4032 
4033  /*
4034  * For child result relations, store the root result relation
4035  * pointer. We do so for the convenience of places that want to
4036  * look at the query's original target relation but don't have the
4037  * mtstate handy.
4038  */
4039  resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
4040  }
4041 
4042  /* Initialize the usesFdwDirectModify flag */
4043  resultRelInfo->ri_usesFdwDirectModify =
4045 
4046  /*
4047  * Verify result relation is a valid target for the current operation
4048  */
4049  CheckValidResultRel(resultRelInfo, operation);
4050 
4051  resultRelInfo++;
4052  i++;
4053  }
4054 
4055  /*
4056  * Now we may initialize the subplan.
4057  */
4058  outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
4059 
4060  /*
4061  * Do additional per-result-relation initialization.
4062  */
4063  for (i = 0; i < nrels; i++)
4064  {
4065  resultRelInfo = &mtstate->resultRelInfo[i];
4066 
4067  /* Let FDWs init themselves for foreign-table result rels */
4068  if (!resultRelInfo->ri_usesFdwDirectModify &&
4069  resultRelInfo->ri_FdwRoutine != NULL &&
4070  resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
4071  {
4072  List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
4073 
4074  resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
4075  resultRelInfo,
4076  fdw_private,
4077  i,
4078  eflags);
4079  }
4080 
4081  /*
4082  * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
4083  * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
4084  * tables, the FDW might have created additional junk attr(s), but
4085  * those are no concern of ours.
4086  */
4087  if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4088  operation == CMD_MERGE)
4089  {
4090  char relkind;
4091 
4092  relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4093  if (relkind == RELKIND_RELATION ||
4094  relkind == RELKIND_MATVIEW ||
4095  relkind == RELKIND_PARTITIONED_TABLE)
4096  {
4097  resultRelInfo->ri_RowIdAttNo =
4098  ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
4099  if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4100  elog(ERROR, "could not find junk ctid column");
4101  }
4102  else if (relkind == RELKIND_FOREIGN_TABLE)
4103  {
4104  /*
4105  * We don't support MERGE with foreign tables for now. (It's
4106  * problematic because the implementation uses CTID.)
4107  */
4108  Assert(operation != CMD_MERGE);
4109 
4110  /*
4111  * When there is a row-level trigger, there should be a
4112  * wholerow attribute. We also require it to be present in
4113  * UPDATE and MERGE, so we can get the values of unchanged
4114  * columns.
4115  */
4116  resultRelInfo->ri_RowIdAttNo =
4118  "wholerow");
4119  if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
4120  !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4121  elog(ERROR, "could not find junk wholerow column");
4122  }
4123  else
4124  {
4125  /* No support for MERGE */
4126  Assert(operation != CMD_MERGE);
4127  /* Other valid target relkinds must provide wholerow */
4128  resultRelInfo->ri_RowIdAttNo =
4130  "wholerow");
4131  if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4132  elog(ERROR, "could not find junk wholerow column");
4133  }
4134  }
4135  }
4136 
4137  /*
4138  * If this is an inherited update/delete/merge, there will be a junk
4139  * attribute named "tableoid" present in the subplan's targetlist. It
4140  * will be used to identify the result relation for a given tuple to be
4141  * updated/deleted/merged.
4142  */
4143  mtstate->mt_resultOidAttno =
4144  ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
4145  Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || nrels == 1);
4146  mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
4147  mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
4148 
4149  /* Get the root target relation */
4150  rel = mtstate->rootResultRelInfo->ri_RelationDesc;
4151 
4152  /*
4153  * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
4154  * or MERGE might need this too, but only if it actually moves tuples
4155  * between partitions; in that case setup is done by
4156  * ExecCrossPartitionUpdate.
4157  */
4158  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
4159  operation == CMD_INSERT)
4160  mtstate->mt_partition_tuple_routing =
4161  ExecSetupPartitionTupleRouting(estate, rel);
4162 
4163  /*
4164  * Initialize any WITH CHECK OPTION constraints if needed.
4165  */
4166  resultRelInfo = mtstate->resultRelInfo;
4167  foreach(l, node->withCheckOptionLists)
4168  {
4169  List *wcoList = (List *) lfirst(l);
4170  List *wcoExprs = NIL;
4171  ListCell *ll;
4172 
4173  foreach(ll, wcoList)
4174  {
4175  WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
4176  ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
4177  &mtstate->ps);
4178 
4179  wcoExprs = lappend(wcoExprs, wcoExpr);
4180  }
4181 
4182  resultRelInfo->ri_WithCheckOptions = wcoList;
4183  resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4184  resultRelInfo++;
4185  }
4186 
4187  /*
4188  * Initialize RETURNING projections if needed.
4189  */
4190  if (node->returningLists)
4191  {
4192  TupleTableSlot *slot;
4193  ExprContext *econtext;
4194 
4195  /*
4196  * Initialize result tuple slot and assign its rowtype using the first
4197  * RETURNING list. We assume the rest will look the same.
4198  */
4199  mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
4200 
4201  /* Set up a slot for the output of the RETURNING projection(s) */
4203  slot = mtstate->ps.ps_ResultTupleSlot;
4204 
4205  /* Need an econtext too */
4206  if (mtstate->ps.ps_ExprContext == NULL)
4207  ExecAssignExprContext(estate, &mtstate->ps);
4208  econtext = mtstate->ps.ps_ExprContext;
4209 
4210  /*
4211  * Build a projection for each result rel.
4212  */
4213  resultRelInfo = mtstate->resultRelInfo;
4214  foreach(l, node->returningLists)
4215  {
4216  List *rlist = (List *) lfirst(l);
4217 
4218  resultRelInfo->ri_returningList = rlist;
4219  resultRelInfo->ri_projectReturning =
4220  ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
4221  resultRelInfo->ri_RelationDesc->rd_att);
4222  resultRelInfo++;
4223  }
4224  }
4225  else
4226  {
4227  /*
4228  * We still must construct a dummy result tuple type, because InitPlan
4229  * expects one (maybe should change that?).
4230  */
4231  mtstate->ps.plan->targetlist = NIL;
4232  ExecInitResultTypeTL(&mtstate->ps);
4233 
4234  mtstate->ps.ps_ExprContext = NULL;
4235  }
4236 
4237  /* Set the list of arbiter indexes if needed for ON CONFLICT */
4238  resultRelInfo = mtstate->resultRelInfo;
4239  if (node->onConflictAction != ONCONFLICT_NONE)
4240  {
4241  /* insert may only have one relation, inheritance is not expanded */
4242  Assert(nrels == 1);
4243  resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
4244  }
4245 
4246  /*
4247  * If needed, Initialize target list, projection and qual for ON CONFLICT
4248  * DO UPDATE.
4249  */
4250  if (node->onConflictAction == ONCONFLICT_UPDATE)
4251  {
4253  ExprContext *econtext;
4254  TupleDesc relationDesc;
4255 
4256  /* already exists if created by RETURNING processing above */
4257  if (mtstate->ps.ps_ExprContext == NULL)
4258  ExecAssignExprContext(estate, &mtstate->ps);
4259 
4260  econtext = mtstate->ps.ps_ExprContext;
4261  relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
4262 
4263  /* create state for DO UPDATE SET operation */
4264  resultRelInfo->ri_onConflict = onconfl;
4265 
4266  /* initialize slot for the existing tuple */
4267  onconfl->oc_Existing =
4268  table_slot_create(resultRelInfo->ri_RelationDesc,
4269  &mtstate->ps.state->es_tupleTable);
4270 
4271  /*
4272  * Create the tuple slot for the UPDATE SET projection. We want a slot
4273  * of the table's type here, because the slot will be used to insert
4274  * into the table, and for RETURNING processing - which may access
4275  * system attributes.
4276  */
4277  onconfl->oc_ProjSlot =
4278  table_slot_create(resultRelInfo->ri_RelationDesc,
4279  &mtstate->ps.state->es_tupleTable);
4280 
4281  /* build UPDATE SET projection state */
4282  onconfl->oc_ProjInfo =
4284  true,
4285  node->onConflictCols,
4286  relationDesc,
4287  econtext,
4288  onconfl->oc_ProjSlot,
4289  &mtstate->ps);
4290 
4291  /* initialize state to evaluate the WHERE clause, if any */
4292  if (node->onConflictWhere)
4293  {
4294  ExprState *qualexpr;
4295 
4296  qualexpr = ExecInitQual((List *) node->onConflictWhere,
4297  &mtstate->ps);
4298  onconfl->oc_WhereClause = qualexpr;
4299  }
4300  }
4301 
4302  /*
4303  * If we have any secondary relations in an UPDATE or DELETE, they need to
4304  * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
4305  * EvalPlanQual mechanism needs to be told about them. This also goes for
4306  * the source relations in a MERGE. Locate the relevant ExecRowMarks.
4307  */
4308  arowmarks = NIL;
4309  foreach(l, node->rowMarks)
4310  {
4312  ExecRowMark *erm;
4313  ExecAuxRowMark *aerm;
4314 
4315  /* ignore "parent" rowmarks; they are irrelevant at runtime */
4316  if (rc->isParent)
4317  continue;
4318 
4319  /* Find ExecRowMark and build ExecAuxRowMark */
4320  erm = ExecFindRowMark(estate, rc->rti, false);
4321  aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
4322  arowmarks = lappend(arowmarks, aerm);
4323  }
4324 
4325  /* For a MERGE command, initialize its state */
4326  if (mtstate->operation == CMD_MERGE)
4327  ExecInitMerge(mtstate, estate);
4328 
4329  EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
4330 
4331  /*
4332  * If there are a lot of result relations, use a hash table to speed the
4333  * lookups. If there are not a lot, a simple linear search is faster.
4334  *
4335  * It's not clear where the threshold is, but try 64 for starters. In a
4336  * debugging build, use a small threshold so that we get some test
4337  * coverage of both code paths.
4338  */
4339 #ifdef USE_ASSERT_CHECKING
4340 #define MT_NRELS_HASH 4
4341 #else
4342 #define MT_NRELS_HASH 64
4343 #endif
4344  if (nrels >= MT_NRELS_HASH)
4345  {
4346  HASHCTL hash_ctl;
4347 
4348  hash_ctl.keysize = sizeof(Oid);
4349  hash_ctl.entrysize = sizeof(MTTargetRelLookup);
4350  hash_ctl.hcxt = CurrentMemoryContext;
4351  mtstate->mt_resultOidHash =
4352  hash_create("ModifyTable target hash",
4353  nrels, &hash_ctl,
4355  for (i = 0; i < nrels; i++)
4356  {
4357  Oid hashkey;
4358  MTTargetRelLookup *mtlookup;
4359  bool found;
4360 
4361  resultRelInfo = &mtstate->resultRelInfo[i];
4362  hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
4363  mtlookup = (MTTargetRelLookup *)
4364  hash_search(mtstate->mt_resultOidHash, &hashkey,
4365  HASH_ENTER, &found);
4366  Assert(!found);
4367  mtlookup->relationIndex = i;
4368  }
4369  }
4370  else
4371  mtstate->mt_resultOidHash = NULL;
4372 
4373  /*
4374  * Determine if the FDW supports batch insert and determine the batch size
4375  * (a FDW may support batching, but it may be disabled for the
4376  * server/table).
4377  *
4378  * We only do this for INSERT, so that for UPDATE/DELETE the batch size
4379  * remains set to 0.
4380  */
4381  if (operation == CMD_INSERT)
4382  {
4383  /* insert may only have one relation, inheritance is not expanded */
4384  Assert(nrels == 1);
4385  resultRelInfo = mtstate->resultRelInfo;
4386  if (!resultRelInfo->ri_usesFdwDirectModify &&
4387  resultRelInfo->ri_FdwRoutine != NULL &&
4388  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
4389  resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
4390  {
4391  resultRelInfo->ri_BatchSize =
4392  resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
4393  Assert(resultRelInfo->ri_BatchSize >= 1);
4394  }
4395  else
4396  resultRelInfo->ri_BatchSize = 1;
4397  }
4398 
4399  /*
4400  * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
4401  * to estate->es_auxmodifytables so that it will be run to completion by
4402  * ExecPostprocessPlan. (It'd actually work fine to add the primary
4403  * ModifyTable node too, but there's no need.) Note the use of lcons not
4404  * lappend: we need later-initialized ModifyTable nodes to be shut down
4405  * before earlier ones. This ensures that we don't throw away RETURNING
4406  * rows that need to be seen by a later CTE subplan.
4407  */
4408  if (!mtstate->canSetTag)
4409  estate->es_auxmodifytables = lcons(mtstate,
4410  estate->es_auxmodifytables);
4411 
4412  return mtstate;
4413 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:460
unsigned int Index
Definition: c.h:603
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:953
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:350
#define ERROR
Definition: elog.h:39
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:518
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:214
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:358
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam, List *resultRelations)
Definition: execMain.c:2563
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2424
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2605
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2401
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation)
Definition: execMain.c:1024
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1756
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1800
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:819
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:488
#define EXEC_FLAG_BACKWARD
Definition: executor.h:68
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:65
#define EXEC_FLAG_MARK
Definition: executor.h:69
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
List * lappend(List *list, void *datum)
Definition: list.c:338
List * lcons(void *datum, List *list)
Definition: list.c:494
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
static void ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate)
static TupleTableSlot * ExecModifyTable(PlanState *pstate)
struct MTTargetRelLookup MTTargetRelLookup
#define MT_NRELS_HASH
static void ExecInitMerge(ModifyTableState *mtstate, EState *estate)
@ ONCONFLICT_NONE
Definition: nodes.h:428
@ ONCONFLICT_UPDATE
Definition: nodes.h:430
CmdType
Definition: nodes.h:274
@ CMD_MERGE
Definition: nodes.h:280
@ CMD_INSERT
Definition: nodes.h:278
@ CMD_DELETE
Definition: nodes.h:279
#define makeNode(_type_)
Definition: nodes.h:176
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define lfirst_int(lc)
Definition: pg_list.h:173
#define linitial_int(l)
Definition: pg_list.h:179
#define linitial(l)
Definition: pg_list.h:178
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define outerPlan(node)
Definition: plannodes.h:182
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelid(relation)
Definition: rel.h:504
List * es_auxmodifytables
Definition: execnodes.h:675
BeginForeignModify_function BeginForeignModify
Definition: fdwapi.h:231
ExecForeignBatchInsert_function ExecForeignBatchInsert
Definition: fdwapi.h:233
GetForeignModifyBatchSize_function GetForeignModifyBatchSize
Definition: fdwapi.h:234
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: pg_list.h:54
CmdType operation
Definition: execnodes.h:1275
double mt_merge_deleted
Definition: execnodes.h:1324
double mt_merge_inserted
Definition: execnodes.h:1322
double mt_merge_updated
Definition: execnodes.h:1323
HTAB * mt_resultOidHash
Definition: execnodes.h:1301
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1287
List * arbiterIndexes
Definition: plannodes.h:246
List * onConflictCols
Definition: plannodes.h:248
CmdType operation
Definition: plannodes.h:232
int epqParam
Definition: plannodes.h:244
List * resultRelations
Definition: plannodes.h:237
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:242
List * onConflictSet
Definition: plannodes.h:247
bool canSetTag
Definition: plannodes.h:233
List * fdwPrivLists
Definition: plannodes.h:241
List * returningLists
Definition: plannodes.h:240
List * withCheckOptionLists
Definition: plannodes.h:239
Index rootRelation
Definition: plannodes.h:235
Node * onConflictWhere
Definition: plannodes.h:249
List * rowMarks
Definition: plannodes.h:243
OnConflictAction onConflictAction
Definition: plannodes.h:245
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:403
TupleTableSlot * oc_Existing
Definition: execnodes.h:402
ExprState * oc_WhereClause
Definition: execnodes.h:405
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:404
bool isParent
Definition: plannodes.h:1387
Plan * plan
Definition: execnodes.h:1036
ExprContext * ps_ExprContext
Definition: execnodes.h:1075
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1074
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1042
List * targetlist
Definition: plannodes.h:152
TupleDesc rd_att
Definition: rel.h:112
Form_pg_class rd_rel
Definition: rel.h:111
OnConflictSetState * ri_onConflict
Definition: execnodes.h:538
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:535
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:573
List * ri_WithCheckOptions
Definition: execnodes.h:512
List * ri_WithCheckOptionExprs
Definition: execnodes.h:515
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:532
List * ri_returningList
Definition: execnodes.h:529
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:464
int ri_BatchSize
Definition: execnodes.h:507

References ModifyTable::arbiterIndexes, Assert(), AttributeNumberIsValid, FdwRoutine::BeginForeignModify, bms_is_member(), ModifyTableState::canSetTag, ModifyTable::canSetTag, CheckValidResultRel(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_UPDATE, CurrentMemoryContext, elog(), HASHCTL::entrysize, ModifyTable::epqParam, ERROR, EState::es_auxmodifytables, EState::es_tupleTable, EvalPlanQualInit(), EvalPlanQualSetPlan(), EXEC_FLAG_BACKWARD, EXEC_FLAG_EXPLAIN_ONLY, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecBuildAuxRowMark(), ExecBuildProjectionInfo(), ExecBuildUpdateProjection(), ExecFindJunkAttributeInTlist(), ExecFindRowMark(), FdwRoutine::ExecForeignBatchInsert, ExecInitMerge(), ExecInitNode(), ExecInitQual(), ExecInitResultRelation(), ExecInitResultTupleSlotTL(), ExecInitResultTypeTL(), ExecModifyTable(), PlanState::ExecProcNode, ExecSetupPartitionTupleRouting(), ExecSetupTransitionCaptureState(), ModifyTable::fdwDirectModifyPlans, ModifyTable::fdwPrivLists, ModifyTableState::fireBSTriggers, FdwRoutine::GetForeignModifyBatchSize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_ENTER, hash_search(), HASHCTL::hcxt, i, InvalidOid, PlanRowMark::isParent, HASHCTL::keysize, lappend(), lcons(), lfirst, lfirst_int, lfirst_node, linitial, linitial_int, list_length(), list_nth(), makeNode, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, ModifyTableState::mt_merge_updated, ModifyTableState::mt_nrels, MT_NRELS_HASH, ModifyTableState::mt_partition_tuple_routing, ModifyTableState::mt_resultOidAttno, ModifyTableState::mt_resultOidHash, NIL, OnConflictSetState::oc_Existing, OnConflictSetState::oc_ProjInfo, OnConflictSetState::oc_ProjSlot, OnConflictSetState::oc_WhereClause, ONCONFLICT_NONE, ONCONFLICT_UPDATE, ModifyTable::onConflictAction, ModifyTable::onConflictCols, ModifyTable::onConflictSet, ModifyTable::onConflictWhere, ModifyTableState::operation, ModifyTable::operation, outerPlan, outerPlanState, palloc(), PlanState::plan, ModifyTableState::ps, PlanState::ps_ExprContext, PlanState::ps_ResultTupleSlot, WithCheckOption::qual, RelationData::rd_att, RelationData::rd_rel, RelationGetRelid, MTTargetRelLookup::relationIndex, ModifyTable::resultRelations, ModifyTableState::resultRelInfo, ModifyTable::returningLists, ResultRelInfo::ri_BatchSize, ResultRelInfo::ri_FdwRoutine, ResultRelInfo::ri_onConflict, ResultRelInfo::ri_onConflictArbiterIndexes, ResultRelInfo::ri_projectReturning, ResultRelInfo::ri_RelationDesc, ResultRelInfo::ri_returningList, ResultRelInfo::ri_RootResultRelInfo, ResultRelInfo::ri_RowIdAttNo, ResultRelInfo::ri_usesFdwDirectModify, ResultRelInfo::ri_WithCheckOptionExprs, ResultRelInfo::ri_WithCheckOptions, ModifyTable::rootRelation, ModifyTableState::rootResultRelInfo, ModifyTable::rowMarks, PlanRowMark::rti, PlanState::state, table_slot_create(), Plan::targetlist, TTSOpsVirtual, and ModifyTable::withCheckOptionLists.

Referenced by ExecInitNode().

◆ ExecInitStoredGenerated()

void ExecInitStoredGenerated ( ResultRelInfo resultRelInfo,
EState estate,
CmdType  cmdtype 
)

Definition at line 346 of file nodeModifyTable.c.

349 {
350  Relation rel = resultRelInfo->ri_RelationDesc;
351  TupleDesc tupdesc = RelationGetDescr(rel);
352  int natts = tupdesc->natts;
353  ExprState **ri_GeneratedExprs;
354  int ri_NumGeneratedNeeded;
355  Bitmapset *updatedCols;
356  MemoryContext oldContext;
357 
358  /* Nothing to do if no generated columns */
359  if (!(tupdesc->constr && tupdesc->constr->has_generated_stored))
360  return;
361 
362  /*
363  * In an UPDATE, we can skip computing any generated columns that do not
364  * depend on any UPDATE target column. But if there is a BEFORE ROW
365  * UPDATE trigger, we cannot skip because the trigger might change more
366  * columns.
367  */
368  if (cmdtype == CMD_UPDATE &&
369  !(rel->trigdesc && rel->trigdesc->trig_update_before_row))
370  updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
371  else
372  updatedCols = NULL;
373 
374  /*
375  * Make sure these data structures are built in the per-query memory
376  * context so they'll survive throughout the query.
377  */
378  oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
379 
380  ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
381  ri_NumGeneratedNeeded = 0;
382 
383  for (int i = 0; i < natts; i++)
384  {
385  if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
386  {
387  Expr *expr;
388 
389  /* Fetch the GENERATED AS expression tree */
390  expr = (Expr *) build_column_default(rel, i + 1);
391  if (expr == NULL)
392  elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
393  i + 1, RelationGetRelationName(rel));
394 
395  /*
396  * If it's an update with a known set of update target columns,
397  * see if we can skip the computation.
398  */
399  if (updatedCols)
400  {
401  Bitmapset *attrs_used = NULL;
402 
403  pull_varattnos((Node *) expr, 1, &attrs_used);
404 
405  if (!bms_overlap(updatedCols, attrs_used))
406  continue; /* need not update this column */
407  }
408 
409  /* No luck, so prepare the expression for execution */
410  ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
411  ri_NumGeneratedNeeded++;
412 
413  /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
414  if (cmdtype == CMD_UPDATE)
415  resultRelInfo->ri_extraUpdatedCols =
416  bms_add_member(resultRelInfo->ri_extraUpdatedCols,
418  }
419  }
420 
421  /* Save in appropriate set of fields */
422  if (cmdtype == CMD_UPDATE)
423  {
424  /* Don't call twice */
425  Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
426 
427  resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
428  resultRelInfo->ri_NumGeneratedNeededU = ri_NumGeneratedNeeded;
429  }
430  else
431  {
432  /* Don't call twice */
433  Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
434 
435  resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
436  resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
437  }
438 
439  MemoryContextSwitchTo(oldContext);
440 }
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:753
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:527
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:736
Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition: execUtils.c:1293
void * palloc0(Size size)
Definition: mcxt.c:1257
#define RelationGetRelationName(relation)
Definition: rel.h:538
Node * build_column_default(Relation rel, int attrno)
MemoryContext es_query_cxt
Definition: execnodes.h:658
Definition: nodes.h:129
TriggerDesc * trigdesc
Definition: rel.h:117
Bitmapset * ri_extraUpdatedCols
Definition: execnodes.h:467
bool trig_update_before_row
Definition: reltrigger.h:61
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:291

References Assert(), bms_add_member(), bms_overlap(), build_column_default(), CMD_UPDATE, TupleDescData::constr, elog(), ERROR, EState::es_query_cxt, ExecGetUpdatedCols(), ExecPrepareExpr(), FirstLowInvalidHeapAttributeNumber, TupleConstr::has_generated_stored, i, MemoryContextSwitchTo(), TupleDescData::natts, palloc0(), pull_varattnos(), RelationGetDescr, RelationGetRelationName, ResultRelInfo::ri_extraUpdatedCols, ResultRelInfo::ri_GeneratedExprsI, ResultRelInfo::ri_GeneratedExprsU, ResultRelInfo::ri_NumGeneratedNeededI, ResultRelInfo::ri_NumGeneratedNeededU, ResultRelInfo::ri_RelationDesc, TriggerDesc::trig_update_before_row, RelationData::trigdesc, and TupleDescAttr.

Referenced by ExecComputeStoredGenerated(), and ExecGetExtraUpdatedCols().

◆ ExecReScanModifyTable()

void ExecReScanModifyTable ( ModifyTableState node)

Definition at line 4478 of file nodeModifyTable.c.

4479 {
4480  /*
4481  * Currently, we don't need to support rescan on ModifyTable nodes. The
4482  * semantics of that would be a bit debatable anyway.
4483  */
4484  elog(ERROR, "ExecReScanModifyTable is not implemented");
4485 }

References elog(), and ERROR.

Referenced by ExecReScan().