]> granicus.if.org Git - postgresql/commitdiff
Old planner() becomes union_planner(); new planner() makes initialization
authorVadim B. Mikheev <vadim4o@yahoo.com>
Fri, 13 Feb 1998 03:37:04 +0000 (03:37 +0000)
committerVadim B. Mikheev <vadim4o@yahoo.com>
Fri, 13 Feb 1998 03:37:04 +0000 (03:37 +0000)
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
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c [new file with mode: 0644]

index b6a9ba0816bd0e91328f7ff2dc69347478cd6489..19690ae2081513696d473db6414c1e817c1c9dfe 100644 (file)
@@ -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
 
index f6a1470be8e9a21e5b21bd7b007546d9e17952c1..85ede31987e4da906753f80a978e1fbf6ca6b1d9 100644 (file)
@@ -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)) &&
index 10e67b76cd2f3103a352cee65737a3564ba30cd6..ca5859cb04bad3c5a61a2631021c795daea81378 100644 (file)
@@ -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
index 5643b675f96392f5ba1e97737124bf199346914c..367978eb57b003a5d7b9e658074c532c73f359a8 100644 (file)
@@ -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);
        }
        
        /*
index 16fd96aae1b316dc2988645c12c2739226e1665b..fc51657b8d0a874923fa37aa960a735605d0fa91 100644 (file)
@@ -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 (file)
index 0000000..4d43787
--- /dev/null
@@ -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);
+}