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