* See src/backend/optimizer/README for discussion of EquivalenceClasses.
*
*
- * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
static EquivalenceMember *add_eq_member(EquivalenceClass *ec,
- Expr *expr, Relids relids,
+ Expr *expr, Relids relids, Relids nullable_relids,
bool is_child, Oid datatype);
static void generate_base_implied_equalities_const(PlannerInfo *root,
EquivalenceClass *ec);
EquivalenceClass *ec);
static List *generate_join_implied_equalities_normal(PlannerInfo *root,
EquivalenceClass *ec,
- RelOptInfo *joinrel,
- RelOptInfo *outer_rel,
- RelOptInfo *inner_rel);
+ Relids join_relids,
+ Relids outer_relids,
+ Relids inner_relids);
static List *generate_join_implied_equalities_broken(PlannerInfo *root,
EquivalenceClass *ec,
- RelOptInfo *joinrel,
- RelOptInfo *outer_rel,
- RelOptInfo *inner_rel);
+ Relids nominal_join_relids,
+ Relids outer_relids,
+ Relids nominal_inner_relids,
+ AppendRelInfo *inner_appinfo);
static Oid select_equality_operator(EquivalenceClass *ec,
Oid lefttype, Oid righttype);
static RestrictInfo *create_join_clause(PlannerInfo *root,
bool outer_on_left);
static bool reconsider_full_join_clause(PlannerInfo *root,
RestrictInfo *rinfo);
-static Index get_parent_relid(PlannerInfo *root, RelOptInfo *rel);
/*
Expr *item1;
Expr *item2;
Relids item1_relids,
- item2_relids;
+ item2_relids,
+ item1_nullable_relids,
+ item2_nullable_relids;
List *opfamilies;
EquivalenceClass *ec1,
*ec2;
return false; /* RHS is non-strict but not constant */
}
+ /* Calculate nullable-relid sets for each side of the clause */
+ item1_nullable_relids = bms_intersect(item1_relids,
+ restrictinfo->nullable_relids);
+ item2_nullable_relids = bms_intersect(item2_relids,
+ restrictinfo->nullable_relids);
+
/*
* We use the declared input types of the operator, not exprType() of the
* inputs, as the nominal datatypes for opfamily lookup. This presumes
}
/*
- * Case 2: need to merge ec1 and ec2. We add ec2's items to ec1, then
- * set ec2's ec_merged link to point to ec1 and remove ec2 from the
- * eq_classes list. We cannot simply delete ec2 because that could
- * leave dangling pointers in existing PathKeys. We leave it behind
- * with a link so that the merged EC can be found.
+ * Case 2: need to merge ec1 and ec2. This should never happen after
+ * we've built any canonical pathkeys; if it did, those pathkeys might
+ * be rendered non-canonical by the merge.
+ */
+ if (root->canon_pathkeys != NIL)
+ elog(ERROR, "too late to merge equivalence classes");
+
+ /*
+ * We add ec2's items to ec1, then set ec2's ec_merged link to point
+ * to ec1 and remove ec2 from the eq_classes list. We cannot simply
+ * delete ec2 because that could leave dangling pointers in existing
+ * PathKeys. We leave it behind with a link so that the merged EC can
+ * be found.
*/
ec1->ec_members = list_concat(ec1->ec_members, ec2->ec_members);
ec1->ec_sources = list_concat(ec1->ec_sources, ec2->ec_sources);
else if (ec1)
{
/* Case 3: add item2 to ec1 */
- em2 = add_eq_member(ec1, item2, item2_relids, false, item2_type);
+ em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids,
+ false, item2_type);
ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo);
ec1->ec_below_outer_join |= below_outer_join;
/* mark the RI as associated with this eclass */
else if (ec2)
{
/* Case 3: add item1 to ec2 */
- em1 = add_eq_member(ec2, item1, item1_relids, false, item1_type);
+ em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids,
+ false, item1_type);
ec2->ec_sources = lappend(ec2->ec_sources, restrictinfo);
ec2->ec_below_outer_join |= below_outer_join;
/* mark the RI as associated with this eclass */
ec->ec_broken = false;
ec->ec_sortref = 0;
ec->ec_merged = NULL;
- em1 = add_eq_member(ec, item1, item1_relids, false, item1_type);
- em2 = add_eq_member(ec, item2, item2_relids, false, item2_type);
+ em1 = add_eq_member(ec, item1, item1_relids, item1_nullable_relids,
+ false, item1_type);
+ em2 = add_eq_member(ec, item2, item2_relids, item2_nullable_relids,
+ false, item2_type);
root->eq_classes = lappend(root->eq_classes, ec);
req_type,
-1,
req_collation,
- COERCE_DONTCARE);
+ COERCE_IMPLICIT_CAST);
else if (exprCollation((Node *) expr) != req_collation)
expr = (Expr *) makeRelabelType(expr,
req_type,
exprTypmod((Node *) expr),
req_collation,
- COERCE_DONTCARE);
+ COERCE_IMPLICIT_CAST);
}
return expr;
*/
static EquivalenceMember *
add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids,
- bool is_child, Oid datatype)
+ Relids nullable_relids, bool is_child, Oid datatype)
{
EquivalenceMember *em = makeNode(EquivalenceMember);
em->em_expr = expr;
em->em_relids = relids;
+ em->em_nullable_relids = nullable_relids;
em->em_is_const = false;
em->em_is_child = is_child;
em->em_datatype = datatype;
* equivalence class it is a member of; if none, optionally build a new
* single-member EquivalenceClass for it.
*
+ * expr is the expression, and nullable_relids is the set of base relids
+ * that are potentially nullable below it. We actually only care about
+ * the set of such relids that are used in the expression; but for caller
+ * convenience, we perform that intersection step here. The caller need
+ * only be sure that nullable_relids doesn't omit any nullable rels that
+ * might appear in the expr.
+ *
* sortref is the SortGroupRef of the originating SortGroupClause, if any,
* or zero if not. (It should never be zero if the expression is volatile!)
*
* If rel is not NULL, it identifies a specific relation we're considering
* a path for, and indicates that child EC members for that relation can be
- * considered. Otherwise child members are ignored. (Note: since child EC
+ * considered. Otherwise child members are ignored. (Note: since child EC
* members aren't guaranteed unique, a non-NULL value means that there could
* be more than one EC that matches the expression; if so it's order-dependent
* which one you get. This is annoying but it only happens in corner cases,
- * so for now we live with just reporting the first match. See also
- * generate_implied_equalities_for_indexcol and match_pathkeys_to_index.)
+ * so for now we live with just reporting the first match. See also
+ * generate_implied_equalities_for_column and match_pathkeys_to_index.)
*
* If create_it is TRUE, we'll build a new EquivalenceClass when there is no
* match. If create_it is FALSE, we just return NULL when no match.
EquivalenceClass *
get_eclass_for_sort_expr(PlannerInfo *root,
Expr *expr,
+ Relids nullable_relids,
List *opfamilies,
Oid opcintype,
Oid collation,
Relids rel,
bool create_it)
{
+ Relids expr_relids;
EquivalenceClass *newec;
EquivalenceMember *newem;
ListCell *lc1;
*/
expr = canonicalize_ec_expression(expr, opcintype, collation);
+ /*
+ * Get the precise set of nullable relids appearing in the expression.
+ */
+ expr_relids = pull_varnos((Node *) expr);
+ nullable_relids = bms_intersect(nullable_relids, expr_relids);
+
/*
* Scan through the existing EquivalenceClasses for a match
*/
if (newec->ec_has_volatile && sortref == 0) /* should not happen */
elog(ERROR, "volatile EquivalenceClass has no sortref");
- newem = add_eq_member(newec, copyObject(expr), pull_varnos((Node *) expr),
- false, opcintype);
+ newem = add_eq_member(newec, copyObject(expr), expr_relids,
+ nullable_relids, false, opcintype);
/*
* add_eq_member doesn't check for volatile functions, set-returning
}
}
- /* Find the constant member to use */
+ /*
+ * Find the constant member to use. We prefer an actual constant to
+ * pseudo-constants (such as Params), because the constraint exclusion
+ * machinery might be able to exclude relations on the basis of generated
+ * "var = const" equalities, but "var = param" won't work for that.
+ */
foreach(lc, ec->ec_members)
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc);
if (cur_em->em_is_const)
{
const_em = cur_em;
- break;
+ if (IsA(cur_em->em_expr, Const))
+ break;
}
}
Assert(const_em != NULL);
}
process_implied_equality(root, eq_op, ec->ec_collation,
cur_em->em_expr, const_em->em_expr,
- ec->ec_relids,
+ bms_copy(ec->ec_relids),
+ bms_union(cur_em->em_nullable_relids,
+ const_em->em_nullable_relids),
ec->ec_below_outer_join,
cur_em->em_is_const);
}
}
process_implied_equality(root, eq_op, ec->ec_collation,
prev_em->em_expr, cur_em->em_expr,
- ec->ec_relids,
+ bms_copy(ec->ec_relids),
+ bms_union(prev_em->em_nullable_relids,
+ cur_em->em_nullable_relids),
ec->ec_below_outer_join,
false);
}
* of the EC back into the main restrictinfo datastructures. Multi-relation
* clauses will be regurgitated later by generate_join_implied_equalities().
* (We do it this way to maintain continuity with the case that ec_broken
- * becomes set only after we've gone up a join level or two.)
+ * becomes set only after we've gone up a join level or two.) However, for
+ * an EC that contains constants, we can adopt a simpler strategy and just
+ * throw back all the source RestrictInfos immediately; that works because
+ * we know that such an EC can't become broken later. (This rule justifies
+ * ignoring ec_has_const ECs in generate_join_implied_equalities, even when
+ * they are broken.)
*/
static void
generate_base_implied_equalities_broken(PlannerInfo *root,
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
- if (bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
+ if (ec->ec_has_const ||
+ bms_membership(restrictinfo->required_relids) != BMS_MULTIPLE)
distribute_restrictinfo_to_rels(root, restrictinfo);
}
}
* that all equivalence-class members computable at that node are equal.
* Since the set of clauses to enforce can vary depending on which subset
* relations are the inputs, we have to compute this afresh for each join
- * path pair. Hence a fresh List of RestrictInfo nodes is built and passed
- * back on each call.
+ * relation pair. Hence a fresh List of RestrictInfo nodes is built and
+ * passed back on each call.
+ *
+ * In addition to its use at join nodes, this can be applied to generate
+ * eclass-based join clauses for use in a parameterized scan of a base rel.
+ * The reason for the asymmetry of specifying the inner rel as a RelOptInfo
+ * and the outer rel by Relids is that this usage occurs before we have
+ * built any join RelOptInfos.
+ *
+ * An annoying special case for parameterized scans is that the inner rel can
+ * be an appendrel child (an "other rel"). In this case we must generate
+ * appropriate clauses using child EC members. add_child_rel_equivalences
+ * must already have been done for the child rel.
*
* The results are sufficient for use in merge, hash, and plain nestloop join
* methods. We do not worry here about selecting clauses that are optimal
- * for use in a nestloop-with-parameterized-inner-scan. indxpath.c makes
- * its own selections of clauses to use, and if the ones we pick here are
- * redundant with those, the extras will be eliminated in createplan.c.
+ * for use in a parameterized indexscan. indxpath.c makes its own selections
+ * of clauses to use, and if the ones we pick here are redundant with those,
+ * the extras will be eliminated at createplan time, using the parent_ec
+ * markers that we provide (see is_redundant_derived_clause()).
*
* Because the same join clauses are likely to be needed multiple times as
* we consider different join paths, we avoid generating multiple copies:
* we check to see if the pair matches any original clause (in ec_sources)
* or previously-built clause (in ec_derives). This saves memory and allows
* re-use of information cached in RestrictInfos.
+ *
+ * join_relids should always equal bms_union(outer_relids, inner_rel->relids).
+ * We could simplify this function's API by computing it internally, but in
+ * all current uses, the caller has the value at hand anyway.
*/
List *
generate_join_implied_equalities(PlannerInfo *root,
- RelOptInfo *joinrel,
- RelOptInfo *outer_rel,
+ Relids join_relids,
+ Relids outer_relids,
RelOptInfo *inner_rel)
{
List *result = NIL;
+ Relids inner_relids = inner_rel->relids;
+ Relids nominal_inner_relids;
+ Relids nominal_join_relids;
+ AppendRelInfo *inner_appinfo;
ListCell *lc;
+ /* If inner rel is a child, extra setup work is needed */
+ if (inner_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
+ {
+ /* Lookup parent->child translation data */
+ inner_appinfo = find_childrel_appendrelinfo(root, inner_rel);
+ /* Construct relids for the parent rel */
+ nominal_inner_relids = bms_make_singleton(inner_appinfo->parent_relid);
+ /* ECs will be marked with the parent's relid, not the child's */
+ nominal_join_relids = bms_union(outer_relids, nominal_inner_relids);
+ }
+ else
+ {
+ inner_appinfo = NULL;
+ nominal_inner_relids = inner_relids;
+ nominal_join_relids = join_relids;
+ }
+
foreach(lc, root->eq_classes)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
continue;
/* We can quickly ignore any that don't overlap the join, too */
- if (!bms_overlap(ec->ec_relids, joinrel->relids))
+ if (!bms_overlap(ec->ec_relids, nominal_join_relids))
continue;
if (!ec->ec_broken)
sublist = generate_join_implied_equalities_normal(root,
ec,
- joinrel,
- outer_rel,
- inner_rel);
+ join_relids,
+ outer_relids,
+ inner_relids);
/* Recover if we failed to generate required derived clauses */
if (ec->ec_broken)
sublist = generate_join_implied_equalities_broken(root,
ec,
- joinrel,
- outer_rel,
- inner_rel);
+ nominal_join_relids,
+ outer_relids,
+ nominal_inner_relids,
+ inner_appinfo);
result = list_concat(result, sublist);
}
static List *
generate_join_implied_equalities_normal(PlannerInfo *root,
EquivalenceClass *ec,
- RelOptInfo *joinrel,
- RelOptInfo *outer_rel,
- RelOptInfo *inner_rel)
+ Relids join_relids,
+ Relids outer_relids,
+ Relids inner_relids)
{
List *result = NIL;
List *new_members = NIL;
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc1);
- if (cur_em->em_is_child)
- continue; /* ignore children here */
- if (!bms_is_subset(cur_em->em_relids, joinrel->relids))
- continue; /* ignore --- not computable yet */
+ /*
+ * We don't need to check explicitly for child EC members. This test
+ * against join_relids will cause them to be ignored except when
+ * considering a child inner rel, which is what we want.
+ */
+ if (!bms_is_subset(cur_em->em_relids, join_relids))
+ continue; /* not computable yet, or wrong child */
- if (bms_is_subset(cur_em->em_relids, outer_rel->relids))
+ if (bms_is_subset(cur_em->em_relids, outer_relids))
outer_members = lappend(outer_members, cur_em);
- else if (bms_is_subset(cur_em->em_relids, inner_rel->relids))
+ else if (bms_is_subset(cur_em->em_relids, inner_relids))
inner_members = lappend(inner_members, cur_em);
else
new_members = lappend(new_members, cur_em);
* generate_join_implied_equalities cleanup after failure
*
* Return any original RestrictInfos that are enforceable at this join.
+ *
+ * In the case of a child inner relation, we have to translate the
+ * original RestrictInfos from parent to child Vars.
*/
static List *
generate_join_implied_equalities_broken(PlannerInfo *root,
EquivalenceClass *ec,
- RelOptInfo *joinrel,
- RelOptInfo *outer_rel,
- RelOptInfo *inner_rel)
+ Relids nominal_join_relids,
+ Relids outer_relids,
+ Relids nominal_inner_relids,
+ AppendRelInfo *inner_appinfo)
{
List *result = NIL;
ListCell *lc;
foreach(lc, ec->ec_sources)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
+ Relids clause_relids = restrictinfo->required_relids;
- if (bms_is_subset(restrictinfo->required_relids, joinrel->relids) &&
- !bms_is_subset(restrictinfo->required_relids, outer_rel->relids) &&
- !bms_is_subset(restrictinfo->required_relids, inner_rel->relids))
+ if (bms_is_subset(clause_relids, nominal_join_relids) &&
+ !bms_is_subset(clause_relids, outer_relids) &&
+ !bms_is_subset(clause_relids, nominal_inner_relids))
result = lappend(result, restrictinfo);
}
+ /*
+ * If we have to translate, just brute-force apply adjust_appendrel_attrs
+ * to all the RestrictInfos at once. This will result in returning
+ * RestrictInfos that are not listed in ec_derives, but there shouldn't be
+ * any duplication, and it's a sufficiently narrow corner case that we
+ * shouldn't sweat too much over it anyway.
+ */
+ if (inner_appinfo)
+ result = (List *) adjust_appendrel_attrs(root, (Node *) result,
+ inner_appinfo);
+
return result;
}
leftem->em_expr,
rightem->em_expr,
bms_union(leftem->em_relids,
- rightem->em_relids));
+ rightem->em_relids),
+ bms_union(leftem->em_nullable_relids,
+ rightem->em_nullable_relids));
/* Mark the clause as redundant, or not */
rinfo->parent_ec = parent_ec;
left_type,
right_type,
inner_datatype;
- Relids inner_relids;
+ Relids inner_relids,
+ inner_nullable_relids;
ListCell *lc1;
Assert(is_opclause(rinfo->clause));
inner_datatype = left_type;
inner_relids = rinfo->left_relids;
}
+ inner_nullable_relids = bms_intersect(inner_relids,
+ rinfo->nullable_relids);
/* Scan EquivalenceClasses for a match to outervar */
foreach(lc1, root->eq_classes)
cur_ec->ec_collation,
innervar,
cur_em->em_expr,
- inner_relids);
+ bms_copy(inner_relids),
+ bms_copy(inner_nullable_relids));
if (process_equivalence(root, newrinfo, true))
match = true;
}
left_type,
right_type;
Relids left_relids,
- right_relids;
+ right_relids,
+ left_nullable_relids,
+ right_nullable_relids;
ListCell *lc1;
/* Can't use an outerjoin_delayed clause here */
rightvar = (Expr *) get_rightop(rinfo->clause);
left_relids = rinfo->left_relids;
right_relids = rinfo->right_relids;
+ left_nullable_relids = bms_intersect(left_relids,
+ rinfo->nullable_relids);
+ right_nullable_relids = bms_intersect(right_relids,
+ rinfo->nullable_relids);
foreach(lc1, root->eq_classes)
{
cur_ec->ec_collation,
leftvar,
cur_em->em_expr,
- left_relids);
+ bms_copy(left_relids),
+ bms_copy(left_nullable_relids));
if (process_equivalence(root, newrinfo, true))
matchleft = true;
}
cur_ec->ec_collation,
rightvar,
cur_em->em_expr,
- right_relids);
+ bms_copy(right_relids),
+ bms_copy(right_nullable_relids));
if (process_equivalence(root, newrinfo, true))
matchright = true;
}
/*
* add_child_rel_equivalences
- * Search for EC members that reference (only) the parent_rel, and
+ * Search for EC members that reference the parent_rel, and
* add transformed members referencing the child_rel.
*
* Note that this function won't be called at all unless we have at least some
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
- if (cur_em->em_is_child)
- continue; /* ignore children here */
+ if (cur_em->em_is_const || cur_em->em_is_child)
+ continue; /* ignore consts and children here */
- /* Does it reference (only) parent_rel? */
- if (bms_equal(cur_em->em_relids, parent_rel->relids))
+ /* Does it reference parent_rel? */
+ if (bms_overlap(cur_em->em_relids, parent_rel->relids))
{
/* Yes, generate transformed child version */
Expr *child_expr;
+ Relids new_relids;
+ Relids new_nullable_relids;
child_expr = (Expr *)
adjust_appendrel_attrs(root,
(Node *) cur_em->em_expr,
appinfo);
- (void) add_eq_member(cur_ec, child_expr, child_rel->relids,
+
+ /*
+ * Transform em_relids to match. Note we do *not* do
+ * pull_varnos(child_expr) here, as for example the
+ * transformation might have substituted a constant, but we
+ * don't want the child member to be marked as constant.
+ */
+ new_relids = bms_difference(cur_em->em_relids,
+ parent_rel->relids);
+ new_relids = bms_add_members(new_relids, child_rel->relids);
+
+ /*
+ * And likewise for nullable_relids. Note this code assumes
+ * parent and child relids are singletons.
+ */
+ new_nullable_relids = cur_em->em_nullable_relids;
+ if (bms_overlap(new_nullable_relids, parent_rel->relids))
+ {
+ new_nullable_relids = bms_difference(new_nullable_relids,
+ parent_rel->relids);
+ new_nullable_relids = bms_add_members(new_nullable_relids,
+ child_rel->relids);
+ }
+
+ (void) add_eq_member(cur_ec, child_expr,
+ new_relids, new_nullable_relids,
true, cur_em->em_datatype);
}
}
/*
* mutate_eclass_expressions
* Apply an expression tree mutator to all expressions stored in
- * equivalence classes.
+ * equivalence classes (but ignore child exprs unless include_child_exprs).
*
* This is a bit of a hack ... it's currently needed only by planagg.c,
* which needs to do a global search-and-replace of MIN/MAX Aggrefs
* after eclasses are already set up. Without changing the eclasses too,
- * subsequent matching of ORDER BY clauses would fail.
+ * subsequent matching of ORDER BY and DISTINCT clauses would fail.
*
* Note that we assume the mutation won't affect relation membership or any
* other properties we keep track of (which is a bit bogus, but by the time
void
mutate_eclass_expressions(PlannerInfo *root,
Node *(*mutator) (),
- void *context)
+ void *context,
+ bool include_child_exprs)
{
ListCell *lc1;
{
EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+ if (cur_em->em_is_child && !include_child_exprs)
+ continue; /* ignore children unless requested */
+
cur_em->em_expr = (Expr *)
mutator((Node *) cur_em->em_expr, context);
}
/*
- * generate_implied_equalities_for_indexcol
- * Create EC-derived joinclauses usable with a specific index column.
+ * generate_implied_equalities_for_column
+ * Create EC-derived joinclauses usable with a specific column.
+ *
+ * This is used by indxpath.c to extract potentially indexable joinclauses
+ * from ECs, and can be used by foreign data wrappers for similar purposes.
+ * We assume that only expressions in Vars of a single table are of interest,
+ * but the caller provides a callback function to identify exactly which
+ * such expressions it would like to know about.
*
- * We assume that any given index column could appear in only one EC.
+ * We assume that any given table/index column could appear in only one EC.
* (This should be true in all but the most pathological cases, and if it
* isn't, we stop on the first match anyway.) Therefore, what we return
- * is a redundant list of clauses equating the index column to each of
+ * is a redundant list of clauses equating the table/index column to each of
* the other-relation values it is known to be equal to. Any one of
- * these clauses can be used to create a parameterized indexscan, and there
- * is no value in using more than one. (But it *is* worthwhile to create
+ * these clauses can be used to create a parameterized path, and there
+ * is no value in using more than one. (But it *is* worthwhile to create
* a separate parameterized path for each one, since that leads to different
* join orders.)
+ *
+ * The caller can pass a Relids set of rels we aren't interested in joining
+ * to, so as to save the work of creating useless clauses.
*/
List *
-generate_implied_equalities_for_indexcol(PlannerInfo *root,
- IndexOptInfo *index,
- int indexcol)
+generate_implied_equalities_for_column(PlannerInfo *root,
+ RelOptInfo *rel,
+ ec_matches_callback_type callback,
+ void *callback_arg,
+ Relids prohibited_rels)
{
List *result = NIL;
- RelOptInfo *rel = index->rel;
bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
Index parent_relid;
ListCell *lc1;
/* If it's a child rel, we'll need to know what its parent is */
if (is_child_rel)
- parent_relid = get_parent_relid(root, rel);
+ parent_relid = find_childrel_appendrelinfo(root, rel)->parent_relid;
else
parent_relid = 0; /* not used, but keep compiler quiet */
continue;
/*
- * Scan members, looking for a match to the indexable column. Note
- * that child EC members are considered, but only when they belong to
- * the target relation. (Unlike regular members, the same expression
+ * Scan members, looking for a match to the target column. Note that
+ * child EC members are considered, but only when they belong to the
+ * target relation. (Unlike regular members, the same expression
* could be a child member of more than one EC. Therefore, it's
- * potentially order-dependent which EC a child relation's index
- * column gets matched to. This is annoying but it only happens in
+ * potentially order-dependent which EC a child relation's target
+ * column gets matched to. This is annoying but it only happens in
* corner cases, so for now we live with just reporting the first
* match. See also get_eclass_for_sort_expr.)
*/
{
cur_em = (EquivalenceMember *) lfirst(lc2);
if (bms_equal(cur_em->em_relids, rel->relids) &&
- eclass_member_matches_indexcol(cur_ec, cur_em,
- index, indexcol))
+ callback(root, rel, cur_ec, cur_em, callback_arg))
break;
cur_em = NULL;
}
bms_overlap(other_em->em_relids, rel->relids))
continue;
+ /* Forget it if caller doesn't want joins to this rel */
+ if (bms_overlap(other_em->em_relids, prohibited_rels))
+ continue;
+
/*
* Also, if this is a child rel, avoid generating a useless join
* to its parent rel.
return result;
}
-/*
- * get_parent_relid
- * Get the relid of a child rel's parent appendrel
- *
- * Possibly this should be somewhere else, but right now the logic is only
- * needed here.
- */
-static Index
-get_parent_relid(PlannerInfo *root, RelOptInfo *rel)
-{
- ListCell *lc;
-
- foreach(lc, root->append_rel_list)
- {
- AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
-
- if (appinfo->child_relid == rel->relid)
- return appinfo->parent_relid;
- }
- /* should have found the entry ... */
- elog(ERROR, "child rel not found in append_rel_list");
- return 0;
-}
-
/*
* have_relevant_eclass_joinclause
* Detect whether there is an EquivalenceClass that could produce
return false;
}
+
+
+/*
+ * is_redundant_derived_clause
+ * Test whether rinfo is derived from same EC as any clause in clauselist;
+ * if so, it can be presumed to represent a condition that's redundant
+ * with that member of the list.
+ */
+bool
+is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist)
+{
+ EquivalenceClass *parent_ec = rinfo->parent_ec;
+ ListCell *lc;
+
+ /* Fail if it's not a potentially-redundant clause from some EC */
+ if (parent_ec == NULL)
+ return false;
+
+ foreach(lc, clauselist)
+ {
+ RestrictInfo *otherrinfo = (RestrictInfo *) lfirst(lc);
+
+ if (otherrinfo->parent_ec == parent_ec)
+ return true;
+ }
+
+ return false;
+}