bool outer_on_left);
static bool reconsider_full_join_clause(PlannerInfo *root,
RestrictInfo *rinfo);
+static Bitmapset *get_eclass_indexes_for_relids(PlannerInfo *root,
+ Relids relids);
+static Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
+ Relids relids2);
/*
/*
* 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.
+ * the ECs have reached canonical state; otherwise, pathkeys could be
+ * rendered non-canonical by the merge, and relation eclass indexes
+ * would get broken by removal of an eq_classes list entry.
*/
- if (root->canon_pathkeys != NIL)
+ if (root->ec_merging_done)
elog(ERROR, "too late to merge equivalence classes");
/*
root->eq_classes = lappend(root->eq_classes, newec);
+ /*
+ * If EC merging is already complete, we have to mop up by adding the new
+ * EC to the eclass_indexes of the relation(s) mentioned in it.
+ */
+ if (root->ec_merging_done)
+ {
+ int ec_index = list_length(root->eq_classes) - 1;
+ int i = -1;
+
+ while ((i = bms_next_member(newec->ec_relids, i)) > 0)
+ {
+ RelOptInfo *rel = root->simple_rel_array[i];
+
+ Assert(rel->reloptkind == RELOPT_BASEREL);
+
+ rel->eclass_indexes = bms_add_member(rel->eclass_indexes,
+ ec_index);
+ }
+ }
+
MemoryContextSwitchTo(oldcontext);
return newec;
void
generate_base_implied_equalities(PlannerInfo *root)
{
+ int ec_index;
ListCell *lc;
- Index rti;
+ /*
+ * At this point, we're done absorbing knowledge of equivalences in the
+ * query, so no further EC merging should happen, and ECs remaining in the
+ * eq_classes list can be considered canonical. (But note that it's still
+ * possible for new single-member ECs to be added through
+ * get_eclass_for_sort_expr().)
+ */
+ root->ec_merging_done = true;
+
+ ec_index = 0;
foreach(lc, root->eq_classes)
{
EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc);
+ bool can_generate_joinclause = false;
+ int i;
Assert(ec->ec_merged == NULL); /* else shouldn't be in list */
Assert(!ec->ec_broken); /* not yet anyway... */
- /* Single-member ECs won't generate any deductions */
- if (list_length(ec->ec_members) <= 1)
- continue;
+ /*
+ * Generate implied equalities that are restriction clauses.
+ * Single-member ECs won't generate any deductions, either here or at
+ * the join level.
+ */
+ if (list_length(ec->ec_members) > 1)
+ {
+ if (ec->ec_has_const)
+ generate_base_implied_equalities_const(root, ec);
+ else
+ generate_base_implied_equalities_no_const(root, ec);
+
+ /* Recover if we failed to generate required derived clauses */
+ if (ec->ec_broken)
+ generate_base_implied_equalities_broken(root, ec);
+
+ /* Detect whether this EC might generate join clauses */
+ can_generate_joinclause =
+ (bms_membership(ec->ec_relids) == BMS_MULTIPLE);
+ }
- if (ec->ec_has_const)
- generate_base_implied_equalities_const(root, ec);
- else
- generate_base_implied_equalities_no_const(root, ec);
+ /*
+ * Mark the base rels cited in each eclass (which should all exist by
+ * now) with the eq_classes indexes of all eclasses mentioning them.
+ * This will let us avoid searching in subsequent lookups. While
+ * we're at it, we can mark base rels that have pending eclass joins;
+ * this is a cheap version of has_relevant_eclass_joinclause().
+ */
+ i = -1;
+ while ((i = bms_next_member(ec->ec_relids, i)) > 0)
+ {
+ RelOptInfo *rel = root->simple_rel_array[i];
- /* Recover if we failed to generate required derived clauses */
- if (ec->ec_broken)
- generate_base_implied_equalities_broken(root, ec);
- }
+ Assert(rel->reloptkind == RELOPT_BASEREL);
- /*
- * This is also a handy place to mark base rels (which should all exist by
- * now) with flags showing whether they have pending eclass joins.
- */
- for (rti = 1; rti < root->simple_rel_array_size; rti++)
- {
- RelOptInfo *brel = root->simple_rel_array[rti];
+ rel->eclass_indexes = bms_add_member(rel->eclass_indexes,
+ ec_index);
- if (brel == NULL)
- continue;
+ if (can_generate_joinclause)
+ rel->has_eclass_joins = true;
+ }
- brel->has_eclass_joins = has_relevant_eclass_joinclause(root, brel);
+ ec_index++;
}
}
Relids outer_relids,
RelOptInfo *inner_rel)
{
- return generate_join_implied_equalities_for_ecs(root,
- root->eq_classes,
- join_relids,
- outer_relids,
- inner_rel);
+ List *result = NIL;
+ Relids inner_relids = inner_rel->relids;
+ Relids nominal_inner_relids;
+ Relids nominal_join_relids;
+ Bitmapset * matching_ecs;
+ int i;
+
+ /* If inner rel is a child, extra setup work is needed */
+ if (IS_OTHER_REL(inner_rel))
+ {
+ Assert(!bms_is_empty(inner_rel->top_parent_relids));
+
+ /* Fetch relid set for the topmost parent rel */
+ nominal_inner_relids = inner_rel->top_parent_relids;
+ /* ECs will be marked with the parent's relid, not the child's */
+ nominal_join_relids = bms_union(outer_relids, nominal_inner_relids);
+ }
+ else
+ {
+ nominal_inner_relids = inner_relids;
+ nominal_join_relids = join_relids;
+ }
+
+ /*
+ * Get all eclasses in common between inner_rel's relids and outer_relids
+ */
+ matching_ecs = get_common_eclass_indexes(root, inner_rel->relids,
+ outer_relids);
+
+ i = -1;
+ while ((i = bms_next_member(matching_ecs, i)) >= 0)
+ {
+ EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
+ List *sublist = NIL;
+
+ /* ECs containing consts do not need any further enforcement */
+ if (ec->ec_has_const)
+ continue;
+
+ /* Single-member ECs won't generate any deductions */
+ if (list_length(ec->ec_members) <= 1)
+ continue;
+
+ /* Sanity check that this eclass overlaps the join */
+ Assert(bms_overlap(ec->ec_relids, nominal_join_relids));
+
+ if (!ec->ec_broken)
+ sublist = generate_join_implied_equalities_normal(root,
+ ec,
+ 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,
+ nominal_join_relids,
+ outer_relids,
+ nominal_inner_relids,
+ inner_rel);
+
+ result = list_concat(result, sublist);
+ }
+
+ return result;
}
/*
Index var2varno = fkinfo->ref_relid;
AttrNumber var2attno = fkinfo->confkey[colno];
Oid eqop = fkinfo->conpfeqop[colno];
+ RelOptInfo *rel1 = root->simple_rel_array[var1varno];
+ RelOptInfo *rel2 = root->simple_rel_array[var2varno];
List *opfamilies = NIL; /* compute only if needed */
- ListCell *lc1;
-
- foreach(lc1, root->eq_classes)
+ Bitmapset *matching_ecs;
+ int i;
+
+ /* Consider only eclasses mentioning both relations */
+ Assert(root->ec_merging_done);
+ Assert(IS_SIMPLE_REL(rel1));
+ Assert(IS_SIMPLE_REL(rel2));
+ matching_ecs = bms_intersect(rel1->eclass_indexes,
+ rel2->eclass_indexes);
+
+ i = -1;
+ while ((i = bms_next_member(matching_ecs, i)) >= 0)
{
- EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
+ EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes,
+ i);
bool item1member = false;
bool item2member = false;
ListCell *lc2;
continue;
/* Note: it seems okay to match to "broken" eclasses here */
- /*
- * If eclass visibly doesn't have members for both rels, there's no
- * need to grovel through the members.
- */
- if (!bms_is_member(var1varno, ec->ec_relids) ||
- !bms_is_member(var2varno, ec->ec_relids))
- continue;
-
foreach(lc2, ec->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
RelOptInfo *parent_rel,
RelOptInfo *child_rel)
{
- ListCell *lc1;
+ int i;
- foreach(lc1, root->eq_classes)
+ /*
+ * EC merging should be complete already, so we can use the parent rel's
+ * eclass_indexes to avoid searching all of root->eq_classes.
+ */
+ Assert(root->ec_merging_done);
+ Assert(IS_SIMPLE_REL(parent_rel));
+
+ i = -1;
+ while ((i = bms_next_member(parent_rel->eclass_indexes, i)) >= 0)
{
- EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+ EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
int num_members;
/*
if (cur_ec->ec_has_volatile)
continue;
- /*
- * No point in searching if child's topmost parent rel is not
- * mentioned in eclass.
- */
- if (!bms_is_subset(child_rel->top_parent_relids, cur_ec->ec_relids))
- continue;
+ /* Sanity check eclass_indexes only contain ECs for parent_rel */
+ Assert(bms_is_subset(child_rel->top_parent_relids, cur_ec->ec_relids));
/*
* We don't use foreach() here because there's no point in scanning
(void) add_eq_member(cur_ec, child_expr,
new_relids, new_nullable_relids,
true, cur_em->em_datatype);
+
+ /* Record this EC index for the child rel */
+ child_rel->eclass_indexes = bms_add_member(child_rel->eclass_indexes, i);
}
}
}
List *result = NIL;
bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
Relids parent_relids;
- ListCell *lc1;
+ int i;
+
+ /* Should be OK to rely on eclass_indexes */
+ Assert(root->ec_merging_done);
/* Indexes are available only on base or "other" member relations. */
Assert(IS_SIMPLE_REL(rel));
else
parent_relids = NULL; /* not used, but keep compiler quiet */
- foreach(lc1, root->eq_classes)
+ i = -1;
+ while ((i = bms_next_member(rel->eclass_indexes, i)) >= 0)
{
- EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
+ EquivalenceClass *cur_ec = (EquivalenceClass *) list_nth(root->eq_classes, i);
EquivalenceMember *cur_em;
ListCell *lc2;
+ /* Sanity check eclass_indexes only contain ECs for rel */
+ Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids));
+
/*
* Won't generate joinclauses if const or single-member (the latter
* test covers the volatile case too)
if (cur_ec->ec_has_const || list_length(cur_ec->ec_members) <= 1)
continue;
- /*
- * No point in searching if rel not mentioned in eclass (but we can't
- * tell that for a child rel).
- */
- if (!is_child_rel &&
- !bms_is_subset(rel->relids, cur_ec->ec_relids))
- continue;
-
/*
* Scan members, looking for a match to the target column. Note that
* child EC members are considered, but only when they belong to the
have_relevant_eclass_joinclause(PlannerInfo *root,
RelOptInfo *rel1, RelOptInfo *rel2)
{
- ListCell *lc1;
+ Bitmapset *matching_ecs;
+ int i;
- foreach(lc1, root->eq_classes)
+ /* Examine only eclasses mentioning both rel1 and rel2 */
+ matching_ecs = get_common_eclass_indexes(root, rel1->relids,
+ rel2->relids);
+
+ i = -1;
+ while ((i = bms_next_member(matching_ecs, i)) >= 0)
{
- EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
+ EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes,
+ i);
+
+ /*
+ * Sanity check that get_common_eclass_indexes gave only ECs
+ * containing both rels.
+ */
+ Assert(bms_overlap(rel1->relids, ec->ec_relids));
+ Assert(bms_overlap(rel2->relids, ec->ec_relids));
/*
* Won't generate joinclauses if single-member (this test covers the
/*
* We do not need to examine the individual members of the EC, because
* all that we care about is whether each rel overlaps the relids of
- * at least one member, and a test on ec_relids is sufficient to prove
- * that. (As with have_relevant_joinclause(), it is not necessary
- * that the EC be able to form a joinclause relating exactly the two
- * given rels, only that it be able to form a joinclause mentioning
- * both, and this will surely be true if both of them overlap
- * ec_relids.)
+ * at least one member, and get_common_eclass_indexes() and the single
+ * member check above are sufficient to prove that. (As with
+ * have_relevant_joinclause(), it is not necessary that the EC be able
+ * to form a joinclause relating exactly the two given rels, only that
+ * it be able to form a joinclause mentioning both, and this will
+ * surely be true if both of them overlap ec_relids.)
*
* Note we don't test ec_broken; if we did, we'd need a separate code
* path to look through ec_sources. Checking the membership anyway is
* since the join result is likely to be small even though it'll end
* up being an unqualified nestloop.
*/
- if (bms_overlap(rel1->relids, ec->ec_relids) &&
- bms_overlap(rel2->relids, ec->ec_relids))
- return true;
+
+ return true;
}
return false;
bool
has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
{
- ListCell *lc1;
+ Bitmapset *matched_ecs;
+ int i;
- foreach(lc1, root->eq_classes)
+ /* Examine only eclasses mentioning rel1 */
+ matched_ecs = get_eclass_indexes_for_relids(root, rel1->relids);
+
+ i = -1;
+ while ((i = bms_next_member(matched_ecs, i)) >= 0)
{
- EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc1);
+ EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes,
+ i);
/*
* Won't generate joinclauses if single-member (this test covers the
* Per the comment in have_relevant_eclass_joinclause, it's sufficient
* to find an EC that mentions both this rel and some other rel.
*/
- if (bms_overlap(rel1->relids, ec->ec_relids) &&
- !bms_is_subset(ec->ec_relids, rel1->relids))
+ if (!bms_is_subset(ec->ec_relids, rel1->relids))
return true;
}
return false;
}
+
+/*
+ * get_eclass_indexes_for_relids
+ * Build and return a Bitmapset containing the indexes into root's
+ * eq_classes list for all eclasses that mention any of these relids
+ */
+static Bitmapset *
+get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids)
+{
+ Bitmapset *ec_indexes = NULL;
+ int i = -1;
+
+ /* Should be OK to rely on eclass_indexes */
+ Assert(root->ec_merging_done);
+
+ while ((i = bms_next_member(relids, i)) > 0)
+ {
+ RelOptInfo *rel = root->simple_rel_array[i];
+
+ ec_indexes = bms_add_members(ec_indexes, rel->eclass_indexes);
+ }
+ return ec_indexes;
+}
+
+/*
+ * get_common_eclass_indexes
+ * Build and return a Bitmapset containing the indexes into root's
+ * eq_classes list for all eclasses that mention rels in both
+ * relids1 and relids2.
+ */
+static Bitmapset *
+get_common_eclass_indexes(PlannerInfo *root, Relids relids1, Relids relids2)
+{
+ Bitmapset *rel1ecs;
+ Bitmapset *rel2ecs;
+ int relid;
+
+ rel1ecs = get_eclass_indexes_for_relids(root, relids1);
+
+ /*
+ * We can get away with just using the relation's eclass_indexes directly
+ * when relids2 is a singleton set.
+ */
+ if (bms_get_singleton_member(relids2, &relid))
+ rel2ecs = root->simple_rel_array[relid]->eclass_indexes;
+ else
+ rel2ecs = get_eclass_indexes_for_relids(root, relids2);
+
+ /* Calculate and return the common EC indexes, recycling the left input. */
+ return bms_int_members(rel1ecs, rel2ecs);
+}