aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeModifyTable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r--src/backend/executor/nodeModifyTable.c247
1 files changed, 130 insertions, 117 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index bf65785e643..249555f234b 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -493,6 +493,14 @@ ExecInsert(ModifyTableState *mtstate,
resultRelationDesc = resultRelInfo->ri_RelationDesc;
/*
+ * Open the table's indexes, if we have not done so already, so that we
+ * can add new index entries for the inserted tuple.
+ */
+ if (resultRelationDesc->rd_rel->relhasindex &&
+ resultRelInfo->ri_IndexRelationDescs == NULL)
+ ExecOpenIndices(resultRelInfo, onconflict != ONCONFLICT_NONE);
+
+ /*
* BEFORE ROW INSERT Triggers.
*
* Note: We fire BEFORE ROW TRIGGERS for every attempted insertion in an
@@ -1276,7 +1284,6 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate,
TupleTableSlot **inserted_tuple)
{
EState *estate = mtstate->ps.state;
- PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
TupleConversionMap *tupconv_map;
bool tuple_deleted;
TupleTableSlot *epqslot = NULL;
@@ -1296,13 +1303,35 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate,
errdetail("The result tuple would appear in a different partition than the original tuple.")));
/*
- * When an UPDATE is run on a leaf partition, we will not have partition
- * tuple routing set up. In that case, fail with partition constraint
- * violation error.
+ * When an UPDATE is run directly on a leaf partition, simply fail with a
+ * partition constraint violation error.
*/
- if (proute == NULL)
+ if (resultRelInfo == mtstate->rootResultRelInfo)
ExecPartitionCheckEmitError(resultRelInfo, slot, estate);
+ /* Initialize tuple routing info if not already done. */
+ if (mtstate->mt_partition_tuple_routing == NULL)
+ {
+ Relation rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
+ MemoryContext oldcxt;
+
+ /* Things built here have to last for the query duration. */
+ oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ mtstate->mt_partition_tuple_routing =
+ ExecSetupPartitionTupleRouting(estate, rootRel);
+
+ /*
+ * Before a partition's tuple can be re-routed, it must first be
+ * converted to the root's format, so we'll need a slot for storing
+ * such tuples.
+ */
+ Assert(mtstate->mt_root_tuple_slot == NULL);
+ mtstate->mt_root_tuple_slot = table_slot_create(rootRel, NULL);
+
+ MemoryContextSwitchTo(oldcxt);
+ }
+
/*
* Row movement, part 1. Delete the tuple, but skip RETURNING processing.
* We want to return rows from INSERT.
@@ -1364,7 +1393,7 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate,
* convert the tuple into root's tuple descriptor if needed, since
* ExecInsert() starts the search from root.
*/
- tupconv_map = resultRelInfo->ri_ChildToRootMap;
+ tupconv_map = ExecGetChildToRootMap(resultRelInfo);
if (tupconv_map != NULL)
slot = execute_attr_map_slot(tupconv_map->attrMap,
slot,
@@ -1436,6 +1465,14 @@ ExecUpdate(ModifyTableState *mtstate,
ExecMaterializeSlot(slot);
+ /*
+ * Open the table's indexes, if we have not done so already, so that we
+ * can add new index entries for the updated tuple.
+ */
+ if (resultRelationDesc->rd_rel->relhasindex &&
+ resultRelInfo->ri_IndexRelationDescs == NULL)
+ ExecOpenIndices(resultRelInfo, false);
+
/* BEFORE ROW UPDATE Triggers */
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_update_before_row)
@@ -2244,38 +2281,8 @@ ExecModifyTable(PlanState *pstate)
/* If it's not the same as last time, we need to locate the rel */
if (resultoid != node->mt_lastResultOid)
- {
- if (node->mt_resultOidHash)
- {
- /* Use the pre-built hash table to locate the rel */
- MTTargetRelLookup *mtlookup;
-
- mtlookup = (MTTargetRelLookup *)
- hash_search(node->mt_resultOidHash, &resultoid,
- HASH_FIND, NULL);
- if (!mtlookup)
- elog(ERROR, "incorrect result rel OID %u", resultoid);
- node->mt_lastResultOid = resultoid;
- node->mt_lastResultIndex = mtlookup->relationIndex;
- resultRelInfo = node->resultRelInfo + mtlookup->relationIndex;
- }
- else
- {
- /* With few target rels, just do a simple search */
- int ndx;
-
- for (ndx = 0; ndx < node->mt_nrels; ndx++)
- {
- resultRelInfo = node->resultRelInfo + ndx;
- if (RelationGetRelid(resultRelInfo->ri_RelationDesc) == resultoid)
- break;
- }
- if (ndx >= node->mt_nrels)
- elog(ERROR, "incorrect result rel OID %u", resultoid);
- node->mt_lastResultOid = resultoid;
- node->mt_lastResultIndex = ndx;
- }
- }
+ resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
+ false, true);
}
/*
@@ -2466,6 +2473,61 @@ ExecModifyTable(PlanState *pstate)
return NULL;
}
+/*
+ * ExecLookupResultRelByOid
+ * If the table with given OID is among the result relations to be
+ * updated by the given ModifyTable node, return its ResultRelInfo.
+ *
+ * If not found, return NULL if missing_ok, else raise error.
+ *
+ * If update_cache is true, then upon successful lookup, update the node's
+ * one-element cache. ONLY ExecModifyTable may pass true for this.
+ */
+ResultRelInfo *
+ExecLookupResultRelByOid(ModifyTableState *node, Oid resultoid,
+ bool missing_ok, bool update_cache)
+{
+ if (node->mt_resultOidHash)
+ {
+ /* Use the pre-built hash table to locate the rel */
+ MTTargetRelLookup *mtlookup;
+
+ mtlookup = (MTTargetRelLookup *)
+ hash_search(node->mt_resultOidHash, &resultoid, HASH_FIND, NULL);
+ if (mtlookup)
+ {
+ if (update_cache)
+ {
+ node->mt_lastResultOid = resultoid;
+ node->mt_lastResultIndex = mtlookup->relationIndex;
+ }
+ return node->resultRelInfo + mtlookup->relationIndex;
+ }
+ }
+ else
+ {
+ /* With few target rels, just search the ResultRelInfo array */
+ for (int ndx = 0; ndx < node->mt_nrels; ndx++)
+ {
+ ResultRelInfo *rInfo = node->resultRelInfo + ndx;
+
+ if (RelationGetRelid(rInfo->ri_RelationDesc) == resultoid)
+ {
+ if (update_cache)
+ {
+ node->mt_lastResultOid = resultoid;
+ node->mt_lastResultIndex = ndx;
+ }
+ return rInfo;
+ }
+ }
+ }
+
+ if (!missing_ok)
+ elog(ERROR, "incorrect result relation OID %u", resultoid);
+ return NULL;
+}
+
/* ----------------------------------------------------------------
* ExecInitModifyTable
* ----------------------------------------------------------------
@@ -2482,7 +2544,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
ListCell *l;
int i;
Relation rel;
- bool update_tuple_routing_needed = node->partColsUpdated;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@@ -2554,8 +2615,18 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
Index resultRelation = lfirst_int(l);
if (resultRelInfo != mtstate->rootResultRelInfo)
+ {
ExecInitResultRelation(estate, resultRelInfo, resultRelation);
+ /*
+ * For child result relations, store the root result relation
+ * pointer. We do so for the convenience of places that want to
+ * look at the query's original target relation but don't have the
+ * mtstate handy.
+ */
+ resultRelInfo->ri_RootResultRelInfo = mtstate->rootResultRelInfo;
+ }
+
/* Initialize the usesFdwDirectModify flag */
resultRelInfo->ri_usesFdwDirectModify = bms_is_member(i,
node->fdwDirectModifyPlans);
@@ -2581,32 +2652,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
{
resultRelInfo = &mtstate->resultRelInfo[i];
- /*
- * If there are indices on the result relation, open them and save
- * descriptors in the result relation info, so that we can add new
- * index entries for the tuples we add/update. We need not do this
- * for a DELETE, however, since deletion doesn't affect indexes. Also,
- * inside an EvalPlanQual operation, the indexes might be open
- * already, since we share the resultrel state with the original
- * query.
- */
- if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
- operation != CMD_DELETE &&
- resultRelInfo->ri_IndexRelationDescs == NULL)
- ExecOpenIndices(resultRelInfo,
- node->onConflictAction != ONCONFLICT_NONE);
-
- /*
- * If this is an UPDATE and a BEFORE UPDATE trigger is present, the
- * trigger itself might modify the partition-key values. So arrange
- * for tuple routing.
- */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->trig_update_before_row &&
- operation == CMD_UPDATE)
- update_tuple_routing_needed = true;
-
- /* Also let FDWs init themselves for foreign-table result rels */
+ /* Let FDWs init themselves for foreign-table result rels */
if (!resultRelInfo->ri_usesFdwDirectModify &&
resultRelInfo->ri_FdwRoutine != NULL &&
resultRelInfo->ri_FdwRoutine->BeginForeignModify != NULL)
@@ -2619,52 +2665,20 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
i,
eflags);
}
-
- /*
- * If needed, initialize a map to convert tuples in the child format
- * to the format of the table mentioned in the query (root relation).
- * It's needed for update tuple routing, because the routing starts
- * from the root relation. It's also needed for capturing transition
- * tuples, because the transition tuple store can only store tuples in
- * the root table format.
- *
- * For INSERT, the map is only initialized for a given partition when
- * the partition itself is first initialized by ExecFindPartition().
- */
- if (update_tuple_routing_needed ||
- (mtstate->mt_transition_capture &&
- mtstate->operation != CMD_INSERT))
- resultRelInfo->ri_ChildToRootMap =
- convert_tuples_by_name(RelationGetDescr(resultRelInfo->ri_RelationDesc),
- RelationGetDescr(mtstate->rootResultRelInfo->ri_RelationDesc));
}
/* Get the root target relation */
rel = mtstate->rootResultRelInfo->ri_RelationDesc;
/*
- * If it's not a partitioned table after all, UPDATE tuple routing should
- * not be attempted.
- */
- if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
- update_tuple_routing_needed = false;
-
- /*
- * Build state for tuple routing if it's an INSERT or if it's an UPDATE of
- * partition key.
+ * Build state for tuple routing if it's a partitioned INSERT. An UPDATE
+ * might need this too, but only if it actually moves tuples between
+ * partitions; in that case setup is done by ExecCrossPartitionUpdate.
*/
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
- (operation == CMD_INSERT || update_tuple_routing_needed))
+ operation == CMD_INSERT)
mtstate->mt_partition_tuple_routing =
- ExecSetupPartitionTupleRouting(estate, mtstate, rel);
-
- /*
- * For update row movement we'll need a dedicated slot to store the tuples
- * that have been converted from partition format to the root table
- * format.
- */
- if (update_tuple_routing_needed)
- mtstate->mt_root_tuple_slot = table_slot_create(rel, NULL);
+ ExecSetupPartitionTupleRouting(estate, rel);
/*
* Initialize any WITH CHECK OPTION constraints if needed.
@@ -2743,7 +2757,11 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* Set the list of arbiter indexes if needed for ON CONFLICT */
resultRelInfo = mtstate->resultRelInfo;
if (node->onConflictAction != ONCONFLICT_NONE)
+ {
+ /* insert may only have one relation, inheritance is not expanded */
+ Assert(nrels == 1);
resultRelInfo->ri_onConflictArbiterIndexes = node->arbiterIndexes;
+ }
/*
* If needed, Initialize target list, projection and qual for ON CONFLICT
@@ -2755,9 +2773,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
TupleDesc relationDesc;
TupleDesc tupDesc;
- /* insert may only have one relation, inheritance is not expanded */
- Assert(nrels == 1);
-
/* already exists if created by RETURNING processing above */
if (mtstate->ps.ps_ExprContext == NULL)
ExecAssignExprContext(estate, &mtstate->ps);
@@ -3036,22 +3051,20 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
*/
if (operation == CMD_INSERT)
{
+ /* insert may only have one relation, inheritance is not expanded */
+ Assert(nrels == 1);
resultRelInfo = mtstate->resultRelInfo;
- for (i = 0; i < nrels; i++)
+ if (!resultRelInfo->ri_usesFdwDirectModify &&
+ resultRelInfo->ri_FdwRoutine != NULL &&
+ resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
+ resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
{
- if (!resultRelInfo->ri_usesFdwDirectModify &&
- resultRelInfo->ri_FdwRoutine != NULL &&
- resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize &&
- resultRelInfo->ri_FdwRoutine->ExecForeignBatchInsert)
- resultRelInfo->ri_BatchSize =
- resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
- else
- resultRelInfo->ri_BatchSize = 1;
-
+ resultRelInfo->ri_BatchSize =
+ resultRelInfo->ri_FdwRoutine->GetForeignModifyBatchSize(resultRelInfo);
Assert(resultRelInfo->ri_BatchSize >= 1);
-
- resultRelInfo++;
}
+ else
+ resultRelInfo->ri_BatchSize = 1;
}
/*