diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 61b5b119b0..4b2cac6396 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -31,8 +31,8 @@ #include "optimizer/restrictinfo.h" #include "utils/lsyscache.h" - -static EquivalenceMember *add_eq_member(EquivalenceClass *ec, +static EquivalenceMember *add_eq_member(PlannerInfo *root, + EquivalenceClass *ec, int ec_index, Expr *expr, Relids relids, Relids nullable_relids, bool is_child, Oid datatype); static void generate_base_implied_equalities_const(PlannerInfo *root, @@ -64,6 +64,10 @@ static bool reconsider_outer_join_clause(PlannerInfo *root, 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 void delete_eclass(PlannerInfo *root, EquivalenceClass *ec, + int ec_index); /* @@ -133,6 +137,9 @@ process_equivalence(PlannerInfo *root, EquivalenceMember *em1, *em2; ListCell *lc1; + int ec1idx, + ec2idx, + cur_ec_idx; /* Should not already be marked as having generated an eclass */ Assert(restrictinfo->left_ec == NULL); @@ -150,6 +157,8 @@ process_equivalence(PlannerInfo *root, item2 = (Expr *) get_rightop(clause); item1_relids = restrictinfo->left_relids; item2_relids = restrictinfo->right_relids; + ec1idx = -1; + ec2idx = -1; /* * Ensure both input expressions expose the desired collation (their types @@ -254,11 +263,14 @@ process_equivalence(PlannerInfo *root, */ ec1 = ec2 = NULL; em1 = em2 = NULL; + cur_ec_idx = -1; foreach(lc1, root->eq_classes) { EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); ListCell *lc2; + cur_ec_idx++; + /* Never match to a volatile EC */ if (cur_ec->ec_has_volatile) continue; @@ -298,6 +310,7 @@ process_equivalence(PlannerInfo *root, { ec1 = cur_ec; em1 = cur_em; + ec1idx = cur_ec_idx; if (ec2) break; } @@ -308,6 +321,7 @@ process_equivalence(PlannerInfo *root, { ec2 = cur_ec; em2 = cur_em; + ec2idx = cur_ec_idx; if (ec1) break; } @@ -366,12 +380,10 @@ process_equivalence(PlannerInfo *root, ec1->ec_max_security = Max(ec1->ec_max_security, ec2->ec_max_security); ec2->ec_merged = ec1; - root->eq_classes = list_delete_ptr(root->eq_classes, ec2); - /* just to avoid debugging confusion w/ dangling pointers: */ - ec2->ec_members = NIL; - ec2->ec_sources = NIL; - ec2->ec_derives = NIL; - ec2->ec_relids = NULL; + + /* Do the deed */ + delete_eclass(root, ec2, ec2idx); + ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join |= below_outer_join; ec1->ec_min_security = Min(ec1->ec_min_security, @@ -388,8 +400,8 @@ process_equivalence(PlannerInfo *root, else if (ec1) { /* Case 3: add item2 to ec1 */ - em2 = add_eq_member(ec1, item2, item2_relids, item2_nullable_relids, - false, item2_type); + em2 = add_eq_member(root, ec1, ec1idx, 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; ec1->ec_min_security = Min(ec1->ec_min_security, @@ -406,8 +418,8 @@ process_equivalence(PlannerInfo *root, else if (ec2) { /* Case 3: add item1 to ec2 */ - em1 = add_eq_member(ec2, item1, item1_relids, item1_nullable_relids, - false, item1_type); + em1 = add_eq_member(root, ec2, ec2idx, 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; ec2->ec_min_security = Min(ec2->ec_min_security, @@ -440,10 +452,12 @@ process_equivalence(PlannerInfo *root, ec->ec_min_security = restrictinfo->security_level; ec->ec_max_security = restrictinfo->security_level; ec->ec_merged = NULL; - 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); + em1 = add_eq_member(root, ec, list_length(root->eq_classes), item1, + item1_relids, item1_nullable_relids, false, + item1_type); + em2 = add_eq_member(root, ec, list_length(root->eq_classes), item2, + item2_relids, item2_nullable_relids, false, + item2_type); root->eq_classes = lappend(root->eq_classes, ec); @@ -539,13 +553,19 @@ canonicalize_ec_expression(Expr *expr, Oid req_type, Oid req_collation) /* * add_eq_member - build a new EquivalenceMember and add it to an EC + * + * ec_index must be the list index that this ec is, or will be stored in + * root's eq_classes. */ static EquivalenceMember * -add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids, - Relids nullable_relids, bool is_child, Oid datatype) +add_eq_member(PlannerInfo *root, EquivalenceClass *ec, int ec_index, + Expr *expr, Relids relids, Relids nullable_relids, + bool is_child, Oid datatype) { EquivalenceMember *em = makeNode(EquivalenceMember); + Assert(ec_index >= 0); + em->em_expr = expr; em->em_relids = relids; em->em_nullable_relids = nullable_relids; @@ -568,9 +588,24 @@ add_eq_member(EquivalenceClass *ec, Expr *expr, Relids relids, ec->ec_has_const = true; /* it can't affect ec_relids */ } - else if (!is_child) /* child members don't add to ec_relids */ + else { - ec->ec_relids = bms_add_members(ec->ec_relids, relids); + int i; + + /* Record this eclass in each rel's eclass_indexes set */ + i = -1; + while ((i = bms_next_member(relids, i)) > 0) + { + RelOptInfo *rel = root->simple_rel_array[i]; + + rel->eclass_indexes = bms_add_member(rel->eclass_indexes, + ec_index); + } + + if (!is_child) /* child members don't add to ec_relids */ + { + ec->ec_relids = bms_add_members(ec->ec_relids, relids); + } } ec->ec_members = lappend(ec->ec_members, em); @@ -720,7 +755,8 @@ get_eclass_for_sort_expr(PlannerInfo *root, if (newec->ec_has_volatile && sortref == 0) /* should not happen */ elog(ERROR, "volatile EquivalenceClass has no sortref"); - newem = add_eq_member(newec, copyObject(expr), expr_relids, + newem = add_eq_member(root, newec, list_length(root->eq_classes), + copyObject(expr), expr_relids, nullable_relids, false, opcintype); /* @@ -1096,7 +1132,10 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root, Relids inner_relids = inner_rel->relids; Relids nominal_inner_relids; Relids nominal_join_relids; - ListCell *lc; + Bitmapset *inner_ecs; + Bitmapset *join_ecs; + Bitmapset *matching_ecs; + int i; /* If inner rel is a child, extra setup work is needed */ if (IS_OTHER_REL(inner_rel)) @@ -1114,9 +1153,16 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root, nominal_join_relids = join_relids; } - foreach(lc, eclasses) + inner_ecs = get_eclass_indexes_for_relids(root, inner_relids); + join_ecs = get_eclass_indexes_for_relids(root, join_relids); + + /* Determine all eclasses in common between inner rels and join rels */ + matching_ecs = bms_int_members(inner_ecs, join_ecs); + + i = -1; + while ((i = bms_next_member(matching_ecs, i)) >= 0) { - EquivalenceClass *ec = (EquivalenceClass *) lfirst(lc); + EquivalenceClass *ec = (EquivalenceClass *) list_nth(root->eq_classes, i); List *sublist = NIL; /* ECs containing consts do not need any further enforcement */ @@ -1127,9 +1173,8 @@ generate_join_implied_equalities_for_ecs(PlannerInfo *root, if (list_length(ec->ec_members) <= 1) continue; - /* We can quickly ignore any that don't overlap the join, too */ - if (!bms_overlap(ec->ec_relids, nominal_join_relids)) - continue; + /* Ensure 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, @@ -2038,31 +2083,32 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root, AttrNumber var2attno = fkinfo->confkey[colno]; Oid eqop = fkinfo->conpfeqop[colno]; List *opfamilies = NIL; /* compute only if needed */ - ListCell *lc1; + Bitmapset *matching_ecs; + int i; - foreach(lc1, root->eq_classes) + /* Determine which eclasses contain members for both of the relations */ + matching_ecs = bms_intersect(root->simple_rel_array[var1varno]->eclass_indexes, + root->simple_rel_array[var2varno]->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; + ListCell *lc; /* Never match to a volatile EC */ if (ec->ec_has_volatile) 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) + /* Ensure no corruption of eclass_indexes */ + Assert(bms_is_member(var1varno, ec->ec_relids)); + Assert(bms_is_member(var2varno, ec->ec_relids)); + foreach(lc, ec->ec_members) { - EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2); + EquivalenceMember *em = (EquivalenceMember *) lfirst(lc); Var *var; if (em->em_is_child) @@ -2120,12 +2166,16 @@ add_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel) { ListCell *lc1; + int i; + i = -1; foreach(lc1, root->eq_classes) { EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1); ListCell *lc2; + i++; + /* * If this EC contains a volatile expression, then generating child * EMs would be downright dangerous, so skip it. We rely on a @@ -2185,7 +2235,7 @@ add_child_rel_equivalences(PlannerInfo *root, child_rel->relids); } - (void) add_eq_member(cur_ec, child_expr, + (void) add_eq_member(root, cur_ec, i, child_expr, new_relids, new_nullable_relids, true, cur_em->em_datatype); } @@ -2227,7 +2277,7 @@ generate_implied_equalities_for_column(PlannerInfo *root, List *result = NIL; bool is_child_rel = (rel->reloptkind == RELOPT_OTHER_MEMBER_REL); Relids parent_relids; - ListCell *lc1; + int i; /* Indexes are available only on base or "other" member relations. */ Assert(IS_SIMPLE_REL(rel)); @@ -2238,11 +2288,12 @@ generate_implied_equalities_for_column(PlannerInfo *root, 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; + ListCell *lc; /* * Won't generate joinclauses if const or single-member (the latter @@ -2252,12 +2303,11 @@ generate_implied_equalities_for_column(PlannerInfo *root, continue; /* - * No point in searching if rel not mentioned in eclass (but we can't - * tell that for a child rel). + * Ensure that these relids are mentioned in the eclass. Child rels + * won't be mentioned in ec_relids, so skip the check for those */ - if (!is_child_rel && - !bms_is_subset(rel->relids, cur_ec->ec_relids)) - continue; + Assert(is_child_rel || bms_is_subset(rel->relids, cur_ec->ec_relids)); + /* * Scan members, looking for a match to the target column. Note that @@ -2270,9 +2320,9 @@ generate_implied_equalities_for_column(PlannerInfo *root, * match. See also get_eclass_for_sort_expr.) */ cur_em = NULL; - foreach(lc2, cur_ec->ec_members) + foreach(lc, cur_ec->ec_members) { - cur_em = (EquivalenceMember *) lfirst(lc2); + cur_em = (EquivalenceMember *) lfirst(lc); if (bms_equal(cur_em->em_relids, rel->relids) && callback(root, rel, cur_ec, cur_em, callback_arg)) break; @@ -2286,9 +2336,9 @@ generate_implied_equalities_for_column(PlannerInfo *root, * Found our match. Scan the other EC members and attempt to generate * joinclauses. */ - foreach(lc2, cur_ec->ec_members) + foreach(lc, cur_ec->ec_members) { - EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc2); + EquivalenceMember *other_em = (EquivalenceMember *) lfirst(lc); Oid eq_op; RestrictInfo *rinfo; @@ -2352,11 +2402,21 @@ bool have_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) { - ListCell *lc1; + Bitmapset *rel1_ecs; + Bitmapset *rel2_ecs; + Bitmapset *matching_ecs; + int i; - foreach(lc1, root->eq_classes) + rel1_ecs = get_eclass_indexes_for_relids(root, rel1->relids); + rel2_ecs = get_eclass_indexes_for_relids(root, rel2->relids); + matching_ecs = bms_int_members(rel1_ecs, rel2_ecs); + + /* XXX or can we just return bms_overlap(rel1_ecs, rel2_ecs) ? */ + + 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); /* * Won't generate joinclauses if single-member (this test covers the @@ -2384,10 +2444,16 @@ have_relevant_eclass_joinclause(PlannerInfo *root, * b.y and a.x = 42", it is worth considering a join between a and b, * since the join result is likely to be small even though it'll end * up being an unqualified nestloop. + * + * Since matching_ecs only contains eclasses common to both relations + * and since we skipped single member eclasses above, we can simply + * return true. */ - if (bms_overlap(rel1->relids, ec->ec_relids) && - bms_overlap(rel2->relids, ec->ec_relids)) - return true; + + Assert(bms_overlap(rel1->relids, ec->ec_relids)); + Assert(bms_overlap(rel2->relids, ec->ec_relids)); + + return true; } return false; @@ -2405,11 +2471,18 @@ have_relevant_eclass_joinclause(PlannerInfo *root, bool has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1) { - ListCell *lc1; + Bitmapset *matched_ecs; + int i; - foreach(lc1, root->eq_classes) + if (IS_SIMPLE_REL(rel1)) + matched_ecs = rel1->eclass_indexes; + else + 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 @@ -2418,12 +2491,13 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1) if (list_length(ec->ec_members) <= 1) continue; + Assert(bms_overlap(rel1->relids, ec->ec_relids)); + /* * 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; } @@ -2556,3 +2630,93 @@ is_redundant_with_indexclauses(RestrictInfo *rinfo, List *indexclauses) 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 have members for these relids + */ +static Bitmapset * +get_eclass_indexes_for_relids(PlannerInfo *root, Relids relids) +{ + Bitmapset *ec_indexes = NULL; + int i = -1; + + 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; +} + +/* + * delete_eclass + * Remove ec from root's eq_classes list and nullify various properties + * of the EquivalenceClass. Does not free memory for ec. + * + * We also resequence each relation's eclass_indexes to remove reference to + * this EquivalenceClass and shift the indexes for higher index classes down + * one place. + * + * ec_index is the index that ec belongs to in root's eq_classes list. We + * could look this up within, but the current caller just happens to know it, + * so passing it in saves looking it up here. + */ +static void +delete_eclass(PlannerInfo *root, EquivalenceClass *ec, int ec_index) +{ + Index i; + + Assert(ec_index >= 0 && ec_index < list_length(root->eq_classes)); + Assert(list_nth(root->eq_classes, ec_index) == ec); + + /* Remove the eclass from the list */ + root->eq_classes = list_delete_ptr(root->eq_classes, ec); + + /* + * Now we must update the eclass_indexes bitmapset for each relation to + * remove mention of this eclass and move higher indexes down 1 bit. + */ + for (i = 1; i < root->simple_rel_array_size; i++) + { + RelOptInfo *rel = root->simple_rel_array[i]; + Bitmapset *new_ec_indexes; + int e; + + if (rel == NULL) + continue; + + /* + * We can skip rels that don't have members in any eclass with an + * index >= to ec_index. If ec_index is 0 then this will just skip + * rels with no eclasses. + */ + if (bms_next_member(rel->eclass_indexes, ec_index - 1) < 0) + continue; + + /* + * XXX is it worth adding a bms supporting function to shift right + * bits from a given position? + */ + new_ec_indexes = NULL; + e = -1; + while ((e = bms_next_member(rel->eclass_indexes, e)) >= 0) + { + if (e < ec_index) + new_ec_indexes = bms_add_member(new_ec_indexes, e); + else if (e > ec_index) + new_ec_indexes = bms_add_member(new_ec_indexes, e - 1); + } + + bms_free(rel->eclass_indexes); + rel->eclass_indexes = new_ec_indexes; + } + + /* just to avoid debugging confusion w/ dangling pointers: */ + ec->ec_members = NIL; + ec->ec_sources = NIL; + ec->ec_derives = NIL; + ec->ec_relids = NULL; +} diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 253e0b7e48..911d0c46fa 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -692,6 +692,8 @@ typedef struct RelOptInfo * baserestrictinfo */ List *joininfo; /* RestrictInfo structures for join clauses * involving this rel */ + Bitmapset *eclass_indexes; /* Indexes into PlannerInfo's eq_classes that + * know about this relation. */ bool has_eclass_joins; /* T means joininfo is incomplete */ /* used by partitionwise joins: */