From ae31ae294cef9660fcc547abfc7543b5b3c85a1f Mon Sep 17 00:00:00 2001
From: Beena Emerson <Beena.Emerson@EnterpriseDB.com>
Date: Tue, 26 Sep 2017 09:26:28 +0530
Subject: [PATCH] Implement runtime partiton pruning

Patch by: Beena Emerson, Dilip Kumar
---
 src/backend/executor/nodeAppend.c       | 179 +++++++++++++++++++++++++++++++-
 src/backend/nodes/copyfuncs.c           |  22 ++++
 src/backend/nodes/outfuncs.c            |  14 +++
 src/backend/nodes/readfuncs.c           |  18 ++++
 src/backend/optimizer/path/allpaths.c   | 171 ++++++++++++++++++++++++++++--
 src/backend/optimizer/path/joinrels.c   |   2 +-
 src/backend/optimizer/plan/createplan.c |  17 +++
 src/backend/optimizer/plan/planner.c    |   2 +-
 src/backend/optimizer/prep/prepunion.c  |   4 +-
 src/backend/optimizer/util/clauses.c    |  17 +++
 src/backend/optimizer/util/pathnode.c   |  38 ++++++-
 src/backend/optimizer/util/relnode.c    |  24 ++++-
 src/backend/utils/cache/plancache.c     |   2 +-
 src/include/nodes/execnodes.h           |  13 +++
 src/include/nodes/nodes.h               |   1 +
 src/include/nodes/plannodes.h           |  13 +++
 src/include/nodes/relation.h            |  13 +++
 src/include/optimizer/clauses.h         |   1 +
 src/include/optimizer/pathnode.h        |   2 +-
 src/include/optimizer/paths.h           |   3 +
 20 files changed, 535 insertions(+), 21 deletions(-)

diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index bed9bb8..19587d4 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);
@@ -165,6 +167,86 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 	 */
 	ExecInitResultTupleSlot(estate, &appendstate->ps);
 
+	if (node->et_rtp_list)
+	{
+		List	   *temp_appendplans = NIL;
+		Append	   *temp_node;
+
+		Datum	   *minkeys,
+				   *maxkeys;
+		Relation	rel;
+		NullTestType keynullness = IS_NOT_NULL;
+		int			min_datum_index,
+					max_datum_index;
+		bool		null_partition_chosen = false;
+		bool		default_partition_chosen = false;
+		int			i = 0;
+		int			n_minkeys = 0,
+					n_maxkeys = 0;
+
+		minkeys = (Datum *) palloc(list_length(node->et_rtp_list) * sizeof(Datum));
+		maxkeys = (Datum *) palloc(list_length(node->et_rtp_list) * sizeof(Datum));
+
+		foreach(lc, node->et_rtp_list)
+		{
+			RuntimePruningInfo *rtp = (RuntimePruningInfo *) lfirst(lc);
+			RuntimePruningExec *rtpe = palloc0(sizeof(RuntimePruningExec));
+
+			if (rtp->maxop)
+			{
+				Node	   *n = eval_const_expressions2(estate->es_param_list_info, (Node *) rtp->maxop);
+
+				maxkeys[i] = ((Const *) n)->constvalue;
+				n_maxkeys++;
+			}
+			if (rtp->minop)
+			{
+				Node	   *n = eval_const_expressions2(estate->es_param_list_info, (Node *) rtp->minop);
+
+				minkeys[i] = ((Const *) n)->constvalue;
+				n_minkeys++;
+			}
+			rtpe->minincl = rtp->minincl;
+			rtpe->maxincl = rtp->maxincl;
+
+			i++;
+		}
+
+		rel = relation_open(node->parentoid, NoLock);
+		appendstate->index = get_partitions_for_keys(rel,
+													 &keynullness,
+													 minkeys, n_minkeys, false,
+													 maxkeys, n_maxkeys, false,
+													 &min_datum_index, &max_datum_index,
+													 &null_partition_chosen,
+													 &default_partition_chosen);
+		relation_close(rel, NoLock);
+
+		i = 0;
+		foreach(lc, appendstate->index)
+		{
+			int			index = lfirst_int(lc);
+
+			Plan	   *initNode = (Plan *) list_nth(node->appendplans, index);
+
+			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;
+
+		return appendstate;
+	}
+
 	/*
 	 * call ExecInitNode on each of the plans to be executed and save the
 	 * results into the array "appendplans".
@@ -190,6 +272,27 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 	appendstate->as_whichplan = 0;
 	exec_append_initialize_next(appendstate);
 
+	if (node->rtp_list)
+	{
+		ListCell   *lc;
+
+		foreach(lc, node->rtp_list)
+		{
+			RuntimePruningInfo *rtp = (RuntimePruningInfo *) lfirst(lc);
+			RuntimePruningExec *rtpe = palloc0(sizeof(RuntimePruningExec));
+
+			rtpe->maxop = ExecInitExpr(rtp->maxop, appendplanstates);
+			rtpe->minop = ExecInitExpr(rtp->minop, appendplanstates);
+			rtpe->minincl = rtp->minincl;
+			rtpe->maxincl = rtp->maxincl;
+
+			appendstate->rtp_list = lappend(appendstate->rtp_list, rtpe);
+		}
+		appendstate->parentoid = node->parentoid;
+		ExecAssignExprContext(estate, &appendstate->ps);
+	}
+
+
 	return appendstate;
 }
 
@@ -237,9 +340,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))
 			return ExecClearTuple(node->ps.ps_ResultTupleSlot);
 
@@ -279,7 +394,60 @@ void
 ExecReScanAppend(AppendState *node)
 {
 	int			i;
+	ListCell   *lc;
+	int			n_minkeys = 0;
+	int			n_maxkeys = 0;
 
+	if (node->ps.chgParam != NULL && node->rtp_list)
+	{
+		Datum	   *minkeys,
+				   *maxkeys;
+		bool		maxisnull,
+					minisnull;
+		Relation	rel;
+		NullTestType keynullness = IS_NOT_NULL;
+		int			min_datum_index,
+					max_datum_index;
+		bool		null_partition_chosen = false;
+		bool		default_partition_chosen = false;
+		int			i;
+
+		i = 0;
+		minkeys = (Datum *) palloc(list_length(node->rtp_list) * sizeof(Datum));
+		maxkeys = (Datum *) palloc(list_length(node->rtp_list) * sizeof(Datum));
+
+		foreach(lc, node->rtp_list)
+		{
+			RuntimePruningExec *rtpe = (RuntimePruningExec *) lfirst(lc);
+
+			if (rtpe->minop)
+			{
+				minkeys[i] = ExecEvalExpr(rtpe->minop, node->ps.ps_ExprContext, &minisnull);
+				n_minkeys++;
+			}
+			if (rtpe->maxop)
+			{
+				maxkeys[i] = ExecEvalExpr(rtpe->maxop, node->ps.ps_ExprContext, &maxisnull);
+				n_maxkeys++;
+			}
+			i++;
+
+			if (node->parentoid)
+			{
+				rel = relation_open(node->parentoid, NoLock);
+
+				/* FIXME:  min_incl and max_incl is currently taken as false */
+				node->index = get_partitions_for_keys(rel,
+													  &keynullness,
+													  minkeys, n_minkeys, false,
+													  maxkeys, n_maxkeys, false,
+													  &min_datum_index, &max_datum_index,
+													  &null_partition_chosen,
+													  &default_partition_chosen);
+				relation_close(rel, NoLock);
+			}
+		}
+	}
 	for (i = 0; i < node->as_nplans; i++)
 	{
 		PlanState  *subnode = node->appendplans[i];
@@ -298,6 +466,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 f1bed14..a1562f3 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -242,6 +242,9 @@ _copyAppend(const Append *from)
 	 */
 	COPY_NODE_FIELD(partitioned_rels);
 	COPY_NODE_FIELD(appendplans);
+	COPY_NODE_FIELD(rtp_list);
+	COPY_NODE_FIELD(et_rtp_list);
+	COPY_SCALAR_FIELD(parentoid);
 
 	return newnode;
 }
@@ -1145,6 +1148,22 @@ _copyNestLoopParam(const NestLoopParam *from)
 }
 
 /*
+ * _copyRuntimePruningInfo,
+ */
+static RuntimePruningInfo *
+_copyRuntimePruningInfo(const RuntimePruningInfo *from)
+{
+	RuntimePruningInfo *newnode = makeNode(RuntimePruningInfo);
+
+	COPY_NODE_FIELD(minop);
+	COPY_NODE_FIELD(maxop);
+	COPY_SCALAR_FIELD(minincl);
+	COPY_SCALAR_FIELD(maxincl);
+
+	return newnode;
+}
+
+/*
  * _copyPlanRowMark
  */
 static PlanRowMark *
@@ -4816,6 +4835,9 @@ copyObjectImpl(const void *from)
 		case T_NestLoopParam:
 			retval = _copyNestLoopParam(from);
 			break;
+		case T_RuntimePruningInfo:
+			retval = _copyRuntimePruningInfo(from);
+			break;
 		case T_PlanRowMark:
 			retval = _copyPlanRowMark(from);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b83d919..5764c7a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -978,6 +978,17 @@ _outNestLoopParam(StringInfo str, const NestLoopParam *node)
 }
 
 static void
+_outRuntimePruningInfo(StringInfo str, const RuntimePruningInfo *node)
+{
+	WRITE_NODE_TYPE("RUNTIMEPRUNINGINFO");
+
+	WRITE_NODE_FIELD(minop);
+	WRITE_NODE_FIELD(maxop);
+	WRITE_BOOL_FIELD(maxincl);
+	WRITE_BOOL_FIELD(minincl);
+}
+
+static void
 _outPlanRowMark(StringInfo str, const PlanRowMark *node)
 {
 	WRITE_NODE_TYPE("PLANROWMARK");
@@ -3746,6 +3757,9 @@ outNode(StringInfo str, const void *obj)
 			case T_NestLoopParam:
 				_outNestLoopParam(str, obj);
 				break;
+			case T_RuntimePruningInfo:
+				_outRuntimePruningInfo(str, obj);
+				break;
 			case T_PlanRowMark:
 				_outPlanRowMark(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index fbf8330..b090837 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2277,6 +2277,22 @@ _readNestLoopParam(void)
 }
 
 /*
+ * _readRuntimePruningInfo
+ */
+static RuntimePruningInfo *
+_readRuntimePruningInfo(void)
+{
+	READ_LOCALS(RuntimePruningInfo);
+
+	READ_NODE_FIELD(minop);
+	READ_NODE_FIELD(maxop);
+	READ_BOOL_FIELD(minincl);
+	READ_BOOL_FIELD(maxincl);
+
+	READ_DONE();
+}
+
+/*
  * _readPlanRowMark
  */
 static PlanRowMark *
@@ -2646,6 +2662,8 @@ parseNodeString(void)
 		return_value = _readLimit();
 	else if (MATCH("NESTLOOPPARAM", 13))
 		return_value = _readNestLoopParam();
+	else if (MATCH("RUNTIMEPRUNINGINFO", 18))
+		return_value = _readRuntimePruningInfo();
 	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 2bb3641..c34826d 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -848,7 +848,7 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 
 static void
 get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses,
-					NullTestType *keynullness)
+					NullTestType *keynullness, bool is_extern)
 {
 	ListCell   *lc;
 	int		keyPos;
@@ -883,6 +883,8 @@ get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses,
 
 				if (equal(leftop, partkey))
 				{
+					if (is_extern && !IsA(rightop, Param))
+						continue;
 					matchedclauses[keyPos] = lappend(matchedclauses[keyPos],
 													 clause);
 					/* A strict operator implies NOT NULL argument. */
@@ -892,6 +894,9 @@ get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses,
 				{
 					Oid		commutator = get_commutator(expr_op);
 
+					if (is_extern && !IsA(leftop, Param))
+						continue;
+
 					if (OidIsValid(commutator))
 					{
 						OpExpr   *commutated_expr;
@@ -929,6 +934,143 @@ get_matching_clause(RelOptInfo *rel, List *clauses, List **matchedclauses,
 }
 
 /*
+ * FIXME, this function can reuse most of the code from get_rel_partitions. we
+ * can make get_rel_partitions function generic so that it can accept the
+ * clause instead of processing all the baserelrestriction info, this way this
+ * function will be more generic and can be used for appendrel as well as for
+ * baserel.
+ */
+List *
+get_rel_partition_info(RelOptInfo *rel, RangeTblEntry *rte, List *clauses,
+					   RuntimePruningInfo * *prtp, bool is_extern)
+{
+	Relation	parent = heap_open(rte->relid, NoLock);
+	ListCell   *lc1;
+	List	   *matchedclauses[PARTITION_MAX_KEYS];
+	NullTestType keynullness[PARTITION_MAX_KEYS];
+	bool		need_next_min,
+				need_next_max,
+				minkey_set[PARTITION_MAX_KEYS],
+				maxkey_set[PARTITION_MAX_KEYS],
+				min_incl,
+				max_incl;
+	int			n_minkeys = 0,
+				n_maxkeys = 0,
+				i;
+
+	List	   *rtp_list = NIL;
+
+	get_matching_clause(rel, clauses, matchedclauses, keynullness, is_extern);
+
+	/*
+	 * Determine the min keys and the max keys using btree semantics-based
+	 * interpretation of the clauses' operators.
+	 */
+
+	/*
+	 * XXX - There should be a step similar to _bt_preprocess_keys() here, to
+	 * eliminate any redundant scan keys for a given partition column.  For
+	 * example, among a <= 4 and a <= 5, we can only keep a <= 4 for being
+	 * more restrictive and discard a <= 5.  While doing that, we can also
+	 * check to see if there exists a contradictory combination of scan keys
+	 * that makes the query trivially false for all records in the table.
+	 */
+	memset(minkey_set, false, sizeof(minkey_set));
+	memset(maxkey_set, false, sizeof(maxkey_set));
+	need_next_min = true;
+	need_next_max = true;
+	for (i = 0; i < rel->part_scheme->partnatts; i++)
+	{
+		/*
+		 * If no scan key existed for the previous column, we are done.
+		 */
+		if (i > n_minkeys)
+			need_next_min = false;
+
+		if (i > n_maxkeys)
+			need_next_max = false;
+
+		foreach(lc1, matchedclauses[i])
+		{
+			Expr	   *clause = lfirst(lc1);
+			Expr	   *leftop = (Expr *) get_leftop(clause);
+			Expr	   *rightop = (Expr *) get_rightop(clause);
+			Oid			opno = ((OpExpr *) clause)->opno,
+						opfamily = rel->part_scheme->partopfamily[i];
+			StrategyNumber strategy;
+			RuntimePruningInfo *rtp_temp = makeNode(RuntimePruningInfo);
+
+			strategy = get_op_opfamily_strategy(opno, opfamily);
+			switch (strategy)
+			{
+				case BTLessStrategyNumber:
+				case BTLessEqualStrategyNumber:
+					if (need_next_max)
+					{
+						rtp_temp->maxop = rightop;
+						if (!maxkey_set[i])
+							n_maxkeys++;
+						maxkey_set[i] = true;
+						max_incl = (strategy == BTLessEqualStrategyNumber);
+					}
+					if (strategy == BTLessStrategyNumber)
+						need_next_max = false;
+					break;
+
+				case BTGreaterStrategyNumber:
+				case BTGreaterEqualStrategyNumber:
+					if (need_next_min)
+					{
+						rtp_temp->minop = rightop;
+						if (!minkey_set[i])
+							n_minkeys++;
+						minkey_set[i] = true;
+						min_incl = (strategy == BTGreaterEqualStrategyNumber);
+					}
+					if (strategy == BTGreaterStrategyNumber)
+						need_next_min = false;
+					break;
+
+				case BTEqualStrategyNumber:
+					if (need_next_min)
+					{
+						rtp_temp->minop = rightop;
+						if (!minkey_set[i])
+							n_minkeys++;
+					}
+					minkey_set[i] = true;
+					min_incl = true;
+
+					if (need_next_max)
+					{
+						rtp_temp->maxop = rightop;
+						if (!maxkey_set[i])
+							n_maxkeys++;
+					}
+					maxkey_set[i] = true;
+					max_incl = true;
+					break;
+
+					/*
+					 * This might mean '<>', but we don't have anything for
+					 * that case yet.  Perhaps, handle that as key < const OR
+					 * key > const, once we have props needed for handling OR
+					 * clauses.
+					 */
+				default:
+					min_incl = max_incl = false;
+					break;
+			}
+
+			rtp_list = lappend(rtp_list, rtp_temp);
+		}
+	}
+
+	heap_close(parent, NoLock);
+	return rtp_list;
+}
+
+/*
  * get_rel_partitions
  *		Return the list of partitions of rel that pass the query clauses
  *
@@ -938,6 +1080,7 @@ static List *
 get_rel_partitions(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
 	Relation		parent = heap_open(rte->relid, NoLock);
+	PartitionDesc partdesc = RelationGetPartitionDesc(parent);
 	List   *indexes;
 	List   *result = NIL;
 	ListCell   *lc1;
@@ -956,7 +1099,7 @@ get_rel_partitions(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 			i;
 
 	get_matching_clause(rel, rel->baserestrictinfo, matchedclauses,
-						keynullness);
+						keynullness, false);
 
 	/*
 	 * Determine the min keys and the max keys using btree semantics-based
@@ -990,11 +1133,17 @@ get_rel_partitions(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 
 		foreach(lc1, matchedclauses[i])
 		{
-			Expr   *clause = lfirst(lc1);
-			Const  *rightop = (Const *) get_rightop(clause);
-			Oid		opno = ((OpExpr *) clause)->opno,
-					opfamily = rel->part_scheme->partopfamily[i];
-			StrategyNumber	strategy;
+			Expr	   *clause = lfirst(lc1);
+			Expr	   *rightop_expr = (Expr *) get_rightop(clause);
+			Oid			opno = ((OpExpr *) clause)->opno,
+						opfamily = rel->part_scheme->partopfamily[i];
+			StrategyNumber strategy;
+			Const	   *rightop;
+
+			if (IsA(rightop_expr, Param))
+				continue;
+
+			rightop = (Const *) rightop_expr;
 
 			strategy = get_op_opfamily_strategy(opno, opfamily);
 			switch (strategy)
@@ -1720,7 +1869,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));
 
 	/*
@@ -1747,7 +1896,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);
 	}
@@ -1801,7 +1950,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));
 	}
 }
@@ -2038,7 +2187,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 6ee2350..4b58eb4 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1217,7 +1217,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 2821662..991cb64 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -991,6 +991,7 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
 	return plan;
 }
 
+
 /*
  * create_append_plan
  *	  Create an Append plan for 'best_path' and (recursively) plans
@@ -1053,6 +1054,22 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 
 	copy_generic_path_info(&plan->plan, (Path *) best_path);
 
+	if (best_path->rtp_list)
+	{
+		ListCell   *lc;
+
+		foreach(lc, best_path->rtp_list)
+		{
+			RuntimePruningInfo *rtp = (RuntimePruningInfo *) lfirst(lc);
+
+			rtp->minop = (Expr *) replace_nestloop_params(root, (Node *) rtp->minop);
+			rtp->maxop = (Expr *) replace_nestloop_params(root, (Node *) rtp->maxop);
+		}
+		plan->rtp_list = best_path->rtp_list;
+	}
+
+	plan->et_rtp_list = best_path->et_rtp_list;
+	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 7f146d6..0e2d05f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3643,7 +3643,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 3e0c3de..88b0033 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 93add27..15620a0 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -189,6 +189,22 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset,
 	return (Expr *) expr;
 }
 
+Node *
+eval_const_expressions2(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);
+}
+
 /*
  * get_leftop
  *
@@ -2507,6 +2523,7 @@ eval_const_expressions_mutator(Node *node,
 								pval = prm->value;
 							else
 								pval = datumCopy(prm->value, typByVal, typLen);
+
 							return (Node *) makeConst(param->paramtype,
 													  param->paramtypmod,
 													  param->paramcollid,
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 26567cb..632105e 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -16,6 +16,7 @@
 
 #include <math.h>
 
+#include "catalog/pg_class.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
@@ -27,6 +28,7 @@
 #include "optimizer/var.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/selfuncs.h"
 
 
@@ -1200,7 +1202,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);
@@ -1245,6 +1247,40 @@ create_append_path(RelOptInfo *rel, List *subpaths, Relids required_outer,
 		Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer));
 	}
 
+	if (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 = RelationIdGetRelation(poid);
+
+			if (prel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+			{
+				RuntimePruningInfo *rtp;
+				ParamPathInfo *ppi = pathnode->path.param_info;
+
+				if (ppi)
+				{
+					List	   *ppi_clauses = ppi->ppi_clauses;
+
+					pathnode->rtp_list = get_rel_partition_info(rel, rte, ppi_clauses, &rtp, false);
+				}
+				/* EXTERN PARAM */
+				if (query_quals)
+				{
+					pathnode->et_rtp_list = get_rel_partition_info(rel, rte, query_quals, &rtp, true);
+				}
+				pathnode->parentoid = poid;
+
+			}
+		}
+
+
+	}
+
 	return pathnode;
 }
 
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f0973b8..d9cc3f8 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -1346,6 +1346,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))
@@ -1357,11 +1360,30 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer)
 	if ((ppi = find_param_path_info(appendrel, required_outer)))
 		return ppi;
 
+
+	/*
+	 * FIXME:  Generally for appendrel we don't fetch the clause from the the
+	 * join clause (only we do so for baserel), but for identifying whether
+	 * the appendrel is applicable for runtime pruning or not we need clause
+	 * also, I think there should be better way to do that.
+	 */
+	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/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index ad8a82f..458767d 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -1041,7 +1041,7 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
 	if (plansource->num_custom_plans < 5)
 		return true;
 
-	avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans;
+	avg_custom_cost = plansource->total_custom_cost / plansource->num_custom_plans + 100000;
 
 	/*
 	 * Prefer generic plan if it's less expensive than the average custom
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index c6d3021..e7de368 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -990,6 +990,14 @@ typedef struct ModifyTableState
 	/* Per plan/partition tuple conversion */
 } ModifyTableState;
 
+typedef struct RuntimePruningExec
+{
+	ExprState  *minop;
+	ExprState  *maxop;
+	bool		minincl;
+	bool		maxincl;
+}			RuntimePruningExec;
+
 /* ----------------
  *	 AppendState information
  *
@@ -1003,6 +1011,11 @@ typedef struct AppendState
 	PlanState **appendplans;	/* array of PlanStates for my inputs */
 	int			as_nplans;
 	int			as_whichplan;
+	Oid			parentoid;
+
+	int			as_whichpartition;
+	List	   *index;
+	List	   *rtp_list;
 } AppendState;
 
 /* ----------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 63196a1..2c16f8b 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_RuntimePruningInfo,
 
 	/*
 	 * TAGS FOR PLAN STATE NODES (execnodes.h)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index a382331..c10d554 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -237,6 +237,15 @@ typedef struct ModifyTable
 	List	   *exclRelTlist;	/* tlist of the EXCLUDED pseudo relation */
 } ModifyTable;
 
+typedef struct RuntimePruning
+{
+	Expr	   *minop;
+	Expr	   *maxop;
+	bool		minincl;
+	bool		maxincl;
+	Oid			parentoid;
+}			RuntimePruning;
+
 /* ----------------
  *	 Append node -
  *		Generate the concatenation of the results of sub-plans.
@@ -248,6 +257,10 @@ typedef struct Append
 	/* RT indexes of non-leaf tables in a partition tree */
 	List	   *partitioned_rels;
 	List	   *appendplans;
+	Oid			parentoid;
+	RuntimePruning *rtp;
+	List	   *rtp_list;
+	List	   *et_rtp_list;
 } Append;
 
 /* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 0f4996b..f1a2234 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1246,6 +1246,15 @@ typedef struct CustomPath
 	const struct CustomPathMethods *methods;
 } CustomPath;
 
+typedef struct RuntimePruningInfo
+{
+	NodeTag		type;
+	Expr	   *minop;
+	Expr	   *maxop;
+	bool		minincl;
+	bool		maxincl;
+}			RuntimePruningInfo;
+
 /*
  * AppendPath represents an Append plan, ie, successive execution of
  * several member plans.
@@ -1261,6 +1270,10 @@ typedef struct AppendPath
 	/* RT indexes of non-leaf tables in a partition tree */
 	List	   *partitioned_rels;
 	List	   *subpaths;		/* list of component Paths */
+	bool		runtime_prunable;
+	Oid			parentoid;
+	List	   *rtp_list;
+	List	   *et_rtp_list;
 } AppendPath;
 
 #define IS_DUMMY_PATH(p) \
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index e367221..c19ee31 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_expressions2(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 e372f88..4bcc3c7 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -63,7 +63,7 @@ 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,
+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,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 4e06b2e..b89cb95 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -59,6 +59,9 @@ extern int compute_parallel_worker(RelOptInfo *rel, double heap_pages,
 extern void create_partial_bitmap_paths(PlannerInfo *root, RelOptInfo *rel,
 							Path *bitmapqual);
 
+List *get_rel_partition_info(RelOptInfo *rel, RangeTblEntry *rte, List *clauses,
+						 RuntimePruningInfo **prtp, bool is_extern);
+
 #ifdef OPTIMIZER_DEBUG
 extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel);
 #endif
-- 
1.8.3.1

