summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/geqo/geqo_eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/geqo/geqo_eval.c')
-rw-r--r--src/backend/optimizer/geqo/geqo_eval.c122
1 files changed, 66 insertions, 56 deletions
diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c
index 3e915747da..91b6c75e8e 100644
--- a/src/backend/optimizer/geqo/geqo_eval.c
+++ b/src/backend/optimizer/geqo/geqo_eval.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: geqo_eval.c,v 1.59 2002/06/20 20:29:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.60 2002/12/16 21:30:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,6 +21,7 @@
#include "postgres.h"
+#include <float.h>
#include <math.h>
#include <limits.h>
@@ -45,6 +46,20 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene)
List *savelist;
/*
+ * Because gimme_tree considers both left- and right-sided trees,
+ * there is no difference between a tour (a,b,c,d,...) and a tour
+ * (b,a,c,d,...) --- the same join orders will be considered.
+ * To avoid redundant cost calculations, we simply reject tours where
+ * tour[0] > tour[1], assigning them an artificially bad fitness.
+ *
+ * (It would be better to tweak the GEQO logic to not generate such tours
+ * in the first place, but I'm not sure of all the implications in the
+ * mutation logic.)
+ */
+ if (num_gene >= 2 && tour[0] > tour[1])
+ return DBL_MAX;
+
+ /*
* Create a private memory context that will hold all temp storage
* allocated inside gimme_tree().
*
@@ -60,11 +75,15 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene)
ALLOCSET_DEFAULT_MAXSIZE);
oldcxt = MemoryContextSwitchTo(mycontext);
- /* preserve root->join_rel_list, which gimme_tree changes */
+ /*
+ * preserve root->join_rel_list, which gimme_tree changes; without this,
+ * it'll be pointing at recycled storage after the MemoryContextDelete
+ * below.
+ */
savelist = root->join_rel_list;
/* construct the best path for the given combination of relations */
- joinrel = gimme_tree(root, initial_rels, tour, num_gene, 0, NULL);
+ joinrel = gimme_tree(root, initial_rels, tour, num_gene);
/*
* compute fitness
@@ -86,70 +105,61 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene)
/*
* gimme_tree
- * this routine considers only LEFT-SIDED TREES!
+ * Form planner estimates for a join tree constructed in the specified
+ * order.
*
* 'root' is the Query
* 'initial_rels' is the list of initial relations (FROM-list items)
* 'tour' is the proposed join order, of length 'num_gene'
- * 'rel_count' is number of initial_rels items already joined (initially 0)
- * 'old_rel' is the preceding join (initially NULL)
*
- * Returns a new join relation incorporating all joins in a left-sided tree.
+ * Returns a new join relation whose cheapest path is the best plan for
+ * this join order.
+ *
+ * Note that at each step we consider using the next rel as both left and
+ * right side of a join. However, we cannot build general ("bushy") plan
+ * trees this way, only left-sided and right-sided trees.
*/
RelOptInfo *
gimme_tree(Query *root, List *initial_rels,
- Gene *tour, int num_gene,
- int rel_count, RelOptInfo *old_rel)
+ Gene *tour, int num_gene)
{
- RelOptInfo *inner_rel; /* current relation */
- int init_rel_index;
+ RelOptInfo *joinrel;
+ int cur_rel_index;
+ int rel_count;
- if (rel_count < num_gene)
+ /*
+ * Start with the first relation ...
+ */
+ cur_rel_index = (int) tour[0];
+
+ joinrel = (RelOptInfo *) nth(cur_rel_index - 1, initial_rels);
+
+ /*
+ * And add on each relation in the specified order ...
+ */
+ for (rel_count = 1; rel_count < num_gene; rel_count++)
{
- /* tree not yet finished */
- init_rel_index = (int) tour[rel_count];
-
- inner_rel = (RelOptInfo *) nth(init_rel_index - 1, initial_rels);
-
- if (rel_count == 0)
- {
- /* processing first join with init_rel_index = (int) tour[0] */
- rel_count++;
- return gimme_tree(root, initial_rels,
- tour, num_gene,
- rel_count, inner_rel);
- }
- else
- {
- /* tree main part */
- List *acceptable_rels = makeList1(inner_rel);
- List *new_rels;
- RelOptInfo *new_rel;
-
- new_rels = make_rels_by_clause_joins(root, old_rel,
- acceptable_rels);
- /* Shouldn't get more than one result */
- Assert(length(new_rels) <= 1);
- if (new_rels == NIL)
- {
- new_rels = make_rels_by_clauseless_joins(root, old_rel,
- acceptable_rels);
- Assert(length(new_rels) <= 1);
- if (new_rels == NIL)
- elog(ERROR, "gimme_tree: failed to construct join rel");
- }
- new_rel = (RelOptInfo *) lfirst(new_rels);
-
- /* Find and save the cheapest paths for this rel */
- set_cheapest(new_rel);
-
- /* and recurse... */
- rel_count++;
- return gimme_tree(root, initial_rels,
- tour, num_gene,
- rel_count, new_rel);
- }
+ RelOptInfo *inner_rel;
+ RelOptInfo *new_rel;
+
+ cur_rel_index = (int) tour[rel_count];
+
+ inner_rel = (RelOptInfo *) nth(cur_rel_index - 1, initial_rels);
+
+ /*
+ * Construct a RelOptInfo representing the previous joinrel joined
+ * to inner_rel. These are always inner joins. 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.
+ */
+ new_rel = make_join_rel(root, joinrel, inner_rel, JOIN_INNER);
+
+ /* Find and save the cheapest paths for this rel */
+ set_cheapest(new_rel);
+
+ /* and repeat... */
+ joinrel = new_rel;
}
- return old_rel; /* tree finished ... */
+ return joinrel;
}