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