From 1c2fe94ba8d103d76e41dca2dd0a4198e1e3170f Mon Sep 17 00:00:00 2001
From: houzj <houzj.fnst@cn.fujitsu.com>
Date: Mon, 24 May 2021 08:54:15 +0800
Subject: [PATCH] improving-ExecPartitionCheck

Currently for a normal partition key it will first generate a CHECK expression
Like : [Keyexpression IS NOT NULL AND Keyexpression > lowboud AND Keyexpression < lowboud].
In this case, Keyexpression will be re-executed which will bring some overhead.

Instead, I think we can try to do the following step:
1)extract the Keyexpression from the CHECK expression
2)evaluate the key expression in advance
3)pass the result of key expression to do the partition CHECK.
In this way ,we only execute the key expression once which looks more efficient.

---
 src/backend/commands/tablecmds.c      |   6 +-
 src/backend/executor/execMain.c       | 114 +++++++++++++++++++++-
 src/backend/optimizer/util/plancat.c  |   2 +-
 src/backend/partitioning/partbounds.c | 176 +++++++++++++++++++++++++++++++---
 src/backend/utils/cache/partcache.c   |  48 ++++++++--
 src/backend/utils/cache/relcache.c    |   2 +
 src/include/nodes/execnodes.h         |   9 ++
 src/include/partitioning/partbounds.h |   2 +-
 src/include/partitioning/partdefs.h   |   2 +
 src/include/utils/partcache.h         |   8 +-
 src/include/utils/rel.h               |   1 +
 11 files changed, 344 insertions(+), 26 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ebc6203..d0dc337 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -17279,9 +17279,9 @@ ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
 	 * If the parent itself is a partition, make sure to include its
 	 * constraint as well.
 	 */
-	partBoundConstraint = get_qual_from_partbound(attachrel, rel, cmd->bound);
+	partBoundConstraint = get_qual_from_partbound(attachrel, rel, cmd->bound, NULL);
 	partConstraint = list_concat(partBoundConstraint,
-								 RelationGetPartitionQual(rel));
+								 RelationGetPartitionQual(rel, NULL));
 
 	/* Skip validation if there are no constraints to validate. */
 	if (partConstraint)
@@ -18083,7 +18083,7 @@ DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
 {
 	List	   *constraintExpr;
 
-	constraintExpr = RelationGetPartitionQual(partRel);
+	constraintExpr = RelationGetPartitionQual(partRel, NULL);
 	constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
 
 	/*
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index b3ce4ba..f2da243 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -53,6 +53,7 @@
 #include "jit/jit.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
@@ -1699,6 +1700,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 {
 	ExprContext *econtext;
 	bool		success;
+	ListCell   *lc;
 
 	/*
 	 * If first time through, build expression state tree for the partition
@@ -1709,12 +1711,80 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 	 */
 	if (resultRelInfo->ri_PartitionCheckExpr == NULL)
 	{
+		int				i;
+		PartKeyContext	partkeycontext;
+		TupleDesc		tupdesc,
+						coltupdesc;
+		List		   *keyexpr_list;
+
 		/*
 		 * Ensure that the qual tree and prepared expression are in the
 		 * query-lifespan context.
 		 */
 		MemoryContext oldcxt = MemoryContextSwitchTo(estate->es_query_cxt);
-		List	   *qual = RelationGetPartitionQual(resultRelInfo->ri_RelationDesc);
+		List	   *qual;
+
+		/*
+		 * Extract the key expressions from the partition check expression to
+		 * avoid re-execution.
+		 */
+
+		/* The attno for key expr starts after the plain column */
+		partkeycontext.keycol_no = slot->tts_tupleDescriptor->natts + 1;
+		partkeycontext.keyexpr_list = NIL;
+		partkeycontext.keyexpr_varattno = NULL;
+
+		qual = RelationGetPartitionQual(resultRelInfo->ri_RelationDesc,
+										&partkeycontext);
+
+		keyexpr_list = partkeycontext.keyexpr_list;
+		resultRelInfo->ri_PartitionKeyExpr = NIL;
+		resultRelInfo->ri_PartitionKeySlot = NULL;
+
+
+		if (keyexpr_list != NIL)
+		{
+			/*
+			 * Build a slot which contains both the partition key and plain
+			 * column
+			 */
+			coltupdesc = slot->tts_tupleDescriptor;
+
+			tupdesc = CreateTemplateTupleDesc(partkeycontext.keycol_no - 1);
+
+			/* Copy the plain column */
+			TupleDescCopy(tupdesc, coltupdesc);
+
+			/* XXX adjust the natts */
+			tupdesc->natts = partkeycontext.keycol_no - 1;
+
+			/* Save the partition key list */
+			i = coltupdesc->natts + 1;
+			foreach(lc, keyexpr_list)
+			{
+				Node *e = lfirst(lc);
+
+				TupleDescInitEntry(tupdesc, i,
+								   NULL,
+								   exprType(e),
+								   exprTypmod(e),
+								   0);
+
+				TupleDescInitEntryCollation(tupdesc,
+											i,
+											exprCollation(e));
+
+				/* initialize each key expression for execution */
+				resultRelInfo->ri_PartitionKeyExpr =
+					lappend(resultRelInfo->ri_PartitionKeyExpr,
+							ExecPrepareExpr((Expr *) e, estate));
+
+				i++;
+			}
+
+			resultRelInfo->ri_PartitionKeySlot =
+				MakeTupleTableSlot(tupdesc, slot->tts_ops);
+		}
 
 		resultRelInfo->ri_PartitionCheckExpr = ExecPrepareCheck(qual, estate);
 		MemoryContextSwitchTo(oldcxt);
@@ -1726,6 +1796,48 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
 	 */
 	econtext = GetPerTupleExprContext(estate);
 
+	if (resultRelInfo->ri_PartitionKeySlot != NULL)
+		ExecClearTuple(resultRelInfo->ri_PartitionKeySlot);
+
+	/*
+	 * Evaluate the partition expression in advance to avoid re-execution,
+	 * and add the result to the slot to do the partition check.
+	 */
+	foreach(lc, resultRelInfo->ri_PartitionKeyExpr)
+	{
+		Datum		datum;
+		bool		isNull;
+		ExprState  *keystate = lfirst_node(ExprState, lc);
+		int			i = foreach_current_index(lc) +
+						slot->tts_tupleDescriptor->natts;
+
+		econtext->ecxt_scantuple = slot;
+		datum = ExecEvalExprSwitchContext(keystate, econtext, &isNull);
+
+		resultRelInfo->ri_PartitionKeySlot->tts_values[i] = datum;
+		resultRelInfo->ri_PartitionKeySlot->tts_isnull[i] = isNull;
+	}
+
+	/*
+	 * Move the values from original slot to the new slot, then the new data
+	 * is like :
+	 * [col1 , ... colN , keyexpr1's result , ... keyexprN's result]
+	 */
+	if (resultRelInfo->ri_PartitionKeyExpr != NIL)
+	{
+		slot_getallattrs(slot);
+		memcpy(resultRelInfo->ri_PartitionKeySlot->tts_values,
+			   slot->tts_values,
+			   slot->tts_tupleDescriptor->natts * sizeof(Datum));
+
+		memcpy(resultRelInfo->ri_PartitionKeySlot->tts_isnull,
+			   slot->tts_isnull,
+			   slot->tts_tupleDescriptor->natts * sizeof(Datum));
+
+		slot = resultRelInfo->ri_PartitionKeySlot;
+		ExecStoreVirtualTuple(slot);
+	}
+
 	/* Arrange for econtext's scan tuple to be the tuple under test */
 	econtext->ecxt_scantuple = slot;
 
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index c5194fd..f274538 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -2399,7 +2399,7 @@ set_baserel_partition_constraint(Relation relation, RelOptInfo *rel)
 	 * implicit-AND format, we'd have to explicitly convert it to explicit-AND
 	 * format and back again.
 	 */
-	partconstr = RelationGetPartitionQual(relation);
+	partconstr = RelationGetPartitionQual(relation, NULL);
 	if (partconstr)
 	{
 		partconstr = (List *) expression_planner((Expr *) partconstr);
diff --git a/src/backend/partitioning/partbounds.c b/src/backend/partitioning/partbounds.c
index 7925fcc..9c551bd 100644
--- a/src/backend/partitioning/partbounds.c
+++ b/src/backend/partitioning/partbounds.c
@@ -231,14 +231,14 @@ static Oid	get_partition_operator(PartitionKey key, int col,
 static List *get_qual_for_hash(Relation parent, PartitionBoundSpec *spec);
 static List *get_qual_for_list(Relation parent, PartitionBoundSpec *spec);
 static List *get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
-								bool for_default);
+								bool for_default, PartKeyContext *context);
 static void get_range_key_properties(PartitionKey key, int keynum,
 									 PartitionRangeDatum *ldatum,
 									 PartitionRangeDatum *udatum,
 									 ListCell **partexprs_item,
 									 Expr **keyCol,
 									 Const **lower_val, Const **upper_val);
-static List *get_range_nulltest(PartitionKey key);
+static List *get_range_nulltest(PartitionKey key, PartKeyContext *context);
 
 /*
  * get_qual_from_partbound
@@ -247,7 +247,7 @@ static List *get_range_nulltest(PartitionKey key);
  */
 List *
 get_qual_from_partbound(Relation rel, Relation parent,
-						PartitionBoundSpec *spec)
+						PartitionBoundSpec *spec, PartKeyContext *context)
 {
 	PartitionKey key = RelationGetPartitionKey(parent);
 	List	   *my_qual = NIL;
@@ -268,7 +268,7 @@ get_qual_from_partbound(Relation rel, Relation parent,
 
 		case PARTITION_STRATEGY_RANGE:
 			Assert(spec->strategy == PARTITION_STRATEGY_RANGE);
-			my_qual = get_qual_for_range(parent, spec, false);
+			my_qual = get_qual_for_range(parent, spec, false, context);
 			break;
 
 		default:
@@ -3153,7 +3153,7 @@ check_default_partition_contents(Relation parent, Relation default_rel,
 
 	new_part_constraints = (new_spec->strategy == PARTITION_STRATEGY_LIST)
 		? get_qual_for_list(parent, new_spec)
-		: get_qual_for_range(parent, new_spec, false);
+		: get_qual_for_range(parent, new_spec, false, NULL);
 	def_part_constraints =
 		get_proposed_default_constraint(new_part_constraints);
 
@@ -4167,7 +4167,7 @@ get_qual_for_list(Relation parent, PartitionBoundSpec *spec)
  */
 static List *
 get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
-				   bool for_default)
+				   bool for_default, PartKeyContext *context)
 {
 	List	   *result = NIL;
 	ListCell   *cell1,
@@ -4190,6 +4190,13 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 			   *upper_or_start_datum;
 	bool		need_next_lower_arm,
 				need_next_upper_arm;
+	AttrNumber *keyexpr_varattno = NULL;
+	int			cur_keyexpr_no,
+				old_keyexpr_no;
+
+	if (context != NULL && context->keyexpr_varattno == NULL)
+		context->keyexpr_varattno =
+			palloc0(sizeof(AttrNumber) * list_length(key->partexprs));
 
 	if (spec->is_default)
 	{
@@ -4226,7 +4233,7 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 			{
 				List	   *part_qual;
 
-				part_qual = get_qual_for_range(parent, bspec, true);
+				part_qual = get_qual_for_range(parent, bspec, true, context);
 
 				/*
 				 * AND the constraints of the partition and add to
@@ -4251,7 +4258,7 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 			 */
 			other_parts_constr =
 				makeBoolExpr(AND_EXPR,
-							 lappend(get_range_nulltest(key),
+							 lappend(get_range_nulltest(key, context),
 									 list_length(or_expr_args) > 1
 									 ? makeBoolExpr(OR_EXPR, or_expr_args,
 													-1)
@@ -4274,7 +4281,13 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 	 * to avoid accumulating the NullTest on the same keys for each partition.
 	 */
 	if (!for_default)
-		result = get_range_nulltest(key);
+		result = get_range_nulltest(key, context);
+
+	if (context != NULL)
+		keyexpr_varattno = context->keyexpr_varattno;
+
+	cur_keyexpr_no = 0;
+	old_keyexpr_no = 0;
 
 	/*
 	 * Iterate over the key columns and check if the corresponding lower and
@@ -4295,6 +4308,8 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 		ExprState  *test_exprstate;
 		Datum		test_result;
 		bool		isNull;
+		int			key_attno = 0;
+		bool		varattno_saved = false;
 
 		ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
 		udatum = castNode(PartitionRangeDatum, lfirst(cell2));
@@ -4306,12 +4321,33 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 		 */
 		partexprs_item_saved = partexprs_item;
 
+		old_keyexpr_no = cur_keyexpr_no;
+
 		get_range_key_properties(key, i, ldatum, udatum,
 								 &partexprs_item,
 								 &keyCol,
 								 &lower_val, &upper_val);
 
 		/*
+		 * Check if we have saved the same key expression, if so , just get
+		 * the attno from keyexpr_varattno
+		 */
+		if (context != NULL && !IsA(keyCol, Var))
+		{
+			if (keyexpr_varattno[cur_keyexpr_no] != 0)
+			{
+				varattno_saved = true;
+				key_attno = keyexpr_varattno[cur_keyexpr_no];
+			}
+			else
+			{
+				varattno_saved = false;
+				key_attno = context->keycol_no;
+			}
+			cur_keyexpr_no++;
+		}
+
+		/*
 		 * If either value is NULL, the corresponding partition bound is
 		 * either MINVALUE or MAXVALUE, and we treat them as unequal, because
 		 * even if they're the same, there is no common value to equate the
@@ -4346,6 +4382,25 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 		if (i == key->partnatts - 1)
 			elog(ERROR, "invalid range bound specification");
 
+		/* If key is not a plain column */
+		if (context != NULL && !IsA(keyCol, Var))
+		{
+			/* Save the keyexpr to keyexpr_list if first time meet */
+			if (!varattno_saved)
+			{
+				context->keyexpr_list = lappend(context->keyexpr_list, keyCol);
+				keyexpr_varattno[old_keyexpr_no] = key_attno;
+				context->keycol_no++;
+			}
+
+			keyCol = (Expr *) makeVar(2,
+									key_attno,
+									key->parttypid[i],
+									key->parttypmod[i],
+									key->parttypcoll[i],
+									0);
+		}
+
 		/* Equal, so generate keyCol = lower_val expression */
 		result = lappend(result,
 						 make_partition_op_expr(key, i, BTEqualStrategyNumber,
@@ -4372,11 +4427,16 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 		j = i;
 		partexprs_item = partexprs_item_saved;
 
+		cur_keyexpr_no = old_keyexpr_no;
+
 		for_both_cell(cell1, spec->lowerdatums, lower_or_start_datum,
 					  cell2, spec->upperdatums, upper_or_start_datum)
 		{
 			PartitionRangeDatum *ldatum_next = NULL,
 					   *udatum_next = NULL;
+			int			key_attno = 0;
+			bool		varattno_saved = false;
+			Expr		   *temp_keyCol;
 
 			ldatum = castNode(PartitionRangeDatum, lfirst(cell1));
 			if (lnext(spec->lowerdatums, cell1))
@@ -4391,6 +4451,26 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 									 &keyCol,
 									 &lower_val, &upper_val);
 
+			/*
+			 * Check if we have saved the same key expression, if so , just get
+			 * the attno from keyexpr_varattno
+			 */
+			temp_keyCol = keyCol;
+			if (context != NULL && !IsA(keyCol, Var))
+			{
+				if (keyexpr_varattno[cur_keyexpr_no] != 0)
+				{
+					varattno_saved = true;
+					key_attno = keyexpr_varattno[cur_keyexpr_no];
+				}
+				else
+				{
+					varattno_saved = false;
+					key_attno = context->keycol_no;
+				}
+				cur_keyexpr_no++;
+			}
+
 			if (need_next_lower_arm && lower_val)
 			{
 				uint16		strategy;
@@ -4410,10 +4490,30 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 				else
 					strategy = BTGreaterStrategyNumber;
 
+				/* If key is not a plain column */
+				if (context != NULL && !IsA(keyCol, Var))
+				{
+					/* Save the keyexpr to keyexpr_list if first time meet */
+					if (!varattno_saved)
+					{
+						context->keyexpr_list = lappend(context->keyexpr_list,
+														keyCol);
+						keyexpr_varattno[cur_keyexpr_no - 1] = key_attno;
+						context->keycol_no++;
+					}
+
+					temp_keyCol = (Expr *) makeVar(2,
+											key_attno,
+											key->parttypid[j],
+											key->parttypmod[j],
+											key->parttypcoll[j],
+											0);
+				}
+
 				lower_or_arm_args = lappend(lower_or_arm_args,
 											make_partition_op_expr(key, j,
 																   strategy,
-																   keyCol,
+																   temp_keyCol,
 																   (Expr *) lower_val));
 			}
 
@@ -4434,6 +4534,26 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 				else
 					strategy = BTLessStrategyNumber;
 
+				/* If key is not a plain column */
+				if (context != NULL && !IsA(keyCol, Var))
+				{
+					/* Save the keyexpr to keyexpr_list if first time meet */
+					if (keyexpr_varattno[cur_keyexpr_no - 1] == 0)
+					{
+						context->keyexpr_list = lappend(context->keyexpr_list,
+														keyCol);
+						keyexpr_varattno[cur_keyexpr_no - 1] = key_attno;
+						context->keycol_no++;
+					}
+
+					keyCol = (Expr *) makeVar(2,
+											key_attno,
+											key->parttypid[j],
+											key->parttypmod[j],
+											key->parttypcoll[j],
+											0);
+				}
+
 				upper_or_arm_args = lappend(upper_or_arm_args,
 											make_partition_op_expr(key, j,
 																   strategy,
@@ -4506,7 +4626,7 @@ get_qual_for_range(Relation parent, PartitionBoundSpec *spec,
 	 */
 	if (result == NIL)
 		result = for_default
-			? get_range_nulltest(key)
+			? get_range_nulltest(key, context)
 			: list_make1(makeBoolConst(true, false));
 
 	return result;
@@ -4572,13 +4692,15 @@ get_range_key_properties(PartitionKey key, int keynum,
  * keys to be null, so emit an IS NOT NULL expression for each key column.
  */
 static List *
-get_range_nulltest(PartitionKey key)
+get_range_nulltest(PartitionKey key, PartKeyContext *context)
 {
 	List	   *result = NIL;
 	NullTest   *nulltest;
 	ListCell   *partexprs_item;
 	int			i;
+	int			cur_keyexpr_no;
 
+	cur_keyexpr_no = 0;
 	partexprs_item = list_head(key->partexprs);
 	for (i = 0; i < key->partnatts; i++)
 	{
@@ -4598,6 +4720,36 @@ get_range_nulltest(PartitionKey key)
 			if (partexprs_item == NULL)
 				elog(ERROR, "wrong number of partition key expressions");
 			keyCol = copyObject(lfirst(partexprs_item));
+
+			if (context != NULL)
+			{
+				int key_attno;
+
+				if (context->keyexpr_varattno[cur_keyexpr_no] != 0)
+				{
+					key_attno = context->keyexpr_varattno[cur_keyexpr_no];
+				}
+				else
+				{
+					key_attno = context->keycol_no;
+
+					context->keyexpr_list = lappend(context->keyexpr_list,
+													keyCol);
+					context->keyexpr_varattno[cur_keyexpr_no] = key_attno;
+
+					context->keycol_no++;
+				}
+
+				cur_keyexpr_no++;
+
+				keyCol = (Expr *) makeVar(2,
+										key_attno,
+										key->parttypid[i],
+										key->parttypmod[i],
+										key->parttypcoll[i],
+										0);
+			}
+
 			partexprs_item = lnext(key->partexprs, partexprs_item);
 		}
 
diff --git a/src/backend/utils/cache/partcache.c b/src/backend/utils/cache/partcache.c
index 21e60f0..117717b 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -38,7 +38,7 @@
 
 
 static void RelationBuildPartitionKey(Relation relation);
-static List *generate_partition_qual(Relation rel);
+static List *generate_partition_qual(Relation rel, PartKeyContext *context);
 
 /*
  * RelationGetPartitionKey -- get partition key, if relation is partitioned
@@ -273,13 +273,13 @@ RelationBuildPartitionKey(Relation relation)
  * Returns a list of partition quals
  */
 List *
-RelationGetPartitionQual(Relation rel)
+RelationGetPartitionQual(Relation rel, PartKeyContext *context)
 {
 	/* Quick exit */
 	if (!rel->rd_rel->relispartition)
 		return NIL;
 
-	return generate_partition_qual(rel);
+	return generate_partition_qual(rel, context);
 }
 
 /*
@@ -305,7 +305,7 @@ get_partition_qual_relid(Oid relid)
 		Relation	rel = relation_open(relid, AccessShareLock);
 		List	   *and_args;
 
-		and_args = generate_partition_qual(rel);
+		and_args = generate_partition_qual(rel, NULL);
 
 		/* Convert implicit-AND list format to boolean expression */
 		if (and_args == NIL)
@@ -333,7 +333,7 @@ get_partition_qual_relid(Oid relid)
  * into long-lived cache contexts, especially if we fail partway through.
  */
 static List *
-generate_partition_qual(Relation rel)
+generate_partition_qual(Relation rel, PartKeyContext *context)
 {
 	HeapTuple	tuple;
 	MemoryContext oldcxt;
@@ -349,7 +349,18 @@ generate_partition_qual(Relation rel)
 
 	/* If we already cached the result, just return a copy */
 	if (rel->rd_partcheckvalid)
+	{
+		if (context != NULL)
+		{
+			context->keyexpr_list = rel->rd_keyexpr_list;
+			context->keycol_no += list_length(context->keyexpr_list);
+		}
+
 		return copyObject(rel->rd_partcheck);
+	}
+
+	if (context != NULL)
+		context->keyexpr_varattno = NULL;
 
 	/*
 	 * Grab at least an AccessShareLock on the parent table.  Must do this
@@ -376,14 +387,27 @@ generate_partition_qual(Relation rel)
 		bound = castNode(PartitionBoundSpec,
 						 stringToNode(TextDatumGetCString(boundDatum)));
 
-		my_qual = get_qual_from_partbound(rel, parent, bound);
+		my_qual = get_qual_from_partbound(rel, parent, bound, context);
 	}
 
 	ReleaseSysCache(tuple);
 
 	/* Add the parent's quals to the list (if any) */
 	if (parent->rd_rel->relispartition)
-		result = list_concat(generate_partition_qual(parent), my_qual);
+	{
+		List *cur_keyexpr_list;
+		if (context != NULL)
+		{
+			cur_keyexpr_list = context->keyexpr_list;
+			context->keyexpr_list = NIL;
+		}
+
+		result = list_concat(generate_partition_qual(parent, context), my_qual);
+
+		if (context != NULL)
+			context->keyexpr_list = list_concat(context->keyexpr_list,
+					cur_keyexpr_list);
+	}
 	else
 		result = my_qual;
 
@@ -394,10 +418,14 @@ generate_partition_qual(Relation rel)
 	 * here.
 	 */
 	result = map_partition_varattnos(result, 1, rel, parent);
+	if (context != NULL)
+		context->keyexpr_list = map_partition_varattnos(context->keyexpr_list,
+														1, rel, parent);
 
 	/* Assert that we're not leaking any old data during assignments below */
 	Assert(rel->rd_partcheckcxt == NULL);
 	Assert(rel->rd_partcheck == NIL);
+	Assert(rel->rd_keyexpr_list == NIL);
 
 	/*
 	 * Save a copy in the relcache.  The order of these operations is fairly
@@ -416,10 +444,16 @@ generate_partition_qual(Relation rel)
 										  RelationGetRelationName(rel));
 		oldcxt = MemoryContextSwitchTo(rel->rd_partcheckcxt);
 		rel->rd_partcheck = copyObject(result);
+		if (context != NULL)
+			rel->rd_keyexpr_list = copyObject(context->keyexpr_list);
 		MemoryContextSwitchTo(oldcxt);
 	}
 	else
+	{
 		rel->rd_partcheck = NIL;
+		rel->rd_keyexpr_list = NIL;
+	}
+
 	rel->rd_partcheckvalid = true;
 
 	/* Keep the parent locked until commit */
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fd05615..f7527da 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1161,6 +1161,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
 	relation->rd_pdcxt = NULL;
 	relation->rd_pddcxt = NULL;
 	relation->rd_partcheck = NIL;
+	relation->rd_keyexpr_list = NIL;
 	relation->rd_partcheckvalid = false;
 	relation->rd_partcheckcxt = NULL;
 
@@ -6041,6 +6042,7 @@ load_relcache_init_file(bool shared)
 		rel->rd_pdcxt = NULL;
 		rel->rd_pddcxt = NULL;
 		rel->rd_partcheck = NIL;
+		rel->rd_keyexpr_list = NIL;
 		rel->rd_partcheckvalid = false;
 		rel->rd_partcheckcxt = NULL;
 		rel->rd_indexprs = NIL;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 7795a69..8dd457c 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -497,6 +497,15 @@ typedef struct ResultRelInfo
 	ExprState  *ri_PartitionCheckExpr;
 
 	/*
+	 * Partition Key expressions that used in PartitionCheckExpr
+	 * (NULL if not set up yet)
+	 */
+	List  *ri_PartitionKeyExpr;
+
+	/* Used to evaluate the PartitionCheckExpr (NULL if not set up yet) */
+	TupleTableSlot *ri_PartitionKeySlot;
+
+	/*
 	 * Information needed by tuple routing target relations
 	 *
 	 * RootResultRelInfo gives the target relation mentioned in the query, if
diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h
index ebf3ff1..800d4dc 100644
--- a/src/include/partitioning/partbounds.h
+++ b/src/include/partitioning/partbounds.h
@@ -86,7 +86,7 @@ extern uint64 compute_partition_hash_value(int partnatts, FmgrInfo *partsupfunc,
 										   Oid *partcollation,
 										   Datum *values, bool *isnull);
 extern List *get_qual_from_partbound(Relation rel, Relation parent,
-									 PartitionBoundSpec *spec);
+									 PartitionBoundSpec *spec, PartKeyContext *context);
 extern PartitionBoundInfo partition_bounds_create(PartitionBoundSpec **boundspecs,
 												  int nparts, PartitionKey key, int **mapping);
 extern bool partition_bounds_equal(int partnatts, int16 *parttyplen,
diff --git a/src/include/partitioning/partdefs.h b/src/include/partitioning/partdefs.h
index d742b96..be5591c 100644
--- a/src/include/partitioning/partdefs.h
+++ b/src/include/partitioning/partdefs.h
@@ -23,4 +23,6 @@ typedef struct PartitionDescData *PartitionDesc;
 
 typedef struct PartitionDirectoryData *PartitionDirectory;
 
+typedef struct PartKeyContext PartKeyContext;
+
 #endif							/* PARTDEFS_H */
diff --git a/src/include/utils/partcache.h b/src/include/utils/partcache.h
index a451bfb..6f1bcbb 100644
--- a/src/include/utils/partcache.h
+++ b/src/include/utils/partcache.h
@@ -46,9 +46,15 @@ typedef struct PartitionKeyData
 	Oid		   *parttypcoll;
 }			PartitionKeyData;
 
+struct PartKeyContext
+{
+	int keycol_no;
+	List *keyexpr_list;
+	AttrNumber *keyexpr_varattno;
+};
 
 extern PartitionKey RelationGetPartitionKey(Relation rel);
-extern List *RelationGetPartitionQual(Relation rel);
+extern List *RelationGetPartitionQual(Relation rel, PartKeyContext *context);
 extern Expr *get_partition_qual_relid(Oid relid);
 
 /*
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 774ac5b..111287b 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -143,6 +143,7 @@ typedef struct RelationData
 
 	/* data managed by RelationGetPartitionQual: */
 	List	   *rd_partcheck;	/* partition CHECK quals */
+	List	   *rd_keyexpr_list;	/* partition key exprs used in CHECK quals */
 	bool		rd_partcheckvalid;	/* true if list has been computed */
 	MemoryContext rd_partcheckcxt;	/* private cxt for rd_partcheck, if any */
 
-- 
2.7.2.windows.1

