From 85d1f50909864a2e56013ebfc20e619d801ff569 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=80=E6=8C=83?= <yizhi.fzh@alibaba-inc.com>
Date: Mon, 17 Aug 2020 21:49:40 +0800
Subject: [PATCH v1] Convert the AlternativeSubplan to Subplan as soon as we
 knows the num_calls

---
 src/backend/optimizer/path/allpaths.c  | 34 ++++++++++++++++++++++++++
 src/backend/optimizer/path/costsize.c  |  6 ++---
 src/backend/optimizer/plan/subselect.c | 28 +++++++++++++++++++++
 src/backend/optimizer/util/relnode.c   | 33 +++++++++++++++++++++++++
 src/include/optimizer/planner.h        |  1 +
 src/include/optimizer/subselect.h      |  4 ++-
 6 files changed, 102 insertions(+), 4 deletions(-)

diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 6da0dcd61c..5aa466c80d 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -41,6 +41,7 @@
 #include "optimizer/plancat.h"
 #include "optimizer/planner.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
 #include "parser/parse_clause.h"
 #include "parser/parsetree.h"
@@ -767,6 +768,39 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
 	Relids		required_outer;
 
+	/*
+	 * We have known the size of the rel, if there is altPlan on this rel
+	 * we can choose now. In this demo, I just track the alt plan in targetlist.
+	 */
+	ListCell *lc;
+	bool recost = false;
+	foreach(lc, rel->reltarget->exprs)
+	{
+		if (IsA(lfirst(lc), PlaceHolderVar))
+		{
+			bool recost_expr = false;
+			PlaceHolderVar *phv = lfirst_node(PlaceHolderVar, lc);
+			if (bms_is_subset(phv->phrels, rel->relids) &&
+				IsA(phv->phexpr, AlternativeSubPlan))
+			{
+				phv->phexpr = (Expr *)choose_subplan((AlternativeSubPlan *)phv->phexpr,
+													 rel->rows,
+													 &recost_expr);
+				if (recost_expr)
+					recost = true;
+			}
+		}
+	}
+
+	if (recost)
+	{
+		set_rel_width(root, rel);
+	}
+
+	/*
+	 * TODO: we can also check subplan in other places
+	 */
+
 	/*
 	 * We don't support pushing join clauses into the quals of a seqscan, but
 	 * it could still have required parameterization due to LATERAL refs in
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index fda4b2c6e8..c730d1bacf 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -91,6 +91,7 @@
 #include "optimizer/plancat.h"
 #include "optimizer/planmain.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/planner.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
 #include "utils/selfuncs.h"
@@ -175,7 +176,6 @@ static Selectivity get_foreign_key_join_selectivity(PlannerInfo *root,
 													List **restrictlist);
 static Cost append_nonpartial_cost(List *subpaths, int numpaths,
 								   int parallel_workers);
-static void set_rel_width(PlannerInfo *root, RelOptInfo *rel);
 static double relation_byte_size(double tuples, int width);
 static double page_size(double tuples, int width);
 static double get_parallel_divisor(Path *path);
@@ -1774,7 +1774,7 @@ cost_tuplesort(Cost *startup_cost, Cost *run_cost,
 
 /*
  * cost_incremental_sort
- * 	Determines and returns the cost of sorting a relation incrementally, when
+ *	Determines and returns the cost of sorting a relation incrementally, when
  *  the input path is presorted by a prefix of the pathkeys.
  *
  * 'presorted_keys' is the number of leading pathkeys by which the input path
@@ -5438,7 +5438,7 @@ set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel)
  * The per-attribute width estimates are cached for possible re-use while
  * building join relations or post-scan/join pathtargets.
  */
-static void
+void
 set_rel_width(PlannerInfo *root, RelOptInfo *rel)
 {
 	Oid			reloid = planner_rt_fetch(rel->relid, root)->relid;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 6eb794669f..721f8bd06d 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2959,3 +2959,31 @@ SS_make_initplan_from_plan(PlannerInfo *root,
 	/* Set costs of SubPlan using info from the plan tree */
 	cost_subplan(subroot, node, plan);
 }
+
+
+/*
+ * Choose the subplan from AlternativeSubPlan as soon as possible.
+ */
+SubPlan *
+choose_subplan(AlternativeSubPlan *altplan, double num_calls, bool *recost)
+{
+
+	double cost1, cost2;
+	SubPlan *subplan1, *subplan2, *res;
+	subplan1 = linitial_node(SubPlan, altplan->subplans);
+	subplan2 = lsecond_node(SubPlan, altplan->subplans);
+	cost1 = subplan1->startup_cost + num_calls * subplan1->per_call_cost;
+	cost2 = subplan2->startup_cost + num_calls * subplan2->per_call_cost;
+
+	if (cost1 < cost2)
+	{
+		res = subplan1;
+		*recost = false;
+	}
+	else
+	{
+		res = subplan2;
+		*recost = true;
+	}
+	return res;
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index a203e6f1ff..318546a4c4 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -27,6 +27,7 @@
 #include "optimizer/placeholder.h"
 #include "optimizer/plancat.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
 #include "utils/hsearch.h"
 #include "utils/lsyscache.h"
@@ -728,6 +729,38 @@ build_join_rel(PlannerInfo *root,
 	set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
 							   sjinfo, restrictlist);
 
+	/* Fix the AlternativeSubPlans */
+	{
+		ListCell *lc;
+		bool recost = false;
+		foreach(lc, joinrel->reltarget->exprs)
+		{
+			if (IsA(lfirst(lc), PlaceHolderVar))
+			{
+				bool recost_expr = false;
+				PlaceHolderVar *phv = lfirst_node(PlaceHolderVar, lc);
+				if (bms_is_subset(phv->phrels, joinrel->relids) &&
+					IsA(phv->phexpr, AlternativeSubPlan))
+				{
+					phv->phexpr = (Expr *)choose_subplan((AlternativeSubPlan *)phv->phexpr,
+														 joinrel->rows,
+														 &recost_expr);
+					if(recost_expr)
+						recost = true;
+					/* track how many exprs are changed, old_ones (AlternativeSubplans)
+					 * and new_ones (SubPlans)
+					 */
+				}
+			}
+		}
+
+		if (recost)
+		{
+			/* get the delta cost by old_costs and new_costs, and then
+			 * modify the cost of the  path in outer_rel and inner_rel.
+			 */
+		}
+	}
 	/*
 	 * Set the consider_parallel flag if this joinrel could potentially be
 	 * scanned within a parallel worker.  If this flag is false for either
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index beb7dbbcbe..a7a7b32eff 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel,
 
 extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr);
 
+extern void set_rel_width(PlannerInfo *root, RelOptInfo *rel);
 #endif							/* PLANNER_H */
diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h
index d6a872bd2c..136d60b7c0 100644
--- a/src/include/optimizer/subselect.h
+++ b/src/include/optimizer/subselect.h
@@ -36,5 +36,7 @@ extern Param *SS_make_initplan_output_param(PlannerInfo *root,
 extern void SS_make_initplan_from_plan(PlannerInfo *root,
 									   PlannerInfo *subroot, Plan *plan,
 									   Param *prm);
-
+extern SubPlan *choose_subplan(AlternativeSubPlan *altplan,
+							   double num_calls,
+							   bool *recost);
 #endif							/* SUBSELECT_H */
-- 
2.21.0

