From fb958b5da86da69651f6fb9f540c2cfb1346cdc5 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Fri, 2 Dec 2022 10:35:55 +0100 Subject: [PATCH] Generalize ri_RootToPartitionMap to use for non-partition children ri_RootToPartitionMap is currently only initialized for tuple routing target partitions, though a future commit will need the ability to use it even for the non-partition child tables, so make adjustments to the decouple it from the partitioning code. Also, make it lazily initialized via ExecGetRootToChildMap(), making that function its preferred access path. Existing third-party code accessing it directly should no longer do so; consequently, it's been renamed to ri_RootToChildMap, which also makes it consistent with ri_ChildToRootMap. ExecGetRootToChildMap() houses the logic of setting the map appropriately depending on whether a given child relation is partition or not. To support this, also add a separate entry point for TupleConversionMap creation that receives an AttrMap. No new code here, just split an existing function in two. Author: Amit Langote Discussion: https://postgr.es/m/CA+HiwqEYUhDXSK5BTvG_xk=eaAEJCD4GS3C6uH7ybBvv+Z_Tmg@mail.gmail.com --- src/backend/access/common/tupconvert.c | 19 +++++++- src/backend/commands/copyfrom.c | 2 +- src/backend/executor/execMain.c | 8 ++-- src/backend/executor/execPartition.c | 27 ++++------- src/backend/executor/execUtils.c | 57 ++++++++++++++++++++---- src/backend/executor/nodeModifyTable.c | 2 +- src/backend/replication/logical/worker.c | 4 +- src/include/access/tupconvert.h | 3 ++ src/include/executor/executor.h | 1 + src/include/nodes/execnodes.h | 36 ++++++++------- 10 files changed, 107 insertions(+), 52 deletions(-) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index b2f892d2fdf..4023f533d74 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -102,9 +102,7 @@ TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc) { - TupleConversionMap *map; AttrMap *attrMap; - int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); @@ -115,6 +113,23 @@ convert_tuples_by_name(TupleDesc indesc, return NULL; } + return convert_tuples_by_name_attrmap(indesc, outdesc, attrMap); +} + +/* + * Set up tuple conversion for input and output TupleDescs using the given + * AttrMap. + */ +TupleConversionMap * +convert_tuples_by_name_attrmap(TupleDesc indesc, + TupleDesc outdesc, + AttrMap *attrMap) +{ + int n = outdesc->natts; + TupleConversionMap *map; + + Assert(attrMap != NULL); + /* Prepare the map structure */ map = (TupleConversionMap *) palloc(sizeof(TupleConversionMap)); map->indesc = indesc; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index a079c70152f..504afcb8110 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -1088,7 +1088,7 @@ CopyFrom(CopyFromState cstate) * We might need to convert from the root rowtype to the partition * rowtype. */ - map = resultRelInfo->ri_RootToPartitionMap; + map = ExecGetRootToChildMap(resultRelInfo, estate); if (insertMethod == CIM_SINGLE || !leafpart_use_multi_insert) { /* non batch insert */ diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b6751da5743..12ff4f3de58 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1256,9 +1256,11 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, * this field is filled in ExecInitModifyTable(). */ resultRelInfo->ri_RootResultRelInfo = partition_root_rri; - resultRelInfo->ri_RootToPartitionMap = NULL; /* set by - * ExecInitRoutingInfo */ - resultRelInfo->ri_PartitionTupleSlot = NULL; /* ditto */ + /* Set by ExecGetRootToChildMap */ + resultRelInfo->ri_RootToChildMap = NULL; + resultRelInfo->ri_RootToChildMapValid = false; + /* Set by ExecInitRoutingInfo */ + resultRelInfo->ri_PartitionTupleSlot = NULL; resultRelInfo->ri_ChildToRootMap = NULL; resultRelInfo->ri_ChildToRootMapValid = false; resultRelInfo->ri_CopyMultiInsertBuffer = NULL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 8e6453aec2a..88d0ea3adb1 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -463,7 +463,7 @@ ExecFindPartition(ModifyTableState *mtstate, */ if (is_leaf) { - TupleConversionMap *map = rri->ri_RootToPartitionMap; + TupleConversionMap *map = ExecGetRootToChildMap(rri, estate); if (map) slot = execute_attr_map_slot(map->attrMap, rootslot, @@ -727,7 +727,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, OnConflictSetState *onconfl = makeNode(OnConflictSetState); TupleConversionMap *map; - map = leaf_part_rri->ri_RootToPartitionMap; + map = ExecGetRootToChildMap(leaf_part_rri, estate); Assert(node->onConflictSet != NIL); Assert(rootResultRelInfo->ri_onConflict != NULL); @@ -977,33 +977,24 @@ ExecInitRoutingInfo(ModifyTableState *mtstate, int partidx, bool is_borrowed_rel) { - ResultRelInfo *rootRelInfo = partRelInfo->ri_RootResultRelInfo; MemoryContext oldcxt; int rri_index; oldcxt = MemoryContextSwitchTo(proute->memcxt); /* - * Set up a tuple conversion map to convert a tuple routed to the - * partition from the parent's type to the partition's. + * Set up tuple conversion between root parent and the partition if the + * two have different rowtypes. If conversion is indeed required, also + * initialize a slot dedicated to storing this partition's converted + * tuples. Various operations that are applied to tuples after routing, + * such as checking constraints, will refer to this slot. */ - partRelInfo->ri_RootToPartitionMap = - convert_tuples_by_name(RelationGetDescr(rootRelInfo->ri_RelationDesc), - RelationGetDescr(partRelInfo->ri_RelationDesc)); - - /* - * If a partition has a different rowtype than the root parent, initialize - * a slot dedicated to storing this partition's tuples. The slot is used - * for various operations that are applied to tuples after routing, such - * as checking constraints. - */ - if (partRelInfo->ri_RootToPartitionMap != NULL) + if (ExecGetRootToChildMap(partRelInfo, estate) != NULL) { Relation partrel = partRelInfo->ri_RelationDesc; /* - * Initialize the slot itself setting its descriptor to this - * partition's TupleDesc; TupleDesc reference will be released at the + * This pins the partition's TupleDesc, which will be released at the * end of the command. */ partRelInfo->ri_PartitionTupleSlot = diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 9695de85b9a..572c87e4536 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1253,6 +1253,45 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } +/* + * Returns the map needed to convert given root result relation's tuples to + * the rowtype of the given child relation. Note that a NULL result is valid + * and means that no conversion is needed. + */ +TupleConversionMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate) +{ + /* Mustn't get called for a non-child result relation. */ + Assert(resultRelInfo->ri_RootResultRelInfo); + + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) + { + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + TupleDesc indesc = RelationGetDescr(rootRelInfo->ri_RelationDesc); + TupleDesc outdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); + Relation childrel = resultRelInfo->ri_RelationDesc; + AttrMap *attrMap; + MemoryContext oldcontext; + + /* + * When this child table is not a partition (!relispartition), it may + * have columns that are not present in the root table, which we ask + * to ignore by passing true for missing_ok. + */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, + !childrel->rd_rel->relispartition); + if (attrMap) + resultRelInfo->ri_RootToChildMap = + convert_tuples_by_name_attrmap(indesc, outdesc, attrMap); + MemoryContextSwitchTo(oldcontext); + resultRelInfo->ri_RootToChildMapValid = true; + } + + return resultRelInfo->ri_RootToChildMap; +} + /* Return a bitmap representing columns being inserted */ Bitmapset * ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) @@ -1273,10 +1312,10 @@ ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) { ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); + if (map != NULL) + return execute_attr_map_cols(map->attrMap, rte->insertedCols); else return rte->insertedCols; } @@ -1307,10 +1346,10 @@ ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); + if (map != NULL) + return execute_attr_map_cols(map->attrMap, rte->updatedCols); else return rte->updatedCols; } @@ -1333,10 +1372,10 @@ ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); + if (map != NULL) + return execute_attr_map_cols(map->attrMap, rte->extraUpdatedCols); else return rte->extraUpdatedCols; } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 271ff2be8ef..a3988b11754 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3481,7 +3481,7 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, /* * Convert the tuple, if necessary. */ - map = partrel->ri_RootToPartitionMap; + map = ExecGetRootToChildMap(partrel, estate); if (map != NULL) { TupleTableSlot *new_slot = partrel->ri_PartitionTupleSlot; diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index e48a3f589ae..f9efe6c4c67 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -2193,7 +2193,7 @@ apply_handle_tuple_routing(ApplyExecutionData *edata, remoteslot_part = partrelinfo->ri_PartitionTupleSlot; if (remoteslot_part == NULL) remoteslot_part = table_slot_create(partrel, &estate->es_tupleTable); - map = partrelinfo->ri_RootToPartitionMap; + map = ExecGetRootToChildMap(partrelinfo, estate); if (map != NULL) { attrmap = map->attrMap; @@ -2353,7 +2353,7 @@ apply_handle_tuple_routing(ApplyExecutionData *edata, if (remoteslot_part == NULL) remoteslot_part = table_slot_create(partrel_new, &estate->es_tupleTable); - map = partrelinfo_new->ri_RootToPartitionMap; + map = ExecGetRootToChildMap(partrelinfo_new, estate); if (map != NULL) { remoteslot_part = execute_attr_map_slot(map->attrMap, diff --git a/src/include/access/tupconvert.h b/src/include/access/tupconvert.h index a37dafc666d..2badb8c239d 100644 --- a/src/include/access/tupconvert.h +++ b/src/include/access/tupconvert.h @@ -39,6 +39,9 @@ extern TupleConversionMap *convert_tuples_by_position(TupleDesc indesc, extern TupleConversionMap *convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc); +extern TupleConversionMap *convert_tuples_by_name_attrmap(TupleDesc indesc, + TupleDesc outdesc, + AttrMap *attrMap); extern HeapTuple execute_attr_map_tuple(HeapTuple tuple, TupleConversionMap *map); extern TupleTableSlot *execute_attr_map_slot(AttrMap *attrMap, diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ed95ed1176d..aaf2bc78b9c 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -600,6 +600,7 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern TupleConversionMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index a2008846c63..71248a94660 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -538,22 +538,6 @@ typedef struct ResultRelInfo /* partition check expression state (NULL if not set up yet) */ ExprState *ri_PartitionCheckExpr; - /* - * Information needed by tuple routing target relations - * - * RootResultRelInfo gives the target relation mentioned in the query, if - * it's a partitioned table. It is not set if the target relation - * mentioned in the query is an inherited table, nor when tuple routing is - * not needed. - * - * RootToPartitionMap and PartitionTupleSlot, initialized by - * ExecInitRoutingInfo, are non-NULL if partition has a different tuple - * format than the root table. - */ - struct ResultRelInfo *ri_RootResultRelInfo; - TupleConversionMap *ri_RootToPartitionMap; - TupleTableSlot *ri_PartitionTupleSlot; - /* * Map to convert child result relation tuples to the format of the table * actually mentioned in the query (called "root"). Computed only if @@ -563,6 +547,26 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * As above, but in the other direction. + */ + TupleConversionMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + + /* + * Information needed by tuple routing target relations + * + * RootResultRelInfo gives the target relation mentioned in the query, if + * it's a partitioned table. It is not set if the target relation + * mentioned in the query is an inherited table, nor when tuple routing is + * not needed. + * + * PartitionTupleSlot is non-NULL if RootToChild conversion is needed and + * the relation is a partition. + */ + struct ResultRelInfo *ri_RootResultRelInfo; + TupleTableSlot *ri_PartitionTupleSlot; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer;