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);
+}