Speed up finding EquivalenceClasses for a given set of rels
authorDavid Rowley <drowley@postgresql.org>
Sun, 21 Jul 2019 05:30:58 +0000 (17:30 +1200)
committerDavid Rowley <drowley@postgresql.org>
Sun, 21 Jul 2019 05:30:58 +0000 (17:30 +1200)
Previously in order to determine which ECs a relation had members in, we
had to loop over all ECs stored in PlannerInfo's eq_classes and check if
ec_relids mentioned the relation.  For the most part, this was fine, as
generally, unless queries were fairly complex, the overhead of performing
the lookup would have not been that significant.  However, when queries
contained large numbers of joins and ECs, the overhead to find the set of
classes matching a given set of relations could become a significant
portion of the overall planning effort.

Here we allow a much more efficient method to access the ECs which match a
given relation or set of relations.  A new Bitmapset field in RelOptInfo
now exists to store the indexes into PlannerInfo's eq_classes list which
each relation is mentioned in.  This allows very fast lookups to find all
ECs belonging to a single relation.  When we need to lookup ECs belonging
to a given pair of relations, we can simply bitwise-AND the Bitmapsets from
each relation and use the result to perform the lookup.

We also take the opportunity to write a new implementation of
generate_join_implied_equalities which makes use of the new indexes.
generate_join_implied_equalities_for_ecs must remain as is as it can be
given a custom list of ECs, which we can't easily determine the indexes of.

This was originally intended to fix the performance penalty of looking up
foreign keys matching a join condition which was introduced by 100340e2d.
However, we're speeding up much more than just that here.

Author: David Rowley, Tom Lane
Reviewed-by: Tom Lane, Tomas Vondra
Discussion: https://postgr.es/m/6970.1545327857@sss.pgh.pa.us

src/backend/nodes/outfuncs.c
src/backend/optimizer/path/equivclass.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/relnode.c
src/include/nodes/pathnodes.h

index 8e31fae47fc982daa991dae249841b5bb4819440..86c31a48c983c42bde0293bbb4ee6733a5b78a54 100644 (file)
@@ -2195,6 +2195,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
        WRITE_NODE_FIELD(cte_plan_ids);
        WRITE_NODE_FIELD(multiexpr_params);
        WRITE_NODE_FIELD(eq_classes);
+       WRITE_BOOL_FIELD(ec_merging_done);
        WRITE_NODE_FIELD(canon_pathkeys);
        WRITE_NODE_FIELD(left_join_clauses);
        WRITE_NODE_FIELD(right_join_clauses);
@@ -2261,6 +2262,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
        WRITE_UINT_FIELD(pages);
        WRITE_FLOAT_FIELD(tuples, "%.0f");
        WRITE_FLOAT_FIELD(allvisfrac, "%.6f");
+       WRITE_BITMAPSET_FIELD(eclass_indexes);
        WRITE_NODE_FIELD(subroot);
        WRITE_NODE_FIELD(subplan_params);
        WRITE_INT_FIELD(rel_parallel_workers);
index 78d076b13cde98ce188de30d9cbad3f53356cfa6..2c595dfb08c77ffcba61719a54322b90ca6c0b7a 100644 (file)
@@ -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 Bitmapset *get_common_eclass_indexes(PlannerInfo *root, Relids relids1,
+                                                                                       Relids relids2);
 
 
 /*
@@ -341,10 +345,11 @@ process_equivalence(PlannerInfo *root,
 
                /*
                 * 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");
 
                /*
@@ -743,6 +748,26 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 
        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;
@@ -800,42 +825,71 @@ get_eclass_for_sort_expr(PlannerInfo *root,
 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++;
        }
 }
 
@@ -1073,11 +1127,72 @@ generate_join_implied_equalities(PlannerInfo *root,
                                                                 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;
 }
 
 /*
@@ -2022,12 +2137,24 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
        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;
@@ -2037,14 +2164,6 @@ match_eclasses_to_foreign_key_col(PlannerInfo *root,
                        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);
@@ -2104,11 +2223,19 @@ add_child_rel_equivalences(PlannerInfo *root,
                                                   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;
 
                /*
@@ -2119,12 +2246,8 @@ add_child_rel_equivalences(PlannerInfo *root,
                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
@@ -2202,6 +2325,9 @@ add_child_rel_equivalences(PlannerInfo *root,
                                (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);
                        }
                }
        }
@@ -2241,7 +2367,10 @@ 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;
+
+       /* 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));
@@ -2252,12 +2381,16 @@ 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;
 
+               /* 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)
@@ -2265,14 +2398,6 @@ generate_implied_equalities_for_column(PlannerInfo *root,
                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
@@ -2366,11 +2491,25 @@ bool
 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
@@ -2382,12 +2521,12 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
                /*
                 * 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
@@ -2399,9 +2538,8 @@ have_relevant_eclass_joinclause(PlannerInfo *root,
                 * 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;
@@ -2419,11 +2557,17 @@ 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)
+       /* 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
@@ -2436,8 +2580,7 @@ has_relevant_eclass_joinclause(PlannerInfo *root, RelOptInfo *rel1)
                 * 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;
        }
 
@@ -2570,3 +2713,54 @@ 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 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);
+}
index 202a4b9db8210c09651c372359626f3d834b8da2..2f4fea241a00d3d07f5fe09cdd336279efc4360b 100644 (file)
@@ -48,9 +48,7 @@ static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
  *       entry if there's not one already.
  *
  * Note that this function must not be used until after we have completed
- * merging EquivalenceClasses.  (We don't try to enforce that here; instead,
- * equivclass.c will complain if a merge occurs after root->canon_pathkeys
- * has become nonempty.)
+ * merging EquivalenceClasses.
  */
 PathKey *
 make_canonical_pathkey(PlannerInfo *root,
@@ -61,6 +59,10 @@ make_canonical_pathkey(PlannerInfo *root,
        ListCell   *lc;
        MemoryContext oldcontext;
 
+       /* Can't make canonical pathkeys if the set of ECs might still change */
+       if (!root->ec_merging_done)
+               elog(ERROR, "too soon to build canonical pathkeys");
+
        /* The passed eclass might be non-canonical, so chase up to the top */
        while (eclass->ec_merged)
                eclass = eclass->ec_merged;
index 2dbf1db8447801b96eac4d5c29189d108a36f52a..df3f8c25440f02a6b62dc33308d3012b9f804e01 100644 (file)
@@ -139,6 +139,12 @@ query_planner(PlannerInfo *root,
                                /* Select cheapest path (pretty easy in this case...) */
                                set_cheapest(final_rel);
 
+                               /*
+                                * We don't need to run generate_base_implied_equalities, but
+                                * we do need to pretend that EC merging is complete.
+                                */
+                               root->ec_merging_done = true;
+
                                /*
                                 * We still are required to call qp_callback, in case it's
                                 * something like "SELECT 2+2 ORDER BY 1".
index ca3b7f29e18ca85c47b4b9c6f2051a9252c640e0..36fefd96a4dad8131ed8b0d53541d36df2e94b4e 100644 (file)
@@ -618,6 +618,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
        root->cte_plan_ids = NIL;
        root->multiexpr_params = NIL;
        root->eq_classes = NIL;
+       root->ec_merging_done = false;
        root->append_rel_list = NIL;
        root->rowMarks = NIL;
        memset(root->upper_rels, 0, sizeof(root->upper_rels));
index e20bee0b330b88cee88855ef3f58f7ce66e054fd..4fbc03fe5418f90c1961f5b420d664b32b13d43a 100644 (file)
@@ -886,6 +886,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
        subroot->cte_plan_ids = NIL;
        subroot->multiexpr_params = NIL;
        subroot->eq_classes = NIL;
+       subroot->ec_merging_done = false;
        subroot->append_rel_list = NIL;
        subroot->rowMarks = NIL;
        memset(subroot->upper_rels, 0, sizeof(subroot->upper_rels));
index f5f934ab5cf394fed32f935a85e58ebfd9fefd14..5a11c1235c395987f3d6c45db2d3c897f715048b 100644 (file)
@@ -120,6 +120,15 @@ plan_set_operations(PlannerInfo *root)
        Assert(parse->windowClause == NIL);
        Assert(parse->distinctClause == NIL);
 
+       /*
+        * In the outer query level, we won't have any true equivalences to deal
+        * with; but we do want to be able to make pathkeys, which will require
+        * single-member EquivalenceClasses.  Indicate that EC merging is complete
+        * so that pathkeys.c won't complain.
+        */
+       Assert(root->eq_classes == NIL);
+       root->ec_merging_done = true;
+
        /*
         * We'll need to build RelOptInfos for each of the leaf subqueries, which
         * are RTE_SUBQUERY rangetable entries in this Query.  Prepare the index
index 6054bd2b5353d96fe5b8ad3829ea8061e23d1da6..37d228ce5d0048217dc9a3697bcc16bcb23baf1d 100644 (file)
@@ -218,6 +218,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
        rel->pages = 0;
        rel->tuples = 0;
        rel->allvisfrac = 0;
+       rel->eclass_indexes = NULL;
        rel->subroot = NULL;
        rel->subplan_params = NIL;
        rel->rel_parallel_workers = -1; /* set up in get_relation_info */
@@ -629,6 +630,7 @@ build_join_rel(PlannerInfo *root,
        joinrel->pages = 0;
        joinrel->tuples = 0;
        joinrel->allvisfrac = 0;
+       joinrel->eclass_indexes = NULL;
        joinrel->subroot = NULL;
        joinrel->subplan_params = NIL;
        joinrel->rel_parallel_workers = -1;
@@ -808,6 +810,7 @@ build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel,
        joinrel->pages = 0;
        joinrel->tuples = 0;
        joinrel->allvisfrac = 0;
+       joinrel->eclass_indexes = NULL;
        joinrel->subroot = NULL;
        joinrel->subplan_params = NIL;
        joinrel->serverid = InvalidOid;
index 441e64eca9a9c3db188541b43786289f683d8902..e3c579ee4437505d305274e5423a77a04439e8ad 100644 (file)
@@ -265,6 +265,8 @@ struct PlannerInfo
 
        List       *eq_classes;         /* list of active EquivalenceClasses */
 
+       bool            ec_merging_done;        /* set true once ECs are canonical */
+
        List       *canon_pathkeys; /* list of "canonical" PathKeys */
 
        List       *left_join_clauses;  /* list of RestrictInfos for mergejoinable
@@ -505,6 +507,8 @@ typedef struct PartitionSchemeData *PartitionScheme;
  *             pages - number of disk pages in relation (zero if not a table)
  *             tuples - number of tuples in relation (not considering restrictions)
  *             allvisfrac - fraction of disk pages that are marked all-visible
+ *             eclass_indexes - EquivalenceClasses that mention this rel (filled
+ *                                              only after EC merging is complete)
  *             subroot - PlannerInfo for subquery (NULL if it's not a subquery)
  *             subplan_params - list of PlannerParamItems to be passed to subquery
  *
@@ -678,6 +682,8 @@ typedef struct RelOptInfo
        BlockNumber pages;                      /* size estimates derived from pg_class */
        double          tuples;
        double          allvisfrac;
+       Bitmapset  *eclass_indexes; /* Indexes in PlannerInfo's eq_classes list of
+                                                                * ECs that mention this rel */
        PlannerInfo *subroot;           /* if subquery */
        List       *subplan_params; /* if subquery */
        int                     rel_parallel_workers;   /* wanted number of parallel workers */