OSDN Git Service

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