I wrote: > the right way to make this faster is to refactor things so that we > don't generate useless equivalence classes in the first place, or > at least don't keep them around in the planner's lists once we realize > they're useless.
After a bit of hacking, I propose the attached patch. > I like Heikki's hack to cut down on searching in make_canonical_pathkey, > but I think that complicating the data structure searching beyond that > is just a band-aid. With the given test case and this patch, we end up with exactly two canonical pathkeys referencing a single EquivalenceClass. So as far as I can tell there's not a lot of point in refining the pathkey searching. Now, the EquivalenceClass has got 483 members, which means that there's still some O(N^2) behavior in get_eclass_for_sort_expr. There might be some use in refining the search for a matching eclass member. It's not sticking out in profiling like it did before though. regards, tom lane
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README index d6402cf911817b1b8c17da91019a1fac19bf051a..5c0786f2fe6dea9a72ad66ba93aa8833ab0e26ba 100644 *** a/src/backend/optimizer/README --- b/src/backend/optimizer/README *************** sort ordering was important; and so usin *** 632,640 **** orderings doesn't create any real problem. - Though Bob Devine <bob.dev...@worldnet.att.net> was not involved in the - coding of our optimizer, he is available to field questions about - optimizer topics. -- bjm & tgl --- 632,670 ---- orderings doesn't create any real problem. + Order of processing for EquivalenceClasses and PathKeys + ------------------------------------------------------- + + As alluded to above, there is a specific sequence of phases in the + processing of EquivalenceClasses and PathKeys during planning. During the + initial scanning of the query's quals (deconstruct_jointree followed by + reconsider_outer_join_clauses), we construct EquivalenceClasses based on + mergejoinable clauses found in the quals. At the end of this process, + we know all we can know about equivalence of different variables, so + subsequently there will be no further merging of EquivalenceClasses. + At that point it is possible to consider the EquivalenceClasses as + "canonical" and build canonical PathKeys that reference them. Before + we reach that point (actually, before entering query_planner at all) + we also ensure that we have constructed EquivalenceClasses for all the + expressions used in the query's ORDER BY and related clauses. These + classes might or might not get merged together, depending on what we + find in the quals. + + Because all the EquivalenceClasses are known before we begin path + generation, we can use them as a guide to which indexes are of interest: + if an index's column is not mentioned in any EquivalenceClass then that + index's sort order cannot possibly be helpful for the query. This allows + short-circuiting of much of the processing of create_index_paths() for + irrelevant indexes. + + There are some cases where planner.c constructs additional + EquivalenceClasses and PathKeys after query_planner has completed. + In these cases, the extra ECs/PKs are needed to represent sort orders + that were not considered during query_planner. Such situations should be + minimized since it is impossible for query_planner to return a plan + producing such a sort order, meaning a explicit sort will always be needed. + Currently this happens only for queries involving multiple window functions + with different orderings, so extra sorts are needed anyway. -- bjm & tgl diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index e44e960b5454d4698ed82e4e857794ffe2a9adf2..c101c272a14b2f1b9d92a54670688df057d84a13 100644 *** a/src/backend/optimizer/path/equivclass.c --- b/src/backend/optimizer/path/equivclass.c *************** static bool reconsider_full_join_clause( *** 78,83 **** --- 78,87 ---- * join. (This is the reason why we need a failure return. It's more * convenient to check this case here than at the call sites...) * + * On success return, we have also initialized the clause's left_ec/right_ec + * fields to point to the EquivalenceClass built from it. This saves lookup + * effort later. + * * Note: constructing merged EquivalenceClasses is a standard UNION-FIND * problem, for which there exist better data structures than simple lists. * If this code ever proves to be a bottleneck then it could be sped up --- *************** process_equivalence(PlannerInfo *root, R *** 106,111 **** --- 110,119 ---- *em2; ListCell *lc1; + /* Should not already be marked as having generated an eclass */ + Assert(restrictinfo->left_ec == NULL); + Assert(restrictinfo->right_ec == NULL); + /* Extract info from given clause */ Assert(is_opclause(clause)); opno = ((OpExpr *) clause)->opno; *************** process_equivalence(PlannerInfo *root, R *** 236,243 **** { ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join |= below_outer_join; /* mark the RI as usable with this pair of EMs */ - /* NB: can't set left_ec/right_ec until merging is finished */ restrictinfo->left_em = em1; restrictinfo->right_em = em2; return true; --- 244,253 ---- { ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join |= below_outer_join; + /* mark the RI as associated with this eclass */ + restrictinfo->left_ec = ec1; + restrictinfo->right_ec = ec1; /* mark the RI as usable with this pair of EMs */ restrictinfo->left_em = em1; restrictinfo->right_em = em2; return true; *************** process_equivalence(PlannerInfo *root, R *** 266,271 **** --- 276,284 ---- ec2->ec_relids = NULL; ec1->ec_sources = lappend(ec1->ec_sources, restrictinfo); ec1->ec_below_outer_join |= below_outer_join; + /* mark the RI as associated with this eclass */ + restrictinfo->left_ec = ec1; + restrictinfo->right_ec = ec1; /* mark the RI as usable with this pair of EMs */ restrictinfo->left_em = em1; restrictinfo->right_em = em2; *************** process_equivalence(PlannerInfo *root, R *** 276,281 **** --- 289,297 ---- em2 = add_eq_member(ec1, item2, item2_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 */ + restrictinfo->left_ec = ec1; + restrictinfo->right_ec = ec1; /* mark the RI as usable with this pair of EMs */ restrictinfo->left_em = em1; restrictinfo->right_em = em2; *************** process_equivalence(PlannerInfo *root, R *** 286,291 **** --- 302,310 ---- em1 = add_eq_member(ec2, item1, item1_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 */ + restrictinfo->left_ec = ec2; + restrictinfo->right_ec = ec2; /* mark the RI as usable with this pair of EMs */ restrictinfo->left_em = em1; restrictinfo->right_em = em2; *************** process_equivalence(PlannerInfo *root, R *** 311,316 **** --- 330,338 ---- root->eq_classes = lappend(root->eq_classes, ec); + /* mark the RI as associated with this eclass */ + restrictinfo->left_ec = ec; + restrictinfo->right_ec = ec; /* mark the RI as usable with this pair of EMs */ restrictinfo->left_em = em1; restrictinfo->right_em = em2; *************** add_eq_member(EquivalenceClass *ec, Expr *** 362,376 **** /* * get_eclass_for_sort_expr * Given an expression and opfamily info, find an existing equivalence ! * class it is a member of; if none, build a new single-member * EquivalenceClass for it. * * sortref is the SortGroupRef of the originating SortGroupClause, if any, * or zero if not. (It should never be zero if the expression is volatile!) * * This can be used safely both before and after EquivalenceClass merging; * since it never causes merging it does not invalidate any existing ECs ! * or PathKeys. * * Note: opfamilies must be chosen consistently with the way * process_equivalence() would do; that is, generated from a mergejoinable --- 384,402 ---- /* * get_eclass_for_sort_expr * Given an expression and opfamily info, find an existing equivalence ! * class it is a member of; if none, optionally build a new single-member * EquivalenceClass for it. * * 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 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. + * * This can be used safely both before and after EquivalenceClass merging; * since it never causes merging it does not invalidate any existing ECs ! * or PathKeys. However, ECs added after path generation has begun are ! * of limited usefulness, so usually it's best to create them beforehand. * * Note: opfamilies must be chosen consistently with the way * process_equivalence() would do; that is, generated from a mergejoinable *************** get_eclass_for_sort_expr(PlannerInfo *ro *** 382,388 **** Expr *expr, Oid expr_datatype, List *opfamilies, ! Index sortref) { EquivalenceClass *newec; EquivalenceMember *newem; --- 408,415 ---- Expr *expr, Oid expr_datatype, List *opfamilies, ! Index sortref, ! bool create_it) { EquivalenceClass *newec; EquivalenceMember *newem; *************** get_eclass_for_sort_expr(PlannerInfo *ro *** 426,433 **** } } /* ! * No match, so build a new single-member EC * * Here, we must be sure that we construct the EC in the right context. We * can assume, however, that the passed expr is long-lived. --- 453,464 ---- } } + /* No match; does caller want a NULL result? */ + if (!create_it) + return NULL; + /* ! * OK, build a new single-member EC * * Here, we must be sure that we construct the EC in the right context. We * can assume, however, that the passed expr is long-lived. *************** create_join_clause(PlannerInfo *root, *** 1094,1101 **** rinfo->parent_ec = parent_ec; /* ! * We can set these now, rather than letting them be looked up later, ! * since this is only used after EC merging is complete. */ rinfo->left_ec = ec; rinfo->right_ec = ec; --- 1125,1132 ---- rinfo->parent_ec = parent_ec; /* ! * We know the correct values for left_ec/right_ec, ie this particular EC, ! * so we can just set them directly instead of forcing another lookup. */ rinfo->left_ec = ec; rinfo->right_ec = ec; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index ab3b9cd394d9af54bcd59e9830a9fbcc0a773fa8..bea134b3f1a02db8c611a6fc46d5bba041961682 100644 *** a/src/backend/optimizer/path/joinpath.c --- b/src/backend/optimizer/path/joinpath.c *************** select_mergejoin_clauses(PlannerInfo *ro *** 1041,1047 **** * mergejoin is not really all that big a deal, and so it's not clear * that improving this is important. */ ! cache_mergeclause_eclasses(root, restrictinfo); if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) || EC_MUST_BE_REDUNDANT(restrictinfo->right_ec)) --- 1041,1047 ---- * mergejoin is not really all that big a deal, and so it's not clear * that improving this is important. */ ! update_mergeclause_eclasses(root, restrictinfo); if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) || EC_MUST_BE_REDUNDANT(restrictinfo->right_ec)) diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 643d57a92dc83f6adf4e2d1da223909528d59805..20c6d73617d507b88fce427a681320a684b47cd0 100644 *** a/src/backend/optimizer/path/pathkeys.c --- b/src/backend/optimizer/path/pathkeys.c *************** static PathKey *make_pathkey_from_sortin *** 40,45 **** --- 40,46 ---- Expr *expr, Oid ordering_op, bool nulls_first, Index sortref, + bool create_it, bool canonicalize); static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno); *************** canonicalize_pathkeys(PlannerInfo *root, *** 230,235 **** --- 231,240 ---- * If the PathKey is being generated from a SortGroupClause, sortref should be * the SortGroupClause's SortGroupRef; otherwise zero. * + * create_it is TRUE if we should create any missing EquivalenceClass + * needed to represent the sort key. If it's FALSE, we return NULL if the + * sort key isn't already present in any EquivalenceClass. + * * canonicalize should always be TRUE after EquivalenceClass merging has * been performed, but FALSE if we haven't done EquivalenceClass merging yet. */ *************** make_pathkey_from_sortinfo(PlannerInfo * *** 238,243 **** --- 243,249 ---- Expr *expr, Oid ordering_op, bool nulls_first, Index sortref, + bool create_it, bool canonicalize) { Oid opfamily, *************** make_pathkey_from_sortinfo(PlannerInfo * *** 300,308 **** COERCE_DONTCARE); } ! /* Now find or create a matching EquivalenceClass */ eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies, ! sortref); /* And finally we can find or create a PathKey node */ if (canonicalize) --- 306,318 ---- COERCE_DONTCARE); } ! /* Now find or (optionally) create a matching EquivalenceClass */ eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies, ! sortref, create_it); ! ! /* Fail if no EC and !create_it */ ! if (!eclass) ! return NULL; /* And finally we can find or create a PathKey node */ if (canonicalize) *************** get_cheapest_fractional_path_for_pathkey *** 478,485 **** * The result is canonical, meaning that redundant pathkeys are removed; * it may therefore have fewer entries than there are index columns. * ! * We generate the full pathkeys list whether or not all are useful for the ! * current query. Caller should do truncate_useless_pathkeys(). */ List * build_index_pathkeys(PlannerInfo *root, --- 488,498 ---- * The result is canonical, meaning that redundant pathkeys are removed; * it may therefore have fewer entries than there are index columns. * ! * Another reason for stopping early is that we may be able to tell that ! * an index column's sort order is uninteresting for this query. However, ! * that test is just based on the existence of an EquivalenceClass and not ! * on position in pathkey lists, so it's not complete. Caller should call ! * truncate_useless_pathkeys() to possibly remove more pathkeys. */ List * build_index_pathkeys(PlannerInfo *root, *************** build_index_pathkeys(PlannerInfo *root, *** 527,540 **** indexprs_item = lnext(indexprs_item); } ! /* OK, make a canonical pathkey for this sort key */ cpathkey = make_pathkey_from_sortinfo(root, indexkey, sortop, nulls_first, 0, true); /* Add to list unless redundant */ if (!pathkey_is_redundant(cpathkey, retval)) retval = lappend(retval, cpathkey); --- 540,562 ---- indexprs_item = lnext(indexprs_item); } ! /* OK, try to make a canonical pathkey for this sort key */ cpathkey = make_pathkey_from_sortinfo(root, indexkey, sortop, nulls_first, 0, + false, true); + /* + * If the sort key isn't already present in any EquivalenceClass, + * then it's not an interesting sort order for this query. So + * we can stop now --- lower-order sort keys aren't useful either. + */ + if (!cpathkey) + break; + /* Add to list unless redundant */ if (!pathkey_is_redundant(cpathkey, retval)) retval = lappend(retval, cpathkey); *************** convert_subquery_pathkeys(PlannerInfo *r *** 644,656 **** outer_expr, sub_member->em_datatype, sub_eclass->ec_opfamilies, ! 0); ! best_pathkey = ! make_canonical_pathkey(root, ! outer_ec, ! sub_pathkey->pk_opfamily, ! sub_pathkey->pk_strategy, ! sub_pathkey->pk_nulls_first); } } else --- 666,685 ---- outer_expr, sub_member->em_datatype, sub_eclass->ec_opfamilies, ! 0, ! false); ! ! /* ! * If we don't find a matching EC, sub-pathkey isn't ! * interesting to the outer query ! */ ! if (outer_ec) ! best_pathkey = ! make_canonical_pathkey(root, ! outer_ec, ! sub_pathkey->pk_opfamily, ! sub_pathkey->pk_strategy, ! sub_pathkey->pk_nulls_first); } } else *************** convert_subquery_pathkeys(PlannerInfo *r *** 738,744 **** outer_expr, sub_member->em_datatype, sub_eclass->ec_opfamilies, ! 0); outer_pk = make_canonical_pathkey(root, outer_ec, sub_pathkey->pk_opfamily, --- 767,782 ---- outer_expr, sub_member->em_datatype, sub_eclass->ec_opfamilies, ! 0, ! false); ! ! /* ! * If we don't find a matching EC, this sub-pathkey isn't ! * interesting to the outer query ! */ ! if (!outer_ec) ! continue; ! outer_pk = make_canonical_pathkey(root, outer_ec, sub_pathkey->pk_opfamily, *************** make_pathkeys_for_sortclauses(PlannerInf *** 859,864 **** --- 897,903 ---- sortcl->sortop, sortcl->nulls_first, sortcl->tleSortGroupRef, + true, canonicalize); /* Canonical form eliminates redundant ordering keys */ *************** make_pathkeys_for_sortclauses(PlannerInf *** 878,923 **** ****************************************************************************/ /* ! * cache_mergeclause_eclasses ! * Make the cached EquivalenceClass links valid in a mergeclause ! * restrictinfo. * * RestrictInfo contains fields in which we may cache pointers to * EquivalenceClasses for the left and right inputs of the mergeclause. * (If the mergeclause is a true equivalence clause these will be the ! * same EquivalenceClass, otherwise not.) */ void ! cache_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo) { Assert(restrictinfo->mergeopfamilies != NIL); ! /* the cached values should be either both set or both not */ ! if (restrictinfo->left_ec == NULL) ! { ! Expr *clause = restrictinfo->clause; ! Oid lefttype, ! righttype; ! /* Need the declared input types of the operator */ ! op_input_types(((OpExpr *) clause)->opno, &lefttype, &righttype); ! /* Find or create a matching EquivalenceClass for each side */ ! restrictinfo->left_ec = ! get_eclass_for_sort_expr(root, ! (Expr *) get_leftop(clause), ! lefttype, ! restrictinfo->mergeopfamilies, ! 0); ! restrictinfo->right_ec = ! get_eclass_for_sort_expr(root, ! (Expr *) get_rightop(clause), ! righttype, ! restrictinfo->mergeopfamilies, ! 0); ! } ! else ! Assert(restrictinfo->right_ec != NULL); } /* --- 917,997 ---- ****************************************************************************/ /* ! * initialize_mergeclause_eclasses ! * Set the EquivalenceClass links in a mergeclause restrictinfo. * * RestrictInfo contains fields in which we may cache pointers to * EquivalenceClasses for the left and right inputs of the mergeclause. * (If the mergeclause is a true equivalence clause these will be the ! * same EquivalenceClass, otherwise not.) If the mergeclause is either ! * used to generate an EquivalenceClass, or derived from an EquivalenceClass, ! * then it's easy to set up the left_ec and right_ec members --- otherwise, ! * this function should be called to set them up. We will generate new ! * EquivalenceClauses if necessary to represent the mergeclause's left and ! * right sides. ! * ! * Note this is called before EC merging is complete, so the links won't ! * necessarily point to canonical ECs. Before they are actually used for ! * anything, update_mergeclause_eclasses must be called to ensure that ! * they've been updated to point to canonical ECs. */ void ! initialize_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo) { + Expr *clause = restrictinfo->clause; + Oid lefttype, + righttype; + + /* Should be a mergeclause ... */ Assert(restrictinfo->mergeopfamilies != NIL); + /* ... with links not yet set */ + Assert(restrictinfo->left_ec == NULL); + Assert(restrictinfo->right_ec == NULL); ! /* Need the declared input types of the operator */ ! op_input_types(((OpExpr *) clause)->opno, &lefttype, &righttype); ! /* Find or create a matching EquivalenceClass for each side */ ! restrictinfo->left_ec = ! get_eclass_for_sort_expr(root, ! (Expr *) get_leftop(clause), ! lefttype, ! restrictinfo->mergeopfamilies, ! 0, ! true); ! restrictinfo->right_ec = ! get_eclass_for_sort_expr(root, ! (Expr *) get_rightop(clause), ! righttype, ! restrictinfo->mergeopfamilies, ! 0, ! true); ! } ! /* ! * update_mergeclause_eclasses ! * Make the cached EquivalenceClass links valid in a mergeclause ! * restrictinfo. ! * ! * These pointers should have been set by process_equivalence or ! * initialize_mergeclause_eclasses, but they might have been set to ! * non-canonical ECs that got merged later. Chase up to the canonical ! * merged parent if so. ! */ ! void ! update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo) ! { ! /* Should be a merge clause ... */ ! Assert(restrictinfo->mergeopfamilies != NIL); ! /* ... with pointers already set */ ! Assert(restrictinfo->left_ec != NULL); ! Assert(restrictinfo->right_ec != NULL); ! ! /* Chase up to the top as needed */ ! while (restrictinfo->left_ec->ec_merged) ! restrictinfo->left_ec = restrictinfo->left_ec->ec_merged; ! while (restrictinfo->right_ec->ec_merged) ! restrictinfo->right_ec = restrictinfo->right_ec->ec_merged; } /* *************** find_mergeclauses_for_pathkeys(PlannerIn *** 953,959 **** { RestrictInfo *rinfo = (RestrictInfo *) lfirst(i); ! cache_mergeclause_eclasses(root, rinfo); } foreach(i, pathkeys) --- 1027,1033 ---- { RestrictInfo *rinfo = (RestrictInfo *) lfirst(i); ! update_mergeclause_eclasses(root, rinfo); } foreach(i, pathkeys) *************** select_outer_pathkeys_for_merge(PlannerI *** 1089,1095 **** ListCell *lc2; /* get the outer eclass */ ! cache_mergeclause_eclasses(root, rinfo); if (rinfo->outer_is_left) oeclass = rinfo->left_ec; --- 1163,1169 ---- ListCell *lc2; /* get the outer eclass */ ! update_mergeclause_eclasses(root, rinfo); if (rinfo->outer_is_left) oeclass = rinfo->left_ec; *************** make_inner_pathkeys_for_merge(PlannerInf *** 1250,1256 **** EquivalenceClass *ieclass; PathKey *pathkey; ! cache_mergeclause_eclasses(root, rinfo); if (rinfo->outer_is_left) { --- 1324,1330 ---- EquivalenceClass *ieclass; PathKey *pathkey; ! update_mergeclause_eclasses(root, rinfo); if (rinfo->outer_is_left) { *************** pathkeys_useful_for_merging(PlannerInfo *** 1366,1372 **** if (restrictinfo->mergeopfamilies == NIL) continue; ! cache_mergeclause_eclasses(root, restrictinfo); if (pathkey->pk_eclass == restrictinfo->left_ec || pathkey->pk_eclass == restrictinfo->right_ec) --- 1440,1446 ---- if (restrictinfo->mergeopfamilies == NIL) continue; ! update_mergeclause_eclasses(root, restrictinfo); if (pathkey->pk_eclass == restrictinfo->left_ec || pathkey->pk_eclass == restrictinfo->right_ec) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index a2ff314679daa30003d59d04ec01066049e1cb13..77ebadd5cc6a634f5e07886c19a3e4c71dbe3c31 100644 *** a/src/backend/optimizer/plan/initsplan.c --- b/src/backend/optimizer/plan/initsplan.c *************** distribute_qual_to_rels(PlannerInfo *roo *** 1066,1071 **** --- 1066,1077 ---- * * If none of the above hold, pass it off to * distribute_restrictinfo_to_rels(). + * + * In all cases, it's important to initialize the left_ec and right_ec + * fields of a mergejoinable clause, so that all possibly mergejoinable + * expressions have representations in EquivalenceClasses. If + * process_equivalence is successful, it will take care of that; + * otherwise, we have to call initialize_mergeclause_eclasses to do it. */ if (restrictinfo->mergeopfamilies) { *************** distribute_qual_to_rels(PlannerInfo *roo *** 1073,1082 **** { if (process_equivalence(root, restrictinfo, below_outer_join)) return; ! /* EC rejected it, so pass to distribute_restrictinfo_to_rels */ } else if (maybe_outer_join && restrictinfo->can_join) { if (bms_is_subset(restrictinfo->left_relids, outerjoin_nonnullable) && !bms_overlap(restrictinfo->right_relids, --- 1079,1093 ---- { if (process_equivalence(root, restrictinfo, below_outer_join)) return; ! /* EC rejected it, so set left_ec/right_ec the hard way ... */ ! initialize_mergeclause_eclasses(root, restrictinfo); ! /* ... and fall through to distribute_restrictinfo_to_rels */ } else if (maybe_outer_join && restrictinfo->can_join) { + /* we need to set up left_ec/right_ec the hard way */ + initialize_mergeclause_eclasses(root, restrictinfo); + /* now see if it should go to any outer-join lists */ if (bms_is_subset(restrictinfo->left_relids, outerjoin_nonnullable) && !bms_overlap(restrictinfo->right_relids, *************** distribute_qual_to_rels(PlannerInfo *roo *** 1104,1109 **** --- 1115,1126 ---- restrictinfo); return; } + /* nope, so fall through to distribute_restrictinfo_to_rels */ + } + else + { + /* we still need to set up left_ec/right_ec */ + initialize_mergeclause_eclasses(root, restrictinfo); } } *************** process_implied_equality(PlannerInfo *ro *** 1404,1409 **** --- 1421,1429 ---- * * This overlaps the functionality of process_implied_equality(), but we * must return the RestrictInfo, not push it into the joininfo tree. + * + * Note: we do not do initialize_mergeclause_eclasses() here. It is + * caller's responsibility that left_ec/right_ec be set as necessary. */ RestrictInfo * build_implied_join_equality(Oid opno, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 5f628eeeb96174a3c9051257e235baff43efcacd..6bce53cce57d5c3d8551ad12c371e36100c009ff 100644 *** a/src/include/optimizer/paths.h --- b/src/include/optimizer/paths.h *************** extern EquivalenceClass *get_eclass_for_ *** 116,122 **** Expr *expr, Oid expr_datatype, List *opfamilies, ! Index sortref); extern void generate_base_implied_equalities(PlannerInfo *root); extern List *generate_join_implied_equalities(PlannerInfo *root, RelOptInfo *joinrel, --- 116,123 ---- Expr *expr, Oid expr_datatype, List *opfamilies, ! Index sortref, ! bool create_it); extern void generate_base_implied_equalities(PlannerInfo *root); extern List *generate_join_implied_equalities(PlannerInfo *root, RelOptInfo *joinrel, *************** extern List *make_pathkeys_for_sortclaus *** 172,178 **** List *sortclauses, List *tlist, bool canonicalize); ! extern void cache_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo); extern List *find_mergeclauses_for_pathkeys(PlannerInfo *root, List *pathkeys, --- 173,181 ---- List *sortclauses, List *tlist, bool canonicalize); ! extern void initialize_mergeclause_eclasses(PlannerInfo *root, ! RestrictInfo *restrictinfo); ! extern void update_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo); extern List *find_mergeclauses_for_pathkeys(PlannerInfo *root, List *pathkeys,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers