diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 44cf3bba12..6fc1e2b41c 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -2684,12 +2684,15 @@ CopyFrom(CopyState cstate) * We might need to convert from the parent rowtype to the * partition rowtype. */ - tuple = ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps ? - proute->parent_child_tupconv_maps[leaf_part_index] : - NULL, - tuple, - proute->partition_tuple_slot, - &slot); + if (proute->parent_child_tupconv_maps) + { + TupleConversionMap *map = + proute->parent_child_tupconv_maps[leaf_part_index]; + + tuple = ConvertPartitionTupleSlot(map, tuple, + proute->partition_tuple_slot, + &slot); + } tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc); } diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 24a9d6b426..2a18a30b3e 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -33,8 +33,7 @@ #define PARTITION_ROUTING_INITSIZE 8 -static void -ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, +static void ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, PartitionTupleRouting *proute); static void ExecExpandRoutingArrays(PartitionTupleRouting *proute); static int ExecInitPartitionInfo(ModifyTableState *mtstate, @@ -43,7 +42,7 @@ static int ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, PartitionDispatch parent, int partidx); static PartitionDispatch ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, - Oid partoid, PartitionDispatch parent_pd, int partidx); + Oid partoid, PartitionDispatch parent_pd, int partidx); static void FormPartitionKeyDatum(PartitionDispatch pd, TupleTableSlot *slot, EState *estate, @@ -91,24 +90,23 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) * single tuple into a single partition. * * We initially allocate enough memory to hold PARTITION_ROUTING_INITSIZE - * PartitionDispatch and ResultRelInfo pointers in their respective arrays. - * More space can be allocated later, if required via + * PartitionDispatch and ResultRelInfo pointers in their respective + * arrays. More space can be allocated later, if required via * ExecExpandRoutingArrays. * - * We're certain to only need just 1 PartitionDispatch; the one for the - * partitioned table which is the target of the command. We'll only setup - * PartitionDispatchs for any subpartitions if tuples actually get routed - * to (through) them. + * The PartitionDispatch for the target partitioned table of the command + * must be setup, but any sub-partitioned tables can be setup lazily as + * and when the tuples get routed to (through) them. */ proute = (PartitionTupleRouting *) palloc(sizeof(PartitionTupleRouting)); proute->partition_root = rel; proute->partition_dispatch_info = (PartitionDispatchData **) - palloc(sizeof(PartitionDispatchData) * PARTITION_ROUTING_INITSIZE); + palloc(sizeof(PartitionDispatchData) * PARTITION_ROUTING_INITSIZE); proute->num_dispatch = 0; proute->dispatch_allocsize = PARTITION_ROUTING_INITSIZE; proute->partitions = (ResultRelInfo **) - palloc(sizeof(ResultRelInfo *) * PARTITION_ROUTING_INITSIZE); + palloc(sizeof(ResultRelInfo *) * PARTITION_ROUTING_INITSIZE); proute->num_partitions = 0; proute->partitions_allocsize = PARTITION_ROUTING_INITSIZE; @@ -118,20 +116,23 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) proute->child_parent_map_not_required = NULL; /* - * Initialize this table's PartitionDispatch object. Here we pass in - * the parent is NULL as we don't need to care about any parent of the - * target partitioned table. + * Initialize this table's PartitionDispatch object. Here we pass in the + * parent is NULL as we don't need to care about any parent of the target + * partitioned table. */ (void) ExecInitPartitionDispatchInfo(proute, RelationGetRelid(rel), NULL, 0); /* - * If UPDATE needs to do tuple routing, we'll need a slot that will - * transiently store the tuple being routed using the root parent's - * rowtype. We must set up at least this slot, because it's needed even - * before tuple routing begins. Other necessary information is - * initialized when tuple routing code calls - * ExecUseUpdateResultRelForRouting. + * If UPDATE needs to do tuple routing, we can reuse partition sub-plan + * result rels after tuple routing, so build a hash table to map the OIDs + * of partitions present in mtstate->resultRelInfo to their + * ResultRelInfos. Every time a tuple is routed to one of the partitions + * present in mtstate->resultRelInfo, looking its OID up in the hash table + * will give us its ResultRelInfo. + * + * Also, we'll need a slot that will transiently store the tuple being + * routed using the root parent's rowtype. */ if (node && node->operation == CMD_UPDATE) { @@ -140,7 +141,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) } else { - proute->subplan_partition_table = NULL; + proute->subplan_resultrel_hash = NULL; proute->root_tuple_slot = NULL; } @@ -175,7 +176,7 @@ ExecFindPartition(ModifyTableState *mtstate, bool isnull[PARTITION_MAX_KEYS]; Relation rel; PartitionDispatch parent; - PartitionDesc partdesc; + PartitionDesc partdesc; ExprContext *ecxt = GetPerTupleExprContext(estate); TupleTableSlot *ecxt_scantuple_old = ecxt->ecxt_scantuple; @@ -244,9 +245,9 @@ ExecFindPartition(ModifyTableState *mtstate, if (partdesc->is_leaf[partidx]) { /* - * Get the index for PartitionTupleRouting->partitions array index - * for this leaf partition. This may require building a new - * ResultRelInfo. + * Get this leaf partition's index in the + * PartitionTupleRouting->partitions array. We may require + * building a new ResultRelInfo. */ if (likely(parent->indexes[partidx] >= 0)) { @@ -256,12 +257,17 @@ ExecFindPartition(ModifyTableState *mtstate, } else { - if (proute->subplan_partition_table) + /* + * A ResultRelInfo has not been setup for this partition yet, + * so either use one of the sub-plan result rels or create a + * fresh one. + */ + if (proute->subplan_resultrel_hash) { ResultRelInfo *rri; Oid partoid = partdesc->oids[partidx]; - rri = hash_search(proute->subplan_partition_table, + rri = hash_search(proute->subplan_resultrel_hash, &partoid, HASH_FIND, NULL); if (rri) @@ -308,8 +314,8 @@ ExecFindPartition(ModifyTableState *mtstate, PartitionDispatch subparent; subparent = ExecInitPartitionDispatchInfo(proute, - partdesc->oids[partidx], - parent, partidx); + partdesc->oids[partidx], + parent, partidx); Assert(parent->indexes[partidx] >= 0 && parent->indexes[partidx] < proute->num_dispatch); parent = subparent; @@ -328,12 +334,12 @@ static void ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, PartitionTupleRouting *proute) { - ModifyTable *node = (ModifyTable *) mtstate->ps.plan; - ResultRelInfo *subplan_result_rels; - HASHCTL ctl; - HTAB *htab; - int nsubplans; - int i; + ModifyTable *node = (ModifyTable *) mtstate->ps.plan; + ResultRelInfo *subplan_result_rels; + HASHCTL ctl; + HTAB *htab; + int nsubplans; + int i; subplan_result_rels = mtstate->resultRelInfo; nsubplans = list_length(node->plans); @@ -345,9 +351,9 @@ ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, htab = hash_create("PartitionTupleRouting table", nsubplans, &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); - proute->subplan_partition_table = htab; + proute->subplan_resultrel_hash = htab; - /* Hash all subplan by Oid */ + /* Hash all subplans by their Oid */ for (i = 0; i < nsubplans; i++) { ResultRelInfo *rri = &subplan_result_rels[i]; @@ -356,16 +362,15 @@ ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, ResultRelInfo **subplanrri; subplanrri = (ResultRelInfo **) hash_search(htab, &partoid, HASH_ENTER, - &found); + &found); if (!found) *subplanrri = rri; /* - * This is required in order to convert the partition's tuple - * to be compatible with the root partitioned table's tuple - * descriptor. When generating the per-subplan result rels, - * this was not set. + * This is required in order to convert the partition's tuple to be + * compatible with the root partitioned table's tuple descriptor. When + * generating the per-subplan result rels, this was not set. */ rri->ri_PartitionRoot = proute->partition_root; } @@ -378,8 +383,8 @@ ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, static void ExecExpandRoutingArrays(PartitionTupleRouting *proute) { - int new_size = proute->partitions_allocsize * 2; - int old_size = proute->partitions_allocsize; + int new_size = proute->partitions_allocsize * 2; + int old_size = proute->partitions_allocsize; proute->partitions_allocsize = new_size; @@ -389,8 +394,8 @@ ExecExpandRoutingArrays(PartitionTupleRouting *proute) if (proute->parent_child_tupconv_maps != NULL) { proute->parent_child_tupconv_maps = (TupleConversionMap **) - repalloc( proute->parent_child_tupconv_maps, - sizeof(TupleConversionMap *) * new_size); + repalloc(proute->parent_child_tupconv_maps, + sizeof(TupleConversionMap *) * new_size); memset(&proute->parent_child_tupconv_maps[old_size], 0, sizeof(TupleConversionMap *) * (new_size - old_size)); } @@ -827,7 +832,7 @@ ExecInitRoutingInfo(ModifyTableState *mtstate, if (map) { - int new_size; + int new_size; /* Allocate parent child map array only if we need to store a map */ if (proute->parent_child_tupconv_maps == NULL) @@ -858,8 +863,8 @@ ExecInitRoutingInfo(ModifyTableState *mtstate, * Initialize PartitionDispatch for a partitioned table * * This also stores it in the proute->partition_dispatch_info array at the - * specified index ('dispatchidx'), possibly expanding the array if there - * isn't space left in it. + * specified index ('partidx'), possibly expanding the array if there isn't + * space left in it. */ static PartitionDispatch ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, @@ -880,7 +885,8 @@ ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, partdesc = RelationGetPartitionDesc(rel); partkey = RelationGetPartitionKey(rel); - pd = (PartitionDispatch) palloc(sizeof(PartitionDispatchData)); + pd = (PartitionDispatch) palloc(offsetof(PartitionDispatchData, indexes) + + (partdesc->nparts * sizeof(int))); pd->reldesc = rel; pd->key = partkey; pd->keystate = NIL; @@ -897,9 +903,9 @@ ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, */ pd->tupslot = MakeSingleTupleTableSlot(tupdesc); pd->tupmap = - convert_tuples_by_name(RelationGetDescr(parent_pd->reldesc), - tupdesc, - gettext_noop("could not convert row type")); + convert_tuples_by_name(RelationGetDescr(parent_pd->reldesc), + tupdesc, + gettext_noop("could not convert row type")); } else { @@ -908,8 +914,6 @@ ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, pd->tupmap = NULL; } - pd->indexes = (int *) palloc(sizeof(int) * partdesc->nparts); - /* * Initialize with -1 to signify that the corresponding partition's * ResultRelInfo or PartitionDispatch has not been created yet. @@ -1046,6 +1050,7 @@ void ExecCleanupTupleRouting(ModifyTableState *mtstate, PartitionTupleRouting *proute) { + HTAB *resultrel_hash = proute->subplan_resultrel_hash; int i; /* @@ -1078,15 +1083,14 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate, * Check if this result rel is one belonging to the node's subplans, * if so, let ExecEndPlan() clean it up. */ - if (proute->subplan_partition_table) + if (resultrel_hash) { Oid partoid; bool found; partoid = RelationGetRelid(resultRelInfo->ri_RelationDesc); - (void) hash_search(proute->subplan_partition_table, &partoid, - HASH_FIND, &found); + (void) hash_search(resultrel_hash, &partoid, HASH_FIND, &found); if (found) continue; } diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 6e0c7862dc..4f7cea7668 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1779,12 +1779,9 @@ ExecPrepareTupleRouting(ModifyTableState *mtstate, /* * Convert the tuple, if necessary. */ - ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps ? - proute->parent_child_tupconv_maps[partidx] : - NULL, - tuple, - proute->partition_tuple_slot, - &slot); + if (proute->parent_child_tupconv_maps) + ConvertPartitionTupleSlot(proute->parent_child_tupconv_maps[partidx], + tuple, proute->partition_tuple_slot, &slot); /* Initialize information needed to handle ON CONFLICT DO UPDATE. */ Assert(mtstate != NULL); diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 4cc7508067..4b3b5ae770 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -27,8 +27,7 @@ typedef struct PartitionDescData { int nparts; /* Number of partitions */ Oid *oids; /* Array of length 'nparts' containing - * partition OIDs in order of the their - * bounds */ + * partition OIDs in order of the their bounds */ bool *is_leaf; /* Array of 'nparts' elements storing whether * a partition is a leaf partition or not */ PartitionBoundInfo boundinfo; /* collection of partition bounds */ diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h index 1b421f2ec5..d921ab6ca0 100644 --- a/src/include/executor/execPartition.h +++ b/src/include/executor/execPartition.h @@ -48,7 +48,7 @@ typedef struct PartitionDispatchData PartitionDesc partdesc; TupleTableSlot *tupslot; TupleConversionMap *tupmap; - int *indexes; + int indexes[FLEXIBLE_ARRAY_MEMBER]; } PartitionDispatchData; typedef struct PartitionDispatchData *PartitionDispatch; @@ -58,23 +58,23 @@ typedef struct PartitionDispatchData *PartitionDispatch; * route a tuple inserted into a partitioned table to one of its leaf * partitions * - * partition_root Root table, that is, the table mentioned in the + * partition_root Root table, that is, the table mentioned in the * command. * - * partition_dispatch_info Contains PartitionDispatch objects for every + * partition_dispatch_info Contains PartitionDispatch objects for every * partitioned table touched by tuple routing. The * entry for the root partitioned table is *always* * present as the first entry of this array. * - * num_dispatch The number of existing entries and also serves as + * num_dispatch The number of existing entries and also serves as * the index of the next entry to be allocated and * placed in 'partition_dispatch_info'. * - * dispatch_allocsize (>= 'num_dispatch') is the number of entries that + * dispatch_allocsize (>= 'num_dispatch') is the number of entries that * can be stored in 'partition_dispatch_info' before * needing to reallocate more space. * - * partitions Contains pointers to a ResultRelInfos of all leaf + * partitions Contains pointers to a ResultRelInfos of all leaf * partitions touched by tuple routing. Some of * these are pointers to "reused" ResultRelInfos, * that is, those that are created and destroyed @@ -83,18 +83,18 @@ typedef struct PartitionDispatchData *PartitionDispatch; * the partition key. Rest of them are pointers to * ResultRelInfos managed by execPartition.c itself * - * num_partitions The number of existing entries and also serves as + * num_partitions The number of existing entries and also serves as * the index of the next entry to be allocated and * placed in 'partitions' * - * partitions_allocsize (>= 'num_partitions') is the number of entries + * partitions_allocsize (>= 'num_partitions') is the number of entries * that can be stored in 'partitions', * 'parent_child_tupconv_maps', * 'child_parent_tupconv_maps' and * 'child_parent_map_not_required' arrays before * needing to reallocate more space * - * parent_child_tupconv_maps Contains information to convert tuples of the + * parent_child_tupconv_maps Contains information to convert tuples of the * root parent's rowtype to those of the leaf * partitions' rowtype, but only for those partitions * whose TupleDescs are physically different from the @@ -105,7 +105,7 @@ typedef struct PartitionDispatchData *PartitionDispatch; * need not be more of these maps than there are * partitions that were touched. * - * partition_tuple_slot This is a tuple slot used to store a tuple using + * partition_tuple_slot This is a tuple slot used to store a tuple using * rowtype of the partition chosen by tuple * routing. Maintained separately because partitions * may have different rowtype. @@ -113,7 +113,7 @@ typedef struct PartitionDispatchData *PartitionDispatch; * Note: The following fields are used only when UPDATE ends up needing to * do tuple routing. * - * child_parent_tupconv_maps Information to convert tuples of the leaf + * child_parent_tupconv_maps Information to convert tuples of the leaf * partitions' rowtype to the root parent's rowtype. * These are needed by transition table machinery * when storing tuples of partition's rowtype into @@ -124,14 +124,14 @@ typedef struct PartitionDispatchData *PartitionDispatch; * needed a conversion map. Also, if non-NULL, is of * the same size as 'partitions'. * - * child_parent_map_not_required Stores if we don't need a conversion + * child_parent_map_not_required Stores if we don't need a conversion * map for a partition so that TupConvMapForLeaf * can return without having to re-check if it needs * to build a map. * - * subplan_partition_table Hash table to store subplan index by Oid. + * subplan_resultrel_hash Hash table to store subplan index by Oid. * - * root_tuple_slot During UPDATE tuple routing, this tuple slot is + * root_tuple_slot During UPDATE tuple routing, this tuple slot is * used to transiently store a tuple using the root * table's rowtype after converting it from the * tuple's source leaf partition's rowtype. That is, @@ -151,7 +151,7 @@ typedef struct PartitionTupleRouting TupleConversionMap **parent_child_tupconv_maps; TupleConversionMap **child_parent_tupconv_maps; bool *child_parent_map_not_required; - HTAB *subplan_partition_table; + HTAB *subplan_resultrel_hash; TupleTableSlot *root_tuple_slot; TupleTableSlot *partition_tuple_slot; } PartitionTupleRouting;