From f5bc74192d2ffb32952a06c62b3458d28ff7f98f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 16 Jul 2009 20:55:44 +0000
Subject: [PATCH] Make GEQO's planning deterministic by having it start from a
 predictable random number seed each time.  This is how it used to work years
 ago, but we got rid of the seed reset because it was resetting the main
 random() sequence and thus having undesirable effects on the rest of the
 system. To fix, establish a private random number state for each execution of
 geqo(), and initialize the state using the new GUC variable geqo_seed. People
 who want to experiment with different random searches can do so by changing
 geqo_seed, but you'll always get the same plan for the same value of
 geqo_seed (if holding all other planner inputs constant, of course).

The new state is kept in PlannerInfo by adding a "void *" field reserved
for use by join_search hooks.  Most of the rather bulky code changes in
this commit are just arranging to pass PlannerInfo around to all the GEQO
functions (many of which formerly didn't receive it).

Andres Freund, with some editorialization by Tom
---
 doc/src/sgml/config.sgml                      | 20 +++-
 doc/src/sgml/geqo.sgml                        | 21 ++--
 src/backend/optimizer/geqo/Makefile           |  4 +-
 src/backend/optimizer/geqo/geqo_copy.c        |  5 +-
 src/backend/optimizer/geqo/geqo_cx.c          |  7 +-
 src/backend/optimizer/geqo/geqo_erx.c         | 51 +++++-----
 src/backend/optimizer/geqo/geqo_eval.c        | 28 +++---
 src/backend/optimizer/geqo/geqo_main.c        | 99 ++++++++++---------
 src/backend/optimizer/geqo/geqo_mutation.c    | 12 +--
 src/backend/optimizer/geqo/geqo_ox1.c         |  9 +-
 src/backend/optimizer/geqo/geqo_ox2.c         |  8 +-
 src/backend/optimizer/geqo/geqo_pmx.c         |  8 +-
 src/backend/optimizer/geqo/geqo_pool.c        | 25 +++--
 src/backend/optimizer/geqo/geqo_px.c          |  9 +-
 src/backend/optimizer/geqo/geqo_random.c      | 40 ++++++++
 .../optimizer/geqo/geqo_recombination.c       | 10 +-
 src/backend/optimizer/geqo/geqo_selection.c   | 24 ++---
 src/backend/utils/misc/guc.c                  | 10 +-
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/nodes/relation.h                  |  5 +-
 src/include/optimizer/geqo.h                  | 20 ++--
 src/include/optimizer/geqo_copy.h             |  7 +-
 src/include/optimizer/geqo_mutation.h         |  7 +-
 src/include/optimizer/geqo_pool.h             | 16 +--
 src/include/optimizer/geqo_random.h           | 15 +--
 src/include/optimizer/geqo_recombination.h    | 37 ++++---
 src/include/optimizer/geqo_selection.h        |  9 +-
 27 files changed, 305 insertions(+), 202 deletions(-)
 create mode 100644 src/backend/optimizer/geqo/geqo_random.c

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 3b527a7ecbd..a86ba6089a4 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.221 2009/07/03 19:14:25 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.222 2009/07/16 20:55:44 tgl Exp $ -->
 
 <chapter Id="runtime-config">
   <title>Server Configuration</title>
@@ -2149,7 +2149,23 @@ archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"'  # Windows
        </para>
       </listitem>
      </varlistentry>
-     
+
+     <varlistentry id="guc-geqo-seed" xreflabel="geqo_seed">
+      <term><varname>geqo_seed</varname> (<type>floating point</type>)</term>
+      <indexterm>
+       <primary><varname>geqo_seed</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Controls the initial value of the random number generator used
+        by GEQO to select random paths through the join order search space.
+        The value can range from zero (the default) to one.  Varying the
+        value changes the set of join paths explored, and may result in a
+        better or worse best path being found.
+       </para>
+      </listitem>
+     </varlistentry>
+
      </variablelist>
     </sect2>
      <sect2 id="runtime-config-query-other">
diff --git a/doc/src/sgml/geqo.sgml b/doc/src/sgml/geqo.sgml
index 2f680762c13..97961272a4a 100644
--- a/doc/src/sgml/geqo.sgml
+++ b/doc/src/sgml/geqo.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/geqo.sgml,v 1.40 2007/07/21 04:02:41 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/geqo.sgml,v 1.41 2009/07/16 20:55:44 tgl Exp $ -->
 
  <chapter id="geqo">
   <chapterinfo>
@@ -49,7 +49,7 @@
     methods</firstterm> (e.g., nested loop, hash join, merge join in
     <productname>PostgreSQL</productname>) to process individual joins
     and a diversity of <firstterm>indexes</firstterm> (e.g.,
-    B-tree, hash, GiST and GIN in <productname>PostgreSQL</productname>) as 
+    B-tree, hash, GiST and GIN in <productname>PostgreSQL</productname>) as
     access paths for relations.
    </para>
 
@@ -88,8 +88,7 @@
 
    <para>
     The genetic algorithm (<acronym>GA</acronym>) is a heuristic optimization method which
-    operates through
-    nondeterministic, randomized search. The set of possible solutions for the
+    operates through randomized search. The set of possible solutions for the
     optimization problem is considered as a
     <firstterm>population</firstterm> of <firstterm>individuals</firstterm>.
     The degree of adaptation of an individual to its environment is specified
@@ -116,7 +115,7 @@
     According to the <systemitem class="resource">comp.ai.genetic</> <acronym>FAQ</acronym> it cannot be stressed too
     strongly that a <acronym>GA</acronym> is not a pure random search for a solution to a
     problem. A <acronym>GA</acronym> uses stochastic processes, but the result is distinctly
-    non-random (better than random). 
+    non-random (better than random).
    </para>
 
    <figure id="geqo-diagram">
@@ -260,9 +259,13 @@
    <para>
     This process is inherently nondeterministic, because of the randomized
     choices made during both the initial population selection and subsequent
-    <quote>mutation</> of the best candidates.  Hence different plans may
-    be selected from one run to the next, resulting in varying run time
-    and varying output row order.
+    <quote>mutation</> of the best candidates.  To avoid surprising changes
+    of the selected plan, each run of the GEQO algorithm restarts its
+    random number generator with the current <xref linkend="guc-geqo-seed">
+    parameter setting.  As long as <varname>geqo_seed</> and the other
+    GEQO parameters are kept fixed, the same plan will be generated for a
+    given query (and other planner inputs such as statistics).  To experiment
+    with different search paths, try changing <varname>geqo_seed</>.
    </para>
 
   </sect2>
@@ -330,7 +333,7 @@
       url="news://comp.ai.genetic"></ulink>)
      </para>
     </listitem>
-   
+
     <listitem>
      <para>
       <ulink url="http://www.red3d.com/cwr/evolve.html">
diff --git a/src/backend/optimizer/geqo/Makefile b/src/backend/optimizer/geqo/Makefile
index becc95f2ef4..9ccfd9e60c8 100644
--- a/src/backend/optimizer/geqo/Makefile
+++ b/src/backend/optimizer/geqo/Makefile
@@ -5,7 +5,7 @@
 #
 # Copyright (c) 1994, Regents of the University of California
 #
-# $PostgreSQL: pgsql/src/backend/optimizer/geqo/Makefile,v 1.20 2008/02/19 10:30:07 petere Exp $
+# $PostgreSQL: pgsql/src/backend/optimizer/geqo/Makefile,v 1.21 2009/07/16 20:55:44 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -14,7 +14,7 @@ top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS =	geqo_copy.o geqo_eval.o geqo_main.o geqo_misc.o \
-	geqo_mutation.o geqo_pool.o geqo_recombination.o \
+	geqo_mutation.o geqo_pool.o geqo_random.o geqo_recombination.o \
 	geqo_selection.o \
 	geqo_erx.o geqo_pmx.o geqo_cx.o geqo_px.o geqo_ox1.o geqo_ox2.o
 
diff --git a/src/backend/optimizer/geqo/geqo_copy.c b/src/backend/optimizer/geqo/geqo_copy.c
index 8e4aab214d6..1c7498c959c 100644
--- a/src/backend/optimizer/geqo/geqo_copy.c
+++ b/src/backend/optimizer/geqo/geqo_copy.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_copy.c,v 1.19 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_copy.c,v 1.20 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,8 @@
  *
  */
 void
-geqo_copy(Chromosome *chromo1, Chromosome *chromo2, int string_length)
+geqo_copy(PlannerInfo *root, Chromosome *chromo1, Chromosome *chromo2,
+		  int string_length)
 {
 	int			i;
 
diff --git a/src/backend/optimizer/geqo/geqo_cx.c b/src/backend/optimizer/geqo/geqo_cx.c
index 616ff41fb97..9f3dfe216e0 100644
--- a/src/backend/optimizer/geqo/geqo_cx.c
+++ b/src/backend/optimizer/geqo/geqo_cx.c
@@ -6,7 +6,7 @@
 *	 CX operator according to Oliver et al
 *	 (Proc 2nd Int'l Conf on GA's)
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_cx.c,v 1.10 2003/11/29 22:39:49 pgsql Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_cx.c,v 1.11 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -44,7 +44,8 @@
  *	 cycle crossover
  */
 int
-cx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
+cx(PlannerInfo *root, Gene *tour1, Gene *tour2, Gene *offspring,
+   int num_gene, City *city_table)
 {
 
 	int			i,
@@ -62,7 +63,7 @@ cx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
 	}
 
 	/* choose random cycle starting position */
-	start_pos = geqo_randint(num_gene - 1, 0);
+	start_pos = geqo_randint(root, num_gene - 1, 0);
 
 	/* child inherits first city  */
 	offspring[start_pos] = tour1[start_pos];
diff --git a/src/backend/optimizer/geqo/geqo_erx.c b/src/backend/optimizer/geqo/geqo_erx.c
index 9c7a3425858..45effbdad95 100644
--- a/src/backend/optimizer/geqo/geqo_erx.c
+++ b/src/backend/optimizer/geqo/geqo_erx.c
@@ -3,7 +3,7 @@
 * geqo_erx.c
 *	 edge recombination crossover [ER]
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_erx.c,v 1.20 2005/10/15 02:49:19 momjian Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_erx.c,v 1.21 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -36,11 +36,11 @@
 #include "optimizer/geqo_random.h"
 
 
-static int	gimme_edge(Gene gene1, Gene gene2, Edge *edge_table);
-static void remove_gene(Gene gene, Edge edge, Edge *edge_table);
-static Gene gimme_gene(Edge edge, Edge *edge_table);
+static int	gimme_edge(PlannerInfo *root, Gene gene1, Gene gene2, Edge *edge_table);
+static void remove_gene(PlannerInfo *root, Gene gene, Edge edge, Edge *edge_table);
+static Gene gimme_gene(PlannerInfo *root, Edge edge, Edge *edge_table);
 
-static Gene edge_failure(Gene *gene, int index, Edge *edge_table, int num_gene);
+static Gene edge_failure(PlannerInfo *root, Gene *gene, int index, Edge *edge_table, int num_gene);
 
 
 /* alloc_edge_table
@@ -50,7 +50,7 @@ static Gene edge_failure(Gene *gene, int index, Edge *edge_table, int num_gene);
  */
 
 Edge *
-alloc_edge_table(int num_gene)
+alloc_edge_table(PlannerInfo *root, int num_gene)
 {
 	Edge	   *edge_table;
 
@@ -70,7 +70,7 @@ alloc_edge_table(int num_gene)
  *
  */
 void
-free_edge_table(Edge *edge_table)
+free_edge_table(PlannerInfo *root, Edge *edge_table)
 {
 	pfree(edge_table);
 }
@@ -89,7 +89,8 @@ free_edge_table(Edge *edge_table)
  *
  */
 float
-gimme_edge_table(Gene *tour1, Gene *tour2, int num_gene, Edge *edge_table)
+gimme_edge_table(PlannerInfo *root, Gene *tour1, Gene *tour2,
+				 int num_gene, Edge *edge_table)
 {
 	int			i,
 				index1,
@@ -121,11 +122,11 @@ gimme_edge_table(Gene *tour1, Gene *tour2, int num_gene, Edge *edge_table)
 		 * twice per edge
 		 */
 
-		edge_total += gimme_edge(tour1[index1], tour1[index2], edge_table);
-		gimme_edge(tour1[index2], tour1[index1], edge_table);
+		edge_total += gimme_edge(root, tour1[index1], tour1[index2], edge_table);
+		gimme_edge(root, tour1[index2], tour1[index1], edge_table);
 
-		edge_total += gimme_edge(tour2[index1], tour2[index2], edge_table);
-		gimme_edge(tour2[index2], tour2[index1], edge_table);
+		edge_total += gimme_edge(root, tour2[index1], tour2[index2], edge_table);
+		gimme_edge(root, tour2[index2], tour2[index1], edge_table);
 	}
 
 	/* return average number of edges per index */
@@ -147,7 +148,7 @@ gimme_edge_table(Gene *tour1, Gene *tour2, int num_gene, Edge *edge_table)
  *			  0 if edge was already registered and edge_table is unchanged
  */
 static int
-gimme_edge(Gene gene1, Gene gene2, Edge *edge_table)
+gimme_edge(PlannerInfo *root, Gene gene1, Gene gene2, Edge *edge_table)
 {
 	int			i;
 	int			edges;
@@ -189,13 +190,13 @@ gimme_edge(Gene gene1, Gene gene2, Edge *edge_table)
  *
  */
 int
-gimme_tour(Edge *edge_table, Gene *new_gene, int num_gene)
+gimme_tour(PlannerInfo *root, Edge *edge_table, Gene *new_gene, int num_gene)
 {
 	int			i;
 	int			edge_failures = 0;
 
-	new_gene[0] = (Gene) geqo_randint(num_gene, 1);		/* choose int between 1
-														 * and num_gene */
+	/* choose int between 1 and num_gene */
+	new_gene[0] = (Gene) geqo_randint(root, num_gene, 1);
 
 	for (i = 1; i < num_gene; i++)
 	{
@@ -204,18 +205,18 @@ gimme_tour(Edge *edge_table, Gene *new_gene, int num_gene)
 		 * table
 		 */
 
-		remove_gene(new_gene[i - 1], edge_table[(int) new_gene[i - 1]], edge_table);
+		remove_gene(root, new_gene[i - 1], edge_table[(int) new_gene[i - 1]], edge_table);
 
 		/* find destination for the newly entered point */
 
 		if (edge_table[new_gene[i - 1]].unused_edges > 0)
-			new_gene[i] = gimme_gene(edge_table[(int) new_gene[i - 1]], edge_table);
+			new_gene[i] = gimme_gene(root, edge_table[(int) new_gene[i - 1]], edge_table);
 
 		else
 		{						/* cope with fault */
 			edge_failures++;
 
-			new_gene[i] = edge_failure(new_gene, i - 1, edge_table, num_gene);
+			new_gene[i] = edge_failure(root, new_gene, i - 1, edge_table, num_gene);
 		}
 
 		/* mark this node as incorporated */
@@ -235,7 +236,7 @@ gimme_tour(Edge *edge_table, Gene *new_gene, int num_gene)
  *
  */
 static void
-remove_gene(Gene gene, Edge edge, Edge *edge_table)
+remove_gene(PlannerInfo *root, Gene gene, Edge edge, Edge *edge_table)
 {
 	int			i,
 				j;
@@ -277,7 +278,7 @@ remove_gene(Gene gene, Edge edge, Edge *edge_table)
  *
  */
 static Gene
-gimme_gene(Edge edge, Edge *edge_table)
+gimme_gene(PlannerInfo *root, Edge edge, Edge *edge_table)
 {
 	int			i;
 	Gene		friend;
@@ -340,7 +341,7 @@ gimme_gene(Edge edge, Edge *edge_table)
 
 
 	/* random decision of the possible candidates to use */
-	rand_decision = (int) geqo_randint(minimum_count - 1, 0);
+	rand_decision = geqo_randint(root, minimum_count - 1, 0);
 
 
 	for (i = 0; i < edge.unused_edges; i++)
@@ -368,7 +369,7 @@ gimme_gene(Edge edge, Edge *edge_table)
  *
  */
 static Gene
-edge_failure(Gene *gene, int index, Edge *edge_table, int num_gene)
+edge_failure(PlannerInfo *root, Gene *gene, int index, Edge *edge_table, int num_gene)
 {
 	int			i;
 	Gene		fail_gene = gene[index];
@@ -401,7 +402,7 @@ edge_failure(Gene *gene, int index, Edge *edge_table, int num_gene)
 	if (four_count != 0)
 	{
 
-		rand_decision = (int) geqo_randint(four_count - 1, 0);
+		rand_decision = geqo_randint(root, four_count - 1, 0);
 
 		for (i = 1; i <= num_gene; i++)
 		{
@@ -423,7 +424,7 @@ edge_failure(Gene *gene, int index, Edge *edge_table, int num_gene)
 	else if (remaining_edges != 0)
 	{
 		/* random decision of the gene with remaining edges */
-		rand_decision = (int) geqo_randint(remaining_edges - 1, 0);
+		rand_decision = geqo_randint(root, remaining_edges - 1, 0);
 
 		for (i = 1; i <= num_gene; i++)
 		{
diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c
index 392cfc5aa31..226d64a94d9 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-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.88 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.89 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,7 @@ static bool desirable_join(PlannerInfo *root,
  * Returns cost of a query tree as an individual of the population.
  */
 Cost
-geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
+geqo_eval(PlannerInfo *root, Gene *tour, int num_gene)
 {
 	MemoryContext mycontext;
 	MemoryContext oldcxt;
@@ -94,13 +94,13 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
 	 * (If we are dealing with enough join rels, which we very likely are, a
 	 * new hash table will get built and used locally.)
 	 */
-	savelength = list_length(evaldata->root->join_rel_list);
-	savehash = evaldata->root->join_rel_hash;
+	savelength = list_length(root->join_rel_list);
+	savehash = root->join_rel_hash;
 
-	evaldata->root->join_rel_hash = NULL;
+	root->join_rel_hash = NULL;
 
 	/* construct the best path for the given combination of relations */
-	joinrel = gimme_tree(tour, num_gene, evaldata);
+	joinrel = gimme_tree(root, tour, num_gene);
 
 	/*
 	 * compute fitness
@@ -117,9 +117,9 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
 	 * Restore join_rel_list to its former state, and put back original
 	 * hashtable if any.
 	 */
-	evaldata->root->join_rel_list = list_truncate(evaldata->root->join_rel_list,
-												  savelength);
-	evaldata->root->join_rel_hash = savehash;
+	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);
@@ -134,7 +134,6 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
  *	  order.
  *
  *	 'tour' is the proposed join order, of length 'num_gene'
- *	 'evaldata' contains the context we need
  *
  * 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.
@@ -153,8 +152,9 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
  * plans.
  */
 RelOptInfo *
-gimme_tree(Gene *tour, int num_gene, GeqoEvalData *evaldata)
+gimme_tree(PlannerInfo *root, Gene *tour, int num_gene)
 {
+	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
 	RelOptInfo **stack;
 	int			stack_depth;
 	RelOptInfo *joinrel;
@@ -193,7 +193,7 @@ gimme_tree(Gene *tour, int num_gene, GeqoEvalData *evaldata)
 
 		/* Get the next input relation and push it */
 		cur_rel_index = (int) tour[rel_count];
-		stack[stack_depth] = (RelOptInfo *) list_nth(evaldata->initial_rels,
+		stack[stack_depth] = (RelOptInfo *) list_nth(private->initial_rels,
 													 cur_rel_index - 1);
 		stack_depth++;
 
@@ -211,7 +211,7 @@ gimme_tree(Gene *tour, int num_gene, GeqoEvalData *evaldata)
 			 * have exhausted the input, the heuristics can't prevent popping.
 			 */
 			if (rel_count < num_gene - 1 &&
-				!desirable_join(evaldata->root, outer_rel, inner_rel))
+				!desirable_join(root, outer_rel, inner_rel))
 				break;
 
 			/*
@@ -220,7 +220,7 @@ gimme_tree(Gene *tour, int num_gene, GeqoEvalData *evaldata)
 			 * root->join_rel_list yet, and so the paths constructed for it
 			 * will only include the ones we want.
 			 */
-			joinrel = make_join_rel(evaldata->root, outer_rel, inner_rel);
+			joinrel = make_join_rel(root, outer_rel, inner_rel);
 
 			/* Can't pop stack here if join order is not valid */
 			if (!joinrel)
diff --git a/src/backend/optimizer/geqo/geqo_main.c b/src/backend/optimizer/geqo/geqo_main.c
index 08b5b1a44db..824ae7b6108 100644
--- a/src/backend/optimizer/geqo/geqo_main.c
+++ b/src/backend/optimizer/geqo/geqo_main.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.56 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.57 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,7 +27,9 @@
 #include <math.h>
 
 #include "optimizer/geqo_misc.h"
+#include "optimizer/geqo_mutation.h"
 #include "optimizer/geqo_pool.h"
+#include "optimizer/geqo_random.h"
 #include "optimizer/geqo_selection.h"
 
 
@@ -38,6 +40,7 @@ int			Geqo_effort;
 int			Geqo_pool_size;
 int			Geqo_generations;
 double		Geqo_selection_bias;
+double		Geqo_seed;
 
 
 static int	gimme_pool_size(int nr_rel);
@@ -63,7 +66,7 @@ static int	gimme_number_generations(int pool_size);
 RelOptInfo *
 geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
 {
-	GeqoEvalData evaldata;
+	GeqoPrivateData private;
 	int			generation;
 	Chromosome *momma;
 	Chromosome *daddy;
@@ -88,9 +91,12 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
 	int			mutations = 0;
 #endif
 
-/* set up evaldata */
-	evaldata.root = root;
-	evaldata.initial_rels = initial_rels;
+/* set up private information */
+	root->join_search_private = (void *) &private;
+	private.initial_rels = initial_rels;
+
+/* initialize private number generator */
+	geqo_set_seed(root, Geqo_seed);
 
 /* set GA parameters */
 	pool_size = gimme_pool_size(number_of_rels);
@@ -98,13 +104,13 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
 	status_interval = 10;
 
 /* allocate genetic pool memory */
-	pool = alloc_pool(pool_size, number_of_rels);
+	pool = alloc_pool(root, pool_size, number_of_rels);
 
 /* random initialization of the pool */
-	random_init_pool(pool, &evaldata);
+	random_init_pool(root, pool);
 
 /* sort the pool according to cheapest path as fitness */
-	sort_pool(pool);			/* we have to do it only one time, since all
+	sort_pool(root, pool);		/* we have to do it only one time, since all
 								 * kids replace the worst individuals in
 								 * future (-> geqo_pool.c:spread_chromo ) */
 
@@ -116,49 +122,49 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
 #endif
 
 /* allocate chromosome momma and daddy memory */
-	momma = alloc_chromo(pool->string_length);
-	daddy = alloc_chromo(pool->string_length);
+	momma = alloc_chromo(root, pool->string_length);
+	daddy = alloc_chromo(root, pool->string_length);
 
 #if defined (ERX)
 #ifdef GEQO_DEBUG
 	elog(DEBUG2, "using edge recombination crossover [ERX]");
 #endif
 /* allocate edge table memory */
-	edge_table = alloc_edge_table(pool->string_length);
+	edge_table = alloc_edge_table(root, pool->string_length);
 #elif defined(PMX)
 #ifdef GEQO_DEBUG
 	elog(DEBUG2, "using partially matched crossover [PMX]");
 #endif
 /* allocate chromosome kid memory */
-	kid = alloc_chromo(pool->string_length);
+	kid = alloc_chromo(root, pool->string_length);
 #elif defined(CX)
 #ifdef GEQO_DEBUG
 	elog(DEBUG2, "using cycle crossover [CX]");
 #endif
 /* allocate city table memory */
-	kid = alloc_chromo(pool->string_length);
-	city_table = alloc_city_table(pool->string_length);
+	kid = alloc_chromo(root, pool->string_length);
+	city_table = alloc_city_table(root, pool->string_length);
 #elif defined(PX)
 #ifdef GEQO_DEBUG
 	elog(DEBUG2, "using position crossover [PX]");
 #endif
 /* allocate city table memory */
-	kid = alloc_chromo(pool->string_length);
-	city_table = alloc_city_table(pool->string_length);
+	kid = alloc_chromo(root, pool->string_length);
+	city_table = alloc_city_table(root, pool->string_length);
 #elif defined(OX1)
 #ifdef GEQO_DEBUG
 	elog(DEBUG2, "using order crossover [OX1]");
 #endif
 /* allocate city table memory */
-	kid = alloc_chromo(pool->string_length);
-	city_table = alloc_city_table(pool->string_length);
+	kid = alloc_chromo(root, pool->string_length);
+	city_table = alloc_city_table(root, pool->string_length);
 #elif defined(OX2)
 #ifdef GEQO_DEBUG
 	elog(DEBUG2, "using order crossover [OX2]");
 #endif
 /* allocate city table memory */
-	kid = alloc_chromo(pool->string_length);
-	city_table = alloc_city_table(pool->string_length);
+	kid = alloc_chromo(root, pool->string_length);
+	city_table = alloc_city_table(root, pool->string_length);
 #endif
 
 
@@ -168,45 +174,45 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
 	for (generation = 0; generation < number_generations; generation++)
 	{
 		/* SELECTION: using linear bias function */
-		geqo_selection(momma, daddy, pool, Geqo_selection_bias);
+		geqo_selection(root, momma, daddy, pool, Geqo_selection_bias);
 
 #if defined (ERX)
 		/* EDGE RECOMBINATION CROSSOVER */
-		difference = gimme_edge_table(momma->string, daddy->string, pool->string_length, edge_table);
+		difference = gimme_edge_table(root, momma->string, daddy->string, pool->string_length, edge_table);
 
 		kid = momma;
 
 		/* are there any edge failures ? */
-		edge_failures += gimme_tour(edge_table, kid->string, pool->string_length);
+		edge_failures += gimme_tour(root, edge_table, kid->string, pool->string_length);
 #elif defined(PMX)
 		/* PARTIALLY MATCHED CROSSOVER */
-		pmx(momma->string, daddy->string, kid->string, pool->string_length);
+		pmx(root, momma->string, daddy->string, kid->string, pool->string_length);
 #elif defined(CX)
 		/* CYCLE CROSSOVER */
-		cycle_diffs = cx(momma->string, daddy->string, kid->string, pool->string_length, city_table);
+		cycle_diffs = cx(root, momma->string, daddy->string, kid->string, pool->string_length, city_table);
 		/* mutate the child */
 		if (cycle_diffs == 0)
 		{
 			mutations++;
-			geqo_mutation(kid->string, pool->string_length);
+			geqo_mutation(root, kid->string, pool->string_length);
 		}
 #elif defined(PX)
 		/* POSITION CROSSOVER */
-		px(momma->string, daddy->string, kid->string, pool->string_length, city_table);
+		px(root, momma->string, daddy->string, kid->string, pool->string_length, city_table);
 #elif defined(OX1)
 		/* ORDER CROSSOVER */
-		ox1(momma->string, daddy->string, kid->string, pool->string_length, city_table);
+		ox1(root, momma->string, daddy->string, kid->string, pool->string_length, city_table);
 #elif defined(OX2)
 		/* ORDER CROSSOVER */
-		ox2(momma->string, daddy->string, kid->string, pool->string_length, city_table);
+		ox2(root, momma->string, daddy->string, kid->string, pool->string_length, city_table);
 #endif
 
 
 		/* EVALUATE FITNESS */
-		kid->worth = geqo_eval(kid->string, pool->string_length, &evaldata);
+		kid->worth = geqo_eval(root, kid->string, pool->string_length);
 
 		/* push the kid into the wilderness of life according to its worth */
-		spread_chromo(kid, pool);
+		spread_chromo(root, kid, pool);
 
 
 #ifdef GEQO_DEBUG
@@ -249,7 +255,7 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
 	 */
 	best_tour = (Gene *) pool->data[0].string;
 
-	best_rel = gimme_tree(best_tour, pool->string_length, &evaldata);
+	best_rel = gimme_tree(root, best_tour, pool->string_length);
 
 	if (best_rel == NULL)
 		elog(ERROR, "failed to make a valid plan");
@@ -260,28 +266,31 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
 #endif
 
 	/* ... free memory stuff */
-	free_chromo(momma);
-	free_chromo(daddy);
+	free_chromo(root, momma);
+	free_chromo(root, daddy);
 
 #if defined (ERX)
-	free_edge_table(edge_table);
+	free_edge_table(root, edge_table);
 #elif defined(PMX)
-	free_chromo(kid);
+	free_chromo(root, kid);
 #elif defined(CX)
-	free_chromo(kid);
-	free_city_table(city_table);
+	free_chromo(root, kid);
+	free_city_table(root, city_table);
 #elif defined(PX)
-	free_chromo(kid);
-	free_city_table(city_table);
+	free_chromo(root, kid);
+	free_city_table(root, city_table);
 #elif defined(OX1)
-	free_chromo(kid);
-	free_city_table(city_table);
+	free_chromo(root, kid);
+	free_city_table(root, city_table);
 #elif defined(OX2)
-	free_chromo(kid);
-	free_city_table(city_table);
+	free_chromo(root, kid);
+	free_city_table(root, city_table);
 #endif
 
-	free_pool(pool);
+	free_pool(root, pool);
+
+	/* ... clear root pointer to our private storage */
+	root->join_search_private = NULL;
 
 	return best_rel;
 }
diff --git a/src/backend/optimizer/geqo/geqo_mutation.c b/src/backend/optimizer/geqo/geqo_mutation.c
index 946eb117e3d..bd527324f5a 100644
--- a/src/backend/optimizer/geqo/geqo_mutation.c
+++ b/src/backend/optimizer/geqo/geqo_mutation.c
@@ -4,7 +4,7 @@
 *
 *	 TSP mutation routines
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_mutation.c,v 1.9 2003/11/29 22:39:49 pgsql Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_mutation.c,v 1.10 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -36,21 +36,21 @@
 #include "optimizer/geqo_random.h"
 
 void
-geqo_mutation(Gene *tour, int num_gene)
+geqo_mutation(PlannerInfo *root, Gene *tour, int num_gene)
 {
 	int			swap1;
 	int			swap2;
-	int			num_swaps = geqo_randint(num_gene / 3, 0);
+	int			num_swaps = geqo_randint(root, num_gene / 3, 0);
 	Gene		temp;
 
 
 	while (num_swaps > 0)
 	{
-		swap1 = geqo_randint(num_gene - 1, 0);
-		swap2 = geqo_randint(num_gene - 1, 0);
+		swap1 = geqo_randint(root, num_gene - 1, 0);
+		swap2 = geqo_randint(root, num_gene - 1, 0);
 
 		while (swap1 == swap2)
-			swap2 = geqo_randint(num_gene - 1, 0);
+			swap2 = geqo_randint(root, num_gene - 1, 0);
 
 		temp = tour[swap1];
 		tour[swap1] = tour[swap2];
diff --git a/src/backend/optimizer/geqo/geqo_ox1.c b/src/backend/optimizer/geqo/geqo_ox1.c
index 268194e295c..d11d3cd9c39 100644
--- a/src/backend/optimizer/geqo/geqo_ox1.c
+++ b/src/backend/optimizer/geqo/geqo_ox1.c
@@ -6,7 +6,7 @@
 *	 OX1 operator according to Davis
 *	 (Proc Int'l Joint Conf on AI)
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_ox1.c,v 1.9 2003/11/29 22:39:49 pgsql Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_ox1.c,v 1.10 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -43,7 +43,8 @@
  *	 position crossover
  */
 void
-ox1(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
+ox1(PlannerInfo *root, Gene *tour1, Gene *tour2, Gene *offspring, int num_gene,
+	City *city_table)
 {
 	int			left,
 				right,
@@ -56,8 +57,8 @@ ox1(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
 		city_table[k].used = 0;
 
 	/* select portion to copy from tour1 */
-	left = geqo_randint(num_gene - 1, 0);
-	right = geqo_randint(num_gene - 1, 0);
+	left = geqo_randint(root, num_gene - 1, 0);
+	right = geqo_randint(root, num_gene - 1, 0);
 
 	if (left > right)
 	{
diff --git a/src/backend/optimizer/geqo/geqo_ox2.c b/src/backend/optimizer/geqo/geqo_ox2.c
index 0c31cccae52..48ed359a630 100644
--- a/src/backend/optimizer/geqo/geqo_ox2.c
+++ b/src/backend/optimizer/geqo/geqo_ox2.c
@@ -6,7 +6,7 @@
 *	 OX2 operator according to Syswerda
 *	 (The Genetic Algorithms Handbook, ed L Davis)
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_ox2.c,v 1.10 2004/10/07 15:21:52 momjian Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_ox2.c,v 1.11 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -43,7 +43,7 @@
  *	 position crossover
  */
 void
-ox2(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
+ox2(PlannerInfo *root, Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
 {
 	int			k,
 				j,
@@ -60,12 +60,12 @@ ox2(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
 	}
 
 	/* determine the number of positions to be inherited from tour1  */
-	num_positions = geqo_randint(2 * num_gene / 3, num_gene / 3);
+	num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3);
 
 	/* make a list of selected cities */
 	for (k = 0; k < num_positions; k++)
 	{
-		pos = geqo_randint(num_gene - 1, 0);
+		pos = geqo_randint(root, num_gene - 1, 0);
 		city_table[pos].select_list = (int) tour1[pos];
 		city_table[(int) tour1[pos]].used = 1;	/* mark used */
 	}
diff --git a/src/backend/optimizer/geqo/geqo_pmx.c b/src/backend/optimizer/geqo/geqo_pmx.c
index dc52b731523..a1f3d1019ec 100644
--- a/src/backend/optimizer/geqo/geqo_pmx.c
+++ b/src/backend/optimizer/geqo/geqo_pmx.c
@@ -6,7 +6,7 @@
 *	 PMX operator according to Goldberg & Lingle
 *	 (Proc Int'l Conf on GA's)
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_pmx.c,v 1.10 2003/11/29 22:39:49 pgsql Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_pmx.c,v 1.11 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -43,7 +43,7 @@
  *	 partially matched crossover
  */
 void
-pmx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene)
+pmx(PlannerInfo *root, Gene *tour1, Gene *tour2, Gene *offspring, int num_gene)
 {
 	int		   *failed = (int *) palloc((num_gene + 1) * sizeof(int));
 	int		   *from = (int *) palloc((num_gene + 1) * sizeof(int));
@@ -71,8 +71,8 @@ pmx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene)
 	}
 
 /* locate crossover points */
-	left = geqo_randint(num_gene - 1, 0);
-	right = geqo_randint(num_gene - 1, 0);
+	left = geqo_randint(root, num_gene - 1, 0);
+	right = geqo_randint(root, num_gene - 1, 0);
 
 	if (left > right)
 	{
diff --git a/src/backend/optimizer/geqo/geqo_pool.c b/src/backend/optimizer/geqo/geqo_pool.c
index 7562d2ee0b3..26137272f70 100644
--- a/src/backend/optimizer/geqo/geqo_pool.c
+++ b/src/backend/optimizer/geqo/geqo_pool.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_pool.c,v 1.33 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_pool.c,v 1.34 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,7 +39,7 @@ static int	compare(const void *arg1, const void *arg2);
  *		allocates memory for GA pool
  */
 Pool *
-alloc_pool(int pool_size, int string_length)
+alloc_pool(PlannerInfo *root, int pool_size, int string_length)
 {
 	Pool	   *new_pool;
 	Chromosome *chromo;
@@ -66,7 +66,7 @@ alloc_pool(int pool_size, int string_length)
  *		deallocates memory for GA pool
  */
 void
-free_pool(Pool *pool)
+free_pool(PlannerInfo *root, Pool *pool)
 {
 	Chromosome *chromo;
 	int			i;
@@ -88,7 +88,7 @@ free_pool(Pool *pool)
  *		initialize genetic pool
  */
 void
-random_init_pool(Pool *pool, GeqoEvalData *evaldata)
+random_init_pool(PlannerInfo *root, Pool *pool)
 {
 	Chromosome *chromo = (Chromosome *) pool->data;
 	int			i;
@@ -105,10 +105,9 @@ random_init_pool(Pool *pool, GeqoEvalData *evaldata)
 	i = 0;
 	while (i < pool->size)
 	{
-		init_tour(chromo[i].string, pool->string_length);
-		pool->data[i].worth = geqo_eval(chromo[i].string,
-										pool->string_length,
-										evaldata);
+		init_tour(root, chromo[i].string, pool->string_length);
+		pool->data[i].worth = geqo_eval(root, chromo[i].string,
+										pool->string_length);
 		if (pool->data[i].worth < DBL_MAX)
 			i++;
 		else
@@ -133,7 +132,7 @@ random_init_pool(Pool *pool, GeqoEvalData *evaldata)
  *	 maybe you have to change compare() for different ordering ...
  */
 void
-sort_pool(Pool *pool)
+sort_pool(PlannerInfo *root, Pool *pool)
 {
 	qsort(pool->data, pool->size, sizeof(Chromosome), compare);
 }
@@ -160,7 +159,7 @@ compare(const void *arg1, const void *arg2)
  *	  allocates a chromosome and string space
  */
 Chromosome *
-alloc_chromo(int string_length)
+alloc_chromo(PlannerInfo *root, int string_length)
 {
 	Chromosome *chromo;
 
@@ -174,7 +173,7 @@ alloc_chromo(int string_length)
  *	  deallocates a chromosome and string space
  */
 void
-free_chromo(Chromosome *chromo)
+free_chromo(PlannerInfo *root, Chromosome *chromo)
 {
 	pfree(chromo->string);
 	pfree(chromo);
@@ -185,7 +184,7 @@ free_chromo(Chromosome *chromo)
  *	 assumes best->worst = smallest->largest
  */
 void
-spread_chromo(Chromosome *chromo, Pool *pool)
+spread_chromo(PlannerInfo *root, Chromosome *chromo, Pool *pool)
 {
 	int			top,
 				mid,
@@ -247,7 +246,7 @@ spread_chromo(Chromosome *chromo, Pool *pool)
 	 * copy new gene into pool storage; always replace worst gene in pool
 	 */
 
-	geqo_copy(&pool->data[pool->size - 1], chromo, pool->string_length);
+	geqo_copy(root, &pool->data[pool->size - 1], chromo, pool->string_length);
 
 	swap_chromo.string = pool->data[pool->size - 1].string;
 	swap_chromo.worth = pool->data[pool->size - 1].worth;
diff --git a/src/backend/optimizer/geqo/geqo_px.c b/src/backend/optimizer/geqo/geqo_px.c
index 8b6732b7224..674529313a2 100644
--- a/src/backend/optimizer/geqo/geqo_px.c
+++ b/src/backend/optimizer/geqo/geqo_px.c
@@ -6,7 +6,7 @@
 *	 PX operator according to Syswerda
 *	 (The Genetic Algorithms Handbook, L Davis, ed)
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_px.c,v 1.10 2003/11/29 22:39:49 pgsql Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_px.c,v 1.11 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -43,7 +43,8 @@
  *	 position crossover
  */
 void
-px(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
+px(PlannerInfo *root, Gene *tour1, Gene *tour2, Gene *offspring, int num_gene,
+   City *city_table)
 {
 
 	int			num_positions;
@@ -57,12 +58,12 @@ px(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table)
 		city_table[i].used = 0;
 
 	/* choose random positions that will be inherited directly from parent */
-	num_positions = geqo_randint(2 * num_gene / 3, num_gene / 3);
+	num_positions = geqo_randint(root, 2 * num_gene / 3, num_gene / 3);
 
 	/* choose random position */
 	for (i = 0; i < num_positions; i++)
 	{
-		pos = geqo_randint(num_gene - 1, 0);
+		pos = geqo_randint(root, num_gene - 1, 0);
 
 		offspring[pos] = tour1[pos];	/* transfer cities to child */
 		city_table[(int) tour1[pos]].used = 1;	/* mark city used */
diff --git a/src/backend/optimizer/geqo/geqo_random.c b/src/backend/optimizer/geqo/geqo_random.c
new file mode 100644
index 00000000000..025407e2d39
--- /dev/null
+++ b/src/backend/optimizer/geqo/geqo_random.c
@@ -0,0 +1,40 @@
+/*------------------------------------------------------------------------
+ *
+ * geqo_random.c
+ *	   random number generator
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_random.c,v 1.1 2009/07/16 20:55:44 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "optimizer/geqo_random.h"
+
+
+void
+geqo_set_seed(PlannerInfo *root, double seed)
+{
+	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
+
+	/*
+	 * XXX. This seeding algorithm could certainly be improved - but
+	 * it is not critical to do so.
+	 */
+	memset(private->random_state, 0, sizeof(private->random_state));
+	memcpy(private->random_state,
+		   &seed,
+		   Min(sizeof(private->random_state), sizeof(seed)));
+}
+
+double
+geqo_rand(PlannerInfo *root)
+{
+	GeqoPrivateData *private = (GeqoPrivateData *) root->join_search_private;
+
+	return erand48(private->random_state);
+}
diff --git a/src/backend/optimizer/geqo/geqo_recombination.c b/src/backend/optimizer/geqo/geqo_recombination.c
index c73e5b2a79e..ebdd2203839 100644
--- a/src/backend/optimizer/geqo/geqo_recombination.c
+++ b/src/backend/optimizer/geqo/geqo_recombination.c
@@ -3,7 +3,7 @@
 * geqo_recombination.c
 *	 misc recombination procedures
 *
-* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_recombination.c,v 1.15 2005/10/15 02:49:19 momjian Exp $
+* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_recombination.c,v 1.16 2009/07/16 20:55:44 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
@@ -35,7 +35,7 @@
  *	 and the procedure repeated.
  */
 void
-init_tour(Gene *tour, int num_gene)
+init_tour(PlannerInfo *root, Gene *tour, int num_gene)
 {
 	Gene	   *tmp;
 	int			remainder;
@@ -53,7 +53,7 @@ init_tour(Gene *tour, int num_gene)
 	for (i = 0; i < num_gene; i++)
 	{
 		/* choose value between 0 and remainder inclusive */
-		next = (int) geqo_randint(remainder, 0);
+		next = geqo_randint(root, remainder, 0);
 		/* output that element of the tmp array */
 		tour[i] = tmp[next];
 		/* and delete it */
@@ -81,7 +81,7 @@ init_tour(Gene *tour, int num_gene)
  *	 allocate memory for city table
  */
 City *
-alloc_city_table(int num_gene)
+alloc_city_table(PlannerInfo *root, int num_gene)
 {
 	City	   *city_table;
 
@@ -99,7 +99,7 @@ alloc_city_table(int num_gene)
  *	  deallocate memory of city table
  */
 void
-free_city_table(City *city_table)
+free_city_table(PlannerInfo *root, City *city_table)
 {
 	pfree(city_table);
 }
diff --git a/src/backend/optimizer/geqo/geqo_selection.c b/src/backend/optimizer/geqo/geqo_selection.c
index 3febca3c36f..0ba8941c22e 100644
--- a/src/backend/optimizer/geqo/geqo_selection.c
+++ b/src/backend/optimizer/geqo/geqo_selection.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_selection.c,v 1.24 2009/01/01 17:23:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_selection.c,v 1.25 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,7 +42,7 @@
 #include "optimizer/geqo_random.h"
 #include "optimizer/geqo_selection.h"
 
-static int	linear(int max, double bias);
+static int	linear(PlannerInfo *root, int max, double bias);
 
 
 /*
@@ -51,22 +51,23 @@ static int	linear(int max, double bias);
  *	 first and second genes are selected from the pool
  */
 void
-geqo_selection(Chromosome *momma, Chromosome *daddy, Pool *pool, double bias)
+geqo_selection(PlannerInfo *root, Chromosome *momma, Chromosome *daddy,
+			   Pool *pool, double bias)
 {
 	int			first,
 				second;
 
-	first = linear(pool->size, bias);
-	second = linear(pool->size, bias);
+	first = linear(root, pool->size, bias);
+	second = linear(root, pool->size, bias);
 
 	if (pool->size > 1)
 	{
 		while (first == second)
-			second = linear(pool->size, bias);
+			second = linear(root, pool->size, bias);
 	}
 
-	geqo_copy(momma, &pool->data[first], pool->string_length);
-	geqo_copy(daddy, &pool->data[second], pool->string_length);
+	geqo_copy(root, momma, &pool->data[first], pool->string_length);
+	geqo_copy(root, daddy, &pool->data[second], pool->string_length);
 }
 
 /*
@@ -74,12 +75,13 @@ geqo_selection(Chromosome *momma, Chromosome *daddy, Pool *pool, double bias)
  *	  generates random integer between 0 and input max number
  *	  using input linear bias
  *
+ *	  bias is y-intercept of linear distribution
+ *
  *	  probability distribution function is: f(x) = bias - 2(bias - 1)x
  *			 bias = (prob of first rule) / (prob of middle rule)
  */
 static int
-linear(int pool_size, double bias)		/* bias is y-intercept of linear
-										 * distribution */
+linear(PlannerInfo *root, int pool_size, double bias)
 {
 	double		index;			/* index between 0 and pop_size */
 	double		max = (double) pool_size;
@@ -95,7 +97,7 @@ linear(int pool_size, double bias)		/* bias is y-intercept of linear
 	{
 		double		sqrtval;
 
-		sqrtval = (bias * bias) - 4.0 * (bias - 1.0) * geqo_rand();
+		sqrtval = (bias * bias) - 4.0 * (bias - 1.0) * geqo_rand(root);
 		if (sqrtval > 0.0)
 			sqrtval = sqrt(sqrtval);
 		index = max * (bias - sqrtval) / 2.0 / (bias - 1.0);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2944249a692..f3b42da99b1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.507 2009/07/16 06:33:44 petere Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.508 2009/07/16 20:55:44 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -2026,6 +2026,14 @@ static struct config_real ConfigureNamesReal[] =
 		DEFAULT_GEQO_SELECTION_BIAS, MIN_GEQO_SELECTION_BIAS,
 		MAX_GEQO_SELECTION_BIAS, NULL, NULL
 	},
+	{
+		{"geqo_seed", PGC_USERSET, QUERY_TUNING_GEQO,
+			gettext_noop("GEQO: seed for random path selection."),
+			NULL
+		},
+		&Geqo_seed,
+		0.0, 0.0, 1.0, NULL, NULL
+	},
 
 	{
 		{"bgwriter_lru_multiplier", PGC_SIGHUP, RESOURCES,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c1b888c0d46..e50d7a44f7b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -213,6 +213,7 @@
 #geqo_pool_size = 0			# selects default based on effort
 #geqo_generations = 0			# selects default based on effort
 #geqo_selection_bias = 2.0		# range 1.5-2.0
+#geqo_seed = 0.0			# range 0.0-1.0
 
 # - Other Planner Options -
 
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 7eb15dbeecd..4432252a72f 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.173 2009/06/11 14:49:11 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.174 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -192,6 +192,9 @@ typedef struct PlannerInfo
 	/* These fields are used only when hasRecursion is true: */
 	int			wt_param_id;	/* PARAM_EXEC ID for the work table */
 	struct Plan *non_recursive_plan;	/* plan for non-recursive term */
+
+	/* optional private data for join_search_hook, e.g., GEQO */
+	void	   *join_search_private;
 } PlannerInfo;
 
 
diff --git a/src/include/optimizer/geqo.h b/src/include/optimizer/geqo.h
index fc06006fd3b..39226a7c7c9 100644
--- a/src/include/optimizer/geqo.h
+++ b/src/include/optimizer/geqo.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/geqo.h,v 1.44 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/geqo.h,v 1.45 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,7 +46,7 @@
 /*
  * Configuration options
  *
- * If you change these, update backend/utils/misc/postgresql.sample.conf
+ * If you change these, update backend/utils/misc/postgresql.conf.sample
  */
 extern int	Geqo_effort;		/* 1 .. 10, knob for adjustment of defaults */
 
@@ -64,16 +64,17 @@ extern double Geqo_selection_bias;
 #define MIN_GEQO_SELECTION_BIAS 1.5
 #define MAX_GEQO_SELECTION_BIAS 2.0
 
+extern double Geqo_seed;		/* 0 .. 1 */
+
 
 /*
- * Data structure to encapsulate information needed for building plan trees
- * (i.e., geqo_eval and gimme_tree).
+ * Private state for a GEQO run --- accessible via root->join_search_private
  */
 typedef struct
 {
-	PlannerInfo *root;			/* the query we are planning */
-	List	   *initial_rels;	/* the base relations */
-} GeqoEvalData;
+	List	   *initial_rels;			/* the base relations we are joining */
+	unsigned short random_state[3];		/* state for erand48() */
+} GeqoPrivateData;
 
 
 /* routines in geqo_main.c */
@@ -81,8 +82,7 @@ extern RelOptInfo *geqo(PlannerInfo *root,
 	 int number_of_rels, List *initial_rels);
 
 /* routines in geqo_eval.c */
-extern Cost geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata);
-extern RelOptInfo *gimme_tree(Gene *tour, int num_gene,
-		   GeqoEvalData *evaldata);
+extern Cost geqo_eval(PlannerInfo *root, Gene *tour, int num_gene);
+extern RelOptInfo *gimme_tree(PlannerInfo *root, Gene *tour, int num_gene);
 
 #endif   /* GEQO_H */
diff --git a/src/include/optimizer/geqo_copy.h b/src/include/optimizer/geqo_copy.h
index 53293c49161..582de22e6c3 100644
--- a/src/include/optimizer/geqo_copy.h
+++ b/src/include/optimizer/geqo_copy.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/geqo_copy.h,v 1.21 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/geqo_copy.h,v 1.22 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,8 +22,9 @@
 #ifndef GEQO_COPY_H
 #define GEQO_COPY_H
 
-#include "optimizer/geqo_gene.h"
+#include "optimizer/geqo.h"
 
-extern void geqo_copy(Chromosome *chromo1, Chromosome *chromo2, int string_length);
+
+extern void geqo_copy(PlannerInfo *root, Chromosome *chromo1, Chromosome *chromo2, int string_length);
 
 #endif   /* GEQO_COPY_H */
diff --git a/src/include/optimizer/geqo_mutation.h b/src/include/optimizer/geqo_mutation.h
index 430797c3707..559c35218db 100644
--- a/src/include/optimizer/geqo_mutation.h
+++ b/src/include/optimizer/geqo_mutation.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/geqo_mutation.h,v 1.21 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/geqo_mutation.h,v 1.22 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,8 +22,9 @@
 #ifndef GEQO_MUTATION_H
 #define GEQO_MUTATION_H
 
-#include "optimizer/geqo_gene.h"
+#include "optimizer/geqo.h"
 
-extern void geqo_mutation(Gene *tour, int num_gene);
+
+extern void geqo_mutation(PlannerInfo *root, Gene *tour, int num_gene);
 
 #endif   /* GEQO_MUTATION_H */
diff --git a/src/include/optimizer/geqo_pool.h b/src/include/optimizer/geqo_pool.h
index 282b2c2c5b7..277b144ebd0 100644
--- a/src/include/optimizer/geqo_pool.h
+++ b/src/include/optimizer/geqo_pool.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/geqo_pool.h,v 1.25 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/geqo_pool.h,v 1.26 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,15 +26,15 @@
 #include "optimizer/geqo.h"
 
 
-extern Pool *alloc_pool(int pool_size, int string_length);
-extern void free_pool(Pool *pool);
+extern Pool *alloc_pool(PlannerInfo *root, int pool_size, int string_length);
+extern void free_pool(PlannerInfo *root, Pool *pool);
 
-extern void random_init_pool(Pool *pool, GeqoEvalData *evaldata);
-extern Chromosome *alloc_chromo(int string_length);
-extern void free_chromo(Chromosome *chromo);
+extern void random_init_pool(PlannerInfo *root, Pool *pool);
+extern Chromosome *alloc_chromo(PlannerInfo *root, int string_length);
+extern void free_chromo(PlannerInfo *root, Chromosome *chromo);
 
-extern void spread_chromo(Chromosome *chromo, Pool *pool);
+extern void spread_chromo(PlannerInfo *root, Chromosome *chromo, Pool *pool);
 
-extern void sort_pool(Pool *pool);
+extern void sort_pool(PlannerInfo *root, Pool *pool);
 
 #endif   /* GEQO_POOL_H */
diff --git a/src/include/optimizer/geqo_random.h b/src/include/optimizer/geqo_random.h
index 327989b5fb8..cb1f60fa34e 100644
--- a/src/include/optimizer/geqo_random.h
+++ b/src/include/optimizer/geqo_random.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/geqo_random.h,v 1.21 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/geqo_random.h,v 1.22 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,13 +26,16 @@
 
 #include <math.h>
 
-/* geqo_rand returns a random float value between 0 and 1 inclusive */
+#include "optimizer/geqo.h"
 
-#define geqo_rand() ((double) random() / (double) MAX_RANDOM_VALUE)
 
-/* geqo_randint returns integer value between lower and upper inclusive */
+extern void geqo_set_seed(PlannerInfo *root, double seed);
 
-#define geqo_randint(upper,lower) \
-	( (int) floor( geqo_rand()*(((upper)-(lower))+0.999999) ) + (lower) )
+/* geqo_rand returns a random float value between 0 and 1 inclusive */
+extern double geqo_rand(PlannerInfo *root);
+
+/* geqo_randint returns integer value between lower and upper inclusive */
+#define geqo_randint(root, upper, lower) \
+	( (int) floor( geqo_rand(root)*(((upper)-(lower))+0.999999) ) + (lower) )
 
 #endif   /* GEQO_RANDOM_H */
diff --git a/src/include/optimizer/geqo_recombination.h b/src/include/optimizer/geqo_recombination.h
index 264560f1128..45678386506 100644
--- a/src/include/optimizer/geqo_recombination.h
+++ b/src/include/optimizer/geqo_recombination.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/geqo_recombination.h,v 1.20 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/geqo_recombination.h,v 1.21 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,9 +24,10 @@
 #ifndef GEQO_RECOMBINATION_H
 #define GEQO_RECOMBINATION_H
 
-#include "optimizer/geqo_gene.h"
+#include "optimizer/geqo.h"
 
-extern void init_tour(Gene *tour, int num_gene);
+
+extern void init_tour(PlannerInfo *root, Gene *tour, int num_gene);
 
 
 /* edge recombination crossover [ERX] */
@@ -38,12 +39,14 @@ typedef struct Edge
 	int			unused_edges;
 } Edge;
 
-extern Edge *alloc_edge_table(int num_gene);
-extern void free_edge_table(Edge *edge_table);
+extern Edge *alloc_edge_table(PlannerInfo *root, int num_gene);
+extern void free_edge_table(PlannerInfo *root, Edge *edge_table);
 
-extern float gimme_edge_table(Gene *tour1, Gene *tour2, int num_gene, Edge *edge_table);
+extern float gimme_edge_table(PlannerInfo *root, Gene *tour1, Gene *tour2,
+							  int num_gene, Edge *edge_table);
 
-extern int	gimme_tour(Edge *edge_table, Gene *new_gene, int num_gene);
+extern int	gimme_tour(PlannerInfo *root, Edge *edge_table, Gene *new_gene,
+					   int num_gene);
 
 
 /* partially matched crossover [PMX] */
@@ -51,7 +54,9 @@ extern int	gimme_tour(Edge *edge_table, Gene *new_gene, int num_gene);
 #define DAD 1					/* indicator for gene from dad */
 #define MOM 0					/* indicator for gene from mom */
 
-extern void pmx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene);
+extern void pmx(PlannerInfo *root,
+				Gene *tour1, Gene *tour2,
+				Gene *offspring, int num_gene);
 
 
 typedef struct City
@@ -62,19 +67,23 @@ typedef struct City
 	int			select_list;
 } City;
 
-extern City *alloc_city_table(int num_gene);
-extern void free_city_table(City *city_table);
+extern City *alloc_city_table(PlannerInfo *root, int num_gene);
+extern void free_city_table(PlannerInfo *root, City *city_table);
 
 /* cycle crossover [CX] */
-extern int	cx(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table);
+extern int	cx(PlannerInfo *root, Gene *tour1, Gene *tour2,
+			   Gene *offspring, int num_gene, City *city_table);
 
 /* position crossover [PX] */
-extern void px(Gene *tour1, Gene *tour2, Gene *offspring, int num_gene, City *city_table);
+extern void px(PlannerInfo *root, Gene *tour1, Gene *tour2, Gene *offspring,
+			   int num_gene, City *city_table);
 
 /* order crossover [OX1] according to Davis */
-extern void ox1(Gene *mom, Gene *dad, Gene *offspring, int num_gene, City *city_table);
+extern void ox1(PlannerInfo *root, Gene *mom, Gene *dad, Gene *offspring,
+				int num_gene, City *city_table);
 
 /* order crossover [OX2] according to Syswerda */
-extern void ox2(Gene *mom, Gene *dad, Gene *offspring, int num_gene, City *city_table);
+extern void ox2(PlannerInfo *root, Gene *mom, Gene *dad, Gene *offspring,
+				int num_gene, City *city_table);
 
 #endif   /* GEQO_RECOMBINATION_H */
diff --git a/src/include/optimizer/geqo_selection.h b/src/include/optimizer/geqo_selection.h
index e37a92b554e..166ba475350 100644
--- a/src/include/optimizer/geqo_selection.h
+++ b/src/include/optimizer/geqo_selection.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/geqo_selection.h,v 1.21 2009/01/01 17:24:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/geqo_selection.h,v 1.22 2009/07/16 20:55:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,8 +23,11 @@
 #ifndef GEQO_SELECTION_H
 #define GEQO_SELECTION_H
 
-#include "optimizer/geqo_gene.h"
+#include "optimizer/geqo.h"
 
-extern void geqo_selection(Chromosome *momma, Chromosome *daddy, Pool *pool, double bias);
+
+extern void geqo_selection(PlannerInfo *root,
+						   Chromosome *momma, Chromosome *daddy,
+						   Pool *pool, double bias);
 
 #endif   /* GEQO_SELECTION_H */
-- 
GitLab