From 7d0bd28f75cf2b26706fe5cc8473aa01950286cb Mon Sep 17 00:00:00 2001
From: "houzj.fnst" <houzj.fnst@cn.fujitsu.com>
Date: Mon, 7 Jun 2021 18:57:14 +0800
Subject: [PATCH] cache-bound-offset_v3_modify

---
 src/backend/executor/execPartition.c | 149 ++++++++++++++++++++++-----
 src/backend/partitioning/partdesc.c  |   2 +
 src/include/partitioning/partdesc.h  |   3 +
 3 files changed, 130 insertions(+), 24 deletions(-)

diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 606c920b06..122d330a1d 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -150,9 +150,15 @@ typedef struct PartitionDispatchData
 	PartitionDesc partdesc;
 	TupleTableSlot *tupslot;
 	AttrMap    *tupmap;
+
+	short		ntupinserts;
+	short		npartchanges;
+	bool		force;
+	bool		check_cached;
 	int			indexes[FLEXIBLE_ARRAY_MEMBER];
 }			PartitionDispatchData;
 
+#define RECHECK_BOUND_CACHE_THRESHOLD 1000
 
 static ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate,
 											EState *estate, PartitionTupleRouting *proute,
@@ -176,7 +182,7 @@ static void FormPartitionKeyDatum(PartitionDispatch pd,
 								  Datum *values,
 								  bool *isnull);
 static int	get_partition_for_tuple(PartitionDispatch pd, Datum *values,
-									bool *isnull);
+									bool *isnull, bool check_cached);
 static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
 												  Datum *values,
 												  bool *isnull,
@@ -305,12 +311,23 @@ ExecFindPartition(ModifyTableState *mtstate,
 		ecxt->ecxt_scantuple = slot;
 		FormPartitionKeyDatum(dispatch, slot, estate, values, isnull);
 
+		if (dispatch->ntupinserts > RECHECK_BOUND_CACHE_THRESHOLD &&
+				!dispatch->force)
+		{
+			if (dispatch->npartchanges == 0)
+				dispatch->check_cached = true;
+			else
+				dispatch->check_cached = ((dispatch->ntupinserts/dispatch->npartchanges) > 1);
+			dispatch->ntupinserts = dispatch->npartchanges = 0;
+		}
+
 		/*
 		 * If this partitioned table has no partitions or no partition for
 		 * these values, error out.
 		 */
 		if (partdesc->nparts == 0 ||
-			(partidx = get_partition_for_tuple(dispatch, values, isnull)) < 0)
+			(partidx = get_partition_for_tuple(dispatch, values, isnull,
+							dispatch->check_cached)) < 0)
 		{
 			char	   *val_desc;
 
@@ -1026,6 +1043,12 @@ ExecInitPartitionDispatchInfo(EState *estate,
 	pd->key = RelationGetPartitionKey(rel);
 	pd->keystate = NIL;
 	pd->partdesc = partdesc;
+	pd->ntupinserts = 0;
+	pd->npartchanges = 0;
+	partdesc->cached_bound_offset = -1;
+
+	pd->check_cached = false;
+
 	if (parent_pd != NULL)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(rel);
@@ -1236,11 +1259,16 @@ FormPartitionKeyDatum(PartitionDispatch pd,
  *		Finds partition of relation which accepts the partition key specified
  *		in values and isnull
  *
+ * If check_cached is true, this short-circuits a full-blown search across all
+ * the bounds, instead checking the tuple against the bound whose offset is
+ * cached in pd->partdesc.
+ *
  * Return value is index of the partition (>= 0 and < partdesc->nparts) if one
  * found or -1 if none found.
  */
 static int
-get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
+get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull,
+						bool check_cached)
 {
 	int			bound_offset;
 	int			part_index = -1;
@@ -1248,10 +1276,18 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 	PartitionDesc partdesc = pd->partdesc;
 	PartitionBoundInfo boundinfo = partdesc->boundinfo;
 
+	/*
+	 * If the cached bound offset is valid, we check below if the that bound
+	 * is satisfied by the new tuple.  If it is, there's no need to perform a
+	 * search across all bounds.
+	 */
+	int			cached_off = partdesc->cached_bound_offset;
+
 	/* Route as appropriate based on partitioning strategy. */
 	switch (key->strategy)
 	{
 		case PARTITION_STRATEGY_HASH:
+			Assert(cached_off < 0);
 			{
 				uint64		rowHash;
 
@@ -1265,6 +1301,7 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 			break;
 
 		case PARTITION_STRATEGY_LIST:
+			pd->ntupinserts++;
 			if (isnull[0])
 			{
 				if (partition_bound_accepts_nulls(boundinfo))
@@ -1272,14 +1309,37 @@ 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 && check_cached)
+				{
+					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];
+						if (cached_off != bound_offset)
+						{
+							pd->npartchanges++;
+							partdesc->cached_bound_offset = bound_offset;
+						}
+					}
+				}
 			}
 			break;
 
@@ -1289,6 +1349,7 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 							range_partkey_has_null = false;
 				int			i;
 
+				pd->ntupinserts++;
 				/*
 				 * No range includes NULL, so this will be accepted by the
 				 * default partition if there is one, and otherwise rejected.
@@ -1304,20 +1365,60 @@ 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 && check_cached)
+					{
+						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];
+						if (cached_off != bound_offset)
+						{
+							pd->npartchanges++;
+							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;
 
 
-- 
2.18.4

