Header And Logo

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

Data Structures | Typedefs | Functions

subselect.c File Reference

#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
#include "optimizer/subselect.h"
#include "optimizer/var.h"
#include "parser/parse_relation.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
Include dependency graph for subselect.c:

Go to the source code of this file.

Data Structures

struct  convert_testexpr_context
struct  process_sublinks_context
struct  finalize_primnode_context

Typedefs

typedef struct
convert_testexpr_context 
convert_testexpr_context
typedef struct
process_sublinks_context 
process_sublinks_context
typedef struct
finalize_primnode_context 
finalize_primnode_context

Functions

static Nodebuild_subplan (PlannerInfo *root, Plan *plan, PlannerInfo *subroot, List *plan_params, SubLinkType subLinkType, Node *testexpr, bool adjust_testexpr, bool unknownEqFalse)
static Listgenerate_subquery_params (PlannerInfo *root, List *tlist, List **paramIds)
static Listgenerate_subquery_vars (PlannerInfo *root, List *tlist, Index varno)
static Nodeconvert_testexpr (PlannerInfo *root, Node *testexpr, List *subst_nodes)
static Nodeconvert_testexpr_mutator (Node *node, convert_testexpr_context *context)
static bool subplan_is_hashable (Plan *plan)
static bool testexpr_is_hashable (Node *testexpr)
static bool hash_ok_operator (OpExpr *expr)
static bool simplify_EXISTS_query (Query *query)
static Queryconvert_EXISTS_to_ANY (PlannerInfo *root, Query *subselect, Node **testexpr, List **paramIds)
static Nodereplace_correlation_vars_mutator (Node *node, PlannerInfo *root)
static Nodeprocess_sublinks_mutator (Node *node, process_sublinks_context *context)
static Bitmapsetfinalize_plan (PlannerInfo *root, Plan *plan, Bitmapset *valid_params, Bitmapset *scan_params)
static bool finalize_primnode (Node *node, finalize_primnode_context *context)
static int assign_param_for_var (PlannerInfo *root, Var *var)
static Paramreplace_outer_var (PlannerInfo *root, Var *var)
Paramassign_nestloop_param_var (PlannerInfo *root, Var *var)
static int assign_param_for_placeholdervar (PlannerInfo *root, PlaceHolderVar *phv)
static Paramreplace_outer_placeholdervar (PlannerInfo *root, PlaceHolderVar *phv)
Paramassign_nestloop_param_placeholdervar (PlannerInfo *root, PlaceHolderVar *phv)
static Paramreplace_outer_agg (PlannerInfo *root, Aggref *agg)
static Paramgenerate_new_param (PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid paramcollation)
int SS_assign_special_param (PlannerInfo *root)
static void get_first_col_type (Plan *plan, Oid *coltype, int32 *coltypmod, Oid *colcollation)
static Nodemake_subplan (PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType, Node *testexpr, bool isTopQual)
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)

Typedef Documentation


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;
}

static int assign_param_for_placeholdervar ( PlannerInfo root,
PlaceHolderVar phv 
) [static]

Definition at line 199 of file subselect.c.

References Assert, copyObject(), PlannerInfo::glob, IncrementVarSublevelsUp(), IsA, PlannerParamItem::item, lappend(), lfirst, makeNode, PlannerGlobal::nParamExec, PlannerParamItem::paramId, PlannerInfo::parent_root, PlaceHolderVar::phid, PlaceHolderVar::phlevelsup, and PlannerInfo::plan_params.

Referenced by assign_nestloop_param_placeholdervar(), and replace_outer_placeholdervar().

{
    ListCell   *ppl;
    PlannerParamItem *pitem;
    Index       levelsup;

    /* Find the query level the PHV belongs to */
    for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
        root = root->parent_root;

    /* If there's already a matching PlannerParamItem there, just use it */
    foreach(ppl, root->plan_params)
    {
        pitem = (PlannerParamItem *) lfirst(ppl);
        if (IsA(pitem->item, PlaceHolderVar))
        {
            PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;

            /* We assume comparing the PHIDs is sufficient */
            if (pphv->phid == phv->phid)
                return pitem->paramId;
        }
    }

    /* Nope, so make a new one */
    phv = (PlaceHolderVar *) copyObject(phv);
    if (phv->phlevelsup != 0)
    {
        IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
        Assert(phv->phlevelsup == 0);
    }

    pitem = makeNode(PlannerParamItem);
    pitem->item = (Node *) phv;
    pitem->paramId = root->glob->nParamExec++;

    root->plan_params = lappend(root->plan_params, pitem);

    return pitem->paramId;
}

static int assign_param_for_var ( PlannerInfo root,
Var var 
) [static]

Definition at line 91 of file subselect.c.

References copyObject(), PlannerInfo::glob, IsA, PlannerParamItem::item, lappend(), lfirst, makeNode, PlannerGlobal::nParamExec, PlannerParamItem::paramId, PlannerInfo::parent_root, PlannerInfo::plan_params, Var::varattno, Var::varcollid, Var::varlevelsup, Var::varno, Var::varnoold, Var::varoattno, Var::vartype, and Var::vartypmod.

Referenced by assign_nestloop_param_var(), and replace_outer_var().

{
    ListCell   *ppl;
    PlannerParamItem *pitem;
    Index       levelsup;

    /* Find the query level the Var belongs to */
    for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
        root = root->parent_root;

    /* If there's already a matching PlannerParamItem there, just use it */
    foreach(ppl, root->plan_params)
    {
        pitem = (PlannerParamItem *) lfirst(ppl);
        if (IsA(pitem->item, Var))
        {
            Var        *pvar = (Var *) pitem->item;

            /*
             * This comparison must match _equalVar(), except for ignoring
             * varlevelsup.  Note that _equalVar() ignores the location.
             */
            if (pvar->varno == var->varno &&
                pvar->varattno == var->varattno &&
                pvar->vartype == var->vartype &&
                pvar->vartypmod == var->vartypmod &&
                pvar->varcollid == var->varcollid &&
                pvar->varnoold == var->varnoold &&
                pvar->varoattno == var->varoattno)
                return pitem->paramId;
        }
    }

    /* Nope, so make a new one */
    var = (Var *) copyObject(var);
    var->varlevelsup = 0;

    pitem = makeNode(PlannerParamItem);
    pitem->item = (Node *) var;
    pitem->paramId = root->glob->nParamExec++;

    root->plan_params = lappend(root->plan_params, pitem);

    return pitem->paramId;
}

static Node * build_subplan ( PlannerInfo root,
Plan plan,
PlannerInfo subroot,
List plan_params,
SubLinkType  subLinkType,
Node testexpr,
bool  adjust_testexpr,
bool  unknownEqFalse 
) [static]

Definition at line 566 of file subselect.c.

References ANY_SUBLINK, arg, SubPlan::args, ARRAY_SUBLINK, Assert, bms_add_member(), BOOLOID, convert_testexpr(), cost_subplan(), elog, enable_material, ERROR, ExecMaterializesOutput(), EXISTS_SUBLINK, TargetEntry::expr, EXPR_SUBLINK, exprCollation(), exprType(), exprTypmod(), SubPlan::firstColCollation, SubPlan::firstColType, SubPlan::firstColTypmod, format_type_be(), generate_new_param(), generate_subquery_params(), get_array_type(), get_first_col_type(), PlannerInfo::glob, PlannerInfo::init_plans, InvalidOid, IsA, PlannerParamItem::item, lappend(), lappend_int(), lfirst, lfirst_int, linitial, list_copy(), list_length(), list_make1_int, lnext, makeNode, materialize_finished_plan(), NIL, nodeTag, NULL, OidIsValid, palloc(), Param::paramid, PlannerParamItem::paramId, SubPlan::paramIds, SubPlan::parParam, SubPlan::plan_id, SubPlan::plan_name, TargetEntry::resjunk, PlannerGlobal::rewindPlanIDs, ROWCOMPARE_SUBLINK, SubPlan::setParam, splan, SS_process_sublinks(), SubPlan::subLinkType, subplan_is_hashable(), PlannerGlobal::subplans, PlannerGlobal::subroots, Plan::targetlist, SubPlan::testexpr, testexpr_is_hashable(), SubPlan::unknownEqFalse, and SubPlan::useHashTable.

Referenced by make_subplan().

{
    Node       *result;
    SubPlan    *splan;
    bool        isInitPlan;
    ListCell   *lc;

    /*
     * Initialize the SubPlan node.  Note plan_id, plan_name, and cost fields
     * are set further down.
     */
    splan = makeNode(SubPlan);
    splan->subLinkType = subLinkType;
    splan->testexpr = NULL;
    splan->paramIds = NIL;
    get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
                       &splan->firstColCollation);
    splan->useHashTable = false;
    splan->unknownEqFalse = unknownEqFalse;
    splan->setParam = NIL;
    splan->parParam = NIL;
    splan->args = NIL;

    /*
     * Make parParam and args lists of param IDs and expressions that current
     * query level will pass to this child plan.
     */
    foreach(lc, plan_params)
    {
        PlannerParamItem *pitem = (PlannerParamItem *) lfirst(lc);
        Node       *arg = pitem->item;

        /*
         * The Var, PlaceHolderVar, or Aggref has already been adjusted to
         * have the correct varlevelsup, phlevelsup, or agglevelsup.
         *
         * If it's a PlaceHolderVar or Aggref, its arguments might contain
         * SubLinks, which have not yet been processed (see the comments for
         * SS_replace_correlation_vars).  Do that now.
         */
        if (IsA(arg, PlaceHolderVar) ||
            IsA(arg, Aggref))
            arg = SS_process_sublinks(root, arg, false);

        splan->parParam = lappend_int(splan->parParam, pitem->paramId);
        splan->args = lappend(splan->args, arg);
    }

    /*
     * Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or
     * ROWCOMPARE types can be used as initPlans.  For EXISTS, EXPR, or ARRAY,
     * we just produce a Param referring to the result of evaluating the
     * initPlan.  For ROWCOMPARE, we must modify the testexpr tree to contain
     * PARAM_EXEC Params instead of the PARAM_SUBLINK Params emitted by the
     * parser.
     */
    if (splan->parParam == NIL && subLinkType == EXISTS_SUBLINK)
    {
        Param      *prm;

        Assert(testexpr == NULL);
        prm = generate_new_param(root, BOOLOID, -1, InvalidOid);
        splan->setParam = list_make1_int(prm->paramid);
        isInitPlan = true;
        result = (Node *) prm;
    }
    else if (splan->parParam == NIL && subLinkType == EXPR_SUBLINK)
    {
        TargetEntry *te = linitial(plan->targetlist);
        Param      *prm;

        Assert(!te->resjunk);
        Assert(testexpr == NULL);
        prm = generate_new_param(root,
                                 exprType((Node *) te->expr),
                                 exprTypmod((Node *) te->expr),
                                 exprCollation((Node *) te->expr));
        splan->setParam = list_make1_int(prm->paramid);
        isInitPlan = true;
        result = (Node *) prm;
    }
    else if (splan->parParam == NIL && subLinkType == ARRAY_SUBLINK)
    {
        TargetEntry *te = linitial(plan->targetlist);
        Oid         arraytype;
        Param      *prm;

        Assert(!te->resjunk);
        Assert(testexpr == NULL);
        arraytype = get_array_type(exprType((Node *) te->expr));
        if (!OidIsValid(arraytype))
            elog(ERROR, "could not find array type for datatype %s",
                 format_type_be(exprType((Node *) te->expr)));
        prm = generate_new_param(root,
                                 arraytype,
                                 exprTypmod((Node *) te->expr),
                                 exprCollation((Node *) te->expr));
        splan->setParam = list_make1_int(prm->paramid);
        isInitPlan = true;
        result = (Node *) prm;
    }
    else if (splan->parParam == NIL && subLinkType == ROWCOMPARE_SUBLINK)
    {
        /* Adjust the Params */
        List       *params;

        Assert(testexpr != NULL);
        params = generate_subquery_params(root,
                                          plan->targetlist,
                                          &splan->paramIds);
        result = convert_testexpr(root,
                                  testexpr,
                                  params);
        splan->setParam = list_copy(splan->paramIds);
        isInitPlan = true;

        /*
         * The executable expression is returned to become part of the outer
         * plan's expression tree; it is not kept in the initplan node.
         */
    }
    else
    {
        /*
         * Adjust the Params in the testexpr, unless caller said it's not
         * needed.
         */
        if (testexpr && adjust_testexpr)
        {
            List       *params;

            params = generate_subquery_params(root,
                                              plan->targetlist,
                                              &splan->paramIds);
            splan->testexpr = convert_testexpr(root,
                                               testexpr,
                                               params);
        }
        else
            splan->testexpr = testexpr;

        /*
         * 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.  But if it's a not-direct-correlated IN (= ANY) test, we
         * might be able to use a hashtable to avoid comparing all the tuples.
         */
        if (subLinkType == ANY_SUBLINK &&
            splan->parParam == NIL &&
            subplan_is_hashable(plan) &&
            testexpr_is_hashable(splan->testexpr))
            splan->useHashTable = true;

        /*
         * Otherwise, we have the option to tack a Material node onto the top
         * of the subplan, to reduce the cost of reading it repeatedly.  This
         * is pointless for a direct-correlated subplan, since we'd have to
         * recompute its results each time anyway.  For uncorrelated/undirect
         * correlated subplans, we add Material unless the subplan's top plan
         * node would materialize its output anyway.  Also, if enable_material
         * is false, then the user does not want us to materialize anything
         * unnecessarily, so we don't.
         */
        else if (splan->parParam == NIL && enable_material &&
                 !ExecMaterializesOutput(nodeTag(plan)))
            plan = materialize_finished_plan(plan);

        result = (Node *) splan;
        isInitPlan = 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, subroot);
    splan->plan_id = list_length(root->glob->subplans);

    if (isInitPlan)
        root->init_plans = lappend(root->init_plans, splan);

    /*
     * A parameterless subplan (not initplan) should be prepared to handle
     * REWIND efficiently.  If it has direct parameters then there's no point
     * since it'll be reset on each scan anyway; and if it's an initplan then
     * there's no point since it won't get re-run without parameter changes
     * anyway.  The input of a hashed subplan doesn't need REWIND either.
     */
    if (splan->parParam == NIL && !isInitPlan && !splan->useHashTable)
        root->glob->rewindPlanIDs = bms_add_member(root->glob->rewindPlanIDs,
                                                   splan->plan_id);

    /* Label the subplan for EXPLAIN purposes */
    if (isInitPlan)
    {
        ListCell   *lc;
        int         offset;

        splan->plan_name = palloc(32 + 12 * list_length(splan->setParam));
        sprintf(splan->plan_name, "InitPlan %d (returns ", splan->plan_id);
        offset = strlen(splan->plan_name);
        foreach(lc, splan->setParam)
        {
            sprintf(splan->plan_name + offset, "$%d%s",
                    lfirst_int(lc),
                    lnext(lc) ? "," : "");
            offset += strlen(splan->plan_name + offset);
        }
        sprintf(splan->plan_name + offset, ")");
    }
    else
    {
        splan->plan_name = palloc(32);
        sprintf(splan->plan_name, "SubPlan %d", splan->plan_id);
    }

    /* Lastly, fill in the cost estimates for use later */
    cost_subplan(root, splan, plan);

    return result;
}

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;
}

static Query * convert_EXISTS_to_ANY ( PlannerInfo root,
Query subselect,
Node **  testexpr,
List **  paramIds 
) [static]

Definition at line 1488 of file subselect.c.

References OpExpr::args, Assert, BOOLOID, canonicalize_qual(), contain_aggs_of_level(), contain_subplans(), contain_vars_of_level(), contain_volatile_functions(), eval_const_expressions(), exprCollation(), exprType(), exprTypmod(), forthree, generate_new_param(), get_commutator(), Query::hasAggs, hash_ok_operator(), IncrementVarSublevelsUp(), OpExpr::inputcollid, InvalidOid, IsA, Query::jointree, lappend(), lappend_int(), lappend_oid(), lfirst, lfirst_oid, linitial, list_head(), lnext, lsecond, make_ands_explicit(), make_ands_implicit(), make_opclause(), makeTargetEntry(), NIL, NULL, OidIsValid, OpExpr::opno, PlannerInfo::parse, FromExpr::quals, and Query::targetList.

Referenced by make_subplan().

{
    Node       *whereClause;
    List       *leftargs,
               *rightargs,
               *opids,
               *opcollations,
               *newWhere,
               *tlist,
               *testlist,
               *paramids;
    ListCell   *lc,
               *rc,
               *oc,
               *cc;
    AttrNumber  resno;

    /*
     * Query must not require a targetlist, since we have to insert a new one.
     * Caller should have dealt with the case already.
     */
    Assert(subselect->targetList == NIL);

    /*
     * 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.)
     *
     * Note: we need not check for Aggrefs separately because we know the
     * sub-select is as yet unoptimized; any uplevel Aggref must therefore
     * contain an uplevel Var reference.  This is not the case below ...
     */
    if (contain_vars_of_level((Node *) subselect, 1))
        return NULL;

    /*
     * We don't risk optimizing if the WHERE clause is volatile, either.
     */
    if (contain_volatile_functions(whereClause))
        return NULL;

    /*
     * Clean up the WHERE clause by doing const-simplification etc on it.
     * Aside from simplifying the processing we're about to do, this is
     * important for being able to pull chunks of the WHERE clause up into the
     * parent query.  Since we are invoked partway through the parent's
     * preprocess_expression() work, earlier steps of preprocess_expression()
     * wouldn't get applied to the pulled-up stuff unless we do them here. For
     * the parts of the WHERE clause that get put back into the child query,
     * this work is partially duplicative, but it shouldn't hurt.
     *
     * Note: we do not run flatten_join_alias_vars.  This is OK because any
     * parent aliases were flattened already, and we're not going to pull any
     * child Vars (of any description) into the parent.
     *
     * Note: passing the parent's root to eval_const_expressions is
     * technically wrong, but we can get away with it since only the
     * boundParams (if any) are used, and those would be the same in a
     * subroot.
     */
    whereClause = eval_const_expressions(root, whereClause);
    whereClause = (Node *) canonicalize_qual((Expr *) whereClause);
    whereClause = (Node *) make_ands_implicit((Expr *) whereClause);

    /*
     * We now have a flattened implicit-AND list of clauses, which we try to
     * break apart into "outervar = innervar" hash clauses. Anything that
     * can't be broken apart just goes back into the newWhere list.  Note that
     * we aren't trying hard yet to ensure that we have only outer or only
     * inner on each side; we'll check that if we get to the end.
     */
    leftargs = rightargs = opids = opcollations = newWhere = NIL;
    foreach(lc, (List *) whereClause)
    {
        OpExpr     *expr = (OpExpr *) lfirst(lc);

        if (IsA(expr, OpExpr) &&
            hash_ok_operator(expr))
        {
            Node       *leftarg = (Node *) linitial(expr->args);
            Node       *rightarg = (Node *) lsecond(expr->args);

            if (contain_vars_of_level(leftarg, 1))
            {
                leftargs = lappend(leftargs, leftarg);
                rightargs = lappend(rightargs, rightarg);
                opids = lappend_oid(opids, expr->opno);
                opcollations = lappend_oid(opcollations, expr->inputcollid);
                continue;
            }
            if (contain_vars_of_level(rightarg, 1))
            {
                /*
                 * We must commute the clause to put the outer var on the
                 * left, because the hashing code in nodeSubplan.c expects
                 * that.  This probably shouldn't ever fail, since hashable
                 * operators ought to have commutators, but be paranoid.
                 */
                expr->opno = get_commutator(expr->opno);
                if (OidIsValid(expr->opno) && hash_ok_operator(expr))
                {
                    leftargs = lappend(leftargs, rightarg);
                    rightargs = lappend(rightargs, leftarg);
                    opids = lappend_oid(opids, expr->opno);
                    opcollations = lappend_oid(opcollations, expr->inputcollid);
                    continue;
                }
                /* If no commutator, no chance to optimize the WHERE clause */
                return NULL;
            }
        }
        /* Couldn't handle it as a hash clause */
        newWhere = lappend(newWhere, expr);
    }

    /*
     * If we didn't find anything we could convert, fail.
     */
    if (leftargs == NIL)
        return NULL;

    /*
     * There mustn't be any parent Vars or Aggs in the stuff that we intend to
     * put back into the child query.  Note: you might think we don't need to
     * check for Aggs separately, because an uplevel Agg must contain an
     * uplevel Var in its argument.  But it is possible that the uplevel Var
     * got optimized away by eval_const_expressions.  Consider
     *
     * SUM(CASE WHEN false THEN uplevelvar ELSE 0 END)
     */
    if (contain_vars_of_level((Node *) newWhere, 1) ||
        contain_vars_of_level((Node *) rightargs, 1))
        return NULL;
    if (root->parse->hasAggs &&
        (contain_aggs_of_level((Node *) newWhere, 1) ||
         contain_aggs_of_level((Node *) rightargs, 1)))
        return NULL;

    /*
     * And there can't be any child Vars in the stuff we intend to pull up.
     * (Note: we'd need to check for child Aggs too, except we know the child
     * has no aggs at all because of simplify_EXISTS_query's check. The same
     * goes for window functions.)
     */
    if (contain_vars_of_level((Node *) leftargs, 0))
        return NULL;

    /*
     * Also reject sublinks in the stuff we intend to pull up.  (It might be
     * possible to support this, but doesn't seem worth the complication.)
     */
    if (contain_subplans((Node *) leftargs))
        return NULL;

    /*
     * Okay, adjust the sublevelsup in the stuff we're pulling up.
     */
    IncrementVarSublevelsUp((Node *) leftargs, -1, 1);

    /*
     * Put back any child-level-only WHERE clauses.
     */
    if (newWhere)
        subselect->jointree->quals = (Node *) make_ands_explicit(newWhere);

    /*
     * Build a new targetlist for the child that emits the expressions we
     * need.  Concurrently, build a testexpr for the parent using Params to
     * reference the child outputs.  (Since we generate Params directly here,
     * there will be no need to convert the testexpr in build_subplan.)
     */
    tlist = testlist = paramids = NIL;
    resno = 1;
    /* there's no "forfour" so we have to chase one of the lists manually */
    cc = list_head(opcollations);
    forthree(lc, leftargs, rc, rightargs, oc, opids)
    {
        Node       *leftarg = (Node *) lfirst(lc);
        Node       *rightarg = (Node *) lfirst(rc);
        Oid         opid = lfirst_oid(oc);
        Oid         opcollation = lfirst_oid(cc);
        Param      *param;

        cc = lnext(cc);
        param = generate_new_param(root,
                                   exprType(rightarg),
                                   exprTypmod(rightarg),
                                   exprCollation(rightarg));
        tlist = lappend(tlist,
                        makeTargetEntry((Expr *) rightarg,
                                        resno++,
                                        NULL,
                                        false));
        testlist = lappend(testlist,
                           make_opclause(opid, BOOLOID, false,
                                         (Expr *) leftarg, (Expr *) param,
                                         InvalidOid, opcollation));
        paramids = lappend_int(paramids, param->paramid);
    }

    /* Put everything where it should go, and we're done */
    subselect->targetList = tlist;
    *testexpr = (Node *) make_ands_explicit(testlist);
    *paramIds = paramids;

    return subselect;
}

static Node * convert_testexpr ( PlannerInfo root,
Node testexpr,
List subst_nodes 
) [static]

Definition at line 866 of file subselect.c.

References convert_testexpr_mutator(), convert_testexpr_context::root, and convert_testexpr_context::subst_nodes.

Referenced by build_subplan(), and convert_ANY_sublink_to_join().

{
    convert_testexpr_context context;

    context.root = root;
    context.subst_nodes = subst_nodes;
    return convert_testexpr_mutator(testexpr, &context);
}

static Node * convert_testexpr_mutator ( Node node,
convert_testexpr_context context 
) [static]

Definition at line 878 of file subselect.c.

References copyObject(), elog, ERROR, expression_tree_mutator(), IsA, list_length(), list_nth(), NULL, PARAM_SUBLINK, Param::paramid, Param::paramkind, and convert_testexpr_context::subst_nodes.

Referenced by convert_testexpr().

{
    if (node == NULL)
        return NULL;
    if (IsA(node, Param))
    {
        Param      *param = (Param *) node;

        if (param->paramkind == PARAM_SUBLINK)
        {
            if (param->paramid <= 0 ||
                param->paramid > list_length(context->subst_nodes))
                elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);

            /*
             * We copy the list item to avoid having doubly-linked
             * substructure in the modified parse tree.  This is probably
             * unnecessary when it's a Param, but be safe.
             */
            return (Node *) copyObject(list_nth(context->subst_nodes,
                                                param->paramid - 1));
        }
    }
    return expression_tree_mutator(node,
                                   convert_testexpr_mutator,
                                   (void *) context);
}

static Bitmapset * finalize_plan ( PlannerInfo root,
Plan plan,
Bitmapset valid_params,
Bitmapset scan_params 
) [static]

Definition at line 2036 of file subselect.c.

References Plan::allParam, bms_add_member(), bms_add_members(), bms_copy(), bms_del_member(), bms_difference(), bms_free(), bms_is_empty(), bms_is_subset(), bms_union(), elog, ModifyTable::epqParam, ERROR, Plan::extParam, finalize_primnode(), PlannerInfo::glob, Plan::lefttree, lfirst, list_length(), list_nth(), nodeTag, NULL, finalize_primnode_context::paramids, NestLoopParam::paramno, ModifyTable::plans, Plan::qual, ModifyTable::returningLists, Plan::righttree, finalize_primnode_context::root, PlannerGlobal::subplans, T_Agg, T_Append, T_BitmapAnd, T_BitmapHeapScan, T_BitmapIndexScan, T_BitmapOr, T_CteScan, T_ForeignScan, T_FunctionScan, T_Group, T_Hash, T_HashJoin, T_IndexOnlyScan, T_IndexScan, T_Limit, T_LockRows, T_Material, T_MergeAppend, T_MergeJoin, T_ModifyTable, T_NestLoop, T_RecursiveUnion, T_Result, T_SeqScan, T_SetOp, T_Sort, T_SubqueryScan, T_TidScan, T_Unique, T_ValuesScan, T_WindowAgg, T_WorkTableScan, and Plan::targetlist.

Referenced by SS_finalize_plan().

{
    finalize_primnode_context context;
    int         locally_added_param;
    Bitmapset  *nestloop_params;
    Bitmapset  *child_params;

    if (plan == NULL)
        return NULL;

    context.root = root;
    context.paramids = NULL;    /* initialize set to empty */
    locally_added_param = -1;   /* there isn't one */
    nestloop_params = NULL;     /* there aren't any */

    /*
     * When we call finalize_primnode, context.paramids sets are automatically
     * merged together.  But when recursing to self, we have to do it the hard
     * way.  We want the paramids set to include params in subplans as well as
     * at this level.
     */

    /* Find params in targetlist and qual */
    finalize_primnode((Node *) plan->targetlist, &context);
    finalize_primnode((Node *) plan->qual, &context);

    /* Check additional node-type-specific fields */
    switch (nodeTag(plan))
    {
        case T_Result:
            finalize_primnode(((Result *) plan)->resconstantqual,
                              &context);
            break;

        case T_SeqScan:
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_IndexScan:
            finalize_primnode((Node *) ((IndexScan *) plan)->indexqual,
                              &context);
            finalize_primnode((Node *) ((IndexScan *) plan)->indexorderby,
                              &context);

            /*
             * we need not look at indexqualorig, since it will have the same
             * param references as indexqual.  Likewise, we can ignore
             * indexorderbyorig.
             */
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_IndexOnlyScan:
            finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexqual,
                              &context);
            finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexorderby,
                              &context);

            /*
             * we need not look at indextlist, since it cannot contain Params.
             */
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_BitmapIndexScan:
            finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual,
                              &context);

            /*
             * we need not look at indexqualorig, since it will have the same
             * param references as indexqual.
             */
            break;

        case T_BitmapHeapScan:
            finalize_primnode((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
                              &context);
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_TidScan:
            finalize_primnode((Node *) ((TidScan *) plan)->tidquals,
                              &context);
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_SubqueryScan:

            /*
             * 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.
             */
            context.paramids = bms_add_members(context.paramids,
                                 ((SubqueryScan *) plan)->subplan->extParam);
            /* We need scan_params too, though */
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_FunctionScan:
            finalize_primnode(((FunctionScan *) plan)->funcexpr,
                              &context);
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_ValuesScan:
            finalize_primnode((Node *) ((ValuesScan *) plan)->values_lists,
                              &context);
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_CteScan:
            {
                /*
                 * You might think we should add the node's cteParam to
                 * paramids, but we shouldn't because that param is just a
                 * linkage mechanism for multiple CteScan nodes for the same
                 * CTE; it is never used for changed-param signaling.  What we
                 * have to do instead is to find the referenced CTE plan and
                 * incorporate its external paramids, so that the correct
                 * things will happen if the CTE references outer-level
                 * variables.  See test cases for bug #4902.
                 */
                int         plan_id = ((CteScan *) plan)->ctePlanId;
                Plan       *cteplan;

                /* so, do this ... */
                if (plan_id < 1 || plan_id > list_length(root->glob->subplans))
                    elog(ERROR, "could not find plan for CteScan referencing plan ID %d",
                         plan_id);
                cteplan = (Plan *) list_nth(root->glob->subplans, plan_id - 1);
                context.paramids =
                    bms_add_members(context.paramids, cteplan->extParam);

#ifdef NOT_USED
                /* ... but not this */
                context.paramids =
                    bms_add_member(context.paramids,
                                   ((CteScan *) plan)->cteParam);
#endif

                context.paramids = bms_add_members(context.paramids,
                                                   scan_params);
            }
            break;

        case T_WorkTableScan:
            context.paramids =
                bms_add_member(context.paramids,
                               ((WorkTableScan *) plan)->wtParam);
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_ForeignScan:
            finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
                              &context);
            context.paramids = bms_add_members(context.paramids, scan_params);
            break;

        case T_ModifyTable:
            {
                ModifyTable *mtplan = (ModifyTable *) plan;
                ListCell   *l;

                /* Force descendant scan nodes to reference epqParam */
                locally_added_param = mtplan->epqParam;
                valid_params = bms_add_member(bms_copy(valid_params),
                                              locally_added_param);
                scan_params = bms_add_member(bms_copy(scan_params),
                                             locally_added_param);
                finalize_primnode((Node *) mtplan->returningLists,
                                  &context);
                foreach(l, mtplan->plans)
                {
                    context.paramids =
                        bms_add_members(context.paramids,
                                        finalize_plan(root,
                                                      (Plan *) lfirst(l),
                                                      valid_params,
                                                      scan_params));
                }
            }
            break;

        case T_Append:
            {
                ListCell   *l;

                foreach(l, ((Append *) plan)->appendplans)
                {
                    context.paramids =
                        bms_add_members(context.paramids,
                                        finalize_plan(root,
                                                      (Plan *) lfirst(l),
                                                      valid_params,
                                                      scan_params));
                }
            }
            break;

        case T_MergeAppend:
            {
                ListCell   *l;

                foreach(l, ((MergeAppend *) plan)->mergeplans)
                {
                    context.paramids =
                        bms_add_members(context.paramids,
                                        finalize_plan(root,
                                                      (Plan *) lfirst(l),
                                                      valid_params,
                                                      scan_params));
                }
            }
            break;

        case T_BitmapAnd:
            {
                ListCell   *l;

                foreach(l, ((BitmapAnd *) plan)->bitmapplans)
                {
                    context.paramids =
                        bms_add_members(context.paramids,
                                        finalize_plan(root,
                                                      (Plan *) lfirst(l),
                                                      valid_params,
                                                      scan_params));
                }
            }
            break;

        case T_BitmapOr:
            {
                ListCell   *l;

                foreach(l, ((BitmapOr *) plan)->bitmapplans)
                {
                    context.paramids =
                        bms_add_members(context.paramids,
                                        finalize_plan(root,
                                                      (Plan *) lfirst(l),
                                                      valid_params,
                                                      scan_params));
                }
            }
            break;

        case T_NestLoop:
            {
                ListCell   *l;

                finalize_primnode((Node *) ((Join *) plan)->joinqual,
                                  &context);
                /* collect set of params that will be passed to right child */
                foreach(l, ((NestLoop *) plan)->nestParams)
                {
                    NestLoopParam *nlp = (NestLoopParam *) lfirst(l);

                    nestloop_params = bms_add_member(nestloop_params,
                                                     nlp->paramno);
                }
            }
            break;

        case T_MergeJoin:
            finalize_primnode((Node *) ((Join *) plan)->joinqual,
                              &context);
            finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
                              &context);
            break;

        case T_HashJoin:
            finalize_primnode((Node *) ((Join *) plan)->joinqual,
                              &context);
            finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
                              &context);
            break;

        case T_Limit:
            finalize_primnode(((Limit *) plan)->limitOffset,
                              &context);
            finalize_primnode(((Limit *) plan)->limitCount,
                              &context);
            break;

        case T_RecursiveUnion:
            /* child nodes are allowed to reference wtParam */
            locally_added_param = ((RecursiveUnion *) plan)->wtParam;
            valid_params = bms_add_member(bms_copy(valid_params),
                                          locally_added_param);
            /* wtParam does *not* get added to scan_params */
            break;

        case T_LockRows:
            /* Force descendant scan nodes to reference epqParam */
            locally_added_param = ((LockRows *) plan)->epqParam;
            valid_params = bms_add_member(bms_copy(valid_params),
                                          locally_added_param);
            scan_params = bms_add_member(bms_copy(scan_params),
                                         locally_added_param);
            break;

        case T_WindowAgg:
            finalize_primnode(((WindowAgg *) plan)->startOffset,
                              &context);
            finalize_primnode(((WindowAgg *) plan)->endOffset,
                              &context);
            break;

        case T_Hash:
        case T_Agg:
        case T_Material:
        case T_Sort:
        case T_Unique:
        case T_SetOp:
        case T_Group:
            break;

        default:
            elog(ERROR, "unrecognized node type: %d",
                 (int) nodeTag(plan));
    }

    /* Process left and right child plans, if any */
    child_params = finalize_plan(root,
                                 plan->lefttree,
                                 valid_params,
                                 scan_params);
    context.paramids = bms_add_members(context.paramids, child_params);

    if (nestloop_params)
    {
        /* right child can reference nestloop_params as well as valid_params */
        child_params = finalize_plan(root,
                                     plan->righttree,
                                     bms_union(nestloop_params, valid_params),
                                     scan_params);
        /* ... and they don't count as parameters used at my level */
        child_params = bms_difference(child_params, nestloop_params);
        bms_free(nestloop_params);
    }
    else
    {
        /* easy case */
        child_params = finalize_plan(root,
                                     plan->righttree,
                                     valid_params,
                                     scan_params);
    }
    context.paramids = bms_add_members(context.paramids, child_params);

    /*
     * Any locally generated parameter doesn't count towards its generating
     * plan node's external dependencies.  (Note: if we changed valid_params
     * and/or scan_params, we leak those bitmapsets; not worth the notational
     * trouble to clean them up.)
     */
    if (locally_added_param >= 0)
    {
        context.paramids = bms_del_member(context.paramids,
                                          locally_added_param);
    }

    /* Now we have all the paramids */

    if (!bms_is_subset(context.paramids, valid_params))
        elog(ERROR, "plan should not reference subplan's variable");

    /*
     * Note: by definition, extParam and allParam should have the same value
     * in any plan node that doesn't have child initPlans.  We set them equal
     * here, and later SS_finalize_plan will update them properly in node(s)
     * that it attaches initPlans to.
     *
     * For speed at execution time, make sure extParam/allParam are actually
     * NULL if they are empty sets.
     */
    if (bms_is_empty(context.paramids))
    {
        plan->extParam = NULL;
        plan->allParam = NULL;
    }
    else
    {
        plan->extParam = context.paramids;
        plan->allParam = bms_copy(context.paramids);
    }

    return plan->allParam;
}

static bool finalize_primnode ( Node node,
finalize_primnode_context context 
) [static]

Definition at line 2436 of file subselect.c.

References SubPlan::args, bms_add_member(), bms_copy(), bms_del_member(), bms_join(), expression_tree_walker(), Plan::extParam, IsA, lfirst_int, NULL, PARAM_EXEC, SubPlan::paramIds, finalize_primnode_context::paramids, SubPlan::parParam, planner_subplan_get_plan, finalize_primnode_context::root, and SubPlan::testexpr.

Referenced by finalize_plan().

{
    if (node == NULL)
        return false;
    if (IsA(node, Param))
    {
        if (((Param *) node)->paramkind == PARAM_EXEC)
        {
            int         paramid = ((Param *) node)->paramid;

            context->paramids = bms_add_member(context->paramids, paramid);
        }
        return false;           /* no more to do here */
    }
    if (IsA(node, SubPlan))
    {
        SubPlan    *subplan = (SubPlan *) node;
        Plan       *plan = planner_subplan_get_plan(context->root, subplan);
        ListCell   *lc;
        Bitmapset  *subparamids;

        /* Recurse into the testexpr, but not into the Plan */
        finalize_primnode(subplan->testexpr, context);

        /*
         * Remove any param IDs of output parameters of the subplan that were
         * referenced in the testexpr.  These are not interesting for
         * parameter change signaling since we always re-evaluate the subplan.
         * Note that this wouldn't work too well if there might be uses of the
         * same param IDs elsewhere in the plan, but that can't happen because
         * generate_new_param never tries to merge params.
         */
        foreach(lc, subplan->paramIds)
        {
            context->paramids = bms_del_member(context->paramids,
                                               lfirst_int(lc));
        }

        /* Also examine args list */
        finalize_primnode((Node *) subplan->args, context);

        /*
         * Add params needed by the subplan to paramids, but excluding those
         * we will pass down to it.
         */
        subparamids = bms_copy(plan->extParam);
        foreach(lc, subplan->parParam)
        {
            subparamids = bms_del_member(subparamids, lfirst_int(lc));
        }
        context->paramids = bms_join(context->paramids, subparamids);

        return false;           /* no more to do here */
    }
    return expression_tree_walker(node, finalize_primnode,
                                  (void *) context);
}

static Param* generate_new_param ( PlannerInfo root,
Oid  paramtype,
int32  paramtypmod,
Oid  paramcollation 
) [static]

Definition at line 345 of file subselect.c.

References PlannerInfo::glob, Param::location, makeNode, PlannerGlobal::nParamExec, Param::paramcollid, Param::paramid, Param::paramkind, Param::paramtype, and Param::paramtypmod.

Referenced by build_subplan(), convert_EXISTS_to_ANY(), generate_subquery_params(), and SS_make_initplan_from_plan().

{
    Param      *retval;

    retval = makeNode(Param);
    retval->paramkind = PARAM_EXEC;
    retval->paramid = root->glob->nParamExec++;
    retval->paramtype = paramtype;
    retval->paramtypmod = paramtypmod;
    retval->paramcollid = paramcollation;
    retval->location = -1;

    return retval;
}

static List * generate_subquery_params ( PlannerInfo root,
List tlist,
List **  paramIds 
) [static]

Definition at line 799 of file subselect.c.

References TargetEntry::expr, exprCollation(), exprType(), exprTypmod(), generate_new_param(), lappend(), lappend_int(), lfirst, Param::paramid, and TargetEntry::resjunk.

Referenced by build_subplan().

{
    List       *result;
    List       *ids;
    ListCell   *lc;

    result = ids = NIL;
    foreach(lc, tlist)
    {
        TargetEntry *tent = (TargetEntry *) lfirst(lc);
        Param      *param;

        if (tent->resjunk)
            continue;

        param = generate_new_param(root,
                                   exprType((Node *) tent->expr),
                                   exprTypmod((Node *) tent->expr),
                                   exprCollation((Node *) tent->expr));
        result = lappend(result, param);
        ids = lappend_int(ids, param->paramid);
    }

    *paramIds = ids;
    return result;
}

static List * generate_subquery_vars ( PlannerInfo root,
List tlist,
Index  varno 
) [static]

Definition at line 832 of file subselect.c.

References lappend(), lfirst, makeVarFromTargetEntry(), and TargetEntry::resjunk.

Referenced by convert_ANY_sublink_to_join().

{
    List       *result;
    ListCell   *lc;

    result = NIL;
    foreach(lc, tlist)
    {
        TargetEntry *tent = (TargetEntry *) lfirst(lc);
        Var        *var;

        if (tent->resjunk)
            continue;

        var = makeVarFromTargetEntry(varno, tent);
        result = lappend(result, var);
    }

    return result;
}

static void get_first_col_type ( Plan plan,
Oid coltype,
int32 coltypmod,
Oid colcollation 
) [static]

Definition at line 385 of file subselect.c.

References Assert, TargetEntry::expr, exprCollation(), exprType(), exprTypmod(), IsA, linitial, TargetEntry::resjunk, and Plan::targetlist.

Referenced by build_subplan(), SS_make_initplan_from_plan(), and SS_process_ctes().

{
    /* In cases such as EXISTS, tlist might be empty; arbitrarily use VOID */
    if (plan->targetlist)
    {
        TargetEntry *tent = (TargetEntry *) linitial(plan->targetlist);

        Assert(IsA(tent, TargetEntry));
        if (!tent->resjunk)
        {
            *coltype = exprType((Node *) tent->expr);
            *coltypmod = exprTypmod((Node *) tent->expr);
            *colcollation = exprCollation((Node *) tent->expr);
            return;
        }
    }
    *coltype = VOIDOID;
    *coltypmod = -1;
    *colcollation = InvalidOid;
}

static bool hash_ok_operator ( OpExpr expr  )  [static]

Definition at line 977 of file subselect.c.

References OpExpr::args, ARRAY_EQ_OP, elog, ERROR, exprType(), func_strict(), GETSTRUCT, HeapTupleIsValid, linitial, list_length(), ObjectIdGetDatum, op_hashjoinable(), OPEROID, OpExpr::opno, ReleaseSysCache(), and SearchSysCache1.

Referenced by convert_EXISTS_to_ANY(), and testexpr_is_hashable().

{
    Oid         opid = expr->opno;

    /* quick out if not a binary operator */
    if (list_length(expr->args) != 2)
        return false;
    if (opid == ARRAY_EQ_OP)
    {
        /* array_eq is strict, but must check input type to ensure hashable */
        /* XXX record_eq will need same treatment when it becomes hashable */
        Node       *leftarg = linitial(expr->args);

        return op_hashjoinable(opid, exprType(leftarg));
    }
    else
    {
        /* else must look up the operator properties */
        HeapTuple   tup;
        Form_pg_operator optup;

        tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opid));
        if (!HeapTupleIsValid(tup))
            elog(ERROR, "cache lookup failed for operator %u", opid);
        optup = (Form_pg_operator) GETSTRUCT(tup);
        if (!optup->oprcanhash || !func_strict(optup->oprcode))
        {
            ReleaseSysCache(tup);
            return false;
        }
        ReleaseSysCache(tup);
        return true;
    }
}

static Node* make_subplan ( PlannerInfo root,
Query orig_subquery,
SubLinkType  subLinkType,
Node testexpr,
bool  isTopQual 
) [static]

Definition at line 425 of file subselect.c.

References ALL_SUBLINK, ANY_SUBLINK, Assert, build_subplan(), convert_EXISTS_to_ANY(), copyObject(), EXISTS_SUBLINK, PlannerInfo::glob, IsA, list_make2, makeNode, NIL, SubPlan::paramIds, SubPlan::parParam, PlannerInfo::plan_params, simplify_EXISTS_query(), subplan_is_hashable(), AlternativeSubPlan::subplans, subquery_planner(), and SubPlan::useHashTable.

Referenced by process_sublinks_mutator().

{
    Query      *subquery;
    bool        simple_exists = false;
    double      tuple_fraction;
    Plan       *plan;
    PlannerInfo *subroot;
    List       *plan_params;
    Node       *result;

    /*
     * 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...
     */
    subquery = (Query *) copyObject(orig_subquery);

    /*
     * If it's an EXISTS subplan, we might be able to simplify it.
     */
    if (subLinkType == EXISTS_SUBLINK)
        simple_exists = simplify_EXISTS_query(subquery);

    /*
     * 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 or matches, so very
     * often not all the tuples will be retrieved; for lack of a better idea,
     * specify 50% retrieval.  For EXPR and ROWCOMPARE subplans, use default
     * behavior (we're only expecting one row out, anyway).
     *
     * NOTE: if you change these numbers, also change cost_subplan() in
     * path/costsize.c.
     *
     * XXX If an ANY subplan is uncorrelated, build_subplan may decide to hash
     * its output.  In that case it would've been better to specify full
     * retrieval.  At present, however, we can only check hashability after
     * we've made the subplan :-(.  (Determining whether it'll fit in work_mem
     * is the really hard part.)  Therefore, 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.
     */
    if (subLinkType == EXISTS_SUBLINK)
        tuple_fraction = 1.0;   /* just like a LIMIT 1 */
    else if (subLinkType == ALL_SUBLINK ||
             subLinkType == ANY_SUBLINK)
        tuple_fraction = 0.5;   /* 50% */
    else
        tuple_fraction = 0.0;   /* default behavior */

    /* plan_params should not be in use in current query level */
    Assert(root->plan_params == NIL);

    /*
     * Generate the plan for the subquery.
     */
    plan = subquery_planner(root->glob, subquery,
                            root,
                            false, tuple_fraction,
                            &subroot);

    /* Isolate the params needed by this specific subplan */
    plan_params = root->plan_params;
    root->plan_params = NIL;

    /* And convert to SubPlan or InitPlan format. */
    result = build_subplan(root, plan, subroot, plan_params,
                           subLinkType, testexpr, true, isTopQual);

    /*
     * If it's a correlated EXISTS with an unimportant targetlist, we might be
     * able to transform it to the equivalent of an IN and then implement it
     * by hashing.  We don't have enough information yet to tell which way is
     * likely to be better (it depends on the expected number of executions of
     * the EXISTS qual, and we are much too early in planning the outer query
     * to be able to guess that).  So we generate both plans, if possible, and
     * leave it to the executor to decide which to use.
     */
    if (simple_exists && IsA(result, SubPlan))
    {
        Node       *newtestexpr;
        List       *paramIds;

        /* Make a second copy of the original subquery */
        subquery = (Query *) copyObject(orig_subquery);
        /* and re-simplify */
        simple_exists = simplify_EXISTS_query(subquery);
        Assert(simple_exists);
        /* See if it can be converted to an ANY query */
        subquery = convert_EXISTS_to_ANY(root, subquery,
                                         &newtestexpr, &paramIds);
        if (subquery)
        {
            /* Generate the plan for the ANY subquery; we'll need all rows */
            plan = subquery_planner(root->glob, subquery,
                                    root,
                                    false, 0.0,
                                    &subroot);

            /* Isolate the params needed by this specific subplan */
            plan_params = root->plan_params;
            root->plan_params = NIL;

            /* Now we can check if it'll fit in work_mem */
            if (subplan_is_hashable(plan))
            {
                SubPlan    *hashplan;
                AlternativeSubPlan *asplan;

                /* OK, convert to SubPlan format. */
                hashplan = (SubPlan *) build_subplan(root, plan, subroot,
                                                     plan_params,
                                                     ANY_SUBLINK, newtestexpr,
                                                     false, true);
                /* Check we got what we expected */
                Assert(IsA(hashplan, SubPlan));
                Assert(hashplan->parParam == NIL);
                Assert(hashplan->useHashTable);
                /* build_subplan won't have filled in paramIds */
                hashplan->paramIds = paramIds;

                /* Leave it to the executor to decide which plan to use */
                asplan = makeNode(AlternativeSubPlan);
                asplan->subplans = list_make2(result, hashplan);
                result = (Node *) asplan;
            }
        }
    }

    return result;
}

static Node * process_sublinks_mutator ( Node node,
process_sublinks_context context 
) [static]

Definition at line 1780 of file subselect.c.

References and_clause(), Assert, expression_tree_mutator(), IsA, process_sublinks_context::isTopQual, lappend(), lfirst, list_concat(), make_andclause(), make_orclause(), make_subplan(), NULL, or_clause(), process_sublinks_context::root, SubLink::subLinkType, SubLink::subselect, and SubLink::testexpr.

Referenced by SS_process_sublinks().

{
    process_sublinks_context locContext;

    locContext.root = context->root;

    if (node == NULL)
        return NULL;
    if (IsA(node, SubLink))
    {
        SubLink    *sublink = (SubLink *) node;
        Node       *testexpr;

        /*
         * First, recursively process the lefthand-side expressions, if any.
         * They're not top-level anymore.
         */
        locContext.isTopQual = false;
        testexpr = process_sublinks_mutator(sublink->testexpr, &locContext);

        /*
         * Now build the SubPlan node and make the expr to return.
         */
        return make_subplan(context->root,
                            (Query *) sublink->subselect,
                            sublink->subLinkType,
                            testexpr,
                            context->isTopQual);
    }

    /*
     * Don't recurse into the arguments of an outer PHV or aggregate here. Any
     * SubLinks in the arguments have to be dealt with at the outer query
     * level; they'll be handled when build_subplan collects the PHV or Aggref
     * into the arguments to be passed down to the current subplan.
     */
    if (IsA(node, PlaceHolderVar))
    {
        if (((PlaceHolderVar *) node)->phlevelsup > 0)
            return node;
    }
    else if (IsA(node, Aggref))
    {
        if (((Aggref *) node)->agglevelsup > 0)
            return node;
    }

    /*
     * We should never see a SubPlan expression in the input (since this is
     * the very routine that creates 'em to begin with).  We shouldn't find
     * ourselves invoked directly on a Query, either.
     */
    Assert(!IsA(node, SubPlan));
    Assert(!IsA(node, AlternativeSubPlan));
    Assert(!IsA(node, Query));

    /*
     * Because make_subplan() could return an AND or OR clause, we have to
     * take steps to preserve AND/OR flatness of a qual.  We assume the input
     * has been AND/OR flattened and so we need no recursion here.
     *
     * (Due to the coding here, we will not get called on the List subnodes of
     * an AND; and the input is *not* yet in implicit-AND format.  So no check
     * is needed for a bare List.)
     *
     * Anywhere within the top-level AND/OR clause structure, we can tell
     * make_subplan() that NULL and FALSE are interchangeable.  So isTopQual
     * propagates down in both cases.  (Note that this is unlike the meaning
     * of "top level qual" used in most other places in Postgres.)
     */
    if (and_clause(node))
    {
        List       *newargs = NIL;
        ListCell   *l;

        /* Still at qual top-level */
        locContext.isTopQual = context->isTopQual;

        foreach(l, ((BoolExpr *) node)->args)
        {
            Node       *newarg;

            newarg = process_sublinks_mutator(lfirst(l), &locContext);
            if (and_clause(newarg))
                newargs = list_concat(newargs, ((BoolExpr *) newarg)->args);
            else
                newargs = lappend(newargs, newarg);
        }
        return (Node *) make_andclause(newargs);
    }

    if (or_clause(node))
    {
        List       *newargs = NIL;
        ListCell   *l;

        /* Still at qual top-level */
        locContext.isTopQual = context->isTopQual;

        foreach(l, ((BoolExpr *) node)->args)
        {
            Node       *newarg;

            newarg = process_sublinks_mutator(lfirst(l), &locContext);
            if (or_clause(newarg))
                newargs = list_concat(newargs, ((BoolExpr *) newarg)->args);
            else
                newargs = lappend(newargs, newarg);
        }
        return (Node *) make_orclause(newargs);
    }

    /*
     * If we recurse down through anything other than an AND or OR node, we
     * are definitely not at top qual level anymore.
     */
    locContext.isTopQual = false;

    return expression_tree_mutator(node,
                                   process_sublinks_mutator,
                                   (void *) &locContext);
}

static Node * replace_correlation_vars_mutator ( Node node,
PlannerInfo root 
) [static]

Definition at line 1737 of file subselect.c.

References expression_tree_mutator(), IsA, NULL, replace_outer_agg(), replace_outer_placeholdervar(), and replace_outer_var().

Referenced by SS_replace_correlation_vars().

{
    if (node == NULL)
        return NULL;
    if (IsA(node, Var))
    {
        if (((Var *) node)->varlevelsup > 0)
            return (Node *) replace_outer_var(root, (Var *) node);
    }
    if (IsA(node, PlaceHolderVar))
    {
        if (((PlaceHolderVar *) node)->phlevelsup > 0)
            return (Node *) replace_outer_placeholdervar(root,
                                                    (PlaceHolderVar *) node);
    }
    if (IsA(node, Aggref))
    {
        if (((Aggref *) node)->agglevelsup > 0)
            return (Node *) replace_outer_agg(root, (Aggref *) node);
    }
    return expression_tree_mutator(node,
                                   replace_correlation_vars_mutator,
                                   (void *) root);
}

static Param* replace_outer_agg ( PlannerInfo root,
Aggref agg 
) [static]

Definition at line 300 of file subselect.c.

References Aggref::agglevelsup, Assert, copyObject(), PlannerInfo::glob, IncrementVarSublevelsUp(), PlannerParamItem::item, lappend(), Param::location, makeNode, PlannerGlobal::nParamExec, Param::paramcollid, Param::paramid, PlannerParamItem::paramId, Param::paramkind, Param::paramtype, Param::paramtypmod, PlannerInfo::parent_root, PlannerInfo::plan_params, and PlannerInfo::query_level.

Referenced by replace_correlation_vars_mutator().

{
    Param      *retval;
    PlannerParamItem *pitem;
    Index       levelsup;

    Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);

    /* Find the query level the Aggref belongs to */
    for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
        root = root->parent_root;

    /*
     * It does not seem worthwhile to try to match duplicate outer aggs. Just
     * make a new slot every time.
     */
    agg = (Aggref *) copyObject(agg);
    IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
    Assert(agg->agglevelsup == 0);

    pitem = makeNode(PlannerParamItem);
    pitem->item = (Node *) agg;
    pitem->paramId = root->glob->nParamExec++;

    root->plan_params = lappend(root->plan_params, pitem);

    retval = makeNode(Param);
    retval->paramkind = PARAM_EXEC;
    retval->paramid = pitem->paramId;
    retval->paramtype = agg->aggtype;
    retval->paramtypmod = -1;
    retval->paramcollid = agg->aggcollid;
    retval->location = agg->location;

    return retval;
}

static Param* replace_outer_placeholdervar ( PlannerInfo root,
PlaceHolderVar phv 
) [static]

Definition at line 247 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, PlaceHolderVar::phlevelsup, and PlannerInfo::query_level.

Referenced by replace_correlation_vars_mutator().

{
    Param      *retval;
    int         i;

    Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level);

    /* Find the PHV in the appropriate plan_params, or add it if not present */
    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;
}

static Param* replace_outer_var ( PlannerInfo root,
Var var 
) [static]

Definition at line 142 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, PlannerInfo::query_level, Var::varcollid, Var::varlevelsup, Var::vartype, and Var::vartypmod.

Referenced by replace_correlation_vars_mutator().

{
    Param      *retval;
    int         i;

    Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);

    /* Find the Var in the appropriate plan_params, or add it if not present */
    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;
}

static bool simplify_EXISTS_query ( Query query  )  [static]

Definition at line 1430 of file subselect.c.

References CMD_SELECT, Query::commandType, Query::distinctClause, expression_returns_set(), Query::groupClause, Query::hasAggs, Query::hasDistinctOn, Query::hasModifyingCTE, Query::hasWindowFuncs, Query::havingQual, Query::limitCount, Query::limitOffset, Query::rowMarks, Query::setOperations, Query::sortClause, Query::targetList, and Query::windowClause.

Referenced by convert_EXISTS_sublink_to_join(), and make_subplan().

{
    /*
     * We don't try to simplify at all if the query uses set operations,
     * aggregates, modifying CTEs, HAVING, LIMIT/OFFSET, or FOR UPDATE/SHARE;
     * none of these seem likely in normal usage and their possible effects
     * are complex.
     */
    if (query->commandType != CMD_SELECT ||
        query->setOperations ||
        query->hasAggs ||
        query->hasWindowFuncs ||
        query->hasModifyingCTE ||
        query->havingQual ||
        query->limitOffset ||
        query->limitCount ||
        query->rowMarks)
        return false;

    /*
     * Mustn't throw away the targetlist if it contains set-returning
     * functions; those could affect whether zero rows are returned!
     */
    if (expression_returns_set((Node *) query->targetList))
        return false;

    /*
     * Otherwise, we can throw away the targetlist, as well as any GROUP,
     * WINDOW, DISTINCT, and ORDER BY clauses; none of those clauses will
     * change a nonzero-rows result to zero rows or vice versa.  (Furthermore,
     * since our parsetree representation of these clauses depends on the
     * targetlist, we'd better throw them away if we drop the targetlist.)
     */
    query->targetList = NIL;
    query->groupClause = NIL;
    query->windowClause = NIL;
    query->distinctClause = NIL;
    query->sortClause = NIL;
    query->hasDistinctOn = false;

    return true;
}

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);
}

static bool subplan_is_hashable ( Plan plan  )  [static]

Definition at line 911 of file subselect.c.

References MAXALIGN, Plan::plan_rows, Plan::plan_width, and work_mem.

Referenced by build_subplan(), and make_subplan().

{
    double      subquery_size;

    /*
     * The estimated size of the subquery result must fit in work_mem. (Note:
     * we use sizeof(HeapTupleHeaderData) here even though the tuples will
     * actually be stored as MinimalTuples; this provides some fudge factor
     * for hashtable overhead.)
     */
    subquery_size = plan->plan_rows *
        (MAXALIGN(plan->plan_width) + MAXALIGN(sizeof(HeapTupleHeaderData)));
    if (subquery_size > work_mem * 1024L)
        return false;

    return true;
}

static bool testexpr_is_hashable ( Node testexpr  )  [static]

Definition at line 933 of file subselect.c.

References and_clause(), hash_ok_operator(), IsA, and lfirst.

Referenced by build_subplan().

{
    /*
     * The testexpr must be a single OpExpr, or an AND-clause containing only
     * OpExprs.
     *
     * The combining operators must be hashable and strict. The need for
     * hashability is obvious, since we want to use hashing. Without
     * strictness, behavior in the presence of nulls is too unpredictable.  We
     * actually must assume even more than plain strictness: they can't yield
     * NULL for non-null inputs, either (see nodeSubplan.c).  However, hash
     * indexes and hash joins assume that too.
     */
    if (testexpr && IsA(testexpr, OpExpr))
    {
        if (hash_ok_operator((OpExpr *) testexpr))
            return true;
    }
    else if (and_clause(testexpr))
    {
        ListCell   *l;

        foreach(l, ((BoolExpr *) testexpr)->args)
        {
            Node       *andarg = (Node *) lfirst(l);

            if (!IsA(andarg, OpExpr))
                return false;
            if (!hash_ok_operator((OpExpr *) andarg))
                return false;
        }
        return true;
    }

    return false;
}