From 2cf1738c6c7a614783fbdb411c09063ed13f7e01 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 11 Jan 2019 15:51:28 +0300
Subject: [PATCH 2/7] Introduce ammatchorderby function

---
 contrib/bloom/blutils.c               |   2 +-
 src/backend/access/brin/brin.c        |   2 +-
 src/backend/access/gin/ginutil.c      |   2 +-
 src/backend/access/gist/gist.c        |   3 +-
 src/backend/access/hash/hash.c        |   2 +-
 src/backend/access/nbtree/nbtree.c    |   2 +-
 src/backend/access/spgist/spgutils.c  |   6 +-
 src/backend/commands/opclasscmds.c    |   2 +-
 src/backend/executor/nodeIndexscan.c  |   4 +-
 src/backend/optimizer/path/indxpath.c | 192 ++++++++++++++++++++--------------
 src/backend/optimizer/util/plancat.c  |   2 +-
 src/backend/utils/adt/amutils.c       |   2 +-
 src/include/access/amapi.h            |  14 ++-
 src/include/nodes/plannodes.h         |   2 +-
 src/include/nodes/relation.h          |   8 +-
 src/include/optimizer/paths.h         |   4 +
 16 files changed, 148 insertions(+), 101 deletions(-)

diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6458376..9bff793 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,7 +109,6 @@ blhandler(PG_FUNCTION_ARGS)
 	amroutine->amstrategies = BLOOM_NSTRATEGIES;
 	amroutine->amsupport = BLOOM_NPROC;
 	amroutine->amcanorder = false;
-	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
@@ -143,6 +142,7 @@ blhandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->ammatchorderby = NULL;
 
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 03c8a6b..d1d2b0f 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -86,7 +86,6 @@ brinhandler(PG_FUNCTION_ARGS)
 	amroutine->amstrategies = 0;
 	amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
 	amroutine->amcanorder = false;
-	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
@@ -120,6 +119,7 @@ brinhandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->ammatchorderby = NULL;
 
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index afc2023..0f6714d 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,7 +41,6 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amstrategies = 0;
 	amroutine->amsupport = GINNProcs;
 	amroutine->amcanorder = false;
-	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
@@ -75,6 +74,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->ammatchorderby = NULL;
 
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index b75b3a8..77ca187 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -18,6 +18,7 @@
 #include "access/gistscan.h"
 #include "catalog/pg_collation.h"
 #include "miscadmin.h"
+#include "optimizer/paths.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
 #include "nodes/execnodes.h"
@@ -64,7 +65,6 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amstrategies = 0;
 	amroutine->amsupport = GISTNProcs;
 	amroutine->amcanorder = false;
-	amroutine->amcanorderbyop = true;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
@@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->ammatchorderby = match_orderbyop_pathkeys;
 
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index f1f01a0..bb5c6a1 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,7 +59,6 @@ hashhandler(PG_FUNCTION_ARGS)
 	amroutine->amstrategies = HTMaxStrategyNumber;
 	amroutine->amsupport = HASHNProcs;
 	amroutine->amcanorder = false;
-	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = true;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = false;
@@ -93,6 +92,7 @@ hashhandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->ammatchorderby = NULL;
 
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 98917de..c56ee5e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -110,7 +110,6 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->amstrategies = BTMaxStrategyNumber;
 	amroutine->amsupport = BTNProcs;
 	amroutine->amcanorder = true;
-	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = true;
 	amroutine->amcanunique = true;
 	amroutine->amcanmulticol = true;
@@ -144,6 +143,7 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = btestimateparallelscan;
 	amroutine->aminitparallelscan = btinitparallelscan;
 	amroutine->amparallelrescan = btparallelrescan;
+	amroutine->ammatchorderby = NULL;
 
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index de147d7..37de33c 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -32,10 +32,6 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
-extern Expr *spgcanorderbyop(IndexOptInfo *index,
-				PathKey *pathkey, int pathkeyno,
-				Expr *orderby_clause, int *indexcol_p);
-
 /*
  * SP-GiST handler function: return IndexAmRoutine with access method parameters
  * and callbacks.
@@ -48,7 +44,6 @@ spghandler(PG_FUNCTION_ARGS)
 	amroutine->amstrategies = 0;
 	amroutine->amsupport = SPGISTNProc;
 	amroutine->amcanorder = false;
-	amroutine->amcanorderbyop = true;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = false;
@@ -82,6 +77,7 @@ spghandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->ammatchorderby = match_orderbyop_pathkeys;
 
 	PG_RETURN_POINTER(amroutine);
 }
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 5a16045..4f670e7 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -1098,7 +1098,7 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 		 */
 		IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
 
-		if (!amroutine->amcanorderbyop)
+		if (!amroutine->ammatchorderby)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("access method \"%s\" does not support ordering operators",
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index f255551..9115c16 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -199,7 +199,7 @@ IndexNextWithReorder(IndexScanState *node)
 	 * with just Asserting here because the system will not try to run the
 	 * plan backwards if ExecSupportsBackwardScan() says it won't work.
 	 * Currently, that is guaranteed because no index AMs support both
-	 * amcanorderbyop and amcanbackward; if any ever do,
+	 * ammatchorderby and amcanbackward; if any ever do,
 	 * ExecSupportsBackwardScan() will need to consider indexorderbys
 	 * explicitly.
 	 */
@@ -1149,7 +1149,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
  * 5. NullTest ("indexkey IS NULL/IS NOT NULL").  We just fill in the
  * ScanKey properly.
  *
- * This code is also used to prepare ORDER BY expressions for amcanorderbyop
+ * This code is also used to prepare ORDER BY expressions for ammatchorderby
  * indexes.  The behavior is exactly the same, except that we have to look up
  * the operator differently.  Note that only cases 1 and 2 are currently
  * possible for ORDER BY.
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index f8e674c..2bf87ad 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -17,6 +17,7 @@
 
 #include <math.h>
 
+#include "access/amapi.h"
 #include "access/stratnum.h"
 #include "access/sysattr.h"
 #include "catalog/pg_am.h"
@@ -156,6 +157,10 @@ static void match_clause_to_index(IndexOptInfo *index,
 static bool match_clause_to_indexcol(IndexOptInfo *index,
 						 int indexcol,
 						 RestrictInfo *rinfo);
+static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
+							int indexcol,
+							Expr *clause,
+							Oid pk_opfamily);
 static bool is_indexable_operator(Oid expr_op, Oid opfamily,
 					  bool indexkey_on_left);
 static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
@@ -166,8 +171,6 @@ static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
 static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
 						List **clause_columns_p);
-static Expr *match_clause_to_ordering_op(IndexOptInfo *index,
-							int indexcol, Expr *clause, Oid pk_opfamily);
 static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
 						   EquivalenceClass *ec, EquivalenceMember *em,
 						   void *arg);
@@ -1000,7 +1003,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		orderbyclauses = NIL;
 		orderbyclausecols = NIL;
 	}
-	else if (index->amcanorderbyop && pathkeys_possibly_useful)
+	else if (index->ammatchorderby && pathkeys_possibly_useful)
 	{
 		/* see if we can generate ordering operators for query_pathkeys */
 		match_pathkeys_to_index(index, root->query_pathkeys,
@@ -2576,6 +2579,99 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
 	return false;
 }
 
+Expr *
+match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey, int *indexcol_p)
+{
+	ListCell   *lc;
+
+	/* Pathkey must request default sort order for the target opfamily */
+	if (pathkey->pk_strategy != BTLessStrategyNumber ||
+		pathkey->pk_nulls_first)
+		return NULL;
+
+	/* If eclass is volatile, no hope of using an indexscan */
+	if (pathkey->pk_eclass->ec_has_volatile)
+		return NULL;
+
+	/*
+	 * Try to match eclass member expression(s) to index.  Note that child
+	 * EC members are considered, but only when they belong to the target
+	 * relation.  (Unlike regular members, the same expression could be a
+	 * child member of more than one EC.  Therefore, the same index could
+	 * be considered to match more than one pathkey list, which is OK
+	 * here.  See also get_eclass_for_sort_expr.)
+	 */
+	foreach(lc, pathkey->pk_eclass->ec_members)
+	{
+		EquivalenceMember *member = castNode(EquivalenceMember, lfirst(lc));
+		int			indexcol;
+		int			indexcol_min;
+		int			indexcol_max;
+
+		/* No possibility of match if it references other relations */
+		if (!bms_equal(member->em_relids, index->rel->relids))
+			continue;
+
+		/* If *indexcol_p is non-negative then try to match only to it */
+		if (*indexcol_p >= 0)
+		{
+			indexcol_min = *indexcol_p;
+			indexcol_max = *indexcol_p + 1;
+		}
+		else	/* try to match all columns */
+		{
+			indexcol_min = 0;
+			indexcol_max = index->ncolumns;
+		}
+
+		/*
+		 * We allow any column of the GiST index to match each pathkey;
+		 * they don't have to match left-to-right as you might expect.
+		 */
+		for (indexcol = indexcol_min; indexcol < indexcol_max; indexcol++)
+		{
+			Expr	   *expr = match_clause_to_ordering_op(index,
+														   indexcol,
+														   member->em_expr,
+														   pathkey->pk_opfamily);
+			if (expr)
+			{
+				*indexcol_p = indexcol;
+				return expr;	/* don't want to look at remaining members */
+			}
+		}
+	}
+
+	return NULL;
+}
+
+bool
+match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+						 List **orderby_clauses_p, List **clause_columns_p)
+{
+	ListCell   *lc;
+
+	foreach(lc, pathkeys)
+	{
+		PathKey	   *pathkey = castNode(PathKey, lfirst(lc));
+		Expr	   *expr;
+		int			indexcol = -1;	/* match all index columns */
+
+		expr = match_orderbyop_pathkey(index, pathkey, &indexcol);
+
+		/*
+		 * Note: for any failure to match, we just return NIL immediately.
+		 * There is no value in matching just some of the pathkeys.
+		 */
+		if (!expr)
+			return false;
+
+		*orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+		*clause_columns_p = lappend_int(*clause_columns_p, indexcol);
+	}
+
+	return true;	/* success */
+}
 
 /****************************************************************************
  *				----  ROUTINES TO CHECK ORDERING OPERATORS	----
@@ -2601,86 +2697,24 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 {
 	List	   *orderby_clauses = NIL;
 	List	   *clause_columns = NIL;
-	ListCell   *lc1;
+	ammatchorderby_function ammatchorderby =
+			(ammatchorderby_function) index->ammatchorderby;
 
-	*orderby_clauses_p = NIL;	/* set default results */
-	*clause_columns_p = NIL;
-
-	/* Only indexes with the amcanorderbyop property are interesting here */
-	if (!index->amcanorderbyop)
-		return;
-
-	foreach(lc1, pathkeys)
+	/* Only indexes with the ammatchorderby function are interesting here */
+	if (ammatchorderby &&
+		ammatchorderby(index, pathkeys, &orderby_clauses, &clause_columns))
 	{
-		PathKey    *pathkey = (PathKey *) lfirst(lc1);
-		bool		found = false;
-		ListCell   *lc2;
-
-		/*
-		 * Note: for any failure to match, we just return NIL immediately.
-		 * There is no value in matching just some of the pathkeys.
-		 */
-
-		/* Pathkey must request default sort order for the target opfamily */
-		if (pathkey->pk_strategy != BTLessStrategyNumber ||
-			pathkey->pk_nulls_first)
-			return;
+		Assert(list_length(pathkeys) == list_length(orderby_clauses));
+		Assert(list_length(pathkeys) == list_length(clause_columns));
 
-		/* If eclass is volatile, no hope of using an indexscan */
-		if (pathkey->pk_eclass->ec_has_volatile)
-			return;
-
-		/*
-		 * Try to match eclass member expression(s) to index.  Note that child
-		 * EC members are considered, but only when they belong to the target
-		 * relation.  (Unlike regular members, the same expression could be a
-		 * child member of more than one EC.  Therefore, the same index could
-		 * be considered to match more than one pathkey list, which is OK
-		 * here.  See also get_eclass_for_sort_expr.)
-		 */
-		foreach(lc2, pathkey->pk_eclass->ec_members)
-		{
-			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
-			int			indexcol;
-
-			/* No possibility of match if it references other relations */
-			if (!bms_equal(member->em_relids, index->rel->relids))
-				continue;
-
-			/*
-			 * We allow any column of the index to match each pathkey; they
-			 * don't have to match left-to-right as you might expect.  This is
-			 * correct for GiST, which is the sole existing AM supporting
-			 * amcanorderbyop.  We might need different logic in future for
-			 * other implementations.
-			 */
-			for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
-			{
-				Expr	   *expr;
-
-				expr = match_clause_to_ordering_op(index,
-												   indexcol,
-												   member->em_expr,
-												   pathkey->pk_opfamily);
-				if (expr)
-				{
-					orderby_clauses = lappend(orderby_clauses, expr);
-					clause_columns = lappend_int(clause_columns, indexcol);
-					found = true;
-					break;
-				}
-			}
-
-			if (found)			/* don't want to look at remaining members */
-				break;
-		}
-
-		if (!found)				/* fail if no match for this pathkey */
-			return;
+		*orderby_clauses_p = orderby_clauses;	/* success! */
+		*clause_columns_p = clause_columns;
+	}
+	else
+	{
+		*orderby_clauses_p = NIL;	/* set default results */
+		*clause_columns_p = NIL;
 	}
-
-	*orderby_clauses_p = orderby_clauses;	/* success! */
-	*clause_columns_p = clause_columns;
 }
 
 /*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 48ffc5f..9c30709 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -265,7 +265,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 			/* We copy just the fields we need, not all of rd_amroutine */
 			amroutine = indexRelation->rd_amroutine;
-			info->amcanorderbyop = amroutine->amcanorderbyop;
+			info->ammatchorderby = amroutine->ammatchorderby;
 			info->amoptionalkey = amroutine->amoptionalkey;
 			info->amsearcharray = amroutine->amsearcharray;
 			info->amsearchnulls = amroutine->amsearchnulls;
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 060ffe5..3907065 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -298,7 +298,7 @@ indexam_property(FunctionCallInfo fcinfo,
 				 * a nonkey column, and null otherwise (meaning we don't
 				 * know).
 				 */
-				if (!iskey || !routine->amcanorderbyop)
+				if (!iskey || !routine->ammatchorderby)
 				{
 					res = false;
 					isnull = false;
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 653ddc9..09411a7 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -21,6 +21,9 @@
  */
 struct PlannerInfo;
 struct IndexPath;
+struct IndexOptInfo;
+struct PathKey;
+struct Expr;
 
 /* Likewise, this file shouldn't depend on execnodes.h. */
 struct IndexInfo;
@@ -140,6 +143,12 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
 /* restore marked scan position */
 typedef void (*amrestrpos_function) (IndexScanDesc scan);
 
+/* does AM support ORDER BY result of an operator on indexed column? */
+typedef bool (*ammatchorderby_function) (struct IndexOptInfo *index,
+										 List *pathkeys,
+										 List **orderby_clauses_p,
+										 List **clause_columns_p);
+
 /*
  * Callback function signatures - for parallel index scans.
  */
@@ -170,8 +179,6 @@ typedef struct IndexAmRoutine
 	uint16		amsupport;
 	/* does AM support ORDER BY indexed column's value? */
 	bool		amcanorder;
-	/* does AM support ORDER BY result of an operator on indexed column? */
-	bool		amcanorderbyop;
 	/* does AM support backward scanning? */
 	bool		amcanbackward;
 	/* does AM support UNIQUE indexes? */
@@ -221,7 +228,8 @@ typedef struct IndexAmRoutine
 	amendscan_function amendscan;
 	ammarkpos_function ammarkpos;	/* can be NULL */
 	amrestrpos_function amrestrpos; /* can be NULL */
-
+	ammatchorderby_function ammatchorderby; /* can be NULL */
+	
 	/* interface functions to support parallel index scans */
 	amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
 	aminitparallelscan_function aminitparallelscan; /* can be NULL */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6d087c2..bd6ce97 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -388,7 +388,7 @@ typedef struct SampleScan
  * indexorderbyops is a list of the OIDs of the operators used to sort the
  * ORDER BY expressions.  This is used together with indexorderbyorig to
  * recheck ordering at run time.  (Note that indexorderby, indexorderbyorig,
- * and indexorderbyops are used for amcanorderbyop cases, not amcanorder.)
+ * and indexorderbyops are used for ammatchorderby cases, not amcanorder.)
  *
  * indexorderdir specifies the scan ordering, for indexscans on amcanorder
  * indexes (for other indexes it should be "don't care").
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 3430061..af6963f 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -804,7 +804,6 @@ typedef struct IndexOptInfo
 	bool		hypothetical;	/* true if index doesn't really exist */
 
 	/* Remaining fields are copied from the index AM's API struct: */
-	bool		amcanorderbyop; /* does AM support order by operator result? */
 	bool		amoptionalkey;	/* can query omit key for the first column? */
 	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
 	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
@@ -813,6 +812,11 @@ typedef struct IndexOptInfo
 	bool		amcanparallel;	/* does AM support parallel scan? */
 	/* Rather than include amapi.h here, we declare amcostestimate like this */
 	void		(*amcostestimate) ();	/* AM's cost estimator */
+								/* AM order-by match function */
+	bool		(*ammatchorderby) (struct IndexOptInfo *index,
+								   List *pathkeys,
+								   List **orderby_clauses_p,
+								   List **clause_columns_p);
 } IndexOptInfo;
 
 /*
@@ -1136,7 +1140,7 @@ typedef struct Path
  * (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.
+ * been found to be usable as ordering operators for an ammatchorderby 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
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 666217c..718182b 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -87,6 +87,10 @@ extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
 							int indexcol,
 							List **indexcolnos,
 							bool *var_on_left_p);
+extern Expr *match_orderbyop_pathkey(IndexOptInfo *index, PathKey *pathkey,
+									 int *indexcol_p);
+extern bool match_orderbyop_pathkeys(IndexOptInfo *index, List *pathkeys,
+						 List **orderby_clauses_p, List **clause_columns_p);
 
 /*
  * tidpath.h
-- 
2.7.4

