diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 2385d02..8ed30c0 100644
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
*************** expression_tree_walker(Node *node,
*** 2192,2197 ****
--- 2192,2208 ----
  				/* groupClauses are deemed uninteresting */
  			}
  			break;
+ 		case T_IndexClause:
+ 			{
+ 				IndexClause *iclause = (IndexClause *) node;
+ 
+ 				if (walker(iclause->rinfo, context))
+ 					return true;
+ 				if (expression_tree_walker((Node *) iclause->indexquals,
+ 										   walker, context))
+ 					return true;
+ 			}
+ 			break;
  		case T_PlaceHolderVar:
  			return walker(((PlaceHolderVar *) node)->phexpr, context);
  		case T_InferenceElem:
*************** expression_tree_mutator(Node *node,
*** 2999,3004 ****
--- 3010,3026 ----
  				return (Node *) newnode;
  			}
  			break;
+ 		case T_IndexClause:
+ 			{
+ 				IndexClause *iclause = (IndexClause *) node;
+ 				IndexClause *newnode;
+ 
+ 				FLATCOPY(newnode, iclause, IndexClause);
+ 				MUTATE(newnode->rinfo, iclause->rinfo, RestrictInfo *);
+ 				MUTATE(newnode->indexquals, iclause->indexquals, List *);
+ 				return (Node *) newnode;
+ 			}
+ 			break;
  		case T_PlaceHolderVar:
  			{
  				PlaceHolderVar *phv = (PlaceHolderVar *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f97cf37..10038a2 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outIndexPath(StringInfo str, const Inde
*** 1744,1751 ****
  
  	WRITE_NODE_FIELD(indexinfo);
  	WRITE_NODE_FIELD(indexclauses);
- 	WRITE_NODE_FIELD(indexquals);
- 	WRITE_NODE_FIELD(indexqualcols);
  	WRITE_NODE_FIELD(indexorderbys);
  	WRITE_NODE_FIELD(indexorderbycols);
  	WRITE_ENUM_FIELD(indexscandir, ScanDirection);
--- 1744,1749 ----
*************** _outRestrictInfo(StringInfo str, const R
*** 2448,2453 ****
--- 2446,2463 ----
  }
  
  static void
+ _outIndexClause(StringInfo str, const IndexClause *node)
+ {
+ 	WRITE_NODE_TYPE("INDEXCLAUSE");
+ 
+ 	WRITE_NODE_FIELD(rinfo);
+ 	WRITE_NODE_FIELD(indexquals);
+ 	WRITE_BOOL_FIELD(lossy);
+ 	WRITE_INT_FIELD(indexcol);
+ 	WRITE_NODE_FIELD(indexcols);
+ }
+ 
+ static void
  _outPlaceHolderVar(StringInfo str, const PlaceHolderVar *node)
  {
  	WRITE_NODE_TYPE("PLACEHOLDERVAR");
*************** outNode(StringInfo str, const void *obj)
*** 4044,4049 ****
--- 4054,4062 ----
  			case T_RestrictInfo:
  				_outRestrictInfo(str, obj);
  				break;
+ 			case T_IndexClause:
+ 				_outIndexClause(str, obj);
+ 				break;
  			case T_PlaceHolderVar:
  				_outPlaceHolderVar(str, obj);
  				break;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index b8d406f..1057dda 100644
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** typedef struct
*** 145,151 ****
  	QualCost	total;
  } cost_qual_eval_context;
  
! static List *extract_nonindex_conditions(List *qual_clauses, List *indexquals);
  static MergeScanSelCache *cached_scansel(PlannerInfo *root,
  			   RestrictInfo *rinfo,
  			   PathKey *pathkey);
--- 145,151 ----
  	QualCost	total;
  } cost_qual_eval_context;
  
! static List *extract_nonindex_conditions(List *qual_clauses, List *indexclauses);
  static MergeScanSelCache *cached_scansel(PlannerInfo *root,
  			   RestrictInfo *rinfo,
  			   PathKey *pathkey);
*************** cost_index(IndexPath *path, PlannerInfo 
*** 517,534 ****
  	{
  		path->path.rows = path->path.param_info->ppi_rows;
  		/* qpquals come from the rel's restriction clauses and ppi_clauses */
! 		qpquals = list_concat(
! 							  extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
! 														  path->indexquals),
  							  extract_nonindex_conditions(path->path.param_info->ppi_clauses,
! 														  path->indexquals));
  	}
  	else
  	{
  		path->path.rows = baserel->rows;
  		/* qpquals come from just the rel's restriction clauses */
  		qpquals = extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
! 											  path->indexquals);
  	}
  
  	if (!enable_indexscan)
--- 517,533 ----
  	{
  		path->path.rows = path->path.param_info->ppi_rows;
  		/* qpquals come from the rel's restriction clauses and ppi_clauses */
! 		qpquals = list_concat(extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
! 														  path->indexclauses),
  							  extract_nonindex_conditions(path->path.param_info->ppi_clauses,
! 														  path->indexclauses));
  	}
  	else
  	{
  		path->path.rows = baserel->rows;
  		/* qpquals come from just the rel's restriction clauses */
  		qpquals = extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
! 											  path->indexclauses);
  	}
  
  	if (!enable_indexscan)
*************** cost_index(IndexPath *path, PlannerInfo 
*** 753,772 ****
   *
   * Given a list of quals to be enforced in an indexscan, extract the ones that
   * will have to be applied as qpquals (ie, the index machinery won't handle
!  * them).  The actual rules for this appear in create_indexscan_plan() in
!  * createplan.c, but the full rules are fairly expensive and we don't want to
!  * go to that much effort for index paths that don't get selected for the
!  * final plan.  So we approximate it as quals that don't appear directly in
!  * indexquals and also are not redundant children of the same EquivalenceClass
!  * as some indexqual.  This method neglects some infrequently-relevant
!  * considerations, specifically clauses that needn't be checked because they
!  * are implied by an indexqual.  It does not seem worth the cycles to try to
!  * factor that in at this stage, even though createplan.c will take pains to
!  * remove such unnecessary clauses from the qpquals list if this path is
!  * selected for use.
   */
  static List *
! extract_nonindex_conditions(List *qual_clauses, List *indexquals)
  {
  	List	   *result = NIL;
  	ListCell   *lc;
--- 752,770 ----
   *
   * Given a list of quals to be enforced in an indexscan, extract the ones that
   * will have to be applied as qpquals (ie, the index machinery won't handle
!  * them).  Here we detect only whether a qual clause is directly redundant
!  * with some indexclause.  If the index path is chosen for use, createplan.c
!  * will try a bit harder to get rid of redundant qual conditions; specifically
!  * it will see if quals can be proven to be implied by the indexquals.  But
!  * it does not seem worth the cycles to try to factor that in at this stage,
!  * since we're only trying to estimate qual eval costs.  Otherwise this must
!  * match the logic in create_indexscan_plan().
!  *
!  * qual_clauses, and the result, are lists of RestrictInfos.
!  * indexclauses is a list of IndexClauses.
   */
  static List *
! extract_nonindex_conditions(List *qual_clauses, List *indexclauses)
  {
  	List	   *result = NIL;
  	ListCell   *lc;
*************** extract_nonindex_conditions(List *qual_c
*** 777,786 ****
  
  		if (rinfo->pseudoconstant)
  			continue;			/* we may drop pseudoconstants here */
! 		if (list_member_ptr(indexquals, rinfo))
! 			continue;			/* simple duplicate */
! 		if (is_redundant_derived_clause(rinfo, indexquals))
! 			continue;			/* derived from same EquivalenceClass */
  		/* ... skip the predicate proof attempt createplan.c will try ... */
  		result = lappend(result, rinfo);
  	}
--- 775,782 ----
  
  		if (rinfo->pseudoconstant)
  			continue;			/* we may drop pseudoconstants here */
! 		if (is_redundant_with_indexclauses(rinfo, indexclauses))
! 			continue;			/* dup or derived from same EquivalenceClass */
  		/* ... skip the predicate proof attempt createplan.c will try ... */
  		result = lappend(result, rinfo);
  	}
*************** has_indexed_join_quals(NestPath *joinpat
*** 4242,4249 ****
  										innerpath->parent->relids,
  										joinrelids))
  		{
! 			if (!(list_member_ptr(indexclauses, rinfo) ||
! 				  is_redundant_derived_clause(rinfo, indexclauses)))
  				return false;
  			found_one = true;
  		}
--- 4238,4244 ----
  										innerpath->parent->relids,
  										joinrelids))
  		{
! 			if (!is_redundant_with_indexclauses(rinfo, indexclauses))
  				return false;
  			found_one = true;
  		}
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 3454f12..2379250 100644
*** a/src/backend/optimizer/path/equivclass.c
--- b/src/backend/optimizer/path/equivclass.c
*************** is_redundant_derived_clause(RestrictInfo
*** 2511,2513 ****
--- 2511,2550 ----
  
  	return false;
  }
+ 
+ /*
+  * is_redundant_with_indexclauses
+  *		Test whether rinfo is redundant with any clause in the IndexClause
+  *		list.  Here, for convenience, we test both simple identity and
+  *		whether it is derived from the same EC as any member of the list.
+  */
+ bool
+ is_redundant_with_indexclauses(RestrictInfo *rinfo, List *indexclauses)
+ {
+ 	EquivalenceClass *parent_ec = rinfo->parent_ec;
+ 	ListCell   *lc;
+ 
+ 	foreach(lc, indexclauses)
+ 	{
+ 		IndexClause *iclause = lfirst_node(IndexClause, lc);
+ 		RestrictInfo *otherrinfo = iclause->rinfo;
+ 
+ 		/* If indexclause is lossy, it won't enforce the condition exactly */
+ 		if (iclause->lossy)
+ 			continue;
+ 
+ 		/* Match if it's same clause (pointer equality should be enough) */
+ 		if (rinfo == otherrinfo)
+ 			return true;
+ 		/* Match if derived from same EC */
+ 		if (parent_ec && otherrinfo->parent_ec == parent_ec)
+ 			return true;
+ 
+ 		/*
+ 		 * No need to look at the derived clauses in iclause->indexquals; they
+ 		 * couldn't match if the parent clause didn't.
+ 		 */
+ 	}
+ 
+ 	return false;
+ }
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 7e1a390..51d2da5 100644
*** a/src/backend/optimizer/path/indxpath.c
--- b/src/backend/optimizer/path/indxpath.c
*************** typedef enum
*** 56,62 ****
  typedef struct
  {
  	bool		nonempty;		/* True if lists are not all empty */
! 	/* Lists of RestrictInfos, one per index column */
  	List	   *indexclauses[INDEX_MAX_KEYS];
  } IndexClauseSet;
  
--- 56,62 ----
  typedef struct
  {
  	bool		nonempty;		/* True if lists are not all empty */
! 	/* Lists of IndexClause nodes, one list per index column */
  	List	   *indexclauses[INDEX_MAX_KEYS];
  } IndexClauseSet;
  
*************** static bool match_boolean_index_clause(N
*** 175,187 ****
  static bool match_special_index_operator(Expr *clause,
  							 Oid opfamily, Oid idxcollation,
  							 bool indexkey_on_left);
  static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
  							IndexOptInfo *index);
  static List *expand_indexqual_opclause(RestrictInfo *rinfo,
! 						  Oid opfamily, Oid idxcollation);
  static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
  							IndexOptInfo *index,
! 							int indexcol);
  static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation,
  			 Const *prefix, Pattern_Prefix_Status pstatus);
  static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily,
--- 175,193 ----
  static bool match_special_index_operator(Expr *clause,
  							 Oid opfamily, Oid idxcollation,
  							 bool indexkey_on_left);
+ static IndexClause *expand_indexqual_conditions(IndexOptInfo *index,
+ 							int indexcol,
+ 							RestrictInfo *rinfo);
  static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
  							IndexOptInfo *index);
  static List *expand_indexqual_opclause(RestrictInfo *rinfo,
! 						  Oid opfamily, Oid idxcollation,
! 						  bool *lossy);
  static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
  							IndexOptInfo *index,
! 							int indexcol,
! 							List **indexcolnos,
! 							bool *lossy);
  static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation,
  			 Const *prefix, Pattern_Prefix_Status pstatus);
  static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily,
*************** consider_index_join_clauses(PlannerInfo 
*** 496,502 ****
   *
   * 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset', and
   *		'bitindexpaths' as above
!  * 'indexjoinclauses' is a list of RestrictInfos for join clauses
   * 'considered_clauses' is the total number of clauses considered (so far)
   * '*considered_relids' is a list of all relids sets already considered
   */
--- 502,508 ----
   *
   * 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset', and
   *		'bitindexpaths' as above
!  * 'indexjoinclauses' is a list of IndexClauses for join clauses
   * 'considered_clauses' is the total number of clauses considered (so far)
   * '*considered_relids' is a list of all relids sets already considered
   */
*************** consider_index_join_outer_rels(PlannerIn
*** 516,523 ****
  	/* Examine relids of each joinclause in the given list */
  	foreach(lc, indexjoinclauses)
  	{
! 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
! 		Relids		clause_relids = rinfo->clause_relids;
  		ListCell   *lc2;
  
  		/* If we already tried its relids set, no need to do so again */
--- 522,530 ----
  	/* Examine relids of each joinclause in the given list */
  	foreach(lc, indexjoinclauses)
  	{
! 		IndexClause *iclause = (IndexClause *) lfirst(lc);
! 		Relids		clause_relids = iclause->rinfo->clause_relids;
! 		EquivalenceClass *parent_ec = iclause->rinfo->parent_ec;
  		ListCell   *lc2;
  
  		/* If we already tried its relids set, no need to do so again */
*************** consider_index_join_outer_rels(PlannerIn
*** 558,565 ****
  			 * parameterization; so skip if any clause derived from the same
  			 * eclass would already have been included when using oldrelids.
  			 */
! 			if (rinfo->parent_ec &&
! 				eclass_already_used(rinfo->parent_ec, oldrelids,
  									indexjoinclauses))
  				continue;
  
--- 565,572 ----
  			 * parameterization; so skip if any clause derived from the same
  			 * eclass would already have been included when using oldrelids.
  			 */
! 			if (parent_ec &&
! 				eclass_already_used(parent_ec, oldrelids,
  									indexjoinclauses))
  				continue;
  
*************** get_join_index_paths(PlannerInfo *root, 
*** 628,638 ****
  		/* First find applicable simple join clauses */
  		foreach(lc, jclauseset->indexclauses[indexcol])
  		{
! 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
  
! 			if (bms_is_subset(rinfo->clause_relids, relids))
  				clauseset.indexclauses[indexcol] =
! 					lappend(clauseset.indexclauses[indexcol], rinfo);
  		}
  
  		/*
--- 635,645 ----
  		/* First find applicable simple join clauses */
  		foreach(lc, jclauseset->indexclauses[indexcol])
  		{
! 			IndexClause *iclause = (IndexClause *) lfirst(lc);
  
! 			if (bms_is_subset(iclause->rinfo->clause_relids, relids))
  				clauseset.indexclauses[indexcol] =
! 					lappend(clauseset.indexclauses[indexcol], iclause);
  		}
  
  		/*
*************** get_join_index_paths(PlannerInfo *root, 
*** 643,654 ****
  		 */
  		foreach(lc, eclauseset->indexclauses[indexcol])
  		{
! 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
  
! 			if (bms_is_subset(rinfo->clause_relids, relids))
  			{
  				clauseset.indexclauses[indexcol] =
! 					lappend(clauseset.indexclauses[indexcol], rinfo);
  				break;
  			}
  		}
--- 650,661 ----
  		 */
  		foreach(lc, eclauseset->indexclauses[indexcol])
  		{
! 			IndexClause *iclause = (IndexClause *) lfirst(lc);
  
! 			if (bms_is_subset(iclause->rinfo->clause_relids, relids))
  			{
  				clauseset.indexclauses[indexcol] =
! 					lappend(clauseset.indexclauses[indexcol], iclause);
  				break;
  			}
  		}
*************** eclass_already_used(EquivalenceClass *pa
*** 688,694 ****
  
  	foreach(lc, indexjoinclauses)
  	{
! 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
  
  		if (rinfo->parent_ec == parent_ec &&
  			bms_is_subset(rinfo->clause_relids, oldrelids))
--- 695,702 ----
  
  	foreach(lc, indexjoinclauses)
  	{
! 		IndexClause *iclause = (IndexClause *) lfirst(lc);
! 		RestrictInfo *rinfo = iclause->rinfo;
  
  		if (rinfo->parent_ec == parent_ec &&
  			bms_is_subset(rinfo->clause_relids, oldrelids))
*************** get_index_paths(PlannerInfo *root, RelOp
*** 848,854 ****
   *
   * 'rel' is the index's heap relation
   * 'index' is the index for which we want to generate paths
!  * 'clauses' is the collection of indexable clauses (RestrictInfo nodes)
   * 'useful_predicate' indicates whether the index has a useful predicate
   * 'scantype' indicates whether we need plain or bitmap scan support
   * 'skip_nonnative_saop' indicates whether to accept SAOP if index AM doesn't
--- 856,862 ----
   *
   * 'rel' is the index's heap relation
   * 'index' is the index for which we want to generate paths
!  * 'clauses' is the collection of indexable clauses (IndexClause nodes)
   * 'useful_predicate' indicates whether the index has a useful predicate
   * 'scantype' indicates whether we need plain or bitmap scan support
   * 'skip_nonnative_saop' indicates whether to accept SAOP if index AM doesn't
*************** build_index_paths(PlannerInfo *root, Rel
*** 865,871 ****
  	List	   *result = NIL;
  	IndexPath  *ipath;
  	List	   *index_clauses;
- 	List	   *clause_columns;
  	Relids		outer_relids;
  	double		loop_count;
  	List	   *orderbyclauses;
--- 873,878 ----
*************** build_index_paths(PlannerInfo *root, Rel
*** 897,910 ****
  	}
  
  	/*
! 	 * 1. Collect the index clauses into a single list.
  	 *
! 	 * We build a list of RestrictInfo nodes for clauses to be used with this
! 	 * index, along with an integer list of the index column numbers (zero
! 	 * based) that each clause should be used with.  The clauses are ordered
! 	 * by index key, so that the column numbers form a nondecreasing sequence.
! 	 * (This order is depended on by btree and possibly other places.)	The
! 	 * lists can be empty, if the index AM allows that.
  	 *
  	 * found_lower_saop_clause is set true if we accept a ScalarArrayOpExpr
  	 * index clause for a non-first index column.  This prevents us from
--- 904,915 ----
  	}
  
  	/*
! 	 * 1. Combine the per-column IndexClause lists into an overall list.
  	 *
! 	 * In the resulting list, clauses are ordered by index key, so that the
! 	 * column numbers form a nondecreasing sequence.  (This order is depended
! 	 * on by btree and possibly other places.)  The list can be empty, if the
! 	 * index AM allows that.
  	 *
  	 * found_lower_saop_clause is set true if we accept a ScalarArrayOpExpr
  	 * index clause for a non-first index column.  This prevents us from
*************** build_index_paths(PlannerInfo *root, Rel
*** 918,924 ****
  	 * otherwise accounted for.
  	 */
  	index_clauses = NIL;
- 	clause_columns = NIL;
  	found_lower_saop_clause = false;
  	outer_relids = bms_copy(rel->lateral_relids);
  	for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
--- 923,928 ----
*************** build_index_paths(PlannerInfo *root, Rel
*** 927,934 ****
  
  		foreach(lc, clauses->indexclauses[indexcol])
  		{
! 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
  
  			if (IsA(rinfo->clause, ScalarArrayOpExpr))
  			{
  				if (!index->amsearcharray)
--- 931,940 ----
  
  		foreach(lc, clauses->indexclauses[indexcol])
  		{
! 			IndexClause *iclause = (IndexClause *) lfirst(lc);
! 			RestrictInfo *rinfo = iclause->rinfo;
  
+ 			/* We might need to omit ScalarArrayOpExpr clauses */
  			if (IsA(rinfo->clause, ScalarArrayOpExpr))
  			{
  				if (!index->amsearcharray)
*************** build_index_paths(PlannerInfo *root, Rel
*** 953,960 ****
  					found_lower_saop_clause = true;
  				}
  			}
! 			index_clauses = lappend(index_clauses, rinfo);
! 			clause_columns = lappend_int(clause_columns, indexcol);
  			outer_relids = bms_add_members(outer_relids,
  										   rinfo->clause_relids);
  		}
--- 959,967 ----
  					found_lower_saop_clause = true;
  				}
  			}
! 
! 			/* OK to include this clause */
! 			index_clauses = lappend(index_clauses, iclause);
  			outer_relids = bms_add_members(outer_relids,
  										   rinfo->clause_relids);
  		}
*************** build_index_paths(PlannerInfo *root, Rel
*** 1036,1042 ****
  	{
  		ipath = create_index_path(root, index,
  								  index_clauses,
- 								  clause_columns,
  								  orderbyclauses,
  								  orderbyclausecols,
  								  useful_pathkeys,
--- 1043,1048 ----
*************** build_index_paths(PlannerInfo *root, Rel
*** 1059,1065 ****
  		{
  			ipath = create_index_path(root, index,
  									  index_clauses,
- 									  clause_columns,
  									  orderbyclauses,
  									  orderbyclausecols,
  									  useful_pathkeys,
--- 1065,1070 ----
*************** build_index_paths(PlannerInfo *root, Rel
*** 1095,1101 ****
  		{
  			ipath = create_index_path(root, index,
  									  index_clauses,
- 									  clause_columns,
  									  NIL,
  									  NIL,
  									  useful_pathkeys,
--- 1100,1105 ----
*************** build_index_paths(PlannerInfo *root, Rel
*** 1113,1119 ****
  			{
  				ipath = create_index_path(root, index,
  										  index_clauses,
- 										  clause_columns,
  										  NIL,
  										  NIL,
  										  useful_pathkeys,
--- 1117,1122 ----
*************** get_bitmap_tree_required_outer(Path *bit
*** 1810,1816 ****
   * find_indexpath_quals
   *
   * Given the Path structure for a plain or bitmap indexscan, extract lists
!  * of all the indexquals and index predicate conditions used in the Path.
   * These are appended to the initial contents of *quals and *preds (hence
   * caller should initialize those to NIL).
   *
--- 1813,1819 ----
   * find_indexpath_quals
   *
   * Given the Path structure for a plain or bitmap indexscan, extract lists
!  * of all the index clauses and index predicate conditions used in the Path.
   * These are appended to the initial contents of *quals and *preds (hence
   * caller should initialize those to NIL).
   *
*************** find_indexpath_quals(Path *bitmapqual, L
*** 1847,1854 ****
  	else if (IsA(bitmapqual, IndexPath))
  	{
  		IndexPath  *ipath = (IndexPath *) bitmapqual;
  
! 		*quals = list_concat(*quals, get_actual_clauses(ipath->indexclauses));
  		*preds = list_concat(*preds, list_copy(ipath->indexinfo->indpred));
  	}
  	else
--- 1850,1863 ----
  	else if (IsA(bitmapqual, IndexPath))
  	{
  		IndexPath  *ipath = (IndexPath *) bitmapqual;
+ 		ListCell   *l;
  
! 		foreach(l, ipath->indexclauses)
! 		{
! 			IndexClause *iclause = (IndexClause *) lfirst(l);
! 
! 			*quals = lappend(*quals, iclause->rinfo->clause);
! 		}
  		*preds = list_concat(*preds, list_copy(ipath->indexinfo->indpred));
  	}
  	else
*************** match_clauses_to_index(IndexOptInfo *ind
*** 2239,2246 ****
   * match_clause_to_index
   *	  Test whether a qual clause can be used with an index.
   *
!  * If the clause is usable, add it to the appropriate list in *clauseset.
!  * *clauseset must be initialized to zeroes before first call.
   *
   * Note: in some circumstances we may find the same RestrictInfos coming from
   * multiple places.  Defend against redundant outputs by refusing to add a
--- 2248,2256 ----
   * match_clause_to_index
   *	  Test whether a qual clause can be used with an index.
   *
!  * If the clause is usable, add an IndexClause entry for it to the appropriate
!  * list in *clauseset.  (*clauseset must be initialized to zeroes before first
!  * call.)
   *
   * Note: in some circumstances we may find the same RestrictInfos coming from
   * multiple places.  Defend against redundant outputs by refusing to add a
*************** match_clause_to_index(IndexOptInfo *inde
*** 2277,2289 ****
  	/* OK, check each index key column for a match */
  	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
  	{
  		if (match_clause_to_indexcol(index,
  									 indexcol,
  									 rinfo))
  		{
  			clauseset->indexclauses[indexcol] =
! 				list_append_unique_ptr(clauseset->indexclauses[indexcol],
! 									   rinfo);
  			clauseset->nonempty = true;
  			return;
  		}
--- 2287,2316 ----
  	/* OK, check each index key column for a match */
  	for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
  	{
+ 		ListCell   *lc;
+ 
+ 		/* Ignore duplicates */
+ 		foreach(lc, clauseset->indexclauses[indexcol])
+ 		{
+ 			IndexClause *iclause = (IndexClause *) lfirst(lc);
+ 
+ 			if (iclause->rinfo == rinfo)
+ 				return;
+ 		}
+ 
+ 		/*
+ 		 * XXX this should be changed so that we generate an IndexClause
+ 		 * immediately upon matching, to avoid repeated work.  To-do soon.
+ 		 */
  		if (match_clause_to_indexcol(index,
  									 indexcol,
  									 rinfo))
  		{
+ 			IndexClause *iclause;
+ 
+ 			iclause = expand_indexqual_conditions(index, indexcol, rinfo);
  			clauseset->indexclauses[indexcol] =
! 				lappend(clauseset->indexclauses[indexcol], iclause);
  			clauseset->nonempty = true;
  			return;
  		}
*************** match_clause_to_index(IndexOptInfo *inde
*** 2335,2341 ****
   *	  target index column.  This is sufficient to guarantee that some index
   *	  condition can be constructed from the RowCompareExpr --- whether the
   *	  remaining columns match the index too is considered in
!  *	  adjust_rowcompare_for_index().
   *
   *	  It is also possible to match ScalarArrayOpExpr clauses to indexes, when
   *	  the clause is of the form "indexkey op ANY (arrayconst)".
--- 2362,2368 ----
   *	  target index column.  This is sufficient to guarantee that some index
   *	  condition can be constructed from the RowCompareExpr --- whether the
   *	  remaining columns match the index too is considered in
!  *	  expand_indexqual_rowcompare().
   *
   *	  It is also possible to match ScalarArrayOpExpr clauses to indexes, when
   *	  the clause is of the form "indexkey op ANY (arrayconst)".
*************** match_index_to_operand(Node *operand,
*** 3342,3349 ****
   * match_boolean_index_clause() similarly detects clauses that can be
   * converted into boolean equality operators.
   *
!  * expand_indexqual_conditions() converts a list of RestrictInfo nodes
!  * (with implicit AND semantics across list elements) into a list of clauses
   * that the executor can actually handle.  For operators that are members of
   * the index's opfamily this transformation is a no-op, but clauses recognized
   * by match_special_index_operator() or match_boolean_index_clause() must be
--- 3369,3376 ----
   * match_boolean_index_clause() similarly detects clauses that can be
   * converted into boolean equality operators.
   *
!  * expand_indexqual_conditions() converts a RestrictInfo node
!  * into an IndexClause, which contains clauses
   * that the executor can actually handle.  For operators that are members of
   * the index's opfamily this transformation is a no-op, but clauses recognized
   * by match_special_index_operator() or match_boolean_index_clause() must be
*************** match_special_index_operator(Expr *claus
*** 3556,3593 ****
  
  /*
   * expand_indexqual_conditions
!  *	  Given a list of RestrictInfo nodes, produce a list of directly usable
!  *	  index qual clauses.
   *
   * Standard qual clauses (those in the index's opfamily) are passed through
   * unchanged.  Boolean clauses and "special" index operators are expanded
   * into clauses that the indexscan machinery will know what to do with.
   * RowCompare clauses are simplified if necessary to create a clause that is
   * fully checkable by the index.
-  *
-  * In addition to the expressions themselves, there are auxiliary lists
-  * of the index column numbers that the clauses are meant to be used with;
-  * we generate an updated column number list for the result.  (This is not
-  * the identical list because one input clause sometimes produces more than
-  * one output clause.)
-  *
-  * The input clauses are sorted by column number, and so the output is too.
-  * (This is depended on in various places in both planner and executor.)
   */
! void
  expand_indexqual_conditions(IndexOptInfo *index,
! 							List *indexclauses, List *indexclausecols,
! 							List **indexquals_p, List **indexqualcols_p)
  {
  	List	   *indexquals = NIL;
- 	List	   *indexqualcols = NIL;
- 	ListCell   *lcc,
- 			   *lci;
  
! 	forboth(lcc, indexclauses, lci, indexclausecols)
  	{
- 		RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
- 		int			indexcol = lfirst_int(lci);
  		Expr	   *clause = rinfo->clause;
  		Oid			curFamily;
  		Oid			curCollation;
--- 3583,3610 ----
  
  /*
   * expand_indexqual_conditions
!  *	  Given a RestrictInfo node, create an IndexClause.
   *
   * Standard qual clauses (those in the index's opfamily) are passed through
   * unchanged.  Boolean clauses and "special" index operators are expanded
   * into clauses that the indexscan machinery will know what to do with.
   * RowCompare clauses are simplified if necessary to create a clause that is
   * fully checkable by the index.
   */
! static IndexClause *
  expand_indexqual_conditions(IndexOptInfo *index,
! 							int indexcol,
! 							RestrictInfo *rinfo)
  {
+ 	IndexClause *iclause = makeNode(IndexClause);
  	List	   *indexquals = NIL;
  
! 	iclause->rinfo = rinfo;
! 	iclause->lossy = false;		/* might get changed below */
! 	iclause->indexcol = indexcol;
! 	iclause->indexcols = NIL;	/* might get changed below */
! 
  	{
  		Expr	   *clause = rinfo->clause;
  		Oid			curFamily;
  		Oid			curCollation;
*************** expand_indexqual_conditions(IndexOptInfo
*** 3607,3616 ****
  												   index);
  			if (boolqual)
  			{
! 				indexquals = lappend(indexquals,
! 									 make_simple_restrictinfo(boolqual));
! 				indexqualcols = lappend_int(indexqualcols, indexcol);
! 				continue;
  			}
  		}
  
--- 3624,3632 ----
  												   index);
  			if (boolqual)
  			{
! 				iclause->indexquals =
! 					list_make1(make_simple_restrictinfo(boolqual));
! 				return iclause;
  			}
  		}
  
*************** expand_indexqual_conditions(IndexOptInfo
*** 3620,3660 ****
  		 */
  		if (is_opclause(clause))
  		{
! 			indexquals = list_concat(indexquals,
! 									 expand_indexqual_opclause(rinfo,
! 															   curFamily,
! 															   curCollation));
! 			/* expand_indexqual_opclause can produce multiple clauses */
! 			while (list_length(indexqualcols) < list_length(indexquals))
! 				indexqualcols = lappend_int(indexqualcols, indexcol);
  		}
  		else if (IsA(clause, ScalarArrayOpExpr))
  		{
  			/* no extra work at this time */
- 			indexquals = lappend(indexquals, rinfo);
- 			indexqualcols = lappend_int(indexqualcols, indexcol);
  		}
  		else if (IsA(clause, RowCompareExpr))
  		{
! 			indexquals = lappend(indexquals,
! 								 expand_indexqual_rowcompare(rinfo,
! 															 index,
! 															 indexcol));
! 			indexqualcols = lappend_int(indexqualcols, indexcol);
  		}
  		else if (IsA(clause, NullTest))
  		{
  			Assert(index->amsearchnulls);
- 			indexquals = lappend(indexquals, rinfo);
- 			indexqualcols = lappend_int(indexqualcols, indexcol);
  		}
  		else
  			elog(ERROR, "unsupported indexqual type: %d",
  				 (int) nodeTag(clause));
  	}
  
! 	*indexquals_p = indexquals;
! 	*indexqualcols_p = indexqualcols;
  }
  
  /*
--- 3636,3698 ----
  		 */
  		if (is_opclause(clause))
  		{
! 			/*
! 			 * Check to see if the indexkey is on the right; if so, commute
! 			 * the clause.  The indexkey should be the side that refers to
! 			 * (only) the base relation.
! 			 */
! 			if (!bms_equal(rinfo->left_relids, index->rel->relids))
! 			{
! 				Oid			opno = ((OpExpr *) clause)->opno;
! 				RestrictInfo *newrinfo;
! 
! 				newrinfo = commute_restrictinfo(rinfo,
! 												get_commutator(opno));
! 
! 				/*
! 				 * For now, assume it couldn't be any case that requires
! 				 * expansion.  (This is OK for the current capabilities of
! 				 * expand_indexqual_opclause, but we'll need to remove the
! 				 * restriction when we open this up for extensions.)
! 				 */
! 				indexquals = list_make1(newrinfo);
! 			}
! 			else
! 				indexquals = expand_indexqual_opclause(rinfo,
! 													   curFamily,
! 													   curCollation,
! 													   &iclause->lossy);
  		}
  		else if (IsA(clause, ScalarArrayOpExpr))
  		{
  			/* no extra work at this time */
  		}
  		else if (IsA(clause, RowCompareExpr))
  		{
! 			RestrictInfo *newrinfo;
! 
! 			newrinfo = expand_indexqual_rowcompare(rinfo,
! 												   index,
! 												   indexcol,
! 												   &iclause->indexcols,
! 												   &iclause->lossy);
! 			if (newrinfo != rinfo)
! 			{
! 				/* We need to report a derived expression */
! 				indexquals = list_make1(newrinfo);
! 			}
  		}
  		else if (IsA(clause, NullTest))
  		{
  			Assert(index->amsearchnulls);
  		}
  		else
  			elog(ERROR, "unsupported indexqual type: %d",
  				 (int) nodeTag(clause));
  	}
  
! 	iclause->indexquals = indexquals;
! 	return iclause;
  }
  
  /*
*************** expand_boolean_index_clause(Node *clause
*** 3725,3737 ****
   * expand_indexqual_opclause --- expand a single indexqual condition
   *		that is an operator clause
   *
!  * The input is a single RestrictInfo, the output a list of RestrictInfos.
   *
!  * In the base case this is just list_make1(), but we have to be prepared to
   * expand special cases that were accepted by match_special_index_operator().
   */
  static List *
! expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
  {
  	Expr	   *clause = rinfo->clause;
  
--- 3763,3777 ----
   * expand_indexqual_opclause --- expand a single indexqual condition
   *		that is an operator clause
   *
!  * The input is a single RestrictInfo, the output a list of RestrictInfos,
!  * or NIL if the RestrictInfo's clause can be used as-is.
   *
!  * In the base case this is just "return NIL", but we have to be prepared to
   * expand special cases that were accepted by match_special_index_operator().
   */
  static List *
! expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation,
! 						  bool *lossy)
  {
  	Expr	   *clause = rinfo->clause;
  
*************** expand_indexqual_opclause(RestrictInfo *
*** 3760,3765 ****
--- 3800,3806 ----
  		case OID_BYTEA_LIKE_OP:
  			if (!op_in_opfamily(expr_op, opfamily))
  			{
+ 				*lossy = true;
  				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, expr_coll,
  											   &prefix, NULL);
  				return prefix_quals(leftop, opfamily, idxcollation, prefix, pstatus);
*************** expand_indexqual_opclause(RestrictInfo *
*** 3771,3776 ****
--- 3812,3818 ----
  		case OID_NAME_ICLIKE_OP:
  			if (!op_in_opfamily(expr_op, opfamily))
  			{
+ 				*lossy = true;
  				/* the right-hand const is type text for all of these */
  				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, expr_coll,
  											   &prefix, NULL);
*************** expand_indexqual_opclause(RestrictInfo *
*** 3783,3788 ****
--- 3825,3831 ----
  		case OID_NAME_REGEXEQ_OP:
  			if (!op_in_opfamily(expr_op, opfamily))
  			{
+ 				*lossy = true;
  				/* the right-hand const is type text for all of these */
  				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, expr_coll,
  											   &prefix, NULL);
*************** expand_indexqual_opclause(RestrictInfo *
*** 3795,3800 ****
--- 3838,3844 ----
  		case OID_NAME_ICREGEXEQ_OP:
  			if (!op_in_opfamily(expr_op, opfamily))
  			{
+ 				*lossy = true;
  				/* the right-hand const is type text for all of these */
  				pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, expr_coll,
  											   &prefix, NULL);
*************** expand_indexqual_opclause(RestrictInfo *
*** 3806,3901 ****
  		case OID_INET_SUBEQ_OP:
  			if (!op_in_opfamily(expr_op, opfamily))
  			{
  				return network_prefix_quals(leftop, expr_op, opfamily,
  											patt->constvalue);
  			}
  			break;
  	}
  
! 	/* Default case: just make a list of the unmodified indexqual */
! 	return list_make1(rinfo);
  }
  
  /*
   * expand_indexqual_rowcompare --- expand a single indexqual condition
   *		that is a RowCompareExpr
   *
-  * This is a thin wrapper around adjust_rowcompare_for_index; we export the
-  * latter so that createplan.c can use it to re-discover which columns of the
-  * index are used by a row comparison indexqual.
-  */
- static RestrictInfo *
- expand_indexqual_rowcompare(RestrictInfo *rinfo,
- 							IndexOptInfo *index,
- 							int indexcol)
- {
- 	RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
- 	Expr	   *newclause;
- 	List	   *indexcolnos;
- 	bool		var_on_left;
- 
- 	newclause = adjust_rowcompare_for_index(clause,
- 											index,
- 											indexcol,
- 											&indexcolnos,
- 											&var_on_left);
- 
- 	/*
- 	 * If we didn't have to change the RowCompareExpr, return the original
- 	 * RestrictInfo.
- 	 */
- 	if (newclause == (Expr *) clause)
- 		return rinfo;
- 
- 	/* Else we need a new RestrictInfo */
- 	return make_simple_restrictinfo(newclause);
- }
- 
- /*
-  * adjust_rowcompare_for_index --- expand a single indexqual condition
-  *		that is a RowCompareExpr
-  *
   * It's already known that the first column of the row comparison matches
   * the specified column of the index.  We can use additional columns of the
   * row comparison as index qualifications, so long as they match the index
   * in the "same direction", ie, the indexkeys are all on the same side of the
   * clause and the operators are all the same-type members of the opfamilies.
   * If all the columns of the RowCompareExpr match in this way, we just use it
!  * as-is.  Otherwise, we build a shortened RowCompareExpr (if more than one
   * column matches) or a simple OpExpr (if the first-column match is all
   * there is).  In these cases the modified clause is always "<=" or ">="
   * even when the original was "<" or ">" --- this is necessary to match all
!  * the rows that could match the original.  (We are essentially building a
!  * lossy version of the row comparison when we do this.)
   *
   * *indexcolnos receives an integer list of the index column numbers (zero
!  * based) used in the resulting expression.  The reason we need to return
!  * that is that if the index is selected for use, createplan.c will need to
!  * call this again to extract that list.  (This is a bit grotty, but row
!  * comparison indexquals aren't used enough to justify finding someplace to
!  * keep the information in the Path representation.)  Since createplan.c
!  * also needs to know which side of the RowCompareExpr is the index side,
!  * we also return *var_on_left_p rather than re-deducing that there.
   */
! Expr *
! adjust_rowcompare_for_index(RowCompareExpr *clause,
  							IndexOptInfo *index,
  							int indexcol,
  							List **indexcolnos,
! 							bool *var_on_left_p)
  {
  	bool		var_on_left;
  	int			op_strategy;
  	Oid			op_lefttype;
  	Oid			op_righttype;
  	int			matching_cols;
  	Oid			expr_op;
  	List	   *opfamilies;
  	List	   *lefttypes;
  	List	   *righttypes;
  	List	   *new_ops;
! 	ListCell   *largs_cell;
! 	ListCell   *rargs_cell;
  	ListCell   *opnos_cell;
  	ListCell   *collids_cell;
  
--- 3850,3914 ----
  		case OID_INET_SUBEQ_OP:
  			if (!op_in_opfamily(expr_op, opfamily))
  			{
+ 				*lossy = true;
  				return network_prefix_quals(leftop, expr_op, opfamily,
  											patt->constvalue);
  			}
  			break;
  	}
  
! 	/* Default case: the clause can be used as-is. */
! 	*lossy = false;
! 	return NIL;
  }
  
  /*
   * expand_indexqual_rowcompare --- expand a single indexqual condition
   *		that is a RowCompareExpr
   *
   * It's already known that the first column of the row comparison matches
   * the specified column of the index.  We can use additional columns of the
   * row comparison as index qualifications, so long as they match the index
   * in the "same direction", ie, the indexkeys are all on the same side of the
   * clause and the operators are all the same-type members of the opfamilies.
+  *
   * If all the columns of the RowCompareExpr match in this way, we just use it
!  * as-is, except for possibly commuting it to put the indexkeys on the left.
!  *
!  * Otherwise, we build a shortened RowCompareExpr (if more than one
   * column matches) or a simple OpExpr (if the first-column match is all
   * there is).  In these cases the modified clause is always "<=" or ">="
   * even when the original was "<" or ">" --- this is necessary to match all
!  * the rows that could match the original.  (We are building a lossy version
!  * of the row comparison when we do this, so we set *lossy = true.)
   *
   * *indexcolnos receives an integer list of the index column numbers (zero
!  * based) used in the resulting expression.  We have to pass that back
!  * because createplan.c will need it.
   */
! static RestrictInfo *
! expand_indexqual_rowcompare(RestrictInfo *rinfo,
  							IndexOptInfo *index,
  							int indexcol,
  							List **indexcolnos,
! 							bool *lossy)
  {
+ 	RowCompareExpr *clause = castNode(RowCompareExpr, rinfo->clause);
  	bool		var_on_left;
  	int			op_strategy;
  	Oid			op_lefttype;
  	Oid			op_righttype;
  	int			matching_cols;
  	Oid			expr_op;
+ 	List	   *expr_ops;
  	List	   *opfamilies;
  	List	   *lefttypes;
  	List	   *righttypes;
  	List	   *new_ops;
! 	List	   *var_args;
! 	List	   *non_var_args;
! 	ListCell   *vargs_cell;
! 	ListCell   *nargs_cell;
  	ListCell   *opnos_cell;
  	ListCell   *collids_cell;
  
*************** adjust_rowcompare_for_index(RowCompareEx
*** 3905,3911 ****
  	Assert(var_on_left ||
  		   match_index_to_operand((Node *) linitial(clause->rargs),
  								  indexcol, index));
! 	*var_on_left_p = var_on_left;
  
  	expr_op = linitial_oid(clause->opnos);
  	if (!var_on_left)
--- 3918,3934 ----
  	Assert(var_on_left ||
  		   match_index_to_operand((Node *) linitial(clause->rargs),
  								  indexcol, index));
! 
! 	if (var_on_left)
! 	{
! 		var_args = clause->largs;
! 		non_var_args = clause->rargs;
! 	}
! 	else
! 	{
! 		var_args = clause->rargs;
! 		non_var_args = clause->largs;
! 	}
  
  	expr_op = linitial_oid(clause->opnos);
  	if (!var_on_left)
*************** adjust_rowcompare_for_index(RowCompareEx
*** 3918,3924 ****
  	/* Initialize returned list of which index columns are used */
  	*indexcolnos = list_make1_int(indexcol);
  
! 	/* Build lists of the opfamilies and operator datatypes in case needed */
  	opfamilies = list_make1_oid(index->opfamily[indexcol]);
  	lefttypes = list_make1_oid(op_lefttype);
  	righttypes = list_make1_oid(op_righttype);
--- 3941,3948 ----
  	/* Initialize returned list of which index columns are used */
  	*indexcolnos = list_make1_int(indexcol);
  
! 	/* Build lists of ops, opfamilies and operator datatypes in case needed */
! 	expr_ops = list_make1_oid(expr_op);
  	opfamilies = list_make1_oid(index->opfamily[indexcol]);
  	lefttypes = list_make1_oid(op_lefttype);
  	righttypes = list_make1_oid(op_righttype);
*************** adjust_rowcompare_for_index(RowCompareEx
*** 3930,3956 ****
  	 * indexed relation.
  	 */
  	matching_cols = 1;
! 	largs_cell = lnext(list_head(clause->largs));
! 	rargs_cell = lnext(list_head(clause->rargs));
  	opnos_cell = lnext(list_head(clause->opnos));
  	collids_cell = lnext(list_head(clause->inputcollids));
  
! 	while (largs_cell != NULL)
  	{
! 		Node	   *varop;
! 		Node	   *constop;
  		int			i;
  
  		expr_op = lfirst_oid(opnos_cell);
! 		if (var_on_left)
! 		{
! 			varop = (Node *) lfirst(largs_cell);
! 			constop = (Node *) lfirst(rargs_cell);
! 		}
! 		else
  		{
- 			varop = (Node *) lfirst(rargs_cell);
- 			constop = (Node *) lfirst(largs_cell);
  			/* indexkey is on right, so commute the operator */
  			expr_op = get_commutator(expr_op);
  			if (expr_op == InvalidOid)
--- 3954,3973 ----
  	 * indexed relation.
  	 */
  	matching_cols = 1;
! 	vargs_cell = lnext(list_head(var_args));
! 	nargs_cell = lnext(list_head(non_var_args));
  	opnos_cell = lnext(list_head(clause->opnos));
  	collids_cell = lnext(list_head(clause->inputcollids));
  
! 	while (vargs_cell != NULL)
  	{
! 		Node	   *varop = (Node *) lfirst(vargs_cell);
! 		Node	   *constop = (Node *) lfirst(nargs_cell);
  		int			i;
  
  		expr_op = lfirst_oid(opnos_cell);
! 		if (!var_on_left)
  		{
  			/* indexkey is on right, so commute the operator */
  			expr_op = get_commutator(expr_op);
  			if (expr_op == InvalidOid)
*************** adjust_rowcompare_for_index(RowCompareEx
*** 3980,4016 ****
  		/* Add column number to returned list */
  		*indexcolnos = lappend_int(*indexcolnos, i);
  
! 		/* Add opfamily and datatypes to lists */
  		get_op_opfamily_properties(expr_op, index->opfamily[i], false,
  								   &op_strategy,
  								   &op_lefttype,
  								   &op_righttype);
  		opfamilies = lappend_oid(opfamilies, index->opfamily[i]);
  		lefttypes = lappend_oid(lefttypes, op_lefttype);
  		righttypes = lappend_oid(righttypes, op_righttype);
  
  		/* This column matches, keep scanning */
  		matching_cols++;
! 		largs_cell = lnext(largs_cell);
! 		rargs_cell = lnext(rargs_cell);
  		opnos_cell = lnext(opnos_cell);
  		collids_cell = lnext(collids_cell);
  	}
  
! 	/* Return clause as-is if it's all usable as index quals */
! 	if (matching_cols == list_length(clause->opnos))
! 		return (Expr *) clause;
  
  	/*
! 	 * We have to generate a subset rowcompare (possibly just one OpExpr). The
! 	 * painful part of this is changing < to <= or > to >=, so deal with that
! 	 * first.
  	 */
! 	if (op_strategy == BTLessEqualStrategyNumber ||
! 		op_strategy == BTGreaterEqualStrategyNumber)
  	{
! 		/* easy, just use the same operators */
! 		new_ops = list_truncate(list_copy(clause->opnos), matching_cols);
  	}
  	else
  	{
--- 3997,4045 ----
  		/* Add column number to returned list */
  		*indexcolnos = lappend_int(*indexcolnos, i);
  
! 		/* Add operator info to lists */
  		get_op_opfamily_properties(expr_op, index->opfamily[i], false,
  								   &op_strategy,
  								   &op_lefttype,
  								   &op_righttype);
+ 		expr_ops = lappend_oid(expr_ops, expr_op);
  		opfamilies = lappend_oid(opfamilies, index->opfamily[i]);
  		lefttypes = lappend_oid(lefttypes, op_lefttype);
  		righttypes = lappend_oid(righttypes, op_righttype);
  
  		/* This column matches, keep scanning */
  		matching_cols++;
! 		vargs_cell = lnext(vargs_cell);
! 		nargs_cell = lnext(nargs_cell);
  		opnos_cell = lnext(opnos_cell);
  		collids_cell = lnext(collids_cell);
  	}
  
! 	/* Result is non-lossy if all columns are usable as index quals */
! 	*lossy = (matching_cols != list_length(clause->opnos));
  
  	/*
! 	 * Return clause as-is if we have var on left and it's all usable as index
! 	 * quals
  	 */
! 	if (var_on_left && !*lossy)
! 		return rinfo;
! 
! 	/*
! 	 * We have to generate a modified rowcompare (possibly just one OpExpr).
! 	 * The painful part of this is changing < to <= or > to >=, so deal with
! 	 * that first.
! 	 */
! 	if (!*lossy)
  	{
! 		/* very easy, just use the commuted operators */
! 		new_ops = expr_ops;
! 	}
! 	else if (op_strategy == BTLessEqualStrategyNumber ||
! 			 op_strategy == BTGreaterEqualStrategyNumber)
! 	{
! 		/* easy, just use the same (possibly commuted) operators */
! 		new_ops = list_truncate(expr_ops, matching_cols);
  	}
  	else
  	{
*************** adjust_rowcompare_for_index(RowCompareEx
*** 4025,4033 ****
  		else
  			elog(ERROR, "unexpected strategy number %d", op_strategy);
  		new_ops = NIL;
! 		lefttypes_cell = list_head(lefttypes);
! 		righttypes_cell = list_head(righttypes);
! 		foreach(opfamilies_cell, opfamilies)
  		{
  			Oid			opfam = lfirst_oid(opfamilies_cell);
  			Oid			lefttype = lfirst_oid(lefttypes_cell);
--- 4054,4062 ----
  		else
  			elog(ERROR, "unexpected strategy number %d", op_strategy);
  		new_ops = NIL;
! 		forthree(opfamilies_cell, opfamilies,
! 				 lefttypes_cell, lefttypes,
! 				 righttypes_cell, righttypes)
  		{
  			Oid			opfam = lfirst_oid(opfamilies_cell);
  			Oid			lefttype = lfirst_oid(lefttypes_cell);
*************** adjust_rowcompare_for_index(RowCompareEx
*** 4038,4053 ****
  			if (!OidIsValid(expr_op))	/* should not happen */
  				elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
  					 op_strategy, lefttype, righttype, opfam);
- 			if (!var_on_left)
- 			{
- 				expr_op = get_commutator(expr_op);
- 				if (!OidIsValid(expr_op))	/* should not happen */
- 					elog(ERROR, "could not find commutator of operator %d(%u,%u) of opfamily %u",
- 						 op_strategy, lefttype, righttype, opfam);
- 			}
  			new_ops = lappend_oid(new_ops, expr_op);
- 			lefttypes_cell = lnext(lefttypes_cell);
- 			righttypes_cell = lnext(righttypes_cell);
  		}
  	}
  
--- 4067,4073 ----
*************** adjust_rowcompare_for_index(RowCompareEx
*** 4056,4084 ****
  	{
  		RowCompareExpr *rc = makeNode(RowCompareExpr);
  
! 		if (var_on_left)
! 			rc->rctype = (RowCompareType) op_strategy;
! 		else
! 			rc->rctype = (op_strategy == BTLessEqualStrategyNumber) ?
! 				ROWCOMPARE_GE : ROWCOMPARE_LE;
  		rc->opnos = new_ops;
  		rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
  									   matching_cols);
  		rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
  										 matching_cols);
! 		rc->largs = list_truncate(copyObject(clause->largs),
  								  matching_cols);
! 		rc->rargs = list_truncate(copyObject(clause->rargs),
  								  matching_cols);
! 		return (Expr *) rc;
  	}
  	else
  	{
! 		return make_opclause(linitial_oid(new_ops), BOOLOID, false,
! 							 copyObject(linitial(clause->largs)),
! 							 copyObject(linitial(clause->rargs)),
! 							 InvalidOid,
! 							 linitial_oid(clause->inputcollids));
  	}
  }
  
--- 4076,4106 ----
  	{
  		RowCompareExpr *rc = makeNode(RowCompareExpr);
  
! 		rc->rctype = (RowCompareType) op_strategy;
  		rc->opnos = new_ops;
  		rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
  									   matching_cols);
  		rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
  										 matching_cols);
! 		rc->largs = list_truncate(copyObject(var_args),
  								  matching_cols);
! 		rc->rargs = list_truncate(copyObject(non_var_args),
  								  matching_cols);
! 		return make_simple_restrictinfo((Expr *) rc);
  	}
  	else
  	{
! 		Expr	   *op;
! 
! 		/* We don't report an index column list in this case */
! 		*indexcolnos = NIL;
! 
! 		op = make_opclause(linitial_oid(new_ops), BOOLOID, false,
! 						   copyObject(linitial(var_args)),
! 						   copyObject(linitial(non_var_args)),
! 						   InvalidOid,
! 						   linitial_oid(clause->inputcollids));
! 		return make_simple_restrictinfo(op);
  	}
  }
  
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 1b4f7db..c7645ac 100644
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
*************** static MergeJoin *create_mergejoin_plan(
*** 152,159 ****
  static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
  static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
  static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
! static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
  static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
  static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
  static List *get_switched_clauses(List *clauses, Relids outerrelids);
  static List *order_qual_clauses(PlannerInfo *root, List *clauses);
--- 152,164 ----
  static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
  static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
  static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
! static void fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
! 						 List **stripped_indexquals_p,
! 						 List **fixed_indexquals_p);
  static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
+ static Node *fix_indexqual_clause(PlannerInfo *root,
+ 					 IndexOptInfo *index, int indexcol,
+ 					 Node *clause, List *indexcolnos);
  static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
  static List *get_switched_clauses(List *clauses, Relids outerrelids);
  static List *order_qual_clauses(PlannerInfo *root, List *clauses);
*************** create_indexscan_plan(PlannerInfo *root,
*** 2607,2613 ****
  					  bool indexonly)
  {
  	Scan	   *scan_plan;
! 	List	   *indexquals = best_path->indexquals;
  	List	   *indexorderbys = best_path->indexorderbys;
  	Index		baserelid = best_path->path.parent->relid;
  	Oid			indexoid = best_path->indexinfo->indexoid;
--- 2612,2618 ----
  					  bool indexonly)
  {
  	Scan	   *scan_plan;
! 	List	   *indexclauses = best_path->indexclauses;
  	List	   *indexorderbys = best_path->indexorderbys;
  	Index		baserelid = best_path->path.parent->relid;
  	Oid			indexoid = best_path->indexinfo->indexoid;
*************** create_indexscan_plan(PlannerInfo *root,
*** 2623,2638 ****
  	Assert(best_path->path.parent->rtekind == RTE_RELATION);
  
  	/*
! 	 * Build "stripped" indexquals structure (no RestrictInfos) to pass to
! 	 * executor as indexqualorig
! 	 */
! 	stripped_indexquals = get_actual_clauses(indexquals);
! 
! 	/*
! 	 * The executor needs a copy with the indexkey on the left of each clause
! 	 * and with index Vars substituted for table ones.
  	 */
! 	fixed_indexquals = fix_indexqual_references(root, best_path);
  
  	/*
  	 * Likewise fix up index attr references in the ORDER BY expressions.
--- 2628,2641 ----
  	Assert(best_path->path.parent->rtekind == RTE_RELATION);
  
  	/*
! 	 * Extract the index qual expressions (stripped of RestrictInfos) from the
! 	 * IndexClauses list, and prepare a copy with index Vars substituted for
! 	 * table Vars.  (This step also does replace_nestloop_params on the
! 	 * fixed_indexquals.)
  	 */
! 	fix_indexqual_references(root, best_path,
! 							 &stripped_indexquals,
! 							 &fixed_indexquals);
  
  	/*
  	 * Likewise fix up index attr references in the ORDER BY expressions.
*************** create_indexscan_plan(PlannerInfo *root,
*** 2648,2661 ****
  	 * included in qpqual.  The upshot is that qpqual must contain
  	 * scan_clauses minus whatever appears in indexquals.
  	 *
! 	 * In normal cases simple pointer equality checks will be enough to spot
! 	 * duplicate RestrictInfos, so we try that first.
! 	 *
! 	 * Another common case is that a scan_clauses entry is generated from the
! 	 * same EquivalenceClass as some indexqual, and is therefore redundant
! 	 * with it, though not equal.  (This happens when indxpath.c prefers a
  	 * different derived equality than what generate_join_implied_equalities
! 	 * picked for a parameterized scan's ppi_clauses.)
  	 *
  	 * In some situations (particularly with OR'd index conditions) we may
  	 * have scan_clauses that are not equal to, but are logically implied by,
--- 2651,2664 ----
  	 * included in qpqual.  The upshot is that qpqual must contain
  	 * scan_clauses minus whatever appears in indexquals.
  	 *
! 	 * is_redundant_with_indexclauses() detects cases where a scan clause is
! 	 * present in the indexclauses list or is generated from the same
! 	 * EquivalenceClass as some indexclause, and is therefore redundant with
! 	 * it, though not equal.  (The latter happens when indxpath.c prefers a
  	 * different derived equality than what generate_join_implied_equalities
! 	 * picked for a parameterized scan's ppi_clauses.)  Note that it will not
! 	 * match to lossy index clauses, which is critical because we have to
! 	 * include the original clause in qpqual in that case.
  	 *
  	 * In some situations (particularly with OR'd index conditions) we may
  	 * have scan_clauses that are not equal to, but are logically implied by,
*************** create_indexscan_plan(PlannerInfo *root,
*** 2674,2685 ****
  
  		if (rinfo->pseudoconstant)
  			continue;			/* we may drop pseudoconstants here */
! 		if (list_member_ptr(indexquals, rinfo))
! 			continue;			/* simple duplicate */
! 		if (is_redundant_derived_clause(rinfo, indexquals))
! 			continue;			/* derived from same EquivalenceClass */
  		if (!contain_mutable_functions((Node *) rinfo->clause) &&
! 			predicate_implied_by(list_make1(rinfo->clause), indexquals, false))
  			continue;			/* provably implied by indexquals */
  		qpqual = lappend(qpqual, rinfo);
  	}
--- 2677,2687 ----
  
  		if (rinfo->pseudoconstant)
  			continue;			/* we may drop pseudoconstants here */
! 		if (is_redundant_with_indexclauses(rinfo, indexclauses))
! 			continue;			/* dup or derived from same EquivalenceClass */
  		if (!contain_mutable_functions((Node *) rinfo->clause) &&
! 			predicate_implied_by(list_make1(rinfo->clause), stripped_indexquals,
! 								 false))
  			continue;			/* provably implied by indexquals */
  		qpqual = lappend(qpqual, rinfo);
  	}
*************** create_bitmap_subplan(PlannerInfo *root,
*** 3040,3045 ****
--- 3042,3049 ----
  	{
  		IndexPath  *ipath = (IndexPath *) bitmapqual;
  		IndexScan  *iscan;
+ 		List	   *subquals;
+ 		List	   *subindexquals;
  		List	   *subindexECs;
  		ListCell   *l;
  
*************** create_bitmap_subplan(PlannerInfo *root,
*** 3060,3067 ****
  		plan->plan_width = 0;	/* meaningless */
  		plan->parallel_aware = false;
  		plan->parallel_safe = ipath->path.parallel_safe;
! 		*qual = get_actual_clauses(ipath->indexclauses);
! 		*indexqual = get_actual_clauses(ipath->indexquals);
  		foreach(l, ipath->indexinfo->indpred)
  		{
  			Expr	   *pred = (Expr *) lfirst(l);
--- 3064,3089 ----
  		plan->plan_width = 0;	/* meaningless */
  		plan->parallel_aware = false;
  		plan->parallel_safe = ipath->path.parallel_safe;
! 		/* Extract original index clauses, actual index quals, relevant ECs */
! 		subquals = NIL;
! 		subindexquals = NIL;
! 		subindexECs = NIL;
! 		foreach(l, ipath->indexclauses)
! 		{
! 			IndexClause *iclause = (IndexClause *) lfirst(l);
! 			RestrictInfo *rinfo = iclause->rinfo;
! 
! 			Assert(!rinfo->pseudoconstant);
! 			subquals = lappend(subquals, rinfo->clause);
! 			if (iclause->indexquals)
! 				subindexquals = list_concat(subindexquals,
! 											get_actual_clauses(iclause->indexquals));
! 			else
! 				subindexquals = lappend(subindexquals, rinfo->clause);
! 			if (rinfo->parent_ec)
! 				subindexECs = lappend(subindexECs, rinfo->parent_ec);
! 		}
! 		/* We can add any index predicate conditions, too */
  		foreach(l, ipath->indexinfo->indpred)
  		{
  			Expr	   *pred = (Expr *) lfirst(l);
*************** create_bitmap_subplan(PlannerInfo *root,
*** 3072,3092 ****
  			 * the conditions that got pushed into the bitmapqual.  Avoid
  			 * generating redundant conditions.
  			 */
! 			if (!predicate_implied_by(list_make1(pred), ipath->indexclauses,
! 									  false))
  			{
! 				*qual = lappend(*qual, pred);
! 				*indexqual = lappend(*indexqual, pred);
  			}
  		}
! 		subindexECs = NIL;
! 		foreach(l, ipath->indexquals)
! 		{
! 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
! 
! 			if (rinfo->parent_ec)
! 				subindexECs = lappend(subindexECs, rinfo->parent_ec);
! 		}
  		*indexECs = subindexECs;
  	}
  	else
--- 3094,3107 ----
  			 * the conditions that got pushed into the bitmapqual.  Avoid
  			 * generating redundant conditions.
  			 */
! 			if (!predicate_implied_by(list_make1(pred), subquals, false))
  			{
! 				subquals = lappend(subquals, pred);
! 				subindexquals = lappend(subindexquals, pred);
  			}
  		}
! 		*qual = subquals;
! 		*indexqual = subindexquals;
  		*indexECs = subindexECs;
  	}
  	else
*************** replace_nestloop_params_mutator(Node *no
*** 4446,4583 ****
   *	  Adjust indexqual clauses to the form the executor's indexqual
   *	  machinery needs.
   *
!  * We have four tasks here:
!  *	* Remove RestrictInfo nodes from the input clauses.
   *	* Replace any outer-relation Var or PHV nodes with nestloop Params.
   *	  (XXX eventually, that responsibility should go elsewhere?)
   *	* Index keys must be represented by Var nodes with varattno set to the
   *	  index's attribute number, not the attribute number in the original rel.
-  *	* If the index key is on the right, commute the clause to put it on the
-  *	  left.
   *
!  * The result is a modified copy of the path's indexquals list --- the
!  * original is not changed.  Note also that the copy shares no substructure
!  * with the original; this is needed in case there is a subplan in it (we need
!  * two separate copies of the subplan tree, or things will go awry).
   */
! static List *
! fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
  {
  	IndexOptInfo *index = index_path->indexinfo;
  	List	   *fixed_indexquals;
! 	ListCell   *lcc,
! 			   *lci;
  
! 	fixed_indexquals = NIL;
  
! 	forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols)
  	{
! 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc);
! 		int			indexcol = lfirst_int(lci);
! 		Node	   *clause;
! 
! 		/*
! 		 * Replace any outer-relation variables with nestloop params.
! 		 *
! 		 * This also makes a copy of the clause, so it's safe to modify it
! 		 * in-place below.
! 		 */
! 		clause = replace_nestloop_params(root, (Node *) rinfo->clause);
  
! 		if (IsA(clause, OpExpr))
  		{
! 			OpExpr	   *op = (OpExpr *) clause;
! 
! 			if (list_length(op->args) != 2)
! 				elog(ERROR, "indexqual clause is not binary opclause");
! 
! 			/*
! 			 * Check to see if the indexkey is on the right; if so, commute
! 			 * the clause.  The indexkey should be the side that refers to
! 			 * (only) the base relation.
! 			 */
! 			if (!bms_equal(rinfo->left_relids, index->rel->relids))
! 				CommuteOpExpr(op);
  
! 			/*
! 			 * Now replace the indexkey expression with an index Var.
! 			 */
! 			linitial(op->args) = fix_indexqual_operand(linitial(op->args),
! 													   index,
! 													   indexcol);
  		}
! 		else if (IsA(clause, RowCompareExpr))
  		{
! 			RowCompareExpr *rc = (RowCompareExpr *) clause;
! 			Expr	   *newrc;
! 			List	   *indexcolnos;
! 			bool		var_on_left;
! 			ListCell   *lca,
! 					   *lcai;
! 
! 			/*
! 			 * Re-discover which index columns are used in the rowcompare.
! 			 */
! 			newrc = adjust_rowcompare_for_index(rc,
! 												index,
! 												indexcol,
! 												&indexcolnos,
! 												&var_on_left);
! 
! 			/*
! 			 * Trouble if adjust_rowcompare_for_index thought the
! 			 * RowCompareExpr didn't match the index as-is; the clause should
! 			 * have gone through that routine already.
! 			 */
! 			if (newrc != (Expr *) rc)
! 				elog(ERROR, "inconsistent results from adjust_rowcompare_for_index");
! 
! 			/*
! 			 * Check to see if the indexkey is on the right; if so, commute
! 			 * the clause.
! 			 */
! 			if (!var_on_left)
! 				CommuteRowCompareExpr(rc);
  
! 			/*
! 			 * Now replace the indexkey expressions with index Vars.
! 			 */
! 			Assert(list_length(rc->largs) == list_length(indexcolnos));
! 			forboth(lca, rc->largs, lcai, indexcolnos)
  			{
! 				lfirst(lca) = fix_indexqual_operand(lfirst(lca),
! 													index,
! 													lfirst_int(lcai));
! 			}
! 		}
! 		else if (IsA(clause, ScalarArrayOpExpr))
! 		{
! 			ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
! 
! 			/* Never need to commute... */
! 
! 			/* Replace the indexkey expression with an index Var. */
! 			linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
! 														 index,
! 														 indexcol);
! 		}
! 		else if (IsA(clause, NullTest))
! 		{
! 			NullTest   *nt = (NullTest *) clause;
  
! 			/* Replace the indexkey expression with an index Var. */
! 			nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
! 													 index,
! 													 indexcol);
  		}
- 		else
- 			elog(ERROR, "unsupported indexqual type: %d",
- 				 (int) nodeTag(clause));
- 
- 		fixed_indexquals = lappend(fixed_indexquals, clause);
  	}
  
! 	return fixed_indexquals;
  }
  
  /*
--- 4461,4527 ----
   *	  Adjust indexqual clauses to the form the executor's indexqual
   *	  machinery needs.
   *
!  * We have three tasks here:
!  *	* Select the actual qual clauses out of the input IndexClause list,
!  *	  and remove RestrictInfo nodes from the qual clauses.
   *	* Replace any outer-relation Var or PHV nodes with nestloop Params.
   *	  (XXX eventually, that responsibility should go elsewhere?)
   *	* Index keys must be represented by Var nodes with varattno set to the
   *	  index's attribute number, not the attribute number in the original rel.
   *
!  * *stripped_indexquals_p receives a list of the actual qual clauses.
!  *
!  * *fixed_indexquals_p receives a list of the adjusted quals.  This is a copy
!  * that shares no substructure with the original; this is needed in case there
!  * are subplans in it (we need two separate copies of the subplan tree, or
!  * things will go awry).
   */
! static void
! fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
! 						 List **stripped_indexquals_p, List **fixed_indexquals_p)
  {
  	IndexOptInfo *index = index_path->indexinfo;
+ 	List	   *stripped_indexquals;
  	List	   *fixed_indexquals;
! 	ListCell   *lc;
  
! 	stripped_indexquals = fixed_indexquals = NIL;
  
! 	foreach(lc, index_path->indexclauses)
  	{
! 		IndexClause *iclause = lfirst_node(IndexClause, lc);
! 		int			indexcol = iclause->indexcol;
  
! 		if (iclause->indexquals == NIL)
  		{
! 			/* rinfo->clause is directly usable as an indexqual */
! 			Node	   *clause = (Node *) iclause->rinfo->clause;
  
! 			stripped_indexquals = lappend(stripped_indexquals, clause);
! 			clause = fix_indexqual_clause(root, index, indexcol,
! 										  clause, iclause->indexcols);
! 			fixed_indexquals = lappend(fixed_indexquals, clause);
  		}
! 		else
  		{
! 			/* Process the derived indexquals */
! 			ListCell   *lc2;
  
! 			foreach(lc2, iclause->indexquals)
  			{
! 				RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
! 				Node	   *clause = (Node *) rinfo->clause;
  
! 				stripped_indexquals = lappend(stripped_indexquals, clause);
! 				clause = fix_indexqual_clause(root, index, indexcol,
! 											  clause, iclause->indexcols);
! 				fixed_indexquals = lappend(fixed_indexquals, clause);
! 			}
  		}
  	}
  
! 	*stripped_indexquals_p = stripped_indexquals;
! 	*fixed_indexquals_p = fixed_indexquals;
  }
  
  /*
*************** fix_indexqual_references(PlannerInfo *ro
*** 4585,4595 ****
   *	  Adjust indexorderby clauses to the form the executor's index
   *	  machinery needs.
   *
!  * This is a simplified version of fix_indexqual_references.  The input does
!  * not have RestrictInfo nodes, and we assume that indxpath.c already
!  * commuted the clauses to put the index keys on the left.  Also, we don't
!  * bother to support any cases except simple OpExprs, since nothing else
!  * is allowed for ordering operators.
   */
  static List *
  fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
--- 4529,4536 ----
   *	  Adjust indexorderby clauses to the form the executor's index
   *	  machinery needs.
   *
!  * This is a simplified version of fix_indexqual_references.  The input is
!  * bare clauses and a separate indexcol list, instead of IndexClauses.
   */
  static List *
  fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
*************** fix_indexorderby_references(PlannerInfo 
*** 4606,4641 ****
  		Node	   *clause = (Node *) lfirst(lcc);
  		int			indexcol = lfirst_int(lci);
  
! 		/*
! 		 * Replace any outer-relation variables with nestloop params.
! 		 *
! 		 * This also makes a copy of the clause, so it's safe to modify it
! 		 * in-place below.
! 		 */
! 		clause = replace_nestloop_params(root, clause);
  
! 		if (IsA(clause, OpExpr))
! 		{
! 			OpExpr	   *op = (OpExpr *) clause;
  
! 			if (list_length(op->args) != 2)
! 				elog(ERROR, "indexorderby clause is not binary opclause");
  
! 			/*
! 			 * Now replace the indexkey expression with an index Var.
! 			 */
! 			linitial(op->args) = fix_indexqual_operand(linitial(op->args),
! 													   index,
! 													   indexcol);
  		}
! 		else
! 			elog(ERROR, "unsupported indexorderby type: %d",
! 				 (int) nodeTag(clause));
  
! 		fixed_indexorderbys = lappend(fixed_indexorderbys, clause);
  	}
  
! 	return fixed_indexorderbys;
  }
  
  /*
--- 4547,4625 ----
  		Node	   *clause = (Node *) lfirst(lcc);
  		int			indexcol = lfirst_int(lci);
  
! 		clause = fix_indexqual_clause(root, index, indexcol, clause, NIL);
! 		fixed_indexorderbys = lappend(fixed_indexorderbys, clause);
! 	}
  
! 	return fixed_indexorderbys;
! }
  
! /*
!  * fix_indexqual_clause
!  *	  Convert a single indexqual clause to the form needed by the executor.
!  *
!  * We replace nestloop params here, and replace the index key variables
!  * or expressions by index Var nodes.
!  */
! static Node *
! fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol,
! 					 Node *clause, List *indexcolnos)
! {
! 	/*
! 	 * Replace any outer-relation variables with nestloop params.
! 	 *
! 	 * This also makes a copy of the clause, so it's safe to modify it
! 	 * in-place below.
! 	 */
! 	clause = replace_nestloop_params(root, clause);
  
! 	if (IsA(clause, OpExpr))
! 	{
! 		OpExpr	   *op = (OpExpr *) clause;
! 
! 		/* Replace the indexkey expression with an index Var. */
! 		linitial(op->args) = fix_indexqual_operand(linitial(op->args),
! 												   index,
! 												   indexcol);
! 	}
! 	else if (IsA(clause, RowCompareExpr))
! 	{
! 		RowCompareExpr *rc = (RowCompareExpr *) clause;
! 		ListCell   *lca,
! 				   *lcai;
! 
! 		/* Replace the indexkey expressions with index Vars. */
! 		Assert(list_length(rc->largs) == list_length(indexcolnos));
! 		forboth(lca, rc->largs, lcai, indexcolnos)
! 		{
! 			lfirst(lca) = fix_indexqual_operand(lfirst(lca),
! 												index,
! 												lfirst_int(lcai));
  		}
! 	}
! 	else if (IsA(clause, ScalarArrayOpExpr))
! 	{
! 		ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
  
! 		/* Replace the indexkey expression with an index Var. */
! 		linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
! 													 index,
! 													 indexcol);
  	}
+ 	else if (IsA(clause, NullTest))
+ 	{
+ 		NullTest   *nt = (NullTest *) clause;
  
! 		/* Replace the indexkey expression with an index Var. */
! 		nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
! 												 index,
! 												 indexcol);
! 	}
! 	else
! 		elog(ERROR, "unsupported indexqual type: %d",
! 			 (int) nodeTag(clause));
! 
! 	return clause;
  }
  
  /*
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b223972..ddb86bd 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** plan_cluster_use_sort(Oid tableOid, Oid 
*** 6136,6142 ****
  
  	/* Estimate the cost of index scan */
  	indexScanPath = create_index_path(root, indexInfo,
! 									  NIL, NIL, NIL, NIL, NIL,
  									  ForwardScanDirection, false,
  									  NULL, 1.0, false);
  
--- 6136,6142 ----
  
  	/* Estimate the cost of index scan */
  	indexScanPath = create_index_path(root, indexInfo,
! 									  NIL, NIL, NIL, NIL,
  									  ForwardScanDirection, false,
  									  NULL, 1.0, false);
  
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 663fa7c..d7ff17c 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** CommuteOpExpr(OpExpr *clause)
*** 2157,2227 ****
  }
  
  /*
-  * CommuteRowCompareExpr: commute a RowCompareExpr clause
-  *
-  * XXX the clause is destructively modified!
-  */
- void
- CommuteRowCompareExpr(RowCompareExpr *clause)
- {
- 	List	   *newops;
- 	List	   *temp;
- 	ListCell   *l;
- 
- 	/* Sanity checks: caller is at fault if these fail */
- 	if (!IsA(clause, RowCompareExpr))
- 		elog(ERROR, "expected a RowCompareExpr");
- 
- 	/* Build list of commuted operators */
- 	newops = NIL;
- 	foreach(l, clause->opnos)
- 	{
- 		Oid			opoid = lfirst_oid(l);
- 
- 		opoid = get_commutator(opoid);
- 		if (!OidIsValid(opoid))
- 			elog(ERROR, "could not find commutator for operator %u",
- 				 lfirst_oid(l));
- 		newops = lappend_oid(newops, opoid);
- 	}
- 
- 	/*
- 	 * modify the clause in-place!
- 	 */
- 	switch (clause->rctype)
- 	{
- 		case ROWCOMPARE_LT:
- 			clause->rctype = ROWCOMPARE_GT;
- 			break;
- 		case ROWCOMPARE_LE:
- 			clause->rctype = ROWCOMPARE_GE;
- 			break;
- 		case ROWCOMPARE_GE:
- 			clause->rctype = ROWCOMPARE_LE;
- 			break;
- 		case ROWCOMPARE_GT:
- 			clause->rctype = ROWCOMPARE_LT;
- 			break;
- 		default:
- 			elog(ERROR, "unexpected RowCompare type: %d",
- 				 (int) clause->rctype);
- 			break;
- 	}
- 
- 	clause->opnos = newops;
- 
- 	/*
- 	 * Note: we need not change the opfamilies list; we assume any btree
- 	 * opfamily containing an operator will also contain its commutator.
- 	 * Collations don't change either.
- 	 */
- 
- 	temp = clause->largs;
- 	clause->largs = clause->rargs;
- 	clause->rargs = temp;
- }
- 
- /*
   * Helper for eval_const_expressions: check that datatype of an attribute
   * is still what it was when the expression was parsed.  This is needed to
   * guard against improper simplification after ALTER COLUMN TYPE.  (XXX we
--- 2157,2162 ----
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index b57de6b..442b44f 100644
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
*************** create_samplescan_path(PlannerInfo *root
*** 1001,1010 ****
   *	  Creates a path node for an index scan.
   *
   * 'index' is a usable index.
!  * 'indexclauses' is a list of RestrictInfo nodes representing clauses
!  *			to be used as index qual conditions in the scan.
!  * 'indexclausecols' is an integer list of index column numbers (zero based)
!  *			the indexclauses can be used with.
   * 'indexorderbys' is a list of bare expressions (no RestrictInfos)
   *			to be used as index ordering operators in the scan.
   * 'indexorderbycols' is an integer list of index column numbers (zero based)
--- 1001,1008 ----
   *	  Creates a path node for an index scan.
   *
   * 'index' is a usable index.
!  * 'indexclauses' is a list of IndexClause nodes representing clauses
!  *			to be enforced as qual conditions in the scan.
   * 'indexorderbys' is a list of bare expressions (no RestrictInfos)
   *			to be used as index ordering operators in the scan.
   * 'indexorderbycols' is an integer list of index column numbers (zero based)
*************** IndexPath *
*** 1025,1031 ****
  create_index_path(PlannerInfo *root,
  				  IndexOptInfo *index,
  				  List *indexclauses,
- 				  List *indexclausecols,
  				  List *indexorderbys,
  				  List *indexorderbycols,
  				  List *pathkeys,
--- 1023,1028 ----
*************** create_index_path(PlannerInfo *root,
*** 1037,1044 ****
  {
  	IndexPath  *pathnode = makeNode(IndexPath);
  	RelOptInfo *rel = index->rel;
- 	List	   *indexquals,
- 			   *indexqualcols;
  
  	pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
  	pathnode->path.parent = rel;
--- 1034,1039 ----
*************** create_index_path(PlannerInfo *root,
*** 1050,1064 ****
  	pathnode->path.parallel_workers = 0;
  	pathnode->path.pathkeys = pathkeys;
  
- 	/* Convert clauses to indexquals the executor can handle */
- 	expand_indexqual_conditions(index, indexclauses, indexclausecols,
- 								&indexquals, &indexqualcols);
- 
- 	/* Fill in the pathnode */
  	pathnode->indexinfo = index;
  	pathnode->indexclauses = indexclauses;
- 	pathnode->indexquals = indexquals;
- 	pathnode->indexqualcols = indexqualcols;
  	pathnode->indexorderbys = indexorderbys;
  	pathnode->indexorderbycols = indexorderbycols;
  	pathnode->indexscandir = indexscandir;
--- 1045,1052 ----
*************** do { \
*** 3712,3718 ****
  
  				FLAT_COPY_PATH(ipath, path, IndexPath);
  				ADJUST_CHILD_ATTRS(ipath->indexclauses);
- 				ADJUST_CHILD_ATTRS(ipath->indexquals);
  				new_path = (Path *) ipath;
  			}
  			break;
--- 3700,3705 ----
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 1c47c70..03e5f12 100644
*** a/src/backend/optimizer/util/restrictinfo.c
--- b/src/backend/optimizer/util/restrictinfo.c
*************** make_sub_restrictinfos(Expr *clause,
*** 289,294 ****
--- 289,358 ----
  }
  
  /*
+  * commute_restrictinfo
+  *
+  * Given a RestrictInfo containing a binary opclause, produce a RestrictInfo
+  * representing the commutation of that clause.  The caller must pass the
+  * OID of the commutator operator (which it's presumably looked up, else
+  * it would not know this is valid).
+  *
+  * Beware that the result shares sub-structure with the given RestrictInfo.
+  * That's okay for the intended usage with derived index quals, but might
+  * be hazardous if the source is subject to change.  Also notice that we
+  * assume without checking that the commutator op is a member of the same
+  * btree and hash opclasses as the original op.
+  */
+ RestrictInfo *
+ commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
+ {
+ 	RestrictInfo *result;
+ 	OpExpr	   *newclause;
+ 	OpExpr	   *clause = castNode(OpExpr, rinfo->clause);
+ 
+ 	Assert(list_length(clause->args) == 2);
+ 
+ 	/* flat-copy all the fields of clause ... */
+ 	newclause = makeNode(OpExpr);
+ 	memcpy(newclause, clause, sizeof(OpExpr));
+ 
+ 	/* ... and adjust those we need to change to commute it */
+ 	newclause->opno = comm_op;
+ 	newclause->opfuncid = InvalidOid;
+ 	newclause->args = list_make2(lsecond(clause->args),
+ 								 linitial(clause->args));
+ 
+ 	/* likewise, flat-copy all the fields of rinfo ... */
+ 	result = makeNode(RestrictInfo);
+ 	memcpy(result, rinfo, sizeof(RestrictInfo));
+ 
+ 	/*
+ 	 * ... and adjust those we need to change.  Note in particular that we can
+ 	 * preserve any cached selectivity or cost estimates, since those ought to
+ 	 * be the same for the new clause.  Likewise we can keep the source's
+ 	 * parent_ec.
+ 	 */
+ 	result->clause = (Expr *) newclause;
+ 	result->left_relids = rinfo->right_relids;
+ 	result->right_relids = rinfo->left_relids;
+ 	Assert(result->orclause == NULL);
+ 	result->left_ec = rinfo->right_ec;
+ 	result->right_ec = rinfo->left_ec;
+ 	result->left_em = rinfo->right_em;
+ 	result->right_em = rinfo->left_em;
+ 	result->scansel_cache = NIL;	/* not worth updating this */
+ 	if (rinfo->hashjoinoperator == clause->opno)
+ 		result->hashjoinoperator = comm_op;
+ 	else
+ 		result->hashjoinoperator = InvalidOid;
+ 	result->left_bucketsize = rinfo->right_bucketsize;
+ 	result->right_bucketsize = rinfo->left_bucketsize;
+ 	result->left_mcvfreq = rinfo->right_mcvfreq;
+ 	result->right_mcvfreq = rinfo->left_mcvfreq;
+ 
+ 	return result;
+ }
+ 
+ /*
   * restriction_is_or_clause
   *
   * Returns t iff the restrictinfo node contains an 'or' clause.
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index fb00504..74fafc6 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** static Selectivity regex_selectivity(con
*** 226,231 ****
--- 226,233 ----
  static Datum string_to_datum(const char *str, Oid datatype);
  static Const *string_to_const(const char *str, Oid datatype);
  static Const *string_to_bytea_const(const char *str, size_t str_len);
+ static IndexQualInfo *deconstruct_indexqual(RestrictInfo *rinfo,
+ 					  IndexOptInfo *index, int indexcol);
  static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals);
  
  
*************** string_to_bytea_const(const char *str, s
*** 6574,6594 ****
   *-------------------------------------------------------------------------
   */
  
  List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
  	IndexOptInfo *index = path->indexinfo;
! 	ListCell   *lcc,
! 			   *lci;
  
! 	forboth(lcc, path->indexquals, lci, path->indexqualcols)
  	{
- 		RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc);
- 		int			indexcol = lfirst_int(lci);
  		Expr	   *clause;
- 		Node	   *leftop,
- 				   *rightop;
  		IndexQualInfo *qinfo;
  
  		clause = rinfo->clause;
--- 6576,6647 ----
   *-------------------------------------------------------------------------
   */
  
+ /* Extract the actual indexquals (as RestrictInfos) from an IndexClause list */
+ static List *
+ get_index_quals(List *indexclauses)
+ {
+ 	List	   *result = NIL;
+ 	ListCell   *lc;
+ 
+ 	foreach(lc, indexclauses)
+ 	{
+ 		IndexClause *iclause = lfirst_node(IndexClause, lc);
+ 
+ 		if (iclause->indexquals == NIL)
+ 		{
+ 			/* rinfo->clause is directly usable as an indexqual */
+ 			result = lappend(result, iclause->rinfo);
+ 		}
+ 		else
+ 		{
+ 			/* report the derived indexquals */
+ 			result = list_concat(result, list_copy(iclause->indexquals));
+ 		}
+ 	}
+ 	return result;
+ }
+ 
  List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
  	IndexOptInfo *index = path->indexinfo;
! 	ListCell   *lc;
  
! 	foreach(lc, path->indexclauses)
! 	{
! 		IndexClause *iclause = lfirst_node(IndexClause, lc);
! 		int			indexcol = iclause->indexcol;
! 		IndexQualInfo *qinfo;
! 
! 		if (iclause->indexquals == NIL)
! 		{
! 			/* rinfo->clause is directly usable as an indexqual */
! 			qinfo = deconstruct_indexqual(iclause->rinfo, index, indexcol);
! 			result = lappend(result, qinfo);
! 		}
! 		else
! 		{
! 			/* Process the derived indexquals */
! 			ListCell   *lc2;
! 
! 			foreach(lc2, iclause->indexquals)
! 			{
! 				RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
! 
! 				qinfo = deconstruct_indexqual(rinfo, index, indexcol);
! 				result = lappend(result, qinfo);
! 			}
! 		}
! 	}
! 	return result;
! }
! 
! static IndexQualInfo *
! deconstruct_indexqual(RestrictInfo *rinfo, IndexOptInfo *index, int indexcol)
! {
  	{
  		Expr	   *clause;
  		IndexQualInfo *qinfo;
  
  		clause = rinfo->clause;
*************** deconstruct_indexquals(IndexPath *path)
*** 6600,6656 ****
  		if (IsA(clause, OpExpr))
  		{
  			qinfo->clause_op = ((OpExpr *) clause)->opno;
! 			leftop = get_leftop(clause);
! 			rightop = get_rightop(clause);
! 			if (match_index_to_operand(leftop, indexcol, index))
! 			{
! 				qinfo->varonleft = true;
! 				qinfo->other_operand = rightop;
! 			}
! 			else
! 			{
! 				Assert(match_index_to_operand(rightop, indexcol, index));
! 				qinfo->varonleft = false;
! 				qinfo->other_operand = leftop;
! 			}
  		}
  		else if (IsA(clause, RowCompareExpr))
  		{
  			RowCompareExpr *rc = (RowCompareExpr *) clause;
  
  			qinfo->clause_op = linitial_oid(rc->opnos);
! 			/* Examine only first columns to determine left/right sides */
! 			if (match_index_to_operand((Node *) linitial(rc->largs),
! 									   indexcol, index))
! 			{
! 				qinfo->varonleft = true;
! 				qinfo->other_operand = (Node *) rc->rargs;
! 			}
! 			else
! 			{
! 				Assert(match_index_to_operand((Node *) linitial(rc->rargs),
! 											  indexcol, index));
! 				qinfo->varonleft = false;
! 				qinfo->other_operand = (Node *) rc->largs;
! 			}
  		}
  		else if (IsA(clause, ScalarArrayOpExpr))
  		{
  			ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
  
  			qinfo->clause_op = saop->opno;
- 			/* index column is always on the left in this case */
- 			Assert(match_index_to_operand((Node *) linitial(saop->args),
- 										  indexcol, index));
- 			qinfo->varonleft = true;
  			qinfo->other_operand = (Node *) lsecond(saop->args);
  		}
  		else if (IsA(clause, NullTest))
  		{
  			qinfo->clause_op = InvalidOid;
- 			Assert(match_index_to_operand((Node *) ((NullTest *) clause)->arg,
- 										  indexcol, index));
- 			qinfo->varonleft = true;
  			qinfo->other_operand = NULL;
  		}
  		else
--- 6653,6677 ----
  		if (IsA(clause, OpExpr))
  		{
  			qinfo->clause_op = ((OpExpr *) clause)->opno;
! 			qinfo->other_operand = get_rightop(clause);
  		}
  		else if (IsA(clause, RowCompareExpr))
  		{
  			RowCompareExpr *rc = (RowCompareExpr *) clause;
  
  			qinfo->clause_op = linitial_oid(rc->opnos);
! 			qinfo->other_operand = (Node *) rc->rargs;
  		}
  		else if (IsA(clause, ScalarArrayOpExpr))
  		{
  			ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
  
  			qinfo->clause_op = saop->opno;
  			qinfo->other_operand = (Node *) lsecond(saop->args);
  		}
  		else if (IsA(clause, NullTest))
  		{
  			qinfo->clause_op = InvalidOid;
  			qinfo->other_operand = NULL;
  		}
  		else
*************** deconstruct_indexquals(IndexPath *path)
*** 6659,6667 ****
  				 (int) nodeTag(clause));
  		}
  
! 		result = lappend(result, qinfo);
  	}
- 	return result;
  }
  
  /*
--- 6680,6687 ----
  				 (int) nodeTag(clause));
  		}
  
! 		return qinfo;
  	}
  }
  
  /*
*************** genericcostestimate(PlannerInfo *root,
*** 6731,6737 ****
  					GenericCosts *costs)
  {
  	IndexOptInfo *index = path->indexinfo;
! 	List	   *indexQuals = path->indexquals;
  	List	   *indexOrderBys = path->indexorderbys;
  	Cost		indexStartupCost;
  	Cost		indexTotalCost;
--- 6751,6757 ----
  					GenericCosts *costs)
  {
  	IndexOptInfo *index = path->indexinfo;
! 	List	   *indexQuals = get_index_quals(path->indexclauses);
  	List	   *indexOrderBys = path->indexorderbys;
  	Cost		indexStartupCost;
  	Cost		indexTotalCost;
*************** btcostestimate(PlannerInfo *root, IndexP
*** 7052,7065 ****
  			}
  		}
  
- 		/*
- 		 * We would need to commute the clause_op if not varonleft, except
- 		 * that we only care if it's equality or not, so that refinement is
- 		 * unnecessary.
- 		 */
- 		clause_op = qinfo->clause_op;
- 
  		/* check for equality operator */
  		if (OidIsValid(clause_op))
  		{
  			op_strategy = get_op_opfamily_strategy(clause_op,
--- 7072,7079 ----
  			}
  		}
  
  		/* check for equality operator */
+ 		clause_op = qinfo->clause_op;
  		if (OidIsValid(clause_op))
  		{
  			op_strategy = get_op_opfamily_strategy(clause_op,
*************** gincost_opexpr(PlannerInfo *root,
*** 7560,7571 ****
  	Oid			clause_op = qinfo->clause_op;
  	Node	   *operand = qinfo->other_operand;
  
- 	if (!qinfo->varonleft)
- 	{
- 		/* must commute the operator */
- 		clause_op = get_commutator(clause_op);
- 	}
- 
  	/* aggressively reduce to a constant, and look through relabeling */
  	operand = estimate_expression_value(root, operand);
  
--- 7574,7579 ----
*************** gincostestimate(PlannerInfo *root, Index
*** 7728,7734 ****
  				double *indexPages)
  {
  	IndexOptInfo *index = path->indexinfo;
! 	List	   *indexQuals = path->indexquals;
  	List	   *indexOrderBys = path->indexorderbys;
  	List	   *qinfos;
  	ListCell   *l;
--- 7736,7742 ----
  				double *indexPages)
  {
  	IndexOptInfo *index = path->indexinfo;
! 	List	   *indexQuals = get_index_quals(path->indexclauses);
  	List	   *indexOrderBys = path->indexorderbys;
  	List	   *qinfos;
  	ListCell   *l;
*************** gincostestimate(PlannerInfo *root, Index
*** 7831,7856 ****
  		numEntries = 1;
  
  	/*
! 	 * Include predicate in selectivityQuals (should match
! 	 * genericcostestimate)
  	 */
! 	if (index->indpred != NIL)
! 	{
! 		List	   *predExtraQuals = NIL;
! 
! 		foreach(l, index->indpred)
! 		{
! 			Node	   *predQual = (Node *) lfirst(l);
! 			List	   *oneQual = list_make1(predQual);
! 
! 			if (!predicate_implied_by(oneQual, indexQuals, false))
! 				predExtraQuals = list_concat(predExtraQuals, oneQual);
! 		}
! 		/* list_concat avoids modifying the passed-in indexQuals list */
! 		selectivityQuals = list_concat(predExtraQuals, indexQuals);
! 	}
! 	else
! 		selectivityQuals = indexQuals;
  
  	/* Estimate the fraction of main-table tuples that will be visited */
  	*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
--- 7839,7849 ----
  		numEntries = 1;
  
  	/*
! 	 * If the index is partial, AND the index predicate with the index-bound
! 	 * quals to produce a more accurate idea of the number of rows covered by
! 	 * the bound conditions.
  	 */
! 	selectivityQuals = add_predicate_to_quals(index, indexQuals);
  
  	/* Estimate the fraction of main-table tuples that will be visited */
  	*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
*************** brincostestimate(PlannerInfo *root, Inde
*** 8053,8059 ****
  				 double *indexPages)
  {
  	IndexOptInfo *index = path->indexinfo;
! 	List	   *indexQuals = path->indexquals;
  	double		numPages = index->pages;
  	RelOptInfo *baserel = index->rel;
  	RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
--- 8046,8052 ----
  				 double *indexPages)
  {
  	IndexOptInfo *index = path->indexinfo;
! 	List	   *indexQuals = get_index_quals(path->indexclauses);
  	double		numPages = index->pages;
  	RelOptInfo *baserel = index->rel;
  	RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index e215ad4..3c003b0 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 262,267 ****
--- 262,268 ----
  	T_PathKey,
  	T_PathTarget,
  	T_RestrictInfo,
+ 	T_IndexClause,
  	T_PlaceHolderVar,
  	T_SpecialJoinInfo,
  	T_AppendRelInfo,
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index d3c477a..0b780b0 100644
*** a/src/include/nodes/pathnodes.h
--- b/src/include/nodes/pathnodes.h
*************** typedef struct Path
*** 1123,1152 ****
   *
   * 'indexinfo' is the index to be scanned.
   *
!  * 'indexclauses' is a list of index qualification clauses, with implicit
!  * AND semantics across the list.  Each clause is a RestrictInfo node from
!  * the query's WHERE or JOIN conditions.  An empty list implies a full
!  * index scan.
!  *
!  * 'indexquals' has the same structure as 'indexclauses', but it contains
!  * the actual index qual conditions that can be used with the index.
!  * In simple cases this is identical to 'indexclauses', but when special
!  * indexable operators appear in 'indexclauses', they are replaced by the
!  * derived indexscannable conditions in 'indexquals'.
!  *
!  * 'indexqualcols' is an integer list of index column numbers (zero-based)
!  * of the same length as 'indexquals', showing which index column each qual
!  * is meant to be used with.  'indexquals' is required to be ordered by
!  * index column, so 'indexqualcols' must form a nondecreasing sequence.
!  * (The order of multiple quals for the same index column is unspecified.)
   *
   * 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
   * been found to be usable as ordering operators for an amcanorderbyop index.
   * The list must match the path's pathkeys, ie, one expression per pathkey
   * in the same order.  These are not RestrictInfos, just bare expressions,
!  * since they generally won't yield booleans.  Also, unlike the case for
!  * quals, it's guaranteed that each expression has the index key on the left
!  * side of the operator.
   *
   * 'indexorderbycols' is an integer list of index column numbers (zero-based)
   * of the same length as 'indexorderbys', showing which index column each
--- 1123,1138 ----
   *
   * 'indexinfo' is the index to be scanned.
   *
!  * 'indexclauses' is a list of IndexClause nodes, each representing one
!  * index-checkable restriction, with implicit AND semantics across the list.
!  * An empty list implies a full index scan.
   *
   * 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
   * been found to be usable as ordering operators for an amcanorderbyop index.
   * The list must match the path's pathkeys, ie, one expression per pathkey
   * in the same order.  These are not RestrictInfos, just bare expressions,
!  * since they generally won't yield booleans.  It's guaranteed that each
!  * expression has the index key on the left side of the operator.
   *
   * 'indexorderbycols' is an integer list of index column numbers (zero-based)
   * of the same length as 'indexorderbys', showing which index column each
*************** typedef struct IndexPath
*** 1172,1179 ****
  	Path		path;
  	IndexOptInfo *indexinfo;
  	List	   *indexclauses;
- 	List	   *indexquals;
- 	List	   *indexqualcols;
  	List	   *indexorderbys;
  	List	   *indexorderbycols;
  	ScanDirection indexscandir;
--- 1158,1163 ----
*************** typedef struct IndexPath
*** 1182,1187 ****
--- 1166,1221 ----
  } IndexPath;
  
  /*
+  * Each IndexClause references a RestrictInfo node from the query's WHERE
+  * or JOIN conditions, and shows how that restriction can be applied to
+  * the particular index.  We support both indexclauses that are directly
+  * usable by the index machinery, which are typically of the form
+  * "indexcol OP pseudoconstant", and those from which an indexable qual
+  * can be derived.  The simplest such transformation is that a clause
+  * of the form "pseudoconstant OP indexcol" can be commuted to produce an
+  * indexable qual (the index machinery expects the indexcol to be on the
+  * left always).  Another example is that we might be able to extract an
+  * indexable range condition from a LIKE condition, as in "x LIKE 'foo%bar'"
+  * giving rise to "x >= 'foo' AND x < 'fop'".  Derivation of such lossy
+  * conditions is done by a planner support function attached to the
+  * indexclause's top-level function or operator.
+  *
+  * If indexquals is NIL, it means that rinfo->clause is directly usable as
+  * an indexqual.  Otherwise indexquals contains one or more directly-usable
+  * indexqual conditions extracted from the given clause.  The 'lossy' flag
+  * indicates whether the indexquals are semantically equivalent to the
+  * original clause, or form a weaker condition.
+  *
+  * Currently, entries in indexquals are RestrictInfos, but they could perhaps
+  * be bare clauses instead; the only advantage of making them RestrictInfos
+  * is the possibility of caching cost and selectivity information across
+  * multiple uses, and it's not clear that that's really worth the price of
+  * constructing RestrictInfos for them.  Note however that the extended-stats
+  * machinery won't do anything with non-RestrictInfo clauses, so that would
+  * have to be fixed.
+  *
+  * Normally, indexcol is the index of the single index column the clause
+  * works on, and indexcols is NIL.  But if the clause is a RowCompareExpr,
+  * indexcol is the index of the leading column, and indexcols is a list of
+  * all the affected columns.  (Note that indexcols matches up with the
+  * columns of the actual indexable RowCompareExpr, which might be in
+  * indexquals rather than rinfo.)
+  *
+  * An IndexPath's IndexClause list is required to be ordered by index
+  * column, i.e. the indexcol values must form a nondecreasing sequence.
+  * (The order of multiple clauses for the same index column is unspecified.)
+  */
+ typedef struct IndexClause
+ {
+ 	NodeTag		type;
+ 	struct RestrictInfo *rinfo; /* original restriction or join clause */
+ 	List	   *indexquals;		/* indexqual(s) derived from it, or NIL */
+ 	bool		lossy;			/* are indexquals a lossy version of clause? */
+ 	AttrNumber	indexcol;		/* index column the clause uses (zero-based) */
+ 	List	   *indexcols;		/* multiple index columns, if RowCompare */
+ } IndexClause;
+ 
+ /*
   * BitmapHeapPath represents one or more indexscans that generate TID bitmaps
   * instead of directly accessing the heap, followed by AND/OR combinations
   * to produce a single bitmap, followed by a heap scan that uses the bitmap.
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 23073c0..95a78cf 100644
*** a/src/include/optimizer/clauses.h
--- b/src/include/optimizer/clauses.h
*************** extern bool is_pseudo_constant_clause_re
*** 51,57 ****
  extern int	NumRelids(Node *clause);
  
  extern void CommuteOpExpr(OpExpr *clause);
- extern void CommuteRowCompareExpr(RowCompareExpr *clause);
  
  extern Query *inline_set_returning_function(PlannerInfo *root,
  							  RangeTblEntry *rte);
--- 51,56 ----
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index d0c8f99..f62acc3 100644
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
*************** extern Path *create_samplescan_path(Plan
*** 41,47 ****
  extern IndexPath *create_index_path(PlannerInfo *root,
  				  IndexOptInfo *index,
  				  List *indexclauses,
- 				  List *indexclausecols,
  				  List *indexorderbys,
  				  List *indexorderbycols,
  				  List *pathkeys,
--- 41,46 ----
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 1b02b3b..040335a 100644
*** a/src/include/optimizer/paths.h
--- b/src/include/optimizer/paths.h
*************** extern bool indexcol_is_bool_constant_fo
*** 78,92 ****
  									int indexcol);
  extern bool match_index_to_operand(Node *operand, int indexcol,
  					   IndexOptInfo *index);
- extern void expand_indexqual_conditions(IndexOptInfo *index,
- 							List *indexclauses, List *indexclausecols,
- 							List **indexquals_p, List **indexqualcols_p);
  extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
- extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
- 							IndexOptInfo *index,
- 							int indexcol,
- 							List **indexcolnos,
- 							bool *var_on_left_p);
  
  /*
   * tidpath.h
--- 78,84 ----
*************** extern bool eclass_useful_for_merging(Pl
*** 175,180 ****
--- 167,174 ----
  						  EquivalenceClass *eclass,
  						  RelOptInfo *rel);
  extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
+ extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
+ 							   List *indexclauses);
  
  /*
   * pathkeys.c
diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h
index feeaf0e..c348760 100644
*** a/src/include/optimizer/restrictinfo.h
--- b/src/include/optimizer/restrictinfo.h
*************** extern RestrictInfo *make_restrictinfo(E
*** 29,34 ****
--- 29,35 ----
  				  Relids required_relids,
  				  Relids outer_relids,
  				  Relids nullable_relids);
+ extern RestrictInfo *commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op);
  extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
  extern bool restriction_is_securely_promotable(RestrictInfo *restrictinfo,
  								   RelOptInfo *rel);
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 6b1ef91..087b56f 100644
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef struct
*** 108,114 ****
  {
  	RestrictInfo *rinfo;		/* the indexqual itself */
  	int			indexcol;		/* zero-based index column number */
- 	bool		varonleft;		/* true if index column is on left of qual */
  	Oid			clause_op;		/* qual's operator OID, if relevant */
  	Node	   *other_operand;	/* non-index operand of qual's operator */
  } IndexQualInfo;
--- 108,113 ----
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 46deb55..4932869 100644
*** a/src/test/regress/expected/create_index.out
--- b/src/test/regress/expected/create_index.out
*************** SELECT count(*) FROM quad_point_tbl WHER
*** 1504,1510 ****
     ->  Bitmap Heap Scan on quad_point_tbl
           Recheck Cond: ('(1000,1000),(200,200)'::box @> p)
           ->  Bitmap Index Scan on sp_quad_ind
!                Index Cond: ('(1000,1000),(200,200)'::box @> p)
  (5 rows)
  
  SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
--- 1504,1510 ----
     ->  Bitmap Heap Scan on quad_point_tbl
           Recheck Cond: ('(1000,1000),(200,200)'::box @> p)
           ->  Bitmap Index Scan on sp_quad_ind
!                Index Cond: (p <@ '(1000,1000),(200,200)'::box)
  (5 rows)
  
  SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
*************** SELECT count(*) FROM kd_point_tbl WHERE 
*** 1623,1629 ****
     ->  Bitmap Heap Scan on kd_point_tbl
           Recheck Cond: ('(1000,1000),(200,200)'::box @> p)
           ->  Bitmap Index Scan on sp_kd_ind
!                Index Cond: ('(1000,1000),(200,200)'::box @> p)
  (5 rows)
  
  SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
--- 1623,1629 ----
     ->  Bitmap Heap Scan on kd_point_tbl
           Recheck Cond: ('(1000,1000),(200,200)'::box @> p)
           ->  Bitmap Index Scan on sp_kd_ind
!                Index Cond: (p <@ '(1000,1000),(200,200)'::box)
  (5 rows)
  
  SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
*************** explain (costs off)
*** 3181,3188 ****
   Limit
     ->  Index Scan using boolindex_b_i_key on boolindex
           Index Cond: (b = true)
!          Filter: b
! (4 rows)
  
  explain (costs off)
    select * from boolindex where b = true order by i desc limit 10;
--- 3181,3187 ----
   Limit
     ->  Index Scan using boolindex_b_i_key on boolindex
           Index Cond: (b = true)
! (3 rows)
  
  explain (costs off)
    select * from boolindex where b = true order by i desc limit 10;
*************** explain (costs off)
*** 3191,3198 ****
   Limit
     ->  Index Scan Backward using boolindex_b_i_key on boolindex
           Index Cond: (b = true)
!          Filter: b
! (4 rows)
  
  explain (costs off)
    select * from boolindex where not b order by i limit 10;
--- 3190,3196 ----
   Limit
     ->  Index Scan Backward using boolindex_b_i_key on boolindex
           Index Cond: (b = true)
! (3 rows)
  
  explain (costs off)
    select * from boolindex where not b order by i limit 10;
*************** explain (costs off)
*** 3201,3208 ****
   Limit
     ->  Index Scan using boolindex_b_i_key on boolindex
           Index Cond: (b = false)
!          Filter: (NOT b)
! (4 rows)
  
  --
  -- Test for multilevel page deletion
--- 3199,3205 ----
   Limit
     ->  Index Scan using boolindex_b_i_key on boolindex
           Index Cond: (b = false)
! (3 rows)
  
  --
  -- Test for multilevel page deletion
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 2829878..fcc82a1 100644
*** a/src/test/regress/expected/join.out
--- b/src/test/regress/expected/join.out
*************** where q1 = thousand or q2 = thousand;
*** 3078,3086 ****
                 Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand))
                 ->  BitmapOr
                       ->  Bitmap Index Scan on tenk1_thous_tenthous
!                            Index Cond: (q1.q1 = thousand)
                       ->  Bitmap Index Scan on tenk1_thous_tenthous
!                            Index Cond: (q2.q2 = thousand)
     ->  Hash
           ->  Seq Scan on int4_tbl
  (15 rows)
--- 3078,3086 ----
                 Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand))
                 ->  BitmapOr
                       ->  Bitmap Index Scan on tenk1_thous_tenthous
!                            Index Cond: (thousand = q1.q1)
                       ->  Bitmap Index Scan on tenk1_thous_tenthous
!                            Index Cond: (thousand = q2.q2)
     ->  Hash
           ->  Seq Scan on int4_tbl
  (15 rows)
*************** where t1.unique1 = 1;
*** 3332,3338 ****
           ->  Bitmap Heap Scan on tenk1 t2
                 Recheck Cond: (t1.hundred = hundred)
                 ->  Bitmap Index Scan on tenk1_hundred
!                      Index Cond: (t1.hundred = hundred)
           ->  Index Scan using tenk1_unique2 on tenk1 t3
                 Index Cond: (unique2 = t2.thousand)
  (11 rows)
--- 3332,3338 ----
           ->  Bitmap Heap Scan on tenk1 t2
                 Recheck Cond: (t1.hundred = hundred)
                 ->  Bitmap Index Scan on tenk1_hundred
!                      Index Cond: (hundred = t1.hundred)
           ->  Index Scan using tenk1_unique2 on tenk1 t3
                 Index Cond: (unique2 = t2.thousand)
  (11 rows)
*************** where t1.unique1 = 1;
*** 3352,3358 ****
           ->  Bitmap Heap Scan on tenk1 t2
                 Recheck Cond: (t1.hundred = hundred)
                 ->  Bitmap Index Scan on tenk1_hundred
!                      Index Cond: (t1.hundred = hundred)
           ->  Index Scan using tenk1_unique2 on tenk1 t3
                 Index Cond: (unique2 = t2.thousand)
  (11 rows)
--- 3352,3358 ----
           ->  Bitmap Heap Scan on tenk1 t2
                 Recheck Cond: (t1.hundred = hundred)
                 ->  Bitmap Index Scan on tenk1_hundred
!                      Index Cond: (hundred = t1.hundred)
           ->  Index Scan using tenk1_unique2 on tenk1 t3
                 Index Cond: (unique2 = t2.thousand)
  (11 rows)
*************** select b.unique1 from
*** 3408,3414 ****
                       ->  Nested Loop
                             ->  Seq Scan on int4_tbl i1
                             ->  Index Scan using tenk1_thous_tenthous on tenk1 b
!                                  Index Cond: ((thousand = i1.f1) AND (i2.f1 = tenthous))
                       ->  Index Scan using tenk1_unique1 on tenk1 a
                             Index Cond: (unique1 = b.unique2)
                 ->  Index Only Scan using tenk1_thous_tenthous on tenk1 c
--- 3408,3414 ----
                       ->  Nested Loop
                             ->  Seq Scan on int4_tbl i1
                             ->  Index Scan using tenk1_thous_tenthous on tenk1 b
!                                  Index Cond: ((thousand = i1.f1) AND (tenthous = i2.f1))
                       ->  Index Scan using tenk1_unique1 on tenk1 a
                             Index Cond: (unique1 = b.unique2)
                 ->  Index Only Scan using tenk1_thous_tenthous on tenk1 c
*************** order by fault;
*** 3444,3450 ****
     Filter: ((COALESCE(tenk1.unique1, '-1'::integer) + int8_tbl.q1) = 122)
     ->  Seq Scan on int8_tbl
     ->  Index Scan using tenk1_unique2 on tenk1
!          Index Cond: (int8_tbl.q2 = unique2)
  (5 rows)
  
  select * from
--- 3444,3450 ----
     Filter: ((COALESCE(tenk1.unique1, '-1'::integer) + int8_tbl.q1) = 122)
     ->  Seq Scan on int8_tbl
     ->  Index Scan using tenk1_unique2 on tenk1
!          Index Cond: (unique2 = int8_tbl.q2)
  (5 rows)
  
  select * from
*************** select q1, unique2, thousand, hundred
*** 3499,3505 ****
     Filter: ((COALESCE(b.thousand, 123) = a.q1) AND (a.q1 = COALESCE(b.hundred, 123)))
     ->  Seq Scan on int8_tbl a
     ->  Index Scan using tenk1_unique2 on tenk1 b
!          Index Cond: (a.q1 = unique2)
  (5 rows)
  
  select q1, unique2, thousand, hundred
--- 3499,3505 ----
     Filter: ((COALESCE(b.thousand, 123) = a.q1) AND (a.q1 = COALESCE(b.hundred, 123)))
     ->  Seq Scan on int8_tbl a
     ->  Index Scan using tenk1_unique2 on tenk1 b
!          Index Cond: (unique2 = a.q1)
  (5 rows)
  
  select q1, unique2, thousand, hundred
*************** explain (costs off)
*** 4586,4592 ****
   Nested Loop Left Join
     ->  Seq Scan on int4_tbl x
     ->  Index Scan using tenk1_unique1 on tenk1
!          Index Cond: (x.f1 = unique1)
  (4 rows)
  
  -- check scoping of lateral versus parent references
--- 4586,4592 ----
   Nested Loop Left Join
     ->  Seq Scan on int4_tbl x
     ->  Index Scan using tenk1_unique1 on tenk1
!          Index Cond: (unique1 = x.f1)
  (4 rows)
  
  -- check scoping of lateral versus parent references
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index c55de5d..bbdc373 100644
*** a/src/test/regress/expected/partition_join.out
--- b/src/test/regress/expected/partition_join.out
*************** SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3
*** 647,653 ****
                             ->  Seq Scan on prt1_e_p1 t3
                                   Filter: (c = 0)
                 ->  Index Scan using iprt2_p1_b on prt2_p1 t2
!                      Index Cond: (t1.a = b)
           ->  Nested Loop Left Join
                 ->  Hash Right Join
                       Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
--- 647,653 ----
                             ->  Seq Scan on prt1_e_p1 t3
                                   Filter: (c = 0)
                 ->  Index Scan using iprt2_p1_b on prt2_p1 t2
!                      Index Cond: (b = t1.a)
           ->  Nested Loop Left Join
                 ->  Hash Right Join
                       Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
*************** SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3
*** 656,662 ****
                             ->  Seq Scan on prt1_e_p2 t3_1
                                   Filter: (c = 0)
                 ->  Index Scan using iprt2_p2_b on prt2_p2 t2_1
!                      Index Cond: (t1_1.a = b)
           ->  Nested Loop Left Join
                 ->  Hash Right Join
                       Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
--- 656,662 ----
                             ->  Seq Scan on prt1_e_p2 t3_1
                                   Filter: (c = 0)
                 ->  Index Scan using iprt2_p2_b on prt2_p2 t2_1
!                      Index Cond: (b = t1_1.a)
           ->  Nested Loop Left Join
                 ->  Hash Right Join
                       Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
*************** SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3
*** 665,671 ****
                             ->  Seq Scan on prt1_e_p3 t3_2
                                   Filter: (c = 0)
                 ->  Index Scan using iprt2_p3_b on prt2_p3 t2_2
!                      Index Cond: (t1_2.a = b)
  (30 rows)
  
  SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
--- 665,671 ----
                             ->  Seq Scan on prt1_e_p3 t3_2
                                   Filter: (c = 0)
                 ->  Index Scan using iprt2_p3_b on prt2_p3 t2_2
!                      Index Cond: (b = t1_2.a)
  (30 rows)
  
  SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
*************** SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 
*** 1878,1888 ****
           ->  Seq Scan on prt1_p3 t1_2
     ->  Append
           ->  Index Scan using iprt2_p1_b on prt2_p1 t2
!                Index Cond: (t1.a < b)
           ->  Index Scan using iprt2_p2_b on prt2_p2 t2_1
!                Index Cond: (t1.a < b)
           ->  Index Scan using iprt2_p3_b on prt2_p3 t2_2
!                Index Cond: (t1.a < b)
  (12 rows)
  
  -- equi-join with join condition on partial keys does not qualify for
--- 1878,1888 ----
           ->  Seq Scan on prt1_p3 t1_2
     ->  Append
           ->  Index Scan using iprt2_p1_b on prt2_p1 t2
!                Index Cond: (b > t1.a)
           ->  Index Scan using iprt2_p2_b on prt2_p2 t2_1
!                Index Cond: (b > t1.a)
           ->  Index Scan using iprt2_p3_b on prt2_p3 t2_2
!                Index Cond: (b > t1.a)
  (12 rows)
  
  -- equi-join with join condition on partial keys does not qualify for
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 120b651..30946f7 100644
*** a/src/test/regress/expected/partition_prune.out
--- b/src/test/regress/expected/partition_prune.out
*************** select * from tbl1 join tprt on tbl1.col
*** 2635,2651 ****
     ->  Seq Scan on tbl1 (actual rows=2 loops=1)
     ->  Append (actual rows=3 loops=2)
           ->  Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=2)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt2_idx on tprt_2 (actual rows=2 loops=1)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt3_idx on tprt_3 (never executed)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt4_idx on tprt_4 (never executed)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt5_idx on tprt_5 (never executed)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt6_idx on tprt_6 (never executed)
!                Index Cond: (tbl1.col1 > col1)
  (15 rows)
  
  explain (analyze, costs off, summary off, timing off)
--- 2635,2651 ----
     ->  Seq Scan on tbl1 (actual rows=2 loops=1)
     ->  Append (actual rows=3 loops=2)
           ->  Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=2)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt2_idx on tprt_2 (actual rows=2 loops=1)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt3_idx on tprt_3 (never executed)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt4_idx on tprt_4 (never executed)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt5_idx on tprt_5 (never executed)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt6_idx on tprt_6 (never executed)
!                Index Cond: (col1 < tbl1.col1)
  (15 rows)
  
  explain (analyze, costs off, summary off, timing off)
*************** select * from tbl1 inner join tprt on tb
*** 2701,2717 ****
     ->  Seq Scan on tbl1 (actual rows=5 loops=1)
     ->  Append (actual rows=5 loops=5)
           ->  Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt2_idx on tprt_2 (actual rows=3 loops=4)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt3_idx on tprt_3 (actual rows=1 loops=2)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt4_idx on tprt_4 (never executed)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt5_idx on tprt_5 (never executed)
!                Index Cond: (tbl1.col1 > col1)
           ->  Index Scan using tprt6_idx on tprt_6 (never executed)
!                Index Cond: (tbl1.col1 > col1)
  (15 rows)
  
  explain (analyze, costs off, summary off, timing off)
--- 2701,2717 ----
     ->  Seq Scan on tbl1 (actual rows=5 loops=1)
     ->  Append (actual rows=5 loops=5)
           ->  Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt2_idx on tprt_2 (actual rows=3 loops=4)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt3_idx on tprt_3 (actual rows=1 loops=2)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt4_idx on tprt_4 (never executed)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt5_idx on tprt_5 (never executed)
!                Index Cond: (col1 < tbl1.col1)
           ->  Index Scan using tprt6_idx on tprt_6 (never executed)
!                Index Cond: (col1 < tbl1.col1)
  (15 rows)
  
  explain (analyze, costs off, summary off, timing off)
*************** select * from tbl1 join tprt on tbl1.col
*** 2786,2802 ****
     ->  Seq Scan on tbl1 (actual rows=1 loops=1)
     ->  Append (actual rows=1 loops=1)
           ->  Index Scan using tprt1_idx on tprt_1 (never executed)
!                Index Cond: (tbl1.col1 < col1)
           ->  Index Scan using tprt2_idx on tprt_2 (never executed)
!                Index Cond: (tbl1.col1 < col1)
           ->  Index Scan using tprt3_idx on tprt_3 (never executed)
!                Index Cond: (tbl1.col1 < col1)
           ->  Index Scan using tprt4_idx on tprt_4 (never executed)
!                Index Cond: (tbl1.col1 < col1)
           ->  Index Scan using tprt5_idx on tprt_5 (never executed)
!                Index Cond: (tbl1.col1 < col1)
           ->  Index Scan using tprt6_idx on tprt_6 (actual rows=1 loops=1)
!                Index Cond: (tbl1.col1 < col1)
  (15 rows)
  
  select tbl1.col1, tprt.col1 from tbl1
--- 2786,2802 ----
     ->  Seq Scan on tbl1 (actual rows=1 loops=1)
     ->  Append (actual rows=1 loops=1)
           ->  Index Scan using tprt1_idx on tprt_1 (never executed)
!                Index Cond: (col1 > tbl1.col1)
           ->  Index Scan using tprt2_idx on tprt_2 (never executed)
!                Index Cond: (col1 > tbl1.col1)
           ->  Index Scan using tprt3_idx on tprt_3 (never executed)
!                Index Cond: (col1 > tbl1.col1)
           ->  Index Scan using tprt4_idx on tprt_4 (never executed)
!                Index Cond: (col1 > tbl1.col1)
           ->  Index Scan using tprt5_idx on tprt_5 (never executed)
!                Index Cond: (col1 > tbl1.col1)
           ->  Index Scan using tprt6_idx on tprt_6 (actual rows=1 loops=1)
!                Index Cond: (col1 > tbl1.col1)
  (15 rows)
  
  select tbl1.col1, tprt.col1 from tbl1
