Header And Logo

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

Data Structures | Functions

geqo_eval.c File Reference

#include "postgres.h"
#include <float.h>
#include <limits.h>
#include <math.h>
#include "optimizer/geqo.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "utils/memutils.h"
Include dependency graph for geqo_eval.c:

Go to the source code of this file.

Data Structures

struct  Clump

Functions

static Listmerge_clump (PlannerInfo *root, List *clumps, Clump *new_clump, bool force)
static bool desirable_join (PlannerInfo *root, RelOptInfo *outer_rel, RelOptInfo *inner_rel)
Cost geqo_eval (PlannerInfo *root, Gene *tour, int num_gene)
RelOptInfogimme_tree (PlannerInfo *root, Gene *tour, int num_gene)

Function Documentation

static bool desirable_join ( PlannerInfo root,
RelOptInfo outer_rel,
RelOptInfo inner_rel 
) [static]

Definition at line 311 of file geqo_eval.c.

References have_join_order_restriction(), and have_relevant_joinclause().

Referenced by merge_clump().

{
    /*
     * Join if there is an applicable join clause, or if there is a join order
     * restriction forcing these rels to be joined.
     */
    if (have_relevant_joinclause(root, outer_rel, inner_rel) ||
        have_join_order_restriction(root, outer_rel, inner_rel))
        return true;

    /* Otherwise postpone the join till later. */
    return false;
}

Cost geqo_eval ( PlannerInfo root,
Gene tour,
int  num_gene 
)

Definition at line 54 of file geqo_eval.c.

References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Assert, RelOptInfo::cheapest_total_path, CurrentMemoryContext, gimme_tree(), PlannerInfo::join_rel_hash, PlannerInfo::join_rel_level, PlannerInfo::join_rel_list, list_length(), list_truncate(), MemoryContextDelete(), MemoryContextSwitchTo(), NULL, and Path::total_cost.

Referenced by geqo(), and random_init_pool().

{
    MemoryContext mycontext;
    MemoryContext oldcxt;
    RelOptInfo *joinrel;
    Path       *best_path;
    Cost        fitness;
    int         savelength;
    struct HTAB *savehash;

    /*
     * Create a private memory context that will hold all temp storage
     * allocated inside gimme_tree().
     *
     * Since geqo_eval() will be called many times, we can't afford to let all
     * that memory go unreclaimed until end of statement.  Note we make the
     * temp context a child of the planner's normal context, so that it will
     * be freed even if we abort via ereport(ERROR).
     */
    mycontext = AllocSetContextCreate(CurrentMemoryContext,
                                      "GEQO",
                                      ALLOCSET_DEFAULT_MINSIZE,
                                      ALLOCSET_DEFAULT_INITSIZE,
                                      ALLOCSET_DEFAULT_MAXSIZE);
    oldcxt = MemoryContextSwitchTo(mycontext);

    /*
     * gimme_tree will add entries to root->join_rel_list, which may or may
     * not already contain some entries.  The newly added entries will be
     * recycled by the MemoryContextDelete below, so we must ensure that the
     * list is restored to its former state before exiting.  We can do this by
     * truncating the list to its original length.  NOTE this assumes that any
     * added entries are appended at the end!
     *
     * We also must take care not to mess up the outer join_rel_hash, if there
     * is one.  We can do this by just temporarily setting the link to NULL.
     * (If we are dealing with enough join rels, which we very likely are, a
     * new hash table will get built and used locally.)
     *
     * join_rel_level[] shouldn't be in use, so just Assert it isn't.
     */
    savelength = list_length(root->join_rel_list);
    savehash = root->join_rel_hash;
    Assert(root->join_rel_level == NULL);

    root->join_rel_hash = NULL;

    /* construct the best path for the given combination of relations */
    joinrel = gimme_tree(root, tour, num_gene);
    best_path = joinrel->cheapest_total_path;

    /*
     * compute fitness
     *
     * XXX geqo does not currently support optimization for partial result
     * retrieval, nor do we take any cognizance of possible use of
     * parameterized paths --- how to fix?
     */
    fitness = best_path->total_cost;

    /*
     * Restore join_rel_list to its former state, and put back original
     * hashtable if any.
     */
    root->join_rel_list = list_truncate(root->join_rel_list,
                                        savelength);
    root->join_rel_hash = savehash;

    /* release all the memory acquired within gimme_tree */
    MemoryContextSwitchTo(oldcxt);
    MemoryContextDelete(mycontext);

    return fitness;
}

RelOptInfo* gimme_tree ( PlannerInfo root,
Gene tour,
int  num_gene 
)

Definition at line 153 of file geqo_eval.c.

References elog, ERROR, PlannerInfo::join_search_private, Clump::joinrel, lfirst, linitial, list_length(), list_nth(), merge_clump(), NIL, palloc(), and Clump::size.

Referenced by geqo(), and geqo_eval().

{
    GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
    List       *clumps;
    int         rel_count;

    /*
     * Sometimes, a relation can't yet be joined to others due to heuristics
     * or actual semantic restrictions.  We maintain a list of "clumps" of
     * successfully joined relations, with larger clumps at the front. Each
     * new relation from the tour is added to the first clump it can be joined
     * to; if there is none then it becomes a new clump of its own. When we
     * enlarge an existing clump we check to see if it can now be merged with
     * any other clumps.  After the tour is all scanned, we forget about the
     * heuristics and try to forcibly join any remaining clumps.  Some forced
     * joins might still fail due to semantics, but we should always be able
     * to find some join order that works.
     */
    clumps = NIL;

    for (rel_count = 0; rel_count < num_gene; rel_count++)
    {
        int         cur_rel_index;
        RelOptInfo *cur_rel;
        Clump      *cur_clump;

        /* Get the next input relation */
        cur_rel_index = (int) tour[rel_count];
        cur_rel = (RelOptInfo *) list_nth(private->initial_rels,
                                          cur_rel_index - 1);

        /* Make it into a single-rel clump */
        cur_clump = (Clump *) palloc(sizeof(Clump));
        cur_clump->joinrel = cur_rel;
        cur_clump->size = 1;

        /* Merge it into the clumps list, using only desirable joins */
        clumps = merge_clump(root, clumps, cur_clump, false);
    }

    if (list_length(clumps) > 1)
    {
        /* Force-join the remaining clumps in some legal order */
        List       *fclumps;
        ListCell   *lc;

        fclumps = NIL;
        foreach(lc, clumps)
        {
            Clump      *clump = (Clump *) lfirst(lc);

            fclumps = merge_clump(root, fclumps, clump, true);
        }
        clumps = fclumps;
    }

    /* Did we succeed in forming a single join relation? */
    if (list_length(clumps) != 1)
        elog(ERROR, "failed to join all relations together");

    return ((Clump *) linitial(clumps))->joinrel;
}

static List * merge_clump ( PlannerInfo root,
List clumps,
Clump new_clump,
bool  force 
) [static]

Definition at line 229 of file geqo_eval.c.

References desirable_join(), Clump::joinrel, lappend(), lappend_cell(), lcons(), lfirst, list_delete_cell(), list_head(), lnext, make_join_rel(), NIL, NULL, pfree(), set_cheapest(), and Clump::size.

Referenced by gimme_tree().

{
    ListCell   *prev;
    ListCell   *lc;

    /* Look for a clump that new_clump can join to */
    prev = NULL;
    foreach(lc, clumps)
    {
        Clump      *old_clump = (Clump *) lfirst(lc);

        if (force ||
            desirable_join(root, old_clump->joinrel, new_clump->joinrel))
        {
            RelOptInfo *joinrel;

            /*
             * Construct a RelOptInfo representing the join of these two input
             * relations.  Note that we expect the joinrel not to exist in
             * root->join_rel_list yet, and so the paths constructed for it
             * will only include the ones we want.
             */
            joinrel = make_join_rel(root,
                                    old_clump->joinrel,
                                    new_clump->joinrel);

            /* Keep searching if join order is not valid */
            if (joinrel)
            {
                /* Find and save the cheapest paths for this joinrel */
                set_cheapest(joinrel);

                /* Absorb new clump into old */
                old_clump->joinrel = joinrel;
                old_clump->size += new_clump->size;
                pfree(new_clump);

                /* Remove old_clump from list */
                clumps = list_delete_cell(clumps, lc, prev);

                /*
                 * Recursively try to merge the enlarged old_clump with
                 * others.  When no further merge is possible, we'll reinsert
                 * it into the list.
                 */
                return merge_clump(root, clumps, old_clump, force);
            }
        }
        prev = lc;
    }

    /*
     * No merging is possible, so add new_clump as an independent clump, in
     * proper order according to size.  We can be fast for the common case
     * where it has size 1 --- it should always go at the end.
     */
    if (clumps == NIL || new_clump->size == 1)
        return lappend(clumps, new_clump);

    /* Check if it belongs at the front */
    lc = list_head(clumps);
    if (new_clump->size > ((Clump *) lfirst(lc))->size)
        return lcons(new_clump, clumps);

    /* Else search for the place to insert it */
    for (;;)
    {
        ListCell   *nxt = lnext(lc);

        if (nxt == NULL || new_clump->size > ((Clump *) lfirst(nxt))->size)
            break;              /* it belongs after 'lc', before 'nxt' */
        lc = nxt;
    }
    lappend_cell(clumps, lc, new_clump);

    return clumps;
}