diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 606c920b06..a775bda553 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -1246,12 +1246,14 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 	int			part_index = -1;
 	PartitionKey key = pd->key;
 	PartitionDesc partdesc = pd->partdesc;
+	int			cached_off = partdesc->cached_bound_offset;
 	PartitionBoundInfo boundinfo = partdesc->boundinfo;
 
 	/* Route as appropriate based on partitioning strategy. */
 	switch (key->strategy)
 	{
 		case PARTITION_STRATEGY_HASH:
+			Assert(cached_off < 0);
 			{
 				uint64		rowHash;
 
@@ -1272,14 +1274,33 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 			}
 			else
 			{
-				bool		equal = false;
-
-				bound_offset = partition_list_bsearch(key->partsupfunc,
-													  key->partcollation,
-													  boundinfo,
-													  values[0], &equal);
-				if (bound_offset >= 0 && equal)
-					part_index = boundinfo->indexes[bound_offset];
+				if (cached_off >= 0)
+				{
+					Datum	bound_datum = boundinfo->datums[cached_off][0];
+					int32	cmpval;
+
+					cmpval = DatumGetInt32(FunctionCall2Coll(&key->partsupfunc[0],
+															 key->partcollation[0],
+															 bound_datum,
+															 values[0]));
+					if (cmpval == 0)
+						part_index = boundinfo->indexes[cached_off];
+				}
+
+				if (part_index < 0)
+				{
+					bool		equal = false;
+
+					bound_offset = partition_list_bsearch(key->partsupfunc,
+														  key->partcollation,
+														  boundinfo,
+														  values[0], &equal);
+					if (bound_offset >= 0 && equal)
+					{
+						part_index = boundinfo->indexes[bound_offset];
+						partdesc->cached_bound_offset = bound_offset;
+					}
+				}
 			}
 			break;
 
@@ -1304,20 +1325,56 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 
 				if (!range_partkey_has_null)
 				{
-					bound_offset = partition_range_datum_bsearch(key->partsupfunc,
-																 key->partcollation,
-																 boundinfo,
-																 key->partnatts,
-																 values,
-																 &equal);
+					if (cached_off >= 0)
+					{
+						Datum   *bound_datums = boundinfo->datums[cached_off];
+						PartitionRangeDatumKind *bound_kind = boundinfo->kind[cached_off];
+						int32	cmpval;
+
+						/* Check if the value is above the low bound */
+						cmpval = partition_rbound_datum_cmp(key->partsupfunc,
+															key->partcollation,
+															bound_datums,
+															bound_kind,
+															values,
+															key->partnatts);
+						if (cmpval == 0)
+							part_index = boundinfo->indexes[cached_off + 1];
+						else if (cmpval < 0 && cached_off + 1 < boundinfo->ndatums)
+						{
+							/* Check if the value is below the high bound */
+							bound_datums = boundinfo->datums[cached_off + 1];
+							bound_kind = boundinfo->kind[cached_off + 1];
+							cmpval = partition_rbound_datum_cmp(key->partsupfunc,
+																key->partcollation,
+																bound_datums,
+																bound_kind,
+																values,
+																key->partnatts);
+
+							if (cmpval > 0)
+								part_index = boundinfo->indexes[cached_off + 1];
+						}
+					}
 
-					/*
-					 * The bound at bound_offset is less than or equal to the
-					 * tuple value, so the bound at offset+1 is the upper
-					 * bound of the partition we're looking for, if there
-					 * actually exists one.
-					 */
-					part_index = boundinfo->indexes[bound_offset + 1];
+					if (part_index < 0)
+					{
+						bound_offset = partition_range_datum_bsearch(key->partsupfunc,
+																	 key->partcollation,
+																	 boundinfo,
+																	 key->partnatts,
+																	 values,
+																	 &equal);
+
+						/*
+						 * The bound at bound_offset is less than or equal to the
+						 * tuple value, so the bound at offset+1 is the upper
+						 * bound of the partition we're looking for, if there
+						 * actually exists one.
+						 */
+						part_index = boundinfo->indexes[bound_offset + 1];
+						partdesc->cached_bound_offset = bound_offset;
+					}
 				}
 			}
 			break;
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index 9a9d6a9643..e6ec01de71 100644
--- a/src/backend/partitioning/partdesc.c
+++ b/src/backend/partitioning/partdesc.c
@@ -285,6 +285,8 @@ RelationBuildPartitionDesc(Relation rel, bool omit_detached)
 		MemoryContextAllocZero(new_pdcxt, sizeof(PartitionDescData));
 	partdesc->nparts = nparts;
 	partdesc->detached_exist = detached_exist;
+	partdesc->cached_bound_offset = -1;
+
 	/* If there are no partitions, the rest of the partdesc can stay zero */
 	if (nparts > 0)
 	{
diff --git a/src/include/partitioning/partdesc.h b/src/include/partitioning/partdesc.h
index 0792f48507..9df28ae14f 100644
--- a/src/include/partitioning/partdesc.h
+++ b/src/include/partitioning/partdesc.h
@@ -36,6 +36,9 @@ typedef struct PartitionDescData
 								 * the corresponding 'oids' element belongs to
 								 * a leaf partition or not */
 	PartitionBoundInfo boundinfo;	/* collection of partition bounds */
+	int			cached_bound_offset; /* offset of the bound datum most
+									  * recently chosen by
+									  * get_partition_for_tuple() */
 } PartitionDescData;
 
 
