From 42d50af82b7d721729b0ee0839c3b9d3f3f5cb75 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 6836129c90..916734f675 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 cc4135e394..422ad047da 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? */
@@ -924,7 +929,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 1087a9011e..2010e4d40f 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -251,6 +251,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 6d05e7bdcd..2d894bae9a 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -44,6 +44,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 943ae91019..d5e4bdcf71 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -66,6 +66,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 fa5b59a150..3aa03083a3 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 696d79c085..4425432fac 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -102,6 +102,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 5b5e6e82d3..8f3dab831d 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 32c6a8bbdc..4c4ca3c7a7 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3084,6 +3084,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);
@@ -3112,20 +3116,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 2c6c307efc..df9bc9b374 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -223,6 +223,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 b9713ec9aa..e0300ff56f 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1173,6 +1173,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.43.0

