diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 606c920b06..2348eb3154 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -133,6 +133,12 @@ struct PartitionTupleRouting * routing it through this table). A NULL value is stored if no tuple * conversion is required. * + * lastPartInfo + * If non-NULL, ResultRelInfo for the partition that was most recently + * chosen as the routing target; ExecFindPartition() checks if the + * same one can be used for the current row before applying the tuple- + * routing algorithm to it. + * * indexes * Array of partdesc->nparts elements. For leaf partitions the index * corresponds to the partition's ResultRelInfo in the encapsulating @@ -150,6 +156,7 @@ typedef struct PartitionDispatchData PartitionDesc partdesc; TupleTableSlot *tupslot; AttrMap *tupmap; + ResultRelInfo *lastPartInfo; int indexes[FLEXIBLE_ARRAY_MEMBER]; } PartitionDispatchData; @@ -291,6 +298,35 @@ ExecFindPartition(ModifyTableState *mtstate, CHECK_FOR_INTERRUPTS(); + /* + * Check if the saved partition accepts this tuple by evaluating its + * partition constraint against the tuple. If it does, we save a trip + * to get_partition_for_tuple(), which can be a slightly more expensive + * way to get the same partition, especially if there are many + * partitions to search through. + */ + if (dispatch->lastPartInfo) + { + TupleTableSlot *tmpslot; + TupleConversionMap *map; + + rri = dispatch->lastPartInfo; + map = rri->ri_RootToPartitionMap; + if (map) + tmpslot = execute_attr_map_slot(map->attrMap, rootslot, + rri->ri_PartitionTupleSlot); + else + tmpslot = rootslot; + if (ExecPartitionCheck(rri, tmpslot, estate, false)) + { + /* and restore ecxt's scantuple */ + ecxt->ecxt_scantuple = ecxt_scantuple_saved; + MemoryContextSwitchTo(oldcxt); + return rri; + } + dispatch->lastPartInfo = rri = NULL; + } + rel = dispatch->reldesc; partdesc = dispatch->partdesc; @@ -372,6 +408,19 @@ ExecFindPartition(ModifyTableState *mtstate, } Assert(rri != NULL); + /* + * Remember this partition for the next tuple inserted into this + * parent; see at the top of this loop how it's decided whether + * the next tuple can indeed reuse this partition. + * + * Do this only if we have range/list partitions, because only + * in that case it's conceivable that consecutively inserted rows + * tend to go into the same partition. + */ + if ((dispatch->key->strategy == PARTITION_STRATEGY_RANGE || + dispatch->key->strategy == PARTITION_STRATEGY_LIST)) + dispatch->lastPartInfo = rri; + /* Signal to terminate the loop */ dispatch = NULL; } @@ -1051,6 +1100,8 @@ ExecInitPartitionDispatchInfo(EState *estate, pd->tupslot = NULL; } + pd->lastPartInfo = NULL; + /* * Initialize with -1 to signify that the corresponding partition's * ResultRelInfo or PartitionDispatch has not been created yet.