From 4a6e3a6a9fa18e01d6ae65015e55a42f36030d0d Mon Sep 17 00:00:00 2001
From: "Vadim B. Mikheev" <vadim4o@yahoo.com>
Date: Fri, 13 Feb 1998 03:37:04 +0000
Subject: [PATCH] Old planner() becomes union_planner(); new planner() makes
 initialization of some global variables to support subselects and calls
 union_planner(). Calls to SS_replace_correlation_vars() and
 SS_process_sublinks() in query_planner() before planning. Get rid of #ifdef
 INDEXSCAN_PATCH in createplan.c.

---
 src/backend/optimizer/plan/Makefile     |   4 +-
 src/backend/optimizer/plan/createplan.c |   4 +-
 src/backend/optimizer/plan/planmain.c   |  19 +-
 src/backend/optimizer/plan/planner.c    |  53 ++-
 src/backend/optimizer/plan/setrefs.c    |  16 +-
 src/backend/optimizer/plan/subselect.c  | 549 ++++++++++++++++++++++++
 6 files changed, 625 insertions(+), 20 deletions(-)
 create mode 100644 src/backend/optimizer/plan/subselect.c

diff --git a/src/backend/optimizer/plan/Makefile b/src/backend/optimizer/plan/Makefile
index b6a9ba0816b..19690ae2081 100644
--- a/src/backend/optimizer/plan/Makefile
+++ b/src/backend/optimizer/plan/Makefile
@@ -4,7 +4,7 @@
 #    Makefile for optimizer/plan
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/optimizer/plan/Makefile,v 1.5 1997/12/20 00:24:31 scrappy Exp $
+#    $Header: /cvsroot/pgsql/src/backend/optimizer/plan/Makefile,v 1.6 1998/02/13 03:36:51 vadim Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -15,7 +15,7 @@ INCLUDE_OPT = -I../..
 
 CFLAGS+=$(INCLUDE_OPT)
 
-OBJS = createplan.o initsplan.o planmain.o planner.o setrefs.o
+OBJS = createplan.o initsplan.o planmain.o planner.o setrefs.o subselect.o
 
 # not ready yet: predmig.o xfunc.o
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index f6a1470be8e..85ede31987e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.25 1998/02/10 04:01:09 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.26 1998/02/13 03:36:54 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -685,13 +685,11 @@ fix_indxqual_references(Node *clause, Path *index_path)
 	else if (IsA(clause, Const))
 	{
 		return (clause);
-#ifdef INDEXSCAN_PATCH
 	}
 	else if (IsA(clause, Param))
 	{
 		/* Function parameter used as index scan arg.  DZ - 27-8-1996 */
 		return (clause);
-#endif
 	}
 	else if (is_opclause(clause) &&
 			 is_funcclause((Node *) get_leftop((Expr *) clause)) &&
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 10e67b76cd2..ca5859cb04b 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.18 1998/02/10 04:01:12 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.19 1998/02/13 03:36:57 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,7 +22,9 @@
 #include "nodes/makefuncs.h"
 
 #include "optimizer/planmain.h"
+#include "optimizer/subselect.h"
 #include "optimizer/internal.h"
+#include "optimizer/prep.h"
 #include "optimizer/paths.h"
 #include "optimizer/clauses.h"
 #include "optimizer/keys.h"
@@ -72,7 +74,18 @@ query_planner(Query *root,
 	List	   *var_only_tlist = NIL;
 	List	   *level_tlist = NIL;
 	Plan	   *subplan = NULL;
-
+	
+	if ( PlannerQueryLevel > 1 )
+	{
+		/* should copy be made ? */
+		tlist = (List *) SS_replace_correlation_vars ((Node*)tlist);
+		qual = (List *) SS_replace_correlation_vars ((Node*)qual);
+	}
+	if (root->hasSubLinks)
+		qual = (List *) SS_process_sublinks ((Node*) qual);
+	
+	qual = cnfify((Expr *) qual, true);
+	
 	/*
 	 * A command without a target list or qualification is an error,
 	 * except for "delete foo".
@@ -145,7 +158,7 @@ query_planner(Query *root,
 					if (constant_qual != NULL)
 					{
 						return ((Plan *) make_result(tlist,
-												  (Node *) constant_qual,
+													 (Node *) constant_qual,
 													 (Plan *) scan));
 					}
 					else
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 5643b675f96..367978eb57b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7,11 +7,12 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.21 1998/01/15 18:59:48 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.22 1998/02/13 03:36:59 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include <sys/types.h>
+#include <string.h>
 
 #include "postgres.h"
 
@@ -30,6 +31,7 @@
 #include "optimizer/plancat.h"
 #include "optimizer/prep.h"
 #include "optimizer/planmain.h"
+#include "optimizer/subselect.h"
 #include "optimizer/paths.h"
 #include "optimizer/cost.h"
 
@@ -56,10 +58,32 @@ extern Plan *make_groupPlan(List **tlist, bool tuplePerGroup,
  *
  *****************************************************************************/
 
+Plan*
+planner(Query *parse)
+{
+	Plan	   *result_plan;
+	
+	PlannerQueryLevel = 1;
+	PlannerVarParam = NULL;
+	PlannerParamVar = NULL;
+	PlannerInitPlan = NULL;
+	PlannerPlanId = 0;
+	
+	result_plan = union_planner (parse);
+	
+	Assert (PlannerQueryLevel == 1);
+	if ( PlannerPlanId > 0 )
+	{
+		result_plan->initPlan = PlannerInitPlan;
+		(void) SS_finalize_plan (result_plan);
+	}
+	result_plan->nParamExec = length (PlannerParamVar);
+	
+	return (result_plan);
+}
 
 /*
- * planner--
- *	  Main query optimizer routine.
+ * union_planner--
  *
  *	  Invokes the planner on union queries if there are any left,
  *	  recursing if necessary to get them all, then processes normal plans.
@@ -68,14 +92,13 @@ extern Plan *make_groupPlan(List **tlist, bool tuplePerGroup,
  *
  */
 Plan	   *
-planner(Query *parse)
+union_planner(Query *parse)
 {
 	List	   *tlist = parse->targetList;
 	List	   *rangetable = parse->rtable;
 
 	Plan	   *result_plan = (Plan *) NULL;
 
-	List	   *primary_qual;
 	Index		rt_index;
 	
 
@@ -100,17 +123,25 @@ planner(Query *parse)
 	}
 	else
 	{
+		List  **vpm = NULL;
+		
 		tlist = preprocess_targetlist(tlist,
 									  parse->commandType,
 									  parse->resultRelation,
 									  parse->rtable);
-
-		primary_qual = cnfify((Expr *) parse->qual, true);
-
+		if ( parse->rtable != NULL )
+		{
+			vpm = (List **) palloc (length (parse->rtable) * sizeof (List*));
+			memset (vpm, 0, length (parse->rtable) * sizeof (List*));
+		}
+		PlannerVarParam = lcons (vpm, PlannerVarParam);
 		result_plan = query_planner(parse,
-									  parse->commandType,
-									  tlist,
-									  primary_qual);
+									parse->commandType,
+									tlist,
+									(List*) parse->qual);
+		PlannerVarParam = lnext (PlannerVarParam);
+		if ( vpm != NULL )
+			pfree (vpm);
 	}
 	
 	/*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 16fd96aae1b..fc51657b8d0 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.17 1998/02/10 04:01:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.18 1998/02/13 03:37:02 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -405,7 +405,21 @@ replace_clause_joinvar_refs(Expr *clause,
 									   leftvar,
 									   rightvar));
 	}
+	else if (is_subplan(clause))
+	{
+		((Expr*) clause)->args =
+		replace_subclause_joinvar_refs(((Expr*) clause)->args,
+										outer_tlist,
+										inner_tlist);
+		((SubPlan*) ((Expr*) clause)->oper)->sublink->oper = 
+		replace_subclause_joinvar_refs(((SubPlan*) ((Expr*) clause)->oper)->sublink->oper,
+										outer_tlist,
+										inner_tlist);
+		return ((List*) clause);
+	}
 	/* shouldn't reach here */
+	elog (ERROR, "replace_clause_joinvar_refs: unsupported clause %d", 
+			nodeTag (clause));
 	return NULL;
 }
 
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
new file mode 100644
index 00000000000..4d4378780c2
--- /dev/null
+++ b/src/backend/optimizer/plan/subselect.c
@@ -0,0 +1,549 @@
+/*-------------------------------------------------------------------------
+ *
+ * subselect.c--
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+
+#include "optimizer/subselect.h"
+#include "optimizer/planner.h"
+#include "optimizer/planmain.h"
+#include "optimizer/internal.h"
+#include "optimizer/paths.h"
+#include "optimizer/clauses.h"
+#include "optimizer/keys.h"
+#include "optimizer/tlist.h"
+#include "optimizer/var.h"
+#include "optimizer/cost.h"
+
+int		PlannerQueryLevel;		/* level of current query */
+List   *PlannerVarParam;		/* correlation Vars to Param mapper */
+List   *PlannerParamVar;		/* to get Var from Param->paramid */
+List   *PlannerInitPlan;		/* init subplans for current query */
+int		PlannerPlanId;
+
+
+static int
+_new_param (Var *var, int varlevel)
+{
+	List   *last;
+	int		i = 0;
+	
+	if ( PlannerParamVar == NULL )
+		last = PlannerParamVar = makeNode(List);
+	else
+	{
+		for (last = PlannerParamVar; ; )
+		{
+			i++;
+			if ( lnext(last) == NULL )
+				break;
+			last = lnext(last);
+		}
+		lnext(last) = makeNode(List);
+		last = lnext(last);
+	}
+	
+	lnext(last) = NULL;
+	lfirst(last) = makeVar (var->varno, var->varattno, var->vartype,
+			var->vartypmod, varlevel, var->varnoold, var->varoattno);
+	
+	return (i);
+}
+
+static Param*
+_replace_var (Var *var)
+{
+	List  **rt = (List**) nth (var->varlevelsup, PlannerVarParam);
+	List   *vpe = rt[var->varno - 1];
+	Param  *retval;
+	int		i;
+	
+	if ( vpe == NULL )
+	{
+		vpe = rt[var->varno - 1] = makeNode(List);
+		lfirsti(vpe) = -1;
+		lnext(vpe) = NULL;
+	}
+	
+	for (i = 1; ; i++)
+	{
+		if ( i == var->varattno )
+			break;
+		if ( lnext(vpe) == NULL )
+		{
+			lnext(vpe) = makeNode(List);
+			vpe = lnext(vpe);
+			lfirsti(vpe) = -1;
+			lnext(vpe) = NULL;
+		}
+		else
+			vpe = lnext(vpe);
+	}
+	
+	if ( (i = lfirsti(vpe)) < 0 )	/* parameter is not assigned */
+	{
+		i = _new_param (var, PlannerQueryLevel - var->varlevelsup);
+	}
+	
+	retval = makeNode(Param);
+	retval->paramkind = PARAM_EXEC;
+	retval->paramid = (AttrNumber) i;
+	retval->paramtype = var->vartype;
+	
+	return (retval);
+}
+
+static Node*
+_make_subplan (SubLink *slink)
+{
+	SubPlan	   *node = makeNode (SubPlan);
+	Plan	   *plan;
+	List	   *lst;
+	Node	   *result;
+	List	   *saved_ip = PlannerInitPlan;
+	
+	PlannerInitPlan = NULL;
+	
+	PlannerQueryLevel++;		/* we becomes child */
+	
+	node->plan = plan = union_planner ((Query*) slink->subselect);
+	
+	/* 
+	 * Assign subPlan, extParam and locParam to plan nodes.
+	 * At the moment, SS_finalize_plan doesn't handle initPlan-s
+	 * and so we assigne them to the topmost plan node and take
+	 * care about its extParam too.
+	 */
+	(void) SS_finalize_plan (plan);
+	plan->initPlan = PlannerInitPlan;
+	
+	/* Get extParam from InitPlan-s */
+	foreach (lst, PlannerInitPlan)
+	{
+		List   *lp;
+		
+		foreach (lp, ((SubPlan*) lfirst (lst))->plan->extParam)
+		{
+			if ( !intMember (lfirsti(lp), plan->extParam) )
+				plan->extParam = lappendi (plan->extParam, lfirsti(lp));
+		}
+	}
+	
+	/* and now we are parent again */
+	PlannerInitPlan = saved_ip;
+	PlannerQueryLevel--;
+	
+	node->plan_id = PlannerPlanId++;
+	node->rtable = ((Query*) slink->subselect)->rtable;
+	node->sublink = slink;
+	slink->subselect = NULL;	/* cool ?! */
+	
+	/* make parParam list */
+	foreach (lst, plan->extParam)
+	{
+		Var	   *var = nth (lfirsti(lst), PlannerParamVar);
+		
+		if ( var->varlevelsup == PlannerQueryLevel )
+			node->parParam = lappendi (node->parParam, lfirsti(lst));
+	}
+	
+	/* 
+	 * Un-correlated or undirect correlated plans of EXISTS or EXPR
+	 * types can be used as initPlans...
+	 */
+	if ( node->parParam == NULL && slink->subLinkType == EXPR_SUBLINK )
+	{
+		int	i = 0;
+		
+		/* transform right side of all sublink Oper-s into Param */
+		foreach (lst, slink->oper)
+		{
+			List		   *rside = lnext(((Expr*) lfirst(lst))->args);
+			TargetEntry	   *te = nth (i, plan->targetlist);
+			Var			   *var = makeVar (0, 0, te->resdom->restype, 
+										   te->resdom->restypmod, 
+										   PlannerQueryLevel, 0, 0);
+			Param		   *prm = makeNode(Param);
+			
+			prm->paramkind = PARAM_EXEC;
+			prm->paramid = (AttrNumber) _new_param (var, PlannerQueryLevel);
+			prm->paramtype = var->vartype;
+			lfirst(rside) = prm;
+			node->setParam = lappendi (node->setParam, prm->paramid);
+			pfree (var);
+			i++;
+		}
+		PlannerInitPlan = lappend (PlannerInitPlan, node);
+		if ( i > 1 )
+			result = (Node*) ((slink->useor) ? make_orclause (slink->oper) : 
+											   make_andclause (slink->oper));
+		else
+			result = (Node*) lfirst (slink->oper);
+	}
+	else if ( node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK )
+	{
+		Var		   *var = makeVar (0, 0, BOOLOID, -1, PlannerQueryLevel, 0, 0);
+		Param	   *prm = makeNode(Param);
+		
+		prm->paramkind = PARAM_EXEC;
+		prm->paramid = (AttrNumber) _new_param (var, PlannerQueryLevel);
+		prm->paramtype = var->vartype;
+		node->setParam = lappendi (node->setParam, prm->paramid);
+		pfree (var);
+		PlannerInitPlan = lappend (PlannerInitPlan, node);
+		result = (Node*) prm;
+	}
+	else	/* make expression of SUBPLAN type */
+	{
+		Expr   *expr = makeNode (Expr);
+		List   *args = NULL;
+		int		i = 0;
+		
+		expr->typeOid = BOOLOID;
+		expr->opType = SUBPLAN_EXPR;
+		expr->oper = (Node*) node;
+		
+		/* 
+		 * Make expr->args from parParam. Left sides of sublink Oper-s
+		 * are handled by optimizer directly...
+		 * Also, transform right side of sublink Oper-s into Const.
+		 */
+		foreach (lst, node->parParam)
+		{
+			Var	   *var = nth (lfirsti (lst), PlannerParamVar);
+			
+			var = (Var*) copyObject (var);
+			var->varlevelsup = 0;
+			args = lappend (args, var);
+		}
+		foreach (lst, slink->oper)
+		{
+			List		   *rside = lnext(((Expr*) lfirst(lst))->args);
+			TargetEntry	   *te = nth (i, plan->targetlist);
+			Const		   *con = makeConst (te->resdom->restype, 
+											 0, 0, true, 0, 0, 0);
+			lfirst(rside) = con;
+			i++;
+		}
+		expr->args = args;
+		result = (Node*) expr;
+	}
+	
+	return (result);
+	
+}
+
+static List *
+set_unioni (List *l1, List *l2)
+{
+	if (l1 == NULL)
+		return (l2);
+	if (l2 == NULL)
+		return (l1);
+	
+	return (nconc (l1, set_differencei (l2, l1)));
+}
+
+static List *
+_finalize_primnode (void *expr, List **subplan)
+{
+	List   *result = NULL;
+	
+	if ( expr == NULL )
+		return (NULL);
+	
+	if (IsA (expr, Param))
+	{
+		if ( ((Param*) expr)->paramkind == PARAM_EXEC )
+			return (lconsi (((Param*) expr)->paramid, (List*) NULL));
+	}
+	else if (single_node(expr))
+		return (NULL);
+	else if (IsA (expr, List))
+	{
+		List   *le;
+		foreach (le, (List*) expr)
+			result = set_unioni (result, 
+				_finalize_primnode (lfirst(le), subplan));
+	}
+	else if (IsA (expr, Iter))
+		return (_finalize_primnode (((Iter*) expr)->iterexpr, subplan));
+	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) || 
+				not_clause (expr) || is_funcclause(expr))
+		return (_finalize_primnode (((Expr*) expr)->args, subplan));
+	else if (IsA (expr, Aggreg))
+		return (_finalize_primnode (((Aggreg *) expr)->target, subplan));
+	else if (IsA (expr, ArrayRef))
+	{
+		result = _finalize_primnode (((ArrayRef*) expr)->refupperindexpr, subplan);
+		result = set_unioni (result, 
+			_finalize_primnode (((ArrayRef *) expr)->reflowerindexpr, subplan));
+		result = set_unioni (result, 
+			_finalize_primnode (((ArrayRef *) expr)->refexpr, subplan));
+		result = set_unioni (result, 
+			_finalize_primnode (((ArrayRef *) expr)->refassgnexpr, subplan));
+	}
+	else if (IsA (expr, TargetEntry))
+		return (_finalize_primnode (((TargetEntry*) expr)->expr, subplan));
+	else if (is_subplan (expr))
+	{
+		List   *lst;
+		
+		*subplan = lappend (*subplan, ((Expr*) expr)->oper);
+		foreach (lst, ((SubPlan*) ((Expr*) expr)->oper)->plan->extParam)
+		{
+			Var	   *var = nth (lfirsti(lst), PlannerParamVar);
+			
+			if ( var->varlevelsup < PlannerQueryLevel && 
+						!intMember (lfirsti(lst), result) )
+				result = lappendi (result, lfirsti(lst));
+		}
+	}
+	else
+		elog (ERROR, "_finalize_primnode: can't handle node %d", 
+			nodeTag (expr));
+	
+	return (result);
+}
+
+Node *
+SS_replace_correlation_vars (Node *expr)
+{
+
+	if ( expr == NULL )
+		return (NULL);
+	if (IsA (expr, List))
+	{
+		List   *le;
+		foreach (le, (List*) expr)
+			lfirst(le) = SS_replace_correlation_vars ((Node*) lfirst(le));
+	}
+	else if (IsA (expr, Var))
+	{
+		if ( ((Var*) expr)->varlevelsup > 0 )
+		{
+			Assert (((Var*) expr)->varlevelsup < PlannerQueryLevel);
+			expr = (Node*) _replace_var ((Var*) expr);
+		}
+	}
+	else if (IsA (expr, Iter))
+	{
+		((Iter*) expr)->iterexpr = 
+			SS_replace_correlation_vars(((Iter*) expr)->iterexpr);
+	}
+	else if (single_node(expr))
+		return (expr);
+	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) || 
+				not_clause (expr) || is_funcclause(expr))
+		((Expr *) expr)->args = (List*) 
+			SS_replace_correlation_vars ((Node*) ((Expr *) expr)->args);
+	else if (IsA (expr, Aggreg))
+		((Aggreg *) expr)->target = 
+				SS_replace_correlation_vars ((Node*) ((Aggreg *) expr)->target);
+	else if (IsA (expr, ArrayRef))
+	{
+		((ArrayRef *) expr)->refupperindexpr = (List*) 
+			SS_replace_correlation_vars ((Node*) ((ArrayRef *) expr)->refupperindexpr);
+		((ArrayRef *) expr)->reflowerindexpr = (List*) 
+			SS_replace_correlation_vars ((Node*) ((ArrayRef *) expr)->reflowerindexpr);
+		((ArrayRef *) expr)->refexpr = 
+			SS_replace_correlation_vars ((Node*) ((ArrayRef *) expr)->refexpr);
+		((ArrayRef *) expr)->refassgnexpr = 
+			SS_replace_correlation_vars (((ArrayRef *) expr)->refassgnexpr);
+	}
+	else if (IsA (expr, TargetEntry))
+		((TargetEntry*) expr)->expr = 
+			SS_replace_correlation_vars ((Node*) ((TargetEntry*) expr)->expr);
+	else if (IsA (expr, SubLink))
+	{
+		List   *le;
+		
+		foreach (le, ((SubLink*) expr)->oper)	/* left sides only */
+		{
+			List   *oparg = ((Expr*) lfirst (le))->args;
+			
+			lfirst (oparg) = (List*) 
+				SS_replace_correlation_vars ((Node*) lfirst (oparg));
+		}
+		((SubLink*) expr)->lefthand = (List*) 
+			SS_replace_correlation_vars ((Node*) ((SubLink*) expr)->lefthand);
+	}
+	else
+		elog (NOTICE, "SS_replace_correlation_vars: can't handle node %d", 
+			nodeTag (expr));
+	
+	return (expr);
+}
+
+Node*
+SS_process_sublinks (Node *expr)
+{
+	if ( expr == NULL )
+		return (NULL);
+	if (IsA (expr, List))
+	{
+		List   *le;
+		foreach (le, (List*) expr)
+			lfirst(le) = SS_process_sublinks ((Node*) lfirst(le));
+	}
+	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) || 
+				not_clause (expr) || is_funcclause(expr))
+		((Expr *) expr)->args = (List*)
+			SS_process_sublinks ((Node*) ((Expr *) expr)->args);
+	else if (IsA (expr, SubLink))		/* got it! */
+		expr = _make_subplan ((SubLink*) expr);
+	
+	return (expr);
+}
+
+List*
+SS_finalize_plan (Plan *plan)
+{
+	List   *extParam = NULL;
+	List   *locParam = NULL;
+	List   *subPlan = NULL;
+	List   *param_list;
+	List   *lst;
+	
+	if ( plan == NULL )
+		return (NULL);
+	
+	param_list = _finalize_primnode (plan->targetlist, &subPlan);
+	Assert (subPlan == NULL);
+	
+	switch (nodeTag(plan))
+	{
+		case T_Result:
+			param_list = set_unioni (param_list, 
+				_finalize_primnode (((Result*) plan)->resconstantqual, &subPlan));
+			break;
+
+		case T_Append:
+			foreach (lst, ((Append*) plan)->unionplans)
+				param_list = set_unioni (param_list, 
+					 SS_finalize_plan ((Plan*) lfirst (lst)));
+			break;
+		
+		case T_IndexScan:
+			param_list = set_unioni (param_list, 
+				_finalize_primnode (((IndexScan*) plan)->indxqual, &subPlan));
+			Assert (subPlan == NULL);
+			break;
+
+		case T_MergeJoin:
+			param_list = set_unioni (param_list, 
+				_finalize_primnode (((MergeJoin*) plan)->mergeclauses, &subPlan));
+			Assert (subPlan == NULL);
+			break;
+
+		case T_HashJoin:
+			param_list = set_unioni (param_list, 
+				_finalize_primnode (((HashJoin*) plan)->hashclauses, &subPlan));
+			Assert (subPlan == NULL);
+			break;
+		
+		case T_Hash:
+			param_list = set_unioni (param_list, 
+				_finalize_primnode (((Hash*) plan)->hashkey, &subPlan));
+			Assert (subPlan == NULL);
+			break;
+
+		case T_Agg:
+			param_list = set_unioni (param_list, 
+				_finalize_primnode (((Agg*) plan)->aggs, &subPlan));
+			Assert (subPlan == NULL);
+			break;
+			
+		case T_SeqScan:
+		case T_NestLoop:
+		case T_Material:
+		case T_Sort:
+		case T_Unique:
+		case T_Group:
+			break;
+		default:
+			elog(ERROR, "SS_finalize_plan: node %d unsupported", nodeTag(plan));
+			return (NULL);
+	}
+	
+	param_list = set_unioni (param_list, _finalize_primnode (plan->qual, &subPlan));
+	param_list = set_unioni (param_list, SS_finalize_plan (plan->lefttree));
+	param_list = set_unioni (param_list, SS_finalize_plan (plan->righttree));
+	
+	foreach (lst, param_list)
+	{
+		Var	   *var = nth (lfirsti(lst), PlannerParamVar);
+		
+		if ( var->varlevelsup < PlannerQueryLevel )
+			extParam = lappendi (extParam, lfirsti(lst));
+		else if ( var->varlevelsup > PlannerQueryLevel )
+			elog (ERROR, "SS_finalize_plan: plan shouldn't reference subplan' variable");
+		else
+		{
+			Assert (var->varno == 0 && var->varattno == 0);
+			locParam = lappendi (locParam, lfirsti(lst));
+		}
+	}
+	
+	plan->extParam = extParam;
+	plan->locParam = locParam;
+	plan->subPlan = subPlan;
+	
+	return (param_list);
+
+}
+
+List *SS_pull_subplan (void *expr);
+
+List *
+SS_pull_subplan (void *expr)
+{
+	List   *result = NULL;
+	
+	if ( expr == NULL || single_node(expr) )
+		return (NULL);
+	
+	if (IsA (expr, List))
+	{
+		List   *le;
+		foreach (le, (List*) expr)
+			result = nconc (result, SS_pull_subplan (lfirst(le)));
+	}
+	else if (IsA (expr, Iter))
+		return (SS_pull_subplan (((Iter*) expr)->iterexpr));
+	else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) || 
+				not_clause (expr) || is_funcclause(expr))
+		return (SS_pull_subplan (((Expr*) expr)->args));
+	else if (IsA (expr, Aggreg))
+		return (SS_pull_subplan (((Aggreg *) expr)->target));
+	else if (IsA (expr, ArrayRef))
+	{
+		result = SS_pull_subplan (((ArrayRef*) expr)->refupperindexpr);
+		result = nconc (result, 
+			SS_pull_subplan (((ArrayRef *) expr)->reflowerindexpr));
+		result = nconc (result, 
+			SS_pull_subplan (((ArrayRef *) expr)->refexpr));
+		result = nconc (result, 
+			SS_pull_subplan (((ArrayRef *) expr)->refassgnexpr));
+	}
+	else if (IsA (expr, TargetEntry))
+		return (SS_pull_subplan (((TargetEntry*) expr)->expr));
+	else if (is_subplan (expr))
+		return (lcons (((Expr*) expr)->oper, NULL));
+	else
+		elog (ERROR, "SS_pull_subplan: can't handle node %d", 
+			nodeTag (expr));
+	
+	return (result);
+}
-- 
GitLab