Header And Logo

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

Typedefs | Functions | Variables

planner.h File Reference

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

Go to the source code of this file.

Typedefs

typedef PlannedStmt *(* planner_hook_type )(Query *parse, int cursorOptions, ParamListInfo boundParams)

Functions

PlannedStmtplanner (Query *parse, int cursorOptions, ParamListInfo boundParams)
PlannedStmtstandard_planner (Query *parse, int cursorOptions, ParamListInfo boundParams)
Plansubquery_planner (PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root, bool hasRecursion, double tuple_fraction, PlannerInfo **subroot)
void add_tlist_costs_to_plan (PlannerInfo *root, Plan *plan, List *tlist)
bool is_dummy_plan (Plan *plan)
Exprexpression_planner (Expr *expr)
Exprpreprocess_phv_expression (PlannerInfo *root, Expr *expr)
bool plan_cluster_use_sort (Oid tableOid, Oid indexOid)

Variables

PGDLLIMPORT planner_hook_type planner_hook

Typedef Documentation

typedef PlannedStmt*(* planner_hook_type)(Query *parse, int cursorOptions, ParamListInfo boundParams)

Definition at line 22 of file planner.h.


Function Documentation

void add_tlist_costs_to_plan ( PlannerInfo root,
Plan plan,
List tlist 
)

Definition at line 1811 of file planner.c.

References cost_qual_eval(), cpu_tuple_cost, QualCost::per_tuple, Plan::plan_rows, QualCost::startup, Plan::startup_cost, tlist_returns_set_rows(), and Plan::total_cost.

Referenced by grouping_planner(), make_agg(), make_group(), make_windowagg(), and optimize_minmax_aggregates().

{
    QualCost    tlist_cost;
    double      tlist_rows;

    cost_qual_eval(&tlist_cost, tlist, root);
    plan->startup_cost += tlist_cost.startup;
    plan->total_cost += tlist_cost.startup +
        tlist_cost.per_tuple * plan->plan_rows;

    tlist_rows = tlist_returns_set_rows(tlist);
    if (tlist_rows > 1)
    {
        /*
         * We assume that execution costs of the tlist proper were all
         * accounted for by cost_qual_eval.  However, it still seems
         * appropriate to charge something more for the executor's general
         * costs of processing the added tuples.  The cost is probably less
         * than cpu_tuple_cost, though, so we arbitrarily use half of that.
         */
        plan->total_cost += plan->plan_rows * (tlist_rows - 1) *
            cpu_tuple_cost / 2;

        plan->plan_rows *= tlist_rows;
    }
}

Expr* expression_planner ( Expr expr  ) 

Definition at line 3414 of file planner.c.

References eval_const_expressions(), fix_opfuncids(), and NULL.

Referenced by ATExecAddColumn(), ATPrepAlterColumnType(), BeginCopyFrom(), CheckMutability(), ExecPrepareExpr(), and GetDomainConstraints().

{
    Node       *result;

    /*
     * Convert named-argument function calls, insert default arguments and
     * simplify constant subexprs
     */
    result = eval_const_expressions(NULL, (Node *) expr);

    /* Fill in opfuncid values if missing */
    fix_opfuncids(result);

    return (Expr *) result;
}

bool is_dummy_plan ( Plan plan  ) 

Definition at line 1848 of file planner.c.

References Const::constisnull, Const::constvalue, DatumGetBool, IsA, linitial, and list_length().

Referenced by create_append_plan(), inheritance_planner(), and set_subquery_pathlist().

{
    if (IsA(plan, Result))
    {
        List       *rcqual = (List *) ((Result *) plan)->resconstantqual;

        if (list_length(rcqual) == 1)
        {
            Const      *constqual = (Const *) linitial(rcqual);

            if (constqual && IsA(constqual, Const))
            {
                if (!constqual->constisnull &&
                    !DatumGetBool(constqual->constvalue))
                    return true;
            }
        }
    }
    return false;
}

bool plan_cluster_use_sort ( Oid  tableOid,
Oid  indexOid 
)

Definition at line 3443 of file planner.c.

References build_simple_rel(), Query::commandType, cost_qual_eval(), cost_sort(), create_index_path(), create_seqscan_path(), CurrentMemoryContext, ForwardScanDirection, get_relation_data_width(), PlannerInfo::glob, RelOptInfo::indexlist, IndexOptInfo::indexoid, IndexOptInfo::indexprs, RangeTblEntry::inFromCl, RangeTblEntry::inh, RangeTblEntry::lateral, lfirst, list_make1, maintenance_work_mem, makeNode, NIL, NULL, RelOptInfo::pages, PlannerInfo::parse, IndexPath::path, QualCost::per_tuple, PlannerInfo::planner_cxt, PlannerInfo::query_level, RangeTblEntry::relid, RangeTblEntry::relkind, RELOPT_BASEREL, RelOptInfo::rows, Query::rtable, RangeTblEntry::rtekind, setup_simple_rel_arrays(), QualCost::startup, Path::total_cost, PlannerInfo::total_table_pages, RelOptInfo::tuples, RelOptInfo::width, and PlannerInfo::wt_param_id.

Referenced by copy_heap_data().

{
    PlannerInfo *root;
    Query      *query;
    PlannerGlobal *glob;
    RangeTblEntry *rte;
    RelOptInfo *rel;
    IndexOptInfo *indexInfo;
    QualCost    indexExprCost;
    Cost        comparisonCost;
    Path       *seqScanPath;
    Path        seqScanAndSortPath;
    IndexPath  *indexScanPath;
    ListCell   *lc;

    /* Set up mostly-dummy planner state */
    query = makeNode(Query);
    query->commandType = CMD_SELECT;

    glob = makeNode(PlannerGlobal);

    root = makeNode(PlannerInfo);
    root->parse = query;
    root->glob = glob;
    root->query_level = 1;
    root->planner_cxt = CurrentMemoryContext;
    root->wt_param_id = -1;

    /* Build a minimal RTE for the rel */
    rte = makeNode(RangeTblEntry);
    rte->rtekind = RTE_RELATION;
    rte->relid = tableOid;
    rte->relkind = RELKIND_RELATION;  /* Don't be too picky. */
    rte->lateral = false;
    rte->inh = false;
    rte->inFromCl = true;
    query->rtable = list_make1(rte);

    /* Set up RTE/RelOptInfo arrays */
    setup_simple_rel_arrays(root);

    /* Build RelOptInfo */
    rel = build_simple_rel(root, 1, RELOPT_BASEREL);

    /* Locate IndexOptInfo for the target index */
    indexInfo = NULL;
    foreach(lc, rel->indexlist)
    {
        indexInfo = (IndexOptInfo *) lfirst(lc);
        if (indexInfo->indexoid == indexOid)
            break;
    }

    /*
     * It's possible that get_relation_info did not generate an IndexOptInfo
     * for the desired index; this could happen if it's not yet reached its
     * indcheckxmin usability horizon, or if it's a system index and we're
     * ignoring system indexes.  In such cases we should tell CLUSTER to not
     * trust the index contents but use seqscan-and-sort.
     */
    if (lc == NULL)             /* not in the list? */
        return true;            /* use sort */

    /*
     * Rather than doing all the pushups that would be needed to use
     * set_baserel_size_estimates, just do a quick hack for rows and width.
     */
    rel->rows = rel->tuples;
    rel->width = get_relation_data_width(tableOid, NULL);

    root->total_table_pages = rel->pages;

    /*
     * Determine eval cost of the index expressions, if any.  We need to
     * charge twice that amount for each tuple comparison that happens during
     * the sort, since tuplesort.c will have to re-evaluate the index
     * expressions each time.  (XXX that's pretty inefficient...)
     */
    cost_qual_eval(&indexExprCost, indexInfo->indexprs, root);
    comparisonCost = 2.0 * (indexExprCost.startup + indexExprCost.per_tuple);

    /* Estimate the cost of seq scan + sort */
    seqScanPath = create_seqscan_path(root, rel, NULL);
    cost_sort(&seqScanAndSortPath, root, NIL,
              seqScanPath->total_cost, rel->tuples, rel->width,
              comparisonCost, maintenance_work_mem, -1.0);

    /* Estimate the cost of index scan */
    indexScanPath = create_index_path(root, indexInfo,
                                      NIL, NIL, NIL, NIL, NIL,
                                      ForwardScanDirection, false,
                                      NULL, 1.0);

    return (seqScanAndSortPath.total_cost < indexScanPath->path.total_cost);
}

PlannedStmt* planner ( Query parse,
int  cursorOptions,
ParamListInfo  boundParams 
)

Definition at line 131 of file planner.c.

References planner_hook, and standard_planner().

Referenced by BeginCopy(), and pg_plan_query().

{
    PlannedStmt *result;

    if (planner_hook)
        result = (*planner_hook) (parse, cursorOptions, boundParams);
    else
        result = standard_planner(parse, cursorOptions, boundParams);
    return result;
}

Expr* preprocess_phv_expression ( PlannerInfo root,
Expr expr 
)

Definition at line 743 of file planner.c.

References EXPRKIND_PHV, and preprocess_expression().

Referenced by extract_lateral_references().

{
    return (Expr *) preprocess_expression(root, (Node *) expr, EXPRKIND_PHV);
}

PlannedStmt* standard_planner ( Query parse,
int  cursorOptions,
ParamListInfo  boundParams 
)

Definition at line 143 of file planner.c.

References Assert, PlannerGlobal::boundParams, Query::canSetTag, PlannedStmt::canSetTag, Query::commandType, PlannedStmt::commandType, CURSOR_OPT_FAST_PLAN, CURSOR_OPT_SCROLL, cursor_tuple_fraction, ExecSupportsBackwardScan(), PlannerGlobal::finalrowmarks, PlannerGlobal::finalrtable, forboth, Query::hasModifyingCTE, PlannedStmt::hasModifyingCTE, PlannedStmt::hasReturning, PlannedStmt::invalItems, PlannerGlobal::invalItems, IsA, PlannerGlobal::lastPHId, PlannerGlobal::lastRowMarkId, lfirst, list_length(), makeNode, materialize_finished_plan(), NIL, PlannedStmt::nParamExec, PlannerGlobal::nParamExec, NULL, PlannedStmt::planTree, Query::queryId, PlannedStmt::queryId, PlannedStmt::relationOids, PlannerGlobal::relationOids, PlannedStmt::resultRelations, PlannerGlobal::resultRelations, Query::returningList, PlannedStmt::rewindPlanIDs, PlannerGlobal::rewindPlanIDs, PlannedStmt::rowMarks, PlannedStmt::rtable, set_plan_references(), PlannedStmt::subplans, PlannerGlobal::subplans, subquery_planner(), PlannerGlobal::subroots, PlannedStmt::transientPlan, PlannerGlobal::transientPlan, PlannedStmt::utilityStmt, and Query::utilityStmt.

Referenced by planner().

{
    PlannedStmt *result;
    PlannerGlobal *glob;
    double      tuple_fraction;
    PlannerInfo *root;
    Plan       *top_plan;
    ListCell   *lp,
               *lr;

    /* Cursor options may come from caller or from DECLARE CURSOR stmt */
    if (parse->utilityStmt &&
        IsA(parse->utilityStmt, DeclareCursorStmt))
        cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options;

    /*
     * Set up global state for this planner invocation.  This data is needed
     * across all levels of sub-Query that might exist in the given command,
     * so we keep it in a separate struct that's linked to by each per-Query
     * PlannerInfo.
     */
    glob = makeNode(PlannerGlobal);

    glob->boundParams = boundParams;
    glob->subplans = NIL;
    glob->subroots = NIL;
    glob->rewindPlanIDs = NULL;
    glob->finalrtable = NIL;
    glob->finalrowmarks = NIL;
    glob->resultRelations = NIL;
    glob->relationOids = NIL;
    glob->invalItems = NIL;
    glob->nParamExec = 0;
    glob->lastPHId = 0;
    glob->lastRowMarkId = 0;
    glob->transientPlan = false;

    /* Determine what fraction of the plan is likely to be scanned */
    if (cursorOptions & CURSOR_OPT_FAST_PLAN)
    {
        /*
         * We have no real idea how many tuples the user will ultimately FETCH
         * from a cursor, but it is often the case that he doesn't want 'em
         * all, or would prefer a fast-start plan anyway so that he can
         * process some of the tuples sooner.  Use a GUC parameter to decide
         * what fraction to optimize for.
         */
        tuple_fraction = cursor_tuple_fraction;

        /*
         * We document cursor_tuple_fraction as simply being a fraction, which
         * means the edge cases 0 and 1 have to be treated specially here.  We
         * convert 1 to 0 ("all the tuples") and 0 to a very small fraction.
         */
        if (tuple_fraction >= 1.0)
            tuple_fraction = 0.0;
        else if (tuple_fraction <= 0.0)
            tuple_fraction = 1e-10;
    }
    else
    {
        /* Default assumption is we need all the tuples */
        tuple_fraction = 0.0;
    }

    /* primary planning entry point (may recurse for subqueries) */
    top_plan = subquery_planner(glob, parse, NULL,
                                false, tuple_fraction, &root);

    /*
     * If creating a plan for a scrollable cursor, make sure it can run
     * backwards on demand.  Add a Material node at the top at need.
     */
    if (cursorOptions & CURSOR_OPT_SCROLL)
    {
        if (!ExecSupportsBackwardScan(top_plan))
            top_plan = materialize_finished_plan(top_plan);
    }

    /* final cleanup of the plan */
    Assert(glob->finalrtable == NIL);
    Assert(glob->finalrowmarks == NIL);
    Assert(glob->resultRelations == NIL);
    top_plan = set_plan_references(root, top_plan);
    /* ... and the subplans (both regular subplans and initplans) */
    Assert(list_length(glob->subplans) == list_length(glob->subroots));
    forboth(lp, glob->subplans, lr, glob->subroots)
    {
        Plan       *subplan = (Plan *) lfirst(lp);
        PlannerInfo *subroot = (PlannerInfo *) lfirst(lr);

        lfirst(lp) = set_plan_references(subroot, subplan);
    }

    /* build the PlannedStmt result */
    result = makeNode(PlannedStmt);

    result->commandType = parse->commandType;
    result->queryId = parse->queryId;
    result->hasReturning = (parse->returningList != NIL);
    result->hasModifyingCTE = parse->hasModifyingCTE;
    result->canSetTag = parse->canSetTag;
    result->transientPlan = glob->transientPlan;
    result->planTree = top_plan;
    result->rtable = glob->finalrtable;
    result->resultRelations = glob->resultRelations;
    result->utilityStmt = parse->utilityStmt;
    result->subplans = glob->subplans;
    result->rewindPlanIDs = glob->rewindPlanIDs;
    result->rowMarks = glob->finalrowmarks;
    result->relationOids = glob->relationOids;
    result->invalItems = glob->invalItems;
    result->nParamExec = glob->nParamExec;

    return result;
}

Plan* subquery_planner ( PlannerGlobal glob,
Query parse,
PlannerInfo parent_root,
bool  hasRecursion,
double  tuple_fraction,
PlannerInfo **  subroot 
)

Definition at line 289 of file planner.c.

References PlannerInfo::append_rel_list, Query::canSetTag, CMD_SELECT, Query::commandType, contain_agg_clause(), contain_subplans(), contain_volatile_functions(), copyObject(), PlannerInfo::cte_plan_ids, Query::cteList, CurrentMemoryContext, WindowClause::endOffset, PlannerInfo::eq_classes, expand_inherited_tables(), EXPRKIND_LIMIT, EXPRKIND_QUAL, EXPRKIND_RTFUNC_LATERAL, EXPRKIND_VALUES_LATERAL, flatten_join_alias_vars(), flatten_simple_union_all(), RangeTblEntry::funcexpr, PlannerInfo::glob, Query::groupClause, grouping_planner(), PlannerInfo::hasHavingQual, PlannerInfo::hasInheritedTarget, PlannerInfo::hasJoinRTEs, PlannerInfo::hasLateralRTEs, PlannerInfo::hasPseudoConstantQuals, PlannerInfo::hasRecursion, Query::hasSubLinks, Query::havingQual, inheritance_planner(), PlannerInfo::init_plans, inline_set_returning_functions(), IS_OUTER_JOIN, Query::jointree, RangeTblEntry::jointype, lappend(), RangeTblEntry::lateral, lfirst, Query::limitCount, Query::limitOffset, list_length(), list_make1, list_make1_int, make_modifytable(), makeNode, PlannerInfo::non_recursive_plan, PlannerGlobal::nParamExec, PlannerInfo::parent_root, PlannerInfo::parse, PlannerInfo::plan_params, PlannerInfo::planner_cxt, preprocess_expression(), preprocess_qual_conditions(), preprocess_rowmarks(), pull_up_sublinks(), pull_up_subqueries(), FromExpr::quals, PlannerInfo::query_level, reduce_outer_joins(), Query::resultRelation, Query::returningList, Query::rowMarks, PlannerInfo::rowMarks, rt_fetch, Query::rtable, RTE_FUNCTION, RTE_JOIN, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, Query::setOperations, SS_assign_special_param(), SS_finalize_plan(), SS_process_ctes(), WindowClause::startOffset, PlannerGlobal::subplans, RangeTblEntry::subquery, Query::targetList, RangeTblEntry::values_lists, Query::windowClause, and PlannerInfo::wt_param_id.

Referenced by make_subplan(), recurse_set_operations(), set_subquery_pathlist(), SS_process_ctes(), and standard_planner().

{
    int         num_old_subplans = list_length(glob->subplans);
    PlannerInfo *root;
    Plan       *plan;
    List       *newHaving;
    bool        hasOuterJoins;
    ListCell   *l;

    /* Create a PlannerInfo data structure for this subquery */
    root = makeNode(PlannerInfo);
    root->parse = parse;
    root->glob = glob;
    root->query_level = parent_root ? parent_root->query_level + 1 : 1;
    root->parent_root = parent_root;
    root->plan_params = NIL;
    root->planner_cxt = CurrentMemoryContext;
    root->init_plans = NIL;
    root->cte_plan_ids = NIL;
    root->eq_classes = NIL;
    root->append_rel_list = NIL;
    root->rowMarks = NIL;
    root->hasInheritedTarget = false;

    root->hasRecursion = hasRecursion;
    if (hasRecursion)
        root->wt_param_id = SS_assign_special_param(root);
    else
        root->wt_param_id = -1;
    root->non_recursive_plan = NULL;

    /*
     * If there is a WITH list, process each WITH query and build an initplan
     * SubPlan structure for it.
     */
    if (parse->cteList)
        SS_process_ctes(root);

    /*
     * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try
     * to transform them into joins.  Note that this step does not descend
     * into subqueries; if we pull up any subqueries below, their SubLinks are
     * processed just before pulling them up.
     */
    if (parse->hasSubLinks)
        pull_up_sublinks(root);

    /*
     * Scan the rangetable for set-returning functions, and inline them if
     * possible (producing subqueries that might get pulled up next).
     * Recursion issues here are handled in the same way as for SubLinks.
     */
    inline_set_returning_functions(root);

    /*
     * Check to see if any subqueries in the jointree can be merged into this
     * query.
     */
    parse->jointree = (FromExpr *)
        pull_up_subqueries(root, (Node *) parse->jointree);

    /*
     * If this is a simple UNION ALL query, flatten it into an appendrel. We
     * do this now because it requires applying pull_up_subqueries to the leaf
     * queries of the UNION ALL, which weren't touched above because they
     * weren't referenced by the jointree (they will be after we do this).
     */
    if (parse->setOperations)
        flatten_simple_union_all(root);

    /*
     * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
     * avoid the expense of doing flatten_join_alias_vars().  Also check for
     * outer joins --- if none, we can skip reduce_outer_joins().  And check
     * for LATERAL RTEs, too.  This must be done after we have done
     * pull_up_subqueries(), of course.
     */
    root->hasJoinRTEs = false;
    root->hasLateralRTEs = false;
    hasOuterJoins = false;
    foreach(l, parse->rtable)
    {
        RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

        if (rte->rtekind == RTE_JOIN)
        {
            root->hasJoinRTEs = true;
            if (IS_OUTER_JOIN(rte->jointype))
                hasOuterJoins = true;
        }
        if (rte->lateral)
            root->hasLateralRTEs = true;
    }

    /*
     * Preprocess RowMark information.  We need to do this after subquery
     * pullup (so that all non-inherited RTEs are present) and before
     * inheritance expansion (so that the info is available for
     * expand_inherited_tables to examine and modify).
     */
    preprocess_rowmarks(root);

    /*
     * Expand any rangetable entries that are inheritance sets into "append
     * relations".  This can add entries to the rangetable, but they must be
     * plain base relations not joins, so it's OK (and marginally more
     * efficient) to do it after checking for join RTEs.  We must do it after
     * pulling up subqueries, else we'd fail to handle inherited tables in
     * subqueries.
     */
    expand_inherited_tables(root);

    /*
     * Set hasHavingQual to remember if HAVING clause is present.  Needed
     * because preprocess_expression will reduce a constant-true condition to
     * an empty qual list ... but "HAVING TRUE" is not a semantic no-op.
     */
    root->hasHavingQual = (parse->havingQual != NULL);

    /* Clear this flag; might get set in distribute_qual_to_rels */
    root->hasPseudoConstantQuals = false;

    /*
     * Do expression preprocessing on targetlist and quals, as well as other
     * random expressions in the querytree.  Note that we do not need to
     * handle sort/group expressions explicitly, because they are actually
     * part of the targetlist.
     */
    parse->targetList = (List *)
        preprocess_expression(root, (Node *) parse->targetList,
                              EXPRKIND_TARGET);

    parse->returningList = (List *)
        preprocess_expression(root, (Node *) parse->returningList,
                              EXPRKIND_TARGET);

    preprocess_qual_conditions(root, (Node *) parse->jointree);

    parse->havingQual = preprocess_expression(root, parse->havingQual,
                                              EXPRKIND_QUAL);

    foreach(l, parse->windowClause)
    {
        WindowClause *wc = (WindowClause *) lfirst(l);

        /* partitionClause/orderClause are sort/group expressions */
        wc->startOffset = preprocess_expression(root, wc->startOffset,
                                                EXPRKIND_LIMIT);
        wc->endOffset = preprocess_expression(root, wc->endOffset,
                                              EXPRKIND_LIMIT);
    }

    parse->limitOffset = preprocess_expression(root, parse->limitOffset,
                                               EXPRKIND_LIMIT);
    parse->limitCount = preprocess_expression(root, parse->limitCount,
                                              EXPRKIND_LIMIT);

    root->append_rel_list = (List *)
        preprocess_expression(root, (Node *) root->append_rel_list,
                              EXPRKIND_APPINFO);

    /* Also need to preprocess expressions within RTEs */
    foreach(l, parse->rtable)
    {
        RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
        int         kind;

        if (rte->rtekind == RTE_SUBQUERY)
        {
            /*
             * We don't want to do all preprocessing yet on the subquery's
             * expressions, since that will happen when we plan it.  But if it
             * contains any join aliases of our level, those have to get
             * expanded now, because planning of the subquery won't do it.
             * That's only possible if the subquery is LATERAL.
             */
            if (rte->lateral && root->hasJoinRTEs)
                rte->subquery = (Query *)
                    flatten_join_alias_vars(root, (Node *) rte->subquery);
        }
        else if (rte->rtekind == RTE_FUNCTION)
        {
            /* Preprocess the function expression fully */
            kind = rte->lateral ? EXPRKIND_RTFUNC_LATERAL : EXPRKIND_RTFUNC;
            rte->funcexpr = preprocess_expression(root, rte->funcexpr, kind);
        }
        else if (rte->rtekind == RTE_VALUES)
        {
            /* Preprocess the values lists fully */
            kind = rte->lateral ? EXPRKIND_VALUES_LATERAL : EXPRKIND_VALUES;
            rte->values_lists = (List *)
                preprocess_expression(root, (Node *) rte->values_lists, kind);
        }
    }

    /*
     * In some cases we may want to transfer a HAVING clause into WHERE. We
     * cannot do so if the HAVING clause contains aggregates (obviously) or
     * volatile functions (since a HAVING clause is supposed to be executed
     * only once per group).  Also, it may be that the clause is so expensive
     * to execute that we're better off doing it only once per group, despite
     * the loss of selectivity.  This is hard to estimate short of doing the
     * entire planning process twice, so we use a heuristic: clauses
     * containing subplans are left in HAVING.  Otherwise, we move or copy the
     * HAVING clause into WHERE, in hopes of eliminating tuples before
     * aggregation instead of after.
     *
     * If the query has explicit grouping then we can simply move such a
     * clause into WHERE; any group that fails the clause will not be in the
     * output because none of its tuples will reach the grouping or
     * aggregation stage.  Otherwise we must have a degenerate (variable-free)
     * HAVING clause, which we put in WHERE so that query_planner() can use it
     * in a gating Result node, but also keep in HAVING to ensure that we
     * don't emit a bogus aggregated row. (This could be done better, but it
     * seems not worth optimizing.)
     *
     * Note that both havingQual and parse->jointree->quals are in
     * implicitly-ANDed-list form at this point, even though they are declared
     * as Node *.
     */
    newHaving = NIL;
    foreach(l, (List *) parse->havingQual)
    {
        Node       *havingclause = (Node *) lfirst(l);

        if (contain_agg_clause(havingclause) ||
            contain_volatile_functions(havingclause) ||
            contain_subplans(havingclause))
        {
            /* keep it in HAVING */
            newHaving = lappend(newHaving, havingclause);
        }
        else if (parse->groupClause)
        {
            /* move it to WHERE */
            parse->jointree->quals = (Node *)
                lappend((List *) parse->jointree->quals, havingclause);
        }
        else
        {
            /* put a copy in WHERE, keep it in HAVING */
            parse->jointree->quals = (Node *)
                lappend((List *) parse->jointree->quals,
                        copyObject(havingclause));
            newHaving = lappend(newHaving, havingclause);
        }
    }
    parse->havingQual = (Node *) newHaving;

    /*
     * If we have any outer joins, try to reduce them to plain inner joins.
     * This step is most easily done after we've done expression
     * preprocessing.
     */
    if (hasOuterJoins)
        reduce_outer_joins(root);

    /*
     * Do the main planning.  If we have an inherited target relation, that
     * needs special processing, else go straight to grouping_planner.
     */
    if (parse->resultRelation &&
        rt_fetch(parse->resultRelation, parse->rtable)->inh)
        plan = inheritance_planner(root);
    else
    {
        plan = grouping_planner(root, tuple_fraction);
        /* If it's not SELECT, we need a ModifyTable node */
        if (parse->commandType != CMD_SELECT)
        {
            List       *returningLists;
            List       *rowMarks;

            /*
             * Set up the RETURNING list-of-lists, if needed.
             */
            if (parse->returningList)
                returningLists = list_make1(parse->returningList);
            else
                returningLists = NIL;

            /*
             * If there was a FOR [KEY] UPDATE/SHARE clause, the LockRows node will
             * have dealt with fetching non-locked marked rows, else we need
             * to have ModifyTable do that.
             */
            if (parse->rowMarks)
                rowMarks = NIL;
            else
                rowMarks = root->rowMarks;

            plan = (Plan *) make_modifytable(root,
                                             parse->commandType,
                                             parse->canSetTag,
                                       list_make1_int(parse->resultRelation),
                                             list_make1(plan),
                                             returningLists,
                                             rowMarks,
                                             SS_assign_special_param(root));
        }
    }

    /*
     * If any subplans were generated, or if there are any parameters to worry
     * about, build initPlan list and extParam/allParam sets for plan nodes,
     * and attach the initPlans to the top plan node.
     */
    if (list_length(glob->subplans) != num_old_subplans ||
        root->glob->nParamExec > 0)
        SS_finalize_plan(root, plan, true);

    /* Return internal info if caller wants it */
    if (subroot)
        *subroot = root;

    return plan;
}


Variable Documentation

Definition at line 48 of file planner.c.

Referenced by planner().