subselect.c 20.1 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * subselect.c
4 5
 *	  Planning routines for subselects and parameters.
 *
B
Add:  
Bruce Momjian 已提交
6 7
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * 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.39 2000/07/12 02:37:09 tgl Exp $
11 12 13 14 15
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

16
#include "catalog/pg_operator.h"
17 18 19
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
20 21
#include "optimizer/cost.h"
#include "optimizer/planmain.h"
B
Bruce Momjian 已提交
22 23
#include "optimizer/planner.h"
#include "optimizer/subselect.h"
24 25 26 27
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "utils/lsyscache.h"

28

29
Index		PlannerQueryLevel;	/* level of current query */
30
List	   *PlannerInitPlan;	/* init subplans for current query */
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
List	   *PlannerParamVar;	/* to get Var from Param->paramid */
int			PlannerPlanId;		/* to assign unique ID to subquery plans */

/*--------------------
 * PlannerParamVar is a list of Var nodes, wherein the n'th entry
 * (n counts from 0) corresponds to Param->paramid = n.  The Var nodes
 * are ordinary except for one thing: their varlevelsup field does NOT
 * have the usual interpretation of "subplan levels out from current".
 * Instead, it contains the absolute plan level, with the outermost
 * plan being level 1 and nested plans having higher level numbers.
 * This nonstandardness is useful because we don't have to run around
 * and update the list elements when we enter or exit a subplan
 * recursion level.  But we must pay attention not to confuse this
 * meaning with the normal meaning of varlevelsup.
 *--------------------
 */
47 48


49 50 51 52 53 54
/*
 * Create a new entry in the PlannerParamVar list, and return its index.
 *
 * var contains the data to be copied, except for varlevelsup which
 * is set from the absolute level value given by varlevel.
 */
55
static int
56
new_param(Var *var, Index varlevel)
57
{
58
	Var		   *paramVar = (Var *) copyObject(var);
59

60
	paramVar->varlevelsup = varlevel;
61

62
	PlannerParamVar = lappend(PlannerParamVar, paramVar);
63

64
	return length(PlannerParamVar) - 1;
65 66
}

67 68 69 70
/*
 * Generate a Param node to replace the given Var,
 * which is expected to have varlevelsup > 0 (ie, it is not local).
 */
71
static Param *
72
replace_var(Var *var)
73
{
74
	List	   *ppv;
75
	Param	   *retval;
76
	Index		varlevel;
77 78
	int			i;

79 80
	Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel);
	varlevel = PlannerQueryLevel - var->varlevelsup;
81

82
	/*
83 84 85 86 87 88 89 90
	 * If there's already a PlannerParamVar entry for this same Var, just
	 * use it.	NOTE: in situations involving UNION or inheritance, it is
	 * possible for the same varno/varlevel to refer to different RTEs in
	 * different parts of the parsetree, so that different fields might
	 * end up sharing the same Param number.  As long as we check the
	 * vartype as well, I believe that this sort of aliasing will cause no
	 * trouble. The correct field should get stored into the Param slot at
	 * execution in each part of the tree.
91 92 93
	 */
	i = 0;
	foreach(ppv, PlannerParamVar)
94
	{
95
		Var		   *pvar = lfirst(ppv);
96 97 98 99 100

		if (pvar->varno == var->varno &&
			pvar->varattno == var->varattno &&
			pvar->varlevelsup == varlevel &&
			pvar->vartype == var->vartype)
101
			break;
102
		i++;
103
	}
104

105
	if (!ppv)
106 107
	{
		/* Nope, so make a new one */
108
		i = new_param(var, varlevel);
109
	}
110

111 112 113 114
	retval = makeNode(Param);
	retval->paramkind = PARAM_EXEC;
	retval->paramid = (AttrNumber) i;
	retval->paramtype = var->vartype;
115

116
	return retval;
117 118
}

119 120 121
/*
 * Convert a bare SubLink (as created by the parser) into a SubPlan.
 */
122
static Node *
123
make_subplan(SubLink *slink)
124
{
125
	SubPlan    *node = makeNode(SubPlan);
126
	Query	   *subquery = (Query *) (slink->subselect);
127
	double		tuple_fraction;
128 129 130 131
	Plan	   *plan;
	List	   *lst;
	Node	   *result;
	List	   *saved_ip = PlannerInitPlan;
132

133
	PlannerInitPlan = NULL;
134

135
	PlannerQueryLevel++;		/* we become child */
136

137 138 139 140 141 142 143
	/* Check to see if this node was already processed; if so we have
	 * trouble.  Someday should change tree representation so that we can
	 * cope with multiple links to the same subquery, but for now...
	 */
	if (subquery == NULL)
		elog(ERROR, "make_subplan: invalid expression structure (subquery already processed?)");

144
	/*
145 146 147 148 149 150 151
	 * For an EXISTS subplan, tell lower-level planner to expect that only
	 * the first tuple will be retrieved.  For ALL and ANY subplans, we
	 * will be able to stop evaluating if the test condition fails, so
	 * very often not all the tuples will be retrieved; for lack of a
	 * better idea, specify 50% retrieval.	For EXPR and MULTIEXPR
	 * subplans, use default behavior (we're only expecting one row out,
	 * anyway).
152
	 *
153 154 155
	 * NOTE: if you change these numbers, also change cost_qual_eval_walker()
	 * in path/costsize.c.
	 *
156 157 158 159 160 161 162 163
	 * XXX If an ALL/ANY subplan is uncorrelated, we may decide to
	 * materialize its result below.  In that case it would've been better
	 * to specify full retrieval.  At present, however, we can only detect
	 * correlation or lack of it after we've made the subplan :-(. Perhaps
	 * detection of correlation should be done as a separate step.
	 * Meanwhile, we don't want to be too optimistic about the percentage
	 * of tuples retrieved, for fear of selecting a plan that's bad for
	 * the materialization case.
164 165 166
	 */
	if (slink->subLinkType == EXISTS_SUBLINK)
		tuple_fraction = 1.0;	/* just like a LIMIT 1 */
167 168
	else if (slink->subLinkType == ALL_SUBLINK ||
			 slink->subLinkType == ANY_SUBLINK)
169
		tuple_fraction = 0.5;	/* 50% */
170 171
	else
		tuple_fraction = -1.0;	/* default behavior */
172

173
	node->plan = plan = subquery_planner(subquery, tuple_fraction);
174 175 176

	/*
	 * Assign subPlan, extParam and locParam to plan nodes. At the moment,
177 178
	 * SS_finalize_plan doesn't handle initPlan-s and so we assign them to
	 * the topmost plan node and take care about its extParam too.
179
	 */
180
	(void) SS_finalize_plan(plan);
181
	plan->initPlan = PlannerInitPlan;
182

183
	/* Create extParam list as union of InitPlan-s' lists */
184
	foreach(lst, PlannerInitPlan)
185
	{
186 187 188
		List	   *lp;

		foreach(lp, ((SubPlan *) lfirst(lst))->plan->extParam)
189
		{
190 191
			if (!intMember(lfirsti(lp), plan->extParam))
				plan->extParam = lappendi(plan->extParam, lfirsti(lp));
192 193
		}
	}
194

195 196 197
	/* and now we are parent again */
	PlannerInitPlan = saved_ip;
	PlannerQueryLevel--;
198

199
	node->plan_id = PlannerPlanId++;
200
	node->rtable = subquery->rtable;
201
	node->sublink = slink;
202
	slink->subselect = NULL;	/* cool ?! see error check above! */
203

204
	/* make parParam list of params coming from current query level */
205
	foreach(lst, plan->extParam)
206
	{
207 208
		Var		   *var = nth(lfirsti(lst), PlannerParamVar);

209
		/* note varlevelsup is absolute level number */
210 211
		if (var->varlevelsup == PlannerQueryLevel)
			node->parParam = lappendi(node->parParam, lfirsti(lst));
212
	}
213 214

	/*
215
	 * Un-correlated or undirect correlated plans of EXISTS, EXPR, or
216 217
	 * MULTIEXPR types can be used as initPlans.  For EXISTS or EXPR, we
	 * just produce a Param referring to the result of evaluating the
218 219 220
	 * initPlan.  For MULTIEXPR, we must build an AND or OR-clause of the
	 * individual comparison operators, using the appropriate lefthand
	 * side expressions and Params for the initPlan's target items.
221
	 */
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
	{
		Var		   *var = makeVar(0, 0, BOOLOID, -1, 0);
		Param	   *prm = makeNode(Param);

		prm->paramkind = PARAM_EXEC;
		prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
		prm->paramtype = var->vartype;
		pfree(var);				/* var is only needed for new_param */
		node->setParam = lappendi(node->setParam, prm->paramid);
		PlannerInitPlan = lappend(PlannerInitPlan, node);
		result = (Node *) prm;
	}
	else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
	{
		TargetEntry *te = lfirst(plan->targetlist);
238

239 240 241 242 243 244 245 246 247 248 249 250 251 252
		/* need a var node just to pass to new_param()... */
		Var		   *var = makeVar(0, 0, te->resdom->restype,
								  te->resdom->restypmod, 0);
		Param	   *prm = makeNode(Param);

		prm->paramkind = PARAM_EXEC;
		prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
		prm->paramtype = var->vartype;
		pfree(var);				/* var is only needed for new_param */
		node->setParam = lappendi(node->setParam, prm->paramid);
		PlannerInitPlan = lappend(PlannerInitPlan, node);
		result = (Node *) prm;
	}
	else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
253
	{
254
		List	   *newoper = NIL;
255 256
		int			i = 0;

257
		/*
258 259
		 * Convert oper list of Opers into a list of Exprs, using lefthand
		 * arguments and Params representing inside results.
260
		 */
261
		foreach(lst, slink->oper)
262
		{
263 264
			Oper	   *oper = (Oper *) lfirst(lst);
			Node	   *lefthand = nth(i, slink->lefthand);
265
			TargetEntry *te = nth(i, plan->targetlist);
266

267
			/* need a var node just to pass to new_param()... */
268
			Var		   *var = makeVar(0, 0, te->resdom->restype,
269
									  te->resdom->restypmod, 0);
270
			Param	   *prm = makeNode(Param);
271 272 273 274
			Operator	tup;
			Form_pg_operator opform;
			Node	   *left,
					   *right;
275

276
			prm->paramkind = PARAM_EXEC;
277
			prm->paramid = (AttrNumber) new_param(var, PlannerQueryLevel);
278
			prm->paramtype = var->vartype;
279
			pfree(var);			/* var is only needed for new_param */
280 281 282 283 284

			Assert(IsA(oper, Oper));
			tup = get_operator_tuple(oper->opno);
			Assert(HeapTupleIsValid(tup));
			opform = (Form_pg_operator) GETSTRUCT(tup);
285 286 287

			/*
			 * Note: we use make_operand in case runtime type conversion
288 289 290 291 292 293 294 295 296 297
			 * function calls must be inserted for this operator!
			 */
			left = make_operand("", lefthand,
								exprType(lefthand), opform->oprleft);
			right = make_operand("", (Node *) prm,
								 prm->paramtype, opform->oprright);
			newoper = lappend(newoper,
							  make_opclause(oper,
											(Var *) left,
											(Var *) right));
298
			node->setParam = lappendi(node->setParam, prm->paramid);
299 300
			i++;
		}
301 302
		slink->oper = newoper;
		slink->lefthand = NIL;
303 304
		PlannerInitPlan = lappend(PlannerInitPlan, node);
		if (i > 1)
305 306
			result = (Node *) ((slink->useor) ? make_orclause(newoper) :
							   make_andclause(newoper));
307
		else
308
			result = (Node *) lfirst(newoper);
309
	}
310
	else
311
	{
312
		Expr	   *expr = makeNode(Expr);
313
		List	   *args = NIL;
314
		List	   *newoper = NIL;
315 316
		int			i = 0;

317
		/*
318 319 320 321 322 323 324 325 326 327
		 * We can't convert subplans of ALL_SUBLINK or ANY_SUBLINK types
		 * to initPlans, even when they are uncorrelated or undirect
		 * correlated, because we need to scan the output of the subplan
		 * for each outer tuple.  However, we have the option to tack a
		 * MATERIAL node onto the top of an uncorrelated/undirect
		 * correlated subplan, which lets us do the work of evaluating the
		 * subplan only once.  We do this if the subplan's top plan node
		 * is anything more complicated than a plain sequential scan, and
		 * we do it even for seqscan if the qual appears selective enough
		 * to eliminate many tuples.
328 329 330 331 332 333 334 335
		 */
		if (node->parParam == NIL)
		{
			bool		use_material;

			switch (nodeTag(plan))
			{
				case T_SeqScan:
336 337 338 339 340 341 342 343 344 345 346 347
					if (plan->initPlan || plan->subPlan)
						use_material = true;
					else
					{
						Selectivity qualsel;

						qualsel = clauselist_selectivity(subquery,
														 plan->qual,
														 0);
						/* Is 10% selectivity a good threshold?? */
						use_material = qualsel < 0.10;
					}
348 349 350
					break;
				case T_Material:
				case T_Sort:
351 352 353 354 355 356

					/*
					 * Don't add another Material node if there's one
					 * already, nor if the top node is a Sort, since Sort
					 * materializes its output anyway.	(I doubt either
					 * case can happen in practice for a subplan, but...)
357 358 359 360 361 362 363 364 365
					 */
					use_material = false;
					break;
				default:
					use_material = true;
					break;
			}
			if (use_material)
			{
366
				plan = (Plan *) make_material(plan->targetlist, plan);
367 368 369 370 371 372 373
				node->plan = plan;
			}
		}

		/*
		 * Make expression of SUBPLAN type
		 */
374
		expr->typeOid = BOOLOID;/* bogus, but we don't really care */
375
		expr->opType = SUBPLAN_EXPR;
376 377 378
		expr->oper = (Node *) node;

		/*
379
		 * Make expr->args from parParam.
380
		 */
381
		foreach(lst, node->parParam)
382
		{
383 384 385
			Var		   *var = nth(lfirsti(lst), PlannerParamVar);

			var = (Var *) copyObject(var);
386 387 388 389 390

			/*
			 * Must fix absolute-level varlevelsup from the
			 * PlannerParamVar entry.  But since var is at current subplan
			 * level, this is easy:
391
			 */
392
			var->varlevelsup = 0;
393
			args = lappend(args, var);
394
		}
395
		expr->args = args;
396

397
		/*
398 399
		 * Convert oper list of Opers into a list of Exprs, using lefthand
		 * arguments and Consts representing inside results.
400
		 */
401
		foreach(lst, slink->oper)
402
		{
403 404
			Oper	   *oper = (Oper *) lfirst(lst);
			Node	   *lefthand = nth(i, slink->lefthand);
405
			TargetEntry *te = nth(i, plan->targetlist);
406 407 408 409 410 411 412
			Const	   *con;
			Operator	tup;
			Form_pg_operator opform;
			Node	   *left,
					   *right;

			/*
413 414
			 * XXX really ought to fill in constlen and constbyval
			 * correctly, but right now ExecEvalExpr won't look at them...
415 416 417 418 419 420 421
			 */
			con = makeConst(te->resdom->restype, 0, 0, true, 0, 0, 0);

			Assert(IsA(oper, Oper));
			tup = get_operator_tuple(oper->opno);
			Assert(HeapTupleIsValid(tup));
			opform = (Form_pg_operator) GETSTRUCT(tup);
422 423 424

			/*
			 * Note: we use make_operand in case runtime type conversion
425 426 427 428 429 430 431 432 433 434
			 * function calls must be inserted for this operator!
			 */
			left = make_operand("", lefthand,
								exprType(lefthand), opform->oprleft);
			right = make_operand("", (Node *) con,
								 con->consttype, opform->oprright);
			newoper = lappend(newoper,
							  make_opclause(oper,
											(Var *) left,
											(Var *) right));
435 436
			i++;
		}
437 438
		slink->oper = newoper;
		slink->lefthand = NIL;
439
		result = (Node *) expr;
440
	}
441

442
	return result;
443 444
}

445 446
/* this oughta be merged with LispUnioni */

447
static List *
448
set_unioni(List *l1, List *l2)
449 450
{
	if (l1 == NULL)
451
		return l2;
452
	if (l2 == NULL)
453
		return l1;
454

455
	return nconc(l1, set_differencei(l2, l1));
456 457
}

458 459
/*
 * finalize_primnode: build lists of subplans and params appearing
460 461
 * in the given expression tree.  NOTE: items are added to lists passed in,
 * so caller must initialize lists to NIL before first call!
462 463 464 465 466 467
 *
 * Note: the subplan list that is constructed here and assigned to the
 * plan's subPlan field will be replaced with an up-to-date list in
 * set_plan_references().  We could almost dispense with building this
 * subplan list at all; I believe the only place that uses it is the
 * check in make_subplan to see whether a subselect has any subselects.
468 469
 */

470 471 472 473
typedef struct finalize_primnode_results
{
	List	   *subplans;		/* List of subplans found in expr */
	List	   *paramids;		/* List of PARAM_EXEC paramids found */
474
} finalize_primnode_results;
475

476
static bool
477
finalize_primnode(Node *node, finalize_primnode_results *results)
478 479 480 481
{
	if (node == NULL)
		return false;
	if (IsA(node, Param))
482
	{
483 484
		if (((Param *) node)->paramkind == PARAM_EXEC)
		{
485
			int			paramid = (int) ((Param *) node)->paramid;
486

487
			if (!intMember(paramid, results->paramids))
488 489 490
				results->paramids = lconsi(paramid, results->paramids);
		}
		return false;			/* no more to do here */
491
	}
492
	if (is_subplan(node))
493
	{
494
		SubPlan    *subplan = (SubPlan *) ((Expr *) node)->oper;
495 496
		List	   *lst;

497 498 499 500
		/* Add subplan to subplans list */
		results->subplans = lappend(results->subplans, subplan);
		/* Check extParam list for params to add to paramids */
		foreach(lst, subplan->plan->extParam)
501
		{
502 503
			int			paramid = lfirsti(lst);
			Var		   *var = nth(paramid, PlannerParamVar);
504

505
			/* note varlevelsup is absolute level number */
506
			if (var->varlevelsup < PlannerQueryLevel &&
507
				!intMember(paramid, results->paramids))
508
				results->paramids = lconsi(paramid, results->paramids);
509
		}
510
		/* fall through to recurse into subplan args */
511
	}
512
	return expression_tree_walker(node, finalize_primnode,
513
								  (void *) results);
514 515
}

516 517
/*
 * Replace correlation vars (uplevel vars) with Params.
518
 */
519 520 521

static Node *replace_correlation_vars_mutator(Node *node, void *context);

522
Node *
523
SS_replace_correlation_vars(Node *expr)
524
{
525 526 527
	/* No setup needed for tree walk, so away we go */
	return replace_correlation_vars_mutator(expr, NULL);
}
528

529 530 531 532 533 534
static Node *
replace_correlation_vars_mutator(Node *node, void *context)
{
	if (node == NULL)
		return NULL;
	if (IsA(node, Var))
535
	{
536 537
		if (((Var *) node)->varlevelsup > 0)
			return (Node *) replace_var((Var *) node);
538
	}
539 540 541
	return expression_tree_mutator(node,
								   replace_correlation_vars_mutator,
								   context);
542 543
}

544 545
/*
 * Expand SubLinks to SubPlans in the given expression.
546
 */
547 548 549

static Node *process_sublinks_mutator(Node *node, void *context);

550 551
Node *
SS_process_sublinks(Node *expr)
552
{
553
	/* No setup needed for tree walk, so away we go */
554
	return process_sublinks_mutator(expr, NULL);
555 556 557 558 559 560
}

static Node *
process_sublinks_mutator(Node *node, void *context)
{
	if (node == NULL)
561
		return NULL;
562
	if (IsA(node, SubLink))
563
	{
564
		SubLink    *sublink = (SubLink *) node;
565

566 567 568 569
		/*
		 * First, scan the lefthand-side expressions, if any. This is a
		 * tad klugy since we modify the input SubLink node, but that
		 * should be OK (make_subplan does it too!)
570
		 */
571 572 573 574
		sublink->lefthand = (List *)
			process_sublinks_mutator((Node *) sublink->lefthand, context);
		/* Now build the SubPlan node and make the expr to return */
		return make_subplan(sublink);
575
	}
576

577 578
	/*
	 * Note that we will never see a SubPlan expression in the input
579 580 581
	 * (since this is the very routine that creates 'em to begin with). So
	 * the code in expression_tree_mutator() that might do inappropriate
	 * things with SubPlans or SubLinks will not be exercised.
582
	 */
583
	Assert(!is_subplan(node));
584

585 586 587
	return expression_tree_mutator(node,
								   process_sublinks_mutator,
								   context);
588 589
}

590 591
List *
SS_finalize_plan(Plan *plan)
592
{
593 594 595
	List	   *extParam = NIL;
	List	   *locParam = NIL;
	finalize_primnode_results results;
596 597 598
	List	   *lst;

	if (plan == NULL)
599
		return NIL;
600

601 602
	results.subplans = NIL;		/* initialize lists to NIL */
	results.paramids = NIL;
603

604 605
	/*
	 * When we call finalize_primnode, results.paramids lists are
606 607 608 609
	 * automatically merged together.  But when recursing to self, we have
	 * to do it the hard way.  We want the paramids list to include params
	 * in subplans as well as at this level. (We don't care about finding
	 * subplans of subplans, though.)
610 611 612
	 */

	/* Find params and subplans in targetlist and qual */
613
	finalize_primnode((Node *) plan->targetlist, &results);
614
	finalize_primnode((Node *) plan->qual, &results);
615

616
	/* Check additional node-type-specific fields */
617 618 619
	switch (nodeTag(plan))
	{
		case T_Result:
620 621
			finalize_primnode(((Result *) plan)->resconstantqual,
							  &results);
622 623 624
			break;

		case T_Append:
625
			foreach(lst, ((Append *) plan)->appendplans)
626
				results.paramids = set_unioni(results.paramids,
627
								 SS_finalize_plan((Plan *) lfirst(lst)));
628
			break;
629

630
		case T_IndexScan:
631 632
			finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
							  &results);
633 634 635

			/*
			 * we need not look at indxqualorig, since it will have the
636 637 638
			 * same param references as indxqual, and we aren't really
			 * concerned yet about having a complete subplan list.
			 */
639 640 641
			break;

		case T_MergeJoin:
642 643
			finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
							  &results);
644 645 646
			break;

		case T_HashJoin:
647 648
			finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
							  &results);
649
			break;
650

651
		case T_Hash:
652
			finalize_primnode(((Hash *) plan)->hashkey,
653
							  &results);
654 655
			break;

656 657
		case T_TidScan:
			finalize_primnode((Node *) ((TidScan *) plan)->tideval,
658
							  &results);
659 660
			break;

661 662 663 664 665 666 667 668
		case T_Agg:
		case T_SeqScan:
		case T_NestLoop:
		case T_Material:
		case T_Sort:
		case T_Unique:
		case T_Group:
			break;
669

670
		default:
671 672
			elog(ERROR, "SS_finalize_plan: node %d unsupported",
				 nodeTag(plan));
673
	}
674

675
	/* Process left and right subplans, if any */
676 677 678 679 680 681
	results.paramids = set_unioni(results.paramids,
								  SS_finalize_plan(plan->lefttree));
	results.paramids = set_unioni(results.paramids,
								  SS_finalize_plan(plan->righttree));

	/* Now we have all the paramids and subplans */
682

683
	foreach(lst, results.paramids)
684
	{
685 686
		Var		   *var = nth(lfirsti(lst), PlannerParamVar);

687
		/* note varlevelsup is absolute level number */
688 689 690
		if (var->varlevelsup < PlannerQueryLevel)
			extParam = lappendi(extParam, lfirsti(lst));
		else if (var->varlevelsup > PlannerQueryLevel)
691
			elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
692 693
		else
		{
694 695
			Assert(var->varno == 0 && var->varattno == 0);
			locParam = lappendi(locParam, lfirsti(lst));
696 697
		}
	}
698

699 700
	plan->extParam = extParam;
	plan->locParam = locParam;
701
	plan->subPlan = results.subplans;
702

703
	return results.paramids;
704
}