#include "nodes/plannodes.h"
#include "nodes/relation.h"
Go to the source code of this file.
Functions | |
void | SS_process_ctes (PlannerInfo *root) |
JoinExpr * | convert_ANY_sublink_to_join (PlannerInfo *root, SubLink *sublink, Relids available_rels) |
JoinExpr * | convert_EXISTS_sublink_to_join (PlannerInfo *root, SubLink *sublink, bool under_not, Relids available_rels) |
Node * | SS_replace_correlation_vars (PlannerInfo *root, Node *expr) |
Node * | SS_process_sublinks (PlannerInfo *root, Node *expr, bool isQual) |
void | SS_finalize_plan (PlannerInfo *root, Plan *plan, bool attach_initplans) |
Param * | SS_make_initplan_from_plan (PlannerInfo *root, Plan *plan, Oid resulttype, int32 resulttypmod, Oid resultcollation) |
Param * | assign_nestloop_param_var (PlannerInfo *root, Var *var) |
Param * | assign_nestloop_param_placeholdervar (PlannerInfo *root, PlaceHolderVar *phv) |
int | SS_assign_special_param (PlannerInfo *root) |
Param* assign_nestloop_param_placeholdervar | ( | PlannerInfo * | root, | |
PlaceHolderVar * | phv | |||
) |
Definition at line 275 of file subselect.c.
References Assert, assign_param_for_placeholdervar(), exprCollation(), exprType(), exprTypmod(), i, Param::location, makeNode, Param::paramcollid, Param::paramid, Param::paramkind, Param::paramtype, Param::paramtypmod, PlaceHolderVar::phexpr, and PlaceHolderVar::phlevelsup.
Referenced by replace_nestloop_params_mutator().
{ Param *retval; int i; Assert(phv->phlevelsup == 0); i = assign_param_for_placeholdervar(root, phv); retval = makeNode(Param); retval->paramkind = PARAM_EXEC; retval->paramid = i; retval->paramtype = exprType((Node *) phv->phexpr); retval->paramtypmod = exprTypmod((Node *) phv->phexpr); retval->paramcollid = exprCollation((Node *) phv->phexpr); retval->location = -1; return retval; }
Param* assign_nestloop_param_var | ( | PlannerInfo * | root, | |
Var * | var | |||
) |
Definition at line 171 of file subselect.c.
References Assert, assign_param_for_var(), i, Var::location, Param::location, makeNode, Param::paramcollid, Param::paramid, Param::paramkind, Param::paramtype, Param::paramtypmod, Var::varcollid, Var::varlevelsup, Var::vartype, and Var::vartypmod.
Referenced by replace_nestloop_params_mutator().
{ Param *retval; int i; Assert(var->varlevelsup == 0); i = assign_param_for_var(root, var); retval = makeNode(Param); retval->paramkind = PARAM_EXEC; retval->paramid = i; retval->paramtype = var->vartype; retval->paramtypmod = var->vartypmod; retval->paramcollid = var->varcollid; retval->location = var->location; return retval; }
JoinExpr* convert_ANY_sublink_to_join | ( | PlannerInfo * | root, | |
SubLink * | sublink, | |||
Relids | available_rels | |||
) |
Definition at line 1165 of file subselect.c.
References addRangeTableEntryForSubquery(), JoinExpr::alias, ANY_SUBLINK, Assert, bms_is_empty(), bms_is_subset(), contain_vars_of_level(), contain_volatile_functions(), convert_testexpr(), generate_subquery_vars(), JoinExpr::isNatural, JoinExpr::jointype, lappend(), JoinExpr::larg, list_length(), makeAlias(), makeNode, NIL, NULL, PlannerInfo::parse, parse(), pull_varnos(), JoinExpr::quals, JoinExpr::rarg, Query::rtable, JoinExpr::rtindex, SubLink::subLinkType, SubLink::subselect, SubLink::testexpr, and JoinExpr::usingClause.
Referenced by pull_up_sublinks_qual_recurse().
{ JoinExpr *result; Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; Relids upper_varnos; int rtindex; RangeTblEntry *rte; RangeTblRef *rtr; List *subquery_vars; Node *quals; Assert(sublink->subLinkType == ANY_SUBLINK); /* * The sub-select must not refer to any Vars of the parent query. (Vars of * higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) return NULL; /* * The test expression must contain some Vars of the parent query, else * it's not gonna be a join. (Note that it won't have Vars referring to * the subquery, rather Params.) */ upper_varnos = pull_varnos(sublink->testexpr); if (bms_is_empty(upper_varnos)) return NULL; /* * However, it can't refer to anything outside available_rels. */ if (!bms_is_subset(upper_varnos, available_rels)) return NULL; /* * The combining operators and left-hand expressions mustn't be volatile. */ if (contain_volatile_functions(sublink->testexpr)) return NULL; /* * Okay, pull up the sub-select into upper range table. * * We rely here on the assumption that the outer query has no references * to the inner (necessarily true, other than the Vars that we build * below). Therefore this is a lot easier than what pull_up_subqueries has * to go through. */ rte = addRangeTableEntryForSubquery(NULL, subselect, makeAlias("ANY_subquery", NIL), false, false); parse->rtable = lappend(parse->rtable, rte); rtindex = list_length(parse->rtable); /* * Form a RangeTblRef for the pulled-up sub-select. */ rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; /* * Build a list of Vars representing the subselect outputs. */ subquery_vars = generate_subquery_vars(root, subselect->targetList, rtindex); /* * Build the new join's qual expression, replacing Params with these Vars. */ quals = convert_testexpr(root, sublink->testexpr, subquery_vars); /* * And finally, build the JoinExpr node. */ result = makeNode(JoinExpr); result->jointype = JOIN_SEMI; result->isNatural = false; result->larg = NULL; /* caller must fill this in */ result->rarg = (Node *) rtr; result->usingClause = NIL; result->quals = quals; result->alias = NULL; result->rtindex = 0; /* we don't need an RTE for it */ return result; }
JoinExpr* convert_EXISTS_sublink_to_join | ( | PlannerInfo * | root, | |
SubLink * | sublink, | |||
bool | under_not, | |||
Relids | available_rels | |||
) |
Definition at line 1266 of file subselect.c.
References JoinExpr::alias, Assert, bms_add_member(), bms_first_member(), bms_free(), bms_is_empty(), bms_is_subset(), contain_vars_of_level(), contain_volatile_functions(), copyObject(), Query::cteList, EXISTS_SUBLINK, FromExpr::fromlist, IncrementVarSublevelsUp(), JoinExpr::isNatural, JOIN_ANTI, Query::jointree, JoinExpr::jointype, JoinExpr::larg, linitial, list_concat(), list_length(), makeNode, NIL, NULL, OffsetVarNodes(), PlannerInfo::parse, parse(), pull_varnos(), JoinExpr::quals, FromExpr::quals, JoinExpr::rarg, Query::rtable, JoinExpr::rtindex, simplify_EXISTS_query(), SubLink::subLinkType, SubLink::subselect, and JoinExpr::usingClause.
Referenced by pull_up_sublinks_qual_recurse().
{ JoinExpr *result; Query *parse = root->parse; Query *subselect = (Query *) sublink->subselect; Node *whereClause; int rtoffset; int varno; Relids clause_varnos; Relids upper_varnos; Assert(sublink->subLinkType == EXISTS_SUBLINK); /* * Can't flatten if it contains WITH. (We could arrange to pull up the * WITH into the parent query's cteList, but that risks changing the * semantics, since a WITH ought to be executed once per associated query * call.) Note that convert_ANY_sublink_to_join doesn't have to reject * this case, since it just produces a subquery RTE that doesn't have to * get flattened into the parent query. */ if (subselect->cteList) return NULL; /* * Copy the subquery so we can modify it safely (see comments in * make_subplan). */ subselect = (Query *) copyObject(subselect); /* * See if the subquery can be simplified based on the knowledge that it's * being used in EXISTS(). If we aren't able to get rid of its * targetlist, we have to fail, because the pullup operation leaves us * with noplace to evaluate the targetlist. */ if (!simplify_EXISTS_query(subselect)) return NULL; /* * The subquery must have a nonempty jointree, else we won't have a join. */ if (subselect->jointree->fromlist == NIL) return NULL; /* * Separate out the WHERE clause. (We could theoretically also remove * top-level plain JOIN/ON clauses, but it's probably not worth the * trouble.) */ whereClause = subselect->jointree->quals; subselect->jointree->quals = NULL; /* * The rest of the sub-select must not refer to any Vars of the parent * query. (Vars of higher levels should be okay, though.) */ if (contain_vars_of_level((Node *) subselect, 1)) return NULL; /* * On the other hand, the WHERE clause must contain some Vars of the * parent query, else it's not gonna be a join. */ if (!contain_vars_of_level(whereClause, 1)) return NULL; /* * We don't risk optimizing if the WHERE clause is volatile, either. */ if (contain_volatile_functions(whereClause)) return NULL; /* * Prepare to pull up the sub-select into top range table. * * We rely here on the assumption that the outer query has no references * to the inner (necessarily true). Therefore this is a lot easier than * what pull_up_subqueries has to go through. * * In fact, it's even easier than what convert_ANY_sublink_to_join has to * do. The machinations of simplify_EXISTS_query ensured that there is * nothing interesting in the subquery except an rtable and jointree, and * even the jointree FromExpr no longer has quals. So we can just append * the rtable to our own and use the FromExpr in our jointree. But first, * adjust all level-zero varnos in the subquery to account for the rtable * merger. */ rtoffset = list_length(parse->rtable); OffsetVarNodes((Node *) subselect, rtoffset, 0); OffsetVarNodes(whereClause, rtoffset, 0); /* * Upper-level vars in subquery will now be one level closer to their * parent than before; in particular, anything that had been level 1 * becomes level zero. */ IncrementVarSublevelsUp((Node *) subselect, -1, 1); IncrementVarSublevelsUp(whereClause, -1, 1); /* * Now that the WHERE clause is adjusted to match the parent query * environment, we can easily identify all the level-zero rels it uses. * The ones <= rtoffset belong to the upper query; the ones > rtoffset do * not. */ clause_varnos = pull_varnos(whereClause); upper_varnos = NULL; while ((varno = bms_first_member(clause_varnos)) >= 0) { if (varno <= rtoffset) upper_varnos = bms_add_member(upper_varnos, varno); } bms_free(clause_varnos); Assert(!bms_is_empty(upper_varnos)); /* * Now that we've got the set of upper-level varnos, we can make the last * check: only available_rels can be referenced. */ if (!bms_is_subset(upper_varnos, available_rels)) return NULL; /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); /* * And finally, build the JoinExpr node. */ result = makeNode(JoinExpr); result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; result->isNatural = false; result->larg = NULL; /* caller must fill this in */ /* flatten out the FromExpr node if it's useless */ if (list_length(subselect->jointree->fromlist) == 1) result->rarg = (Node *) linitial(subselect->jointree->fromlist); else result->rarg = (Node *) subselect->jointree; result->usingClause = NIL; result->quals = whereClause; result->alias = NULL; result->rtindex = 0; /* we don't need an RTE for it */ return result; }
int SS_assign_special_param | ( | PlannerInfo * | root | ) |
Definition at line 370 of file subselect.c.
References PlannerInfo::glob, and PlannerGlobal::nParamExec.
Referenced by inheritance_planner(), SS_process_ctes(), and subquery_planner().
{ return root->glob->nParamExec++; }
void SS_finalize_plan | ( | PlannerInfo * | root, | |
Plan * | plan, | |||
bool | attach_initplans | |||
) |
Definition at line 1913 of file subselect.c.
References Plan::allParam, bms_add_member(), bms_add_members(), bms_copy(), bms_del_members(), bms_free(), bms_is_empty(), Plan::extParam, finalize_plan(), PlannerInfo::init_plans, Plan::initPlan, lfirst, lfirst_int, NULL, PlannerParamItem::paramId, PlannerInfo::parent_root, SubPlan::per_call_cost, PlannerInfo::plan_params, planner_subplan_get_plan, SubPlan::setParam, Plan::startup_cost, SubPlan::startup_cost, Plan::total_cost, and PlannerInfo::wt_param_id.
Referenced by SS_make_initplan_from_plan(), and subquery_planner().
{ Bitmapset *valid_params, *initExtParam, *initSetParam; Cost initplan_cost; PlannerInfo *proot; ListCell *l; /* * Examine any initPlans to determine the set of external params they * reference, the set of output params they supply, and their total cost. * We'll use at least some of this info below. (Note we are assuming that * finalize_plan doesn't touch the initPlans.) * * In the case where attach_initplans is false, we are assuming that the * existing initPlans are siblings that might supply params needed by the * current plan. */ initExtParam = initSetParam = NULL; initplan_cost = 0; foreach(l, root->init_plans) { SubPlan *initsubplan = (SubPlan *) lfirst(l); Plan *initplan = planner_subplan_get_plan(root, initsubplan); ListCell *l2; initExtParam = bms_add_members(initExtParam, initplan->extParam); foreach(l2, initsubplan->setParam) { initSetParam = bms_add_member(initSetParam, lfirst_int(l2)); } initplan_cost += initsubplan->startup_cost + initsubplan->per_call_cost; } /* * Now determine the set of params that are validly referenceable in this * query level; to wit, those available from outer query levels plus the * output parameters of any local initPlans. (We do not include output * parameters of regular subplans. Those should only appear within the * testexpr of SubPlan nodes, and are taken care of locally within * finalize_primnode. Likewise, special parameters that are generated by * nodes such as ModifyTable are handled within finalize_plan.) */ valid_params = bms_copy(initSetParam); for (proot = root->parent_root; proot != NULL; proot = proot->parent_root) { /* Include ordinary Var/PHV/Aggref params */ foreach(l, proot->plan_params) { PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l); valid_params = bms_add_member(valid_params, pitem->paramId); } /* Include any outputs of outer-level initPlans */ foreach(l, proot->init_plans) { SubPlan *initsubplan = (SubPlan *) lfirst(l); ListCell *l2; foreach(l2, initsubplan->setParam) { valid_params = bms_add_member(valid_params, lfirst_int(l2)); } } /* Include worktable ID, if a recursive query is being planned */ if (proot->wt_param_id >= 0) valid_params = bms_add_member(valid_params, proot->wt_param_id); } /* * Now recurse through plan tree. */ (void) finalize_plan(root, plan, valid_params, NULL); bms_free(valid_params); /* * Finally, attach any initPlans to the topmost plan node, and add their * extParams to the topmost node's, too. However, any setParams of the * initPlans should not be present in the topmost node's extParams, only * in its allParams. (As of PG 8.1, it's possible that some initPlans * have extParams that are setParams of other initPlans, so we have to * take care of this situation explicitly.) * * We also add the eval cost of each initPlan to the startup cost of the * top node. This is a conservative overestimate, since in fact each * initPlan might be executed later than plan startup, or even not at all. */ if (attach_initplans) { plan->initPlan = root->init_plans; root->init_plans = NIL; /* make sure they're not attached twice */ /* allParam must include all these params */ plan->allParam = bms_add_members(plan->allParam, initExtParam); plan->allParam = bms_add_members(plan->allParam, initSetParam); /* extParam must include any child extParam */ plan->extParam = bms_add_members(plan->extParam, initExtParam); /* but extParam shouldn't include any setParams */ plan->extParam = bms_del_members(plan->extParam, initSetParam); /* ensure extParam is exactly NULL if it's empty */ if (bms_is_empty(plan->extParam)) plan->extParam = NULL; plan->startup_cost += initplan_cost; plan->total_cost += initplan_cost; } }
Param* SS_make_initplan_from_plan | ( | PlannerInfo * | root, | |
Plan * | plan, | |||
Oid | resulttype, | |||
int32 | resulttypmod, | |||
Oid | resultcollation | |||
) |
Definition at line 2505 of file subselect.c.
References cost_subplan(), SubPlan::firstColCollation, SubPlan::firstColType, SubPlan::firstColTypmod, generate_new_param(), get_first_col_type(), PlannerInfo::glob, PlannerInfo::init_plans, lappend(), list_length(), list_make1_int, makeNode, palloc(), Param::paramid, SubPlan::plan_id, SubPlan::plan_name, SubPlan::setParam, SS_finalize_plan(), SubPlan::subLinkType, PlannerGlobal::subplans, and PlannerGlobal::subroots.
Referenced by make_agg_subplan().
{ SubPlan *node; Param *prm; /* * We must run SS_finalize_plan(), since that's normally done before a * subplan gets put into the initplan list. Tell it not to attach any * pre-existing initplans to this one, since they are siblings not * children of this initplan. (This is something else that could perhaps * be cleaner if we did extParam/allParam processing in setrefs.c instead * of here? See notes for materialize_finished_plan.) */ /* * Build extParam/allParam sets for plan nodes. */ SS_finalize_plan(root, plan, false); /* * Add the subplan and its PlannerInfo to the global lists. */ root->glob->subplans = lappend(root->glob->subplans, plan); root->glob->subroots = lappend(root->glob->subroots, root); /* * Create a SubPlan node and add it to the outer list of InitPlans. Note * it has to appear after any other InitPlans it might depend on (see * comments in ExecReScan). */ node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation); node->plan_id = list_length(root->glob->subplans); root->init_plans = lappend(root->init_plans, node); /* * The node can't have any inputs (since it's an initplan), so the * parParam and args lists remain empty. */ cost_subplan(root, node, plan); /* * Make a Param that will be the subplan's output. */ prm = generate_new_param(root, resulttype, resulttypmod, resultcollation); node->setParam = list_make1_int(prm->paramid); /* Label the subplan for EXPLAIN purposes */ node->plan_name = palloc(64); sprintf(node->plan_name, "InitPlan %d (returns $%d)", node->plan_id, prm->paramid); return prm; }
void SS_process_ctes | ( | PlannerInfo * | root | ) |
Definition at line 1022 of file subselect.c.
References SubPlan::args, Assert, CMD_SELECT, copyObject(), cost_subplan(), PlannerInfo::cte_plan_ids, Query::cteList, CommonTableExpr::ctename, CommonTableExpr::ctequery, CommonTableExpr::cterecursive, CommonTableExpr::cterefcount, elog, ERROR, SubPlan::firstColCollation, SubPlan::firstColType, SubPlan::firstColTypmod, get_first_col_type(), PlannerInfo::glob, PlannerInfo::init_plans, lappend(), lappend_int(), lfirst, list_length(), list_make1_int, makeNode, NIL, palloc(), SubPlan::paramIds, SubPlan::parParam, PlannerInfo::parse, SubPlan::plan_id, SubPlan::plan_name, PlannerInfo::plan_params, SubPlan::setParam, splan, SS_assign_special_param(), SubPlan::subLinkType, PlannerGlobal::subplans, subquery_planner(), PlannerGlobal::subroots, SubPlan::testexpr, SubPlan::unknownEqFalse, and SubPlan::useHashTable.
Referenced by subquery_planner().
{ ListCell *lc; Assert(root->cte_plan_ids == NIL); foreach(lc, root->parse->cteList) { CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc); CmdType cmdType = ((Query *) cte->ctequery)->commandType; Query *subquery; Plan *plan; PlannerInfo *subroot; SubPlan *splan; int paramid; /* * Ignore SELECT CTEs that are not actually referenced anywhere. */ if (cte->cterefcount == 0 && cmdType == CMD_SELECT) { /* Make a dummy entry in cte_plan_ids */ root->cte_plan_ids = lappend_int(root->cte_plan_ids, -1); continue; } /* * Copy the source Query node. Probably not necessary, but let's keep * this similar to make_subplan. */ subquery = (Query *) copyObject(cte->ctequery); /* plan_params should not be in use in current query level */ Assert(root->plan_params == NIL); /* * Generate the plan for the CTE query. Always plan for full * retrieval --- we don't have enough info to predict otherwise. */ plan = subquery_planner(root->glob, subquery, root, cte->cterecursive, 0.0, &subroot); /* * Since the current query level doesn't yet contain any RTEs, it * should not be possible for the CTE to have requested parameters of * this level. */ if (root->plan_params) elog(ERROR, "unexpected outer reference in CTE query"); /* * Make a SubPlan node for it. This is just enough unlike * build_subplan that we can't share code. * * Note plan_id, plan_name, and cost fields are set further down. */ splan = makeNode(SubPlan); splan->subLinkType = CTE_SUBLINK; splan->testexpr = NULL; splan->paramIds = NIL; get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation); splan->useHashTable = false; splan->unknownEqFalse = false; splan->setParam = NIL; splan->parParam = NIL; splan->args = NIL; /* * The node can't have any inputs (since it's an initplan), so the * parParam and args lists remain empty. (It could contain references * to earlier CTEs' output param IDs, but CTE outputs are not * propagated via the args list.) */ /* * Assign a param ID to represent the CTE's output. No ordinary * "evaluation" of this param slot ever happens, but we use the param * ID for setParam/chgParam signaling just as if the CTE plan were * returning a simple scalar output. (Also, the executor abuses the * ParamExecData slot for this param ID for communication among * multiple CteScan nodes that might be scanning this CTE.) */ paramid = SS_assign_special_param(root); splan->setParam = list_make1_int(paramid); /* * Add the subplan and its PlannerInfo to the global lists. */ root->glob->subplans = lappend(root->glob->subplans, plan); root->glob->subroots = lappend(root->glob->subroots, subroot); splan->plan_id = list_length(root->glob->subplans); root->init_plans = lappend(root->init_plans, splan); root->cte_plan_ids = lappend_int(root->cte_plan_ids, splan->plan_id); /* Label the subplan for EXPLAIN purposes */ splan->plan_name = palloc(4 + strlen(cte->ctename) + 1); sprintf(splan->plan_name, "CTE %s", cte->ctename); /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); } }
Node* SS_process_sublinks | ( | PlannerInfo * | root, | |
Node * | expr, | |||
bool | isQual | |||
) |
Definition at line 1770 of file subselect.c.
References process_sublinks_context::isTopQual, process_sublinks_mutator(), and process_sublinks_context::root.
Referenced by build_subplan(), and preprocess_expression().
{ process_sublinks_context context; context.root = root; context.isTopQual = isQual; return process_sublinks_mutator(expr, &context); }
Node* SS_replace_correlation_vars | ( | PlannerInfo * | root, | |
Node * | expr | |||
) |
Definition at line 1730 of file subselect.c.
References replace_correlation_vars_mutator().
Referenced by preprocess_expression().
{ /* No setup needed for tree walk, so away we go */ return replace_correlation_vars_mutator(expr, root); }