v20-0012-Use-CompareType-more-and-StrategyNumber-less.patch.no-cfbot

application/octet-stream

Filename: v20-0012-Use-CompareType-more-and-StrategyNumber-less.patch.no-cfbot
Type: application/octet-stream
Part: 1
Message: Re: Remove extra Sort node above a btree-compatible Index Scan
From 00dc3e0cae56d8c6a9b38afb43ad4d8dd7ea4267 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Tue, 26 Nov 2024 08:53:22 -0500
Subject: [PATCH v20 12/16] Use CompareType more and StrategyNumber less

Reduce the number of places that hardcode a Btree strategy number by
instead using comparison types.

Rather than hardcoding BTREE_AM_OID in get_ordering_op_properties,
add a parameter to filter the index access methods to consider.  For
mainline code, callers now pass BTREE_AM_OID for this filter, which
means the behavior (and the regression test expected output) is
unchanged.

Author: Mark Dilger <mark.dilger@enterprisedb.com>
Discussion: https://www.postgresql.org/message-id/flat/E72EAA49-354D-4C2E-8EB9-255197F55330@enterprisedb.com
---
 contrib/postgres_fdw/postgres_fdw.c        |   4 +-
 src/backend/commands/explain.c             |   3 +-
 src/backend/commands/indexcmds.c           |   5 +-
 src/backend/commands/matview.c             |   6 +-
 src/backend/executor/execExpr.c            |   2 +
 src/backend/executor/nodeIncrementalSort.c |   3 +-
 src/backend/executor/nodeIndexscan.c       |  14 +-
 src/backend/executor/nodeMergejoin.c       |   5 +-
 src/backend/optimizer/path/allpaths.c      |  25 ++--
 src/backend/optimizer/path/costsize.c      |   6 +
 src/backend/optimizer/path/equivclass.c    |   4 +-
 src/backend/optimizer/path/indxpath.c      |  26 +++-
 src/backend/optimizer/path/pathkeys.c      |  97 ++++++++++---
 src/backend/optimizer/plan/createplan.c    |  13 +-
 src/backend/optimizer/plan/planagg.c       |   4 +-
 src/backend/optimizer/plan/planner.c       |  13 +-
 src/backend/optimizer/prep/prepunion.c     |  19 ++-
 src/backend/optimizer/util/plancat.c       |  31 ++--
 src/backend/optimizer/util/predtest.c      |  18 ++-
 src/backend/parser/parse_clause.c          |   8 +-
 src/backend/parser/parse_expr.c            |   6 +-
 src/backend/partitioning/partprune.c       |  17 ++-
 src/backend/utils/adt/network.c            |  12 +-
 src/backend/utils/adt/selfuncs.c           | 120 +++++++--------
 src/backend/utils/cache/lsyscache.c        | 161 ++++++++++++++++-----
 src/backend/utils/cache/typcache.c         |  15 +-
 src/backend/utils/sort/sortsupport.c       |   9 +-
 src/include/nodes/pathnodes.h              |  10 +-
 src/include/optimizer/paths.h              |   8 +-
 src/include/utils/lsyscache.h              |  29 +++-
 src/include/utils/selfuncs.h               |   9 +-
 src/tools/pgindent/typedefs.list           |   2 +-
 32 files changed, 475 insertions(+), 229 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index de43727a2a0..6db93fc4b29 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -994,8 +994,10 @@ get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
 
 		/* Looks like we can generate a pathkey, so let's do it. */
 		pathkey = make_canonical_pathkey(root, cur_ec,
+										 get_opfamily_method(linitial_oid(cur_ec->ec_opfamilies)),
 										 linitial_oid(cur_ec->ec_opfamilies),
-										 BTLessStrategyNumber,
+										 InvalidStrategy,
+										 COMPARE_LT,
 										 false);
 		useful_pathkeys_list = lappend(useful_pathkeys_list,
 									   list_make1(pathkey));
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c0d614866a9..72354b9ec68 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -14,6 +14,7 @@
 #include "postgres.h"
 
 #include "access/xact.h"
+#include "catalog/pg_am.h"		/* for BTREE_AM_OID */
 #include "catalog/pg_type.h"
 #include "commands/createas.h"
 #include "commands/defrem.h"
@@ -3037,7 +3038,7 @@ show_sortorder_options(StringInfo buf, Node *sortexpr,
 			elog(ERROR, "cache lookup failed for operator %u", sortOperator);
 		appendStringInfo(buf, " USING %s", opname);
 		/* Determine whether operator would be considered ASC or DESC */
-		(void) get_equality_op_for_ordering_op(sortOperator, &reverse);
+		(void) get_equality_op_for_ordering_op(sortOperator, BTREE_AM_OID, &reverse);
 	}
 
 	/* Add NULLS FIRST/LAST only if it wouldn't be default */
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index c92f5620ec1..87124b02d45 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1057,10 +1057,11 @@ DefineIndex(Oid tableId,
 						Oid			idx_eqop = InvalidOid;
 
 						if (stmt->unique && !stmt->iswithoutoverlaps)
-							idx_eqop = get_opfamily_member(idx_opfamily,
+							idx_eqop = get_opmethod_member(InvalidOid,
+														   idx_opfamily,
 														   idx_opcintype,
 														   idx_opcintype,
-														   BTEqualStrategyNumber);
+														   COMPARE_EQ);
 						else if (exclusion)
 							idx_eqop = indexInfo->ii_ExclusionOps[j];
 						Assert(idx_eqop);
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 0bfbc5ca6dc..3d93d62d213 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -779,11 +779,11 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner,
 				opcintype = cla_tup->opcintype;
 				ReleaseSysCache(cla_ht);
 
-				op = get_opfamily_member(opfamily, opcintype, opcintype,
-										 BTEqualStrategyNumber);
+				op = get_opmethod_member(InvalidOid, opfamily, opcintype, opcintype,
+										 COMPARE_EQ);
 				if (!OidIsValid(op))
 					elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
-						 BTEqualStrategyNumber, opcintype, opcintype, opfamily);
+						 COMPARE_EQ, opcintype, opcintype, opfamily);
 
 				/*
 				 * If we find the same column with the same equality semantics
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 03566c4d181..dbf98383117 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2097,7 +2097,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
 					FunctionCallInfo fcinfo;
 
 					get_op_opfamily_properties(opno, opfamily, false,
+											   NULL,		/* don't need opmethod */
 											   &strategy,
+											   NULL,		/* don't need cmptype */
 											   &lefttype,
 											   &righttype);
 					proc = get_opfamily_proc(opfamily,
diff --git a/src/backend/executor/nodeIncrementalSort.c b/src/backend/executor/nodeIncrementalSort.c
index 975b0397e7a..7d666eb2980 100644
--- a/src/backend/executor/nodeIncrementalSort.c
+++ b/src/backend/executor/nodeIncrementalSort.c
@@ -78,6 +78,7 @@
 
 #include "postgres.h"
 
+#include "catalog/pg_am.h"			/* for BTREE_AM_OID */
 #include "executor/execdebug.h"
 #include "executor/nodeIncrementalSort.h"
 #include "miscadmin.h"
@@ -180,7 +181,7 @@ preparePresortedCols(IncrementalSortState *node)
 		key->attno = plannode->sort.sortColIdx[i];
 
 		equalityOp = get_equality_op_for_ordering_op(plannode->sort.sortOperators[i],
-													 NULL);
+													 BTREE_AM_OID, NULL);
 		if (!OidIsValid(equalityOp))
 			elog(ERROR, "missing equality operator for ordering operator %u",
 				 plannode->sort.sortOperators[i]);
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index c30b9c2c197..bab9e74fcad 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -1180,6 +1180,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
 		RegProcedure opfuncid;	/* operator proc id used in scan */
 		Oid			opfamily;	/* opfamily of index column */
 		int			op_strategy;	/* operator's strategy number */
+		CompareType op_cmptype;
 		Oid			op_lefttype;	/* operator's declared input types */
 		Oid			op_righttype;
 		Expr	   *leftop;		/* expr on lhs of operator */
@@ -1222,7 +1223,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
 			opfamily = index->rd_opfamily[varattno - 1];
 
 			get_op_opfamily_properties(opno, opfamily, isorderby,
+									   NULL,		/* don't need opmethod */
 									   &op_strategy,
+									   NULL,		/* don't need cmptype */
 									   &op_lefttype,
 									   &op_righttype);
 
@@ -1340,11 +1343,13 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
 				opfamily = index->rd_opfamily[varattno - 1];
 
 				get_op_opfamily_properties(opno, opfamily, isorderby,
+										   NULL,		/* don't need opmethod */
 										   &op_strategy,
+										   &op_cmptype,
 										   &op_lefttype,
 										   &op_righttype);
 
-				if (op_strategy != rc->cmptype)
+				if (op_cmptype != rc->cmptype)
 					elog(ERROR, "RowCompare index qualification contains wrong operator");
 
 				opfuncid = get_opfamily_proc(opfamily,
@@ -1421,7 +1426,10 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
 			MemSet(this_scan_key, 0, sizeof(ScanKeyData));
 			this_scan_key->sk_flags = SK_ROW_HEADER;
 			this_scan_key->sk_attno = first_sub_key->sk_attno;
-			this_scan_key->sk_strategy = rc->cmptype;
+			this_scan_key->sk_strategy = IndexAmTranslateCompareType(rc->cmptype,
+																	 index->rd_rel->relam,
+																	 0,  // FIXME
+																	 false);
 			/* sk_subtype, sk_collation, sk_func not used in a header */
 			this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
 		}
@@ -1463,7 +1471,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
 			opfamily = index->rd_opfamily[varattno - 1];
 
 			get_op_opfamily_properties(opno, opfamily, isorderby,
+									   NULL,		/* don't need opmethod */
 									   &op_strategy,
+									   NULL,		/* don't need cmptype */
 									   &op_lefttype,
 									   &op_righttype);
 
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index f70239a2c9d..84f92fb4f8a 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -196,6 +196,7 @@ MJExamineQuals(List *mergeclauses,
 		bool		reversed = mergereversals[iClause];
 		bool		nulls_first = mergenullsfirst[iClause];
 		int			op_strategy;
+		CompareType op_cmptype;
 		Oid			op_lefttype;
 		Oid			op_righttype;
 		Oid			sortfunc;
@@ -217,10 +218,12 @@ MJExamineQuals(List *mergeclauses,
 
 		/* Extract the operator's declared left/right datatypes */
 		get_op_opfamily_properties(qual->opno, opfamily, false,
+								   NULL,		/* don't need opmethod */
 								   &op_strategy,
+								   &op_cmptype,
 								   &op_lefttype,
 								   &op_righttype);
-		if (op_strategy != BTEqualStrategyNumber)	/* should not happen */
+		if (op_cmptype != COMPARE_EQ)			/* should not happen */
 			elog(ERROR, "cannot merge using non-equality operator %u",
 				 qual->opno);
 
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b5bc9b602e2..e6050b492cf 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2299,16 +2299,15 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
 
 	runopexpr = NULL;
 	runoperator = InvalidOid;
-	opinfos = get_op_btree_interpretation(opexpr->opno);
+	opinfos = get_op_index_interpretation(opexpr->opno);
 
 	foreach(lc, opinfos)
 	{
-		OpBtreeInterpretation *opinfo = (OpBtreeInterpretation *) lfirst(lc);
-		int			strategy = opinfo->strategy;
+		OpIndexInterpretation *opinfo = (OpIndexInterpretation *) lfirst(lc);
+		CompareType cmptype = opinfo->cmptype;
 
 		/* handle < / <= */
-		if (strategy == BTLessStrategyNumber ||
-			strategy == BTLessEqualStrategyNumber)
+		if (cmptype == COMPARE_LT || cmptype == COMPARE_LE)
 		{
 			/*
 			 * < / <= is supported for monotonically increasing functions in
@@ -2325,8 +2324,7 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
 			break;
 		}
 		/* handle > / >= */
-		else if (strategy == BTGreaterStrategyNumber ||
-				 strategy == BTGreaterEqualStrategyNumber)
+		else if (cmptype == COMPARE_GT || cmptype == COMPARE_GE)
 		{
 			/*
 			 * > / >= is supported for monotonically decreasing functions in
@@ -2343,9 +2341,9 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
 			break;
 		}
 		/* handle = */
-		else if (strategy == BTEqualStrategyNumber)
+		else if (cmptype == COMPARE_EQ)
 		{
-			int16		newstrategy;
+			CompareType newcmptype;
 
 			/*
 			 * When both monotonically increasing and decreasing then the
@@ -2369,19 +2367,20 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
 			 * below the value in the equality condition.
 			 */
 			if (res->monotonic & MONOTONICFUNC_INCREASING)
-				newstrategy = wfunc_left ? BTLessEqualStrategyNumber : BTGreaterEqualStrategyNumber;
+				newcmptype = wfunc_left ? COMPARE_LE : COMPARE_GE;
 			else
-				newstrategy = wfunc_left ? BTGreaterEqualStrategyNumber : BTLessEqualStrategyNumber;
+				newcmptype = wfunc_left ? COMPARE_GE : COMPARE_LE;
 
 			/* We must keep the original equality qual */
 			*keep_original = true;
 			runopexpr = opexpr;
 
 			/* determine the operator to use for the WindowFuncRunCondition */
-			runoperator = get_opfamily_member(opinfo->opfamily_id,
+			runoperator = get_opmethod_member(opinfo->opmethod,
+											  opinfo->opfamily_id,
 											  opinfo->oplefttype,
 											  opinfo->oprighttype,
-											  newstrategy);
+											  newcmptype);
 			break;
 		}
 	}
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 73d78617009..b7f6ab40dec 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -3610,6 +3610,7 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
 		if (opathkey->pk_opfamily != ipathkey->pk_opfamily ||
 			opathkey->pk_eclass->ec_collation != ipathkey->pk_eclass->ec_collation ||
 			opathkey->pk_strategy != ipathkey->pk_strategy ||
+			opathkey->pk_cmptype != ipathkey->pk_cmptype ||
 			opathkey->pk_nulls_first != ipathkey->pk_nulls_first)
 			elog(ERROR, "left and right pathkeys do not match in mergejoin");
 
@@ -4096,7 +4097,10 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
 			cache->collation == pathkey->pk_eclass->ec_collation &&
 			cache->strategy == pathkey->pk_strategy &&
 			cache->nulls_first == pathkey->pk_nulls_first)
+		{
+			Assert(cache->cmptype == pathkey->pk_cmptype);
 			return cache;
+		}
 	}
 
 	/* Nope, do the computation */
@@ -4104,6 +4108,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
 					 (Node *) rinfo->clause,
 					 pathkey->pk_opfamily,
 					 pathkey->pk_strategy,
+					 pathkey->pk_cmptype,
 					 pathkey->pk_nulls_first,
 					 &leftstartsel,
 					 &leftendsel,
@@ -4117,6 +4122,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey)
 	cache->opfamily = pathkey->pk_opfamily;
 	cache->collation = pathkey->pk_eclass->ec_collation;
 	cache->strategy = pathkey->pk_strategy;
+	cache->cmptype = pathkey->pk_cmptype;
 	cache->nulls_first = pathkey->pk_nulls_first;
 	cache->leftstartsel = leftstartsel;
 	cache->leftendsel = leftendsel;
diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c
index 0f9ecf5ee8b..eb68413a321 100644
--- a/src/backend/optimizer/path/equivclass.c
+++ b/src/backend/optimizer/path/equivclass.c
@@ -1788,8 +1788,8 @@ select_equality_operator(EquivalenceClass *ec, Oid lefttype, Oid righttype)
 		Oid			opfamily = lfirst_oid(lc);
 		Oid			opno;
 
-		opno = get_opfamily_member(opfamily, lefttype, righttype,
-								   BTEqualStrategyNumber);
+		opno = get_opmethod_member(InvalidOid, opfamily, lefttype, righttype,
+								   COMPARE_EQ);
 		if (!OidIsValid(opno))
 			continue;
 		/* If no barrier quals in query, don't worry about leaky operators */
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 9202d9a3697..e0971beb1d1 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3526,6 +3526,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
 {
 	IndexClause *iclause = makeNode(IndexClause);
 	RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
+	CompareType op_cmptype;
+	Oid			op_method;
 	int			op_strategy;
 	Oid			op_lefttype;
 	Oid			op_righttype;
@@ -3553,7 +3555,9 @@ expand_indexqual_rowcompare(PlannerInfo *root,
 	}
 
 	get_op_opfamily_properties(expr_op, index->opfamily[indexcol], false,
+							   &op_method,
 							   &op_strategy,
+							   &op_cmptype,
 							   &op_lefttype,
 							   &op_righttype);
 
@@ -3614,7 +3618,9 @@ expand_indexqual_rowcompare(PlannerInfo *root,
 
 		/* Add operator info to lists */
 		get_op_opfamily_properties(expr_op, index->opfamily[i], false,
+								   &op_method,
 								   &op_strategy,
+								   &op_cmptype,
 								   &op_lefttype,
 								   &op_righttype);
 		expr_ops = lappend_oid(expr_ops, expr_op);
@@ -3647,8 +3653,7 @@ expand_indexqual_rowcompare(PlannerInfo *root,
 			/* very easy, just use the commuted operators */
 			new_ops = expr_ops;
 		}
-		else if (op_strategy == BTLessEqualStrategyNumber ||
-				 op_strategy == BTGreaterEqualStrategyNumber)
+		else if (op_cmptype == COMPARE_LE || op_cmptype == COMPARE_GE)
 		{
 			/* easy, just use the same (possibly commuted) operators */
 			new_ops = list_truncate(expr_ops, matching_cols);
@@ -3659,10 +3664,16 @@ expand_indexqual_rowcompare(PlannerInfo *root,
 			ListCell   *lefttypes_cell;
 			ListCell   *righttypes_cell;
 
-			if (op_strategy == BTLessStrategyNumber)
-				op_strategy = BTLessEqualStrategyNumber;
-			else if (op_strategy == BTGreaterStrategyNumber)
-				op_strategy = BTGreaterEqualStrategyNumber;
+			if (op_cmptype == COMPARE_LT)
+			{
+				op_cmptype = COMPARE_LE;
+				op_strategy = IndexAmTranslateCompareType(op_cmptype, op_method, 0, true);  // FIXME
+			}
+			else if (op_cmptype == COMPARE_GT)
+			{
+				op_cmptype = COMPARE_GE;
+				op_strategy = IndexAmTranslateCompareType(op_cmptype, op_method, 0, true);  // FIXME
+			}
 			else
 				elog(ERROR, "unexpected strategy number %d", op_strategy);
 			new_ops = NIL;
@@ -3760,8 +3771,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 
 
 		/* Pathkey must request default sort order for the target opfamily */
-		if (pathkey->pk_strategy != BTLessStrategyNumber ||
-			pathkey->pk_nulls_first)
+		if (pathkey->pk_cmptype != COMPARE_LT || pathkey->pk_nulls_first)
 			return;
 
 		/* If eclass is volatile, no hope of using an indexscan */
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 154eb505d75..a0826ed5008 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -17,7 +17,9 @@
  */
 #include "postgres.h"
 
+#include "access/amapi.h"
 #include "access/stratnum.h"
+#include "catalog/pg_am.h"			/* for BTREE_AM_OID */
 #include "catalog/pg_opfamily.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/cost.h"
@@ -53,18 +55,40 @@ static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
  * merging EquivalenceClasses.
  */
 PathKey *
-make_canonical_pathkey(PlannerInfo *root,
-					   EquivalenceClass *eclass, Oid opfamily,
-					   int strategy, bool nulls_first)
+make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass,
+					   Oid opmethod, Oid opfamily, int strategy,
+					   CompareType cmptype, bool nulls_first)
 {
 	PathKey    *pk;
 	ListCell   *lc;
 	MemoryContext oldcontext;
 
+	Assert(OidIsValid(opmethod));
+	Assert(OidIsValid(opfamily));
+	Assert(opmethod == get_opfamily_method(opfamily));
+
 	/* Can't make canonical pathkeys if the set of ECs might still change */
 	if (!root->ec_merging_done)
 		elog(ERROR, "too soon to build canonical pathkeys");
 
+	/*
+	 * Can't make canonical pathkeys if neither the strategy nor the cmptype
+	 * were supplied.  For callers who only gave us one of them, help out
+	 * by looking up the other.  This simplifies the work the caller needs
+	 * to do and reduces code duplication.
+	 */
+	if (strategy == InvalidStrategy)
+	{
+		Assert(cmptype != COMPARE_INVALID);
+		Assert(OidIsValid(opfamily));
+		strategy = IndexAmTranslateCompareType(cmptype, opmethod, opfamily, false);
+	}
+	else if (cmptype == COMPARE_INVALID)
+	{
+		Assert(OidIsValid(opfamily));
+		cmptype = IndexAmTranslateStrategy(strategy, opmethod, opfamily, false);
+	}
+
 	/* The passed eclass might be non-canonical, so chase up to the top */
 	while (eclass->ec_merged)
 		eclass = eclass->ec_merged;
@@ -75,6 +99,7 @@ make_canonical_pathkey(PlannerInfo *root,
 		if (eclass == pk->pk_eclass &&
 			opfamily == pk->pk_opfamily &&
 			strategy == pk->pk_strategy &&
+			cmptype == pk->pk_cmptype &&
 			nulls_first == pk->pk_nulls_first)
 			return pk;
 	}
@@ -89,6 +114,7 @@ make_canonical_pathkey(PlannerInfo *root,
 	pk->pk_eclass = eclass;
 	pk->pk_opfamily = opfamily;
 	pk->pk_strategy = strategy;
+	pk->pk_cmptype = cmptype;
 	pk->pk_nulls_first = nulls_first;
 
 	root->canon_pathkeys = lappend(root->canon_pathkeys, pk);
@@ -197,6 +223,7 @@ pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys)
 static PathKey *
 make_pathkey_from_sortinfo(PlannerInfo *root,
 						   Expr *expr,
+						   Oid opmethod,
 						   Oid opfamily,
 						   Oid opcintype,
 						   Oid collation,
@@ -206,12 +233,18 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
 						   Relids rel,
 						   bool create_it)
 {
+	CompareType cmptype;
 	int16		strategy;
 	Oid			equality_op;
 	List	   *opfamilies;
 	EquivalenceClass *eclass;
 
-	strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
+	Assert(OidIsValid(opmethod));
+	Assert(OidIsValid(opfamily));
+	Assert(opmethod == get_opfamily_method(opfamily));
+
+	cmptype = reverse_sort ? COMPARE_GT : COMPARE_LT;
+	strategy = IndexAmTranslateCompareType(cmptype, opmethod, opfamily, false);
 
 	/*
 	 * EquivalenceClasses need to contain opfamily lists based on the family
@@ -219,13 +252,14 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
 	 * more than one opfamily.  So we have to look up the opfamily's equality
 	 * operator and get its membership.
 	 */
-	equality_op = get_opfamily_member(opfamily,
+	equality_op = get_opmethod_member(opmethod,
+									  opfamily,
 									  opcintype,
 									  opcintype,
-									  BTEqualStrategyNumber);
+									  COMPARE_EQ);
 	if (!OidIsValid(equality_op))	/* shouldn't happen */
 		elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
-			 BTEqualStrategyNumber, opcintype, opcintype, opfamily);
+			 COMPARE_EQ, opcintype, opcintype, opfamily);
 	opfamilies = get_mergejoin_opfamilies(equality_op);
 	if (!opfamilies)			/* certainly should find some */
 		elog(ERROR, "could not find opfamilies for equality operator %u",
@@ -241,8 +275,8 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
 		return NULL;
 
 	/* And finally we can find or create a PathKey node */
-	return make_canonical_pathkey(root, eclass, opfamily,
-								  strategy, nulls_first);
+	return make_canonical_pathkey(root, eclass, opmethod, opfamily,
+								  strategy, cmptype, nulls_first);
 }
 
 /*
@@ -255,20 +289,21 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
 static PathKey *
 make_pathkey_from_sortop(PlannerInfo *root,
 						 Expr *expr,
+						 Oid opmethodfilter,
 						 Oid ordering_op,
 						 bool reverse_sort,
 						 bool nulls_first,
 						 Index sortref,
 						 bool create_it)
 {
-	Oid			opfamily,
+	Oid			opmethod,
+				opfamily,
 				opcintype,
 				collation;
-	int16		strategy;
 
 	/* Find the operator in pg_amop --- failure shouldn't happen */
-	if (!get_ordering_op_properties(ordering_op,
-									&opfamily, &opcintype, &strategy))
+	if (!get_ordering_op_properties(ordering_op, opmethodfilter, &opmethod,
+									&opfamily, &opcintype, NULL, NULL))
 		elog(ERROR, "operator %u is not a valid ordering operator",
 			 ordering_op);
 
@@ -277,6 +312,7 @@ make_pathkey_from_sortop(PlannerInfo *root,
 
 	return make_pathkey_from_sortinfo(root,
 									  expr,
+									  opmethod,
 									  opfamily,
 									  opcintype,
 									  collation,
@@ -783,6 +819,7 @@ build_index_pathkeys(PlannerInfo *root,
 		 */
 		cpathkey = make_pathkey_from_sortinfo(root,
 											  indexkey,
+											  index->relam,
 											  index->sortopfamily[i],
 											  index->opcintype[i],
 											  index->indexcollations[i],
@@ -942,6 +979,7 @@ build_partition_pathkeys(PlannerInfo *root, RelOptInfo *partrel,
 		 */
 		cpathkey = make_pathkey_from_sortinfo(root,
 											  keyCol,
+											  get_opfamily_method(partscheme->partopfamily[i]),		/* TODO: OPMETHOD */
 											  partscheme->partopfamily[i],
 											  partscheme->partopcintype[i],
 											  partscheme->partcollation[i],
@@ -1004,24 +1042,26 @@ build_expression_pathkey(PlannerInfo *root,
 						 bool create_it)
 {
 	List	   *pathkeys;
-	Oid			opfamily,
+	Oid			opmethod,
+				opfamily,
 				opcintype;
-	int16		strategy;
+	CompareType cmptype;
 	PathKey    *cpathkey;
 
 	/* Find the operator in pg_amop --- failure shouldn't happen */
-	if (!get_ordering_op_properties(opno,
-									&opfamily, &opcintype, &strategy))
+	if (!get_ordering_op_properties(opno, BTREE_AM_OID, &opmethod, &opfamily,
+									&opcintype, NULL, &cmptype))
 		elog(ERROR, "operator %u is not a valid ordering operator",
 			 opno);
 
 	cpathkey = make_pathkey_from_sortinfo(root,
 										  expr,
+										  opmethod,
 										  opfamily,
 										  opcintype,
 										  exprCollation((Node *) expr),
-										  (strategy == BTGreaterStrategyNumber),
-										  (strategy == BTGreaterStrategyNumber),
+										  (cmptype == COMPARE_GT),
+										  (cmptype == COMPARE_GT),
 										  0,
 										  rel,
 										  create_it);
@@ -1117,8 +1157,10 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 					best_pathkey =
 						make_canonical_pathkey(root,
 											   outer_ec,
+											   get_opfamily_method(sub_pathkey->pk_opfamily),
 											   sub_pathkey->pk_opfamily,
 											   sub_pathkey->pk_strategy,
+											   sub_pathkey->pk_cmptype,
 											   sub_pathkey->pk_nulls_first);
 			}
 		}
@@ -1199,8 +1241,10 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
 
 					outer_pk = make_canonical_pathkey(root,
 													  outer_ec,
+													  get_opfamily_method(sub_pathkey->pk_opfamily),
 													  sub_pathkey->pk_opfamily,
 													  sub_pathkey->pk_strategy,
+													  sub_pathkey->pk_cmptype,
 													  sub_pathkey->pk_nulls_first);
 					/* score = # of equivalence peers */
 					score = list_length(outer_ec->ec_members) - 1;
@@ -1333,6 +1377,7 @@ build_join_pathkeys(PlannerInfo *root,
  */
 List *
 make_pathkeys_for_sortclauses(PlannerInfo *root,
+							  Oid opmethodfilter,
 							  List *sortclauses,
 							  List *tlist)
 {
@@ -1340,6 +1385,7 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
 	bool		sortable;
 
 	result = make_pathkeys_for_sortclauses_extended(root,
+													opmethodfilter,
 													&sortclauses,
 													tlist,
 													false,
@@ -1378,6 +1424,7 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
  */
 List *
 make_pathkeys_for_sortclauses_extended(PlannerInfo *root,
+									   Oid opmethodfilter,
 									   List **sortclauses,
 									   List *tlist,
 									   bool remove_redundant,
@@ -1411,6 +1458,7 @@ make_pathkeys_for_sortclauses_extended(PlannerInfo *root,
 		}
 		pathkey = make_pathkey_from_sortop(root,
 										   sortkey,
+										   opmethodfilter,
 										   sortcl->sortop,
 										   sortcl->reverse_sort,
 										   sortcl->nulls_first,
@@ -1815,8 +1863,10 @@ select_outer_pathkeys_for_merge(PlannerInfo *root,
 		scores[best_j] = -1;
 		pathkey = make_canonical_pathkey(root,
 										 ec,
+										 get_opfamily_method(linitial_oid(ec->ec_opfamilies)),
 										 linitial_oid(ec->ec_opfamilies),
-										 BTLessStrategyNumber,
+										 InvalidStrategy,
+										 COMPARE_LT,
 										 false);
 		/* can't be redundant because no duplicate ECs */
 		Assert(!pathkey_is_redundant(pathkey, pathkeys));
@@ -1908,8 +1958,10 @@ make_inner_pathkeys_for_merge(PlannerInfo *root,
 		else
 			pathkey = make_canonical_pathkey(root,
 											 ieclass,
+											 get_opfamily_method(opathkey->pk_opfamily),
 											 opathkey->pk_opfamily,
 											 opathkey->pk_strategy,
+											 opathkey->pk_cmptype,
 											 opathkey->pk_nulls_first);
 
 		/*
@@ -2134,12 +2186,13 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
 			 * want to prefer only one of the two possible directions, and we
 			 * might as well use this one.
 			 */
-			return (pathkey->pk_strategy == query_pathkey->pk_strategy);
+			return (pathkey->pk_strategy == query_pathkey->pk_strategy &&
+					pathkey->pk_cmptype == query_pathkey->pk_cmptype);
 		}
 	}
 
 	/* If no matching ORDER BY request, prefer the ASC direction */
-	return (pathkey->pk_strategy == BTLessStrategyNumber);
+	return (pathkey->pk_cmptype == COMPARE_LT);
 }
 
 /*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 816a2b2a576..6a7a8d68db5 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -19,6 +19,7 @@
 #include <math.h>
 
 #include "access/sysattr.h"
+#include "catalog/pg_am.h"			/* for BTREE_AM_OID */
 #include "catalog/pg_class.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
@@ -1883,7 +1884,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 			 * cross-type operators then the equality operators are the ones
 			 * for the IN clause operators' RHS datatype.
 			 */
-			eqop = get_equality_op_for_ordering_op(sortop, NULL);
+			eqop = get_equality_op_for_ordering_op(sortop, BTREE_AM_OID, NULL);
 			if (!OidIsValid(eqop))	/* shouldn't happen */
 				elog(ERROR, "could not find equality operator for ordering operator %u",
 					 sortop);
@@ -4751,13 +4752,14 @@ create_mergejoin_plan(PlannerInfo *root,
 			elog(ERROR, "left and right pathkeys do not match in mergejoin");
 		if (first_inner_match &&
 			(opathkey->pk_strategy != ipathkey->pk_strategy ||
+			 opathkey->pk_cmptype != ipathkey->pk_cmptype ||
 			 opathkey->pk_nulls_first != ipathkey->pk_nulls_first))
 			elog(ERROR, "left and right pathkeys do not match in mergejoin");
 
 		/* OK, save info for executor */
 		mergefamilies[i] = opathkey->pk_opfamily;
 		mergecollations[i] = opathkey->pk_eclass->ec_collation;
-		mergereversals[i] = (opathkey->pk_strategy == BTGreaterStrategyNumber ? true : false);
+		mergereversals[i] = opathkey->pk_cmptype == COMPARE_GT ? true : false;
 		mergenullsfirst[i] = opathkey->pk_nulls_first;
 		i++;
 	}
@@ -6906,13 +6908,14 @@ make_unique_from_pathkeys(Plan *lefttree, List *pathkeys, int numCols)
 		 * Look up the correct equality operator from the PathKey's slightly
 		 * abstracted representation.
 		 */
-		eqop = get_opfamily_member(pathkey->pk_opfamily,
+		eqop = get_opmethod_member(InvalidOid,
+								   pathkey->pk_opfamily,
 								   pk_datatype,
 								   pk_datatype,
-								   BTEqualStrategyNumber);
+								   COMPARE_EQ);
 		if (!OidIsValid(eqop))	/* should not happen */
 			elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
-				 BTEqualStrategyNumber, pk_datatype, pk_datatype,
+				 COMPARE_EQ, pk_datatype, pk_datatype,
 				 pathkey->pk_opfamily);
 
 		uniqColIdx[keyno] = tle->resno;
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 64605be3178..d98f2ef91fe 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -30,6 +30,7 @@
 
 #include "access/htup_details.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_am.h"			/* for BTREE_AM_OID */
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
@@ -160,7 +161,7 @@ preprocess_minmax_aggregates(PlannerInfo *root)
 		 * We'll need the equality operator that goes with the aggregate's
 		 * ordering operator.
 		 */
-		eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, &reverse);
+		eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, BTREE_AM_OID, &reverse);
 		if (!OidIsValid(eqop))	/* shouldn't happen */
 			elog(ERROR, "could not find equality operator for ordering operator %u",
 				 mminfo->aggsortop);
@@ -485,6 +486,7 @@ minmax_qp_callback(PlannerInfo *root, void *extra)
 
 	root->sort_pathkeys =
 		make_pathkeys_for_sortclauses(root,
+									  BTREE_AM_OID,
 									  root->parse->sortClause,
 									  root->parse->targetList);
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 36ee6dd43de..9527ab83a8d 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -23,6 +23,7 @@
 #include "access/sysattr.h"
 #include "access/table.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
@@ -1459,6 +1460,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction,
 		 */
 		Assert(parse->distinctClause == NIL);
 		root->sort_pathkeys = make_pathkeys_for_sortclauses(root,
+															BTREE_AM_OID,
 															parse->sortClause,
 															root->processed_tlist);
 	}
@@ -3232,7 +3234,9 @@ adjust_group_pathkeys_for_groupagg(PlannerInfo *root)
 			else
 				sortlist = aggref->aggorder;
 
-			pathkeys = make_pathkeys_for_sortclauses(root, sortlist,
+			pathkeys = make_pathkeys_for_sortclauses(root,
+													 BTREE_AM_OID,
+													 sortlist,
 													 aggref->args);
 
 			/*
@@ -3375,6 +3379,7 @@ standard_qp_callback(PlannerInfo *root, void *extra)
 			 */
 			root->group_pathkeys =
 				make_pathkeys_for_sortclauses_extended(root,
+													   BTREE_AM_OID,
 													   &groupClause,
 													   tlist,
 													   false,
@@ -3408,6 +3413,7 @@ standard_qp_callback(PlannerInfo *root, void *extra)
 		 */
 		root->group_pathkeys =
 			make_pathkeys_for_sortclauses_extended(root,
+												   BTREE_AM_OID,
 												   &root->processed_groupClause,
 												   tlist,
 												   true,
@@ -3460,6 +3466,7 @@ standard_qp_callback(PlannerInfo *root, void *extra)
 		root->processed_distinctClause = list_copy(parse->distinctClause);
 		root->distinct_pathkeys =
 			make_pathkeys_for_sortclauses_extended(root,
+												   BTREE_AM_OID,
 												   &root->processed_distinctClause,
 												   tlist,
 												   true,
@@ -3474,6 +3481,7 @@ standard_qp_callback(PlannerInfo *root, void *extra)
 
 	root->sort_pathkeys =
 		make_pathkeys_for_sortclauses(root,
+									  BTREE_AM_OID,
 									  parse->sortClause,
 									  tlist);
 
@@ -3487,6 +3495,7 @@ standard_qp_callback(PlannerInfo *root, void *extra)
 
 		root->setop_pathkeys =
 			make_pathkeys_for_sortclauses_extended(root,
+												   BTREE_AM_OID,
 												   &groupClauses,
 												   tlist,
 												   false,
@@ -6146,6 +6155,7 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
 		bool		sortable;
 
 		window_pathkeys = make_pathkeys_for_sortclauses_extended(root,
+																 BTREE_AM_OID,
 																 &wc->partitionClause,
 																 tlist,
 																 true,
@@ -6168,6 +6178,7 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
 		List	   *orderby_pathkeys;
 
 		orderby_pathkeys = make_pathkeys_for_sortclauses(root,
+														 BTREE_AM_OID,
 														 wc->orderClause,
 														 tlist);
 
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index eab44da65b8..e6d8b7c0a78 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -24,6 +24,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -730,7 +731,9 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
 		{
 			try_sorted = true;
 			/* Determine the pathkeys for sorting by the whole target list */
-			union_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
+			union_pathkeys = make_pathkeys_for_sortclauses(root,
+														   BTREE_AM_OID,
+														   groupList,
 														   tlist);
 
 			root->query_pathkeys = union_pathkeys;
@@ -926,7 +929,10 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
 			/* Try Sort -> Unique on the Append path */
 			if (groupList != NIL)
 				path = (Path *) create_sort_path(root, result_rel, path,
-												 make_pathkeys_for_sortclauses(root, groupList, tlist),
+												 make_pathkeys_for_sortclauses(root,
+																			   BTREE_AM_OID,
+																			   groupList,
+																			   tlist),
 												 -1.0);
 
 			path = (Path *) create_upper_unique_path(root,
@@ -943,7 +949,10 @@ generate_union_paths(SetOperationStmt *op, PlannerInfo *root,
 				path = gpath;
 
 				path = (Path *) create_sort_path(root, result_rel, path,
-												 make_pathkeys_for_sortclauses(root, groupList, tlist),
+												 make_pathkeys_for_sortclauses(root,
+																			   BTREE_AM_OID,
+																			   groupList,
+																			   tlist),
 												 -1.0);
 
 				path = (Path *) create_upper_unique_path(root,
@@ -1075,7 +1084,7 @@ generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
 	if (can_sort)
 	{
 		/* Determine the pathkeys for sorting by the whole target list */
-		nonunion_pathkeys = make_pathkeys_for_sortclauses(root, groupList,
+		nonunion_pathkeys = make_pathkeys_for_sortclauses(root, BTREE_AM_OID, groupList,
 														  tlist);
 
 		root->query_pathkeys = nonunion_pathkeys;
@@ -1196,6 +1205,7 @@ generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
 
 		/* First the left input ... */
 		pathkeys = make_pathkeys_for_sortclauses(root,
+												 BTREE_AM_OID,
 												 groupList,
 												 lpath_tlist);
 		if (pathkeys_contained_in(pathkeys, lpath->pathkeys))
@@ -1218,6 +1228,7 @@ generate_nonunion_paths(SetOperationStmt *op, PlannerInfo *root,
 
 		/* and now the same for the right. */
 		pathkeys = make_pathkeys_for_sortclauses(root,
+												 BTREE_AM_OID,
 												 groupList,
 												 rpath_tlist);
 		if (pathkeys_contained_in(pathkeys, rpath->pathkeys))
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 71abb01f655..a4b0d3465e8 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -382,27 +382,36 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 					{
 						int16		opt = indexRelation->rd_indoption[i];
 						Oid			ltopr;
-						Oid			btopfamily;
-						Oid			btopcintype;
-						int16		btstrategy;
+						Oid			opmethod;
+						Oid			opfamily;
+						Oid			opcintype;
+						CompareType cmptype;
 
 						info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
 						info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
 
-						ltopr = get_opfamily_member(info->opfamily[i],
+						ltopr = get_opmethod_member(info->relam,
+													info->opfamily[i],
 													info->opcintype[i],
 													info->opcintype[i],
-													BTLessStrategyNumber);
+													COMPARE_LT);
 						if (OidIsValid(ltopr) &&
 							get_ordering_op_properties(ltopr,
-													   &btopfamily,
-													   &btopcintype,
-													   &btstrategy) &&
-							btopcintype == info->opcintype[i] &&
-							btstrategy == BTLessStrategyNumber)
+													   info->relam,
+													   &opmethod,
+													   &opfamily,
+													   &opcintype,
+													   NULL,
+													   &cmptype) &&
+							opcintype == info->opcintype[i] &&
+							cmptype == COMPARE_LT)
 						{
 							/* Successful mapping */
-							info->sortopfamily[i] = btopfamily;
+							Assert(info->relam == opmethod);
+							Assert(info->relam == get_opfamily_method(opfamily));
+							info->sortopfamily[i] = opfamily;
+
+							/* TODO: OPMETHOD: store opmethod here? */
 						}
 						else
 						{
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index b76fc81b08d..1aa07ffde7e 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -1613,7 +1613,7 @@ clause_is_strict_for(Node *clause, Node *subexpr, bool allow_false)
  * and in addition we use 6 to represent <>.  <> is not a btree-indexable
  * operator, but we assume here that if an equality operator of a btree
  * opfamily has a negator operator, the negator behaves as <> for the opfamily.
- * (This convention is also known to get_op_btree_interpretation().)
+ * (This convention is also known to get_op_index_interpretation().)
  *
  * BT_implies_table[] and BT_refutes_table[] are used for cases where we have
  * two identical subexpressions and we want to know whether one operator
@@ -2165,20 +2165,20 @@ lookup_proof_cache(Oid pred_op, Oid clause_op, bool refute_it)
 	 * operator.  This can happen in cases with incomplete sets of cross-type
 	 * comparison operators.
 	 */
-	clause_op_infos = get_op_btree_interpretation(clause_op);
+	clause_op_infos = get_op_index_interpretation(clause_op);
 	if (clause_op_infos)
-		pred_op_infos = get_op_btree_interpretation(pred_op);
+		pred_op_infos = get_op_index_interpretation(pred_op);
 	else						/* no point in looking */
 		pred_op_infos = NIL;
 
 	foreach(lcp, pred_op_infos)
 	{
-		OpBtreeInterpretation *pred_op_info = lfirst(lcp);
+		OpIndexInterpretation *pred_op_info = lfirst(lcp);
 		Oid			opfamily_id = pred_op_info->opfamily_id;
 
 		foreach(lcc, clause_op_infos)
 		{
-			OpBtreeInterpretation *clause_op_info = lfirst(lcc);
+			OpIndexInterpretation *clause_op_info = lfirst(lcc);
 			StrategyNumber pred_strategy,
 						clause_strategy,
 						test_strategy;
@@ -2187,7 +2187,8 @@ lookup_proof_cache(Oid pred_op, Oid clause_op, bool refute_it)
 			if (opfamily_id != clause_op_info->opfamily_id)
 				continue;
 			/* Lefttypes should match */
-			Assert(clause_op_info->oplefttype == pred_op_info->oplefttype);
+			if (clause_op_info->oplefttype != pred_op_info->oplefttype)
+				continue;
 
 			pred_strategy = pred_op_info->strategy;
 			clause_strategy = clause_op_info->strategy;
@@ -2221,10 +2222,11 @@ lookup_proof_cache(Oid pred_op, Oid clause_op, bool refute_it)
 			 */
 			if (test_strategy == BTNE)
 			{
-				test_op = get_opfamily_member(opfamily_id,
+				test_op = get_opmethod_member(InvalidOid,
+											  opfamily_id,
 											  pred_op_info->oprighttype,
 											  clause_op_info->oprighttype,
-											  BTEqualStrategyNumber);
+											  COMPARE_EQ);
 				if (OidIsValid(test_op))
 					test_op = get_negator(test_op);
 			}
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 2e64fcae7b2..7fee19dd1b6 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -2915,7 +2915,6 @@ transformWindowDefinitions(ParseState *pstate,
 		{
 			SortGroupClause *sortcl;
 			Node	   *sortkey;
-			int16		rangestrategy;
 
 			if (list_length(wc->orderClause) != 1)
 				ereport(ERROR,
@@ -2926,9 +2925,12 @@ transformWindowDefinitions(ParseState *pstate,
 			sortkey = get_sortgroupclause_expr(sortcl, *targetlist);
 			/* Find the sort operator in pg_amop */
 			if (!get_ordering_op_properties(sortcl->sortop,
+											BTREE_AM_OID,
+											NULL,
 											&rangeopfamily,
 											&rangeopcintype,
-											&rangestrategy))
+											NULL,
+											NULL))
 				elog(ERROR, "operator %u is not a valid ordering operator",
 					 sortcl->sortop);
 			/* Record properties of sort ordering */
@@ -3455,7 +3457,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 			 * equality operator, and determine whether to consider it like
 			 * ASC or DESC.
 			 */
-			eqop = get_equality_op_for_ordering_op(sortop, &reverse);
+			eqop = get_equality_op_for_ordering_op(sortop, BTREE_AM_OID, &reverse);
 			if (!OidIsValid(eqop))
 				ereport(ERROR,
 						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index bad1df732ea..2f04db30efb 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2902,7 +2902,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
 		Bitmapset  *this_strats;
 		ListCell   *j;
 
-		opinfo_lists[i] = get_op_btree_interpretation(opno);
+		opinfo_lists[i] = get_op_index_interpretation(opno);
 
 		/*
 		 * convert strategy numbers into a Bitmapset to make the intersection
@@ -2911,7 +2911,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
 		this_strats = NULL;
 		foreach(j, opinfo_lists[i])
 		{
-			OpBtreeInterpretation *opinfo = lfirst(j);
+			OpIndexInterpretation *opinfo = lfirst(j);
 
 			this_strats = bms_add_member(this_strats, opinfo->strategy);
 		}
@@ -2961,7 +2961,7 @@ make_row_comparison_op(ParseState *pstate, List *opname,
 
 		foreach(j, opinfo_lists[i])
 		{
-			OpBtreeInterpretation *opinfo = lfirst(j);
+			OpIndexInterpretation *opinfo = lfirst(j);
 
 			if (opinfo->strategy == cmptype)
 			{
diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c
index 48a35f763e9..f55648efb78 100644
--- a/src/backend/partitioning/partprune.c
+++ b/src/backend/partitioning/partprune.c
@@ -1454,7 +1454,9 @@ gen_prune_steps_from_opexps(GeneratePruningStepsContext *context,
 				get_op_opfamily_properties(pc->opno,
 										   part_scheme->partopfamily[i],
 										   false,
+										   NULL,		/* don't need opmethod */
 										   &pc->op_strategy,
+										   NULL,		/* don't need cmptype */
 										   &lefttype,
 										   &righttype);
 
@@ -1982,7 +1984,10 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
 		if (op_in_opfamily(opno, partopfamily))
 		{
 			get_op_opfamily_properties(opno, partopfamily, false,
-									   &op_strategy, &op_lefttype,
+									   NULL,		/* don't need opmethod */
+									   &op_strategy,
+									   NULL,		/* don't need cmptype */
+									   &op_lefttype,
 									   &op_righttype);
 		}
 		else
@@ -1996,7 +2001,10 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
 			if (OidIsValid(negator) && op_in_opfamily(negator, partopfamily))
 			{
 				get_op_opfamily_properties(negator, partopfamily, false,
-										   &op_strategy, &op_lefttype,
+										   NULL,		/* don't need opmethod */
+										   &op_strategy,
+										   NULL,		/* don't need cmptype */
+										   &op_lefttype,
 										   &op_righttype);
 				if (op_strategy == BTEqualStrategyNumber)
 					is_opne_listp = true;	/* bingo */
@@ -2211,7 +2219,10 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context,
 							righttype;
 
 				get_op_opfamily_properties(negator, partopfamily,
-										   false, &strategy,
+										   false,
+										   NULL,		/* don't need opmethod */
+										   &strategy,
+										   NULL,		/* don't need cmptype */
 										   &lefttype, &righttype);
 				if (strategy != BTEqualStrategyNumber)
 					return PARTCLAUSE_NOMATCH;
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 450dacd031c..eedc72e7b1e 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -1114,15 +1114,15 @@ match_network_subset(Node *leftop,
 	 */
 	if (is_eq)
 	{
-		opr1oid = get_opfamily_member(opfamily, datatype, datatype,
-									  BTGreaterEqualStrategyNumber);
+		opr1oid = get_opmethod_member(InvalidOid, opfamily, datatype, datatype,
+									  COMPARE_GE);
 		if (opr1oid == InvalidOid)
 			elog(ERROR, "no >= operator for opfamily %u", opfamily);
 	}
 	else
 	{
-		opr1oid = get_opfamily_member(opfamily, datatype, datatype,
-									  BTGreaterStrategyNumber);
+		opr1oid = get_opmethod_member(InvalidOid, opfamily, datatype, datatype,
+									  COMPARE_GT);
 		if (opr1oid == InvalidOid)
 			elog(ERROR, "no > operator for opfamily %u", opfamily);
 	}
@@ -1140,8 +1140,8 @@ match_network_subset(Node *leftop,
 
 	/* create clause "key <= network_scan_last( rightopval )" */
 
-	opr2oid = get_opfamily_member(opfamily, datatype, datatype,
-								  BTLessEqualStrategyNumber);
+	opr2oid = get_opmethod_member(InvalidOid, opfamily, datatype, datatype,
+								  COMPARE_LE);
 	if (opr2oid == InvalidOid)
 		elog(ERROR, "no <= operator for opfamily %u", opfamily);
 
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c2918c9c831..7a832c3767e 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -2953,8 +2953,8 @@ scalargejoinsel(PG_FUNCTION_ARGS)
  *		*rightstart, *rightend similarly for the right-hand variable.
  */
 void
-mergejoinscansel(PlannerInfo *root, Node *clause,
-				 Oid opfamily, int strategy, bool nulls_first,
+mergejoinscansel(PlannerInfo *root, Node *clause, Oid opfamily, int strategy,
+				 CompareType cmptype, bool nulls_first,
 				 Selectivity *leftstart, Selectivity *leftend,
 				 Selectivity *rightstart, Selectivity *rightend)
 {
@@ -3003,7 +3003,9 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
 
 	/* Extract the operator's declared left/right datatypes */
 	get_op_opfamily_properties(opno, opfamily, false,
+							   NULL,		/* don't need opmethod */
 							   &op_strategy,
+							   NULL,		/* don't need cmptype */
 							   &op_lefttype,
 							   &op_righttype);
 	Assert(op_strategy == BTEqualStrategyNumber);
@@ -3015,19 +3017,17 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
 	 * Note: we expect that pg_statistic histograms will be sorted by the '<'
 	 * operator, regardless of which sort direction we are considering.
 	 */
-	switch (strategy)
+	switch (cmptype)
 	{
-		case BTLessStrategyNumber:
+		case COMPARE_LT:
 			isgt = false;
 			if (op_lefttype == op_righttype)
 			{
 				/* easy case */
-				ltop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTLessStrategyNumber);
-				leop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTLessEqualStrategyNumber);
+				ltop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+										   op_righttype, COMPARE_LT);
+				leop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+										   op_righttype, COMPARE_LE);
 				lsortop = ltop;
 				rsortop = ltop;
 				lstatop = lsortop;
@@ -3037,75 +3037,61 @@ mergejoinscansel(PlannerInfo *root, Node *clause,
 			}
 			else
 			{
-				ltop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTLessStrategyNumber);
-				leop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTLessEqualStrategyNumber);
-				lsortop = get_opfamily_member(opfamily,
-											  op_lefttype, op_lefttype,
-											  BTLessStrategyNumber);
-				rsortop = get_opfamily_member(opfamily,
-											  op_righttype, op_righttype,
-											  BTLessStrategyNumber);
+				ltop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+										   op_righttype, COMPARE_LT);
+				leop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+										   op_righttype, COMPARE_LE);
+				lsortop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+											  op_lefttype, COMPARE_LT);
+				rsortop = get_opmethod_member(InvalidOid, opfamily, op_righttype,
+											  op_righttype, COMPARE_LT);
 				lstatop = lsortop;
 				rstatop = rsortop;
-				revltop = get_opfamily_member(opfamily,
-											  op_righttype, op_lefttype,
-											  BTLessStrategyNumber);
-				revleop = get_opfamily_member(opfamily,
-											  op_righttype, op_lefttype,
-											  BTLessEqualStrategyNumber);
+				revltop = get_opmethod_member(InvalidOid, opfamily, op_righttype,
+											  op_lefttype, COMPARE_LT);
+				revleop = get_opmethod_member(InvalidOid, opfamily, op_righttype,
+											  op_lefttype, COMPARE_LE);
 			}
 			break;
-		case BTGreaterStrategyNumber:
+		case COMPARE_GT:
 			/* descending-order case */
 			isgt = true;
 			if (op_lefttype == op_righttype)
 			{
 				/* easy case */
-				ltop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTGreaterStrategyNumber);
-				leop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTGreaterEqualStrategyNumber);
+				ltop = get_opmethod_member(InvalidOid,
+										   opfamily, op_lefttype, op_righttype,
+										   COMPARE_GT);
+				leop = get_opmethod_member(InvalidOid,
+										   opfamily, op_lefttype, op_righttype,
+										   COMPARE_GE);
 				lsortop = ltop;
 				rsortop = ltop;
-				lstatop = get_opfamily_member(opfamily,
-											  op_lefttype, op_lefttype,
-											  BTLessStrategyNumber);
+				lstatop = get_opmethod_member(InvalidOid,
+											  opfamily, op_lefttype, op_lefttype,
+											  COMPARE_LT);
 				rstatop = lstatop;
 				revltop = ltop;
 				revleop = leop;
 			}
 			else
 			{
-				ltop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTGreaterStrategyNumber);
-				leop = get_opfamily_member(opfamily,
-										   op_lefttype, op_righttype,
-										   BTGreaterEqualStrategyNumber);
-				lsortop = get_opfamily_member(opfamily,
-											  op_lefttype, op_lefttype,
-											  BTGreaterStrategyNumber);
-				rsortop = get_opfamily_member(opfamily,
-											  op_righttype, op_righttype,
-											  BTGreaterStrategyNumber);
-				lstatop = get_opfamily_member(opfamily,
-											  op_lefttype, op_lefttype,
-											  BTLessStrategyNumber);
-				rstatop = get_opfamily_member(opfamily,
-											  op_righttype, op_righttype,
-											  BTLessStrategyNumber);
-				revltop = get_opfamily_member(opfamily,
-											  op_righttype, op_lefttype,
-											  BTGreaterStrategyNumber);
-				revleop = get_opfamily_member(opfamily,
-											  op_righttype, op_lefttype,
-											  BTGreaterEqualStrategyNumber);
+				ltop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+										   op_righttype, COMPARE_GT);
+				leop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+										   op_righttype, COMPARE_GE);
+				lsortop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+											  op_lefttype, COMPARE_GT);
+				rsortop = get_opmethod_member(InvalidOid, opfamily, op_righttype,
+											  op_righttype, COMPARE_GT);
+				lstatop = get_opmethod_member(InvalidOid, opfamily, op_lefttype,
+											  op_lefttype, COMPARE_LT);
+				rstatop = get_opmethod_member(InvalidOid, opfamily, op_righttype,
+											  op_righttype, COMPARE_LT);
+				revltop = get_opmethod_member(InvalidOid, opfamily, op_righttype,
+											  op_lefttype, COMPARE_GT);
+				revleop = get_opmethod_member(InvalidOid, opfamily, op_righttype,
+											  op_lefttype, COMPARE_GE);
 			}
 			break;
 		default:
@@ -7135,10 +7121,11 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
 		Oid			sortop;
 		AttStatsSlot sslot;
 
-		sortop = get_opfamily_member(index->opfamily[0],
+		sortop = get_opmethod_member(InvalidOid,
+									 index->opfamily[0],
 									 index->opcintype[0],
 									 index->opcintype[0],
-									 BTLessStrategyNumber);
+									 COMPARE_LT);
 		if (OidIsValid(sortop) &&
 			get_attstatsslot(&sslot, vardata.statsTuple,
 							 STATISTIC_KIND_CORRELATION, sortop,
@@ -7369,7 +7356,10 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	 * find a matching pg_amop entry.)
 	 */
 	get_op_opfamily_properties(clause_op, index->opfamily[indexcol], false,
-							   &strategy_op, &lefttype, &righttype);
+							   NULL,		/* don't need opmethod */
+							   &strategy_op,
+							   NULL,		/* don't need cmptype */
+							   &lefttype, &righttype);
 
 	/*
 	 * GIN always uses the "default" support functions, which are those with
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 7bd476f3de7..83b180a37dd 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -32,6 +32,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_opfamily.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_publication.h"
 #include "catalog/pg_range.h"
@@ -135,7 +136,9 @@ get_op_opfamily_sortfamily(Oid opno, Oid opfamily)
  */
 void
 get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op,
+						   Oid *opmethod,
 						   int *strategy,
+						   CompareType *cmptype,
 						   Oid *lefttype,
 						   Oid *righttype)
 {
@@ -150,12 +153,45 @@ get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op,
 		elog(ERROR, "operator %u is not a member of opfamily %u",
 			 opno, opfamily);
 	amop_tup = (Form_pg_amop) GETSTRUCT(tp);
-	*strategy = amop_tup->amopstrategy;
-	*lefttype = amop_tup->amoplefttype;
-	*righttype = amop_tup->amoprighttype;
+	if (opmethod)
+		*opmethod = amop_tup->amopmethod;
+	if (strategy)
+		*strategy = amop_tup->amopstrategy;
+	if (cmptype)
+		*cmptype = IndexAmTranslateStrategy(amop_tup->amopstrategy,
+											amop_tup->amopmethod,
+											opfamily,
+											true);
+	if (lefttype)
+		*lefttype = amop_tup->amoplefttype;
+	if (righttype)
+		*righttype = amop_tup->amoprighttype;
 	ReleaseSysCache(tp);
 }
 
+/*
+ * get_opmethod_member
+ *		Get the OID of the operator that implements the specified row
+ *		comparison with the specified datatypes for the specified opfamily.
+ *
+ * Returns InvalidOid if there is no pg_amop entry for the given keys.
+ */
+Oid
+get_opmethod_member(Oid opmethod, Oid opfamily, Oid lefttype, Oid righttype,
+					CompareType cmptype)
+{
+	StrategyNumber	strategy;
+
+	if (!OidIsValid(opmethod))
+	{
+		Assert(OidIsValid(opfamily));
+		opmethod = get_opfamily_method(opfamily);
+	}
+
+	strategy = IndexAmTranslateCompareType(cmptype, opmethod, opfamily, false);
+	return get_opfamily_member(opfamily, lefttype, righttype, strategy);
+}
+
 /*
  * get_opfamily_member
  *		Get the OID of the operator that implements the specified strategy
@@ -186,9 +222,9 @@ get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
 
 /*
  * get_ordering_op_properties
- *		Given the OID of an ordering operator (a btree "<" or ">" operator),
- *		determine its opfamily, its declared input datatype, and its
- *		strategy number (BTLessStrategyNumber or BTGreaterStrategyNumber).
+ *		Given the OID of an ordering operator (a "<" or ">" operator),
+ *		determine its opmethod, its opfamily, its declared input datatype, its
+ *		strategy number, and its row comparison type.
  *
  * Returns true if successful, false if no matching pg_amop entry exists.
  * (This indicates that the operator is not a valid ordering operator.)
@@ -205,17 +241,26 @@ get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
  * additional effort on ensuring consistency.
  */
 bool
-get_ordering_op_properties(Oid opno,
-						   Oid *opfamily, Oid *opcintype, int16 *strategy)
+get_ordering_op_properties(Oid opno, Oid opmethodfilter,
+						   Oid *opmethod, Oid *opfamily,
+						   Oid *opcintype, int16 *strategy,
+						   CompareType *cmptype)
 {
 	bool		result = false;
 	CatCList   *catlist;
 	int			i;
 
 	/* ensure outputs are initialized on failure */
-	*opfamily = InvalidOid;
-	*opcintype = InvalidOid;
-	*strategy = 0;
+	if (opmethod)
+		*opmethod = InvalidOid;
+	if (opfamily)
+		*opfamily = InvalidOid;
+	if (opcintype)
+		*opcintype = InvalidOid;
+	if (strategy)
+		*strategy = InvalidStrategy;
+	if (cmptype)
+		*cmptype = COMPARE_INVALID;
 
 	/*
 	 * Search pg_amop to see if the target operator is registered as the "<"
@@ -227,21 +272,36 @@ get_ordering_op_properties(Oid opno,
 	{
 		HeapTuple	tuple = &catlist->members[i]->tuple;
 		Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+		CompareType am_cmptype;
 
-		/* must be btree */
-		if (aform->amopmethod != BTREE_AM_OID)
+		/* must be acceptable to our opmethod filter */
+		if (OidIsValid(opmethodfilter) && aform->amopmethod != opmethodfilter)
 			continue;
 
-		if (aform->amopstrategy == BTLessStrategyNumber ||
-			aform->amopstrategy == BTGreaterStrategyNumber)
+		am_cmptype = IndexAmTranslateStrategy(aform->amopstrategy,
+											  aform->amopmethod,
+											  aform->amopfamily,
+											  true);
+
+		if (am_cmptype == COMPARE_LT || am_cmptype == COMPARE_GT)
 		{
 			/* Found it ... should have consistent input types */
 			if (aform->amoplefttype == aform->amoprighttype)
 			{
 				/* Found a suitable opfamily, return info */
-				*opfamily = aform->amopfamily;
-				*opcintype = aform->amoplefttype;
-				*strategy = aform->amopstrategy;
+				if (opmethod)
+					*opmethod = aform->amopmethod;
+				if (opfamily)
+					*opfamily = aform->amopfamily;
+				if (opcintype)
+					*opcintype = aform->amoplefttype;
+				if (strategy)
+					*strategy = aform->amopstrategy;
+				if (cmptype)
+					*cmptype = IndexAmTranslateStrategy(aform->amopstrategy,
+														aform->amopmethod,
+														aform->amopfamily,
+														false);
 				result = true;
 				break;
 			}
@@ -265,24 +325,27 @@ get_ordering_op_properties(Oid opno,
  * (This indicates that the operator is not a valid ordering operator.)
  */
 Oid
-get_equality_op_for_ordering_op(Oid opno, bool *reverse)
+get_equality_op_for_ordering_op(Oid opno, Oid opmethodfilter, bool *reverse)
 {
 	Oid			result = InvalidOid;
+	Oid			opmethod;
 	Oid			opfamily;
 	Oid			opcintype;
 	int16		strategy;
+	CompareType cmptype;
 
 	/* Find the operator in pg_amop */
-	if (get_ordering_op_properties(opno,
-								   &opfamily, &opcintype, &strategy))
+	if (get_ordering_op_properties(opno, opmethodfilter, &opmethod, &opfamily,
+								   &opcintype, &strategy, &cmptype))
 	{
 		/* Found a suitable opfamily, get matching equality operator */
-		result = get_opfamily_member(opfamily,
+		result = get_opmethod_member(opmethod,
+									 opfamily,
 									 opcintype,
 									 opcintype,
-									 BTEqualStrategyNumber);
+									 COMPARE_EQ);
 		if (reverse)
-			*reverse = (strategy == BTGreaterStrategyNumber);
+			*reverse = (cmptype == COMPARE_GT);
 	}
 
 	return result;
@@ -330,9 +393,10 @@ get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type)
 			Oid			typid;
 
 			typid = use_lhs_type ? aform->amoplefttype : aform->amoprighttype;
-			result = get_opfamily_member(aform->amopfamily,
+			result = get_opmethod_member(InvalidOid,
+										 aform->amopfamily,
 										 typid, typid,
-										 BTLessStrategyNumber);
+										 COMPARE_LT);
 			if (OidIsValid(result))
 				break;
 			/* failure probably shouldn't happen, but keep looking if so */
@@ -589,20 +653,20 @@ get_op_hash_functions(Oid opno,
 }
 
 /*
- * get_op_btree_interpretation
+ * get_op_index_interpretation
  *		Given an operator's OID, find out which btree opfamilies it belongs to,
  *		and what properties it has within each one.  The results are returned
- *		as a palloc'd list of OpBtreeInterpretation structs.
+ *		as a palloc'd list of OpIndexInterpretation structs.
  *
  * In addition to the normal btree operators, we consider a <> operator to be
  * a "member" of an opfamily if its negator is an equality operator of the
  * opfamily.  COMPARE_NE is returned as the strategy number for this case.
  */
 List *
-get_op_btree_interpretation(Oid opno)
+get_op_index_interpretation(Oid opno)
 {
 	List	   *result = NIL;
-	OpBtreeInterpretation *thisresult;
+	OpIndexInterpretation *thisresult;
 	CatCList   *catlist;
 	int			i;
 
@@ -625,10 +689,15 @@ get_op_btree_interpretation(Oid opno)
 		op_strategy = (StrategyNumber) op_form->amopstrategy;
 		Assert(op_strategy >= 1 && op_strategy <= 5);
 
-		thisresult = (OpBtreeInterpretation *)
-			palloc(sizeof(OpBtreeInterpretation));
+		thisresult = (OpIndexInterpretation *)
+			palloc(sizeof(OpIndexInterpretation));
+		thisresult->opmethod = op_form->amopmethod;
 		thisresult->opfamily_id = op_form->amopfamily;
 		thisresult->strategy = op_strategy;
+		thisresult->cmptype = IndexAmTranslateStrategy(thisresult->strategy,
+													   thisresult->opmethod,
+													   thisresult->opfamily_id,
+													   false);
 		thisresult->oplefttype = op_form->amoplefttype;
 		thisresult->oprighttype = op_form->amoprighttype;
 		result = lappend(result, thisresult);
@@ -668,8 +737,8 @@ get_op_btree_interpretation(Oid opno)
 					continue;
 
 				/* OK, report it with "strategy" COMPARE_NE */
-				thisresult = (OpBtreeInterpretation *)
-					palloc(sizeof(OpBtreeInterpretation));
+				thisresult = (OpIndexInterpretation *)
+					palloc(sizeof(OpIndexInterpretation));
 				thisresult->opfamily_id = op_form->amopfamily;
 				thisresult->strategy = COMPARE_NE;
 				thisresult->oplefttype = op_form->amoplefttype;
@@ -1180,6 +1249,30 @@ get_language_name(Oid langoid, bool missing_ok)
 	return NULL;
 }
 
+/*				---------- OPFAMILY CACHE ----------						 */
+
+/*
+ * get_opfamily_method
+ *
+ *		Returns the OID of the operator method the opfamily belongs to.
+ */
+Oid
+get_opfamily_method(Oid opfamily)
+{
+	HeapTuple	tp;
+	Form_pg_opfamily opf_tup;
+	Oid			result;
+
+	tp = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamily));
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for opfamily %u", opfamily);
+	opf_tup = (Form_pg_opfamily) GETSTRUCT(tp);
+
+	result = opf_tup->opfmethod;
+	ReleaseSysCache(tp);
+	return result;
+}
+
 /*				---------- OPCLASS CACHE ----------						 */
 
 /*
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 5a3b3788d02..92a780afa35 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -633,10 +633,11 @@ lookup_type_cache(Oid type_id, int flags)
 		Oid			eq_opr = InvalidOid;
 
 		if (typentry->btree_opf != InvalidOid)
-			eq_opr = get_opfamily_member(typentry->btree_opf,
+			eq_opr = get_opmethod_member(InvalidOid,
+										 typentry->btree_opf,
 										 typentry->btree_opintype,
 										 typentry->btree_opintype,
-										 BTEqualStrategyNumber);
+										 COMPARE_EQ);
 		if (eq_opr == InvalidOid &&
 			typentry->hash_opf != InvalidOid)
 			eq_opr = get_opfamily_member(typentry->hash_opf,
@@ -680,10 +681,11 @@ lookup_type_cache(Oid type_id, int flags)
 		Oid			lt_opr = InvalidOid;
 
 		if (typentry->btree_opf != InvalidOid)
-			lt_opr = get_opfamily_member(typentry->btree_opf,
+			lt_opr = get_opmethod_member(InvalidOid,
+										 typentry->btree_opf,
 										 typentry->btree_opintype,
 										 typentry->btree_opintype,
-										 BTLessStrategyNumber);
+										 COMPARE_LT);
 
 		/*
 		 * As above, make sure array_cmp or record_cmp will succeed; but again
@@ -705,10 +707,11 @@ lookup_type_cache(Oid type_id, int flags)
 		Oid			gt_opr = InvalidOid;
 
 		if (typentry->btree_opf != InvalidOid)
-			gt_opr = get_opfamily_member(typentry->btree_opf,
+			gt_opr = get_opmethod_member(InvalidOid,
+										 typentry->btree_opf,
 										 typentry->btree_opintype,
 										 typentry->btree_opintype,
-										 BTGreaterStrategyNumber);
+										 COMPARE_GT);
 
 		/*
 		 * As above, make sure array_cmp or record_cmp will succeed; but again
diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c
index 9b855be690e..8bbbf4c4578 100644
--- a/src/backend/utils/sort/sortsupport.c
+++ b/src/backend/utils/sort/sortsupport.c
@@ -135,17 +135,18 @@ PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
 {
 	Oid			opfamily;
 	Oid			opcintype;
-	int16		strategy;
+	CompareType cmptype;
 
 	Assert(ssup->comparator == NULL);
 
 	/* Find the operator in pg_amop */
-	if (!get_ordering_op_properties(orderingOp, &opfamily, &opcintype,
-									&strategy))
+	if (!get_ordering_op_properties(orderingOp, BTREE_AM_OID, NULL, &opfamily,
+									&opcintype, NULL, &cmptype))
 		elog(ERROR, "operator %u is not a valid ordering operator",
 			 orderingOp);
-	ssup->ssup_reverse = (strategy == BTGreaterStrategyNumber);
+	ssup->ssup_reverse = (cmptype == COMPARE_GT);
 
+	/* TODO: OPMETHOD: pass into FinishSortSupportFunction? */
 	FinishSortSupportFunction(opfamily, opcintype, ssup);
 }
 
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index fbf05322c75..45a1f760f3b 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1501,8 +1501,9 @@ typedef struct PathKey
 
 	/* the value that is ordered */
 	EquivalenceClass *pk_eclass pg_node_attr(copy_as_scalar, equal_as_scalar);
-	Oid			pk_opfamily;	/* btree opfamily defining the ordering */
-	int			pk_strategy;	/* sort direction (ASC or DESC) */
+	Oid			pk_opfamily;	/* index opfamily defining the ordering */
+	int			pk_strategy;	/* sort direction as opmethod specific strategy */
+	CompareType pk_cmptype;	/* sort direction (COMPARE_LT or COMPARE_GT) */
 	bool		pk_nulls_first; /* do NULLs come before normal values? */
 } PathKey;
 
@@ -2767,9 +2768,10 @@ typedef struct RestrictInfo
 typedef struct MergeScanSelCache
 {
 	/* Ordering details (cache lookup key) */
-	Oid			opfamily;		/* btree opfamily defining the ordering */
+	Oid			opfamily;		/* index opfamily defining the ordering */
 	Oid			collation;		/* collation for the ordering */
-	int			strategy;		/* sort direction (ASC or DESC) */
+	int			strategy;		/* sort direction as opmethod specific strategy */
+	CompareType cmptype;		/* sort direction (COMPARE_LT or COMPARE_GT) */
 	bool		nulls_first;	/* do NULLs come before normal values? */
 	/* Results */
 	Selectivity leftstartsel;	/* first-join fraction for clause left side */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index bc5dfd7db41..79407df1781 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -238,9 +238,11 @@ extern List *build_join_pathkeys(PlannerInfo *root,
 								 JoinType jointype,
 								 List *outer_pathkeys);
 extern List *make_pathkeys_for_sortclauses(PlannerInfo *root,
+										   Oid opmethodfilter,
 										   List *sortclauses,
 										   List *tlist);
 extern List *make_pathkeys_for_sortclauses_extended(PlannerInfo *root,
+													Oid opmethodfilter,
 													List **sortclauses,
 													List *tlist,
 													bool remove_redundant,
@@ -269,8 +271,10 @@ extern List *truncate_useless_pathkeys(PlannerInfo *root,
 extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel);
 extern List *append_pathkeys(List *target, List *source);
 extern PathKey *make_canonical_pathkey(PlannerInfo *root,
-									   EquivalenceClass *eclass, Oid opfamily,
-									   int strategy, bool nulls_first);
+									   EquivalenceClass *eclass, Oid opmethod,
+									   Oid opfamily, int strategy,
+									   CompareType cmptype,
+									   bool nulls_first);
 extern void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel,
 									List *live_childrels);
 
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 6fab7aa6009..912f82176ea 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -14,20 +14,23 @@
 #define LSYSCACHE_H
 
 #include "access/attnum.h"
+#include "access/cmptype.h"
 #include "access/htup.h"
 #include "nodes/pg_list.h"
 
 /* avoid including subscripting.h here */
 struct SubscriptRoutines;
 
-/* Result list element for get_op_btree_interpretation */
-typedef struct OpBtreeInterpretation
+/* Result list element for get_op_index_interpretation */
+typedef struct OpIndexInterpretation
 {
+	Oid			opmethod;		/* index access method of opfamily */
 	Oid			opfamily_id;	/* btree opfamily containing operator */
 	int			strategy;		/* its strategy number */
+	CompareType cmptype;		/* its generic row comparison type */
 	Oid			oplefttype;		/* declared left input datatype */
 	Oid			oprighttype;	/* declared right input datatype */
-} OpBtreeInterpretation;
+} OpIndexInterpretation;
 
 /* I/O function selector for get_type_io_data */
 typedef enum IOFuncSelector
@@ -68,22 +71,31 @@ extern PGDLLIMPORT get_attavgwidth_hook_type get_attavgwidth_hook;
 extern bool op_in_opfamily(Oid opno, Oid opfamily);
 extern int	get_op_opfamily_strategy(Oid opno, Oid opfamily);
 extern Oid	get_op_opfamily_sortfamily(Oid opno, Oid opfamily);
-extern void get_op_opfamily_properties(Oid opno, Oid opfamily, bool ordering_op,
+extern void get_op_opfamily_properties(Oid opno,
+									   Oid opfamily,
+									   bool ordering_op,
+									   Oid *opmethod,
 									   int *strategy,
+									   CompareType *cmptype,
 									   Oid *lefttype,
 									   Oid *righttype);
+extern Oid	get_opmethod_member(Oid opmethod, Oid opfamily, Oid lefttype,
+								Oid righttype, CompareType cmptype);
 extern Oid	get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
 								int16 strategy);
-extern bool get_ordering_op_properties(Oid opno,
-									   Oid *opfamily, Oid *opcintype, int16 *strategy);
-extern Oid	get_equality_op_for_ordering_op(Oid opno, bool *reverse);
+extern bool get_ordering_op_properties(Oid opno, Oid opmethodfilter,
+									   Oid *opmethod, Oid *opfamily,
+									   Oid *opcintype, int16 *strategy,
+									   CompareType *cmptype);
+extern Oid	get_equality_op_for_ordering_op(Oid opno, Oid opmethodfilter,
+											bool *reverse);
 extern Oid	get_ordering_op_for_equality_op(Oid opno, bool use_lhs_type);
 extern List *get_mergejoin_opfamilies(Oid opno);
 extern bool get_compatible_hash_operators(Oid opno,
 										  Oid *lhs_opno, Oid *rhs_opno);
 extern bool get_op_hash_functions(Oid opno,
 								  RegProcedure *lhs_procno, RegProcedure *rhs_procno);
-extern List *get_op_btree_interpretation(Oid opno);
+extern List *get_op_index_interpretation(Oid opno);
 extern bool equality_ops_are_compatible(Oid opno1, Oid opno2);
 extern bool comparison_ops_are_compatible(Oid opno1, Oid opno2);
 extern Oid	get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype,
@@ -103,6 +115,7 @@ extern Oid	get_constraint_index(Oid conoid);
 extern char get_constraint_type(Oid conoid);
 
 extern char *get_language_name(Oid langoid, bool missing_ok);
+extern Oid	get_opfamily_method(Oid opfamily);
 extern Oid	get_opclass_family(Oid opclass);
 extern Oid	get_opclass_input_type(Oid opclass);
 extern bool get_opclass_opfamily_and_input_type(Oid opclass,
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index d35939651f3..ba4e0e9f97c 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -209,10 +209,11 @@ extern Selectivity rowcomparesel(PlannerInfo *root,
 								 RowCompareExpr *clause,
 								 int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo);
 
-extern void mergejoinscansel(PlannerInfo *root, Node *clause,
-							 Oid opfamily, int strategy, bool nulls_first,
-							 Selectivity *leftstart, Selectivity *leftend,
-							 Selectivity *rightstart, Selectivity *rightend);
+extern void mergejoinscansel(PlannerInfo *root, Node *clause, Oid opfamily,
+							 int strategy, CompareType cmptype,
+							 bool nulls_first, Selectivity *leftstart,
+							 Selectivity *leftend, Selectivity *rightstart,
+							 Selectivity *rightend);
 
 extern double estimate_num_groups(PlannerInfo *root, List *groupExprs,
 								  double input_rows, List **pgset,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index e3e09a2207e..3698a975921 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1764,11 +1764,11 @@ OnConflictAction
 OnConflictClause
 OnConflictExpr
 OnConflictSetState
-OpBtreeInterpretation
 OpClassCacheEnt
 OpExpr
 OpFamilyMember
 OpFamilyOpFuncGroup
+OpIndexInterpretation
 OpclassInfo
 Operator
 OperatorElement
-- 
2.48.1