Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions

subselect.h File Reference

#include "nodes/plannodes.h"
#include "nodes/relation.h"
Include dependency graph for subselect.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

void SS_process_ctes (PlannerInfo *root)
JoinExprconvert_ANY_sublink_to_join (PlannerInfo *root, SubLink *sublink, Relids available_rels)
JoinExprconvert_EXISTS_sublink_to_join (PlannerInfo *root, SubLink *sublink, bool under_not, Relids available_rels)
NodeSS_replace_correlation_vars (PlannerInfo *root, Node *expr)
NodeSS_process_sublinks (PlannerInfo *root, Node *expr, bool isQual)
void SS_finalize_plan (PlannerInfo *root, Plan *plan, bool attach_initplans)
ParamSS_make_initplan_from_plan (PlannerInfo *root, Plan *plan, Oid resulttype, int32 resulttypmod, Oid resultcollation)
Paramassign_nestloop_param_var (PlannerInfo *root, Var *var)
Paramassign_nestloop_param_placeholdervar (PlannerInfo *root, PlaceHolderVar *phv)
int SS_assign_special_param (PlannerInfo *root)

Function Documentation

Param* assign_nestloop_param_placeholdervar ( PlannerInfo root,
PlaceHolderVar phv 
)
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);
}