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

Reply via email to