Current version of postgresql don't support incremental sort using
ordered scan on access method.

Example database:

CREATE TABLE t (id serial, p point, PRIMARY KEY(id));
INSERT INTO t (SELECT generate_series(1, 10000000), point(random(), random()));
CREATE INDEX p_idx ON t USING gist(p);
ANALYZE;

Now i want closest points to center:

SELECT id, p <-> point(0.5, 0.5) dist FROM t ORDER BY dist LIMIT 10;

Everything works good (Execution Time: 0.276 ms).

Now i want predictable sorting for points with same distance:

SELECT id, p <-> point(0.5, 0.5) dist FROM t ORDER BY dist, id LIMIT 10;

Execution time is now 1 000 x slower (589.486 ms) and postgresql uses
full sort istead of incremental:

Sort (cost=205818.51..216235.18 rows=4166667 width=12)

Postgres allows incremental sort only for ordered indexes. Function
build_index_paths dont build partial order paths for access methods
with order support. My patch adds support for incremental ordering
with access method. Results with patch:

Incremental Sort (cost=5522.10..1241841.02 rows=10000000 width=12)
(actual time=0.404..0.405 rows=10 loops=1)
  Sort Key: ((p <-> '(0.5,0.5)'::point)), id
  Presorted Key: ((p <-> '(0.5,0.5)'::point))

Execution Time: 0.437 ms
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 9f4698f2a2..c580d657cf 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -186,7 +186,8 @@ static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
 												bool var_on_left);
 static void match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 									List **orderby_clauses_p,
-									List **clause_columns_p);
+									List **clause_columns_p,
+									int *match_pathkeys_length_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,
@@ -853,6 +854,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	bool		index_is_ordered;
 	bool		index_only_scan;
 	int			indexcol;
+	int			match_pathkeys_length;
 
 	/*
 	 * Check that index supports the desired scan type(s)
@@ -977,9 +979,14 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		/* see if we can generate ordering operators for query_pathkeys */
 		match_pathkeys_to_index(index, root->query_pathkeys,
 								&orderbyclauses,
-								&orderbyclausecols);
-		if (orderbyclauses)
-			useful_pathkeys = root->query_pathkeys;
+								&orderbyclausecols,
+								&match_pathkeys_length);
+		if (orderbyclauses) {
+			if (match_pathkeys_length == list_length(root->query_pathkeys))
+				useful_pathkeys = root->query_pathkeys;
+			else
+				useful_pathkeys = list_truncate(list_copy(root->query_pathkeys), match_pathkeys_length);
+		}
 		else
 			useful_pathkeys = NIL;
 	}
@@ -3065,7 +3072,8 @@ expand_indexqual_rowcompare(PlannerInfo *root,
 static void
 match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 						List **orderby_clauses_p,
-						List **clause_columns_p)
+						List **clause_columns_p,
+						int *match_pathkeys_length_p)
 {
 	List	   *orderby_clauses = NIL;
 	List	   *clause_columns = NIL;
@@ -3073,6 +3081,7 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 
 	*orderby_clauses_p = NIL;	/* set default results */
 	*clause_columns_p = NIL;
+	*match_pathkeys_length_p = 0;
 
 	/* Only indexes with the amcanorderbyop property are interesting here */
 	if (!index->amcanorderbyop)
@@ -3092,11 +3101,11 @@ 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)
-			return;
+			break;
 
 		/* If eclass is volatile, no hope of using an indexscan */
 		if (pathkey->pk_eclass->ec_has_volatile)
-			return;
+			break;
 
 		/*
 		 * Try to match eclass member expression(s) to index.  Note that child
@@ -3140,12 +3149,14 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
 				}
 			}
 
-			if (found)			/* don't want to look at remaining members */
+			if (found) {		/* don't want to look at remaining members */
+				(*match_pathkeys_length_p)++;
 				break;
+			}
 		}
 
 		if (!found)				/* fail if no match for this pathkey */
-			return;
+			break;
 	}
 
 	*orderby_clauses_p = orderby_clauses;	/* success! */

Reply via email to