subselect.c 20.7 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * subselect.c
4 5
 *	  Planning routines for subselects and parameters.
 *
B
Bruce Momjian 已提交
6
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
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.59 2002/12/05 15:50:35 tgl Exp $
11 12 13 14 15
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

16
#include "catalog/pg_operator.h"
17 18
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
19
#include "nodes/params.h"
20
#include "optimizer/clauses.h"
21 22
#include "optimizer/cost.h"
#include "optimizer/planmain.h"
B
Bruce Momjian 已提交
23 24
#include "optimizer/planner.h"
#include "optimizer/subselect.h"
25
#include "parser/parsetree.h"
26 27
#include "parser/parse_expr.h"
#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 39 40 41 42 43 44 45 46 47

/*--------------------
 * 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.
48 49 50 51
 *
 * We also need to create Param slots that don't correspond to any outer Var.
 * For these, we set varno = 0 and varlevelsup = 0, so that they can't
 * accidentally match an outer Var.
52 53
 *--------------------
 */
54 55


56 57 58 59
static void convert_sublink_opers(SubLink *slink, List *targetlist,
								  List **setParams);


60 61 62
/*
 * Create a new entry in the PlannerParamVar list, and return its index.
 *
63 64 65 66
 * var contains the data to use, except for varlevelsup which
 * is set from the absolute level value given by varlevel.  NOTE that
 * the passed var is scribbled on and placed directly into the list!
 * Generally, caller should have just created or copied it.
67
 */
68
static int
69
new_param(Var *var, Index varlevel)
70
{
71
	var->varlevelsup = varlevel;
72

73
	PlannerParamVar = lappend(PlannerParamVar, var);
74

75
	return length(PlannerParamVar) - 1;
76 77
}

78 79 80 81
/*
 * Generate a Param node to replace the given Var,
 * which is expected to have varlevelsup > 0 (ie, it is not local).
 */
82
static Param *
83
replace_var(Var *var)
84
{
85
	List	   *ppv;
86
	Param	   *retval;
87
	Index		varlevel;
88 89
	int			i;

90 91
	Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel);
	varlevel = PlannerQueryLevel - var->varlevelsup;
92

93
	/*
94
	 * If there's already a PlannerParamVar entry for this same Var, just
B
Bruce Momjian 已提交
95 96 97 98 99 100
	 * use it.	NOTE: in sufficiently complex querytrees, 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
101
	 * execution in each part of the tree.
102 103 104
	 */
	i = 0;
	foreach(ppv, PlannerParamVar)
105
	{
106
		Var		   *pvar = lfirst(ppv);
107 108 109 110 111

		if (pvar->varno == var->varno &&
			pvar->varattno == var->varattno &&
			pvar->varlevelsup == varlevel &&
			pvar->vartype == var->vartype)
112
			break;
113
		i++;
114
	}
115

116
	if (!ppv)
117 118
	{
		/* Nope, so make a new one */
119
		i = new_param((Var *) copyObject(var), varlevel);
120
	}
121

122 123 124 125
	retval = makeNode(Param);
	retval->paramkind = PARAM_EXEC;
	retval->paramid = (AttrNumber) i;
	retval->paramtype = var->vartype;
126

127
	return retval;
128 129
}

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
/*
 * Generate a new Param node that will not conflict with any other.
 */
static Param *
generate_new_param(Oid paramtype, int32 paramtypmod)
{
	Var		   *var = makeVar(0, 0, paramtype, paramtypmod, 0);
	Param	   *retval = makeNode(Param);

	retval->paramkind = PARAM_EXEC;
	retval->paramid = (AttrNumber) new_param(var, 0);
	retval->paramtype = paramtype;

	return retval;
}

146 147 148
/*
 * Convert a bare SubLink (as created by the parser) into a SubPlan.
 */
149
static Node *
150
make_subplan(SubLink *slink)
151
{
152
	SubPlan    *node = makeNode(SubPlan);
153
	Query	   *subquery = (Query *) (slink->subselect);
154
	Oid			result_type = exprType((Node *) slink);
155
	double		tuple_fraction;
156 157 158
	Plan	   *plan;
	List	   *lst;
	Node	   *result;
159

160 161 162 163
	/*
	 * Check to see if this node was already processed; if so we have
	 * trouble.  We check to see if the linked-to Query appears to have
	 * been planned already, too.
164 165
	 */
	if (subquery == NULL)
166 167
		elog(ERROR, "make_subplan: invalid expression structure (SubLink already processed?)");
	if (subquery->base_rel_list != NIL)
168 169
		elog(ERROR, "make_subplan: invalid expression structure (subquery already processed?)");

170
	/*
B
Bruce Momjian 已提交
171 172 173 174
	 * Copy the source Query node.	This is a quick and dirty kluge to
	 * resolve the fact that the parser can generate trees with multiple
	 * links to the same sub-Query node, but the planner wants to scribble
	 * on the Query. Try to clean this up when we do querytree redesign...
175 176 177
	 */
	subquery = (Query *) copyObject(subquery);

178
	/*
179 180 181 182 183 184 185
	 * 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).
186
	 *
187 188 189
	 * NOTE: if you change these numbers, also change cost_qual_eval_walker()
	 * in path/costsize.c.
	 *
190 191 192 193 194 195 196 197
	 * 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.
198 199 200
	 */
	if (slink->subLinkType == EXISTS_SUBLINK)
		tuple_fraction = 1.0;	/* just like a LIMIT 1 */
201 202
	else if (slink->subLinkType == ALL_SUBLINK ||
			 slink->subLinkType == ANY_SUBLINK)
203
		tuple_fraction = 0.5;	/* 50% */
204 205
	else
		tuple_fraction = -1.0;	/* default behavior */
206

207
	/*
208
	 * Generate the plan for the subquery.
209
	 */
210
	node->plan = plan = subquery_planner(subquery, tuple_fraction);
211

B
Bruce Momjian 已提交
212 213
	node->plan_id = PlannerPlanId++;	/* Assign unique ID to this
										 * SubPlan */
214

215
	node->rtable = subquery->rtable;
216
	node->sublink = slink;
217

218
	slink->subselect = NULL;	/* cool ?! see error check above! */
219

220
	/*
B
Bruce Momjian 已提交
221 222
	 * Make parParam list of params that current query level will pass to
	 * this child plan.
223
	 */
224
	foreach(lst, plan->extParam)
225
	{
226 227
		int			paramid = lfirsti(lst);
		Var		   *var = nth(paramid, PlannerParamVar);
228

229
		/* note varlevelsup is absolute level number */
230
		if (var->varlevelsup == PlannerQueryLevel)
231
			node->parParam = lappendi(node->parParam, paramid);
232
	}
233 234

	/*
235
	 * Un-correlated or undirect correlated plans of EXISTS, EXPR, or
236 237
	 * MULTIEXPR types can be used as initPlans.  For EXISTS or EXPR, we
	 * just produce a Param referring to the result of evaluating the
238 239 240
	 * 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.
241
	 */
242 243
	if (node->parParam == NIL && slink->subLinkType == EXISTS_SUBLINK)
	{
244
		Param	   *prm;
245

246
		prm = generate_new_param(BOOLOID, -1);
247 248 249 250 251 252 253
		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);
254
		Param	   *prm;
255

256
		prm = generate_new_param(te->resdom->restype, te->resdom->restypmod);
257 258 259 260 261
		node->setParam = lappendi(node->setParam, prm->paramid);
		PlannerInitPlan = lappend(PlannerInitPlan, node);
		result = (Node *) prm;
	}
	else if (node->parParam == NIL && slink->subLinkType == MULTIEXPR_SUBLINK)
262
	{
263
		convert_sublink_opers(slink, plan->targetlist, &node->setParam);
264
		PlannerInitPlan = lappend(PlannerInitPlan, node);
265 266 267
		if (length(slink->oper) > 1)
			result = (Node *) ((slink->useor) ? make_orclause(slink->oper) :
							   make_andclause(slink->oper));
268
		else
269
			result = (Node *) lfirst(slink->oper);
270
	}
271
	else
272
	{
273
		Expr	   *expr = makeNode(Expr);
274
		List	   *args = NIL;
275

276
		/*
277 278 279 280 281 282 283 284 285 286
		 * 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.
287 288 289 290 291 292
		 *
		 * XXX It's pretty ugly to be inserting a MATERIAL node at this
		 * point.  Since subquery_planner has already run SS_finalize_plan
		 * on the subplan tree, we have to kluge up parameter lists for
		 * the MATERIAL node.  Possibly this could be fixed by postponing
		 * SS_finalize_plan processing until setrefs.c is run.
293 294 295 296 297 298 299 300
		 */
		if (node->parParam == NIL)
		{
			bool		use_material;

			switch (nodeTag(plan))
			{
				case T_SeqScan:
301
					if (plan->initPlan)
302 303 304 305 306 307 308 309 310 311 312
						use_material = true;
					else
					{
						Selectivity qualsel;

						qualsel = clauselist_selectivity(subquery,
														 plan->qual,
														 0);
						/* Is 10% selectivity a good threshold?? */
						use_material = qualsel < 0.10;
					}
313 314
					break;
				case T_Material:
315
				case T_FunctionScan:
316
				case T_Sort:
317 318 319

					/*
					 * Don't add another Material node if there's one
320 321
					 * already, nor if the top node is any other type that
					 * materializes its output anyway.
322 323 324 325 326 327 328 329 330
					 */
					use_material = false;
					break;
				default:
					use_material = true;
					break;
			}
			if (use_material)
			{
B
Bruce Momjian 已提交
331
				Plan	   *matplan;
332
				Path		matpath; /* dummy for result of cost_material */
333 334

				matplan = (Plan *) make_material(plan->targetlist, plan);
335 336 337 338 339 340 341 342
				/* need to calculate costs */
				cost_material(&matpath,
							  plan->total_cost,
							  plan->plan_rows,
							  plan->plan_width);
				matplan->startup_cost = matpath.startup_cost;
				matplan->total_cost = matpath.total_cost;
				/* parameter kluge --- see comments above */
343 344 345
				matplan->extParam = listCopy(plan->extParam);
				matplan->locParam = listCopy(plan->locParam);
				node->plan = plan = matplan;
346 347 348
			}
		}

349 350 351
		/* Fix the SubLink's oper list */
		convert_sublink_opers(slink, plan->targetlist, NULL);

352 353 354
		/*
		 * Make expression of SUBPLAN type
		 */
355
		expr->typeOid = result_type;
356
		expr->opType = SUBPLAN_EXPR;
357 358 359
		expr->oper = (Node *) node;

		/*
360
		 * Make expr->args from parParam.
361
		 */
362
		foreach(lst, node->parParam)
363
		{
364 365 366
			Var		   *var = nth(lfirsti(lst), PlannerParamVar);

			var = (Var *) copyObject(var);
367 368 369 370 371

			/*
			 * Must fix absolute-level varlevelsup from the
			 * PlannerParamVar entry.  But since var is at current subplan
			 * level, this is easy:
372
			 */
373
			var->varlevelsup = 0;
374
			args = lappend(args, var);
375
		}
376
		expr->args = args;
377

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
		result = (Node *) expr;
	}

	return result;
}

/*
 * convert_sublink_opers: convert a SubLink's oper list from the
 * parser/rewriter format into the executor's format.
 *
 * The oper list is initially just a list of Oper nodes.  We replace it
 * with a list of actually executable expressions, in which the specified
 * operators are applied to corresponding elements of the lefthand list
 * and Params representing the results of the subplan.  lefthand is then
 * set to NIL.
 *
 * If setParams is not NULL, the paramids of the Params created are added
 * to the *setParams list.
 */
static void
convert_sublink_opers(SubLink *slink, List *targetlist,
					  List **setParams)
{
	List	   *newoper = NIL;
	List	   *leftlist = slink->lefthand;
	List	   *lst;

	foreach(lst, slink->oper)
	{
		Oper	   *oper = (Oper *) lfirst(lst);
		Node	   *lefthand = lfirst(leftlist);
		TargetEntry *te = lfirst(targetlist);
		Param	   *prm;
		Operator	tup;
		Form_pg_operator opform;
		Node	   *left,
				   *right;

		/* Make the Param node representing the subplan's result */
		prm = generate_new_param(te->resdom->restype,
								 te->resdom->restypmod);

		/* Record its ID if needed */
		if (setParams)
			*setParams = lappendi(*setParams, prm->paramid);

		/* Look up the operator to check its declared input types */
		Assert(IsA(oper, Oper));
		tup = SearchSysCache(OPEROID,
							 ObjectIdGetDatum(oper->opno),
							 0, 0, 0);
		if (!HeapTupleIsValid(tup))
			elog(ERROR, "cache lookup failed for operator %u", oper->opno);
		opform = (Form_pg_operator) GETSTRUCT(tup);

433
		/*
434 435 436 437
		 * Make the expression node.
		 *
		 * Note: we use make_operand in case runtime type conversion
		 * function calls must be inserted for this operator!
438
		 */
439 440 441 442 443 444
		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));
445

446 447 448 449
		ReleaseSysCache(tup);

		leftlist = lnext(leftlist);
		targetlist = lnext(targetlist);
450
	}
451

452 453
	slink->oper = newoper;
	slink->lefthand = NIL;
454 455
}

456
/*
457 458 459
 * finalize_primnode: build lists of params appearing
 * in the given expression tree.  NOTE: items are added to list passed in,
 * so caller must initialize list to NIL before first call!
460 461
 */

462 463 464
typedef struct finalize_primnode_results
{
	List	   *paramids;		/* List of PARAM_EXEC paramids found */
465
} finalize_primnode_results;
466

467
static bool
468
finalize_primnode(Node *node, finalize_primnode_results *results)
469 470 471 472
{
	if (node == NULL)
		return false;
	if (IsA(node, Param))
473
	{
474 475
		if (((Param *) node)->paramkind == PARAM_EXEC)
		{
476
			int			paramid = (int) ((Param *) node)->paramid;
477

478
			if (!intMember(paramid, results->paramids))
479 480 481
				results->paramids = lconsi(paramid, results->paramids);
		}
		return false;			/* no more to do here */
482
	}
483
	if (is_subplan(node))
484
	{
485
		SubPlan    *subplan = (SubPlan *) ((Expr *) node)->oper;
486 487
		List	   *lst;

488 489
		/* Check extParam list for params to add to paramids */
		foreach(lst, subplan->plan->extParam)
490
		{
491 492
			int			paramid = lfirsti(lst);
			Var		   *var = nth(paramid, PlannerParamVar);
493

494
			/* note varlevelsup is absolute level number */
495
			if (var->varlevelsup < PlannerQueryLevel &&
496
				!intMember(paramid, results->paramids))
497
				results->paramids = lconsi(paramid, results->paramids);
498
		}
499
		/* fall through to recurse into subplan args */
500
	}
501
	return expression_tree_walker(node, finalize_primnode,
502
								  (void *) results);
503 504
}

505 506
/*
 * Replace correlation vars (uplevel vars) with Params.
507
 */
508 509 510

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

511
Node *
512
SS_replace_correlation_vars(Node *expr)
513
{
514 515 516
	/* No setup needed for tree walk, so away we go */
	return replace_correlation_vars_mutator(expr, NULL);
}
517

518 519 520 521 522 523
static Node *
replace_correlation_vars_mutator(Node *node, void *context)
{
	if (node == NULL)
		return NULL;
	if (IsA(node, Var))
524
	{
525 526
		if (((Var *) node)->varlevelsup > 0)
			return (Node *) replace_var((Var *) node);
527
	}
528 529 530
	return expression_tree_mutator(node,
								   replace_correlation_vars_mutator,
								   context);
531 532
}

533 534
/*
 * Expand SubLinks to SubPlans in the given expression.
535
 */
536 537 538

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

539 540
Node *
SS_process_sublinks(Node *expr)
541
{
542
	/* No setup needed for tree walk, so away we go */
543
	return process_sublinks_mutator(expr, NULL);
544 545 546 547 548 549
}

static Node *
process_sublinks_mutator(Node *node, void *context)
{
	if (node == NULL)
550
		return NULL;
551
	if (IsA(node, SubLink))
552
	{
553
		SubLink    *sublink = (SubLink *) node;
554

555 556 557 558
		/*
		 * 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!)
559
		 */
560 561 562 563
		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);
564
	}
565

566 567
	/*
	 * Note that we will never see a SubPlan expression in the input
568 569 570
	 * (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.
571
	 */
572
	Assert(!is_subplan(node));
573

574 575 576
	return expression_tree_mutator(node,
								   process_sublinks_mutator,
								   context);
577 578
}

579
List *
580
SS_finalize_plan(Plan *plan, List *rtable)
581
{
582 583 584
	List	   *extParam = NIL;
	List	   *locParam = NIL;
	finalize_primnode_results results;
585 586 587
	List	   *lst;

	if (plan == NULL)
588
		return NIL;
589

590
	results.paramids = NIL;		/* initialize list to NIL */
591

592 593
	/*
	 * When we call finalize_primnode, results.paramids lists are
594 595
	 * automatically merged together.  But when recursing to self, we have
	 * to do it the hard way.  We want the paramids list to include params
596
	 * in subplans as well as at this level.
597 598
	 */

599
	/* Find params 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 605 606
	switch (nodeTag(plan))
	{
		case T_Result:
607 608
			finalize_primnode(((Result *) plan)->resconstantqual,
							  &results);
609 610
			break;

611 612 613 614 615 616
		case T_IndexScan:
			finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
							  &results);

			/*
			 * we need not look at indxqualorig, since it will have the
617
			 * same param references as indxqual.
618 619 620 621 622 623
			 */
			break;

		case T_TidScan:
			finalize_primnode((Node *) ((TidScan *) plan)->tideval,
							  &results);
624
			break;
625

626
		case T_SubqueryScan:
B
Bruce Momjian 已提交
627

628
			/*
B
Bruce Momjian 已提交
629 630 631 632 633
			 * In a SubqueryScan, SS_finalize_plan has already been run on
			 * the subplan by the inner invocation of subquery_planner, so
			 * there's no need to do it again.  Instead, just pull out the
			 * subplan's extParams list, which represents the params it
			 * needs from my level and higher levels.
634
			 */
635
			results.paramids = set_unioni(results.paramids,
B
Bruce Momjian 已提交
636
							 ((SubqueryScan *) plan)->subplan->extParam);
637 638
			break;

639 640 641
		case T_FunctionScan:
			{
				RangeTblEntry *rte;
642

643 644 645 646 647 648 649 650 651 652
				rte = rt_fetch(((FunctionScan *) plan)->scan.scanrelid,
							   rtable);
				Assert(rte->rtekind == RTE_FUNCTION);
				finalize_primnode(rte->funcexpr, &results);
			}
			break;

		case T_Append:
			foreach(lst, ((Append *) plan)->appendplans)
				results.paramids = set_unioni(results.paramids,
B
Bruce Momjian 已提交
653 654
								   SS_finalize_plan((Plan *) lfirst(lst),
													rtable));
655 656
			break;

657 658 659 660 661
		case T_NestLoop:
			finalize_primnode((Node *) ((Join *) plan)->joinqual,
							  &results);
			break;

662
		case T_MergeJoin:
663 664
			finalize_primnode((Node *) ((Join *) plan)->joinqual,
							  &results);
665 666
			finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
							  &results);
667 668 669
			break;

		case T_HashJoin:
670 671
			finalize_primnode((Node *) ((Join *) plan)->joinqual,
							  &results);
672 673
			finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
							  &results);
674
			break;
675

676
		case T_Hash:
677
			finalize_primnode((Node *) ((Hash *) plan)->hashkeys,
678
							  &results);
679 680 681 682 683 684 685
			break;

		case T_Agg:
		case T_SeqScan:
		case T_Material:
		case T_Sort:
		case T_Unique:
686
		case T_SetOp:
687
		case T_Limit:
688 689
		case T_Group:
			break;
690

691
		default:
692 693
			elog(ERROR, "SS_finalize_plan: node %d unsupported",
				 nodeTag(plan));
694
	}
695

696
	/* Process left and right child plans, if any */
697
	results.paramids = set_unioni(results.paramids,
698 699
								  SS_finalize_plan(plan->lefttree,
												   rtable));
700
	results.paramids = set_unioni(results.paramids,
701 702
								  SS_finalize_plan(plan->righttree,
												   rtable));
703

704
	/* Now we have all the paramids */
705

706
	foreach(lst, results.paramids)
707
	{
708 709
		int			paramid = lfirsti(lst);
		Var		   *var = nth(paramid, PlannerParamVar);
710

711
		/* note varlevelsup is absolute level number */
712
		if (var->varlevelsup < PlannerQueryLevel)
713
			extParam = lappendi(extParam, paramid);
714
		else if (var->varlevelsup > PlannerQueryLevel)
715
			elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
716 717
		else
		{
718
			Assert(var->varno == 0 && var->varattno == 0);
719
			locParam = lappendi(locParam, paramid);
720 721
		}
	}
722

723 724 725
	plan->extParam = extParam;
	plan->locParam = locParam;

726
	return results.paramids;
727
}