Header And Logo

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

Enumerations | Functions

pathnode.c File Reference

#include "postgres.h"
#include <math.h>
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
Include dependency graph for pathnode.c:

Go to the source code of this file.

Enumerations

enum  PathCostComparison { COSTS_EQUAL, COSTS_BETTER1, COSTS_BETTER2, COSTS_DIFFERENT }

Functions

static Listtranslate_sub_tlist (List *tlist, int relid)
static bool query_is_distinct_for (Query *query, List *colnos, List *opids)
static Oid distinct_col_search (int colno, List *colnos, List *opids)
int compare_path_costs (Path *path1, Path *path2, CostSelector criterion)
int compare_fractional_path_costs (Path *path1, Path *path2, double fraction)
static PathCostComparison compare_path_costs_fuzzily (Path *path1, Path *path2, double fuzz_factor, bool consider_startup)
void set_cheapest (RelOptInfo *parent_rel)
void add_path (RelOptInfo *parent_rel, Path *new_path)
bool add_path_precheck (RelOptInfo *parent_rel, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer)
Pathcreate_seqscan_path (PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
IndexPathcreate_index_path (PlannerInfo *root, IndexOptInfo *index, List *indexclauses, List *indexclausecols, List *indexorderbys, List *indexorderbycols, List *pathkeys, ScanDirection indexscandir, bool indexonly, Relids required_outer, double loop_count)
BitmapHeapPathcreate_bitmap_heap_path (PlannerInfo *root, RelOptInfo *rel, Path *bitmapqual, Relids required_outer, double loop_count)
BitmapAndPathcreate_bitmap_and_path (PlannerInfo *root, RelOptInfo *rel, List *bitmapquals)
BitmapOrPathcreate_bitmap_or_path (PlannerInfo *root, RelOptInfo *rel, List *bitmapquals)
TidPathcreate_tidscan_path (PlannerInfo *root, RelOptInfo *rel, List *tidquals, Relids required_outer)
AppendPathcreate_append_path (RelOptInfo *rel, List *subpaths, Relids required_outer)
MergeAppendPathcreate_merge_append_path (PlannerInfo *root, RelOptInfo *rel, List *subpaths, List *pathkeys, Relids required_outer)
ResultPathcreate_result_path (List *quals)
MaterialPathcreate_material_path (RelOptInfo *rel, Path *subpath)
UniquePathcreate_unique_path (PlannerInfo *root, RelOptInfo *rel, Path *subpath, SpecialJoinInfo *sjinfo)
Pathcreate_subqueryscan_path (PlannerInfo *root, RelOptInfo *rel, List *pathkeys, Relids required_outer)
Pathcreate_functionscan_path (PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
Pathcreate_valuesscan_path (PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
Pathcreate_ctescan_path (PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
Pathcreate_worktablescan_path (PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
ForeignPathcreate_foreignscan_path (PlannerInfo *root, RelOptInfo *rel, double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, List *fdw_private)
Relids calc_nestloop_required_outer (Path *outer_path, Path *inner_path)
Relids calc_non_nestloop_required_outer (Path *outer_path, Path *inner_path)
NestPathcreate_nestloop_path (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, JoinCostWorkspace *workspace, SpecialJoinInfo *sjinfo, SemiAntiJoinFactors *semifactors, Path *outer_path, Path *inner_path, List *restrict_clauses, List *pathkeys, Relids required_outer)
MergePathcreate_mergejoin_path (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, JoinCostWorkspace *workspace, SpecialJoinInfo *sjinfo, Path *outer_path, Path *inner_path, List *restrict_clauses, List *pathkeys, Relids required_outer, List *mergeclauses, List *outersortkeys, List *innersortkeys)
HashPathcreate_hashjoin_path (PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, JoinCostWorkspace *workspace, SpecialJoinInfo *sjinfo, SemiAntiJoinFactors *semifactors, Path *outer_path, Path *inner_path, List *restrict_clauses, Relids required_outer, List *hashclauses)
Pathreparameterize_path (PlannerInfo *root, Path *path, Relids required_outer, double loop_count)

Enumeration Type Documentation

Enumerator:
COSTS_EQUAL 
COSTS_BETTER1 
COSTS_BETTER2 
COSTS_DIFFERENT 

Definition at line 32 of file pathnode.c.

{
    COSTS_EQUAL,                /* path costs are fuzzily equal */
    COSTS_BETTER1,              /* first path is cheaper than second */
    COSTS_BETTER2,              /* second path is cheaper than first */
    COSTS_DIFFERENT             /* neither path dominates the other on cost */
} PathCostComparison;


Function Documentation

void add_path ( RelOptInfo parent_rel,
Path new_path 
)

Definition at line 395 of file pathnode.c.

References BMS_EQUAL, BMS_SUBSET1, BMS_SUBSET2, bms_subset_compare(), CHECK_FOR_INTERRUPTS, compare_path_costs_fuzzily(), compare_pathkeys(), RelOptInfo::consider_startup, COSTS_BETTER1, COSTS_BETTER2, COSTS_DIFFERENT, COSTS_EQUAL, IsA, lappend_cell(), lcons(), lfirst, list_delete_cell(), list_head(), lnext, NIL, NULL, Path::param_info, PATH_REQ_OUTER, Path::pathkeys, PATHKEYS_BETTER1, PATHKEYS_BETTER2, PATHKEYS_DIFFERENT, RelOptInfo::pathlist, pfree(), Path::rows, and Path::total_cost.

Referenced by create_index_paths(), create_tidscan_paths(), fileGetForeignPaths(), generate_mergeappend_paths(), get_index_paths(), mark_dummy_rel(), postgresGetForeignPaths(), set_append_rel_pathlist(), set_cte_pathlist(), set_dummy_rel_pathlist(), set_function_pathlist(), set_plain_rel_pathlist(), set_subquery_pathlist(), set_values_pathlist(), set_worktable_pathlist(), try_hashjoin_path(), try_mergejoin_path(), and try_nestloop_path().

{
    bool        accept_new = true;      /* unless we find a superior old path */
    ListCell   *insert_after = NULL;    /* where to insert new item */
    List       *new_path_pathkeys;
    ListCell   *p1;
    ListCell   *p1_prev;
    ListCell   *p1_next;

    /*
     * This is a convenient place to check for query cancel --- no part of the
     * planner goes very long without calling add_path().
     */
    CHECK_FOR_INTERRUPTS();

    /* Pretend parameterized paths have no pathkeys, per comment above */
    new_path_pathkeys = new_path->param_info ? NIL : new_path->pathkeys;

    /*
     * Loop to check proposed new path against old paths.  Note it is possible
     * for more than one old path to be tossed out because new_path dominates
     * it.
     *
     * We can't use foreach here because the loop body may delete the current
     * list cell.
     */
    p1_prev = NULL;
    for (p1 = list_head(parent_rel->pathlist); p1 != NULL; p1 = p1_next)
    {
        Path       *old_path = (Path *) lfirst(p1);
        bool        remove_old = false; /* unless new proves superior */
        PathCostComparison costcmp;
        PathKeysComparison keyscmp;
        BMS_Comparison outercmp;

        p1_next = lnext(p1);

        /*
         * Do a fuzzy cost comparison with 1% fuzziness limit.  (XXX does this
         * percentage need to be user-configurable?)
         */
        costcmp = compare_path_costs_fuzzily(new_path, old_path, 1.01,
                                             parent_rel->consider_startup);

        /*
         * If the two paths compare differently for startup and total cost,
         * then we want to keep both, and we can skip comparing pathkeys and
         * required_outer rels.  If they compare the same, proceed with the
         * other comparisons.  Row count is checked last.  (We make the tests
         * in this order because the cost comparison is most likely to turn
         * out "different", and the pathkeys comparison next most likely.  As
         * explained above, row count very seldom makes a difference, so even
         * though it's cheap to compare there's not much point in checking it
         * earlier.)
         */
        if (costcmp != COSTS_DIFFERENT)
        {
            /* Similarly check to see if either dominates on pathkeys */
            List       *old_path_pathkeys;

            old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
            keyscmp = compare_pathkeys(new_path_pathkeys,
                                       old_path_pathkeys);
            if (keyscmp != PATHKEYS_DIFFERENT)
            {
                switch (costcmp)
                {
                    case COSTS_EQUAL:
                        outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path),
                                                   PATH_REQ_OUTER(old_path));
                        if (keyscmp == PATHKEYS_BETTER1)
                        {
                            if ((outercmp == BMS_EQUAL ||
                                 outercmp == BMS_SUBSET1) &&
                                new_path->rows <= old_path->rows)
                                remove_old = true;      /* new dominates old */
                        }
                        else if (keyscmp == PATHKEYS_BETTER2)
                        {
                            if ((outercmp == BMS_EQUAL ||
                                 outercmp == BMS_SUBSET2) &&
                                new_path->rows >= old_path->rows)
                                accept_new = false;     /* old dominates new */
                        }
                        else    /* keyscmp == PATHKEYS_EQUAL */
                        {
                            if (outercmp == BMS_EQUAL)
                            {
                                /*
                                 * Same pathkeys and outer rels, and fuzzily
                                 * the same cost, so keep just one; to decide
                                 * which, first check rows and then do a fuzzy
                                 * cost comparison with very small fuzz limit.
                                 * (We used to do an exact cost comparison,
                                 * but that results in annoying
                                 * platform-specific plan variations due to
                                 * roundoff in the cost estimates.)  If things
                                 * are still tied, arbitrarily keep only the
                                 * old path.  Notice that we will keep only
                                 * the old path even if the less-fuzzy
                                 * comparison decides the startup and total
                                 * costs compare differently.
                                 */
                                if (new_path->rows < old_path->rows)
                                    remove_old = true;  /* new dominates old */
                                else if (new_path->rows > old_path->rows)
                                    accept_new = false; /* old dominates new */
                                else if (compare_path_costs_fuzzily(new_path,
                                                                    old_path,
                                                                    1.0000000001,
                                                                    parent_rel->consider_startup) == COSTS_BETTER1)
                                    remove_old = true;  /* new dominates old */
                                else
                                    accept_new = false; /* old equals or
                                                         * dominates new */
                            }
                            else if (outercmp == BMS_SUBSET1 &&
                                     new_path->rows <= old_path->rows)
                                remove_old = true;      /* new dominates old */
                            else if (outercmp == BMS_SUBSET2 &&
                                     new_path->rows >= old_path->rows)
                                accept_new = false;     /* old dominates new */
                            /* else different parameterizations, keep both */
                        }
                        break;
                    case COSTS_BETTER1:
                        if (keyscmp != PATHKEYS_BETTER2)
                        {
                            outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path),
                                                   PATH_REQ_OUTER(old_path));
                            if ((outercmp == BMS_EQUAL ||
                                 outercmp == BMS_SUBSET1) &&
                                new_path->rows <= old_path->rows)
                                remove_old = true;      /* new dominates old */
                        }
                        break;
                    case COSTS_BETTER2:
                        if (keyscmp != PATHKEYS_BETTER1)
                        {
                            outercmp = bms_subset_compare(PATH_REQ_OUTER(new_path),
                                                   PATH_REQ_OUTER(old_path));
                            if ((outercmp == BMS_EQUAL ||
                                 outercmp == BMS_SUBSET2) &&
                                new_path->rows >= old_path->rows)
                                accept_new = false;     /* old dominates new */
                        }
                        break;
                    case COSTS_DIFFERENT:

                        /*
                         * can't get here, but keep this case to keep compiler
                         * quiet
                         */
                        break;
                }
            }
        }

        /*
         * Remove current element from pathlist if dominated by new.
         */
        if (remove_old)
        {
            parent_rel->pathlist = list_delete_cell(parent_rel->pathlist,
                                                    p1, p1_prev);

            /*
             * Delete the data pointed-to by the deleted cell, if possible
             */
            if (!IsA(old_path, IndexPath))
                pfree(old_path);
            /* p1_prev does not advance */
        }
        else
        {
            /* new belongs after this old path if it has cost >= old's */
            if (new_path->total_cost >= old_path->total_cost)
                insert_after = p1;
            /* p1_prev advances */
            p1_prev = p1;
        }

        /*
         * If we found an old path that dominates new_path, we can quit
         * scanning the pathlist; we will not add new_path, and we assume
         * new_path cannot dominate any other elements of the pathlist.
         */
        if (!accept_new)
            break;
    }

    if (accept_new)
    {
        /* Accept the new path: insert it at proper place in pathlist */
        if (insert_after)
            lappend_cell(parent_rel->pathlist, insert_after, new_path);
        else
            parent_rel->pathlist = lcons(new_path, parent_rel->pathlist);
    }
    else
    {
        /* Reject and recycle the new path */
        if (!IsA(new_path, IndexPath))
            pfree(new_path);
    }
}

bool add_path_precheck ( RelOptInfo parent_rel,
Cost  startup_cost,
Cost  total_cost,
List pathkeys,
Relids  required_outer 
)

Definition at line 620 of file pathnode.c.

References bms_equal(), compare_pathkeys(), lfirst, NIL, Path::param_info, PATH_REQ_OUTER, Path::pathkeys, PATHKEYS_BETTER2, PATHKEYS_EQUAL, RelOptInfo::pathlist, Path::startup_cost, and Path::total_cost.

Referenced by try_hashjoin_path(), try_mergejoin_path(), and try_nestloop_path().

{
    List       *new_path_pathkeys;
    ListCell   *p1;

    /* Pretend parameterized paths have no pathkeys, per add_path policy */
    new_path_pathkeys = required_outer ? NIL : pathkeys;

    foreach(p1, parent_rel->pathlist)
    {
        Path       *old_path = (Path *) lfirst(p1);
        PathKeysComparison keyscmp;

        /*
         * We are looking for an old_path with the same parameterization (and
         * by assumption the same rowcount) that dominates the new path on
         * pathkeys as well as both cost metrics.  If we find one, we can
         * reject the new path.
         *
         * For speed, we make exact rather than fuzzy cost comparisons. If an
         * old path dominates the new path exactly on both costs, it will
         * surely do so fuzzily.
         */
        if (total_cost >= old_path->total_cost)
        {
            /* can win on startup cost only if unparameterized */
            if (startup_cost >= old_path->startup_cost || required_outer)
            {
                /* new path does not win on cost, so check pathkeys... */
                List       *old_path_pathkeys;

                old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
                keyscmp = compare_pathkeys(new_path_pathkeys,
                                           old_path_pathkeys);
                if (keyscmp == PATHKEYS_EQUAL ||
                    keyscmp == PATHKEYS_BETTER2)
                {
                    /* new path does not win on pathkeys... */
                    if (bms_equal(required_outer, PATH_REQ_OUTER(old_path)))
                    {
                        /* Found an old path that dominates the new one */
                        return false;
                    }
                }
            }
        }
        else
        {
            /*
             * Since the pathlist is sorted by total_cost, we can stop looking
             * once we reach a path with a total_cost larger than the new
             * path's.
             */
            break;
        }
    }

    return true;
}

Relids calc_nestloop_required_outer ( Path outer_path,
Path inner_path 
)

Definition at line 1746 of file pathnode.c.

References Assert, bms_copy(), bms_del_members(), bms_free(), bms_is_empty(), bms_overlap(), bms_union(), Path::parent, PATH_REQ_OUTER, and RelOptInfo::relids.

Referenced by try_nestloop_path().

{
    Relids      outer_paramrels = PATH_REQ_OUTER(outer_path);
    Relids      inner_paramrels = PATH_REQ_OUTER(inner_path);
    Relids      required_outer;

    /* inner_path can require rels from outer path, but not vice versa */
    Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids));
    /* easy case if inner path is not parameterized */
    if (!inner_paramrels)
        return bms_copy(outer_paramrels);
    /* else, form the union ... */
    required_outer = bms_union(outer_paramrels, inner_paramrels);
    /* ... and remove any mention of now-satisfied outer rels */
    required_outer = bms_del_members(required_outer,
                                     outer_path->parent->relids);
    /* maintain invariant that required_outer is exactly NULL if empty */
    if (bms_is_empty(required_outer))
    {
        bms_free(required_outer);
        required_outer = NULL;
    }
    return required_outer;
}

Relids calc_non_nestloop_required_outer ( Path outer_path,
Path inner_path 
)

Definition at line 1778 of file pathnode.c.

References Assert, bms_overlap(), bms_union(), Path::parent, PATH_REQ_OUTER, and RelOptInfo::relids.

Referenced by try_hashjoin_path(), and try_mergejoin_path().

{
    Relids      outer_paramrels = PATH_REQ_OUTER(outer_path);
    Relids      inner_paramrels = PATH_REQ_OUTER(inner_path);
    Relids      required_outer;

    /* neither path can require rels from the other */
    Assert(!bms_overlap(outer_paramrels, inner_path->parent->relids));
    Assert(!bms_overlap(inner_paramrels, outer_path->parent->relids));
    /* form the union ... */
    required_outer = bms_union(outer_paramrels, inner_paramrels);
    /* we do not need an explicit test for empty; bms_union gets it right */
    return required_outer;
}

int compare_fractional_path_costs ( Path path1,
Path path2,
double  fraction 
)

Definition at line 101 of file pathnode.c.

References compare_path_costs(), Path::startup_cost, Path::total_cost, and TOTAL_COST.

Referenced by choose_hashed_distinct(), choose_hashed_grouping(), choose_hashed_setop(), get_cheapest_fractional_path_for_pathkeys(), and query_planner().

{
    Cost        cost1,
                cost2;

    if (fraction <= 0.0 || fraction >= 1.0)
        return compare_path_costs(path1, path2, TOTAL_COST);
    cost1 = path1->startup_cost +
        fraction * (path1->total_cost - path1->startup_cost);
    cost2 = path2->startup_cost +
        fraction * (path2->total_cost - path2->startup_cost);
    if (cost1 < cost2)
        return -1;
    if (cost1 > cost2)
        return +1;
    return 0;
}

int compare_path_costs ( Path path1,
Path path2,
CostSelector  criterion 
)

Definition at line 55 of file pathnode.c.

References Path::startup_cost, STARTUP_COST, and Path::total_cost.

Referenced by compare_fractional_path_costs(), get_cheapest_path_for_pathkeys(), match_unsorted_outer(), and set_cheapest().

{
    if (criterion == STARTUP_COST)
    {
        if (path1->startup_cost < path2->startup_cost)
            return -1;
        if (path1->startup_cost > path2->startup_cost)
            return +1;

        /*
         * If paths have the same startup cost (not at all unlikely), order
         * them by total cost.
         */
        if (path1->total_cost < path2->total_cost)
            return -1;
        if (path1->total_cost > path2->total_cost)
            return +1;
    }
    else
    {
        if (path1->total_cost < path2->total_cost)
            return -1;
        if (path1->total_cost > path2->total_cost)
            return +1;

        /*
         * If paths have the same total cost, order them by startup cost.
         */
        if (path1->startup_cost < path2->startup_cost)
            return -1;
        if (path1->startup_cost > path2->startup_cost)
            return +1;
    }
    return 0;
}

static PathCostComparison compare_path_costs_fuzzily ( Path path1,
Path path2,
double  fuzz_factor,
bool  consider_startup 
) [static]

Definition at line 152 of file pathnode.c.

References NULL, Path::param_info, Path::startup_cost, and Path::total_cost.

Referenced by add_path().

{
    /*
     * Check total cost first since it's more likely to be different; many
     * paths have zero startup cost.
     */
    if (path1->total_cost > path2->total_cost * fuzz_factor)
    {
        /* path1 fuzzily worse on total cost */
        if (consider_startup &&
            path2->startup_cost > path1->startup_cost * fuzz_factor &&
            path1->param_info == NULL)
        {
            /* ... but path2 fuzzily worse on startup, so DIFFERENT */
            return COSTS_DIFFERENT;
        }
        /* else path2 dominates */
        return COSTS_BETTER2;
    }
    if (path2->total_cost > path1->total_cost * fuzz_factor)
    {
        /* path2 fuzzily worse on total cost */
        if (consider_startup &&
            path1->startup_cost > path2->startup_cost * fuzz_factor &&
            path2->param_info == NULL)
        {
            /* ... but path1 fuzzily worse on startup, so DIFFERENT */
            return COSTS_DIFFERENT;
        }
        /* else path1 dominates */
        return COSTS_BETTER1;
    }
    /* fuzzily the same on total cost */
    /* (so we may as well compare startup cost, even if !consider_startup) */
    if (path1->startup_cost > path2->startup_cost * fuzz_factor &&
        path2->param_info == NULL)
    {
        /* ... but path1 fuzzily worse on startup, so path2 wins */
        return COSTS_BETTER2;
    }
    if (path2->startup_cost > path1->startup_cost * fuzz_factor &&
        path1->param_info == NULL)
    {
        /* ... but path2 fuzzily worse on startup, so path1 wins */
        return COSTS_BETTER1;
    }
    /* fuzzily the same on both costs */
    return COSTS_EQUAL;
}

AppendPath* create_append_path ( RelOptInfo rel,
List subpaths,
Relids  required_outer 
)

Definition at line 890 of file pathnode.c.

References Assert, bms_equal(), get_appendrel_parampathinfo(), lfirst, list_head(), makeNode, Path::param_info, Path::parent, AppendPath::path, PATH_REQ_OUTER, Path::pathkeys, Path::pathtype, Path::rows, Path::startup_cost, subpath(), AppendPath::subpaths, and Path::total_cost.

Referenced by mark_dummy_rel(), set_append_rel_pathlist(), and set_dummy_rel_pathlist().

{
    AppendPath *pathnode = makeNode(AppendPath);
    ListCell   *l;

    pathnode->path.pathtype = T_Append;
    pathnode->path.parent = rel;
    pathnode->path.param_info = get_appendrel_parampathinfo(rel,
                                                            required_outer);
    pathnode->path.pathkeys = NIL;      /* result is always considered
                                         * unsorted */
    pathnode->subpaths = subpaths;

    /*
     * We don't bother with inventing a cost_append(), but just do it here.
     *
     * Compute rows and costs as sums of subplan rows and costs.  We charge
     * nothing extra for the Append itself, which perhaps is too optimistic,
     * but since it doesn't do any selection or projection, it is a pretty
     * cheap node.  If you change this, see also make_append().
     */
    pathnode->path.rows = 0;
    pathnode->path.startup_cost = 0;
    pathnode->path.total_cost = 0;
    foreach(l, subpaths)
    {
        Path       *subpath = (Path *) lfirst(l);

        pathnode->path.rows += subpath->rows;

        if (l == list_head(subpaths))   /* first node? */
            pathnode->path.startup_cost = subpath->startup_cost;
        pathnode->path.total_cost += subpath->total_cost;

        /* All child paths must have same parameterization */
        Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer));
    }

    return pathnode;
}

BitmapAndPath* create_bitmap_and_path ( PlannerInfo root,
RelOptInfo rel,
List bitmapquals 
)

Definition at line 815 of file pathnode.c.

References BitmapAndPath::bitmapquals, cost_bitmap_and_node(), makeNode, Path::param_info, Path::parent, BitmapAndPath::path, Path::pathkeys, and Path::pathtype.

Referenced by choose_bitmap_and().

{
    BitmapAndPath *pathnode = makeNode(BitmapAndPath);

    pathnode->path.pathtype = T_BitmapAnd;
    pathnode->path.parent = rel;
    pathnode->path.param_info = NULL;   /* not used in bitmap trees */
    pathnode->path.pathkeys = NIL;      /* always unordered */

    pathnode->bitmapquals = bitmapquals;

    /* this sets bitmapselectivity as well as the regular cost fields: */
    cost_bitmap_and_node(pathnode, root);

    return pathnode;
}

BitmapHeapPath* create_bitmap_heap_path ( PlannerInfo root,
RelOptInfo rel,
Path bitmapqual,
Relids  required_outer,
double  loop_count 
)

Definition at line 787 of file pathnode.c.

References BitmapHeapPath::bitmapqual, cost_bitmap_heap_scan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, BitmapHeapPath::path, Path::pathkeys, and Path::pathtype.

Referenced by create_index_paths(), and reparameterize_path().

{
    BitmapHeapPath *pathnode = makeNode(BitmapHeapPath);

    pathnode->path.pathtype = T_BitmapHeapScan;
    pathnode->path.parent = rel;
    pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
                                                          required_outer);
    pathnode->path.pathkeys = NIL;      /* always unordered */

    pathnode->bitmapqual = bitmapqual;

    cost_bitmap_heap_scan(&pathnode->path, root, rel,
                          pathnode->path.param_info,
                          bitmapqual, loop_count);

    return pathnode;
}

BitmapOrPath* create_bitmap_or_path ( PlannerInfo root,
RelOptInfo rel,
List bitmapquals 
)

Definition at line 839 of file pathnode.c.

References BitmapOrPath::bitmapquals, cost_bitmap_or_node(), makeNode, Path::param_info, Path::parent, BitmapOrPath::path, Path::pathkeys, and Path::pathtype.

Referenced by generate_bitmap_or_paths().

{
    BitmapOrPath *pathnode = makeNode(BitmapOrPath);

    pathnode->path.pathtype = T_BitmapOr;
    pathnode->path.parent = rel;
    pathnode->path.param_info = NULL;   /* not used in bitmap trees */
    pathnode->path.pathkeys = NIL;      /* always unordered */

    pathnode->bitmapquals = bitmapquals;

    /* this sets bitmapselectivity as well as the regular cost fields: */
    cost_bitmap_or_node(pathnode, root);

    return pathnode;
}

Path* create_ctescan_path ( PlannerInfo root,
RelOptInfo rel,
Relids  required_outer 
)

Definition at line 1668 of file pathnode.c.

References cost_ctescan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.

Referenced by set_cte_pathlist().

{
    Path       *pathnode = makeNode(Path);

    pathnode->pathtype = T_CteScan;
    pathnode->parent = rel;
    pathnode->param_info = get_baserel_parampathinfo(root, rel,
                                                     required_outer);
    pathnode->pathkeys = NIL;   /* XXX for now, result is always unordered */

    cost_ctescan(pathnode, root, rel, pathnode->param_info);

    return pathnode;
}

ForeignPath* create_foreignscan_path ( PlannerInfo root,
RelOptInfo rel,
double  rows,
Cost  startup_cost,
Cost  total_cost,
List pathkeys,
Relids  required_outer,
List fdw_private 
)

Definition at line 1717 of file pathnode.c.

References ForeignPath::fdw_private, get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, ForeignPath::path, Path::pathkeys, Path::pathtype, Path::rows, Path::startup_cost, and Path::total_cost.

Referenced by fileGetForeignPaths(), and postgresGetForeignPaths().

{
    ForeignPath *pathnode = makeNode(ForeignPath);

    pathnode->path.pathtype = T_ForeignScan;
    pathnode->path.parent = rel;
    pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
                                                          required_outer);
    pathnode->path.rows = rows;
    pathnode->path.startup_cost = startup_cost;
    pathnode->path.total_cost = total_cost;
    pathnode->path.pathkeys = pathkeys;

    pathnode->fdw_private = fdw_private;

    return pathnode;
}

Path* create_functionscan_path ( PlannerInfo root,
RelOptInfo rel,
Relids  required_outer 
)

Definition at line 1624 of file pathnode.c.

References cost_functionscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.

Referenced by set_function_pathlist().

{
    Path       *pathnode = makeNode(Path);

    pathnode->pathtype = T_FunctionScan;
    pathnode->parent = rel;
    pathnode->param_info = get_baserel_parampathinfo(root, rel,
                                                     required_outer);
    pathnode->pathkeys = NIL;   /* for now, assume unordered result */

    cost_functionscan(pathnode, root, rel, pathnode->param_info);

    return pathnode;
}

HashPath* create_hashjoin_path ( PlannerInfo root,
RelOptInfo joinrel,
JoinType  jointype,
JoinCostWorkspace workspace,
SpecialJoinInfo sjinfo,
SemiAntiJoinFactors semifactors,
Path outer_path,
Path inner_path,
List restrict_clauses,
Relids  required_outer,
List hashclauses 
)

Definition at line 1952 of file pathnode.c.

References final_cost_hashjoin(), get_joinrel_parampathinfo(), JoinPath::innerjoinpath, JoinPath::joinrestrictinfo, JoinPath::jointype, HashPath::jpath, makeNode, JoinPath::outerjoinpath, Path::param_info, Path::parent, JoinPath::path, HashPath::path_hashclauses, Path::pathkeys, and Path::pathtype.

Referenced by try_hashjoin_path().

{
    HashPath   *pathnode = makeNode(HashPath);

    pathnode->jpath.path.pathtype = T_HashJoin;
    pathnode->jpath.path.parent = joinrel;
    pathnode->jpath.path.param_info =
        get_joinrel_parampathinfo(root,
                                  joinrel,
                                  outer_path,
                                  inner_path,
                                  sjinfo,
                                  required_outer,
                                  &restrict_clauses);

    /*
     * A hashjoin never has pathkeys, since its output ordering is
     * unpredictable due to possible batching.  XXX If the inner relation is
     * small enough, we could instruct the executor that it must not batch,
     * and then we could assume that the output inherits the outer relation's
     * ordering, which might save a sort step.  However there is considerable
     * downside if our estimate of the inner relation size is badly off. For
     * the moment we don't risk it.  (Note also that if we wanted to take this
     * seriously, joinpath.c would have to consider many more paths for the
     * outer rel than it does now.)
     */
    pathnode->jpath.path.pathkeys = NIL;
    pathnode->jpath.jointype = jointype;
    pathnode->jpath.outerjoinpath = outer_path;
    pathnode->jpath.innerjoinpath = inner_path;
    pathnode->jpath.joinrestrictinfo = restrict_clauses;
    pathnode->path_hashclauses = hashclauses;
    /* final_cost_hashjoin will fill in pathnode->num_batches */

    final_cost_hashjoin(root, pathnode, workspace, sjinfo, semifactors);

    return pathnode;
}

IndexPath* create_index_path ( PlannerInfo root,
IndexOptInfo index,
List indexclauses,
List indexclausecols,
List indexorderbys,
List indexorderbycols,
List pathkeys,
ScanDirection  indexscandir,
bool  indexonly,
Relids  required_outer,
double  loop_count 
)

Definition at line 733 of file pathnode.c.

References cost_index(), expand_indexqual_conditions(), get_baserel_parampathinfo(), IndexPath::indexclauses, IndexPath::indexinfo, IndexPath::indexorderbycols, IndexPath::indexorderbys, IndexPath::indexqualcols, IndexPath::indexquals, IndexPath::indexscandir, makeNode, Path::param_info, Path::parent, IndexPath::path, Path::pathkeys, Path::pathtype, IndexOptInfo::rel, and T_IndexOnlyScan.

Referenced by build_index_paths(), and plan_cluster_use_sort().

{
    IndexPath  *pathnode = makeNode(IndexPath);
    RelOptInfo *rel = index->rel;
    List       *indexquals,
               *indexqualcols;

    pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
    pathnode->path.parent = rel;
    pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
                                                          required_outer);
    pathnode->path.pathkeys = pathkeys;

    /* Convert clauses to indexquals the executor can handle */
    expand_indexqual_conditions(index, indexclauses, indexclausecols,
                                &indexquals, &indexqualcols);

    /* Fill in the pathnode */
    pathnode->indexinfo = index;
    pathnode->indexclauses = indexclauses;
    pathnode->indexquals = indexquals;
    pathnode->indexqualcols = indexqualcols;
    pathnode->indexorderbys = indexorderbys;
    pathnode->indexorderbycols = indexorderbycols;
    pathnode->indexscandir = indexscandir;

    cost_index(pathnode, root, loop_count);

    return pathnode;
}

MaterialPath* create_material_path ( RelOptInfo rel,
Path subpath 
)

Definition at line 1050 of file pathnode.c.

References Assert, cost_material(), makeNode, Path::param_info, Path::parent, MaterialPath::path, Path::pathkeys, Path::pathtype, Path::rows, Path::startup_cost, MaterialPath::subpath, Path::total_cost, and RelOptInfo::width.

Referenced by match_unsorted_outer().

{
    MaterialPath *pathnode = makeNode(MaterialPath);

    Assert(subpath->parent == rel);

    pathnode->path.pathtype = T_Material;
    pathnode->path.parent = rel;
    pathnode->path.param_info = subpath->param_info;
    pathnode->path.pathkeys = subpath->pathkeys;

    pathnode->subpath = subpath;

    cost_material(&pathnode->path,
                  subpath->startup_cost,
                  subpath->total_cost,
                  subpath->rows,
                  rel->width);

    return pathnode;
}

MergeAppendPath* create_merge_append_path ( PlannerInfo root,
RelOptInfo rel,
List subpaths,
List pathkeys,
Relids  required_outer 
)

Definition at line 937 of file pathnode.c.

References PlannerInfo::all_baserels, Assert, bms_equal(), cost_merge_append(), cost_sort(), get_appendrel_parampathinfo(), lfirst, PlannerInfo::limit_tuples, MergeAppendPath::limit_tuples, list_length(), makeNode, Path::param_info, Path::parent, MergeAppendPath::path, PATH_REQ_OUTER, Path::pathkeys, pathkeys_contained_in(), Path::pathtype, RelOptInfo::relids, Path::rows, Path::startup_cost, subpath(), MergeAppendPath::subpaths, Path::total_cost, RelOptInfo::tuples, RelOptInfo::width, and work_mem.

Referenced by generate_mergeappend_paths().

{
    MergeAppendPath *pathnode = makeNode(MergeAppendPath);
    Cost        input_startup_cost;
    Cost        input_total_cost;
    ListCell   *l;

    pathnode->path.pathtype = T_MergeAppend;
    pathnode->path.parent = rel;
    pathnode->path.param_info = get_appendrel_parampathinfo(rel,
                                                            required_outer);
    pathnode->path.pathkeys = pathkeys;
    pathnode->subpaths = subpaths;

    /*
     * Apply query-wide LIMIT if known and path is for sole base relation.
     * (Handling this at this low level is a bit klugy.)
     */
    if (bms_equal(rel->relids, root->all_baserels))
        pathnode->limit_tuples = root->limit_tuples;
    else
        pathnode->limit_tuples = -1.0;

    /*
     * Add up the sizes and costs of the input paths.
     */
    pathnode->path.rows = 0;
    input_startup_cost = 0;
    input_total_cost = 0;
    foreach(l, subpaths)
    {
        Path       *subpath = (Path *) lfirst(l);

        pathnode->path.rows += subpath->rows;

        if (pathkeys_contained_in(pathkeys, subpath->pathkeys))
        {
            /* Subpath is adequately ordered, we won't need to sort it */
            input_startup_cost += subpath->startup_cost;
            input_total_cost += subpath->total_cost;
        }
        else
        {
            /* We'll need to insert a Sort node, so include cost for that */
            Path        sort_path;      /* dummy for result of cost_sort */

            cost_sort(&sort_path,
                      root,
                      pathkeys,
                      subpath->total_cost,
                      subpath->parent->tuples,
                      subpath->parent->width,
                      0.0,
                      work_mem,
                      pathnode->limit_tuples);
            input_startup_cost += sort_path.startup_cost;
            input_total_cost += sort_path.total_cost;
        }

        /* All child paths must have same parameterization */
        Assert(bms_equal(PATH_REQ_OUTER(subpath), required_outer));
    }

    /* Now we can compute total costs of the MergeAppend */
    cost_merge_append(&pathnode->path, root,
                      pathkeys, list_length(subpaths),
                      input_startup_cost, input_total_cost,
                      rel->tuples);

    return pathnode;
}

MergePath* create_mergejoin_path ( PlannerInfo root,
RelOptInfo joinrel,
JoinType  jointype,
JoinCostWorkspace workspace,
SpecialJoinInfo sjinfo,
Path outer_path,
Path inner_path,
List restrict_clauses,
List pathkeys,
Relids  required_outer,
List mergeclauses,
List outersortkeys,
List innersortkeys 
)

Definition at line 1894 of file pathnode.c.

References final_cost_mergejoin(), get_joinrel_parampathinfo(), JoinPath::innerjoinpath, MergePath::innersortkeys, JoinPath::joinrestrictinfo, JoinPath::jointype, MergePath::jpath, makeNode, JoinPath::outerjoinpath, MergePath::outersortkeys, Path::param_info, Path::parent, JoinPath::path, MergePath::path_mergeclauses, Path::pathkeys, and Path::pathtype.

Referenced by try_mergejoin_path().

{
    MergePath  *pathnode = makeNode(MergePath);

    pathnode->jpath.path.pathtype = T_MergeJoin;
    pathnode->jpath.path.parent = joinrel;
    pathnode->jpath.path.param_info =
        get_joinrel_parampathinfo(root,
                                  joinrel,
                                  outer_path,
                                  inner_path,
                                  sjinfo,
                                  required_outer,
                                  &restrict_clauses);
    pathnode->jpath.path.pathkeys = pathkeys;
    pathnode->jpath.jointype = jointype;
    pathnode->jpath.outerjoinpath = outer_path;
    pathnode->jpath.innerjoinpath = inner_path;
    pathnode->jpath.joinrestrictinfo = restrict_clauses;
    pathnode->path_mergeclauses = mergeclauses;
    pathnode->outersortkeys = outersortkeys;
    pathnode->innersortkeys = innersortkeys;
    /* pathnode->materialize_inner will be set by final_cost_mergejoin */

    final_cost_mergejoin(root, pathnode, workspace, sjinfo);

    return pathnode;
}

NestPath* create_nestloop_path ( PlannerInfo root,
RelOptInfo joinrel,
JoinType  jointype,
JoinCostWorkspace workspace,
SpecialJoinInfo sjinfo,
SemiAntiJoinFactors semifactors,
Path outer_path,
Path inner_path,
List restrict_clauses,
List pathkeys,
Relids  required_outer 
)

Definition at line 1812 of file pathnode.c.

References bms_overlap(), bms_union(), final_cost_nestloop(), get_joinrel_parampathinfo(), JoinPath::innerjoinpath, join_clause_is_movable_into(), JoinPath::joinrestrictinfo, JoinPath::jointype, lappend(), lfirst, makeNode, JoinPath::outerjoinpath, Path::param_info, Path::parent, JoinPath::path, PATH_REQ_OUTER, Path::pathkeys, Path::pathtype, and RelOptInfo::relids.

Referenced by try_nestloop_path().

{
    NestPath   *pathnode = makeNode(NestPath);
    Relids      inner_req_outer = PATH_REQ_OUTER(inner_path);

    /*
     * If the inner path is parameterized by the outer, we must drop any
     * restrict_clauses that are due to be moved into the inner path.  We have
     * to do this now, rather than postpone the work till createplan time,
     * because the restrict_clauses list can affect the size and cost
     * estimates for this path.
     */
    if (bms_overlap(inner_req_outer, outer_path->parent->relids))
    {
        Relids      inner_and_outer = bms_union(inner_path->parent->relids,
                                                inner_req_outer);
        List       *jclauses = NIL;
        ListCell   *lc;

        foreach(lc, restrict_clauses)
        {
            RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);

            if (!join_clause_is_movable_into(rinfo,
                                             inner_path->parent->relids,
                                             inner_and_outer))
                jclauses = lappend(jclauses, rinfo);
        }
        restrict_clauses = jclauses;
    }

    pathnode->path.pathtype = T_NestLoop;
    pathnode->path.parent = joinrel;
    pathnode->path.param_info =
        get_joinrel_parampathinfo(root,
                                  joinrel,
                                  outer_path,
                                  inner_path,
                                  sjinfo,
                                  required_outer,
                                  &restrict_clauses);
    pathnode->path.pathkeys = pathkeys;
    pathnode->jointype = jointype;
    pathnode->outerjoinpath = outer_path;
    pathnode->innerjoinpath = inner_path;
    pathnode->joinrestrictinfo = restrict_clauses;

    final_cost_nestloop(root, pathnode, workspace, sjinfo, semifactors);

    return pathnode;
}

ResultPath* create_result_path ( List quals  ) 

Definition at line 1019 of file pathnode.c.

References cpu_tuple_cost, makeNode, Path::param_info, Path::parent, ResultPath::path, Path::pathkeys, Path::pathtype, ResultPath::quals, Path::rows, Path::startup_cost, and Path::total_cost.

Referenced by query_planner().

{
    ResultPath *pathnode = makeNode(ResultPath);

    pathnode->path.pathtype = T_Result;
    pathnode->path.parent = NULL;
    pathnode->path.param_info = NULL;       /* there are no other rels... */
    pathnode->path.pathkeys = NIL;
    pathnode->quals = quals;

    /* Hardly worth defining a cost_result() function ... just do it */
    pathnode->path.rows = 1;
    pathnode->path.startup_cost = 0;
    pathnode->path.total_cost = cpu_tuple_cost;

    /*
     * In theory we should include the qual eval cost as well, but at present
     * that doesn't accomplish much except duplicate work that will be done
     * again in make_result; since this is only used for degenerate cases,
     * nothing interesting will be done with the path cost values...
     */

    return pathnode;
}

Path* create_seqscan_path ( PlannerInfo root,
RelOptInfo rel,
Relids  required_outer 
)

Definition at line 693 of file pathnode.c.

References cost_seqscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.

Referenced by plan_cluster_use_sort(), reparameterize_path(), and set_plain_rel_pathlist().

{
    Path       *pathnode = makeNode(Path);

    pathnode->pathtype = T_SeqScan;
    pathnode->parent = rel;
    pathnode->param_info = get_baserel_parampathinfo(root, rel,
                                                     required_outer);
    pathnode->pathkeys = NIL;   /* seqscan has unordered result */

    cost_seqscan(pathnode, root, rel, pathnode->param_info);

    return pathnode;
}

Path* create_subqueryscan_path ( PlannerInfo root,
RelOptInfo rel,
List pathkeys,
Relids  required_outer 
)

Definition at line 1602 of file pathnode.c.

References cost_subqueryscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.

Referenced by reparameterize_path(), and set_subquery_pathlist().

{
    Path       *pathnode = makeNode(Path);

    pathnode->pathtype = T_SubqueryScan;
    pathnode->parent = rel;
    pathnode->param_info = get_baserel_parampathinfo(root, rel,
                                                     required_outer);
    pathnode->pathkeys = pathkeys;

    cost_subqueryscan(pathnode, root, rel, pathnode->param_info);

    return pathnode;
}

TidPath* create_tidscan_path ( PlannerInfo root,
RelOptInfo rel,
List tidquals,
Relids  required_outer 
)

Definition at line 863 of file pathnode.c.

References cost_tidscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, TidPath::path, Path::pathkeys, Path::pathtype, and TidPath::tidquals.

Referenced by create_tidscan_paths().

{
    TidPath    *pathnode = makeNode(TidPath);

    pathnode->path.pathtype = T_TidScan;
    pathnode->path.parent = rel;
    pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
                                                          required_outer);
    pathnode->path.pathkeys = NIL;      /* always unordered */

    pathnode->tidquals = tidquals;

    cost_tidscan(&pathnode->path, root, rel, tidquals,
                 pathnode->path.param_info);

    return pathnode;
}

UniquePath* create_unique_path ( PlannerInfo root,
RelOptInfo rel,
Path subpath,
SpecialJoinInfo sjinfo 
)

Definition at line 1084 of file pathnode.c.

References AGG_HASHED, OpExpr::args, Assert, bms_equal(), bms_is_empty(), bms_is_subset(), bms_overlap(), bms_union(), RelOptInfo::cheapest_total_path, RelOptInfo::cheapest_unique_path, contain_volatile_functions(), copyObject(), cost_agg(), cost_sort(), cpu_operator_cost, enable_hashagg, estimate_num_groups(), exprType(), get_commutator(), get_mergejoin_opfamilies(), UniquePath::in_operators, IsA, SpecialJoinInfo::join_quals, JOIN_SEMI, SpecialJoinInfo::jointype, lappend(), lappend_oid(), lfirst, linitial, list_length(), lsecond, makeNode, MemoryContextSwitchTo(), NIL, NULL, OidIsValid, op_hashjoinable(), op_mergejoinable(), OpExpr::opno, Path::param_info, Path::parent, UniquePath::path, Path::pathkeys, Path::pathtype, PlannerInfo::planner_cxt, planner_rt_fetch, pull_varnos(), query_is_distinct_for(), relation_has_unique_index_for(), RelOptInfo::relid, RelOptInfo::relids, RelOptInfo::rows, Path::rows, RTE_RELATION, RTE_SUBQUERY, RelOptInfo::rtekind, Path::startup_cost, UniquePath::subpath, RangeTblEntry::subquery, SpecialJoinInfo::syn_righthand, Path::total_cost, translate_sub_tlist(), UniquePath::umethod, UniquePath::uniq_exprs, UNIQUE_PATH_HASH, RelOptInfo::width, and work_mem.

Referenced by hash_inner_and_outer(), join_is_legal(), make_join_rel(), match_unsorted_outer(), and sort_inner_and_outer().

{
    UniquePath *pathnode;
    Path        sort_path;      /* dummy for result of cost_sort */
    Path        agg_path;       /* dummy for result of cost_agg */
    MemoryContext oldcontext;
    List       *in_operators;
    List       *uniq_exprs;
    bool        all_btree;
    bool        all_hash;
    int         numCols;
    ListCell   *lc;

    /* Caller made a mistake if subpath isn't cheapest_total ... */
    Assert(subpath == rel->cheapest_total_path);
    Assert(subpath->parent == rel);
    /* ... or if SpecialJoinInfo is the wrong one */
    Assert(sjinfo->jointype == JOIN_SEMI);
    Assert(bms_equal(rel->relids, sjinfo->syn_righthand));

    /* If result already cached, return it */
    if (rel->cheapest_unique_path)
        return (UniquePath *) rel->cheapest_unique_path;

    /* If we previously failed, return NULL quickly */
    if (sjinfo->join_quals == NIL)
        return NULL;

    /*
     * We must ensure path struct and subsidiary data are allocated in main
     * planning context; otherwise GEQO memory management causes trouble.
     */
    oldcontext = MemoryContextSwitchTo(root->planner_cxt);

    /*----------
     * Look to see whether the semijoin's join quals consist of AND'ed
     * equality operators, with (only) RHS variables on only one side of
     * each one.  If so, we can figure out how to enforce uniqueness for
     * the RHS.
     *
     * Note that the input join_quals list is the list of quals that are
     * *syntactically* associated with the semijoin, which in practice means
     * the synthesized comparison list for an IN or the WHERE of an EXISTS.
     * Particularly in the latter case, it might contain clauses that aren't
     * *semantically* associated with the join, but refer to just one side or
     * the other.  We can ignore such clauses here, as they will just drop
     * down to be processed within one side or the other.  (It is okay to
     * consider only the syntactically-associated clauses here because for a
     * semijoin, no higher-level quals could refer to the RHS, and so there
     * can be no other quals that are semantically associated with this join.
     * We do things this way because it is useful to be able to run this test
     * before we have extracted the list of quals that are actually
     * semantically associated with the particular join.)
     *
     * Note that the in_operators list consists of the joinqual operators
     * themselves (but commuted if needed to put the RHS value on the right).
     * These could be cross-type operators, in which case the operator
     * actually needed for uniqueness is a related single-type operator.
     * We assume here that that operator will be available from the btree
     * or hash opclass when the time comes ... if not, create_unique_plan()
     * will fail.
     *----------
     */
    in_operators = NIL;
    uniq_exprs = NIL;
    all_btree = true;
    all_hash = enable_hashagg;  /* don't consider hash if not enabled */
    foreach(lc, sjinfo->join_quals)
    {
        OpExpr     *op = (OpExpr *) lfirst(lc);
        Oid         opno;
        Node       *left_expr;
        Node       *right_expr;
        Relids      left_varnos;
        Relids      right_varnos;
        Relids      all_varnos;
        Oid         opinputtype;

        /* Is it a binary opclause? */
        if (!IsA(op, OpExpr) ||
            list_length(op->args) != 2)
        {
            /* No, but does it reference both sides? */
            all_varnos = pull_varnos((Node *) op);
            if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
                bms_is_subset(all_varnos, sjinfo->syn_righthand))
            {
                /*
                 * Clause refers to only one rel, so ignore it --- unless it
                 * contains volatile functions, in which case we'd better
                 * punt.
                 */
                if (contain_volatile_functions((Node *) op))
                    goto no_unique_path;
                continue;
            }
            /* Non-operator clause referencing both sides, must punt */
            goto no_unique_path;
        }

        /* Extract data from binary opclause */
        opno = op->opno;
        left_expr = linitial(op->args);
        right_expr = lsecond(op->args);
        left_varnos = pull_varnos(left_expr);
        right_varnos = pull_varnos(right_expr);
        all_varnos = bms_union(left_varnos, right_varnos);
        opinputtype = exprType(left_expr);

        /* Does it reference both sides? */
        if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
            bms_is_subset(all_varnos, sjinfo->syn_righthand))
        {
            /*
             * Clause refers to only one rel, so ignore it --- unless it
             * contains volatile functions, in which case we'd better punt.
             */
            if (contain_volatile_functions((Node *) op))
                goto no_unique_path;
            continue;
        }

        /* check rel membership of arguments */
        if (!bms_is_empty(right_varnos) &&
            bms_is_subset(right_varnos, sjinfo->syn_righthand) &&
            !bms_overlap(left_varnos, sjinfo->syn_righthand))
        {
            /* typical case, right_expr is RHS variable */
        }
        else if (!bms_is_empty(left_varnos) &&
                 bms_is_subset(left_varnos, sjinfo->syn_righthand) &&
                 !bms_overlap(right_varnos, sjinfo->syn_righthand))
        {
            /* flipped case, left_expr is RHS variable */
            opno = get_commutator(opno);
            if (!OidIsValid(opno))
                goto no_unique_path;
            right_expr = left_expr;
        }
        else
            goto no_unique_path;

        /* all operators must be btree equality or hash equality */
        if (all_btree)
        {
            /* oprcanmerge is considered a hint... */
            if (!op_mergejoinable(opno, opinputtype) ||
                get_mergejoin_opfamilies(opno) == NIL)
                all_btree = false;
        }
        if (all_hash)
        {
            /* ... but oprcanhash had better be correct */
            if (!op_hashjoinable(opno, opinputtype))
                all_hash = false;
        }
        if (!(all_btree || all_hash))
            goto no_unique_path;

        /* so far so good, keep building lists */
        in_operators = lappend_oid(in_operators, opno);
        uniq_exprs = lappend(uniq_exprs, copyObject(right_expr));
    }

    /* Punt if we didn't find at least one column to unique-ify */
    if (uniq_exprs == NIL)
        goto no_unique_path;

    /*
     * The expressions we'd need to unique-ify mustn't be volatile.
     */
    if (contain_volatile_functions((Node *) uniq_exprs))
        goto no_unique_path;

    /*
     * If we get here, we can unique-ify using at least one of sorting and
     * hashing.  Start building the result Path object.
     */
    pathnode = makeNode(UniquePath);

    pathnode->path.pathtype = T_Unique;
    pathnode->path.parent = rel;
    pathnode->path.param_info = subpath->param_info;

    /*
     * Assume the output is unsorted, since we don't necessarily have pathkeys
     * to represent it.  (This might get overridden below.)
     */
    pathnode->path.pathkeys = NIL;

    pathnode->subpath = subpath;
    pathnode->in_operators = in_operators;
    pathnode->uniq_exprs = uniq_exprs;

    /*
     * If the input is a relation and it has a unique index that proves the
     * uniq_exprs are unique, then we don't need to do anything.  Note that
     * relation_has_unique_index_for automatically considers restriction
     * clauses for the rel, as well.
     */
    if (rel->rtekind == RTE_RELATION && all_btree &&
        relation_has_unique_index_for(root, rel, NIL,
                                      uniq_exprs, in_operators))
    {
        pathnode->umethod = UNIQUE_PATH_NOOP;
        pathnode->path.rows = rel->rows;
        pathnode->path.startup_cost = subpath->startup_cost;
        pathnode->path.total_cost = subpath->total_cost;
        pathnode->path.pathkeys = subpath->pathkeys;

        rel->cheapest_unique_path = (Path *) pathnode;

        MemoryContextSwitchTo(oldcontext);

        return pathnode;
    }

    /*
     * If the input is a subquery whose output must be unique already, then we
     * don't need to do anything.  The test for uniqueness has to consider
     * exactly which columns we are extracting; for example "SELECT DISTINCT
     * x,y" doesn't guarantee that x alone is distinct. So we cannot check for
     * this optimization unless uniq_exprs consists only of simple Vars
     * referencing subquery outputs.  (Possibly we could do something with
     * expressions in the subquery outputs, too, but for now keep it simple.)
     */
    if (rel->rtekind == RTE_SUBQUERY)
    {
        RangeTblEntry *rte = planner_rt_fetch(rel->relid, root);
        List       *sub_tlist_colnos;

        sub_tlist_colnos = translate_sub_tlist(uniq_exprs, rel->relid);

        if (sub_tlist_colnos &&
            query_is_distinct_for(rte->subquery,
                                  sub_tlist_colnos, in_operators))
        {
            pathnode->umethod = UNIQUE_PATH_NOOP;
            pathnode->path.rows = rel->rows;
            pathnode->path.startup_cost = subpath->startup_cost;
            pathnode->path.total_cost = subpath->total_cost;
            pathnode->path.pathkeys = subpath->pathkeys;

            rel->cheapest_unique_path = (Path *) pathnode;

            MemoryContextSwitchTo(oldcontext);

            return pathnode;
        }
    }

    /* Estimate number of output rows */
    pathnode->path.rows = estimate_num_groups(root, uniq_exprs, rel->rows);
    numCols = list_length(uniq_exprs);

    if (all_btree)
    {
        /*
         * Estimate cost for sort+unique implementation
         */
        cost_sort(&sort_path, root, NIL,
                  subpath->total_cost,
                  rel->rows,
                  rel->width,
                  0.0,
                  work_mem,
                  -1.0);

        /*
         * Charge one cpu_operator_cost per comparison per input tuple. We
         * assume all columns get compared at most of the tuples. (XXX
         * probably this is an overestimate.)  This should agree with
         * make_unique.
         */
        sort_path.total_cost += cpu_operator_cost * rel->rows * numCols;
    }

    if (all_hash)
    {
        /*
         * Estimate the overhead per hashtable entry at 64 bytes (same as in
         * planner.c).
         */
        int         hashentrysize = rel->width + 64;

        if (hashentrysize * pathnode->path.rows > work_mem * 1024L)
            all_hash = false;   /* don't try to hash */
        else
            cost_agg(&agg_path, root,
                     AGG_HASHED, NULL,
                     numCols, pathnode->path.rows,
                     subpath->startup_cost,
                     subpath->total_cost,
                     rel->rows);
    }

    if (all_btree && all_hash)
    {
        if (agg_path.total_cost < sort_path.total_cost)
            pathnode->umethod = UNIQUE_PATH_HASH;
        else
            pathnode->umethod = UNIQUE_PATH_SORT;
    }
    else if (all_btree)
        pathnode->umethod = UNIQUE_PATH_SORT;
    else if (all_hash)
        pathnode->umethod = UNIQUE_PATH_HASH;
    else
        goto no_unique_path;

    if (pathnode->umethod == UNIQUE_PATH_HASH)
    {
        pathnode->path.startup_cost = agg_path.startup_cost;
        pathnode->path.total_cost = agg_path.total_cost;
    }
    else
    {
        pathnode->path.startup_cost = sort_path.startup_cost;
        pathnode->path.total_cost = sort_path.total_cost;
    }

    rel->cheapest_unique_path = (Path *) pathnode;

    MemoryContextSwitchTo(oldcontext);

    return pathnode;

no_unique_path:         /* failure exit */

    /* Mark the SpecialJoinInfo as not unique-able */
    sjinfo->join_quals = NIL;

    MemoryContextSwitchTo(oldcontext);

    return NULL;
}

Path* create_valuesscan_path ( PlannerInfo root,
RelOptInfo rel,
Relids  required_outer 
)

Definition at line 1646 of file pathnode.c.

References cost_valuesscan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.

Referenced by set_values_pathlist().

{
    Path       *pathnode = makeNode(Path);

    pathnode->pathtype = T_ValuesScan;
    pathnode->parent = rel;
    pathnode->param_info = get_baserel_parampathinfo(root, rel,
                                                     required_outer);
    pathnode->pathkeys = NIL;   /* result is always unordered */

    cost_valuesscan(pathnode, root, rel, pathnode->param_info);

    return pathnode;
}

Path* create_worktablescan_path ( PlannerInfo root,
RelOptInfo rel,
Relids  required_outer 
)

Definition at line 1689 of file pathnode.c.

References cost_ctescan(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, Path::pathkeys, and Path::pathtype.

Referenced by set_worktable_pathlist().

{
    Path       *pathnode = makeNode(Path);

    pathnode->pathtype = T_WorkTableScan;
    pathnode->parent = rel;
    pathnode->param_info = get_baserel_parampathinfo(root, rel,
                                                     required_outer);
    pathnode->pathkeys = NIL;   /* result is always unordered */

    /* Cost is the same as for a regular CTE scan */
    cost_ctescan(pathnode, root, rel, pathnode->param_info);

    return pathnode;
}

static Oid distinct_col_search ( int  colno,
List colnos,
List opids 
) [static]

Definition at line 1583 of file pathnode.c.

References forboth, lfirst_int, and lfirst_oid.

Referenced by query_is_distinct_for().

{
    ListCell   *lc1,
               *lc2;

    forboth(lc1, colnos, lc2, opids)
    {
        if (colno == lfirst_int(lc1))
            return lfirst_oid(lc2);
    }
    return InvalidOid;
}

static bool query_is_distinct_for ( Query query,
List colnos,
List opids 
) [static]

Definition at line 1468 of file pathnode.c.

References SetOperationStmt::all, Assert, distinct_col_search(), Query::distinctClause, SortGroupClause::eqop, equality_ops_are_compatible(), get_sortgroupclause_tle(), Query::groupClause, SetOperationStmt::groupClauses, Query::hasAggs, Query::havingQual, IsA, lfirst, list_head(), list_length(), lnext, NULL, OidIsValid, SetOperationStmt::op, TargetEntry::resjunk, TargetEntry::resno, SETOP_NONE, Query::setOperations, and Query::targetList.

Referenced by create_unique_path().

{
    ListCell   *l;
    Oid         opid;

    Assert(list_length(colnos) == list_length(opids));

    /*
     * DISTINCT (including DISTINCT ON) guarantees uniqueness if all the
     * columns in the DISTINCT clause appear in colnos and operator semantics
     * match.
     */
    if (query->distinctClause)
    {
        foreach(l, query->distinctClause)
        {
            SortGroupClause *sgc = (SortGroupClause *) lfirst(l);
            TargetEntry *tle = get_sortgroupclause_tle(sgc,
                                                       query->targetList);

            opid = distinct_col_search(tle->resno, colnos, opids);
            if (!OidIsValid(opid) ||
                !equality_ops_are_compatible(opid, sgc->eqop))
                break;          /* exit early if no match */
        }
        if (l == NULL)          /* had matches for all? */
            return true;
    }

    /*
     * Similarly, GROUP BY guarantees uniqueness if all the grouped columns
     * appear in colnos and operator semantics match.
     */
    if (query->groupClause)
    {
        foreach(l, query->groupClause)
        {
            SortGroupClause *sgc = (SortGroupClause *) lfirst(l);
            TargetEntry *tle = get_sortgroupclause_tle(sgc,
                                                       query->targetList);

            opid = distinct_col_search(tle->resno, colnos, opids);
            if (!OidIsValid(opid) ||
                !equality_ops_are_compatible(opid, sgc->eqop))
                break;          /* exit early if no match */
        }
        if (l == NULL)          /* had matches for all? */
            return true;
    }
    else
    {
        /*
         * If we have no GROUP BY, but do have aggregates or HAVING, then the
         * result is at most one row so it's surely unique, for any operators.
         */
        if (query->hasAggs || query->havingQual)
            return true;
    }

    /*
     * UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row,
     * except with ALL.
     */
    if (query->setOperations)
    {
        SetOperationStmt *topop = (SetOperationStmt *) query->setOperations;

        Assert(IsA(topop, SetOperationStmt));
        Assert(topop->op != SETOP_NONE);

        if (!topop->all)
        {
            ListCell   *lg;

            /* We're good if all the nonjunk output columns are in colnos */
            lg = list_head(topop->groupClauses);
            foreach(l, query->targetList)
            {
                TargetEntry *tle = (TargetEntry *) lfirst(l);
                SortGroupClause *sgc;

                if (tle->resjunk)
                    continue;   /* ignore resjunk columns */

                /* non-resjunk columns should have grouping clauses */
                Assert(lg != NULL);
                sgc = (SortGroupClause *) lfirst(lg);
                lg = lnext(lg);

                opid = distinct_col_search(tle->resno, colnos, opids);
                if (!OidIsValid(opid) ||
                    !equality_ops_are_compatible(opid, sgc->eqop))
                    break;      /* exit early if no match */
            }
            if (l == NULL)      /* had matches for all? */
                return true;
        }
    }

    /*
     * XXX Are there any other cases in which we can easily see the result
     * must be distinct?
     */

    return false;
}

Path* reparameterize_path ( PlannerInfo root,
Path path,
Relids  required_outer,
double  loop_count 
)

Definition at line 2019 of file pathnode.c.

References BitmapHeapPath::bitmapqual, bms_is_subset(), cost_index(), create_bitmap_heap_path(), create_seqscan_path(), create_subqueryscan_path(), get_baserel_parampathinfo(), makeNode, Path::param_info, Path::parent, IndexPath::path, PATH_REQ_OUTER, Path::pathkeys, Path::pathtype, T_BitmapHeapScan, T_IndexOnlyScan, T_IndexScan, T_SeqScan, and T_SubqueryScan.

Referenced by set_append_rel_pathlist().

{
    RelOptInfo *rel = path->parent;

    /* Can only increase, not decrease, path's parameterization */
    if (!bms_is_subset(PATH_REQ_OUTER(path), required_outer))
        return NULL;
    switch (path->pathtype)
    {
        case T_SeqScan:
            return create_seqscan_path(root, rel, required_outer);
        case T_IndexScan:
        case T_IndexOnlyScan:
            {
                IndexPath  *ipath = (IndexPath *) path;
                IndexPath  *newpath = makeNode(IndexPath);

                /*
                 * We can't use create_index_path directly, and would not want
                 * to because it would re-compute the indexqual conditions
                 * which is wasted effort.  Instead we hack things a bit:
                 * flat-copy the path node, revise its param_info, and redo
                 * the cost estimate.
                 */
                memcpy(newpath, ipath, sizeof(IndexPath));
                newpath->path.param_info =
                    get_baserel_parampathinfo(root, rel, required_outer);
                cost_index(newpath, root, loop_count);
                return (Path *) newpath;
            }
        case T_BitmapHeapScan:
            {
                BitmapHeapPath *bpath = (BitmapHeapPath *) path;

                return (Path *) create_bitmap_heap_path(root,
                                                        rel,
                                                        bpath->bitmapqual,
                                                        required_outer,
                                                        loop_count);
            }
        case T_SubqueryScan:
            return create_subqueryscan_path(root, rel, path->pathkeys,
                                            required_outer);
        default:
            break;
    }
    return NULL;
}

void set_cheapest ( RelOptInfo parent_rel  ) 

Definition at line 226 of file pathnode.c.

References Assert, BMS_DIFFERENT, BMS_EQUAL, BMS_SUBSET1, BMS_SUBSET2, bms_subset_compare(), RelOptInfo::cheapest_parameterized_paths, RelOptInfo::cheapest_startup_path, RelOptInfo::cheapest_total_path, RelOptInfo::cheapest_unique_path, compare_path_costs(), compare_pathkeys(), elog, ERROR, IsA, lappend(), lcons(), lfirst, NIL, NULL, Path::param_info, PATH_REQ_OUTER, Path::pathkeys, PATHKEYS_BETTER2, RelOptInfo::pathlist, STARTUP_COST, and TOTAL_COST.

Referenced by mark_dummy_rel(), merge_clump(), set_append_rel_pathlist(), set_cte_pathlist(), set_dummy_rel_pathlist(), set_foreign_pathlist(), set_function_pathlist(), set_plain_rel_pathlist(), set_subquery_pathlist(), set_values_pathlist(), set_worktable_pathlist(), and standard_join_search().

{
    Path       *cheapest_startup_path;
    Path       *cheapest_total_path;
    Path       *best_param_path;
    List       *parameterized_paths;
    ListCell   *p;

    Assert(IsA(parent_rel, RelOptInfo));

    if (parent_rel->pathlist == NIL)
        elog(ERROR, "could not devise a query plan for the given query");

    cheapest_startup_path = cheapest_total_path = best_param_path = NULL;
    parameterized_paths = NIL;

    foreach(p, parent_rel->pathlist)
    {
        Path       *path = (Path *) lfirst(p);
        int         cmp;

        if (path->param_info)
        {
            /* Parameterized path, so add it to parameterized_paths */
            parameterized_paths = lappend(parameterized_paths, path);

            /*
             * If we have an unparameterized cheapest-total, we no longer care
             * about finding the best parameterized path, so move on.
             */
            if (cheapest_total_path)
                continue;

            /*
             * Otherwise, track the best parameterized path, which is the one
             * with least total cost among those of the minimum
             * parameterization.
             */
            if (best_param_path == NULL)
                best_param_path = path;
            else
            {
                switch (bms_subset_compare(PATH_REQ_OUTER(path),
                                           PATH_REQ_OUTER(best_param_path)))
                {
                    case BMS_EQUAL:
                        /* keep the cheaper one */
                        if (compare_path_costs(path, best_param_path,
                                               TOTAL_COST) < 0)
                            best_param_path = path;
                        break;
                    case BMS_SUBSET1:
                        /* new path is less-parameterized */
                        best_param_path = path;
                        break;
                    case BMS_SUBSET2:
                        /* old path is less-parameterized, keep it */
                        break;
                    case BMS_DIFFERENT:
                        /*
                         * This means that neither path has the least possible
                         * parameterization for the rel.  We'll sit on the old
                         * path until something better comes along.
                         */
                        break;
                }
            }
        }
        else
        {
            /* Unparameterized path, so consider it for cheapest slots */
            if (cheapest_total_path == NULL)
            {
                cheapest_startup_path = cheapest_total_path = path;
                continue;
            }

            /*
             * If we find two paths of identical costs, try to keep the
             * better-sorted one.  The paths might have unrelated sort
             * orderings, in which case we can only guess which might be
             * better to keep, but if one is superior then we definitely
             * should keep that one.
             */
            cmp = compare_path_costs(cheapest_startup_path, path, STARTUP_COST);
            if (cmp > 0 ||
                (cmp == 0 &&
                 compare_pathkeys(cheapest_startup_path->pathkeys,
                                  path->pathkeys) == PATHKEYS_BETTER2))
                cheapest_startup_path = path;

            cmp = compare_path_costs(cheapest_total_path, path, TOTAL_COST);
            if (cmp > 0 ||
                (cmp == 0 &&
                 compare_pathkeys(cheapest_total_path->pathkeys,
                                  path->pathkeys) == PATHKEYS_BETTER2))
                cheapest_total_path = path;
        }
    }

    /* Add cheapest unparameterized path, if any, to parameterized_paths */
    if (cheapest_total_path)
        parameterized_paths = lcons(cheapest_total_path, parameterized_paths);

    /*
     * If there is no unparameterized path, use the best parameterized path
     * as cheapest_total_path (but not as cheapest_startup_path).
     */
    if (cheapest_total_path == NULL)
        cheapest_total_path = best_param_path;
    Assert(cheapest_total_path != NULL);

    parent_rel->cheapest_startup_path = cheapest_startup_path;
    parent_rel->cheapest_total_path = cheapest_total_path;
    parent_rel->cheapest_unique_path = NULL;    /* computed only if needed */
    parent_rel->cheapest_parameterized_paths = parameterized_paths;
}

static List * translate_sub_tlist ( List tlist,
int  relid 
) [static]

Definition at line 1434 of file pathnode.c.

References IsA, lappend_int(), lfirst, NIL, Var::varattno, and Var::varno.

Referenced by create_unique_path().

{
    List       *result = NIL;
    ListCell   *l;

    foreach(l, tlist)
    {
        Var        *var = (Var *) lfirst(l);

        if (!var || !IsA(var, Var) ||
            var->varno != relid)
            return NIL;         /* punt */

        result = lappend_int(result, var->varattno);
    }
    return result;
}