Create a function variable "join_search_hook" to let plugins override the
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 26 Sep 2007 18:51:51 +0000 (18:51 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 26 Sep 2007 18:51:51 +0000 (18:51 +0000)
join search order portion of the planner; this is specifically intended to
simplify developing a replacement for GEQO planning.  Patch by Julius
Stroffek, editorialized on by me.  I renamed make_one_rel_by_joins to
standard_join_search and make_rels_by_joins to join_search_one_level to better
reflect their place within this scheme.

src/backend/optimizer/README
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/joinrels.c
src/include/optimizer/paths.h

index 6f9d008cbfffa5abe99a45c49fe13dcb638575b6..fa16404fd4e38f877e31bbec34073ac28ea3ea1d 100644 (file)
@@ -292,16 +292,17 @@ planner()
    find qual clauses that enable merge and hash joins
 ----make_one_rel()
      set_base_rel_pathlist()
-      find scan and all index paths for each base relation
+      find seqscan and all index paths for each base relation
       find selectivity of columns used in joins
------make_one_rel_by_joins()
-      jump to geqo if needed
-      else call make_rels_by_joins() for each level of join tree needed
-      make_rels_by_joins():
+     make_rel_from_joinlist()
+      hand off join subproblems to a plugin, GEQO, or standard_join_search()
+-----standard_join_search()
+      call join_search_one_level() for each level of join tree needed
+      join_search_one_level():
         For each joinrel of the prior level, do make_rels_by_clause_joins()
         if it has join clauses, or make_rels_by_clauseless_joins() if not.
         Also generate "bushy plan" joins between joinrels of lower levels.
-      Back at make_one_rel_by_joins(), apply set_cheapest() to extract the
+      Back at standard_join_search(), apply set_cheapest() to extract the
       cheapest path for each newly constructed joinrel.
       Loop back if this wasn't the top join level.
    Back at query_planner:
index 0ad3dc5aae1b4c0f32107b20fe62ffc7a825c533..cc82380dc6d7fc7ed69aa798532ae924a181cd5b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.164 2007/05/26 18:23:01 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.165 2007/09/26 18:51:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,6 +37,9 @@
 bool           enable_geqo = false;    /* just in case GUC doesn't set it */
 int                    geqo_threshold;
 
+/* Hook for plugins to replace standard_join_search() */
+join_search_hook_type join_search_hook = NULL;
+
 
 static void set_base_rel_pathlists(PlannerInfo *root);
 static void set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
@@ -53,8 +56,6 @@ static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
 static void set_values_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                        RangeTblEntry *rte);
 static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
-static RelOptInfo *make_one_rel_by_joins(PlannerInfo *root, int levels_needed,
-                                         List *initial_rels);
 static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
                                                  bool *differentTypes);
 static bool recurse_pushdown_safe(Node *setOp, Query *topquery,
@@ -672,18 +673,20 @@ make_rel_from_joinlist(PlannerInfo *root, List *joinlist)
        {
                /*
                 * Consider the different orders in which we could join the rels,
-                * using either GEQO or regular optimizer.
+                * using a plugin, GEQO, or the regular join search code.
                 */
-               if (enable_geqo && levels_needed >= geqo_threshold)
+               if (join_search_hook)
+                       return (*join_search_hook) (root, levels_needed, initial_rels);
+               else if (enable_geqo && levels_needed >= geqo_threshold)
                        return geqo(root, levels_needed, initial_rels);
                else
-                       return make_one_rel_by_joins(root, levels_needed, initial_rels);
+                       return standard_join_search(root, levels_needed, initial_rels);
        }
 }
 
 /*
- * make_one_rel_by_joins
- *       Find all possible joinpaths for a query by successively finding ways
+ * standard_join_search
+ *       Find possible joinpaths for a query by successively finding ways
  *       to join component relations into join relations.
  *
  * 'levels_needed' is the number of iterations needed, ie, the number of
@@ -691,12 +694,27 @@ make_rel_from_joinlist(PlannerInfo *root, List *joinlist)
  *
  * 'initial_rels' is a list of RelOptInfo nodes for each independent
  *             jointree item.  These are the components to be joined together.
+ *             Note that levels_needed == list_length(initial_rels).
  *
  * Returns the final level of join relations, i.e., the relation that is
  * the result of joining all the original relations together.
+ * At least one implementation path must be provided for this relation and
+ * all required sub-relations.
+ *
+ * To support loadable plugins that modify planner behavior by changing the
+ * join searching algorithm, we provide a hook variable that lets a plugin
+ * replace or supplement this function.  Any such hook must return the same
+ * final join relation as the standard code would, but it might have a
+ * different set of implementation paths attached, and only the sub-joinrels
+ * needed for these paths need have been instantiated.
+ *
+ * Note to plugin authors: the functions invoked during standard_join_search()
+ * modify root->join_rel_list and root->join_rel_hash.  If you want to do more
+ * than one join-order search, you'll probably need to save and restore the
+ * original states of those data structures.  See geqo_eval() for an example.
  */
-static RelOptInfo *
-make_one_rel_by_joins(PlannerInfo *root, int levels_needed, List *initial_rels)
+RelOptInfo *
+standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
 {
        List      **joinitems;
        int                     lev;
@@ -725,7 +743,7 @@ make_one_rel_by_joins(PlannerInfo *root, int levels_needed, List *initial_rels)
                 * level, and build paths for making each one from every available
                 * pair of lower-level relations.
                 */
-               joinitems[lev] = make_rels_by_joins(root, lev, joinitems);
+               joinitems[lev] = join_search_one_level(root, lev, joinitems);
 
                /*
                 * Do cleanup work on each just-processed rel.
index d7f9d802bb39d48cdf59bfd7f00047848ab08c82..6ee442236ef967a28cf36d907d1fa8c73d042bf2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.86 2007/02/16 00:14:01 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.87 2007/09/26 18:51:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,10 +29,10 @@ static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
 
 
 /*
- * make_rels_by_joins
+ * join_search_one_level
  *       Consider ways to produce join relations containing exactly 'level'
  *       jointree items.  (This is one step of the dynamic-programming method
- *       embodied in make_one_rel_by_joins.)  Join rel nodes for each feasible
+ *       embodied in standard_join_search.)  Join rel nodes for each feasible
  *       combination of lower-level rels are created and returned in a list.
  *       Implementation paths are created for each such joinrel, too.
  *
@@ -40,7 +40,7 @@ static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
  * joinrels[j], 1 <= j < level, is a list of rels containing j items.
  */
 List *
-make_rels_by_joins(PlannerInfo *root, int level, List **joinrels)
+join_search_one_level(PlannerInfo *root, int level, List **joinrels)
 {
        List       *result_rels = NIL;
        List       *new_rels;
@@ -638,9 +638,9 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2)
  * Note: this is only a problem if one side of a degenerate outer join
  * contains multiple rels, or a clauseless join is required within an IN's
  * RHS; else we will find a join path via the "last ditch" case in
- * make_rels_by_joins().  We could dispense with this test if we were willing
- * to try bushy plans in the "last ditch" case, but that seems much less
- * efficient.
+ * join_search_one_level().  We could dispense with this test if we were
+ * willing to try bushy plans in the "last ditch" case, but that seems much
+ * less efficient.
  */
 bool
 have_join_order_restriction(PlannerInfo *root,
index 7199a545b29d840c6209a7b76f9843dff3e4e6ae..cbde0c7b9a5a97915eb17596d6ef40c991a8d33c 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.98 2007/05/22 01:40:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.99 2007/09/26 18:51:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 extern bool enable_geqo;
 extern int     geqo_threshold;
 
+/* Hook for plugins to replace standard_join_search() */
+typedef RelOptInfo * (*join_search_hook_type) (PlannerInfo *root,
+                                                                                          int levels_needed,
+                                                                                          List *initial_rels);
+extern PGDLLIMPORT join_search_hook_type join_search_hook;
+
+
 extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist);
+extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
+                                                                               List *initial_rels);
 
 #ifdef OPTIMIZER_DEBUG
 extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel);
@@ -89,7 +98,8 @@ extern void add_paths_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel,
  * joinrels.c
  *       routines to determine which relations to join
  */
-extern List *make_rels_by_joins(PlannerInfo *root, int level, List **joinrels);
+extern List *join_search_one_level(PlannerInfo *root, int level,
+                                                                  List **joinrels);
 extern RelOptInfo *make_join_rel(PlannerInfo *root,
                          RelOptInfo *rel1, RelOptInfo *rel2);
 extern bool have_join_order_restriction(PlannerInfo *root,