From 77e135e68ea1753720921e4805b773fef2259fdc Mon Sep 17 00:00:00 2001
From: Beena Emerson <Beena.Emerson@EnterpriseDB.com>
Date: Tue, 14 Nov 2017 11:19:33 +0530
Subject: [PATCH 2/2] Implement runtime partiton pruning

Patch by: Beena Emerson, Dilip Kumar
Discussion: https://postgr.es/m/CAOG9ApE16ac-_VVZVvv0gePSgkg_BwYEV1NBqZFqDR2bBE0X0A@mail.gmail.com
---
 src/backend/catalog/partition.c         |  53 +++---
 src/backend/executor/nodeAppend.c       | 274 ++++++++++++++++++++++++++++++--
 src/backend/nodes/copyfuncs.c           |  27 ++++
 src/backend/nodes/readfuncs.c           |   2 +
 src/backend/optimizer/path/allpaths.c   |  76 +++++++--
 src/backend/optimizer/path/joinrels.c   |   2 +-
 src/backend/optimizer/plan/createplan.c |  46 ++++++
 src/backend/optimizer/plan/planner.c    |   2 +-
 src/backend/optimizer/prep/prepunion.c  |   4 +-
 src/backend/optimizer/util/clauses.c    |  16 ++
 src/backend/optimizer/util/pathnode.c   |  36 ++++-
 src/backend/optimizer/util/relnode.c    |  23 ++-
 src/include/catalog/partition.h         |   2 +-
 src/include/nodes/execnodes.h           |   4 +
 src/include/nodes/nodes.h               |   1 +
 src/include/nodes/plannodes.h           |   4 +
 src/include/nodes/relation.h            |  54 ++++---
 src/include/optimizer/clauses.h         |   1 +
 src/include/optimizer/pathnode.h        |   4 +-
 src/include/optimizer/paths.h           |   5 +
 20 files changed, 556 insertions(+), 80 deletions(-)

diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 97f3050..7ef97fe 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1529,9 +1529,11 @@ get_partitions_from_clauses_guts(Relation relation, List *clauses)
 	List *or_clauses;
 	ListCell *lc;
 
+	memset(&keys, 0, sizeof(PartScanKeyInfo));
+
 	nkeys = classify_partition_bounding_keys(relation, clauses,
 											 &keys, &constfalse,
-											 &or_clauses);
+											 &or_clauses, false);
 	/*
 	 * Only look up in the partition decriptor if the query provides
 	 * constraints on the keys at all.
@@ -1595,7 +1597,7 @@ get_partitions_from_clauses_guts(Relation relation, List *clauses)
 int
 classify_partition_bounding_keys(Relation relation, List *clauses,
 								 PartScanKeyInfo *keys, bool *constfalse,
-								 List **or_clauses)
+								 List **or_clauses, bool is_runtime)
 {
 	PartitionKey partkey = RelationGetPartitionKey(relation);
 	int		i;
@@ -2079,7 +2081,6 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
 		n_eqkeys = 0;
 
 	/* Populate keys. */
-	memset(keys, 0, sizeof(PartScanKeyInfo));
 	if (n_eqkeys + n_minkeys + n_maxkeys + n_keynullness > 0)
 	{
 		Datum	value;
@@ -2088,11 +2089,12 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
 		n_datums_resolved = 0;
 		for (i = 0; i < n_eqkeys; i++)
 		{
-			if (partkey_datum_from_expr(eqkey_exprs[i], &value))
-			{
-				keys->eqkeys[i] = value;
-				n_datums_resolved++;
-			}
+			if (IsA(eqkey_exprs[i], Const) &&
+				partkey_datum_from_expr(eqkey_exprs[i], &value))
+				keys->eqkeys_datums[i] = value;
+			else
+				keys->eqkeys = lappend(keys->eqkeys, eqkey_exprs[i]);
+			n_datums_resolved++;
 		}
 		keys->n_eqkeys = n_datums_resolved;
 		n_total += keys->n_eqkeys;
@@ -2100,11 +2102,12 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
 		n_datums_resolved = 0;
 		for (i = 0; i < n_minkeys; i++)
 		{
-			if (partkey_datum_from_expr(minkey_exprs[i], &value))
-			{
-				keys->minkeys[i] = value;
-				n_datums_resolved++;
-			}
+			if (IsA(minkey_exprs[i], Const) &&
+				partkey_datum_from_expr(minkey_exprs[i], &value))
+				keys->minkeys_datums[i] = value;
+			else
+				keys->minkeys = lappend(keys->minkeys, minkey_exprs[i]);
+			n_datums_resolved++;
 		}
 		keys->n_minkeys = n_datums_resolved;
 		n_total += keys->n_minkeys;
@@ -2113,11 +2116,12 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
 		n_datums_resolved = 0;
 		for (i = 0; i < n_maxkeys; i++)
 		{
-			if (partkey_datum_from_expr(maxkey_exprs[i], &value))
-			{
-				keys->maxkeys[i] = value;
-				n_datums_resolved++;
-			}
+			if (IsA(maxkey_exprs[i], Const) &&
+				partkey_datum_from_expr(maxkey_exprs[i], &value))
+				keys->maxkeys_datums[i] = value;
+			else
+				keys->maxkeys = lappend(keys->maxkeys, maxkey_exprs[i]);
+			n_datums_resolved++;
 		}
 		keys->n_maxkeys = n_datums_resolved;
 		n_total += keys->n_maxkeys;
@@ -2128,6 +2132,9 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
 		n_total += n_keynullness;
 	}
 
+	if (n_total == 0)
+		keys = NULL;
+
 	return n_total;
 }
 
@@ -2148,6 +2155,10 @@ partkey_datum_from_expr(const Expr *expr, Datum *value)
 			*value = ((Const *) expr)->constvalue;
 			return true;
 
+		case T_Param:
+			*value = '\0';
+			return true;
+
 		default:
 			return false;
 	}
@@ -2427,7 +2438,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
 	if (keys->n_eqkeys > 0)
 	{
 		memset(&arg, 0, sizeof(PartitionBoundCmpArg));
-		arg.datums = keys->eqkeys;
+		arg.datums = keys->eqkeys_datums;
 		arg.ndatums = keys->n_eqkeys;
 		eqoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
 
@@ -2475,7 +2486,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
 	if (keys->n_minkeys > 0)
 	{
 		memset(&arg, 0, sizeof(PartitionBoundCmpArg));
-		arg.datums = keys->minkeys;
+		arg.datums = keys->minkeys_datums;
 		arg.ndatums = keys->n_minkeys;
 		minoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
 
@@ -2561,7 +2572,7 @@ get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
 	if (keys->n_maxkeys > 0)
 	{
 		memset(&arg, 0, sizeof(PartitionBoundCmpArg));
-		arg.datums = keys->maxkeys;
+		arg.datums = keys->maxkeys_datums;
 		arg.ndatums = keys->n_maxkeys;
 		maxoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
 
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 1d2fb35..c0e0c05 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -57,9 +57,11 @@
 
 #include "postgres.h"
 
+#include "nodes/relation.h"
 #include "executor/execdebug.h"
 #include "executor/nodeAppend.h"
 #include "miscadmin.h"
+#include "optimizer/clauses.h"
 
 static TupleTableSlot *ExecAppend(PlanState *pstate);
 static bool exec_append_initialize_next(AppendState *appendstate);
@@ -108,6 +110,114 @@ exec_append_initialize_next(AppendState *appendstate)
 }
 
 /* ----------------------------------------------------------------
+ *	   extern_eval_const_expressions
+ *
+ *		Evalute the expressions to a constant.
+ * ----------------------------------------------------------------
+ */
+static PartScanKeyInfo *
+extern_eval_const_expressions(EState *estate, PartScanKeyInfo *old_keys)
+{
+	PartScanKeyInfo *keys;
+	int			i;
+	ListCell   *lc;
+
+	if (old_keys == NULL)
+		return NULL;
+
+	keys = makeNode(PartScanKeyInfo);
+	i = 0;
+	foreach(lc, old_keys->eqkeys)
+	{
+		Node	   *val = lfirst(lc);
+		Node	   *n =
+		eval_const_expressions_from_list(estate->es_param_list_info, val);
+
+		Assert(IsA(n, Const));
+		keys->eqkeys_datums[i++] = ((Const *) n)->constvalue;
+	}
+	keys->n_eqkeys = old_keys->n_eqkeys;
+
+	i = 0;
+	foreach(lc, old_keys->minkeys)
+	{
+		Node	   *val = lfirst(lc);
+		Node	   *n =
+		eval_const_expressions_from_list(estate->es_param_list_info, val);
+
+		Assert(IsA(n, Const));
+		keys->minkeys_datums[i++] = ((Const *) n)->constvalue;
+	}
+
+	keys->n_minkeys = old_keys->n_minkeys;
+	keys->min_incl = old_keys->min_incl;
+
+	i = 0;
+	foreach(lc, old_keys->maxkeys)
+	{
+		Node	   *val = lfirst(lc);
+		Node	   *n =
+		eval_const_expressions_from_list(estate->es_param_list_info, val);
+
+		Assert(IsA(n, Const));
+		keys->maxkeys_datums[i++] = ((Const *) n)->constvalue;
+	}
+	keys->n_maxkeys = old_keys->n_maxkeys;
+	keys->max_incl = old_keys->max_incl;
+
+	memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+
+	return keys;
+}
+
+/* ----------------------------------------------------------------
+ *	   exec_InitExpr
+ *
+ *		Evaluate and store the output of ExecInitExpr for each of the keys.
+ * ----------------------------------------------------------------
+ */
+static PartScanKeyInfo *
+exec_InitExpr(PlanState *parent, PartScanKeyInfo *old_keys)
+{
+	PartScanKeyInfo *keys;
+	ListCell   *lc;
+
+	if (old_keys == NULL)
+		return NULL;
+
+	keys = makeNode(PartScanKeyInfo);
+
+	foreach(lc, old_keys->eqkeys)
+	{
+		Expr	   *val = (Expr *) lfirst(lc);
+
+		keys->eqkeys = lappend(keys->eqkeys, ExecInitExpr(val, parent));
+	}
+	keys->n_eqkeys = old_keys->n_eqkeys;
+
+	foreach(lc, old_keys->minkeys)
+	{
+		Expr	   *val = (Expr *) lfirst(lc);
+
+		keys->minkeys = lappend(keys->minkeys, ExecInitExpr(val, parent));
+	}
+	keys->n_minkeys = old_keys->n_minkeys;
+	keys->min_incl = old_keys->min_incl;
+
+	foreach(lc, old_keys->maxkeys)
+	{
+		Expr	   *val = (Expr *) lfirst(lc);
+
+		keys->maxkeys = lappend(keys->maxkeys, ExecInitExpr(val, parent));
+	}
+	keys->n_maxkeys = old_keys->n_maxkeys;
+	keys->max_incl = old_keys->max_incl;
+
+	memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+	return keys;
+}
+
+/* ----------------------------------------------------------------
  *		ExecInitAppend
  *
  *		Begin all of the subscans of the append node.
@@ -165,22 +272,67 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 	 */
 	ExecInitResultTupleSlot(estate, &appendstate->ps);
 
-	/*
-	 * call ExecInitNode on each of the plans to be executed and save the
-	 * results into the array "appendplans".
-	 */
-	i = 0;
-	foreach(lc, node->appendplans)
+	if (node->et_keys)
 	{
-		Plan	   *initNode = (Plan *) lfirst(lc);
+		List	   *temp_appendplans = NIL;
+		Append	   *temp_node;
+		Relation	rel;
+		PartScanKeyInfo *keys;
+		Bitmapset  *partset;
+		int			i,
+					j;
 
-		appendplanstates[i] = ExecInitNode(initNode, estate, eflags);
-		i++;
+		keys = extern_eval_const_expressions(estate, node->et_keys);
+
+		rel = relation_open(node->parentoid, NoLock);
+		partset = get_partitions_for_keys(rel, keys);
+		relation_close(rel, NoLock);
+
+		if (!bms_is_empty(partset))
+		{
+			i = 0;
+			while ((j = bms_first_member(partset)) >= 0)
+			{
+				Plan	   *initNode = (Plan *) list_nth(node->appendplans, j);
+
+				temp_appendplans = lappend(temp_appendplans, initNode);
+				appendplanstates[i++] = ExecInitNode(initNode, estate, eflags);
+			}
+		}
+		/* create new AppendState for our append node */
+		temp_node = copyObject(node);
+		temp_node->appendplans = temp_appendplans;
+		((Plan *) temp_node)->plan_rows = i;
+		appendstate->as_nplans = i;
+		appendstate->ps.plan = (Plan *) temp_node;
+		appendstate->ps.state = estate;
+		appendstate->ps.ExecProcNode = ExecAppend;
+		appendstate->appendplans = (PlanState **) palloc0(i * sizeof(PlanState *));
+		appendstate->appendplans = appendplanstates;
 	}
+	else
+	{
+		appendstate->parentoid = node->parentoid;
+		ExecAssignExprContext(estate, &appendstate->ps);
+
+		/*
+		 * call ExecInitNode on each of the plans to be executed and save the
+		 * results into the array "appendplans".
+		 */
+		i = 0;
+		foreach(lc, node->appendplans)
+		{
+			Plan	   *initNode = (Plan *) lfirst(lc);
+
+			appendplanstates[i] = ExecInitNode(initNode, estate, eflags);
+			i++;
+		}
+	}
+
+	if (node->ex_keys)
+		appendstate->ex_keys =
+			exec_InitExpr((PlanState *) appendstate, node->ex_keys);
 
-	/*
-	 * initialize output tuple type
-	 */
 	ExecAssignResultTypeFromTL(&appendstate->ps);
 	appendstate->ps.ps_ProjInfo = NULL;
 
@@ -237,7 +389,21 @@ ExecAppend(PlanState *pstate)
 		 * ExecInitAppend.
 		 */
 		if (ScanDirectionIsForward(node->ps.state->es_direction))
-			node->as_whichplan++;
+		{
+			/*
+			 * For runtime partition pruning, goto the next valid partition
+			 * index
+			 */
+			if (node->index)
+			{
+				if (++node->as_whichpartition < list_length(node->index))
+					node->as_whichplan = list_nth_int(node->index, node->as_whichpartition);
+
+				return ExecClearTuple(node->ps.ps_ResultTupleSlot);
+			}
+			else
+				node->as_whichplan++;
+		}
 		else
 			node->as_whichplan--;
 		if (!exec_append_initialize_next(node))
@@ -275,11 +441,84 @@ ExecEndAppend(AppendState *node)
 		ExecEndNode(appendplans[i]);
 }
 
+/* ----------------------------------------------------------------
+ *	   exec_EvalExpr
+ *
+ *		Convert the expressions to a constant.
+ * ----------------------------------------------------------------
+ */
+static PartScanKeyInfo *
+exec_EvalExpr(ExprContext *econtext, PartScanKeyInfo *old_keys)
+{
+	PartScanKeyInfo *keys;
+	int			i;
+	bool		isnull;
+	ListCell   *lc;
+
+	if (old_keys == NULL)
+		return NULL;
+
+	keys = makeNode(PartScanKeyInfo);
+	i = 0;
+	foreach(lc, old_keys->eqkeys)
+	{
+		ExprState  *val = lfirst(lc);
+
+		keys->eqkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull);
+	}
+	keys->n_eqkeys = old_keys->n_eqkeys;
+
+	i = 0;
+	foreach(lc, old_keys->minkeys)
+	{
+		ExprState  *val = lfirst(lc);
+
+		keys->minkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull);
+	}
+	keys->n_minkeys = old_keys->n_minkeys;
+	keys->min_incl = old_keys->min_incl;
+
+	i = 0;
+	foreach(lc, old_keys->maxkeys)
+	{
+		ExprState  *val = lfirst(lc);
+
+		keys->maxkeys_datums[i++] = ExecEvalExpr(val, econtext, &isnull);
+	}
+	keys->n_maxkeys = old_keys->n_maxkeys;
+	keys->max_incl = old_keys->max_incl;
+
+	memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+	return keys;
+}
+
 void
 ExecReScanAppend(AppendState *node)
 {
 	int			i;
 
+	if (node->ps.chgParam != NULL && node->ex_keys)
+	{
+		Bitmapset  *partset;
+		PartScanKeyInfo *ex_keys;
+
+		node->index = NIL;
+		ex_keys = exec_EvalExpr(node->ps.ps_ExprContext, node->ex_keys);
+		if (node->parentoid)
+		{
+			Relation	rel = relation_open(node->parentoid, NoLock);
+
+			partset = get_partitions_for_keys(rel, ex_keys);
+			relation_close(rel, NoLock);
+		}
+		if (!bms_is_empty(partset))
+		{
+			int			j;
+
+			while ((j = bms_first_member(partset)) >= 0)
+				node->index = lappend_int(node->index, j);
+		}
+	}
 	for (i = 0; i < node->as_nplans; i++)
 	{
 		PlanState  *subnode = node->appendplans[i];
@@ -298,6 +537,13 @@ ExecReScanAppend(AppendState *node)
 		if (subnode->chgParam == NULL)
 			ExecReScan(subnode);
 	}
-	node->as_whichplan = 0;
+
+	if (node->index)
+	{
+		node->as_whichplan = linitial_int(node->index);
+		node->as_whichpartition = 0;
+	}
+	else
+		node->as_whichplan = 0;
 	exec_append_initialize_next(node);
 }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c1a83ca..95dddab 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -224,6 +224,27 @@ _copyModifyTable(const ModifyTable *from)
 	return newnode;
 }
 
+static PartScanKeyInfo *
+_copyPartScanKeyInfo(const PartScanKeyInfo *from)
+{
+	PartScanKeyInfo *newnode = makeNode(PartScanKeyInfo);
+
+	COPY_SCALAR_FIELD(n_eqkeys);
+	COPY_NODE_FIELD(eqkeys);
+
+	COPY_SCALAR_FIELD(n_minkeys);
+	COPY_NODE_FIELD(minkeys);
+	COPY_SCALAR_FIELD(min_incl);
+
+	COPY_SCALAR_FIELD(n_maxkeys);
+	COPY_NODE_FIELD(maxkeys);
+	COPY_SCALAR_FIELD(max_incl);
+
+	memcpy(newnode->keynullness, from->keynullness, sizeof(newnode->keynullness));
+
+	return newnode;
+}
+
 /*
  * _copyAppend
  */
@@ -242,6 +263,9 @@ _copyAppend(const Append *from)
 	 */
 	COPY_NODE_FIELD(partitioned_rels);
 	COPY_NODE_FIELD(appendplans);
+	COPY_NODE_FIELD(ex_keys);
+	COPY_NODE_FIELD(et_keys);
+	COPY_SCALAR_FIELD(parentoid);
 
 	return newnode;
 }
@@ -5014,6 +5038,9 @@ copyObjectImpl(const void *from)
 		case T_PlaceHolderInfo:
 			retval = _copyPlaceHolderInfo(from);
 			break;
+		case T_PartScanKeyInfo:
+			retval = _copyPartScanKeyInfo(from);
+			break;
 
 			/*
 			 * VALUE NODES
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index ccb6a1f..15e6764 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2652,6 +2652,8 @@ parseNodeString(void)
 		return_value = _readLimit();
 	else if (MATCH("NESTLOOPPARAM", 13))
 		return_value = _readNestLoopParam();
+	else if (MATCH("PARTSCANKEYINFO", 15))
+		return_value = _readNestLoopParam();
 	else if (MATCH("PLANROWMARK", 11))
 		return_value = _readPlanRowMark();
 	else if (MATCH("PLANINVALITEM", 13))
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index a3d0468..166eb2e 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -18,6 +18,7 @@
 #include <limits.h>
 #include <math.h>
 
+#include "catalog/partition.h"
 #include "access/sysattr.h"
 #include "access/tsmapi.h"
 #include "catalog/partition.h"
@@ -140,11 +141,11 @@ static void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 						List *live_childrels);
 static List *get_append_rel_partitions(PlannerInfo *root,
 						  RelOptInfo *rel,
-						  RangeTblEntry *rte);
+						  RangeTblEntry *rte, bool is_runtime);
 static List *match_clauses_to_partkey(RelOptInfo *rel,
 						 List *clauses,
 						 bool *contains_const,
-						 bool *constfalse);
+						 bool *constfalse, bool is_runtime);
 
 
 /*
@@ -855,6 +856,47 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	rel->fdwroutine->GetForeignPaths(root, rel, rte->relid);
 }
 
+
+PartScanKeyInfo *
+get_append_partition_info(RelOptInfo *rel, RangeTblEntry *rte,
+						  bool is_runtime, List *clauses)
+{
+	bool		constfalse,
+				contains_constant;
+	PartScanKeyInfo *keys = NULL;
+	List	   *partclauses;
+
+	/*
+	 * Get the clauses that match the partition key, including information
+	 * about any nullness tests against partition keys.  Set keynullness to a
+	 * invalid value of NullTestType, which 0 is not.
+	 */
+	partclauses = match_clauses_to_partkey(rel,
+										   list_copy(clauses),
+										   &contains_constant,
+										   &constfalse, is_runtime);
+
+	if (partclauses != NIL)
+	{
+		bool		constfalse;
+		List	   *or_clauses;
+		Relation	parent = heap_open(rte->relid, NoLock);
+		PartScanKeyInfo *temp_keys = makeNode(PartScanKeyInfo);
+		int			nkeys = classify_partition_bounding_keys(parent, partclauses,
+															 temp_keys,
+															 &constfalse,
+															 &or_clauses,
+															 true);
+
+		if (nkeys > 0)
+			keys = temp_keys;
+
+		heap_close(parent, NoLock);
+	}
+	return keys;
+}
+
+
 /*
  * get_append_rel_partitions
  *		Return the list of partitions of rel that pass the clauses mentioned
@@ -865,7 +907,7 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 static List *
 get_append_rel_partitions(PlannerInfo *root,
 						  RelOptInfo *rel,
-						  RangeTblEntry *rte)
+						  RangeTblEntry *rte, bool is_runtime)
 {
 	Relation	parent = heap_open(rte->relid, NoLock);
 	PartitionDesc partdesc = RelationGetPartitionDesc(parent);
@@ -884,7 +926,7 @@ get_append_rel_partitions(PlannerInfo *root,
 	partclauses = match_clauses_to_partkey(rel,
 										   list_copy(rel->baserestrictinfo),
 										   &contains_const,
-										   &constfalse);
+										   &constfalse, is_runtime);
 
 	/*
 	 * If the matched clauses contains at least some constant operands, use
@@ -956,7 +998,8 @@ static List *
 match_clauses_to_partkey(RelOptInfo *rel,
 						 List *clauses,
 						 bool *contains_const,
-						 bool *constfalse)
+						 bool *constfalse,
+						 bool is_runtime)
 {
 	PartitionScheme	partscheme = rel->part_scheme;
 	List	   *result = NIL;
@@ -1003,7 +1046,8 @@ match_clauses_to_partkey(RelOptInfo *rel,
 				match_clauses_to_partkey(rel,
 									 list_copy(((BoolExpr *) clause)->args),
 										 &contains_const1,
-										 &constfalse1) != NIL)
+										 &constfalse1,
+										 is_runtime) != NIL)
 			{
 				result = lappend(result, clause);
 				*contains_const = contains_const1;
@@ -1079,6 +1123,12 @@ match_clauses_to_partkey(RelOptInfo *rel,
 					/* Neither argument matches the partition key. */
 					continue;
 
+				if (IsA(constexpr, Const) && is_runtime)
+					continue;
+
+				if (IsA(constexpr, Param) && !is_runtime)
+					continue;
+
 				/*
 				 * Only allow strict operators to think sanely about the
 				 * behavior with null arguments.
@@ -1184,7 +1234,7 @@ match_clauses_to_partkey(RelOptInfo *rel,
 				if (!PartCollMatchesExprColl(partcoll, saop_coll))
 					continue;
 			}
-			else if (IsA(clause, NullTest))
+			else if (!is_runtime && IsA(clause, NullTest))
 			{
 				NullTest   *nulltest = (NullTest *) clause;
 				Node	   *arg = (Node *) nulltest->arg;
@@ -1200,7 +1250,7 @@ match_clauses_to_partkey(RelOptInfo *rel,
 			 * Boolean conditions have a special shape, which accept if the
 			 * partitioning opfamily accepts Boolean conditions.
 			 */
-			else if (IsBooleanOpfamily(partopfamily) &&
+			else if (!is_runtime && IsBooleanOpfamily(partopfamily) &&
 					 (IsA(clause, BooleanTest) ||
 					  IsA(clause, Var) || not_clause((Node *) clause)))
 			{
@@ -1272,7 +1322,7 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
 	}
 	else
 	{
-		rel_appinfos = get_append_rel_partitions(root, rel, rte);
+		rel_appinfos = get_append_rel_partitions(root, rel, rte, false);
 		rel->live_partitioned_rels = list_make1_int(rti);
 	}
 
@@ -1834,7 +1884,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 	 * if we have zero or one live subpath due to constraint exclusion.)
 	 */
 	if (subpaths_valid)
-		add_path(rel, (Path *) create_append_path(rel, subpaths, NULL, 0,
+		add_path(rel, (Path *) create_append_path(root, rel, subpaths, NULL, 0,
 												  partitioned_rels));
 
 	/*
@@ -1861,7 +1911,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 		Assert(parallel_workers > 0);
 
 		/* Generate a partial append path. */
-		appendpath = create_append_path(rel, partial_subpaths, NULL,
+		appendpath = create_append_path(root, rel, partial_subpaths, NULL,
 										parallel_workers, partitioned_rels);
 		add_partial_path(rel, (Path *) appendpath);
 	}
@@ -1915,7 +1965,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 
 		if (subpaths_valid)
 			add_path(rel, (Path *)
-					 create_append_path(rel, subpaths, required_outer, 0,
+					 create_append_path(root, rel, subpaths, required_outer, 0,
 										partitioned_rels));
 	}
 }
@@ -2152,7 +2202,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
 	rel->pathlist = NIL;
 	rel->partial_pathlist = NIL;
 
-	add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL));
+	add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NULL, 0, NIL));
 
 	/*
 	 * We set the cheapest path immediately, to ensure that IS_DUMMY_REL()
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 7356683..af7e790 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1232,7 +1232,7 @@ mark_dummy_rel(RelOptInfo *rel)
 	rel->partial_pathlist = NIL;
 
 	/* Set up the dummy path */
-	add_path(rel, (Path *) create_append_path(rel, NIL, NULL, 0, NIL));
+	add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NULL, 0, NIL));
 
 	/* Set or update cheapest_total_path and related fields */
 	set_cheapest(rel);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 9c74e39..9014ef2 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1001,6 +1001,47 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
 	return plan;
 }
 
+static PartScanKeyInfo *
+replace_partition_nestloop_params(PlannerInfo *root, PartScanKeyInfo *old_keys)
+{
+	PartScanKeyInfo *keys;
+	ListCell   *lc;
+
+	if (old_keys == NULL)
+		return NULL;
+
+	keys = makeNode(PartScanKeyInfo);
+	foreach(lc, old_keys->eqkeys)
+	{
+		Node	   *n = lfirst(lc);
+
+		keys->eqkeys = lappend(keys->eqkeys, replace_nestloop_params(root, n));
+	}
+	keys->n_eqkeys = old_keys->n_eqkeys;
+
+	foreach(lc, old_keys->minkeys)
+	{
+		Node	   *n = lfirst(lc);
+
+		keys->minkeys = lappend(keys->minkeys, replace_nestloop_params(root, n));
+	}
+	keys->n_minkeys = old_keys->n_minkeys;
+	keys->min_incl = old_keys->min_incl;
+
+	foreach(lc, old_keys->maxkeys)
+	{
+		Node	   *n = lfirst(lc);
+
+		keys->maxkeys = lappend(keys->maxkeys, replace_nestloop_params(root, n));
+	}
+	keys->n_maxkeys = old_keys->n_maxkeys;
+	keys->max_incl = old_keys->max_incl;
+
+	memcpy(keys->keynullness, old_keys->keynullness, sizeof(keys->keynullness));
+	return keys;
+}
+
+
 /*
  * create_append_plan
  *	  Create an Append plan for 'best_path' and (recursively) plans
@@ -1063,6 +1104,11 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 
 	copy_generic_path_info(&plan->plan, (Path *) best_path);
 
+
+	plan->ex_keys = replace_partition_nestloop_params(root, best_path->ex_keys);
+	plan->et_keys = best_path->et_keys;
+	plan->parentoid = best_path->parentoid;
+
 	return (Plan *) plan;
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fd0e483..8a8876f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3675,7 +3675,7 @@ create_grouping_paths(PlannerInfo *root,
 				paths = lappend(paths, path);
 			}
 			path = (Path *)
-				create_append_path(grouped_rel,
+				create_append_path(root, grouped_rel,
 								   paths,
 								   NULL,
 								   0,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index f620243..12d0f85 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -590,7 +590,7 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root,
 	/*
 	 * Append the child results together.
 	 */
-	path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL);
+	path = (Path *) create_append_path(root, result_rel, pathlist, NULL, 0, NIL);
 
 	/* We have to manually jam the right tlist into the path; ick */
 	path->pathtarget = create_pathtarget(root, tlist);
@@ -702,7 +702,7 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
 	/*
 	 * Append the child results together.
 	 */
-	path = (Path *) create_append_path(result_rel, pathlist, NULL, 0, NIL);
+	path = (Path *) create_append_path(root, result_rel, pathlist, NULL, 0, NIL);
 
 	/* We have to manually jam the right tlist into the path; ick */
 	path->pathtarget = create_pathtarget(root, tlist);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 30cdd3d..5479a59f 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -5197,3 +5197,19 @@ tlist_matches_coltypelist(List *tlist, List *coltypelist)
 
 	return true;
 }
+
+Node *
+eval_const_expressions_from_list(ParamListInfo prmList, Node *node)
+{
+	eval_const_expressions_context context;
+
+	if (prmList)
+		context.boundParams = prmList;	/* bound Params */
+	else
+		context.boundParams = NULL;
+	context.root = NULL;
+	context.active_fns = NIL;	/* nothing being recursively simplified */
+	context.case_val = NULL;	/* no CASE being examined */
+	context.estimate = false;	/* safe transformations only */
+	return eval_const_expressions_mutator(node, &context);
+}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 36ec025..8910d39 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1208,7 +1208,7 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
  * Note that we must handle subpaths = NIL, representing a dummy access path.
  */
 AppendPath *
-create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer,
+create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths, Relids required_outer,
 				   int parallel_workers, List *partitioned_rels)
 {
 	AppendPath *pathnode = makeNode(AppendPath);
@@ -1253,6 +1253,40 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer,
 		Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer));
 	}
 
+	if (root && (required_outer || rel->baserestrictinfo))
+	{
+		RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
+		List	   *query_quals = rel->baserestrictinfo;
+
+		if (rte && rte->rtekind == RTE_RELATION)
+		{
+			Oid			poid = rte->relid;
+			Relation	prel = relation_open(poid, NoLock);
+
+			if (prel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+			{
+				ParamPathInfo *ppi = pathnode->path.param_info;
+
+				/* Set the ex_keys for Exec Params */
+				if (ppi)
+				{
+					List	   *ppi_clauses = ppi->ppi_clauses;
+
+					pathnode->ex_keys =
+						get_append_partition_info(rel, rte, true, ppi_clauses);
+				}
+				/* Set et_keys for extern params */
+				if (query_quals)
+				{
+					pathnode->et_keys =
+						get_append_partition_info(rel, rte, true, query_quals);
+				}
+				pathnode->parentoid = poid;
+			}
+			relation_close(prel, NoLock);
+		}
+	}
+
 	return pathnode;
 }
 
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index b06696b..9abc79c 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1556,6 +1556,9 @@ ParamPathInfo *
 get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer)
 {
 	ParamPathInfo *ppi;
+	Relids		joinrelids;
+	List	   *pclauses;
+	ListCell   *lc;
 
 	/* Unparameterized paths have no ParamPathInfo */
 	if (bms_is_empty(required_outer))
@@ -1567,11 +1570,29 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer)
 	if ((ppi = find_param_path_info(appendrel, required_outer)))
 		return ppi;
 
+
+	/*
+	 * Generally for appendrel we don't fetch the clause from the join clause
+	 * (only we do so for baserel), but for identifying whether the appendrel
+	 * is applicable for runtime pruning or not.
+	 */
+	joinrelids = bms_union(appendrel->relids, required_outer);
+	pclauses = NIL;
+	foreach(lc, appendrel->joininfo)
+	{
+		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
+
+		if (join_clause_is_movable_into(rinfo,
+										appendrel->relids,
+										joinrelids))
+			pclauses = lappend(pclauses, rinfo);
+	}
+
 	/* Else build the ParamPathInfo */
 	ppi = makeNode(ParamPathInfo);
 	ppi->ppi_req_outer = required_outer;
 	ppi->ppi_rows = 0;
-	ppi->ppi_clauses = NIL;
+	ppi->ppi_clauses = pclauses;
 	appendrel->ppilist = lappend(appendrel->ppilist, ppi);
 
 	return ppi;
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 7956f04..4331695 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -114,7 +114,7 @@ extern Bitmapset *get_partitions_from_clauses(Relation relation,
 							List *partclauses);
 extern int classify_partition_bounding_keys(Relation relation, List *clauses,
 								 PartScanKeyInfo *keys, bool *constfalse,
-								 List **or_clauses);
+								 List **or_clauses, bool is_runtime);
 Bitmapset *get_partitions_for_keys(Relation rel,
 						PartScanKeyInfo *keys);
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e05bc04..adbac23 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1006,6 +1006,10 @@ typedef struct AppendState
 	PlanState **appendplans;	/* array of PlanStates for my inputs */
 	int			as_nplans;
 	int			as_whichplan;
+	Oid			parentoid;
+	List	   *index;			/* subplan indexes to scan for runtime pruning */
+	int			as_whichpartition;	/* current partition scanned from list */
+	PartScanKeyInfo *ex_keys;
 } AppendState;
 
 /* ----------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ffeeb49..5e09258 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -87,6 +87,7 @@ typedef enum NodeTag
 	T_NestLoopParam,
 	T_PlanRowMark,
 	T_PlanInvalItem,
+	T_PartScanKeyInfo,
 
 	/*
 	 * TAGS FOR PLAN STATE NODES (execnodes.h)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index dd74efa..96f0632 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -19,6 +19,7 @@
 #include "nodes/bitmapset.h"
 #include "nodes/lockoptions.h"
 #include "nodes/primnodes.h"
+#include "nodes/relation.h"
 
 
 /* ----------------------------------------------------------------
@@ -248,6 +249,9 @@ typedef struct Append
 	/* RT indexes of non-leaf tables in a partition tree */
 	List	   *partitioned_rels;
 	List	   *appendplans;
+	Oid			parentoid;
+	PartScanKeyInfo *ex_keys;
+	PartScanKeyInfo *et_keys;
 } Append;
 
 /* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3e47de7..78c7654 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -370,43 +370,48 @@ typedef struct PartitionSchemeData *PartitionScheme;
  */
 typedef struct PartScanKeyInfo
 {
+	NodeTag		type;
+
 	/*
-	 * Constants constituting the *whole* partition key compared using
-	 * partitioning-compatible equality operator(s).  When n_eqkeys > 0, other
-	 * keys (minkeys and maxkeys) are irrelevant.
+	 * Expressions or constants constituting the *whole* partition key
+	 * compared using partitioning-compatible equality operator(s).  When
+	 * n_eqkeys > 0, other keys (minkeys and maxkeys) are irrelevant.
 	 */
-	Datum	eqkeys[PARTITION_MAX_KEYS];
-	int		n_eqkeys;
+	List	   *eqkeys;
+	Datum		eqkeys_datums[PARTITION_MAX_KEYS];
+	int			n_eqkeys;
 
 	/*
-	 * Constants that constitute the lower bound on the partition key or a
-	 * prefix thereof.  The last of those constants is compared using > or >=
-	 * operator compatible with partitioning, making this the lower bound in
-	 * a range query.
+	 * Expressions or constants that constitute the lower bound on the
+	 * partition key or a prefix thereof.  The last of those constants is
+	 * compared using > or >= operator compatible with partitioning, making
+	 * this the lower bound in a range query.
 	 */
-	Datum	minkeys[PARTITION_MAX_KEYS];
-	int		n_minkeys;
-	bool	min_incl;
+	List	   *minkeys;
+	Datum		minkeys_datums[PARTITION_MAX_KEYS];
+	int			n_minkeys;
+	bool		min_incl;
 
 	/*
-	 * Constants that constitute the upper bound on the partition key or a
-	 * prefix thereof.  The last of those constants is compared using < or <=
-	 * operator compatible with partitioning, making this the upper bound in
-	 * a range query.
+	 * Expressions or constants that constitute the upper bound on the
+	 * partition key or a prefix thereof.  The last of those constants is
+	 * compared using < or <= operator compatible with partitioning, making
+	 * this the upper bound in a range query.
 	 */
-	Datum	maxkeys[PARTITION_MAX_KEYS];
-	int		n_maxkeys;
-	bool	max_incl;
+	List	   *maxkeys;
+	Datum		maxkeys_datums[PARTITION_MAX_KEYS];
+	int			n_maxkeys;
+	bool		max_incl;
 
 	/*
 	 * Specifies the type of NullTest that was applied to each of the
 	 * partition key columns or -1 if none was applied.  Partitioning handles
 	 * null partition keys specially depending on the partitioning method in
-	 * use, so get_partitions_for_keys can return partitions according to
-	 * the nullness condition for partition keys.
+	 * use, so get_partitions_for_keys can return partitions according to the
+	 * nullness condition for partition keys.
 	 */
-	NullTestType	keynullness[PARTITION_MAX_KEYS];
-} PartScanKeyInfo;
+	NullTestType keynullness[PARTITION_MAX_KEYS];
+}			PartScanKeyInfo;
 
 /*----------
  * RelOptInfo
@@ -1334,6 +1339,9 @@ typedef struct AppendPath
 	/* RT indexes of non-leaf tables in a partition tree */
 	List	   *partitioned_rels;
 	List	   *subpaths;		/* list of component Paths */
+	PartScanKeyInfo *ex_keys;	/* Runtime Partition Pruning - EXEC Params */
+	PartScanKeyInfo *et_keys;	/* Runtime Partition Pruning - EXTERN Params */
+	Oid			parentoid;
 } AppendPath;
 
 #define IS_DUMMY_PATH(p) \
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index e367221..c7f5262 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -79,6 +79,7 @@ extern void CommuteOpExpr(OpExpr *clause);
 extern void CommuteRowCompareExpr(RowCompareExpr *clause);
 
 extern Node *eval_const_expressions(PlannerInfo *root, Node *node);
+extern Node *eval_const_expressions_from_list(ParamListInfo prm_list, Node *node);
 
 extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
 
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index c1f2fc9..7c1fbfd 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -63,8 +63,8 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root,
 					  List *bitmapquals);
 extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
 					List *tidquals, Relids required_outer);
-extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths,
-				   Relids required_outer, int parallel_workers,
+extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel,
+				   List *subpaths, Relids required_outer, int parallel_workers,
 				   List *partitioned_rels);
 extern MergeAppendPath *create_merge_append_path(PlannerInfo *root,
 						 RelOptInfo *rel,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ea886b6..525c9df 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -234,4 +234,9 @@ extern PathKey *make_canonical_pathkey(PlannerInfo *root,
 					   EquivalenceClass *eclass, Oid opfamily,
 					   int strategy, bool nulls_first);
 
+extern PartScanKeyInfo *get_append_partition_info(RelOptInfo *rel,
+												   RangeTblEntry *rte,
+												   bool is_runtime,
+												   List *clauses);
+
 #endif							/* PATHS_H */
-- 
1.8.3.1

