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! */