From 240dde791039ef5bac163e453da4daa1c382dd94 Mon Sep 17 00:00:00 2001
From: "Anton A. Melnikov" <a.melnikov@postgrespro.ru>
Date: Sun, 31 Dec 2023 14:10:23 +0300
Subject: [PATCH 1/5] Introduce IndexAmRoutine.ammorderbyopfirstcol

Currently IndexAmRoutine.amcanorderbyop property means that index access method
supports "column op const" ordering for every indexed column.  Upcoming
knn-btree supports it only for the first column.  In future we will probably
need to a callback to check whether particular ordering is supported.  But now
there are no potential use-cases around.  So, don't overengineer it and leave
with just boolean property.

Discussion: https://postgr.es/m/ce35e97b-cf34-3f5d-6b99-2c25bae49999%40postgrespro.ru
Author: Nikita Glukhov
Reviewed-by: Robert Haas, Tom Lane, Anastasia Lubennikova, Alexander Korotkov
---
 contrib/bloom/blutils.c               |  1 +
 doc/src/sgml/indexam.sgml             |  9 ++++++++-
 src/backend/access/brin/brin.c        |  1 +
 src/backend/access/gin/ginutil.c      |  1 +
 src/backend/access/gist/gist.c        |  1 +
 src/backend/access/hash/hash.c        |  1 +
 src/backend/access/nbtree/nbtree.c    |  1 +
 src/backend/access/spgist/spgutils.c  |  1 +
 src/backend/optimizer/path/indxpath.c | 22 +++++++++++++++-------
 src/include/access/amapi.h            |  6 ++++++
 src/include/nodes/pathnodes.h         |  2 ++
 11 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 6836129c90d..916734f675c 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -112,6 +112,7 @@ blhandler(PG_FUNCTION_ARGS)
 	amroutine->amoptsprocnum = BLOOM_OPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
+	amroutine->amorderbyopfirstcol = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index e3c1539a1e3..a69135cb780 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -103,6 +103,11 @@ typedef struct IndexAmRoutine
     bool        amcanorder;
     /* does AM support ORDER BY result of an operator on indexed column? */
     bool        amcanorderbyop;
+    /*
+     * Does AM support only the one ORDER BY operator on first indexed column?
+     * amcanorderbyop is implied.
+     */
+    bool        amorderbyopfirstcol;
     /* does AM support backward scanning? */
     bool        amcanbackward;
     /* does AM support UNIQUE indexes? */
@@ -932,7 +937,9 @@ amparallelrescan (IndexScanDesc scan);
        an order satisfying <literal>ORDER BY</literal> <replaceable>index_key</replaceable>
        <replaceable>operator</replaceable> <replaceable>constant</replaceable>.  Scan modifiers
        of that form can be passed to <function>amrescan</function> as described
-       previously.
+       previously.  If the access method supports the only one ORDER BY operator
+       on the first indexed column, then it should set
+       <structfield>amorderbyopfirstcol</structfield> to true.
       </para>
      </listitem>
     </itemizedlist>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 6467bed604a..c371f726979 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -253,6 +253,7 @@ brinhandler(PG_FUNCTION_ARGS)
 	amroutine->amoptsprocnum = BRIN_PROCNUM_OPTIONS;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
+	amroutine->amorderbyopfirstcol = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 5747ae6a4ca..e9536fd9238 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -43,6 +43,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amoptsprocnum = GIN_OPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
+	amroutine->amorderbyopfirstcol = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index ed4ffa63a77..f6ed5023ef0 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -65,6 +65,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amoptsprocnum = GIST_OPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = true;
+	amroutine->amorderbyopfirstcol = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = true;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 01d06b7c328..5a090907bb3 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -63,6 +63,7 @@ hashhandler(PG_FUNCTION_ARGS)
 	amroutine->amoptsprocnum = HASHOPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
+	amroutine->amorderbyopfirstcol = false;
 	amroutine->amcanbackward = true;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = false;
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 686a3206f72..2d65adb22f7 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -107,6 +107,7 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->amoptsprocnum = BTOPTIONS_PROC;
 	amroutine->amcanorder = true;
 	amroutine->amcanorderbyop = false;
+	amroutine->amorderbyopfirstcol = false;
 	amroutine->amcanbackward = true;
 	amroutine->amcanunique = true;
 	amroutine->amcanmulticol = true;
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 76b80146ff0..2ba8cbc966b 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -50,6 +50,7 @@ spghandler(PG_FUNCTION_ARGS)
 	amroutine->amoptsprocnum = SPGIST_OPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = true;
+	amroutine->amorderbyopfirstcol = false;
 	amroutine->amcanbackward = false;
 	amroutine->amcanunique = false;
 	amroutine->amcanmulticol = false;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c0fcc7d78df..c9685a86196 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3030,6 +3030,10 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 	if (!index->amcanorderbyop)
 		return;
 
+	/* Only the one pathkey is supported when amorderbyopfirstcol is true */
+	if (index->amorderbyopfirstcol && list_length(pathkeys) != 1)
+		return;
+
 	foreach(lc1, pathkeys)
 	{
 		PathKey    *pathkey = (PathKey *) lfirst(lc1);
@@ -3058,20 +3062,24 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 		{
 			EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2);
 			int			indexcol;
+			int			ncolumns;
 
 			/* 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, and it doesn't matter for SP-GiST because
-			 * that doesn't handle multiple columns anyway, and no other
-			 * existing AMs support amcanorderbyop.  We might need different
-			 * logic in future for other implementations.
+			 * We allow any column or only the first 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, and it doesn't matter
+			 * for SP-GiST and B-Tree because they do not handle multiple
+			 * columns anyway, and no other existing AMs support
+			 * amcanorderbyop.  We might need different logic in future for
+			 * other implementations.
 			 */
-			for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
+			ncolumns = index->amorderbyopfirstcol ? 1 : index->nkeycolumns;
+
+			for (indexcol = 0; indexcol < ncolumns; indexcol++)
 			{
 				Expr	   *expr;
 
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index f25c9d58a7d..21b5d3149f8 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -224,6 +224,12 @@ typedef struct IndexAmRoutine
 	bool		amcanorder;
 	/* does AM support ORDER BY result of an operator on indexed column? */
 	bool		amcanorderbyop;
+
+	/*
+	 * Does AM support only the one ORDER BY operator on first indexed column?
+	 * amcanorderbyop is implied.
+	 */
+	bool		amorderbyopfirstcol;
 	/* does AM support backward scanning? */
 	bool		amcanbackward;
 	/* does AM support UNIQUE indexes? */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 14ccfc1ac1c..b7267f3ecfd 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1188,6 +1188,8 @@ struct IndexOptInfo
 	 * (IndexAmRoutine).  These fields are not set for partitioned indexes.
 	 */
 	bool		amcanorderbyop;
+	bool		amorderbyopfirstcol;	/* order by op is supported only on
+										 * first column? */
 	bool		amoptionalkey;
 	bool		amsearcharray;
 	bool		amsearchnulls;
-- 
2.45.2

