diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 7e3a777..d58c50b 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -39,6 +39,7 @@
 #include "nodes/nodeFuncs.h"
 #include "nodes/parsenodes.h"
 #include "optimizer/clauses.h"
+#include "optimizer/pathnode.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/predtest.h"
@@ -1712,6 +1713,122 @@ get_partitions_from_clauses(Relation relation, int rt_index,
 	return result;
 }
 
+/*
+ * make_partition_pruneinfo
+ *		Build PartitionPruneInfo tree to allow the output of
+ *		get_partitions_from_clauses to be translated into
+ *		subpath indexes
+ */
+PartitionPruneInfo *
+make_partition_pruneinfo(PlannerInfo *root, RelOptInfo *rel,
+						 List *partition_rels, List *subpaths)
+{
+	PartitionPruneInfo *pinfo;
+	AppendRelInfo	   *appinfo;
+	ListCell		   *lc;
+	int					i;
+	int					partidx;
+	int					nparts = rel->nparts;
+
+	pinfo = makeNode(PartitionPruneInfo);
+	pinfo->parentid = rel->relid;
+	pinfo->subnodeindex = (int *) palloc(sizeof(int) * nparts);
+	pinfo->subpartindex = (PartitionPruneInfo **)
+						palloc0(sizeof(PartitionPruneInfo *) * nparts);
+	pinfo->nparts = nparts;
+
+	/*
+	 * -1 represents a partition that's already been pruned. Set them all to
+	 * this initially.  We'll determine the subpath index for the non-pruned
+	 * ones below.
+	 */
+	for (i = 0; i < nparts; i++)
+		pinfo->subnodeindex[i] = -1;
+
+	i = -1;
+	foreach(lc, subpaths)
+	{
+		Path *path = (Path *) lfirst(lc);
+
+		i++; /* track subnode index */
+
+		/* Find the AppendRelInfo for the Append child */
+		appinfo = find_childrel_appendrelinfo(root, path->parent);
+
+		/*
+		 * Skip subpaths who's rel is not directly parented by rel.  We'll
+		 * process any we skip here later when looping through partition_rels
+		 */
+		if (appinfo->parent_relid != rel->relid)
+			continue;
+
+		/*
+		 * Determine the element in part_appinfo which belongs to this
+		 * subnode.
+		 */
+		for (partidx = 0; partidx < nparts; partidx++)
+		{
+
+			if (rel->part_appinfos[partidx]->child_relid != appinfo->child_relid)
+				continue;
+
+			/* found it!  Save the subnode index */
+			pinfo->subnodeindex[partidx] = i;
+			break;
+		}
+	}
+
+	/*
+	 * Some of the tables returned by get_partitions_from_clauses may be other
+	 * partitioned tables.  Unlike the case above, these won't be subnodes of
+	 * the Append.  To handle these we must make use of another
+	 * PartitionPruneInfo which we must use to recursively search until we
+	 * reach the leaf partitions.
+	 */
+	foreach(lc, partition_rels)
+	{
+		Index rti = lfirst_int(lc);
+		RelOptInfo *subpart = find_base_rel(root, rti);
+
+		/*
+		 * partition_rels contains the rti of the relation being queried.  We
+		 * only care about sub-partition parents here.
+		 */
+		if (subpart->reloptkind == RELOPT_BASEREL)
+			continue;
+
+		appinfo = find_childrel_appendrelinfo(root, subpart);
+
+		/*
+		 * We only want to deal with sub-partition parents that are directly
+		 * below rel. We'll search until we get down to the leaf partitions by
+		 * recursively calling ourself.
+		 */
+		if (appinfo->parent_relid != rel->relid)
+			continue;
+
+		/*
+		 * Some of the bits returned by get_partitions_from_clauses will
+		 * reference a partition parent.  Here we mark that the index
+		 * references a partition parent, this allows a partition search
+		 * to recursively search for all subnodes matching a search.
+		 */
+		for (partidx = 0; partidx < nparts; partidx++)
+		{
+			if (rel->part_appinfos[partidx]->child_relid != appinfo->child_relid)
+				continue;
+
+			pinfo->subpartindex[partidx] = make_partition_pruneinfo(root,
+																	subpart,
+															partition_rels,
+																	subpaths);
+			break;
+		}
+	}
+
+	return pinfo;
+}
+
 /* Module-local functions */
 
 /*
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 0e93713..fc66124 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -57,6 +57,7 @@
 
 #include "postgres.h"
 
+#include "parser/parsetree.h"
 #include "executor/execdebug.h"
 #include "executor/nodeAppend.h"
 #include "miscadmin.h"
@@ -82,6 +83,10 @@ static TupleTableSlot *ExecAppend(PlanState *pstate);
 static bool choose_next_subplan_locally(AppendState *node);
 static bool choose_next_subplan_for_leader(AppendState *node);
 static bool choose_next_subplan_for_worker(AppendState *node);
+static void set_valid_runtime_subplans(AppendState *node,
+						   EState *estate, PartitionPruneInfo *pinfo,
+						   List *clauses);
+
 
 /* ----------------------------------------------------------------
  *		ExecInitAppend
@@ -129,6 +134,18 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
 	appendstate->as_nplans = nplans;
 
 	/*
+	 * The presence  of a part_prune_info means that run-time pruning is
+	 * enabled, so here we'll determine which subplans need to be scanned.
+	 */
+	if (node->part_prune_info)
+		set_valid_runtime_subplans(appendstate, estate, node->part_prune_info,
+								   NULL /* XXX get the clause list */);
+
+	/* else, mark all subplans as requiring a scan */
+	else
+		appendstate->as_valid_subplans = bms_add_range(NULL, 0, nplans - 1);
+
+	/*
 	 * Miscellaneous initialization
 	 *
 	 * Append plans don't have expression contexts because they never call
@@ -366,22 +383,19 @@ static bool
 choose_next_subplan_locally(AppendState *node)
 {
 	int			whichplan = node->as_whichplan;
+	int			nextplan;
 
 	/* We should never see INVALID_SUBPLAN_INDEX in this case. */
 	Assert(whichplan >= 0 && whichplan <= node->as_nplans);
 
 	if (ScanDirectionIsForward(node->ps.state->es_direction))
-	{
-		if (whichplan >= node->as_nplans - 1)
-			return false;
-		node->as_whichplan++;
-	}
+		nextplan = bms_next_member(node->as_valid_subplans, whichplan);
 	else
-	{
-		if (whichplan <= 0)
-			return false;
-		node->as_whichplan--;
-	}
+		nextplan = bms_prev_member(node->as_valid_subplans, whichplan);
+
+	if (nextplan < 0)
+		return false;
+	node->as_whichplan = nextplan;
 
 	return true;
 }
@@ -392,6 +406,7 @@ choose_next_subplan_locally(AppendState *node)
  *      Try to pick a plan which doesn't commit us to doing much
  *      work locally, so that as much work as possible is done in
  *      the workers.  Cheapest subplans are at the end.
+ *      XXX needs updated to use node->as_valid_subplans
  * ----------------------------------------------------------------
  */
 static bool
@@ -450,6 +465,7 @@ choose_next_subplan_for_leader(AppendState *node)
  *		in order of descending cost and then spreads out the
  *		workers as evenly as possible across the remaining partial
  *		plans.
+ *		XXX needs updated to use node->as_valid_subplans
  * ----------------------------------------------------------------
  */
 static bool
@@ -526,3 +542,45 @@ choose_next_subplan_for_worker(AppendState *node)
 
 	return true;
 }
+
+/*
+ * get_valid_runtime_subplans
+ *		Recursively descend through each PartitionPruneInfo, along the way
+ *		determine which subnode indexes we need to scan to get all rows that
+ *		satisfy 'clauses'.
+ */
+static void
+set_valid_runtime_subplans(AppendState *node, EState *estate,
+						   PartitionPruneInfo *pinfo, List *clauses)
+{
+	Bitmapset	   *partset;
+	Relation		rel;
+	Oid				relid;
+	int				i;
+
+	relid = getrelid(pinfo->parentid, estate->es_range_table);
+
+	rel = relation_open(relid, NoLock);
+
+	/* Determine which partition indexes we need to scan */
+	partset = get_partitions_from_clauses(rel, pinfo->parentid, clauses);
+
+	/* Translate partset into subnode indexes */
+	i = -1;
+	while ((i = bms_next_member(partset, i)) >= 0)
+	{
+		if (pinfo->subnodeindex[i] >= 0)
+			node->as_valid_subplans = bms_add_member(node->as_valid_subplans,
+													 pinfo->subnodeindex[i]);
+		else if (pinfo->subpartindex[i] != NULL)
+			set_valid_runtime_subplans(node, estate, pinfo->subpartindex[i],
+									   clauses);
+		else
+		{
+			/* If this happens then we're somehow missing an Append subnode */
+			elog(ERROR, "Partition missing from Append subnodes");
+		}
+	}
+
+	relation_close(rel, NoLock);
+}
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index ae30072..7c7f985 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -84,6 +84,25 @@ static const uint8 rightmost_one_pos[256] = {
 	4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
 };
 
+static const uint8 leftmost_one_pos[256] = {
+	0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+	4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+	7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+};
+
 static const uint8 number_of_ones[256] = {
 	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
 	1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
@@ -1045,6 +1064,79 @@ bms_next_member(const Bitmapset *a, int prevbit)
 }
 
 /*
+ * bms_prev_member - find prev member of a set
+ *
+ * Returns largest member less than "prevbit", or -2 if there is none.
+ * "prevbit" must NOT be more than one above the highest possible bit that can
+ * be set at the Bitmapset at its current size.
+ *
+ * To ease finding the highest set bit for the initial loop the special
+ * prevbit value of -1 can be passed to have the function find the highest
+ * 1-bit in the set.
+ *
+ * This is intended as support for iterating through the members of a set in
+ * reverse.  The typical pattern is
+ *
+ *			x = -1;
+ *			while ((x = bms_prev_member(inputset, x)) >= 0)
+ *				process member x;
+ *
+ * Notice that when there are no more members, we return -2, not -1 as you
+ * might expect.  The rationale for that is to allow distinguishing the
+ * loop-not-started state (x == -1) from the loop-completed state (x == -2).
+ * It makes no difference in simple loop usage, but complex iteration logic
+ * might need such an ability.
+ */
+
+int
+bms_prev_member(const Bitmapset *a, int prevbit)
+{
+	int			wordnum;
+	int			ushiftbits;
+	bitmapword	mask;
+
+	/*
+	 * If set is NULL or if there are no more bits to the right then we've
+	 * nothing to do.
+	 */
+	if (a == NULL || prevbit == 0)
+		return -2;
+
+	/* transform -1 to the highest possible bit we could have set */
+	if (prevbit == -1)
+		prevbit = a->nwords * BITS_PER_BITMAPWORD - 1;
+	else
+		prevbit--;
+
+	ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(prevbit) + 1);
+	mask = (~(bitmapword) 0) >> ushiftbits;
+	for (wordnum = WORDNUM(prevbit); wordnum >= 0; wordnum--)
+	{
+		bitmapword	w = a->words[wordnum];
+
+		/* switch off bits left of prevbit */
+		w &= mask;
+
+		if (w != 0)
+		{
+			int			result;
+			int			shift = 24;
+			result = wordnum * BITS_PER_BITMAPWORD;
+
+			while ((w >> shift) == 0)
+				shift -= 8;
+
+			result += shift + leftmost_one_pos[(w >> shift) & 255];
+			return result;
+		}
+
+		/* in subsequent words, consider all bits */
+		mask = (~(bitmapword) 0);
+	}
+	return -2;
+}
+
+/*
  * bms_hash_value - compute a hash key for a Bitmapset
  *
  * Note: we must ensure that any two bitmapsets that are bms_equal() will
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index d222eff..156022a 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -144,6 +144,7 @@ static List *get_append_rel_partitions(PlannerInfo *root,
 static List *match_clauses_to_partkey(PlannerInfo *root,
 						 RelOptInfo *rel,
 						 List *clauses,
+						 Bitmapset **paramids,
 						 bool *contains_const,
 						 bool *constfalse);
 
@@ -877,6 +878,7 @@ get_append_rel_partitions(PlannerInfo *root,
 	Relation		parent;
 	PartitionDesc	partdesc;
 	Bitmapset	   *partindexes;
+	Bitmapset	   *paramids = NULL;
 
 	/*
 	 * Get the clauses that match the partition key, including information
@@ -885,6 +887,7 @@ get_append_rel_partitions(PlannerInfo *root,
 	 */
 	partclauses = match_clauses_to_partkey(root, rel,
 										   list_copy(rel->baserestrictinfo),
+										   &paramids,
 										   &contains_const,
 										   &constfalse);
 
@@ -967,6 +970,7 @@ static List *
 match_clauses_to_partkey(PlannerInfo *root,
 						 RelOptInfo *rel,
 						 List *clauses,
+						 Bitmapset **paramids,
 						 bool *contains_const,
 						 bool *constfalse)
 {
@@ -1030,6 +1034,7 @@ match_clauses_to_partkey(PlannerInfo *root,
 							constfalse1;
 
 					if (match_clauses_to_partkey(root, rel, list_make1(arg),
+												 paramids,
 												 &contains_const1,
 												 &constfalse1) != NIL)
 					{
@@ -1154,8 +1159,17 @@ match_clauses_to_partkey(PlannerInfo *root,
 				 */
 				result = lappend(result, clause);
 
-				if (!*contains_const)
-					*contains_const = IsA(constexpr, Const);
+				if (IsA(constexpr, Const))
+					*contains_const = true;
+				else if (IsA(constexpr, Param))
+				{
+					/*
+					 * XXX there's a bunch of other places to gather Params
+					 * from. I've not done that yet.
+					 */
+					Param *param = (Param *) constexpr;
+					*paramids = bms_add_member(*paramids, param->paramid);
+				}
 			}
 			else if (IsA(clause, ScalarArrayOpExpr))
 			{
@@ -1715,6 +1729,7 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 	List	   *partitioned_rels = NIL;
 	RangeTblEntry *rte;
 	double		partial_rows = -1;
+	bool		runtimeprune = false;
 
 	/*
 	 * AppendPath generated for partitioned tables must record the RT indexes
@@ -1739,7 +1754,17 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 		rte = planner_rt_fetch(rel->relid, root);
 		if (rte->rtekind == RTE_RELATION &&
 			rte->relkind == RELKIND_PARTITIONED_TABLE)
-		partitioned_rels = rel->live_partitioned_rels;
+		{
+			partitioned_rels = rel->live_partitioned_rels;
+
+			/*
+			 * XXX Should only enable if we find Params matching the partkey.
+			 * We may have to record the ParamIds we matched to the partition
+			 * key in RelOptInfo, but I'm hoping there is a better way.
+			 */
+			if (rel->reloptkind == RELOPT_BASEREL)
+				runtimeprune = true;
+		}
 	}
 	else if (rel->reloptkind == RELOPT_JOINREL && rel->part_scheme)
 	{
@@ -1911,9 +1936,10 @@ 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, NIL,
+		add_path(rel, (Path *) create_append_path(root, rel, subpaths, NIL,
 												  NULL, 0, false,
-												  partitioned_rels, -1));
+												  partitioned_rels, -1,
+												  runtimeprune));
 
 	/*
 	 * Consider an append of unordered, unparameterized partial paths.  Make
@@ -1953,10 +1979,11 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 		Assert(parallel_workers > 0);
 
 		/* Generate a partial append path. */
-		appendpath = create_append_path(rel, NIL, partial_subpaths, NULL,
-										parallel_workers,
+		appendpath = create_append_path(root, rel, NIL, partial_subpaths,
+										NULL, parallel_workers,
 										enable_parallel_append,
-										partitioned_rels, -1);
+										partitioned_rels, -1,
+										runtimeprune);
 
 		/*
 		 * Make sure any subsequent partial paths use the same row count
@@ -2002,10 +2029,11 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 							   max_parallel_workers_per_gather);
 		Assert(parallel_workers > 0);
 
-		appendpath = create_append_path(rel, pa_nonpartial_subpaths,
+		appendpath = create_append_path(root, rel, pa_nonpartial_subpaths,
 										pa_partial_subpaths,
 										NULL, parallel_workers, true,
-										partitioned_rels, partial_rows);
+										partitioned_rels, partial_rows,
+										runtimeprune);
 		add_partial_path(rel, (Path *) appendpath);
 	}
 
@@ -2058,9 +2086,10 @@ add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 
 		if (subpaths_valid)
 			add_path(rel, (Path *)
-					 create_append_path(rel, subpaths, NIL,
+					 create_append_path(root, rel, subpaths, NIL,
 										required_outer, 0, false,
-										partitioned_rels, -1));
+										partitioned_rels, -1,
+										runtimeprune));
 	}
 }
 
@@ -2323,8 +2352,8 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
 	rel->pathlist = NIL;
 	rel->partial_pathlist = NIL;
 
-	add_path(rel, (Path *) create_append_path(rel, NIL, NIL, NULL,
-											  0, false, NIL, -1));
+	add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, NULL,
+											  0, false, NIL, -1, false));
 
 	/*
 	 * 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 5bd3031..15d1426 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -1232,8 +1232,8 @@ mark_dummy_rel(RelOptInfo *rel)
 	rel->partial_pathlist = NIL;
 
 	/* Set up the dummy path */
-	add_path(rel, (Path *) create_append_path(rel, NIL, NIL, NULL,
-											  0, false, NIL, -1));
+	add_path(rel, (Path *) create_append_path(NULL, rel, NIL, NIL, NULL,
+											  0, false, NIL, -1, false));
 
 	/* 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 f6c83d0..b72c30e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -204,7 +204,8 @@ static NamedTuplestoreScan *make_namedtuplestorescan(List *qptlist, List *qpqual
 static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
 				   Index scanrelid, int wtParam);
 static Append *make_append(List *appendplans, int first_partial_plan,
-			List *tlist, List *partitioned_rels);
+			List *tlist, List *partitioned_rels,
+			PartitionPruneInfo *partpruneinfo);
 static RecursiveUnion *make_recursive_union(List *tlist,
 					 Plan *lefttree,
 					 Plan *righttree,
@@ -1061,7 +1062,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
 	 */
 
 	plan = make_append(subplans, best_path->first_partial_path,
-					   tlist, best_path->partitioned_rels);
+					   tlist, best_path->partitioned_rels,
+					   best_path->partprune);
 
 	copy_generic_path_info(&plan->plan, (Path *) best_path);
 
@@ -5297,7 +5299,8 @@ make_foreignscan(List *qptlist,
 
 static Append *
 make_append(List *appendplans, int first_partial_plan,
-			List *tlist, List *partitioned_rels)
+			List *tlist, List *partitioned_rels,
+			PartitionPruneInfo *partpruneinfo)
 {
 	Append	   *node = makeNode(Append);
 	Plan	   *plan = &node->plan;
@@ -5309,7 +5312,7 @@ make_append(List *appendplans, int first_partial_plan,
 	node->partitioned_rels = partitioned_rels;
 	node->appendplans = appendplans;
 	node->first_partial_plan = first_partial_plan;
-
+	node->part_prune_info = partpruneinfo;
 	return node;
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7a09f07..2fe1d4a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -3678,14 +3678,16 @@ create_grouping_paths(PlannerInfo *root,
 				paths = lappend(paths, path);
 			}
 			path = (Path *)
-				create_append_path(grouped_rel,
+				create_append_path(root,
+								   grouped_rel,
 								   paths,
 								   NIL,
 								   NULL,
 								   0,
 								   false,
 								   NIL,
-								   -1);
+								   -1,
+								   false);
 			path->pathtarget = target;
 		}
 		else
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index a24e8ac..cc08769 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -590,8 +590,8 @@ generate_union_path(SetOperationStmt *op, PlannerInfo *root,
 	/*
 	 * Append the child results together.
 	 */
-	path = (Path *) create_append_path(result_rel, pathlist, NIL,
-									   NULL, 0, false, NIL, -1);
+	path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
+									   NULL, 0, false, NIL, -1, false);
 	/* We have to manually jam the right tlist into the path; ick */
 	path->pathtarget = create_pathtarget(root, tlist);
 
@@ -702,8 +702,8 @@ generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
 	/*
 	 * Append the child results together.
 	 */
-	path = (Path *) create_append_path(result_rel, pathlist, NIL,
-									   NULL, 0, false, NIL, -1);
+	path = (Path *) create_append_path(root, result_rel, pathlist, NIL,
+									   NULL, 0, false, NIL, -1, false);
 
 	/* 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/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 54126fb..8832b9b 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -17,6 +17,7 @@
 #include <math.h>
 
 #include "miscadmin.h"
+#include "catalog/partition.h"
 #include "nodes/nodeFuncs.h"
 #include "nodes/extensible.h"
 #include "optimizer/clauses.h"
@@ -1210,11 +1211,13 @@ 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,
+create_append_path(PlannerInfo *root,
+				   RelOptInfo *rel,
 				   List *subpaths, List *partial_subpaths,
 				   Relids required_outer,
 				   int parallel_workers, bool parallel_aware,
-				   List *partitioned_rels, double rows)
+				   List *partitioned_rels, double rows,
+				   bool runtimeprune)
 {
 	AppendPath *pathnode = makeNode(AppendPath);
 	ListCell   *l;
@@ -1269,6 +1272,11 @@ create_append_path(RelOptInfo *rel,
 	if (rows >= 0)
 		pathnode->path.rows = rows;
 
+	/* Generate data structure required for run-time partition pruning */
+	if (runtimeprune)
+		pathnode->partprune = make_partition_pruneinfo(root, rel,
+													   partitioned_rels,
+													   subpaths);
 	return pathnode;
 }
 
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 7a5ab45..9decfc5 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -16,6 +16,7 @@
 #include "fmgr.h"
 #include "executor/tuptable.h"
 #include "nodes/execnodes.h"
+#include "nodes/relation.h"
 #include "parser/parse_node.h"
 #include "utils/rel.h"
 
@@ -74,4 +75,8 @@ extern int get_partition_for_tuple(Relation relation, Datum *values,
 /* For partition-pruning */
 extern Bitmapset *get_partitions_from_clauses(Relation relation, int rt_index,
 							List *partclauses);
+extern PartitionPruneInfo *make_partition_pruneinfo(PlannerInfo *root,
+						 RelOptInfo *rel,
+						 List *partition_rels, List *subpaths);
+
 #endif							/* PARTITION_H */
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 3b62a97..25910c4 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -98,6 +98,7 @@ extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b);
 /* support for iterating through the integer elements of a set: */
 extern int	bms_first_member(Bitmapset *a);
 extern int	bms_next_member(const Bitmapset *a, int prevbit);
+extern int	bms_prev_member(const Bitmapset *a, int nextbit);
 
 /* support for hashtables using Bitmapsets as keys: */
 extern uint32 bms_hash_value(const Bitmapset *a);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1a35c5c..a04e6b6 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -999,6 +999,7 @@ typedef struct ModifyTableState
  *
  *		nplans			how many plans are in the array
  *		whichplan		which plan is being executed (0 .. n-1)
+ *		valid_subplans	for runtime pruning, valid appendplans indexes to scan
  * ----------------
  */
 
@@ -1013,6 +1014,7 @@ struct AppendState
 	PlanState **appendplans;	/* array of PlanStates for my inputs */
 	int			as_nplans;
 	int			as_whichplan;
+	Bitmapset  *as_valid_subplans;
 	ParallelAppendState *as_pstate; /* parallel coordination info */
 	Size		pstate_len;		/* size of parallel coordination info */
 	bool		(*choose_next_subplan) (AppendState *);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c5b5115..cbd76ca 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -218,6 +218,7 @@ typedef enum NodeTag
 	T_IndexOptInfo,
 	T_ForeignKeyOptInfo,
 	T_ParamPathInfo,
+	T_PartitionPruneInfo,
 	T_Path,
 	T_IndexPath,
 	T_BitmapHeapPath,
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 02fb366..64f3d75 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -249,6 +249,11 @@ typedef struct Append
 	List	   *partitioned_rels;
 	List	   *appendplans;
 	int			first_partial_plan;
+
+	/*
+	 * Details of how to prune surplus subplans or NULL when disabled enabled.
+	 */
+	PartitionPruneInfo *part_prune_info;
 } Append;
 
 /* ----------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 074ae0a..d056655 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1506,4 +1506,20 @@ typedef struct OnConflictExpr
 	List	   *exclRelTlist;	/* tlist of the EXCLUDED pseudo relation */
 } OnConflictExpr;
 
+/*
+ * PartitionPruneInfo
+ *
+ * Used for execution time partition pruning of Append subnodes
+ */
+typedef struct PartitionPruneInfo
+{
+	NodeTag		type;
+	Index		parentid; /* relid of parent partition rel */
+	int			nparts; /* length of the following arrays */
+	int		   *subnodeindex; /* subnode index indexed by partition id */
+
+	/* sub-PartitionPruneInfo indexed by partition id */
+	struct PartitionPruneInfo **subpartindex;
+} PartitionPruneInfo;
+
 #endif							/* PRIMNODES_H */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index bcb669d..02738a3 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1295,6 +1295,11 @@ typedef struct AppendPath
 
 	/* Index of first partial path in subpaths */
 	int			first_partial_path;
+
+	/*
+	 * Details of how to prune surplus subplans or NULL when disabled enabled.
+	 */
+	PartitionPruneInfo *partprune;
 } AppendPath;
 
 #define IS_DUMMY_PATH(p) \
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 00c134d..892469c 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -64,11 +64,11 @@ 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,
+extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel,
 				   List *subpaths, List *partial_subpaths,
 				   Relids required_outer,
 				   int parallel_workers, bool parallel_aware,
-				   List *partitioned_rels, double rows);
+				   List *partitioned_rels, double rows, bool runtimeprune);
 extern MergeAppendPath *create_merge_append_path(PlannerInfo *root,
 						 RelOptInfo *rel,
 						 List *subpaths,
