From 6972ae859d2543225baf669363bfe6f8e70c0add Mon Sep 17 00:00:00 2001
From: houzj <houzj.fnst@cn.fujitsu.com>
Date: Mon, 17 May 2021 17:43:57 +0800
Subject: [PATCH] skip-tuple-routing-for-constant-partition-key

---
 src/backend/access/common/tupconvert.c | 37 +++++++++++++++
 src/backend/commands/copyfrom.c        |  3 ++
 src/backend/executor/execPartition.c   | 84 ++++++++++++++++++++++++++++++++--
 src/backend/executor/nodeModifyTable.c | 51 ++++++++++++++++++++-
 src/include/access/tupconvert.h        |  1 +
 src/include/nodes/execnodes.h          |  9 ++++
 6 files changed, 179 insertions(+), 6 deletions(-)

diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c
index 64f5439..f22414f 100644
--- a/src/backend/access/common/tupconvert.c
+++ b/src/backend/access/common/tupconvert.c
@@ -278,6 +278,43 @@ execute_attr_map_cols(AttrMap *attrMap, Bitmapset *in_cols)
 }
 
 /*
+ * Perform conversion of bitmap of columns according to the map.
+ *
+ * Only convert normal user column.
+ *
+ * output column that does not correspond to any input column will
+ * still be set in bitmap.
+ */
+Bitmapset *
+execute_attr_map_cols_with_null(AttrMap *attrMap, Bitmapset *in_cols)
+{
+	Bitmapset  *out_cols;
+	int			out_attnum;
+
+	/* fast path for the common trivial case */
+	if (in_cols == NULL)
+		return NULL;
+
+	out_cols = NULL;
+
+	for (out_attnum = 1;
+		 out_attnum <= attrMap->maplen;
+		 out_attnum++)
+	{
+		int			in_attnum;
+
+		in_attnum = attrMap->attnums[out_attnum - 1];
+
+		if (in_attnum == 0 ||
+			bms_is_member(in_attnum, in_cols))
+			out_cols = bms_add_member(out_cols, out_attnum);
+	}
+
+	return out_cols;
+}
+
+
+/*
  * Free a TupleConversionMap structure.
  */
 void
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index 40a54ad..ea62cfa 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -670,6 +670,9 @@ CopyFrom(CopyFromState cstate)
 	mtstate->mt_nrels = 1;
 	mtstate->resultRelInfo = resultRelInfo;
 	mtstate->rootResultRelInfo = resultRelInfo;
+	mtstate->const_bms = NULL;
+	mtstate->cache_routed_rel = false;
+	mtstate->targetpartrel = NULL;
 
 	if (resultRelInfo->ri_FdwRoutine != NULL &&
 		resultRelInfo->ri_FdwRoutine->BeginForeignInsert != NULL)
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 606c920..dca6799 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -24,6 +24,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "optimizer/optimizer.h"
 #include "partitioning/partbounds.h"
 #include "partitioning/partdesc.h"
 #include "partitioning/partprune.h"
@@ -191,7 +192,7 @@ static void find_matching_subplans_recurse(PartitionPruningData *prunedata,
 										   PartitionedRelPruningData *pprune,
 										   bool initial_prune,
 										   Bitmapset **validsubplans);
-
+static bool ExecSimplifyTupleRouting(PartitionDispatch pd, Bitmapset *constcols);
 
 /*
  * ExecSetupPartitionTupleRouting - sets up information needed during
@@ -271,6 +272,15 @@ ExecFindPartition(ModifyTableState *mtstate,
 	TupleTableSlot *myslot = NULL;
 	MemoryContext oldcxt;
 	ResultRelInfo *rri = NULL;
+	bool		need_tuple_routing;
+	Bitmapset  *const_bms = mtstate->const_bms;
+	Bitmapset  *root_const_bms = mtstate->const_bms;
+
+	need_tuple_routing = !(RelationGetRelid(rootResultRelInfo->ri_RelationDesc) ==
+			   RelationGetRelid(mtstate->rootResultRelInfo->ri_RelationDesc));
+
+	if (mtstate->targetpartrel != NULL && !need_tuple_routing)
+		return mtstate->targetpartrel;
 
 	/* use per-tuple context here to avoid leaking memory */
 	oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
@@ -327,6 +337,11 @@ ExecFindPartition(ModifyTableState *mtstate,
 					 errtable(rel)));
 		}
 
+		/* Check if we can skip tuple routing next time */
+		if (mtstate->cache_routed_rel)
+			mtstate->cache_routed_rel = ExecSimplifyTupleRouting(dispatch,
+																 const_bms);
+
 		is_leaf = partdesc->is_leaf[partidx];
 		if (is_leaf)
 		{
@@ -415,8 +430,8 @@ ExecFindPartition(ModifyTableState *mtstate,
 			}
 
 			/*
-			 * Convert the tuple to the new parent's layout, if different from
-			 * the previous parent.
+			 * Convert the tuple and constant value bitmap to the new parent's
+			 * layout, if different from the previous parent.
 			 */
 			if (dispatch->tupslot)
 			{
@@ -424,6 +439,10 @@ ExecFindPartition(ModifyTableState *mtstate,
 				TupleTableSlot *tempslot = myslot;
 
 				myslot = dispatch->tupslot;
+
+				if (mtstate->cache_routed_rel)
+					const_bms = execute_attr_map_cols_with_null(map, const_bms);
+
 				slot = execute_attr_map_slot(map, slot, myslot);
 
 				if (tempslot != NULL)
@@ -450,24 +469,43 @@ ExecFindPartition(ModifyTableState *mtstate,
 			 *
 			 * Note that we have a map to convert from root to current
 			 * partition, but not from immediate parent to current partition.
-			 * So if we have to convert, do it from the root slot; if not, use
-			 * the root slot as-is.
+			 * So if we have to convert, do it from the root slot and constant
+			 * value bitmap; if not, use the root slot as-is.
 			 */
 			if (is_leaf)
 			{
 				TupleConversionMap *map = rri->ri_RootToPartitionMap;
 
 				if (map)
+				{
+					if (mtstate->cache_routed_rel)
+						const_bms = execute_attr_map_cols_with_null(map->attrMap,
+																	const_bms);
+
 					slot = execute_attr_map_slot(map->attrMap, rootslot,
 												 rri->ri_PartitionTupleSlot);
+				}
 				else
+				{
+					const_bms = root_const_bms;
 					slot = rootslot;
+				}
 			}
 
 			ExecPartitionCheck(rri, slot, estate, true);
 		}
 	}
 
+	/*
+	 * If all of the columns used in partition key are constant, cache the
+	 * target partition if first time reach here.
+	 */
+	if (mtstate->cache_routed_rel)
+	{
+		mtstate->cache_routed_rel = false;
+		mtstate->targetpartrel = rri;
+	}
+
 	/* Release the tuple in the lowest parent's dedicated slot. */
 	if (myslot != NULL)
 		ExecClearTuple(myslot);
@@ -1165,6 +1203,42 @@ ExecCleanupTupleRouting(ModifyTableState *mtstate,
 	}
 }
 
+/*
+ * Check whether all of the columns used in partition key are
+ * const value
+ */
+static bool
+ExecSimplifyTupleRouting(PartitionDispatch pd,
+						 Bitmapset *constcols)
+{
+	int	i;
+	List	   *expr_vars;
+	ListCell   *lc;
+
+	if (constcols == NULL)
+		return false;
+
+	/* Check plain columns */
+	for (i = 0; i < pd->key->partnatts; i++)
+	{
+		AttrNumber	keycol = pd->key->partattrs[i];
+		if (keycol != 0 && !bms_is_member(keycol, constcols))
+			return false;
+	}
+
+	/* Check columns in partition expression */
+	expr_vars = pull_var_clause((Node *) pd->key->partexprs, 0);
+	foreach(lc, expr_vars)
+	{
+		Var *var = lfirst(lc);
+
+		if (var->varno == 1 && !bms_is_member(var->varattno, constcols))
+			return false;
+	}
+
+	return true;
+}
+
 /* ----------------
  *		FormPartitionKeyDatum
  *			Construct values[] and isnull[] arrays for the partition key
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 0816027..5d7dcb2 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1445,7 +1445,11 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate,
 	/* Initialize tuple routing info if not already done. */
 	if (mtstate->mt_partition_tuple_routing == NULL)
 	{
+		ListCell   *lc, *lc2;
+		List	   *updateColnos;
+		List	   *targetlist;
 		Relation	rootRel = mtstate->rootResultRelInfo->ri_RelationDesc;
+		ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
 		MemoryContext oldcxt;
 
 		/* Things built here have to last for the query duration. */
@@ -1454,6 +1458,22 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate,
 		mtstate->mt_partition_tuple_routing =
 			ExecSetupPartitionTupleRouting(estate, rootRel);
 
+		/* Initialize constant value bitmapset */
+		Assert(mtstate->const_bms == NULL);
+		mtstate->cache_routed_rel = true;
+		updateColnos = (List *) list_nth(node->updateColnosLists,
+			resultRelInfo - mtstate->resultRelInfo);
+
+		targetlist = outerPlan(node)->targetlist;
+		forboth(lc, targetlist, lc2, updateColnos)
+		{
+			TargetEntry *tle = lfirst_node(TargetEntry, lc);
+			AttrNumber	targetattnum = lfirst_int(lc2);
+			if (!tle->resjunk && IsA(tle->expr, Const))
+				mtstate->const_bms = bms_add_member(mtstate->const_bms,
+													targetattnum);
+		}
+
 		/*
 		 * Before a partition's tuple can be re-routed, it must first be
 		 * converted to the root's format, so we'll need a slot for storing
@@ -1532,10 +1552,17 @@ ExecCrossPartitionUpdate(ModifyTableState *mtstate,
 	 */
 	tupconv_map = ExecGetChildToRootMap(resultRelInfo);
 	if (tupconv_map != NULL)
-		slot = execute_attr_map_slot(tupconv_map->attrMap,
+	{
+		AttrMap *attrMap = tupconv_map->attrMap;
+		slot = execute_attr_map_slot(attrMap,
 									 slot,
 									 mtstate->mt_root_tuple_slot);
 
+		if (mtstate->cache_routed_rel)
+			mtstate->const_bms = execute_attr_map_cols_with_null(attrMap,
+														mtstate->const_bms);
+	}
+
 	/* Tuple routing starts from the root table. */
 	*inserted_tuple = ExecInsert(mtstate, mtstate->rootResultRelInfo, slot,
 								 planSlot, estate, canSetTag);
@@ -2874,6 +2901,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	/* Get the root target relation */
 	rel = mtstate->rootResultRelInfo->ri_RelationDesc;
 
+	mtstate->cache_routed_rel = false;
+	mtstate->const_bms = NULL;
+	mtstate->targetpartrel = NULL;
+
 	/*
 	 * Build state for tuple routing if it's a partitioned INSERT.  An UPDATE
 	 * might need this too, but only if it actually moves tuples between
@@ -2881,9 +2912,27 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	 */
 	if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
 		operation == CMD_INSERT)
+	{
 		mtstate->mt_partition_tuple_routing =
 			ExecSetupPartitionTupleRouting(estate, rel);
 
+		mtstate->cache_routed_rel = true;
+
+		/*
+		 * Build the constant bitmap for tuple routing simplification if
+		 * it's a partitioned INSERT. Cross partition UPDATE will build it
+		 * in ExecCrossPartitionUpdate.
+		 */
+		i = 1;
+		foreach(l, subplan->targetlist)
+		{
+			TargetEntry *tc = (TargetEntry *) lfirst(l);
+			if (!tc->resjunk && IsA(tc->expr, Const))
+				mtstate->const_bms = bms_add_member(mtstate->const_bms, i);
+			i++;
+		}
+	}
+
 	/*
 	 * Initialize any WITH CHECK OPTION constraints if needed.
 	 */
diff --git a/src/include/access/tupconvert.h b/src/include/access/tupconvert.h
index a2cc4b3..ea8eeac 100644
--- a/src/include/access/tupconvert.h
+++ b/src/include/access/tupconvert.h
@@ -45,6 +45,7 @@ extern TupleTableSlot *execute_attr_map_slot(AttrMap *attrMap,
 											 TupleTableSlot *in_slot,
 											 TupleTableSlot *out_slot);
 extern Bitmapset *execute_attr_map_cols(AttrMap *attrMap, Bitmapset *inbitmap);
+extern Bitmapset *execute_attr_map_cols_with_null(AttrMap *attrMap, Bitmapset *inbitmap);
 
 extern void free_conversion_map(TupleConversionMap *map);
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 7795a69..5ba392b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1213,6 +1213,15 @@ typedef struct ModifyTableState
 	HTAB	   *mt_resultOidHash;	/* optional hash table to speed lookups */
 
 	/*
+	 * Cached target partition if all the columns in partition key are
+	 * constant, otherwise NULL
+	 */
+	ResultRelInfo *targetpartrel;
+	Bitmapset  *const_bms;			/* column numbers of constant value */
+	bool		cache_routed_rel;	/* do we need to cache the target
+									 * partition after tuple routing */
+
+	/*
 	 * Slot for storing tuples in the root partitioned table's rowtype during
 	 * an UPDATE of a partitioned table.
 	 */
-- 
2.7.2.windows.1

