Re: plan time of MASSIVE partitioning ...

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: plan time of MASSIVE partitioning ...
Дата
Msg-id 24391.1288332514@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: plan time of MASSIVE partitioning ...  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: plan time of MASSIVE partitioning ...  (Boszormenyi Zoltan <zb@cybertec.at>)
Список pgsql-hackers
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.devine@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,

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Pavel Golub
Дата:
Сообщение: Re: add label to enum syntax
Следующее
От: Heikki Linnakangas
Дата:
Сообщение: Re: sorted writes for checkpoints