From bc879dc79c3a76de3d56acd980c06227c1f44c45 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=8C=83?= <yizhi.fzh@alibaba-inc.com>
Date: Wed, 7 Oct 2020 16:19:25 +0800
Subject: [PATCH v1] Reduce some generic plan cost by adjusting the Append node
 cost only

Generic plan includes some costs which can be reduced at initial
partition prune stage, while such cost is reduced at plan time for
custom plan.  So choose_custom_plan always return true for this case.

This patch try to fix this issue by estimating how many partitions can
be pruned at initial partition prune time (based on the prune result from
custom plan). But it doesn't help much since reducing the cost from Append
node is not enough. In join case, after the initial partition prune, not
only append's cost can be reduced,  the overall join cost should be reduced
as well.
---
 src/backend/nodes/copyfuncs.c           |  16 ++
 src/backend/nodes/nodeFuncs.c           |  13 ++
 src/backend/nodes/outfuncs.c            |   1 +
 src/backend/nodes/readfuncs.c           |   1 +
 src/backend/optimizer/path/costsize.c   |   8 -
 src/backend/optimizer/plan/createplan.c |   4 +
 src/backend/optimizer/plan/planner.c    |   4 +
 src/backend/optimizer/plan/setrefs.c    |   2 +
 src/backend/optimizer/util/inherit.c    |  63 +++++++-
 src/backend/utils/cache/plancache.c     | 190 +++++++++++++++++++++++-
 src/include/nodes/nodeFuncs.h           |   2 +
 src/include/nodes/nodes.h               |   1 +
 src/include/nodes/pathnodes.h           |  13 ++
 src/include/nodes/plannodes.h           |  10 +-
 src/include/optimizer/cost.h            |   7 +
 src/include/optimizer/inherit.h         |   2 +-
 src/include/utils/plancache.h           |   2 +
 17 files changed, 321 insertions(+), 18 deletions(-)

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 0409a40b82..90be22bcfc 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -94,6 +94,7 @@ _copyPlannedStmt(const PlannedStmt *from)
 	COPY_NODE_FIELD(rootResultRelations);
 	COPY_NODE_FIELD(appendRelations);
 	COPY_NODE_FIELD(subplans);
+	COPY_NODE_FIELD(flatten_live_parts);
 	COPY_BITMAPSET_FIELD(rewindPlanIDs);
 	COPY_NODE_FIELD(rowMarks);
 	COPY_NODE_FIELD(relationOids);
@@ -243,6 +244,7 @@ _copyAppend(const Append *from)
 	 * copy remainder of node
 	 */
 	COPY_BITMAPSET_FIELD(apprelids);
+	COPY_SCALAR_FIELD(relid);
 	COPY_NODE_FIELD(appendplans);
 	COPY_SCALAR_FIELD(first_partial_plan);
 	COPY_NODE_FIELD(part_prune_info);
@@ -4673,6 +4675,16 @@ _copyPartitionCmd(const PartitionCmd *from)
 	return newnode;
 }
 
+static LivePartition *
+_copyLivePartition(const LivePartition *from)
+{
+	LivePartition *newnode = makeNode(LivePartition);
+	COPY_SCALAR_FIELD(relid);
+	COPY_SCALAR_FIELD(lived_count);
+	COPY_SCALAR_FIELD(count);
+	return newnode;
+}
+
 static CreatePublicationStmt *
 _copyCreatePublicationStmt(const CreatePublicationStmt *from)
 {
@@ -5706,6 +5718,10 @@ copyObjectImpl(const void *from)
 			retval = _copyPartitionCmd(from);
 			break;
 
+		case T_LivePartition:
+			retval = _copyLivePartition(from);
+			break;
+
 			/*
 			 * MISCELLANEOUS NODES
 			 */
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 9ce8f43385..df96b72290 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -1681,6 +1681,19 @@ set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
 		opexpr->opfuncid = get_opcode(opexpr->opno);
 }
 
+LivePartition *
+find_related_liveparts(List *live_parts, Oid relid)
+{
+	ListCell *lc;
+	foreach(lc, live_parts)
+	{
+		LivePartition *live_part = lfirst_node(LivePartition, lc);
+		if (live_part->relid == relid)
+			return live_part;
+	}
+	return NULL;
+}
+
 
 /*
  *	check_functions_in_node -
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f0386480ab..0d09fa6235 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -433,6 +433,7 @@ _outAppend(StringInfo str, const Append *node)
 	_outPlanInfo(str, (const Plan *) node);
 
 	WRITE_BITMAPSET_FIELD(apprelids);
+	WRITE_UINT_FIELD(relid);
 	WRITE_NODE_FIELD(appendplans);
 	WRITE_INT_FIELD(first_partial_plan);
 	WRITE_NODE_FIELD(part_prune_info);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 42050ab719..e7e4ef52e6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1669,6 +1669,7 @@ _readAppend(void)
 	ReadCommonPlan(&local_node->plan);
 
 	READ_BITMAPSET_FIELD(apprelids);
+	READ_UINT_FIELD(relid);
 	READ_NODE_FIELD(appendplans);
 	READ_INT_FIELD(first_partial_plan);
 	READ_NODE_FIELD(part_prune_info);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index cd3716d494..4dde6867a8 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -100,14 +100,6 @@
 
 #define LOG2(x)  (log(x) / 0.693147180559945)
 
-/*
- * Append and MergeAppend nodes are less expensive than some other operations
- * which use cpu_tuple_cost; instead of adding a separate GUC, estimate the
- * per-tuple cost as cpu_tuple_cost multiplied by this value.
- */
-#define APPEND_CPU_COST_MULTIPLIER 0.5
-
-
 double		seq_page_cost = DEFAULT_SEQ_PAGE_COST;
 double		random_page_cost = DEFAULT_RANDOM_PAGE_COST;
 double		cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 99278eed93..e1dfb62d3b 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1132,6 +1132,10 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
 	plan->plan.lefttree = NULL;
 	plan->plan.righttree = NULL;
 	plan->apprelids = rel->relids;
+	if (IS_PARTITIONED_REL(rel) && rel->relid > 0)
+		plan->relid = planner_rt_fetch(rel->relid, root)->relid;
+	else
+		plan->relid = InvalidOid;
 
 	if (pathkeys != NIL)
 	{
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 3e2b4965c4..fa9cb2db92 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -315,6 +315,8 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 	glob->lastPlanNodeId = 0;
 	glob->transientPlan = false;
 	glob->dependsOnRole = false;
+	glob->flatten_live_parts = NIL;
+	glob->append_plans = NIL;
 
 	/*
 	 * Assess whether it's feasible to use parallel mode for this query. We
@@ -523,6 +525,8 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 	result->rootResultRelations = glob->rootResultRelations;
 	result->appendRelations = glob->appendRelations;
 	result->subplans = glob->subplans;
+	result->flatten_live_parts = glob->flatten_live_parts;
+	result->append_plans = glob->append_plans;
 	result->rewindPlanIDs = glob->rewindPlanIDs;
 	result->rowMarks = glob->finalrowmarks;
 	result->relationOids = glob->relationOids;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index dd8e2e966d..a15fa7f552 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1000,6 +1000,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			}
 			break;
 		case T_Append:
+			root->glob->append_plans = lappend(root->glob->append_plans,
+											   plan);
 			/* Needs special treatment, see comments below */
 			return set_append_references(root,
 										 (Append *) plan,
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c
index 3132fd35a5..a209ff3673 100644
--- a/src/backend/optimizer/util/inherit.c
+++ b/src/backend/optimizer/util/inherit.c
@@ -21,6 +21,7 @@
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "optimizer/appendinfo.h"
 #include "optimizer/inherit.h"
 #include "optimizer/optimizer.h"
@@ -38,7 +39,8 @@
 static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 									   RangeTblEntry *parentrte,
 									   Index parentRTindex, Relation parentrel,
-									   PlanRowMark *top_parentrc, LOCKMODE lockmode);
+									   PlanRowMark *top_parentrc, LOCKMODE lockmode,
+									   bool *any_pruned);
 static void expand_single_inheritance_child(PlannerInfo *root,
 											RangeTblEntry *parentrte,
 											Index parentRTindex, Relation parentrel,
@@ -131,6 +133,7 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
 	/* Scan the inheritance set and expand it */
 	if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 	{
+		bool any_pruned = false;
 		/*
 		 * Partitioned table, so set up for partitioning.
 		 */
@@ -141,7 +144,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
 		 * extract the partition key columns of all the partitioned tables.
 		 */
 		expand_partitioned_rtentry(root, rel, rte, rti,
-								   oldrelation, oldrc, lockmode);
+								   oldrelation, oldrc, lockmode, &any_pruned);
+		if (any_pruned)
+			count_live_partitions(root, rel);
 	}
 	else
 	{
@@ -278,13 +283,15 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
 
 /*
  * expand_partitioned_rtentry
- *		Recursively expand an RTE for a partitioned table.
+ *		Recursively expand an RTE for a partitioned table. any_pruned is an
+ * out parameter which shows if any partition is pruned at this step.
  */
 static void
 expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 						   RangeTblEntry *parentrte,
 						   Index parentRTindex, Relation parentrel,
-						   PlanRowMark *top_parentrc, LOCKMODE lockmode)
+						   PlanRowMark *top_parentrc, LOCKMODE lockmode,
+						   bool *any_pruned)
 {
 	PartitionDesc partdesc;
 	Bitmapset  *live_parts;
@@ -334,6 +341,9 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 	if (num_live_parts > 0)
 		expand_planner_arrays(root, num_live_parts);
 
+	if (num_live_parts < partdesc->nparts)
+		*any_pruned = true;
+
 	/*
 	 * We also store partition RelOptInfo pointers in the parent relation.
 	 * Since we're palloc0'ing, slots corresponding to pruned partitions will
@@ -383,7 +393,8 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 		if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
 			expand_partitioned_rtentry(root, childrelinfo,
 									   childrte, childRTindex,
-									   childrel, top_parentrc, lockmode);
+									   childrel, top_parentrc, lockmode,
+									   any_pruned);
 
 		/* Close child relation, but keep locks */
 		table_close(childrel, NoLock);
@@ -804,3 +815,45 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 
 	return true;
 }
+
+
+/*
+ * See how many child partitions survived for a partitioned relation
+ */
+void
+count_live_partitions(PlannerInfo *root, RelOptInfo *rel)
+{
+	int live_parts = 0, i = -1;
+	RelOptInfo *childrel;
+	LivePartition *live_part_stat;
+	Oid relid;
+
+	Assert(IS_PARTITIONED_REL(rel));
+
+	while((i = bms_next_member(rel->all_partrels, i)) >= 0)
+	{
+		childrel = root->simple_rel_array[i];
+		if (!IS_PARTITIONED_REL(childrel))
+			live_parts += 1;
+		else
+			live_parts += bms_num_members(childrel->all_partrels);
+	}
+
+	relid = planner_rt_fetch(rel->relid, root)->relid;
+	live_part_stat = find_related_liveparts(root->glob->flatten_live_parts, relid);
+
+	if (live_part_stat)
+	{
+		live_part_stat->count++;
+		live_part_stat->lived_count += live_parts;
+	}
+	else
+	{
+		LivePartition *tmp = makeNode(LivePartition);
+		tmp->relid = relid;
+		tmp->lived_count = live_parts;
+		tmp->count = 1;
+		root->glob->flatten_live_parts = lappend(root->glob->flatten_live_parts,
+												 tmp);
+	}
+}
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 50d6ad28b4..9c640bb68e 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -62,6 +62,7 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/optimizer.h"
+#include "optimizer/cost.h"
 #include "parser/analyze.h"
 #include "parser/parsetree.h"
 #include "storage/lmgr.h"
@@ -105,6 +106,8 @@ static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 static bool choose_custom_plan(CachedPlanSource *plansource,
 							   ParamListInfo boundParams);
 static double cached_plan_cost(CachedPlan *plan, bool include_planner);
+static double cost_generic_plan(CachedPlanSource *plansource, List *plist);
+static void merge_live_partitions(List **accumulatedl_live_parts, List *curr_live_parts);
 static Query *QueryListGetPrimaryStmt(List *stmts);
 static void AcquireExecutorLocks(List *stmt_list, bool acquire);
 static void AcquirePlannerLocks(List *stmt_list, bool acquire);
@@ -220,6 +223,7 @@ CreateCachedPlan(RawStmt *raw_parse_tree,
 	plansource->total_custom_cost = 0;
 	plansource->num_generic_plans = 0;
 	plansource->num_custom_plans = 0;
+	plansource->total_lived_parts = NIL;
 
 	MemoryContextSwitchTo(oldcxt);
 
@@ -957,6 +961,15 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 		 */
 		MemoryContextSwitchTo(plan_context);
 
+		if (boundParams == NULL)
+		{
+			/*
+			 * Generic plan, PlannedStmt->append_plans is not copied on purpose
+			 * (see comments in find_append_plans), so we must cost_generic_plan
+			 * before copyObject.
+			 **/
+			plansource->generic_cost = cost_generic_plan(plansource, plist);
+		}
 		plist = copyObject(plist);
 	}
 	else
@@ -1065,12 +1078,123 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams)
 	return true;
 }
 
+/*
+ * append_plan_cost_reduction
+ *	Get append plan cost reduction based on the plan time partition prune from
+ *  custom plan.
+ */
+static double
+append_plan_cost_reduction(Append *aplan, LivePartition *live_parts)
+{
+	double result;
+	double total_child_plans = 0, total_child_cost = 0;
+	double avg_lives, live_part_ratio;
+	ListCell *lc;
+	foreach(lc, aplan->appendplans)
+	{
+		Plan	*plan = lfirst(lc);
+		++total_child_plans;
+		total_child_cost += plan->total_cost;
+	}
+
+	avg_lives = live_parts->lived_count / live_parts->count;
+	if (avg_lives < 1)
+		avg_lives = 1;
+	live_part_ratio = avg_lives / total_child_plans;
+	result = (1 - live_part_ratio) * total_child_cost;
+	result +=
+		cpu_tuple_cost * APPEND_CPU_COST_MULTIPLIER *
+		( 1 - live_part_ratio ) * aplan->plan.plan_rows;
+
+	elog(INFO, "append_plan_cost_reduction %d %f", aplan->plan.plan_node_id, result);
+	/*
+	 * XXX: I assume no need to consider the parallel sine all the plan_rows
+	 * and subplan->total_cost have considered it already.
+	 */
+	return result;
+}
+
+
+/*
+ * find_append_plans
+ *	find append plans from PlannedStmt.
+ */
+static List *
+find_append_plans(PlannedStmt *stmt)
+{
+	/*
+	 * Method 1: with append_plan being gathered at set_plan_refs stage.
+	 * however it might be a big stuff for a partitioned table with
+	 * thousands of partitions and there is no use to CachedPlanSource,
+	 * so I didn't copy it in _copyPlannedStmt on purpose, however coping
+	 * a object partially might a bad practice, so I have method 2 in mind
+	 * but it is not implemented yet.
+	 */
+	return stmt->append_plans;
+
+	/*
+	 * Method 2: writing a plantree_walker(Plan, bool (*walker)(), void *context)
+	 * and go though all the plans in PlannedStmt which is not implemented yet.
+	 */
+}
+
+
+/*
+ * cost_generic_plan
+ *
+ * cost generic plan by guessing how many cost should be reduced at
+ * initial partition prune stage, then reduce such cost from total_cost.
+ */
+static double
+cost_generic_plan(CachedPlanSource *plansource, List *plist)
+{
+	double	result = 0;
+	ListCell	*lc1, *lc2;
+	double init_prune_cost_reduction = 0;
+
+	foreach(lc1, plist)
+	{
+		PlannedStmt *stmt = lfirst_node(PlannedStmt, lc1);
+		if (stmt->commandType == CMD_UTILITY)
+			continue;
+		result += stmt->planTree->total_cost;
+	}
+
+	if (plansource->total_lived_parts != NULL)
+	{
+		Assert(list_length(plansource->total_lived_parts) == list_length(plist));
+		forboth(lc1, plist, lc2, plansource->total_lived_parts)
+		{
+			PlannedStmt *stmt = lfirst_node(PlannedStmt, lc1);
+			List	*lived_parts = lfirst_node(List, lc2);
+			ListCell	*lc3;
+			if (stmt->commandType == CMD_UTILITY)
+				continue;
+			foreach(lc3, find_append_plans(stmt))
+			{
+				Append *aplan = lfirst_node(Append, lc3);
+				LivePartition *live_part;
+				if (!OidIsValid(aplan->relid))
+					continue;
+			   live_part = find_related_liveparts(lived_parts, aplan->relid);
+				if (live_part)
+					init_prune_cost_reduction += append_plan_cost_reduction(aplan, live_part);
+												
+			}
+		}
+	}
+
+	return result - init_prune_cost_reduction;
+}
+
 /*
  * cached_plan_cost: calculate estimated cost of a plan
  *
  * If include_planner is true, also include the estimated cost of constructing
  * the plan.  (We must factor that into the cost of using a custom plan, but
  * we don't count it for a generic plan.)
+ * XXX: can be renamed to custom_plan_cost since the generic plan is cost
+ * int cost_generic_plan.
  */
 static double
 cached_plan_cost(CachedPlan *plan, bool include_planner)
@@ -1119,6 +1243,68 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
 	return result;
 }
 
+
+/*
+ * record_runtime_partition_prunes
+ *	Record the plan time partition pruned information from custom plan to
+ * CachedPlanSource for later use.
+ */
+static void
+record_runtime_partition_prunes(CachedPlanSource *plansource, CachedPlan *cplan)
+{
+	MemoryContext oldCtx;
+
+	oldCtx = MemoryContextSwitchTo(plansource->context);
+	if (plansource->total_lived_parts == NIL)
+	{
+		ListCell	*lc;
+		foreach(lc, cplan->stmt_list)
+		{
+			PlannedStmt *stmt = lfirst_node(PlannedStmt, lc);
+			plansource->total_lived_parts = lappend(plansource->total_lived_parts,
+													copyObject(stmt->flatten_live_parts));
+		}
+	}
+	else
+	{
+		ListCell	*lc1, *lc2;
+		Assert(list_length(plansource->total_lived_parts) == list_length(cplan->stmt_list));
+		forboth(lc1, plansource->total_lived_parts, lc2, cplan->stmt_list)
+		{
+			List	*plansource_live_parts = lfirst_node(List, lc1);
+			List	*stmt_live_parts = lfirst_node(PlannedStmt, lc2)->flatten_live_parts;
+			merge_live_partitions(&plansource_live_parts, stmt_live_parts);
+		}
+	}
+	MemoryContextSwitchTo(oldCtx);
+}
+
+/*
+ * merge_live_partitions
+ */
+static void
+merge_live_partitions(List **accumulatedl_live_parts, List *curr_live_parts)
+{
+	ListCell *lc;
+	foreach(lc, curr_live_parts)
+	{
+		LivePartition *curr_live_part = lfirst_node(LivePartition, lc);
+		LivePartition *accm_live_part = find_related_liveparts(*accumulatedl_live_parts,
+															   curr_live_part->relid);
+		if (accm_live_part)
+		{
+			accm_live_part->lived_count += curr_live_part->lived_count;
+			accm_live_part->count += curr_live_part->count;
+		}
+			
+		else
+			*accumulatedl_live_parts = lappend(*accumulatedl_live_parts,
+												copyObject(curr_live_part));
+	}
+}
+
+
+
 /*
  * GetCachedPlan: get a cached plan from a CachedPlanSource.
  *
@@ -1188,8 +1374,6 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 				MemoryContextSetParent(plan->context,
 									   MemoryContextGetParent(plansource->context));
 			}
-			/* Update generic_cost whenever we make a new generic plan */
-			plansource->generic_cost = cached_plan_cost(plan, false);
 
 			/*
 			 * If, based on the now-known value of generic_cost, we'd not have
@@ -1217,7 +1401,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
 		plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
 		/* Accumulate total costs of custom plans */
 		plansource->total_custom_cost += cached_plan_cost(plan, true);
-
+		record_runtime_partition_prunes(plansource, plan);
 		plansource->num_custom_plans++;
 	}
 	else
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index 9cc56eecaa..c508f73389 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -14,6 +14,7 @@
 #define NODEFUNCS_H
 
 #include "nodes/parsenodes.h"
+#include "nodes/pathnodes.h"
 
 
 /* flags bits for query_tree_walker and query_tree_mutator */
@@ -53,6 +54,7 @@ extern int	exprLocation(const Node *expr);
 extern void fix_opfuncids(Node *node);
 extern void set_opfuncid(OpExpr *opexpr);
 extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
+extern LivePartition *find_related_liveparts(List *live_parts, Oid relid);
 
 /* Is clause a FuncExpr clause? */
 static inline bool
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 7ddd8c011b..b1b3f727bf 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -274,6 +274,7 @@ typedef enum NodeTag
 	T_RollupData,
 	T_GroupingSetData,
 	T_StatisticExtInfo,
+	T_LivePartition,
 
 	/*
 	 * TAGS FOR MEMORY NODES (memnodes.h)
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index dbe86e7af6..6297010fe9 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -147,6 +147,10 @@ typedef struct PlannerGlobal
 	char		maxParallelHazard;	/* worst PROPARALLEL hazard level */
 
 	PartitionDirectory partition_directory; /* partition descriptors */
+
+	List		*flatten_live_parts;
+
+	List	    *append_plans; /* set it on set_plan_refs */
 } PlannerGlobal;
 
 /* macro for fetching the Plan associated with a SubPlan node */
@@ -2513,6 +2517,15 @@ typedef struct
 	int64		offset_est;
 } FinalPathExtraData;
 
+
+typedef struct
+{
+	NodeTag		tag;
+	Oid		relid;
+	int		lived_count;
+	int	    count;
+} LivePartition;
+
 /*
  * For speed reasons, cost estimation for join paths is performed in two
  * phases: the first phase tries to quickly derive a lower bound for the
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 83e01074ed..a15c17cb4e 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -79,6 +79,14 @@ typedef struct PlannedStmt
 	List	   *subplans;		/* Plan trees for SubPlan expressions; note
 								 * that some could be NULL */
 
+	List	   *flatten_live_parts;
+
+	/*
+	 * I didn't copy it on purpose to keep the size small,
+	 * This might be a bad practice ...
+	 */
+	List	   *append_plans;
+
 	Bitmapset  *rewindPlanIDs;	/* indices of subplans that require REWIND */
 
 	List	   *rowMarks;		/* a list of PlanRowMark's */
@@ -252,8 +260,8 @@ typedef struct Append
 {
 	Plan		plan;
 	Bitmapset  *apprelids;		/* RTIs of appendrel(s) formed by this node */
+	Oid			relid;  /* set to relation's oid for partitioned table only */
 	List	   *appendplans;
-
 	/*
 	 * All 'appendplans' preceding this index are non-partial plans. All
 	 * 'appendplans' from this index onwards are partial plans.
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 6141654e47..07bfd9066d 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -31,6 +31,13 @@
 
 #define DEFAULT_EFFECTIVE_CACHE_SIZE  524288	/* measured in pages */
 
+/*
+ * Append and MergeAppend nodes are less expensive than some other operations
+ * which use cpu_tuple_cost; instead of adding a separate GUC, estimate the
+ * per-tuple cost as cpu_tuple_cost multiplied by this value.
+ */
+#define APPEND_CPU_COST_MULTIPLIER 0.5
+
 typedef enum
 {
 	CONSTRAINT_EXCLUSION_OFF,	/* do not use c_e */
diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h
index 0ba6132652..396b25d4e3 100644
--- a/src/include/optimizer/inherit.h
+++ b/src/include/optimizer/inherit.h
@@ -23,5 +23,5 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
 extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 								  RelOptInfo *childrel, RangeTblEntry *childRTE,
 								  AppendRelInfo *appinfo);
-
+extern void count_live_partitions(PlannerInfo *root, RelOptInfo *rel);
 #endif							/* INHERIT_H */
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index 4901568553..2d1dcde5c6 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -18,6 +18,7 @@
 #include "access/tupdesc.h"
 #include "lib/ilist.h"
 #include "nodes/params.h"
+#include "nodes/pathnodes.h"
 #include "tcop/cmdtag.h"
 #include "utils/queryenvironment.h"
 #include "utils/resowner.h"
@@ -130,6 +131,7 @@ typedef struct CachedPlanSource
 	/* State kept to help decide whether to use custom or generic plans: */
 	double		generic_cost;	/* cost of generic plan, or -1 if not known */
 	double		total_custom_cost;	/* total cost of custom plans so far */
+	List	   *total_lived_parts;
 	int64		num_custom_plans;	/* # of custom plans included in total */
 	int64		num_generic_plans;	/* # of generic plans */
 } CachedPlanSource;
-- 
2.21.0

