diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 51b16d4f168..0395cee9a7c 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -3114,7 +3114,7 @@ add_child_rel_equivalences(PlannerInfo *root, /* * We save the knowledge that 'child_em' can be translated * using 'child_rel'. This knowledge is useful for - * add_transformed_child_version() to find child members from + * iterate_child_rel_equivalences() to find child members from * the given Relids. */ cur_em->em_child_relids = bms_add_member(cur_em->em_child_relids, @@ -3209,6 +3209,7 @@ add_child_join_rel_equivalences(PlannerInfo *root, Expr *child_expr; Relids new_relids; EquivalenceMember *child_em; + int j; if (parent_joinrel->reloptkind == RELOPT_JOINREL) { @@ -3248,12 +3249,39 @@ add_child_join_rel_equivalences(PlannerInfo *root, /* * We save the knowledge that 'child_em' can be translated * using 'child_joinrel'. This knowledge is useful for - * add_transformed_child_version() to find child members from + * iterate_child_rel_equivalences() to find child members from * the given Relids. */ cur_em->em_child_joinrel_relids = bms_add_member(cur_em->em_child_joinrel_relids, child_joinrel->join_rel_list_index); + + /* + * Update the corresponding inverted indexes. + */ + j = -1; + while ((j = bms_next_member(child_joinrel->relids, j)) >= 0) + { + EquivalenceClassIndexes *indexes = + &root->eclass_indexes_array[j]; + + /* + * We do not need to update the inverted index of the top + * parent relations. This is because EquivalenceMembers + * that have only such parent relations as em_relids are + * already present in the ec_members, and so cannot be + * candidates for additional iteration by + * iterate_child_rel_equivalences(). Since the function + * needs EquivalenceMembers whose em_relids has child + * relations, skipping the update of this inverted index + * allows for faster iteration. + */ + if (root->top_parent_relid_array[j] == -1) + continue; + indexes->joinrel_indexes = + bms_add_member(indexes->joinrel_indexes, + child_joinrel->join_rel_list_index); + } } } } @@ -3316,7 +3344,7 @@ add_setop_child_rel_equivalences(PlannerInfo *root, RelOptInfo *child_rel, /* * We save the knowledge that 'child_em' can be translated using * 'child_rel'. This knowledge is useful for - * add_transformed_child_version() to find child members from the + * iterate_child_rel_equivalences() to find child members from the * given Relids. */ parent_em->em_child_relids = @@ -3449,8 +3477,10 @@ add_transformed_child_version(EquivalenceChildMemberIterator *it, /* * iterate_child_rel_equivalences - * Add transformed EquivalenceMembers referencing child rels in the given - * child_relids to the iterator. + * Add child EquivalenceMembers whose em_relids is a subset of the given + * 'child_relids' to the iterator. Note that this function may add false + * positives, so callers must check that the members that this function adds + * satisfy the desired condition. * * The transformation is done in add_transformed_child_version(). */ @@ -3461,9 +3491,6 @@ iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it, EquivalenceMember *parent_em, Relids child_relids) { - int i; - Relids matching_relids; - /* The given EquivalenceMember should be parent */ Assert(!parent_em->em_is_child); @@ -3473,46 +3500,111 @@ iterate_child_rel_equivalences(EquivalenceChildMemberIterator *it, * is done in add_child_rel_equivalences() and * add_child_join_rel_equivalences(). To retrieve child EquivalenceMembers * of some parent, we need to know which RelOptInfos have such child - * members. This information is stored in indexes named em_child_relids - * and em_child_joinrel_relids. + * members. We can know this information using indexes named + * EquivalenceMember->em_child_relids, + * EquivalenceMember->em_child_joinrel_relids, and + * EquivalenceClassIndexes->joinrel_indexes. + * + * We use an inverted index mechanism to quickly iterate over the members + * whose em_relids is a subset of the given 'child_relids'. The inverted + * indexes store RelOptInfo indices that have EquivalenceMembers + * mentioning them. Taking the union of these indexes allows to find which + * RelOptInfos have the EquivalenceMember we are looking for. With this + * method, the em_relids of the newly iterated ones overlap the given + * 'child_relids', but may not be subsets, so the caller must check that + * they satisfy the desired condition. * - * This function iterates over the child EquivalenceMembers that reference - * the given 'child_relids'. To do this, we first intersect 'child_relids' - * with these indexes. The result contains Relids of RelOptInfos that have - * child EquivalenceMembers we want to retrieve. Then we get the child - * members from the RelOptInfos using add_transformed_child_version(). + * The above comments are about joinrels, and for simple rels, this + * mechanism is simpler. It is sufficient to simply add the child + * EquivalenceMembers of RelOptInfo to the iterator. * - * We need to do these steps for each of the two indexes. + * We need to perform these steps for each of the two types of relations. */ /* * First, we translate simple rels. */ - matching_relids = bms_intersect(parent_em->em_child_relids, - child_relids); - i = -1; - while ((i = bms_next_member(matching_relids, i)) >= 0) + if (!bms_is_empty(parent_em->em_child_relids)) { - /* Add transformed child version for this child rel */ - RelOptInfo *child_rel = root->simple_rel_array[i]; + Relids matching_relids; + int i; + + /* Step 1: Get simple rels' Relids that have wanted EMs. */ + matching_relids = bms_intersect(parent_em->em_child_relids, + child_relids); - Assert(child_rel != NULL); - add_transformed_child_version(it, root, ec, parent_em, child_rel); + /* Step 2: Fetch wanted EMs. */ + i = -1; + while ((i = bms_next_member(matching_relids, i)) >= 0) + { + RelOptInfo *child_rel = root->simple_rel_array[i]; + + Assert(child_rel != NULL); + add_transformed_child_version(it, root, ec, parent_em, child_rel); + } + bms_free(matching_relids); } - bms_free(matching_relids); /* * Next, we try to translate join rels. */ - i = -1; - while ((i = bms_next_member(parent_em->em_child_joinrel_relids, i)) >= 0) + if (!bms_is_empty(parent_em->em_child_joinrel_relids)) { - /* Add transformed child version for this child join rel */ - RelOptInfo *child_joinrel = - list_nth_node(RelOptInfo, root->join_rel_list, i); + Bitmapset *matching_indexes; + int i; + + /* Step 1: Get join rels' indices that have wanted EMs. */ + i = -1; + matching_indexes = NULL; + while ((i = bms_next_member(child_relids, i)) >= 0) + { + EquivalenceClassIndexes *indexes = &root->eclass_indexes_array[i]; + + matching_indexes = + bms_add_members(matching_indexes, indexes->joinrel_indexes); + } + matching_indexes = bms_int_members(matching_indexes, + parent_em->em_child_joinrel_relids); - Assert(child_joinrel != NULL); - add_transformed_child_version(it, root, ec, parent_em, child_joinrel); + /* Step 2: Fetch wanted EMs. */ + i = -1; + while ((i = bms_next_member(matching_indexes, i)) >= 0) + { + RelOptInfo *child_joinrel = + list_nth_node(RelOptInfo, root->join_rel_list, i); + + Assert(child_joinrel != NULL); + + /* + * If this joinrel's Relids is not a subset of the given one, then + * the child EquivalenceMembers it holds should never be a subset + * either. + */ + if (bms_is_subset(child_joinrel->relids, child_relids)) + add_transformed_child_version(it, root, ec, parent_em, child_joinrel); +#ifdef USE_ASSERT_CHECKING + else + { + /* + * Verify that the above comment is correct. + * + * NOTE: We may remove this assertion after the beta process. + */ + + ListCell *lc; + + foreach(lc, child_joinrel->eclass_child_members) + { + EquivalenceMember *child_em = lfirst_node(EquivalenceMember, lc); + + if (child_em->em_parent != parent_em) + continue; + Assert(!bms_is_subset(child_em->em_relids, child_relids)); + } + } +#endif + } + bms_free(matching_indexes); } } @@ -4040,11 +4132,12 @@ eclass_rinfo_iterator_next(RestrictInfoIterator *iter) ++(iter->index); bitnum = iter->index % BITS_PER_BITMAPWORD; mask = (~(bitmapword) 0) << bitnum; + w = iter->last_word & mask; /* * Do we need to consume a new word? */ - if (bitnum == 0 || (iter->last_word & mask) == 0) + if (bitnum == 0 || w == 0) { /* * Yes, we need a new word. @@ -4114,8 +4207,7 @@ eclass_rinfo_iterator_next(RestrictInfoIterator *iter) if (word != 0) { /* Yes, we find new ones. */ - iter->last_word = word; - mask = (~(bitmapword) 0); + w = iter->last_word = word; break; } /* No, we need to consume more word */ @@ -4123,9 +4215,8 @@ eclass_rinfo_iterator_next(RestrictInfoIterator *iter) } /* - * Here, 'iter->last_word' must have a new member. We get it. + * Here, 'iter->last_word' or 'w' must have a new member. We get it. */ - w = iter->last_word & mask; Assert(w != 0); iter->index = iter->wordnum * BITS_PER_BITMAPWORD + bmw_rightmost_one_pos(w); diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 35af63bb399..cef24c10459 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -1590,11 +1590,15 @@ typedef struct * EquivalenceClassIndexes * * As mentioned in the EquivalenceClass comment, we introduce a - * bitmapset-based indexing mechanism for faster lookups of RestrictInfo. This - * struct exists for each relation and holds the corresponding indexes. + * bitmapset-based indexing mechanism for faster lookups of child + * EquivalenceMembers and RestrictInfos. This struct exists for each relation + * and holds the corresponding indexes. */ typedef struct EquivalenceClassIndexes { + Bitmapset *joinrel_indexes; /* Indexes in PlannerInfo's join_rel_list + * list for RelOptInfos that mention this + * relation */ Bitmapset *source_indexes; /* Indexes in PlannerInfo's eq_sources list * for RestrictInfos that mention this * relation */