Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  geqo_eval.c

  Sprache: C
 

/*------------------------------------------------------------------------
 *
 * geqo_eval.c
 *   Routines to evaluate query trees
 *
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/backend/optimizer/geqo/geqo_eval.c
 *
 *-------------------------------------------------------------------------
 */


/* contributed by:
   =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
   *  Martin Utesch     * Institute of Automatic Control    *
   =        = University of Mining and Technology =
   *  utesch@aut.tu-freiberg.de  * Freiberg, Germany       *
   =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
 */


#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"


/* A "clump" of already-joined relations within gimme_tree */
typedef struct
{
 RelOptInfo *joinrel;  /* joinrel for the set of relations */
 int   size;   /* number of input relations in clump */
} Clump;

static List *merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump,
       int num_gene, bool force);
static bool desirable_join(PlannerInfo *root,
         RelOptInfo *outer_rel, RelOptInfo *inner_rel);


/*
 * geqo_eval
 *
 * Returns cost of a query tree as an individual of the population.
 *
 * If no legal join order can be extracted from the proposed tour,
 * returns DBL_MAX.
 */

Cost
geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
{
 MemoryContext mycontext;
 MemoryContext oldcxt;
 RelOptInfo *joinrel;
 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_SIZES);
 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);

 /*
  * compute fitness, if we found a valid join
  *
  * 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?
 */

 if (joinrel)
 {
  Path    *best_path = joinrel->cheapest_total_path;

  fitness = best_path->total_cost;
 }
 else
  fitness = DBL_MAX;

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

/*
 * gimme_tree
 *   Form planner estimates for a join tree constructed in the specified
 *   order.
 *
 *  'tour' is the proposed join order, of length 'num_gene'
 *
 * Returns a new join relation whose cheapest path is the best plan for
 * this join order.  NB: will return NULL if join order is invalid and
 * we can't modify it into a valid order.
 *
 * The original implementation of this routine always joined in the specified
 * order, and so could only build left-sided plans (and right-sided and
 * mixtures, as a byproduct of the fact that make_join_rel() is symmetric).
 * It could never produce a "bushy" plan.  This had a couple of big problems,
 * of which the worst was that there are situations involving join order
 * restrictions where the only valid plans are bushy.
 *
 * The present implementation takes the given tour as a guideline, but
 * postpones joins that are illegal or seem unsuitable according to some
 * heuristic rules.  This allows correct bushy plans to be generated at need,
 * and as a nice side-effect it seems to materially improve the quality of the
 * generated plans.  Note however that since it's just a heuristic, it can
 * still fail in some cases.  (In particular, we might clump together
 * relations that actually mustn't be joined yet due to LATERAL restrictions;
 * since there's no provision for un-clumping, this must lead to failure.)
 */

RelOptInfo *
gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
{
 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.  If we are
  * unable to merge all the clumps into one, fail.
 */

 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, num_gene, 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, num_gene, true);
  }
  clumps = fclumps;
 }

 /* Did we succeed in forming a single join relation? */
 if (list_length(clumps) != 1)
  return NULL;

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

/*
 * Merge a "clump" into the list of existing clumps for gimme_tree.
 *
 * We try to merge the clump into some existing clump, and repeat if
 * successful.  When no more merging is possible, insert the clump
 * into the list, preserving the list ordering rule (namely, that
 * clumps of larger size appear earlier).
 *
 * If force is true, merge anywhere a join is legal, even if it causes
 * a cartesian join to be performed.  When force is false, do only
 * "desirable" joins.
 */

static List *
merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, int num_gene,
   bool force)
{
 ListCell   *lc;
 int   pos;

 /* Look for a clump that new_clump can join to */
 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)
   {
    /* Create paths for partitionwise joins. */
    generate_partitionwise_join_paths(root, joinrel);

    /*
     * Except for the topmost scan/join rel, consider gathering
     * partial paths.  We'll do the same for the topmost scan/join
     * rel once we know the final targetlist (see
     * grouping_planner).
 */

    if (!bms_equal(joinrel->relids, root->all_query_rels))
     generate_useful_gather_paths(root, joinrel, false);

    /* 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 = foreach_delete_current(clumps, lc);

    /*
     * 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, num_gene, force);
   }
  }
 }

 /*
  * 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);

 /* Else search for the place to insert it */
 for (pos = 0; pos < list_length(clumps); pos++)
 {
  Clump    *old_clump = (Clump *) list_nth(clumps, pos);

  if (new_clump->size > old_clump->size)
   break;    /* new_clump belongs before old_clump */
 }
 clumps = list_insert_nth(clumps, pos, new_clump);

 return clumps;
}

/*
 * Heuristics for gimme_tree: do we want to join these two relations?
 */

static bool
desirable_join(PlannerInfo *root,
      RelOptInfo *outer_rel, RelOptInfo *inner_rel)
{
 /*
  * 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;
}

Messung V0.5 in Prozent
C=92 H=93 G=92

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002