On Sun, Aug 16, 2009 at 5:31 PM, Robert Haas<[email protected]> wrote:
> It seems that the needed checks are very similar to the ones that we
> already implement when setting restrictinfo->mergeopfamilies. That is
> filled in by get_mergejoin_opfamilies(), which checks for btree
> opfamilies where the strategy number is BTEqualStrategyNumber. This
> might cease to be the correct check in the (not-too-distant?) future
> if we end up implementing other kinds of unique indices, but right now
> btrees are all there is.
>
> One possibility would be to have relation_is_distinct_for() call
> get_mergejoin_opfamilies() for each operator; then for each index we
> can check whether the opfamily of the relevant index column is in the
> returned list. This seems a bit wasteful, though, since I believe
> that relation_is_distinct_for() would be called from joinpath.c, which
> has access to restrictinfo->mergeopfamilies already.
>
> I'm wondering whether it would make more sense to modify the proposed
> API for relation_is_distinct_for() in some way so that we don't lose
> this information.
Here is an attempt at the latter approach. This doesn't actually
remove the join yet; it just checks whether the join can be removed.
I haven't tested it extensively yet, but am hoping for some feedback
on the basic approach.
...Robert
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
***************
*** 109,114 **** bool enable_hashagg = true;
--- 109,115 ----
bool enable_nestloop = true;
bool enable_mergejoin = true;
bool enable_hashjoin = true;
+ bool enable_joinremoval = true;
typedef struct
{
*** a/src/backend/optimizer/path/joinpath.c
--- b/src/backend/optimizer/path/joinpath.c
***************
*** 16,26 ****
--- 16,39 ----
#include <math.h>
+ #include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+ typedef struct path_operator_clause {
+ List *mergeclause_list;
+ List *hashclause_list;
+ List *joinremovalclause_list;
+ List *joinremovalvarattno_list;
+ } path_operator_clause;
+
+ static bool join_is_removable(RelOptInfo *joinrel,
+ RelOptInfo *outerrel,
+ RelOptInfo *innerrel,
+ path_operator_clause *poc,
+ JoinType jointype);
static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
***************
*** 31,47 **** static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
JoinType jointype, SpecialJoinInfo *sjinfo);
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
! List *restrictlist,
JoinType jointype, SpecialJoinInfo *sjinfo);
static Path *best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
RelOptInfo *outer_rel, JoinType jointype);
! static List *select_mergejoin_clauses(PlannerInfo *root,
! RelOptInfo *joinrel,
! RelOptInfo *outerrel,
! RelOptInfo *innerrel,
! List *restrictlist,
! JoinType jointype);
!
/*
* add_paths_to_joinrel
--- 44,61 ----
JoinType jointype, SpecialJoinInfo *sjinfo);
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
! List *restrictlist, List *hashclauses,
JoinType jointype, SpecialJoinInfo *sjinfo);
static Path *best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
RelOptInfo *outer_rel, JoinType jointype);
! static bool rel_attrs_needed_above_join(RelOptInfo *rel, RelOptInfo *joinrel);
! static void select_operator_clauses(PlannerInfo *root,
! RelOptInfo *joinrel,
! RelOptInfo *outerrel,
! RelOptInfo *innerrel,
! List *restrictlist,
! JoinType jointype,
! path_operator_clause *poc);
/*
* add_paths_to_joinrel
***************
*** 75,135 **** add_paths_to_joinrel(PlannerInfo *root,
SpecialJoinInfo *sjinfo,
List *restrictlist)
{
! List *mergeclause_list = NIL;
/*
! * Find potential mergejoin clauses. We can skip this if we are not
! * interested in doing a mergejoin. However, mergejoin is currently our
! * only way of implementing full outer joins, so override mergejoin
! * disable if it's a full join.
*/
! if (enable_mergejoin || jointype == JOIN_FULL)
! mergeclause_list = select_mergejoin_clauses(root,
! joinrel,
! outerrel,
! innerrel,
! restrictlist,
! jointype);
/*
! * 1. Consider mergejoin paths where both relations must be explicitly
* sorted.
*/
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
! restrictlist, mergeclause_list, jointype, sjinfo);
/*
! * 2. Consider paths where the outer relation need not be explicitly
* sorted. This includes both nestloops and mergejoins where the outer
* path is already ordered.
*/
match_unsorted_outer(root, joinrel, outerrel, innerrel,
! restrictlist, mergeclause_list, jointype, sjinfo);
!
! #ifdef NOT_USED
!
! /*
! * 3. Consider paths where the inner relation need not be explicitly
! * sorted. This includes mergejoins only (nestloops were already built in
! * match_unsorted_outer).
! *
! * Diked out as redundant 2/13/2000 -- tgl. There isn't any really
! * significant difference between the inner and outer side of a mergejoin,
! * so match_unsorted_inner creates no paths that aren't equivalent to
! * those made by match_unsorted_outer when add_paths_to_joinrel() is
! * invoked with the two rels given in the other order.
! */
! match_unsorted_inner(root, joinrel, outerrel, innerrel,
! restrictlist, mergeclause_list, jointype, sjinfo);
! #endif
/*
* 4. Consider paths where both outer and inner relations must be hashed
* before being joined.
*/
! if (enable_hashjoin)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
! restrictlist, jointype, sjinfo);
}
/*
--- 89,166 ----
SpecialJoinInfo *sjinfo,
List *restrictlist)
{
! path_operator_clause poc;
!
! /*
! * Use operator classes to find potential mergejoin clauses, hash join
! * clauses, and join removal clauses.
! */
! memset(&poc, 0, sizeof(path_operator_clause));
! select_operator_clauses(root, joinrel, outerrel, innerrel,
! restrictlist, jointype, &poc);
/*
! * 1. Consider join removal. This is always the most efficient strategy,
! * so if it works, there's no need to consider anything further.
*/
! if (poc.joinremovalclause_list != NIL
! && join_is_removable(joinrel, outerrel, innerrel, &poc, jointype))
! {
! /* XXX add noop path */
! elog(DEBUG1, "join is removable");
! /* XXX return; */
! }
/*
! * 2. Consider mergejoin paths where both relations must be explicitly
* sorted.
*/
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
! restrictlist, poc.mergeclause_list, jointype, sjinfo);
/*
! * 3. Consider paths where the outer relation need not be explicitly
* sorted. This includes both nestloops and mergejoins where the outer
* path is already ordered.
*/
match_unsorted_outer(root, joinrel, outerrel, innerrel,
! restrictlist, poc.mergeclause_list, jointype, sjinfo);
/*
* 4. Consider paths where both outer and inner relations must be hashed
* before being joined.
*/
! if (poc.hashclause_list != NIL)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
! restrictlist, poc.hashclause_list, jointype,
! sjinfo);
! }
!
! /*
! * Test whether a join can be implemented by scanning the outer side and
! * ignoring the inner side altogether.
! */
! static bool
! join_is_removable(RelOptInfo *joinrel,
! RelOptInfo *outerrel,
! RelOptInfo *innerrel,
! path_operator_clause *poc,
! JoinType jointype)
! {
! /* Shouldn't get here unless we have a left join with suitable clauses. */
! Assert(jointype == JOIN_LEFT && poc->joinremovalclause_list != NIL);
!
! /* We can't remove the join if it's providing needed attributes. */
! if (rel_attrs_needed_above_join(innerrel, joinrel))
! return false;
!
! /* Nullable side of the left join must be suitably unique. */
! if (!relation_is_distinct_for(innerrel, poc->joinremovalvarattno_list,
! poc->joinremovalclause_list))
! return false;
!
! /* OK to remove it. */
! return true;
}
/*
***************
*** 743,748 **** match_unsorted_outer(PlannerInfo *root,
--- 774,780 ----
* 'innerrel' is the inner join relation
* 'restrictlist' contains all of the RestrictInfo nodes for restriction
* clauses that apply to this join
+ * 'hashclauses' contains all the available hashclauses
* 'jointype' is the type of join to do
* 'sjinfo' is extra info about the join for selectivity estimation
*/
***************
*** 752,876 **** hash_inner_and_outer(PlannerInfo *root,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype,
SpecialJoinInfo *sjinfo)
{
- bool isouterjoin;
- List *hashclauses;
- ListCell *l;
-
/*
! * Hashjoin only supports inner, left, semi, and anti joins.
*/
! switch (jointype)
! {
! case JOIN_INNER:
! case JOIN_SEMI:
! case JOIN_UNIQUE_OUTER:
! case JOIN_UNIQUE_INNER:
! isouterjoin = false;
! break;
! case JOIN_LEFT:
! case JOIN_ANTI:
! isouterjoin = true;
! break;
! default:
! return;
! }
! /*
! * We need to build only one hashpath for any given pair of outer and
! * inner relations; all of the hashable clauses will be used as keys.
! *
! * Scan the join's restrictinfo list to find hashjoinable clauses that are
! * usable with this pair of sub-relations.
! */
! hashclauses = NIL;
! foreach(l, restrictlist)
{
! RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
!
! if (!restrictinfo->can_join ||
! restrictinfo->hashjoinoperator == InvalidOid)
! continue; /* not hashjoinable */
!
! /*
! * If processing an outer join, only use its own join clauses for
! * hashing. For inner joins we need not be so picky.
! */
! if (isouterjoin && restrictinfo->is_pushed_down)
! continue;
!
! /*
! * Check if clause is usable with these input rels.
! */
! if (bms_is_subset(restrictinfo->left_relids, outerrel->relids) &&
! bms_is_subset(restrictinfo->right_relids, innerrel->relids))
! {
! /* righthand side is inner */
! }
! else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) &&
! bms_is_subset(restrictinfo->right_relids, outerrel->relids))
! {
! /* lefthand side is inner */
! }
! else
! continue; /* no good for these input relations */
!
! hashclauses = lappend(hashclauses, restrictinfo);
}
!
! /* If we found any usable hashclauses, make a path */
! if (hashclauses)
{
! /*
! * We consider both the cheapest-total-cost and cheapest-startup-cost
! * outer paths. There's no need to consider any but the
! * cheapest-total-cost inner path, however.
! */
! Path *cheapest_startup_outer = outerrel->cheapest_startup_path;
! Path *cheapest_total_outer = outerrel->cheapest_total_path;
! Path *cheapest_total_inner = innerrel->cheapest_total_path;
!
! /* Unique-ify if need be */
! if (jointype == JOIN_UNIQUE_OUTER)
! {
! cheapest_total_outer = (Path *)
! create_unique_path(root, outerrel,
! cheapest_total_outer, sjinfo);
! Assert(cheapest_total_outer);
! cheapest_startup_outer = cheapest_total_outer;
! jointype = JOIN_INNER;
! }
! else if (jointype == JOIN_UNIQUE_INNER)
! {
! cheapest_total_inner = (Path *)
! create_unique_path(root, innerrel,
! cheapest_total_inner, sjinfo);
! Assert(cheapest_total_inner);
! jointype = JOIN_INNER;
! }
add_path(joinrel, (Path *)
create_hashjoin_path(root,
joinrel,
jointype,
sjinfo,
! cheapest_total_outer,
cheapest_total_inner,
restrictlist,
hashclauses));
- if (cheapest_startup_outer != cheapest_total_outer)
- add_path(joinrel, (Path *)
- create_hashjoin_path(root,
- joinrel,
- jointype,
- sjinfo,
- cheapest_startup_outer,
- cheapest_total_inner,
- restrictlist,
- hashclauses));
- }
}
/*
--- 784,840 ----
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
+ List *hashclauses,
JoinType jointype,
SpecialJoinInfo *sjinfo)
{
/*
! * We consider both the cheapest-total-cost and cheapest-startup-cost
! * outer paths. There's no need to consider any but the
! * cheapest-total-cost inner path, however.
*/
! Path *cheapest_startup_outer = outerrel->cheapest_startup_path;
! Path *cheapest_total_outer = outerrel->cheapest_total_path;
! Path *cheapest_total_inner = innerrel->cheapest_total_path;
! /* Unique-ify if need be */
! if (jointype == JOIN_UNIQUE_OUTER)
{
! cheapest_total_outer = (Path *)
! create_unique_path(root, outerrel,
! cheapest_total_outer, sjinfo);
! Assert(cheapest_total_outer);
! cheapest_startup_outer = cheapest_total_outer;
! jointype = JOIN_INNER;
}
! else if (jointype == JOIN_UNIQUE_INNER)
{
! cheapest_total_inner = (Path *)
! create_unique_path(root, innerrel,
! cheapest_total_inner, sjinfo);
! Assert(cheapest_total_inner);
! jointype = JOIN_INNER;
! }
+ add_path(joinrel, (Path *)
+ create_hashjoin_path(root,
+ joinrel,
+ jointype,
+ sjinfo,
+ cheapest_total_outer,
+ cheapest_total_inner,
+ restrictlist,
+ hashclauses));
+ if (cheapest_startup_outer != cheapest_total_outer)
add_path(joinrel, (Path *)
create_hashjoin_path(root,
joinrel,
jointype,
sjinfo,
! cheapest_startup_outer,
cheapest_total_inner,
restrictlist,
hashclauses));
}
/*
***************
*** 943,973 **** best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,
}
/*
! * select_mergejoin_clauses
! * Select mergejoin clauses that are usable for a particular join.
! * Returns a list of RestrictInfo nodes for those clauses.
*
* We also mark each selected RestrictInfo to show which side is currently
* being considered as outer. These are transient markings that are only
* good for the duration of the current add_paths_to_joinrel() call!
*
* We examine each restrictinfo clause known for the join to see
! * if it is mergejoinable and involves vars from the two sub-relations
! * currently of interest.
*/
! static List *
! select_mergejoin_clauses(PlannerInfo *root,
! RelOptInfo *joinrel,
! RelOptInfo *outerrel,
! RelOptInfo *innerrel,
! List *restrictlist,
! JoinType jointype)
{
- List *result_list = NIL;
bool isouterjoin = IS_OUTER_JOIN(jointype);
bool have_nonmergeable_joinclause = false;
ListCell *l;
foreach(l, restrictlist)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
--- 907,972 ----
}
/*
! * rel_attrs_needed_above_join
! * Determine whether any attributes from rel are used outside of joinrel.
! *
! * As a micro-optimization, it seems better to start with max_attr and count
! * down rather than starting with min_attr and counting up, on the theory that
! * the system attributes are somewhat less likely to be what is wanted and
! * should be tested last.
! */
! static bool
! rel_attrs_needed_above_join(RelOptInfo *rel, RelOptInfo *joinrel)
! {
! int attroff;
! for (attroff = rel->max_attr - rel->min_attr; attroff >= 0; --attroff)
! if (!bms_is_subset(rel->attr_needed[attroff], joinrel->relids))
! return true;
! return false;
! }
!
! /*
! * select_operator_clauses
! * Select operator clauses that are usable for merging, hashing, or
! * removal of a particular join. Populates path_operator_clause structure.
*
* We also mark each selected RestrictInfo to show which side is currently
* being considered as outer. These are transient markings that are only
* good for the duration of the current add_paths_to_joinrel() call!
*
* We examine each restrictinfo clause known for the join to see
! * if it is mergejoinable and/or hashjoinable and involves vars from the two
! * sub-relations currently of interest. We also test for claues that could
! * possibly be useful for join removal.
! *
! * All results are returned through the path_operator_clause structure.
*/
! static void
! select_operator_clauses(PlannerInfo *root,
! RelOptInfo *joinrel,
! RelOptInfo *outerrel,
! RelOptInfo *innerrel,
! List *restrictlist,
! JoinType jointype,
! path_operator_clause *poc)
{
bool isouterjoin = IS_OUTER_JOIN(jointype);
bool have_nonmergeable_joinclause = false;
+ bool joinremovalOK = enable_joinremoval;
ListCell *l;
+ /*
+ * Currently, we only know how to remove left joins to a baserel with
+ * unique indices. We can check most of these criteria pretty trivially
+ * to avoid doing useless extra work. But checking whether any of the
+ * indices are unique would require iterating over the indexlist, so for
+ * now we just make sure there are indices of some sort or other. If none
+ * of them are unique, join removal will still fail, just slightly later.
+ */
+ if (jointype != JOIN_LEFT || innerrel->reloptkind != RELOPT_BASEREL
+ || innerrel->rtekind != RTE_RELATION || innerrel->indexlist == NIL)
+ joinremovalOK = false;
+
foreach(l, restrictlist)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
***************
*** 981,988 **** select_mergejoin_clauses(PlannerInfo *root,
if (isouterjoin && restrictinfo->is_pushed_down)
continue;
! if (!restrictinfo->can_join ||
! restrictinfo->mergeopfamilies == NIL)
{
have_nonmergeable_joinclause = true;
continue; /* not mergejoinable */
--- 980,991 ----
if (isouterjoin && restrictinfo->is_pushed_down)
continue;
! /*
! * If this restrictinfo is not a binary opclause with nonoverlapping
! * sets of relids, it is useless for all purposes we care about here.
! * See comments regarding can_join in include/nodes/relation.h
! */
! if (!restrictinfo->can_join)
{
have_nonmergeable_joinclause = true;
continue; /* not mergejoinable */
***************
*** 1012,1046 **** select_mergejoin_clauses(PlannerInfo *root,
}
/*
! * Insist that each side have a non-redundant eclass. This
! * restriction is needed because various bits of the planner expect
! * that each clause in a merge be associatable with some pathkey in a
! * canonical pathkey list, but redundant eclasses can't appear in
! * canonical sort orderings. (XXX it might be worth relaxing this,
! * but not enough time to address it for 8.3.)
! *
! * Note: it would be bad if this condition failed for an otherwise
! * mergejoinable FULL JOIN clause, since that would result in
! * undesirable planner failure. I believe that is not possible
! * however; a variable involved in a full join could only appear in
! * below_outer_join eclasses, which aren't considered redundant.
*
! * This case *can* happen for left/right join clauses: the outer-side
! * variable could be equated to a constant. Because we will propagate
! * that constant across the join clause, the loss of ability to do a
! * mergejoin is not really all that big a deal, and so it's not clear
! * that improving this is important.
*/
! cache_mergeclause_eclasses(root, restrictinfo);
!
! if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) ||
! EC_MUST_BE_REDUNDANT(restrictinfo->right_ec))
{
! have_nonmergeable_joinclause = true;
! continue; /* can't handle redundant eclasses */
}
! result_list = lappend(result_list, restrictinfo);
}
/*
--- 1015,1089 ----
}
/*
! * Mergejoin-specific checks.
*
! * At present, mergejoin is our only way of implementing FULL joins,
! * so we ignore the enable_mergejoin flag if this is a FULL join.
*/
! if (enable_mergejoin || jointype == JOIN_FULL)
{
! if (restrictinfo->mergeopfamilies == NIL)
! have_nonmergeable_joinclause = true;
! else
! {
! /*
! * Insist that each side have a non-redundant eclass. This
! * restriction is needed because various bits of the planner
! * expect that each clause in a merge be associatable with some
! * pathkey in a canonical pathkey list, but redundant eclasses
! * can't appear in canonical sort orderings. (XXX it might be
! * worth relaxing this, but not enough time to address it for
! * 8.3.)
! *
! * Note: it would be bad if this condition failed for an
! * otherwise mergejoinable FULL JOIN clause, since that would
! * result in undesirable planner failure. I believe that is
! * not possible however; a variable involved in a full join
! * could only appear in below_outer_join eclasses, which aren't
! * considered redundant.
! *
! * This case *can* happen for left/right join clauses: the
! * outer-side variable could be equated to a constant. Because
! * we will propagate that constant across the join clause, the
! * loss of ability to do a mergejoin is not really all that big
! * a deal, and so it's not clear that improving this is
! * important.
! */
! cache_mergeclause_eclasses(root, restrictinfo);
!
! if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) ||
! EC_MUST_BE_REDUNDANT(restrictinfo->right_ec))
! have_nonmergeable_joinclause = true;
! else
! poc->mergeclause_list = lappend(poc->mergeclause_list,
! restrictinfo);
! }
}
! /*
! * Hashjoin-specific checks.
! */
! if (enable_hashjoin && restrictinfo->hashjoinoperator != InvalidOid
! && jointype != JOIN_FULL && jointype != JOIN_RIGHT)
! poc->hashclause_list = lappend(poc->hashclause_list, restrictinfo);
!
! /*
! * Joinremoval-specific checks.
! */
! if (joinremovalOK && restrictinfo->mergeopfamilies != NIL)
! {
! Expr *expr = (Expr *) restrictinfo->clause;
! Node *outernode = restrictinfo->outer_is_left ? get_leftop(expr)
! : get_rightop(expr);
! if (IsA(outernode, Var))
! {
! poc->joinremovalclause_list =
! lappend(poc->joinremovalclause_list, restrictinfo);
! poc->joinremovalvarattno_list =
! lappend_int(poc->joinremovalvarattno_list,
! ((Var *) outernode)->varattno);
! }
! }
}
/*
***************
*** 1055,1061 **** select_mergejoin_clauses(PlannerInfo *root,
switch (jointype)
{
case JOIN_RIGHT:
! return NIL; /* not mergejoinable */
case JOIN_FULL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
--- 1098,1105 ----
switch (jointype)
{
case JOIN_RIGHT:
! poc->mergeclause_list = NIL; /* not mergejoinable */
! break;
case JOIN_FULL:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 1066,1071 **** select_mergejoin_clauses(PlannerInfo *root,
break;
}
}
-
- return result_list;
}
--- 1110,1113 ----
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
***************
*** 1084,1089 **** translate_sub_tlist(List *tlist, int relid)
--- 1084,1091 ----
* compatibility. That looks at btree or hash opfamily membership, and so
* should give trustworthy answers for all operators that we might need
* to deal with here.)
+ *
+ * See also relation_is_distinct_for().
*/
static bool
query_is_distinct_for(Query *query, List *colnos, List *opids)
***************
*** 1214,1219 **** distinct_col_search(int colno, List *colnos, List *opids)
--- 1216,1287 ----
return InvalidOid;
}
+
+ /*
+ * relation_is_distinct_for - does relation never contain duplicates over the
+ * specified columns?
+ *
+ * colnos are the column numbers of interest, and restrictlist is a list (of
+ * equal length) of RestrictInfos referencing operator clauses.
+ */
+ bool
+ relation_is_distinct_for(RelOptInfo *rel, List *colnos, List *restrictlist)
+ {
+ ListCell *ic;
+
+ foreach (ic, rel->indexlist)
+ {
+ IndexOptInfo *ind = (IndexOptInfo *) lfirst(ic);
+ int c;
+
+ /*
+ * If the index is not unique or if it's a partial index that doesn't
+ * match the query, it's useless to us. We are not equipped to make
+ * use of expression indexes, so reject those quickly as well.
+ */
+ if (!ind->unique || (ind->indpred != NIL && !ind->predOK)
+ || ind->indexprs != NIL)
+ continue;
+
+ /* Check each column. O(n^2), but we expect the lists to be short. */
+ for (c = 0; c < ind->ncolumns; ++c)
+ {
+ ListCell *lc1,
+ *lc2;
+ RestrictInfo *rinfo = NULL;
+
+ /* Find the RestrictInfo for this column. */
+ forboth(lc1, colnos, lc2, restrictlist)
+ {
+ if (ind->indexkeys[c] == lfirst_int(lc1))
+ {
+ rinfo = lfirst(lc2);
+ break;
+ }
+ }
+
+ /*
+ * If a RestrictInfo was found, check whether the opfamily of the
+ * index column is one of the opfamilies to which the opclause's
+ * opearator belongs.
+ */
+ if (rinfo != NULL
+ && list_member_oid(rinfo->mergeopfamilies, ind->opfamily[c]))
+ break;
+ }
+
+ /* Found them all? */
+ if (c == ind->ncolumns)
+ return true;
+ }
+
+ /*
+ * Some day it would be nice to check for other methods of establishing
+ * distinctness.
+ */
+ return false;
+ }
+
/*
* create_subqueryscan_path
* Creates a path corresponding to a sequential scan of a subquery,
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 663,668 **** static struct config_bool ConfigureNamesBool[] =
--- 663,676 ----
true, NULL, NULL
},
{
+ {"enable_joinremoval", PGC_USERSET, QUERY_TUNING_METHOD,
+ gettext_noop("Enables the planner's use of join removal."),
+ NULL
+ },
+ &enable_joinremoval,
+ true, NULL, NULL
+ },
+ {
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
*** a/src/include/optimizer/cost.h
--- b/src/include/optimizer/cost.h
***************
*** 59,64 **** extern bool enable_hashagg;
--- 59,65 ----
extern bool enable_nestloop;
extern bool enable_mergejoin;
extern bool enable_hashjoin;
+ extern bool enable_joinremoval;
extern int constraint_exclusion;
extern double clamp_row_est(double nrows);
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
***************
*** 87,92 **** extern HashPath *create_hashjoin_path(PlannerInfo *root,
--- 87,95 ----
List *restrict_clauses,
List *hashclauses);
+ bool relation_is_distinct_for(RelOptInfo *rel, List *colnos,
+ List *restrictlist);
+
/*
* prototypes for relnode.c
*/
*** a/src/test/regress/expected/rangefuncs.out
--- b/src/test/regress/expected/rangefuncs.out
***************
*** 1,16 ****
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
! name | setting
! -------------------+---------
! enable_bitmapscan | on
! enable_hashagg | on
! enable_hashjoin | on
! enable_indexscan | on
! enable_mergejoin | on
! enable_nestloop | on
! enable_seqscan | on
! enable_sort | on
! enable_tidscan | on
! (9 rows)
CREATE TABLE foo2(fooid int, f2 int);
INSERT INTO foo2 VALUES(1, 11);
--- 1,17 ----
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
! name | setting
! --------------------+---------
! enable_bitmapscan | on
! enable_hashagg | on
! enable_hashjoin | on
! enable_indexscan | on
! enable_joinremoval | on
! enable_mergejoin | on
! enable_nestloop | on
! enable_seqscan | on
! enable_sort | on
! enable_tidscan | on
! (10 rows)
CREATE TABLE foo2(fooid int, f2 int);
INSERT INTO foo2 VALUES(1, 11);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers