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

509{
510 Relation rel = resultRelInfo->ri_RelationDesc;
511 TupleDesc tupdesc = RelationGetDescr(rel);
512 int natts = tupdesc->natts;
513 ExprContext *econtext = GetPerTupleExprContext(estate);
514 ExprState **ri_GeneratedExprs;
515 MemoryContext oldContext;
516 Datum *values;
517 bool *nulls;
518
519 /* We should not be called unless this is true */
520 Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
521
522 /*
523 * Initialize the expressions if we didn't already, and check whether we
524 * can exit early because nothing needs to be computed.
525 */
526 if (cmdtype == CMD_UPDATE)
527 {
528 if (resultRelInfo->ri_GeneratedExprsU == NULL)
529 ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
530 if (resultRelInfo->ri_NumGeneratedNeededU == 0)
531 return;
532 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
533 }
534 else
535 {
536 if (resultRelInfo->ri_GeneratedExprsI == NULL)
537 ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
538 /* Early exit is impossible given the prior Assert */
539 Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
540 ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
541 }
542
544
545 values = palloc(sizeof(*values) * natts);
546 nulls = palloc(sizeof(*nulls) * natts);
547
548 slot_getallattrs(slot);
549 memcpy(nulls, slot->tts_isnull, sizeof(*nulls) * natts);
550
551 for (int i = 0; i < natts; i++)
552 {
553 CompactAttribute *attr = TupleDescCompactAttr(tupdesc, i);
554
555 if (ri_GeneratedExprs[i])
556 {
557 Datum val;
558 bool isnull;
559
560 Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
561
562 econtext->ecxt_scantuple = slot;
563
564 val = ExecEvalExpr(ri_GeneratedExprs[i], econtext, &isnull);
565
566 /*
567 * We must make a copy of val as we have no guarantees about where
568 * memory for a pass-by-reference Datum is located.
569 */
570 if (!isnull)
571 val = datumCopy(val, attr->attbyval, attr->attlen);
572
573 values[i] = val;
574 nulls[i] = isnull;
575 }
576 else
577 {
578 if (!nulls[i])
579 values[i] = datumCopy(slot->tts_values[i], attr->attbyval, attr->attlen);
580 }
581 }
582
583 ExecClearTuple(slot);
584 memcpy(slot->tts_values, values, sizeof(*values) * natts);
585 memcpy(slot->tts_isnull, nulls, sizeof(*nulls) * natts);
588
589 MemoryContextSwitchTo(oldContext);
590}
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define Assert(condition)
Definition: c.h:815
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1739
#define GetPerTupleExprContext(estate)
Definition: executor.h:563
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:568
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:346
long val
Definition: informix.c:689
int i
Definition: isn.c:72
void * palloc(Size size)
Definition: mcxt.c:1317
void ExecInitStoredGenerated(ResultRelInfo *resultRelInfo, EState *estate, CmdType cmdtype)
@ CMD_UPDATE
Definition: nodes.h:266
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
uintptr_t Datum
Definition: postgres.h:69
#define RelationGetDescr(relation)
Definition: rel.h:531
int16 attlen
Definition: tupdesc.h:70
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:267
Relation ri_RelationDesc
Definition: execnodes.h:474
ExprState ** ri_GeneratedExprsI
Definition: execnodes.h:552
int ri_NumGeneratedNeededU
Definition: execnodes.h:557
ExprState ** ri_GeneratedExprsU
Definition: execnodes.h:553
int ri_NumGeneratedNeededI
Definition: execnodes.h:556
bool has_generated_stored
Definition: tupdesc.h:46
TupleConstr * constr
Definition: tupdesc.h:134
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:153
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:168
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368
static void ExecMaterializeSlot(TupleTableSlot *slot)
Definition: tuptable.h:472

References Assert, CompactAttribute::attbyval, CompactAttribute::attlen, 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(), TupleDescCompactAttr(), val, and values.

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

◆ ExecEndModifyTable()

void ExecEndModifyTable ( ModifyTableState node)

Definition at line 4936 of file nodeModifyTable.c.

4937{
4938 int i;
4939
4940 /*
4941 * Allow any FDWs to shut down
4942 */
4943 for (i = 0; i < node->mt_nrels; i++)
4944 {
4945 int j;
4946 ResultRelInfo *resultRelInfo = node->resultRelInfo + i;
4947
4948 if (!resultRelInfo->ri_usesFdwDirectModify &&
4949 resultRelInfo->ri_FdwRoutine != NULL &&
4950 resultRelInfo->ri_FdwRoutine->EndForeignModify != NULL)
4951 resultRelInfo->ri_FdwRoutine->EndForeignModify(node->ps.state,
4952 resultRelInfo);
4953
4954 /*
4955 * Cleanup the initialized batch slots. This only matters for FDWs
4956 * with batching, but the other cases will have ri_NumSlotsInitialized
4957 * == 0.
4958 */
4959 for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
4960 {
4961 ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
4963 }
4964 }
4965
4966 /*
4967 * Close all the partitioned tables, leaf partitions, and their indices
4968 * and release the slot used for tuple routing, if set.
4969 */
4971 {
4973
4974 if (node->mt_root_tuple_slot)
4976 }
4977
4978 /*
4979 * Terminate EPQ execution if active
4980 */
4982
4983 /*
4984 * shut down subplan
4985 */
4987}
void EvalPlanQualEnd(EPQState *epqstate)
Definition: execMain.c:2996
void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute)
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1441
#define outerPlanState(node)
Definition: execnodes.h:1237
int j
Definition: isn.c:73
EndForeignModify_function EndForeignModify
Definition: fdwapi.h:237
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1384
struct PartitionTupleRouting * mt_partition_tuple_routing
Definition: execnodes.h:1415
TupleTableSlot * mt_root_tuple_slot
Definition: execnodes.h:1412
EPQState mt_epqstate
Definition: execnodes.h:1394
PlanState ps
Definition: execnodes.h:1379
EState * state
Definition: execnodes.h:1143
TupleTableSlot ** ri_Slots
Definition: execnodes.h:537
int ri_NumSlotsInitialized
Definition: execnodes.h:535
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:525
TupleTableSlot ** ri_PlanSlots
Definition: execnodes.h:538
bool ri_usesFdwDirectModify
Definition: execnodes.h:531

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

3785{
3786 EState *estate = mtstate->ps.state;
3787
3788 Assert(!resultRelInfo->ri_projectNewInfoValid);
3789
3790 resultRelInfo->ri_oldTupleSlot =
3791 table_slot_create(resultRelInfo->ri_RelationDesc,
3792 &estate->es_tupleTable);
3793 resultRelInfo->ri_newTupleSlot =
3794 table_slot_create(resultRelInfo->ri_RelationDesc,
3795 &estate->es_tupleTable);
3796 resultRelInfo->ri_projectNewInfoValid = true;
3797}
List * es_tupleTable
Definition: execnodes.h:693
bool ri_projectNewInfoValid
Definition: execnodes.h:501
TupleTableSlot * ri_oldTupleSlot
Definition: execnodes.h:499
TupleTableSlot * ri_newTupleSlot
Definition: execnodes.h:497
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 4451 of file nodeModifyTable.c.

4452{
4453 ModifyTableState *mtstate;
4454 Plan *subplan = outerPlan(node);
4455 CmdType operation = node->operation;
4456 int nrels = list_length(node->resultRelations);
4457 ResultRelInfo *resultRelInfo;
4458 List *arowmarks;
4459 ListCell *l;
4460 int i;
4461 Relation rel;
4462
4463 /* check for unsupported flags */
4464 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
4465
4466 /*
4467 * create state structure
4468 */
4469 mtstate = makeNode(ModifyTableState);
4470 mtstate->ps.plan = (Plan *) node;
4471 mtstate->ps.state = estate;
4472 mtstate->ps.ExecProcNode = ExecModifyTable;
4473
4474 mtstate->operation = operation;
4475 mtstate->canSetTag = node->canSetTag;
4476 mtstate->mt_done = false;
4477
4478 mtstate->mt_nrels = nrels;
4479 mtstate->resultRelInfo = (ResultRelInfo *)
4480 palloc(nrels * sizeof(ResultRelInfo));
4481
4482 mtstate->mt_merge_pending_not_matched = NULL;
4483 mtstate->mt_merge_inserted = 0;
4484 mtstate->mt_merge_updated = 0;
4485 mtstate->mt_merge_deleted = 0;
4486
4487 /*----------
4488 * Resolve the target relation. This is the same as:
4489 *
4490 * - the relation for which we will fire FOR STATEMENT triggers,
4491 * - the relation into whose tuple format all captured transition tuples
4492 * must be converted, and
4493 * - the root partitioned table used for tuple routing.
4494 *
4495 * If it's a partitioned or inherited table, the root partition or
4496 * appendrel RTE doesn't appear elsewhere in the plan and its RT index is
4497 * given explicitly in node->rootRelation. Otherwise, the target relation
4498 * is the sole relation in the node->resultRelations list.
4499 *----------
4500 */
4501 if (node->rootRelation > 0)
4502 {
4505 node->rootRelation);
4506 }
4507 else
4508 {
4509 Assert(list_length(node->resultRelations) == 1);
4510 mtstate->rootResultRelInfo = mtstate->resultRelInfo;
4511 ExecInitResultRelation(estate, mtstate->resultRelInfo,
4513 }
4514
4515 /* set up epqstate with dummy subplan data for the moment */
4516 EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL,
4517 node->epqParam, node->resultRelations);
4518 mtstate->fireBSTriggers = true;
4519
4520 /*
4521 * Build state for collecting transition tuples. This requires having a
4522 * valid trigger query context, so skip it in explain-only mode.
4523 */
4524 if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
4525 ExecSetupTransitionCaptureState(mtstate, estate);
4526
4527 /*
4528 * Open all the result relations and initialize the ResultRelInfo structs.
4529 * (But root relation was initialized above, if it's part of the array.)
4530 * We must do this before initializing the subplan, because direct-modify
4531 * FDWs expect their ResultRelInfos to be available.
4532 */
4533 resultRelInfo = mtstate->resultRelInfo;
4534 i = 0;
4535 foreach(l, node->resultRelations)
4536 {
4537 Index resultRelation = lfirst_int(l);
4538 List *mergeActions = NIL;
4539
4540 if (node->mergeActionLists)
4541 mergeActions = list_nth(node->mergeActionLists, i);
4542
4543 if (resultRelInfo != mtstate->rootResultRelInfo)
4544 {
4545 ExecInitResultRelation(estate, resultRelInfo, resultRelation);
4546
4547 /*
4548 * For child result relations, store the root result relation
4549 * pointer. We do so for the convenience of places that want to
4550 * look at the query's original target relation but don't have the
4551 * mtstate handy.
4552 */
4553 resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
4554 }
4555
4556 /* Initialize the usesFdwDirectModify flag */
4557 resultRelInfo->ri_usesFdwDirectModify =
4559
4560 /*
4561 * Verify result relation is a valid target for the current operation
4562 */
4563 CheckValidResultRel(resultRelInfo, operation, mergeActions);
4564
4565 resultRelInfo++;
4566 i++;
4567 }
4568
4569 /*
4570 * Now we may initialize the subplan.
4571 */
4572 outerPlanState(mtstate) = ExecInitNode(subplan, estate, eflags);
4573
4574 /*
4575 * Do additional per-result-relation initialization.
4576 */
4577 for (i = 0; i < nrels; i++)
4578 {
4579 resultRelInfo = &mtstate->resultRelInfo[i];
4580
4581 /* Let FDWs init themselves for foreign-table result rels */
4582 if (!resultRelInfo->ri_usesFdwDirectModify &&
4583 resultRelInfo->ri_FdwRoutine != NULL &&
4584 resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
4585 {
4586 List *fdw_private = (List *) list_nth(node->fdwPrivLists, i);
4587
4588 resultRelInfo->ri_FdwRoutine->BeginForeignModify(mtstate,
4589 resultRelInfo,
4590 fdw_private,
4591 i,
4592 eflags);
4593 }
4594
4595 /*
4596 * For UPDATE/DELETE/MERGE, find the appropriate junk attr now, either
4597 * a 'ctid' or 'wholerow' attribute depending on relkind. For foreign
4598 * tables, the FDW might have created additional junk attr(s), but
4599 * those are no concern of ours.
4600 */
4601 if (operation == CMD_UPDATE || operation == CMD_DELETE ||
4602 operation == CMD_MERGE)
4603 {
4604 char relkind;
4605
4606 relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
4607 if (relkind == RELKIND_RELATION ||
4608 relkind == RELKIND_MATVIEW ||
4609 relkind == RELKIND_PARTITIONED_TABLE)
4610 {
4611 resultRelInfo->ri_RowIdAttNo =
4612 ExecFindJunkAttributeInTlist(subplan->targetlist, "ctid");
4613 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4614 elog(ERROR, "could not find junk ctid column");
4615 }
4616 else if (relkind == RELKIND_FOREIGN_TABLE)
4617 {
4618 /*
4619 * We don't support MERGE with foreign tables for now. (It's
4620 * problematic because the implementation uses CTID.)
4621 */
4622 Assert(operation != CMD_MERGE);
4623
4624 /*
4625 * When there is a row-level trigger, there should be a
4626 * wholerow attribute. We also require it to be present in
4627 * UPDATE and MERGE, so we can get the values of unchanged
4628 * columns.
4629 */
4630 resultRelInfo->ri_RowIdAttNo =
4632 "wholerow");
4633 if ((mtstate->operation == CMD_UPDATE || mtstate->operation == CMD_MERGE) &&
4634 !AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4635 elog(ERROR, "could not find junk wholerow column");
4636 }
4637 else
4638 {
4639 /* Other valid target relkinds must provide wholerow */
4640 resultRelInfo->ri_RowIdAttNo =
4642 "wholerow");
4643 if (!AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
4644 elog(ERROR, "could not find junk wholerow column");
4645 }
4646 }
4647 }
4648
4649 /*
4650 * If this is an inherited update/delete/merge, there will be a junk
4651 * attribute named "tableoid" present in the subplan's targetlist. It
4652 * will be used to identify the result relation for a given tuple to be
4653 * updated/deleted/merged.
4654 */
4655 mtstate->mt_resultOidAttno =
4656 ExecFindJunkAttributeInTlist(subplan->targetlist, "tableoid");
4657 Assert(AttributeNumberIsValid(mtstate->mt_resultOidAttno) || nrels == 1);
4658 mtstate->mt_lastResultOid = InvalidOid; /* force lookup at first tuple */
4659 mtstate->mt_lastResultIndex = 0; /* must be zero if no such attr */
4660
4661 /* Get the root target relation */
4662 rel = mtstate->rootResultRelInfo->ri_RelationDesc;
4663
4664 /*
4665 * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
4666 * or MERGE might need this too, but only if it actually moves tuples
4667 * between partitions; in that case setup is done by
4668 * ExecCrossPartitionUpdate.
4669 */
4670 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
4671 operation == CMD_INSERT)
4673 ExecSetupPartitionTupleRouting(estate, rel);
4674
4675 /*
4676 * Initialize any WITH CHECK OPTION constraints if needed.
4677 */
4678 resultRelInfo = mtstate->resultRelInfo;
4679 foreach(l, node->withCheckOptionLists)
4680 {
4681 List *wcoList = (List *) lfirst(l);
4682 List *wcoExprs = NIL;
4683 ListCell *ll;
4684
4685 foreach(ll, wcoList)
4686 {
4687 WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
4688 ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
4689 &mtstate->ps);
4690
4691 wcoExprs = lappend(wcoExprs, wcoExpr);
4692 }
4693
4694 resultRelInfo->ri_WithCheckOptions = wcoList;
4695 resultRelInfo->ri_WithCheckOptionExprs = wcoExprs;
4696 resultRelInfo++;
4697 }
4698
4699 /*
4700 * Initialize RETURNING projections if needed.
4701 */
4702 if (node->returningLists)
4703 {
4704 TupleTableSlot *slot;
4705 ExprContext *econtext;
4706
4707 /*
4708 * Initialize result tuple slot and assign its rowtype using the first
4709 * RETURNING list. We assume the rest will look the same.
4710 */
4711 mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
4712
4713 /* Set up a slot for the output of the RETURNING projection(s) */
4715 slot = mtstate->ps.ps_ResultTupleSlot;
4716
4717 /* Need an econtext too */
4718 if (mtstate->ps.ps_ExprContext == NULL)
4719 ExecAssignExprContext(estate, &mtstate->ps);
4720 econtext = mtstate->ps.ps_ExprContext;
4721
4722 /*
4723 * Build a projection for each result rel.
4724 */
4725 resultRelInfo = mtstate->resultRelInfo;
4726 foreach(l, node->returningLists)
4727 {
4728 List *rlist = (List *) lfirst(l);
4729
4730 resultRelInfo->ri_returningList = rlist;
4731 resultRelInfo->ri_projectReturning =
4732 ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
4733 resultRelInfo->ri_RelationDesc->rd_att);
4734 resultRelInfo++;
4735 }
4736 }
4737 else
4738 {
4739 /*
4740 * We still must construct a dummy result tuple type, because InitPlan
4741 * expects one (maybe should change that?).
4742 */
4743 mtstate->ps.plan->targetlist = NIL;
4744 ExecInitResultTypeTL(&mtstate->ps);
4745
4746 mtstate->ps.ps_ExprContext = NULL;
4747 }
4748
4749 /* Set the list of arbiter indexes if needed for ON CONFLICT */
4750 resultRelInfo = mtstate->resultRelInfo;
4751 if (node->onConflictAction != ONCONFLICT_NONE)
4752 {
4753 /* insert may only have one relation, inheritance is not expanded */
4754 Assert(nrels == 1);
4755 resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
4756 }
4757
4758 /*
4759 * If needed, Initialize target list, projection and qual for ON CONFLICT
4760 * DO UPDATE.
4761 */
4763 {
4765 ExprContext *econtext;
4766 TupleDesc relationDesc;
4767
4768 /* already exists if created by RETURNING processing above */
4769 if (mtstate->ps.ps_ExprContext == NULL)
4770 ExecAssignExprContext(estate, &mtstate->ps);
4771
4772 econtext = mtstate->ps.ps_ExprContext;
4773 relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
4774
4775 /* create state for DO UPDATE SET operation */
4776 resultRelInfo->ri_onConflict = onconfl;
4777
4778 /* initialize slot for the existing tuple */
4779 onconfl->oc_Existing =
4780 table_slot_create(resultRelInfo->ri_RelationDesc,
4781 &mtstate->ps.state->es_tupleTable);
4782
4783 /*
4784 * Create the tuple slot for the UPDATE SET projection. We want a slot
4785 * of the table's type here, because the slot will be used to insert
4786 * into the table, and for RETURNING processing - which may access
4787 * system attributes.
4788 */
4789 onconfl->oc_ProjSlot =
4790 table_slot_create(resultRelInfo->ri_RelationDesc,
4791 &mtstate->ps.state->es_tupleTable);
4792
4793 /* build UPDATE SET projection state */
4794 onconfl->oc_ProjInfo =
4796 true,
4797 node->onConflictCols,
4798 relationDesc,
4799 econtext,
4800 onconfl->oc_ProjSlot,
4801 &mtstate->ps);
4802
4803 /* initialize state to evaluate the WHERE clause, if any */
4804 if (node->onConflictWhere)
4805 {
4806 ExprState *qualexpr;
4807
4808 qualexpr = ExecInitQual((List *) node->onConflictWhere,
4809 &mtstate->ps);
4810 onconfl->oc_WhereClause = qualexpr;
4811 }
4812 }
4813
4814 /*
4815 * If we have any secondary relations in an UPDATE or DELETE, they need to
4816 * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
4817 * EvalPlanQual mechanism needs to be told about them. This also goes for
4818 * the source relations in a MERGE. Locate the relevant ExecRowMarks.
4819 */
4820 arowmarks = NIL;
4821 foreach(l, node->rowMarks)
4822 {
4824 ExecRowMark *erm;
4825 ExecAuxRowMark *aerm;
4826
4827 /* ignore "parent" rowmarks; they are irrelevant at runtime */
4828 if (rc->isParent)
4829 continue;
4830
4831 /* Find ExecRowMark and build ExecAuxRowMark */
4832 erm = ExecFindRowMark(estate, rc->rti, false);
4833 aerm = ExecBuildAuxRowMark(erm, subplan->targetlist);
4834 arowmarks = lappend(arowmarks, aerm);
4835 }
4836
4837 /* For a MERGE command, initialize its state */
4838 if (mtstate->operation == CMD_MERGE)
4839 ExecInitMerge(mtstate, estate);
4840
4841 EvalPlanQualSetPlan(&mtstate->mt_epqstate, subplan, arowmarks);
4842
4843 /*
4844 * If there are a lot of result relations, use a hash table to speed the
4845 * lookups. If there are not a lot, a simple linear search is faster.
4846 *
4847 * It's not clear where the threshold is, but try 64 for starters. In a
4848 * debugging build, use a small threshold so that we get some test
4849 * coverage of both code paths.
4850 */
4851#ifdef USE_ASSERT_CHECKING
4852#define MT_NRELS_HASH 4
4853#else
4854#define MT_NRELS_HASH 64
4855#endif
4856 if (nrels >= MT_NRELS_HASH)
4857 {
4858 HASHCTL hash_ctl;
4859
4860 hash_ctl.keysize = sizeof(Oid);
4861 hash_ctl.entrysize = sizeof(MTTargetRelLookup);
4862 hash_ctl.hcxt = CurrentMemoryContext;
4863 mtstate->mt_resultOidHash =
4864 hash_create("ModifyTable target hash",
4865 nrels, &hash_ctl,
4867 for (i = 0; i < nrels; i++)
4868 {
4869 Oid hashkey;
4870 MTTargetRelLookup *mtlookup;
4871 bool found;
4872
4873 resultRelInfo = &mtstate->resultRelInfo[i];
4874 hashkey = RelationGetRelid(resultRelInfo->ri_RelationDesc);
4875 mtlookup = (MTTargetRelLookup *)
4876 hash_search(mtstate->mt_resultOidHash, &hashkey,
4877 HASH_ENTER, &found);
4878 Assert(!found);
4879 mtlookup->relationIndex = i;
4880 }
4881 }
4882 else
4883 mtstate->mt_resultOidHash = NULL;
4884
4885 /*
4886 * Determine if the FDW supports batch insert and determine the batch size
4887 * (a FDW may support batching, but it may be disabled for the
4888 * server/table).
4889 *
4890 * We only do this for INSERT, so that for UPDATE/DELETE the batch size
4891 * remains set to 0.
4892 */
4893 if (operation == CMD_INSERT)
4894 {
4895 /* insert may only have one relation, inheritance is not expanded */
4896 Assert(nrels == 1);
4897 resultRelInfo = mtstate->resultRelInfo;
4898 if (!resultRelInfo->ri_usesFdwDirectModify &&
4899 resultRelInfo->ri_FdwRoutine != NULL &&
4900 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
4901 resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
4902 {
4903 resultRelInfo->ri_BatchSize =
4904 resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
4905 Assert(resultRelInfo->ri_BatchSize >= 1);
4906 }
4907 else
4908 resultRelInfo->ri_BatchSize = 1;
4909 }
4910
4911 /*
4912 * Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
4913 * to estate->es_auxmodifytables so that it will be run to completion by
4914 * ExecPostprocessPlan. (It'd actually work fine to add the primary
4915 * ModifyTable node too, but there's no need.) Note the use of lcons not
4916 * lappend: we need later-initialized ModifyTable nodes to be shut down
4917 * before earlier ones. This ensures that we don't throw away RETURNING
4918 * rows that need to be seen by a later CTE subplan.
4919 */
4920 if (!mtstate->canSetTag)
4921 estate->es_auxmodifytables = lcons(mtstate,
4922 estate->es_auxmodifytables);
4923
4924 return mtstate;
4925}
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
unsigned int Index
Definition: c.h:571
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
ProjectionInfo * ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent, TupleDesc inputDesc)
Definition: execExpr.c:370
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:229
ProjectionInfo * ExecBuildUpdateProjection(List *targetList, bool evalTargetList, List *targetColnos, TupleDesc relDesc, ExprContext *econtext, TupleTableSlot *slot, PlanState *parent)
Definition: execExpr.c:547
AttrNumber ExecFindJunkAttributeInTlist(List *targetlist, const char *attrName)
Definition: execJunk.c:222
ExecRowMark * ExecFindRowMark(EState *estate, Index rti, bool missing_ok)
Definition: execMain.c:2391
ExecAuxRowMark * ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist)
Definition: execMain.c:2414
void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation, List *mergeActions)
Definition: execMain.c:1026
void EvalPlanQualInit(EPQState *epqstate, EState *parentestate, Plan *subplan, List *auxrowmarks, int epqParam, List *resultRelations)
Definition: execMain.c:2553
void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks)
Definition: execMain.c:2595
PartitionTupleRouting * ExecSetupPartitionTupleRouting(EState *estate, Relation rel)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1942
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1986
void ExecInitResultRelation(EState *estate, ResultRelInfo *resultRelInfo, Index rti)
Definition: execUtils.c:859
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:485
#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:339
List * lcons(void *datum, List *list)
Definition: list.c:495
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
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:418
@ ONCONFLICT_UPDATE
Definition: nodes.h:420
CmdType
Definition: nodes.h:263
@ CMD_MERGE
Definition: nodes.h:269
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
#define makeNode(_type_)
Definition: nodes.h:155
#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
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define outerPlan(node)
Definition: plannodes.h:183
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
#define RelationGetRelid(relation)
Definition: rel.h:505
List * es_auxmodifytables
Definition: execnodes.h:708
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:1380
TupleTableSlot * mt_merge_pending_not_matched
Definition: execnodes.h:1434
double mt_merge_deleted
Definition: execnodes.h:1439
double mt_merge_inserted
Definition: execnodes.h:1437
double mt_merge_updated
Definition: execnodes.h:1438
HTAB * mt_resultOidHash
Definition: execnodes.h:1406
ResultRelInfo * rootResultRelInfo
Definition: execnodes.h:1392
List * arbiterIndexes
Definition: plannodes.h:249
List * onConflictCols
Definition: plannodes.h:251
CmdType operation
Definition: plannodes.h:233
int epqParam
Definition: plannodes.h:247
List * resultRelations
Definition: plannodes.h:238
Bitmapset * fdwDirectModifyPlans
Definition: plannodes.h:245
List * onConflictSet
Definition: plannodes.h:250
List * mergeActionLists
Definition: plannodes.h:255
bool canSetTag
Definition: plannodes.h:234
List * fdwPrivLists
Definition: plannodes.h:244
List * returningLists
Definition: plannodes.h:243
List * withCheckOptionLists
Definition: plannodes.h:240
Index rootRelation
Definition: plannodes.h:236
Node * onConflictWhere
Definition: plannodes.h:252
List * rowMarks
Definition: plannodes.h:246
OnConflictAction onConflictAction
Definition: plannodes.h:248
TupleTableSlot * oc_ProjSlot
Definition: execnodes.h:428
TupleTableSlot * oc_Existing
Definition: execnodes.h:427
ExprState * oc_WhereClause
Definition: execnodes.h:430
ProjectionInfo * oc_ProjInfo
Definition: execnodes.h:429
bool isParent
Definition: plannodes.h:1389
Plan * plan
Definition: execnodes.h:1141
ExprContext * ps_ExprContext
Definition: execnodes.h:1180
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1179
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1147
List * targetlist
Definition: plannodes.h:153
TupleDesc rd_att
Definition: rel.h:112
Form_pg_class rd_rel
Definition: rel.h:111
OnConflictSetState * ri_onConflict
Definition: execnodes.h:569
List * ri_onConflictArbiterIndexes
Definition: execnodes.h:566
struct ResultRelInfo * ri_RootResultRelInfo
Definition: execnodes.h:606
List * ri_WithCheckOptions
Definition: execnodes.h:541
List * ri_WithCheckOptionExprs
Definition: execnodes.h:544
ProjectionInfo * ri_projectReturning
Definition: execnodes.h:563
List * ri_returningList
Definition: execnodes.h:560
AttrNumber ri_RowIdAttNo
Definition: execnodes.h:489
int ri_BatchSize
Definition: execnodes.h:536

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, ModifyTable::mergeActionLists, ModifyTableState::mt_done, ModifyTableState::mt_epqstate, ModifyTableState::mt_lastResultIndex, ModifyTableState::mt_lastResultOid, ModifyTableState::mt_merge_deleted, ModifyTableState::mt_merge_inserted, ModifyTableState::mt_merge_pending_not_matched, 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 406 of file nodeModifyTable.c.

409{
410 Relation rel = resultRelInfo->ri_RelationDesc;
411 TupleDesc tupdesc = RelationGetDescr(rel);
412 int natts = tupdesc->natts;
413 ExprState **ri_GeneratedExprs;
414 int ri_NumGeneratedNeeded;
415 Bitmapset *updatedCols;
416 MemoryContext oldContext;
417
418 /* Nothing to do if no generated columns */
419 if (!(tupdesc->constr && tupdesc->constr->has_generated_stored))
420 return;
421
422 /*
423 * In an UPDATE, we can skip computing any generated columns that do not
424 * depend on any UPDATE target column. But if there is a BEFORE ROW
425 * UPDATE trigger, we cannot skip because the trigger might change more
426 * columns.
427 */
428 if (cmdtype == CMD_UPDATE &&
430 updatedCols = ExecGetUpdatedCols(resultRelInfo, estate);
431 else
432 updatedCols = NULL;
433
434 /*
435 * Make sure these data structures are built in the per-query memory
436 * context so they'll survive throughout the query.
437 */
438 oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
439
440 ri_GeneratedExprs = (ExprState **) palloc0(natts * sizeof(ExprState *));
441 ri_NumGeneratedNeeded = 0;
442
443 for (int i = 0; i < natts; i++)
444 {
445 if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
446 {
447 Expr *expr;
448
449 /* Fetch the GENERATED AS expression tree */
450 expr = (Expr *) build_column_default(rel, i + 1);
451 if (expr == NULL)
452 elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
453 i + 1, RelationGetRelationName(rel));
454
455 /*
456 * If it's an update with a known set of update target columns,
457 * see if we can skip the computation.
458 */
459 if (updatedCols)
460 {
461 Bitmapset *attrs_used = NULL;
462
463 pull_varattnos((Node *) expr, 1, &attrs_used);
464
465 if (!bms_overlap(updatedCols, attrs_used))
466 continue; /* need not update this column */
467 }
468
469 /* No luck, so prepare the expression for execution */
470 ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
471 ri_NumGeneratedNeeded++;
472
473 /* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
474 if (cmdtype == CMD_UPDATE)
475 resultRelInfo->ri_extraUpdatedCols =
476 bms_add_member(resultRelInfo->ri_extraUpdatedCols,
478 }
479 }
480
481 /* Save in appropriate set of fields */
482 if (cmdtype == CMD_UPDATE)
483 {
484 /* Don't call twice */
485 Assert(resultRelInfo->ri_GeneratedExprsU == NULL);
486
487 resultRelInfo->ri_GeneratedExprsU = ri_GeneratedExprs;
488 resultRelInfo->ri_NumGeneratedNeededU = ri_NumGeneratedNeeded;
489 }
490 else
491 {
492 /* Don't call twice */
493 Assert(resultRelInfo->ri_GeneratedExprsI == NULL);
494
495 resultRelInfo->ri_GeneratedExprsI = ri_GeneratedExprs;
496 resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
497 }
498
499 MemoryContextSwitchTo(oldContext);
500}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:582
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:765
Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
Definition: execUtils.c:1361
void * palloc0(Size size)
Definition: mcxt.c:1347
#define RelationGetRelationName(relation)
Definition: rel.h:539
Node * build_column_default(Relation rel, int attrno)
MemoryContext es_query_cxt
Definition: execnodes.h:691
Definition: nodes.h:129
TriggerDesc * trigdesc
Definition: rel.h:117
Bitmapset * ri_extraUpdatedCols
Definition: execnodes.h:492
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:296

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

4991{
4992 /*
4993 * Currently, we don't need to support rescan on ModifyTable nodes. The
4994 * semantics of that would be a bit debatable anyway.
4995 */
4996 elog(ERROR, "ExecReScanModifyTable is not implemented");
4997}

References elog, and ERROR.

Referenced by ExecReScan().