diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32d2461528..22a814bcbe 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1343,7 +1343,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_PartitionCheck = partition_check; resultRelInfo->ri_PartitionRoot = partition_root; - resultRelInfo->ri_PartitionInfo = NULL; /* May be set later */ + resultRelInfo->ri_PartitionInfo = NULL; /* may be set later */ } /* diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index b2d394676f..592daab1be 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -38,48 +38,46 @@ * route a tuple inserted into a partitioned table to one of its leaf * partitions. * - * partition_root The partitioned table that's the target of the - * command. + * partition_root + * The partitioned table that's the target of the command. * - * partition_dispatch_info Array of 'dispatch_allocsize' elements containing - * a pointer to a PartitionDispatch object for every - * partitioned table touched by tuple routing. The - * entry for the target partitioned table is *always* - * present in the 0th element of this array. See - * comment for PartitionDispatchData->indexes for - * details on how this array is indexed. + * partition_dispatch_info + * Array of 'max_dispatch' elements containing a pointer to a + * PartitionDispatch object for every partitioned table touched by tuple + * routing. The entry for the target partitioned table is *always* + * present in the 0th element of this array. See comment for + * PartitionDispatchData->indexes for details on how this array is + * indexed. * - * num_dispatch The current number of items stored in the - * 'partition_dispatch_info' array. Also serves as - * the index of the next free array element for new - * PartitionDispatch objects that need to be stored. + * num_dispatch + * The current number of items stored in the 'partition_dispatch_info' + * array. Also serves as the index of the next free array element for + * new PartitionDispatch objects that need to be stored. * - * dispatch_allocsize The current allocated size of the - * 'partition_dispatch_info' array. + * max_dispatch + * The current allocated size of the 'partition_dispatch_info' array. * - * partitions Array of 'partitions_allocsize' elements - * containing a pointer to a ResultRelInfo for every - * leaf partitions touched by tuple routing. Some of - * these are pointers to ResultRelInfos which are - * borrowed out of 'subplan_resultrel_hash'. The - * remainder have been built especially for tuple - * routing. See comment for - * PartitionDispatchData->indexes for details on how - * this array is indexed. + * partitions + * Array of 'max_partitions' elements containing a pointer to a + * ResultRelInfo for every leaf partitions touched by tuple routing. + * Some of these are pointers to ResultRelInfos which are borrowed out of + * 'subplan_resultrel_hash'. The remainder have been built especially + * for tuple routing. See comment for PartitionDispatchData->indexes for + * details on how this array is indexed. * - * num_partitions The current number of items stored in the - * 'partitions' array. Also serves as the index of - * the next free array element for new ResultRelInfo - * objects that need to be stored. + * num_partitions + * The current number of items stored in the 'partitions' array. Also + * serves as the index of the next free array element for new + * ResultRelInfo objects that need to be stored. * - * partitions_allocsize The current allocated size of the 'partitions' - * array. + * max_partitions + * The current allocated size of the 'partitions' array. * - * subplan_resultrel_hash Hash table to store subplan ResultRelInfos by Oid. - * This is used to cache ResultRelInfos from subplans - * of an UPDATE ModifyTable node. Some of these may - * be useful for tuple routing to save having to build - * duplicates. + * subplan_resultrel_hash + * Hash table to store subplan ResultRelInfos by Oid. This is used to + * cache ResultRelInfos from subplans of an UPDATE ModifyTable node; + * NULL in other cases. Some of these may be useful for tuple routing + * to save having to build duplicates. *----------------------- */ typedef struct PartitionTupleRouting @@ -87,10 +85,10 @@ typedef struct PartitionTupleRouting Relation partition_root; PartitionDispatch *partition_dispatch_info; int num_dispatch; - int dispatch_allocsize; + int max_dispatch; ResultRelInfo **partitions; int num_partitions; - int partitions_allocsize; + int max_partitions; HTAB *subplan_resultrel_hash; } PartitionTupleRouting; @@ -132,11 +130,16 @@ typedef struct PartitionDispatchData int indexes[FLEXIBLE_ARRAY_MEMBER]; } PartitionDispatchData; +/* struct to hold result relations coming from UPDATE subplans */ +typedef struct SubplanResultRelHashElem +{ + Oid relid; /* hash key -- must be first */ + ResultRelInfo *rri; +} SubplanResultRelHashElem; + static void ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, PartitionTupleRouting *proute); -static void ExecCheckPartitionArraySpace(PartitionTupleRouting *proute); -static void ExecCheckDispatchArraySpace(PartitionTupleRouting *proute); static ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate, ResultRelInfo *rootResultRelInfo, PartitionTupleRouting *proute, @@ -147,6 +150,9 @@ static void ExecInitRoutingInfo(ModifyTableState *mtstate, ResultRelInfo *partRelInfo); static PartitionDispatch ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, PartitionDispatch parent_pd, int partidx); +static void ExecRememberPartitionRel(EState *estate, PartitionTupleRouting *proute, + int partidx, ResultRelInfo *rri, + PartitionDispatch dispatch); static void FormPartitionKeyDatum(PartitionDispatch pd, TupleTableSlot *slot, EState *estate, @@ -192,39 +198,17 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) * demand, only when we actually need to route a tuple to that partition. * The reason for this is that a common case is for INSERT to insert a * single tuple into a partitioned table and this must be fast. - * - * We initially size the 'partition_dispatch_info' and 'partitions' arrays - * to allow storage of PARTITION_ROUTING_INITSIZE pointers. If we route - * tuples to more than this many partitions or through more than that many - * sub-partitioned tables then we'll need to increase the size of these - * arrays. - * - * Initially we must only set up 1 PartitionDispatch object; the one for - * the partitioned table that's the target of the command. If we must - * route a tuple via some sub-partitioned table, then its - * PartitionDispatch is only built the first time it's required. */ - proute = (PartitionTupleRouting *) palloc(sizeof(PartitionTupleRouting)); + proute = (PartitionTupleRouting *) palloc0(sizeof(PartitionTupleRouting)); proute->partition_root = rel; - proute->partition_dispatch_info = (PartitionDispatchData **) - 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); - - /* Mark that no items are yet stored in the 'partitions' array. */ - proute->num_partitions = 0; - proute->partitions_allocsize = PARTITION_ROUTING_INITSIZE; + /* Rest of members initialized by zeroing */ /* * Initialize this table's PartitionDispatch object. Here we pass in the * parent as NULL as we don't need to care about any parent of the target * partitioned table. */ - (void) ExecInitPartitionDispatchInfo(proute, RelationGetRelid(rel), NULL, - 0); + ExecInitPartitionDispatchInfo(proute, RelationGetRelid(rel), NULL, 0); /* * If performing an UPDATE with tuple routing, we can reuse partition @@ -236,8 +220,6 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate, Relation rel) */ if (node && node->operation == CMD_UPDATE) ExecHashSubPlanResultRelsByOid(mtstate, proute); - else - proute->subplan_resultrel_hash = NULL; return proute; } @@ -292,6 +274,8 @@ ExecFindPartition(ModifyTableState *mtstate, AttrNumber *map = dispatch->tupmap; int partidx = -1; + CHECK_FOR_INTERRUPTS(); + rel = dispatch->reldesc; partdesc = dispatch->partdesc; @@ -319,7 +303,7 @@ ExecFindPartition(ModifyTableState *mtstate, /* * If this partitioned table has no partitions or no partition for - * these values, then error out. + * these values, error out. */ if (partdesc->nparts == 0 || (partidx = get_partition_for_tuple(dispatch, values, isnull)) < 0) @@ -333,7 +317,9 @@ ExecFindPartition(ModifyTableState *mtstate, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("no partition of relation \"%s\" found for row", RelationGetRelationName(rel)), - val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0)); + val_desc ? + errdetail("Partition key of the failing row contains %s.", + val_desc) : 0)); } if (partdesc->is_leaf[partidx]) @@ -352,55 +338,39 @@ ExecFindPartition(ModifyTableState *mtstate, } else { - int rri_index = -1; + bool found = false; /* - * A ResultRelInfo has not been set up for this partition yet, - * so either use one of the sub-plan result rels or build a - * new one. + * We have not yet set up a ResultRelInfo for this partition, + * but if we have a subplan hash table, we might have one + * there. If not, we'll have to create one. */ if (proute->subplan_resultrel_hash) { Oid partoid = partdesc->oids[partidx]; + SubplanResultRelHashElem *elem; - rri = hash_search(proute->subplan_resultrel_hash, - &partoid, HASH_FIND, NULL); - - if (rri) + elem = hash_search(proute->subplan_resultrel_hash, + &partoid, HASH_FIND, NULL); + if (elem) { - /* Found one! */ + found = true; + rri = elem->rri; /* Verify this ResultRelInfo allows INSERTs */ CheckValidResultRel(rri, CMD_INSERT); - /* This shouldn't have be set up yet */ - Assert(rri->ri_PartitionInfo == NULL); - /* Set up the PartitionRoutingInfo for it */ ExecInitRoutingInfo(mtstate, estate, rri); - - rri_index = proute->num_partitions++; - dispatch->indexes[partidx] = rri_index; - - ExecCheckPartitionArraySpace(proute); - - /* - * Store it in the partitions array so we don't have - * to look it up again. - */ - proute->partitions[rri_index] = rri; + ExecRememberPartitionRel(estate, proute, partidx, rri, dispatch); } } /* We need to create a new one. */ - if (rri_index < 0) - { - MemoryContextSwitchTo(oldcxt); + if (!found) rri = ExecInitPartitionInfo(mtstate, rootResultRelInfo, proute, estate, dispatch, partidx); - MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - } } /* Release the tuple in the lowest parent's dedicated slot. */ @@ -460,38 +430,31 @@ 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; - subplan_result_rels = mtstate->resultRelInfo; - nsubplans = list_length(node->plans); - memset(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); - ctl.entrysize = sizeof(ResultRelInfo **); + ctl.entrysize = sizeof(SubplanResultRelHashElem); ctl.hcxt = CurrentMemoryContext; - htab = hash_create("PartitionTupleRouting table", nsubplans, &ctl, - HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); + htab = hash_create("PartitionTupleRouting table", mtstate->mt_nplans, + &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); proute->subplan_resultrel_hash = htab; /* Hash all subplans by their Oid */ - for (i = 0; i < nsubplans; i++) + for (i = 0; i < mtstate->mt_nplans; i++) { - ResultRelInfo *rri = &subplan_result_rels[i]; + ResultRelInfo *rri = &mtstate->resultRelInfo[i]; bool found; Oid partoid = RelationGetRelid(rri->ri_RelationDesc); - ResultRelInfo **subplanrri; + SubplanResultRelHashElem *elem; - subplanrri = (ResultRelInfo **) hash_search(htab, &partoid, HASH_ENTER, - &found); - - if (!found) - *subplanrri = rri; + elem = (SubplanResultRelHashElem *) + hash_search(htab, &partoid, HASH_ENTER, &found); + Assert(!found); + elem->rri = rri; /* * This is required in order to convert the partition's tuple to be @@ -503,41 +466,6 @@ ExecHashSubPlanResultRelsByOid(ModifyTableState *mtstate, } /* - * ExecCheckPartitionArraySpace - * Ensure there's enough space in the proute->partitions array - */ -static void -ExecCheckPartitionArraySpace(PartitionTupleRouting *proute) -{ - if (proute->num_partitions >= proute->partitions_allocsize) - { - proute->partitions_allocsize *= 2; - proute->partitions = (ResultRelInfo **) - repalloc(proute->partitions, sizeof(ResultRelInfo *) * - proute->partitions_allocsize); - } -} - -/* - * ExecCheckDispatchArraySpace - * Ensure there's enough space in the proute->partition_dispatch_info - * array. - */ -static void -ExecCheckDispatchArraySpace(PartitionTupleRouting *proute) -{ - if (proute->num_dispatch >= proute->dispatch_allocsize) - { - /* Expand allocated space. */ - proute->dispatch_allocsize *= 2; - proute->partition_dispatch_info = (PartitionDispatchData **) - repalloc(proute->partition_dispatch_info, - sizeof(PartitionDispatchData *) * - proute->dispatch_allocsize); - } -} - -/* * ExecInitPartitionInfo * Initialize ResultRelInfo and other information for a partition * and store it in the next empty slot in the proute->partitions array. @@ -559,7 +487,6 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, MemoryContext oldContext; AttrNumber *part_attnos = NULL; bool found_whole_row; - int part_result_rel_index; /* * We locked all the partitions in ExecSetupPartitionTupleRouting @@ -903,13 +830,7 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, } } - part_result_rel_index = proute->num_partitions++; - dispatch->indexes[partidx] = part_result_rel_index; - - ExecCheckPartitionArraySpace(proute); - - /* Save here for later use. */ - proute->partitions[part_result_rel_index] = leaf_part_rri; + ExecRememberPartitionRel(estate, proute, partidx, leaf_part_rri, dispatch); MemoryContextSwitchTo(oldContext); @@ -1018,8 +939,8 @@ ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, rel = proute->partition_root; partdesc = RelationGetPartitionDesc(rel); - pd = (PartitionDispatch) palloc(offsetof(PartitionDispatchData, indexes) - + (partdesc->nparts * sizeof(int))); + pd = (PartitionDispatch) palloc(offsetof(PartitionDispatchData, indexes) + + partdesc->nparts * sizeof(int)); pd->reldesc = rel; pd->key = RelationGetPartitionKey(rel); pd->keystate = NIL; @@ -1045,8 +966,8 @@ ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, else { /* Not required for the root partitioned table */ - pd->tupslot = NULL; pd->tupmap = NULL; + pd->tupslot = NULL; } /* @@ -1055,25 +976,79 @@ ExecInitPartitionDispatchInfo(PartitionTupleRouting *proute, Oid partoid, */ memset(pd->indexes, -1, sizeof(int) * partdesc->nparts); + /* Track in PartitionTupleRouting for later use */ dispatchidx = proute->num_dispatch++; - ExecCheckDispatchArraySpace(proute); - - /* Save here for later use. */ + /* Allocate or enlarge the array, as needed */ + if (proute->num_dispatch >= proute->max_dispatch) + { + if (proute->max_dispatch == 0) + { + proute->max_dispatch = PARTITION_ROUTING_INITSIZE; + proute->partition_dispatch_info = (PartitionDispatch *) + palloc(sizeof(PartitionDispatch) * proute->max_dispatch); + } + else + { + proute->max_dispatch *= 2; + proute->partition_dispatch_info = (PartitionDispatch *) + repalloc(proute->partition_dispatch_info, + sizeof(PartitionDispatch) * proute->max_dispatch); + } + } proute->partition_dispatch_info[dispatchidx] = pd; /* * Finally, if setting up a PartitionDispatch for a sub-partitioned table, - * install the link to allow us to descend the partition hierarchy for - * future searches + * install a downlink in the parent to allow quick descent. */ if (parent_pd) + { + Assert(parent_pd->indexes[partidx] == -1); parent_pd->indexes[partidx] = dispatchidx; + } return pd; } /* + * Store the given ResultRelInfo as corresponding to partition partidx in + * proute, tracking which array item was used in dispatch->indexes. + */ +static void +ExecRememberPartitionRel(EState *estate, PartitionTupleRouting *proute, int partidx, + ResultRelInfo *rri, PartitionDispatch dispatch) +{ + int rri_index; + + Assert(dispatch->indexes[partidx] == -1); + + rri_index = proute->num_partitions++; + + /* Allocate or enlarge the array, as needed */ + if (proute->num_partitions >= proute->max_partitions) + { + if (proute->max_partitions == 0) + { + proute->max_partitions = PARTITION_ROUTING_INITSIZE; + proute->partitions = (ResultRelInfo **) + MemoryContextAlloc(estate->es_query_cxt, + sizeof(ResultRelInfo *) * proute->max_partitions); + } + else + { + proute->max_partitions *= 2; + proute->partitions = (ResultRelInfo **) + repalloc(proute->partitions, sizeof(ResultRelInfo *) * + proute->max_partitions); + } + } + + proute->partitions[rri_index] = rri; + dispatch->indexes[partidx] = rri_index; +} + +/* * ExecCleanupTupleRouting -- Clean up objects allocated for partition tuple * routing. * @@ -1107,7 +1082,6 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate, { ResultRelInfo *resultRelInfo = proute->partitions[i]; - /* * Check if this result rel is one belonging to the node's subplans, * if so, let ExecEndPlan() clean it up. diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 423118cbbc..de27d88e63 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -34,7 +34,6 @@ struct PlanState; /* forward references in this file */ struct PartitionRoutingInfo; -struct PartitionTupleRouting; struct ParallelHashJoinState; struct ExecRowMark; struct ExprState; @@ -471,7 +470,7 @@ typedef struct ResultRelInfo /* relation descriptor for root partitioned table */ Relation ri_PartitionRoot; - /* Additional information that's specific to partition tuple routing */ + /* Additional information specific to partition tuple routing */ struct PartitionRoutingInfo *ri_PartitionInfo; } ResultRelInfo;