From b86db0abe56ee6126c3dbf5e3f59da9baaff594f Mon Sep 17 00:00:00 2001
From: houzj <houzj.fnst@fujitsu.com>
Date: Tue, 1 Jun 2021 14:25:21 +0800
Subject: [PATCH] cache-bound-offset

Cached the bound offset in PartitionDescData.

Everytime we try to find a partition, we first use the cached offset to get
the target bound datum(low bound value and low bound value) from
PartitionBoundInfoData and check if the partition key value match the bound datum.
If match, we skip the get_partition_for_tuple.

Currently, only cache the bound offset in LIST and RANGE partition strategy.

---
 src/backend/executor/execPartition.c | 170 +++++++++++++++++++++++++++++------
 src/backend/partitioning/partdesc.c  |   2 +
 src/include/partitioning/partdesc.h  |   2 +
 3 files changed, 147 insertions(+), 27 deletions(-)

diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 606c920..7885f5d 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -176,7 +176,9 @@ static void FormPartitionKeyDatum(PartitionDispatch pd,
 								  Datum *values,
 								  bool *isnull);
 static int	get_partition_for_tuple(PartitionDispatch pd, Datum *values,
-									bool *isnull);
+									bool *isnull, int *bound_offset);
+static int check_partition_for_tuple(PartitionDispatch pd, Datum *values,
+									  bool *isnull, int partidx);
 static char *ExecBuildSlotPartitionKeyDescription(Relation rel,
 												  Datum *values,
 												  bool *isnull,
@@ -287,12 +289,14 @@ ExecFindPartition(ModifyTableState *mtstate,
 	while (dispatch != NULL)
 	{
 		int			partidx = -1;
+		int			cached_bound_offset = -1;
 		bool		is_leaf;
 
 		CHECK_FOR_INTERRUPTS();
 
 		rel = dispatch->reldesc;
 		partdesc = dispatch->partdesc;
+		cached_bound_offset = partdesc->bound_offset;
 
 		/*
 		 * Extract partition key from tuple. Expression evaluation machinery
@@ -305,26 +309,39 @@ ExecFindPartition(ModifyTableState *mtstate,
 		ecxt->ecxt_scantuple = slot;
 		FormPartitionKeyDatum(dispatch, slot, estate, values, isnull);
 
-		/*
-		 * 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)
+		/* Check if cache the most recently chosen bound offset */
+		if (partdesc->nparts > 0 &&
+			cached_bound_offset >= 0 &&
+			cached_bound_offset < partdesc->boundinfo->ndatums)
 		{
-			char	   *val_desc;
-
-			val_desc = ExecBuildSlotPartitionKeyDescription(rel,
-															values, isnull, 64);
-			Assert(OidIsValid(RelationGetRelid(rel)));
-			ereport(ERROR,
-					(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,
-					 errtable(rel)));
+			partidx = check_partition_for_tuple(dispatch, values, isnull,
+												cached_bound_offset);
+		}
+
+		if (partidx < 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,
+												&partdesc->bound_offset)) < 0)
+			{
+				char	   *val_desc;
+
+				val_desc = ExecBuildSlotPartitionKeyDescription(rel,
+																values, isnull, 64);
+				Assert(OidIsValid(RelationGetRelid(rel)));
+				ereport(ERROR,
+						(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,
+						 errtable(rel)));
+			}
 		}
 
 		is_leaf = partdesc->is_leaf[partidx];
@@ -475,6 +492,7 @@ ExecFindPartition(ModifyTableState *mtstate,
 	ecxt->ecxt_scantuple = ecxt_scantuple_saved;
 	MemoryContextSwitchTo(oldcxt);
 
+
 	return rri;
 }
 
@@ -1232,6 +1250,104 @@ FormPartitionKeyDatum(PartitionDispatch pd,
 }
 
 /*
+ * check_partition_for_tuple
+ *		Check if the tuple value match the target partition bounds.
+ *
+ * Return value is index of the partition (>= 0 and < partdesc->nparts) if
+ * match or -1 if not.
+ */
+static int
+check_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull, int bound_offset)
+{
+	PartitionKey	key = pd->key;
+	PartitionDesc	partdesc = pd->partdesc;
+	PartitionBoundInfo boundinfo = partdesc->boundinfo;
+	FmgrInfo	   *partsupfunc = key->partsupfunc;
+	Oid			   *partcollation = key->partcollation;
+	Datum		  **datums = boundinfo->datums;
+	int				part_index = -1;
+	int				offset = bound_offset;
+	int32			cmpval;
+
+	/*
+	 * Compare the tuple value with the target partition bounds based on
+	 * partitioning strategy.
+	 *
+	 * Only check LIST and RANGE STRATEGY.
+	 */
+	switch (key->strategy)
+	{
+		case PARTITION_STRATEGY_LIST:
+			if (!isnull[0])
+			{
+				cmpval = DatumGetInt32(FunctionCall2Coll(&partsupfunc[0],
+														 partcollation[0],
+														 datums[offset][0],
+														 values[0]));
+				if (cmpval == 0)
+					part_index = boundinfo->indexes[offset];
+			}
+			break;
+		case PARTITION_STRATEGY_RANGE:
+			{
+				int			i;
+				bool		is_default = false;
+				int16		partnatts = key->partnatts;
+				PartitionRangeDatumKind **kind = boundinfo->kind;
+
+				/*
+				 * No range includes NULL, so this will be accepted by the
+				 * default partition if there is one, and otherwise rejected.
+				 */
+				for (i = 0; i < partnatts; i++)
+				{
+					if (isnull[i])
+					{
+						is_default = true;
+						break;
+					}
+				}
+
+				if (!is_default)
+				{
+					/* Check if the value is above the low bound */
+					cmpval = partition_rbound_datum_cmp(partsupfunc,
+														partcollation,
+														datums[offset],
+														kind[offset],
+														values,
+														partnatts);
+					if (cmpval == 0)
+						part_index = boundinfo->indexes[offset + 1];
+
+					else if (cmpval < 0 && offset + 1 < boundinfo->ndatums)
+					{
+						/* Check if the value is below the high bound */
+						offset ++;
+						cmpval = partition_rbound_datum_cmp(partsupfunc,
+															partcollation,
+															datums[offset],
+															kind[offset],
+															values,
+															partnatts);
+
+						if (cmpval > 0)
+							part_index = boundinfo->indexes[offset];
+					}
+				}
+				else if (boundinfo->indexes[offset + 1] == boundinfo->default_index)
+					part_index = boundinfo->indexes[offset + 1];
+			}
+			break;
+		default:
+			break;
+	}
+
+	return part_index;
+
+}
+
+/*
  * get_partition_for_tuple
  *		Finds partition of relation which accepts the partition key specified
  *		in values and isnull
@@ -1240,9 +1356,8 @@ FormPartitionKeyDatum(PartitionDispatch pd,
  * 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, int *bound_offset)
 {
-	int			bound_offset;
 	int			part_index = -1;
 	PartitionKey key = pd->key;
 	PartitionDesc partdesc = pd->partdesc;
@@ -1261,6 +1376,7 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 													   values, isnull);
 
 				part_index = boundinfo->indexes[rowHash % boundinfo->nindexes];
+				*bound_offset = -1;
 			}
 			break;
 
@@ -1274,12 +1390,12 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 			{
 				bool		equal = false;
 
-				bound_offset = partition_list_bsearch(key->partsupfunc,
+				*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 (*bound_offset >= 0 && equal)
+					part_index = boundinfo->indexes[*bound_offset];
 			}
 			break;
 
@@ -1304,7 +1420,7 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 
 				if (!range_partkey_has_null)
 				{
-					bound_offset = partition_range_datum_bsearch(key->partsupfunc,
+					*bound_offset = partition_range_datum_bsearch(key->partsupfunc,
 																 key->partcollation,
 																 boundinfo,
 																 key->partnatts,
@@ -1317,7 +1433,7 @@ get_partition_for_tuple(PartitionDispatch pd, Datum *values, bool *isnull)
 					 * bound of the partition we're looking for, if there
 					 * actually exists one.
 					 */
-					part_index = boundinfo->indexes[bound_offset + 1];
+					part_index = boundinfo->indexes[*bound_offset + 1];
 				}
 			}
 			break;
diff --git a/src/backend/partitioning/partdesc.c b/src/backend/partitioning/partdesc.c
index 9a9d6a9..b072da2 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->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 0792f48..70ab0a2 100644
--- a/src/include/partitioning/partdesc.h
+++ b/src/include/partitioning/partdesc.h
@@ -36,6 +36,8 @@ typedef struct PartitionDescData
 								 * the corresponding 'oids' element belongs to
 								 * a leaf partition or not */
 	PartitionBoundInfo boundinfo;	/* collection of partition bounds */
+	int			bound_offset;	/* offset of bound info that was most recently
+								 * chosen */
 } PartitionDescData;
 
 
-- 
2.7.2.windows.1

