]> granicus.if.org Git - postgresql/blob - src/backend/optimizer/plan/subselect.c
Update copyright to 2002.
[postgresql] / src / backend / optimizer / plan / subselect.c
1 /*-------------------------------------------------------------------------
2  *
3  * subselect.c
4  *        Planning routines for subselects and parameters.
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.54 2002/06/20 20:29:31 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "catalog/pg_operator.h"
17 #include "catalog/pg_type.h"
18 #include "nodes/makefuncs.h"
19 #include "optimizer/clauses.h"
20 #include "optimizer/cost.h"
21 #include "optimizer/planmain.h"
22 #include "optimizer/planner.h"
23 #include "optimizer/subselect.h"
24 #include "parser/parsetree.h"
25 #include "parser/parse_expr.h"
26 #include "parser/parse_oper.h"
27 #include "utils/syscache.h"
28
29
30 Index           PlannerQueryLevel;      /* level of current query */
31 List       *PlannerInitPlan;    /* init subplans for current query */
32 List       *PlannerParamVar;    /* to get Var from Param->paramid */
33
34 int                     PlannerPlanId = 0;      /* to assign unique ID to subquery plans */
35
36 /*--------------------
37  * PlannerParamVar is a list of Var nodes, wherein the n'th entry
38  * (n counts from 0) corresponds to Param->paramid = n.  The Var nodes
39  * are ordinary except for one thing: their varlevelsup field does NOT
40  * have the usual interpretation of "subplan levels out from current".
41  * Instead, it contains the absolute plan level, with the outermost
42  * plan being level 1 and nested plans having higher level numbers.
43  * This nonstandardness is useful because we don't have to run around
44  * and update the list elements when we enter or exit a subplan
45  * recursion level.  But we must pay attention not to confuse this
46  * meaning with the normal meaning of varlevelsup.
47  *--------------------
48  */
49
50
51 /*
52  * Create a new entry in the PlannerParamVar list, and return its index.
53  *
54  * var contains the data to be copied, except for varlevelsup which
55  * is set from the absolute level value given by varlevel.
56  */
57 static int
58 new_param(Var *var, Index varlevel)
59 {
60         Var                *paramVar = (Var *) copyObject(var);
61
62         paramVar->varlevelsup = varlevel;
63
64         PlannerParamVar = lappend(PlannerParamVar, paramVar);
65
66         return length(PlannerParamVar) - 1;
67 }
68
69 /*
70  * Generate a Param node to replace the given Var,
71  * which is expected to have varlevelsup > 0 (ie, it is not local).
72  */
73 static Param *
74 replace_var(Var *var)
75 {
76         List       *ppv;
77         Param      *retval;
78         Index           varlevel;
79         int                     i;
80
81         Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel);
82         varlevel = PlannerQueryLevel - var->varlevelsup;
83
84         /*
85          * If there's already a PlannerParamVar entry for this same Var, just
86          * use it.      NOTE: in sufficiently complex querytrees, it is possible
87          * for the same varno/varlevel to refer to different RTEs in different
88          * parts of the parsetree, so that different fields might end up
89          * sharing the same Param number.  As long as we check the vartype as
90          * well, I believe that this sort of aliasing will cause no trouble.
91          * The correct field should get stored into the Param slot at
92          * execution in each part of the tree.
93          */
94         i = 0;
95         foreach(ppv, PlannerParamVar)
96         {
97                 Var                *pvar = lfirst(ppv);
98
99                 if (pvar->varno == var->varno &&
100                         pvar->varattno == var->varattno &&
101                         pvar->varlevelsup == varlevel &&
102                         pvar->vartype == var->vartype)
103                         break;
104                 i++;
105         }
106
107         if (!ppv)
108         {
109                 /* Nope, so make a new one */
110                 i = new_param(var, varlevel);
111         }
112
113         retval = makeNode(Param);
114         retval->paramkind = PARAM_EXEC;
115         retval->paramid = (AttrNumber) i;
116         retval->paramtype = var->vartype;
117
118         return retval;
119 }
120
121 /*
122  * Convert a bare SubLink (as created by the parser) into a SubPlan.
123  */
124 static Node *
125 make_subplan(SubLink *slink)
126 {
127         SubPlan    *node = makeNode(SubPlan);
128         Query      *subquery = (Query *) (slink->subselect);
129         Oid                     result_type = exprType((Node *) slink);
130         double          tuple_fraction;
131         Plan       *plan;
132         List       *lst;
133         Node       *result;
134
135         /*
136          * Check to see if this node was already processed; if so we have
137          * trouble.  We check to see if the linked-to Query appears to have
138          * been planned already, too.
139          */
140         if (subquery == NULL)
141                 elog(ERROR, "make_subplan: invalid expression structure (SubLink already processed?)");
142         if (subquery->base_rel_list != NIL)
143                 elog(ERROR, "make_subplan: invalid expression structure (subquery already processed?)");
144
145         /*
146          * Copy the source Query node.  This is a quick and dirty kluge to
147          * resolve the fact that the parser can generate trees with multiple
148          * links to the same sub-Query node, but the planner wants to scribble
149          * on the Query. Try to clean this up when we do querytree redesign...
150          */
151         subquery = (Query *) copyObject(subquery);
152
153         /*
154          * For an EXISTS subplan, tell lower-level planner to expect that only
155          * the first tuple will be retrieved.  For ALL and ANY subplans, we
156          * will be able to stop evaluating if the test condition fails, so
157          * very often not all the tuples will be retrieved; for lack of a
158          * better idea, specify 50% retrieval.  For EXPR and MULTIEXPR
159          * subplans, use default behavior (we're only expecting one row out,
160          * anyway).
161          *
162          * NOTE: if you change these numbers, also change cost_qual_eval_walker()
163          * in path/costsize.c.
164          *
165          * XXX If an ALL/ANY subplan is uncorrelated, we may decide to
166          * materialize its result below.  In that case it would've been better
167          * to specify full retrieval.  At present, however, we can only detect
168          * correlation or lack of it after we've made the subplan :-(. Perhaps
169          * detection of correlation should be done as a separate step.
170          * Meanwhile, we don't want to be too optimistic about the percentage
171          * of tuples retrieved, for fear of selecting a plan that's bad for
172          * the materialization case.
173          */
174         if (slink->subLinkType == EXISTS_SUBLINK)
175                 tuple_fraction = 1.0;   /* just like a LIMIT 1 */
176         else if (slink->subLinkType == ALL_SUBLINK ||
177                          slink->subLinkType == ANY_SUBLINK)
178                 tuple_fraction = 0.5;   /* 50% */
179         else
180                 tuple_fraction = -1.0;  /* default behavior */
181
182         /*
183          * Generate the plan for the subquery.
184          */
185         node->plan = plan = subquery_planner(subquery, tuple_fraction);
186
187         node->plan_id = PlannerPlanId++;        /* Assign unique ID to this
188                                                                                  * SubPlan */
189
190         node->rtable = subquery->rtable;
191         node->sublink = slink;
192
193         slink->subselect = NULL;        /* cool ?! see error check above! */
194
195         /*
196          * Make parParam list of params that current query level will pass to
197          * this child plan.
198          */
199         foreach(lst, plan->extParam)
200         {
201                 int                     paramid = lfirsti(lst);
202                 Var                *var = nth(paramid, PlannerParamVar);
203
204                 /* note varlevelsup is absolute level number */
205                 if (var->varlevelsup == PlannerQueryLevel)
206                         node->parParam = lappendi(node->parParam, paramid);
207         }
208
209         /*
210          * Un-correlated or undirect correlated plans of EXISTS, EXPR, or
211          * MULTIEXPR types can be used as initPlans.  For EXISTS or EXPR, we
212          * just produce a Param referring to the result of evaluating the
213          * initPlan.  For MULTIEXPR, we must build an AND or OR-clause of the
214          * individual comparison operators, using the appropriate lefthand
215          * side expressions and Params for the initPlan's target items.
216          */
217         if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
218         {
219                 Var                *var = makeVar(0, 0, BOOLOID, -1, 0);
220                 Param      *prm = makeNode(Param);
221
222                 prm->paramkind = PARAM_EXEC;
223                 prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
224                 prm->paramtype = var->vartype;
225                 pfree(var);                             /* var is only needed for new_param */
226                 node->setParam = lappendi(node->setParam, prm->paramid);
227                 PlannerInitPlan = lappend(PlannerInitPlan, node);
228                 result = (Node *) prm;
229         }
230         else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
231         {
232                 TargetEntry *te = lfirst(plan->targetlist);
233
234                 /* need a var node just to pass to new_param()... */
235                 Var                *var = makeVar(0, 0, te->resdom->restype,
236                                                                   te->resdom->restypmod, 0);
237                 Param      *prm = makeNode(Param);
238
239                 prm->paramkind = PARAM_EXEC;
240                 prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
241                 prm->paramtype = var->vartype;
242                 pfree(var);                             /* var is only needed for new_param */
243                 node->setParam = lappendi(node->setParam, prm->paramid);
244                 PlannerInitPlan = lappend(PlannerInitPlan, node);
245                 result = (Node *) prm;
246         }
247         else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
248         {
249                 List       *newoper = NIL;
250                 int                     i = 0;
251
252                 /*
253                  * Convert oper list of Opers into a list of Exprs, using lefthand
254                  * arguments and Params representing inside results.
255                  */
256                 foreach(lst, slink->oper)
257                 {
258                         Oper       *oper = (Oper *) lfirst(lst);
259                         Node       *lefthand = nth(i, slink->lefthand);
260                         TargetEntry *te = nth(i, plan->targetlist);
261
262                         /* need a var node just to pass to new_param()... */
263                         Var                *var = makeVar(0, 0, te->resdom->restype,
264                                                                           te->resdom->restypmod, 0);
265                         Param      *prm = makeNode(Param);
266                         Operator        tup;
267                         Form_pg_operator opform;
268                         Node       *left,
269                                            *right;
270
271                         prm->paramkind = PARAM_EXEC;
272                         prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
273                         prm->paramtype = var->vartype;
274                         pfree(var);                     /* var is only needed for new_param */
275
276                         Assert(IsA(oper, Oper));
277                         tup = SearchSysCache(OPEROID,
278                                                                  ObjectIdGetDatum(oper->opno),
279                                                                  0, 0, 0);
280                         if (!HeapTupleIsValid(tup))
281                                 elog(ERROR, "cache lookup failed for operator %u", oper->opno);
282                         opform = (Form_pg_operator) GETSTRUCT(tup);
283
284                         /*
285                          * Note: we use make_operand in case runtime type conversion
286                          * function calls must be inserted for this operator!
287                          */
288                         left = make_operand(lefthand,
289                                                                 exprType(lefthand), opform->oprleft);
290                         right = make_operand((Node *) prm,
291                                                                  prm->paramtype, opform->oprright);
292                         ReleaseSysCache(tup);
293
294                         newoper = lappend(newoper,
295                                                           make_opclause(oper,
296                                                                                         (Var *) left,
297                                                                                         (Var *) right));
298                         node->setParam = lappendi(node->setParam, prm->paramid);
299                         i++;
300                 }
301                 slink->oper = newoper;
302                 slink->lefthand = NIL;
303                 PlannerInitPlan = lappend(PlannerInitPlan, node);
304                 if (i > 1)
305                         result = (Node *) ((slink->useor) ? make_orclause(newoper) :
306                                                            make_andclause(newoper));
307                 else
308                         result = (Node *) lfirst(newoper);
309         }
310         else
311         {
312                 Expr       *expr = makeNode(Expr);
313                 List       *args = NIL;
314                 List       *newoper = NIL;
315                 int                     i = 0;
316
317                 /*
318                  * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types
319                  * to initPlans, even when they are uncorrelated or undirect
320                  * correlated, because we need to scan the output of the subplan
321                  * for each outer tuple.  However, we have the option to tack a
322                  * MATERIAL node onto the top of an uncorrelated/undirect
323                  * correlated subplan, which lets us do the work of evaluating the
324                  * subplan only once.  We do this if the subplan's top plan node
325                  * is anything more complicated than a plain sequential scan, and
326                  * we do it even for seqscan if the qual appears selective enough
327                  * to eliminate many tuples.
328                  *
329                  * XXX It's pretty ugly to be inserting a MATERIAL node at this
330                  * point.  Since subquery_planner has already run SS_finalize_plan
331                  * on the subplan tree, we have to kluge up parameter lists for
332                  * the MATERIAL node.  Possibly this could be fixed by postponing
333                  * SS_finalize_plan processing until setrefs.c is run.
334                  */
335                 if (node->parParam == NIL)
336                 {
337                         bool            use_material;
338
339                         switch (nodeTag(plan))
340                         {
341                                 case T_SeqScan:
342                                         if (plan->initPlan || plan->subPlan)
343                                                 use_material = true;
344                                         else
345                                         {
346                                                 Selectivity qualsel;
347
348                                                 qualsel = clauselist_selectivity(subquery,
349                                                                                                                  plan->qual,
350                                                                                                                  0);
351                                                 /* Is 10% selectivity a good threshold?? */
352                                                 use_material = qualsel < 0.10;
353                                         }
354                                         break;
355                                 case T_Material:
356                                 case T_FunctionScan:
357                                 case T_Sort:
358
359                                         /*
360                                          * Don't add another Material node if there's one
361                                          * already, nor if the top node is any other type that
362                                          * materializes its output anyway.
363                                          */
364                                         use_material = false;
365                                         break;
366                                 default:
367                                         use_material = true;
368                                         break;
369                         }
370                         if (use_material)
371                         {
372                                 Plan   *matplan;
373
374                                 matplan = (Plan *) make_material(plan->targetlist, plan);
375                                 /* kluge --- see comments above */
376                                 matplan->extParam = listCopy(plan->extParam);
377                                 matplan->locParam = listCopy(plan->locParam);
378                                 node->plan = plan = matplan;
379                         }
380                 }
381
382                 /*
383                  * Make expression of SUBPLAN type
384                  */
385                 expr->typeOid = result_type;
386                 expr->opType = SUBPLAN_EXPR;
387                 expr->oper = (Node *) node;
388
389                 /*
390                  * Make expr->args from parParam.
391                  */
392                 foreach(lst, node->parParam)
393                 {
394                         Var                *var = nth(lfirsti(lst), PlannerParamVar);
395
396                         var = (Var *) copyObject(var);
397
398                         /*
399                          * Must fix absolute-level varlevelsup from the
400                          * PlannerParamVar entry.  But since var is at current subplan
401                          * level, this is easy:
402                          */
403                         var->varlevelsup = 0;
404                         args = lappend(args, var);
405                 }
406                 expr->args = args;
407
408                 /*
409                  * Convert oper list of Opers into a list of Exprs, using lefthand
410                  * arguments and Consts representing inside results.
411                  */
412                 foreach(lst, slink->oper)
413                 {
414                         Oper       *oper = (Oper *) lfirst(lst);
415                         Node       *lefthand = nth(i, slink->lefthand);
416                         TargetEntry *te = nth(i, plan->targetlist);
417                         Const      *con;
418                         Operator        tup;
419                         Form_pg_operator opform;
420                         Node       *left,
421                                            *right;
422
423                         con = makeNullConst(te->resdom->restype);
424
425                         Assert(IsA(oper, Oper));
426                         tup = SearchSysCache(OPEROID,
427                                                                  ObjectIdGetDatum(oper->opno),
428                                                                  0, 0, 0);
429                         if (!HeapTupleIsValid(tup))
430                                 elog(ERROR, "cache lookup failed for operator %u", oper->opno);
431                         opform = (Form_pg_operator) GETSTRUCT(tup);
432
433                         /*
434                          * Note: we use make_operand in case runtime type conversion
435                          * function calls must be inserted for this operator!
436                          */
437                         left = make_operand(lefthand,
438                                                                 exprType(lefthand), opform->oprleft);
439                         right = make_operand((Node *) con,
440                                                                  con->consttype, opform->oprright);
441                         ReleaseSysCache(tup);
442
443                         newoper = lappend(newoper,
444                                                           make_opclause(oper,
445                                                                                         (Var *) left,
446                                                                                         (Var *) right));
447                         i++;
448                 }
449                 slink->oper = newoper;
450                 slink->lefthand = NIL;
451                 result = (Node *) expr;
452         }
453
454         return result;
455 }
456
457 /*
458  * finalize_primnode: build lists of subplans and params appearing
459  * in the given expression tree.  NOTE: items are added to lists passed in,
460  * so caller must initialize lists to NIL before first call!
461  *
462  * Note: the subplan list that is constructed here and assigned to the
463  * plan's subPlan field will be replaced with an up-to-date list in
464  * set_plan_references().  We could almost dispense with building this
465  * subplan list at all; I believe the only place that uses it is the
466  * check in make_subplan to see whether a subselect has any subselects.
467  */
468
469 typedef struct finalize_primnode_results
470 {
471         List       *subplans;           /* List of subplans found in expr */
472         List       *paramids;           /* List of PARAM_EXEC paramids found */
473 } finalize_primnode_results;
474
475 static bool
476 finalize_primnode(Node *node, finalize_primnode_results *results)
477 {
478         if (node == NULL)
479                 return false;
480         if (IsA(node, Param))
481         {
482                 if (((Param *) node)->paramkind == PARAM_EXEC)
483                 {
484                         int                     paramid = (int) ((Param *) node)->paramid;
485
486                         if (!intMember(paramid, results->paramids))
487                                 results->paramids = lconsi(paramid, results->paramids);
488                 }
489                 return false;                   /* no more to do here */
490         }
491         if (is_subplan(node))
492         {
493                 SubPlan    *subplan = (SubPlan *) ((Expr *) node)->oper;
494                 List       *lst;
495
496                 /* Add subplan to subplans list */
497                 results->subplans = lappend(results->subplans, subplan);
498                 /* Check extParam list for params to add to paramids */
499                 foreach(lst, subplan->plan->extParam)
500                 {
501                         int                     paramid = lfirsti(lst);
502                         Var                *var = nth(paramid, PlannerParamVar);
503
504                         /* note varlevelsup is absolute level number */
505                         if (var->varlevelsup < PlannerQueryLevel &&
506                                 !intMember(paramid, results->paramids))
507                                 results->paramids = lconsi(paramid, results->paramids);
508                 }
509                 /* fall through to recurse into subplan args */
510         }
511         return expression_tree_walker(node, finalize_primnode,
512                                                                   (void *) results);
513 }
514
515 /*
516  * Replace correlation vars (uplevel vars) with Params.
517  */
518
519 static Node *replace_correlation_vars_mutator(Node *node, void *context);
520
521 Node *
522 SS_replace_correlation_vars(Node *expr)
523 {
524         /* No setup needed for tree walk, so away we go */
525         return replace_correlation_vars_mutator(expr, NULL);
526 }
527
528 static Node *
529 replace_correlation_vars_mutator(Node *node, void *context)
530 {
531         if (node == NULL)
532                 return NULL;
533         if (IsA(node, Var))
534         {
535                 if (((Var *) node)->varlevelsup > 0)
536                         return (Node *) replace_var((Var *) node);
537         }
538         return expression_tree_mutator(node,
539                                                                    replace_correlation_vars_mutator,
540                                                                    context);
541 }
542
543 /*
544  * Expand SubLinks to SubPlans in the given expression.
545  */
546
547 static Node *process_sublinks_mutator(Node *node, void *context);
548
549 Node *
550 SS_process_sublinks(Node *expr)
551 {
552         /* No setup needed for tree walk, so away we go */
553         return process_sublinks_mutator(expr, NULL);
554 }
555
556 static Node *
557 process_sublinks_mutator(Node *node, void *context)
558 {
559         if (node == NULL)
560                 return NULL;
561         if (IsA(node, SubLink))
562         {
563                 SubLink    *sublink = (SubLink *) node;
564
565                 /*
566                  * First, scan the lefthand-side expressions, if any. This is a
567                  * tad klugy since we modify the input SubLink node, but that
568                  * should be OK (make_subplan does it too!)
569                  */
570                 sublink->lefthand = (List *)
571                         process_sublinks_mutator((Node *) sublink->lefthand, context);
572                 /* Now build the SubPlan node and make the expr to return */
573                 return make_subplan(sublink);
574         }
575
576         /*
577          * Note that we will never see a SubPlan expression in the input
578          * (since this is the very routine that creates 'em to begin with). So
579          * the code in expression_tree_mutator() that might do inappropriate
580          * things with SubPlans or SubLinks will not be exercised.
581          */
582         Assert(!is_subplan(node));
583
584         return expression_tree_mutator(node,
585                                                                    process_sublinks_mutator,
586                                                                    context);
587 }
588
589 List *
590 SS_finalize_plan(Plan *plan, List *rtable)
591 {
592         List       *extParam = NIL;
593         List       *locParam = NIL;
594         finalize_primnode_results results;
595         List       *lst;
596
597         if (plan == NULL)
598                 return NIL;
599
600         results.subplans = NIL;         /* initialize lists to NIL */
601         results.paramids = NIL;
602
603         /*
604          * When we call finalize_primnode, results.paramids lists are
605          * automatically merged together.  But when recursing to self, we have
606          * to do it the hard way.  We want the paramids list to include params
607          * in subplans as well as at this level. (We don't care about finding
608          * subplans of subplans, though.)
609          */
610
611         /* Find params and subplans in targetlist and qual */
612         finalize_primnode((Node *) plan->targetlist, &results);
613         finalize_primnode((Node *) plan->qual, &results);
614
615         /* Check additional node-type-specific fields */
616         switch (nodeTag(plan))
617         {
618                 case T_Result:
619                         finalize_primnode(((Result *) plan)->resconstantqual,
620                                                           &results);
621                         break;
622
623                 case T_IndexScan:
624                         finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
625                                                           &results);
626
627                         /*
628                          * we need not look at indxqualorig, since it will have the
629                          * same param references as indxqual, and we aren't really
630                          * concerned yet about having a complete subplan list.
631                          */
632                         break;
633
634                 case T_TidScan:
635                         finalize_primnode((Node *) ((TidScan *) plan)->tideval,
636                                                           &results);
637                         break;
638
639                 case T_SubqueryScan:
640
641                         /*
642                          * In a SubqueryScan, SS_finalize_plan has already been run on
643                          * the subplan by the inner invocation of subquery_planner, so
644                          * there's no need to do it again.  Instead, just pull out the
645                          * subplan's extParams list, which represents the params it
646                          * needs from my level and higher levels.
647                          */
648                         results.paramids = set_unioni(results.paramids,
649                                                          ((SubqueryScan *) plan)->subplan->extParam);
650                         break;
651
652                 case T_FunctionScan:
653                         {
654                                 RangeTblEntry *rte;
655
656                                 rte = rt_fetch(((FunctionScan *) plan)->scan.scanrelid,
657                                                            rtable);
658                                 Assert(rte->rtekind == RTE_FUNCTION);
659                                 finalize_primnode(rte->funcexpr, &results);
660                         }
661                         break;
662
663                 case T_Append:
664                         foreach(lst, ((Append *) plan)->appendplans)
665                                 results.paramids = set_unioni(results.paramids,
666                                                                  SS_finalize_plan((Plan *) lfirst(lst),
667                                                                                                   rtable));
668                         break;
669
670                 case T_NestLoop:
671                         finalize_primnode((Node *) ((Join *) plan)->joinqual,
672                                                           &results);
673                         break;
674
675                 case T_MergeJoin:
676                         finalize_primnode((Node *) ((Join *) plan)->joinqual,
677                                                           &results);
678                         finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
679                                                           &results);
680                         break;
681
682                 case T_HashJoin:
683                         finalize_primnode((Node *) ((Join *) plan)->joinqual,
684                                                           &results);
685                         finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
686                                                           &results);
687                         break;
688
689                 case T_Hash:
690                         finalize_primnode(((Hash *) plan)->hashkey,
691                                                           &results);
692                         break;
693
694                 case T_Agg:
695                 case T_SeqScan:
696                 case T_Material:
697                 case T_Sort:
698                 case T_Unique:
699                 case T_SetOp:
700                 case T_Limit:
701                 case T_Group:
702                         break;
703
704                 default:
705                         elog(ERROR, "SS_finalize_plan: node %d unsupported",
706                                  nodeTag(plan));
707         }
708
709         /* Process left and right subplans, if any */
710         results.paramids = set_unioni(results.paramids,
711                                                                   SS_finalize_plan(plan->lefttree,
712                                                                                                    rtable));
713         results.paramids = set_unioni(results.paramids,
714                                                                   SS_finalize_plan(plan->righttree,
715                                                                                                    rtable));
716
717         /* Now we have all the paramids and subplans */
718
719         foreach(lst, results.paramids)
720         {
721                 int                     paramid = lfirsti(lst);
722                 Var                *var = nth(paramid, PlannerParamVar);
723
724                 /* note varlevelsup is absolute level number */
725                 if (var->varlevelsup < PlannerQueryLevel)
726                         extParam = lappendi(extParam, paramid);
727                 else if (var->varlevelsup > PlannerQueryLevel)
728                         elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
729                 else
730                 {
731                         Assert(var->varno == 0 && var->varattno == 0);
732                         locParam = lappendi(locParam, paramid);
733                 }
734         }
735
736         plan->extParam = extParam;
737         plan->locParam = locParam;
738         plan->subPlan = results.subplans;
739
740         return results.paramids;
741 }