From 5ca611841bcd37c7ee8448c46c8398ef8d8edcc4 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Fri, 26 Jun 2015 09:40:47 -0400
Subject: [PATCH] Improve handling of CustomPath/CustomPlan(State) children.

Allow CustomPath to have a list of paths, CustomPlan a list of plans,
and CustomPlanState a list of planstates known to the core system, so
that custom path/plan providers can more reasonably use this
infrastructure for nodes with multiple children.

KaiGai Kohei, per a design suggestion from Tom Lane, with some
further kibitzing by me.
---
 doc/src/sgml/custom-scan.sgml           | 10 +++++++++-
 src/backend/commands/explain.c          | 22 ++++++++++++++++++++++
 src/backend/optimizer/plan/createplan.c | 13 ++++++++++++-
 src/backend/optimizer/plan/setrefs.c    |  8 ++++++++
 src/backend/optimizer/plan/subselect.c  | 25 +++++++++++++++++++++----
 src/include/nodes/execnodes.h           |  1 +
 src/include/nodes/plannodes.h           |  1 +
 src/include/nodes/relation.h            |  4 +++-
 8 files changed, 77 insertions(+), 7 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index 62a8a3305bb..dc327b11b21 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -60,6 +60,7 @@ typedef struct CustomPath
 {
     Path      path;
     uint32    flags;
+    List     *custom_paths;
     List     *custom_private;
     const CustomPathMethods *methods;
 } CustomPath;
@@ -73,6 +74,9 @@ typedef struct CustomPath
     <literal>CUSTOMPATH_SUPPORT_BACKWARD_SCAN</> if the custom path can support
     a backward scan and <literal>CUSTOMPATH_SUPPORT_MARK_RESTORE</> if it
     can support mark and restore.  Both capabilities are optional.
+    An optional <structfield>custom_paths</> is a list of <structname>Path</>
+    nodes used by this custom-path node; these will be transformed into
+    <structname>Plan</> nodes by planner.
     <structfield>custom_private</> can be used to store the custom path's
     private data.  Private data should be stored in a form that can be handled
     by <literal>nodeToString</>, so that debugging routines that attempt to
@@ -112,7 +116,8 @@ Plan *(*PlanCustomPath) (PlannerInfo *root,
                          RelOptInfo *rel,
                          CustomPath *best_path,
                          List *tlist,
-                         List *clauses);
+                         List *clauses,
+                         List *custom_plans);
 </programlisting>
     Convert a custom path to a finished plan.  The return value will generally
     be a <literal>CustomScan</> object, which the callback must allocate and
@@ -145,6 +150,7 @@ typedef struct CustomScan
 {
     Scan      scan;
     uint32    flags;
+    List     *custom_plans;
     List     *custom_exprs;
     List     *custom_private;
     List     *custom_scan_tlist;
@@ -159,6 +165,8 @@ typedef struct CustomScan
     estimated costs, target lists, qualifications, and so on.
     <structfield>flags</> is a bitmask with the same meaning as in
     <structname>CustomPath</>.
+    <structfield>custom_plans</> can be used to store child
+    <structname>Plan</> nodes.
     <structfield>custom_exprs</> should be used to
     store expression trees that will need to be fixed up by
     <filename>setrefs.c</> and <filename>subselect.c</>, while
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a82c6ff7b4d..0d1ecc2a3ed 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -115,6 +115,8 @@ static void ExplainMemberNodes(List *plans, PlanState **planstates,
 				   List *ancestors, ExplainState *es);
 static void ExplainSubPlans(List *plans, List *ancestors,
 				const char *relationship, ExplainState *es);
+static void ExplainCustomChildren(CustomScanState *css,
+								  List *ancestors, ExplainState *es);
 static void ExplainProperty(const char *qlabel, const char *value,
 				bool numeric, ExplainState *es);
 static void ExplainOpenGroup(const char *objtype, const char *labelname,
@@ -1624,6 +1626,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		IsA(plan, BitmapAnd) ||
 		IsA(plan, BitmapOr) ||
 		IsA(plan, SubqueryScan) ||
+		(IsA(planstate, CustomScanState) &&
+		 ((CustomScanState *) planstate)->custom_ps != NIL) ||
 		planstate->subPlan;
 	if (haschildren)
 	{
@@ -1678,6 +1682,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
 						"Subquery", NULL, es);
 			break;
+		case T_CustomScan:
+			ExplainCustomChildren((CustomScanState *) planstate,
+								  ancestors, es);
+			break;
 		default:
 			break;
 	}
@@ -2647,6 +2655,20 @@ ExplainSubPlans(List *plans, List *ancestors,
 	}
 }
 
+/*
+ * Explain a list of children of a CustomScan.
+ */
+static void
+ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
+{
+	ListCell   *cell;
+	const char *label =
+		(list_length(css->custom_ps) != 1 ? "children" : "child");
+
+	foreach (cell, css->custom_ps)
+		ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
+}
+
 /*
  * Explain a property, such as sort keys or targets, that takes the form of
  * a list of unlabeled items.  "data" is a list of C strings.
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index a3482def643..dc2dcbf93f7 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2157,6 +2157,16 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
 {
 	CustomScan *cplan;
 	RelOptInfo *rel = best_path->path.parent;
+	List	   *custom_plans = NIL;
+	ListCell   *lc;
+
+	/* Recursively transform child paths. */
+	foreach (lc, best_path->custom_paths)
+	{
+		Plan   *plan = create_plan_recurse(root, (Path *) lfirst(lc));
+
+		custom_plans = lappend(custom_plans, plan);
+	}
 
 	/*
 	 * Sort clauses into the best execution order, although custom-scan
@@ -2172,7 +2182,8 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
 															  rel,
 															  best_path,
 															  tlist,
-															  scan_clauses);
+															  scan_clauses,
+															  custom_plans);
 	Assert(IsA(cplan, CustomScan));
 
 	/*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index a7f65dd529f..c0641a7cfb0 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1151,6 +1151,8 @@ set_customscan_references(PlannerInfo *root,
 						  CustomScan *cscan,
 						  int rtoffset)
 {
+	ListCell   *lc;
+
 	/* Adjust scanrelid if it's valid */
 	if (cscan->scan.scanrelid > 0)
 		cscan->scan.scanrelid += rtoffset;
@@ -1194,6 +1196,12 @@ set_customscan_references(PlannerInfo *root,
 			fix_scan_list(root, cscan->custom_exprs, rtoffset);
 	}
 
+	/* Adjust child plan-nodes recursively, if needed */
+	foreach (lc, cscan->custom_plans)
+	{
+		lfirst(lc) = set_plan_refs(root, (Plan *) lfirst(lc), rtoffset);
+	}
+
 	/* Adjust custom_relids if needed */
 	if (rtoffset > 0)
 	{
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index f80abb494c7..4708b87f330 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2373,10 +2373,27 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_CustomScan:
-			finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs,
-							  &context);
-			/* We assume custom_scan_tlist cannot contain Params */
-			context.paramids = bms_add_members(context.paramids, scan_params);
+			{
+				CustomScan *cscan = (CustomScan *) plan;
+				ListCell   *lc;
+
+				finalize_primnode((Node *) cscan->custom_exprs,
+								  &context);
+				/* We assume custom_scan_tlist cannot contain Params */
+				context.paramids =
+					bms_add_members(context.paramids, scan_params);
+
+				/* child nodes if any */
+				foreach (lc, cscan->custom_plans)
+				{
+					context.paramids =
+						bms_add_members(context.paramids,
+										finalize_plan(root,
+													  (Plan *) lfirst(lc),
+													  valid_params,
+													  scan_params));
+				}
+			}
 			break;
 
 		case T_ModifyTable:
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index db5bd7faf04..541ee187356 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1616,6 +1616,7 @@ typedef struct CustomScanState
 {
 	ScanState	ss;
 	uint32		flags;			/* mask of CUSTOMPATH_* flags, see relation.h */
+	List	   *custom_ps;		/* list of child PlanState nodes, if any */
 	const CustomExecMethods *methods;
 } CustomScanState;
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index d967219c0b5..5f538f3e8cc 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -550,6 +550,7 @@ typedef struct CustomScan
 {
 	Scan		scan;
 	uint32		flags;			/* mask of CUSTOMPATH_* flags, see relation.h */
+	List	   *custom_plans;	/* list of Plan nodes, if any */
 	List	   *custom_exprs;	/* expressions that custom code may evaluate */
 	List	   *custom_private; /* private data for custom code */
 	List	   *custom_scan_tlist;		/* optional tlist describing scan
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 33b08745702..cb916ea8e14 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -929,7 +929,8 @@ typedef struct CustomPathMethods
 												RelOptInfo *rel,
 												struct CustomPath *best_path,
 												List *tlist,
-												List *clauses);
+												List *clauses,
+												List *custom_plans);
 	/* Optional: print additional fields besides "private" */
 	void		(*TextOutCustomPath) (StringInfo str,
 											  const struct CustomPath *node);
@@ -939,6 +940,7 @@ typedef struct CustomPath
 {
 	Path		path;
 	uint32		flags;			/* mask of CUSTOMPATH_* flags, see above */
+	List	   *custom_paths;	/* list of child Path nodes, if any */
 	List	   *custom_private;
 	const CustomPathMethods *methods;
 } CustomPath;
-- 
GitLab