po 17. 4. 2023 o 15:26 Richard Guo <[email protected]> napísal(a):
>
>
> On Sun, Apr 16, 2023 at 1:20 AM Miroslav Bendik <[email protected]>
> wrote:
>>
>> 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.
>
>
> I think this is a meaningful optimization. I reviewed the patch and
> here are the comments from me.
>
> * I understand the new param 'match_pathkeys_length_p' is used to tell
> how many presorted keys are useful. I think list_length(orderbyclauses)
> will do the same. So there is no need to add the new param, thus we can
> reduce the code diffs.
>
> * Now that match_pathkeys_to_index() returns a prefix of the pathkeys
> rather than returns NIL immediately when there is a failure to match, it
> seems the two local variables 'orderby_clauses' and 'clause_columns' are
> not necessary any more. I think we can instead lappend the matched
> 'expr' and 'indexcol' to '*orderby_clauses_p' and '*clause_columns_p'
> directly. In this way we can still call 'return' when we come to a
> failure to match.
>
> * In build_index_paths(), I think the diff can be reduced to
>
> - if (orderbyclauses)
> - useful_pathkeys = root->query_pathkeys;
> - else
> - useful_pathkeys = NIL;
> + useful_pathkeys = list_truncate(list_copy(root->query_pathkeys),
> + list_length(orderbyclauses));
>
> * Several comments in match_pathkeys_to_index() are out of date. We
> need to revise them to cope with the change.
>
> * I think it's better to provide a test case.
>
> Thanks
> Richard
Thank you for advice,
here is an updated patch with proposed changes.
--
Best regards
Miroslav
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index b8000da56d..202779fb10 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -864,8 +864,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
List *index_clauses;
Relids outer_relids;
double loop_count;
- List *orderbyclauses;
- List *orderbyclausecols;
+ List *orderbyclauses = NIL;
+ List *orderbyclausecols = NIL;
List *index_pathkeys;
List *useful_pathkeys;
bool found_lower_saop_clause;
@@ -1001,10 +1001,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
match_pathkeys_to_index(index, root->query_pathkeys,
&orderbyclauses,
&orderbyclausecols);
- if (orderbyclauses)
- useful_pathkeys = root->query_pathkeys;
- else
- useful_pathkeys = NIL;
+ useful_pathkeys = list_truncate(list_copy(root->query_pathkeys),
+ list_length(orderbyclauses));
}
else
{
@@ -3071,16 +3069,14 @@ expand_indexqual_rowcompare(PlannerInfo *root,
* index column numbers (zero based) that each clause would be used with.
* NIL lists are returned if the ordering is not achievable this way.
*
- * On success, the result list is ordered by pathkeys, and in fact is
- * one-to-one with the requested pathkeys.
+ * On success, the result list is ordered by pathkeys. Result can be shorter
+ * than pathkeys on partial prefix match.
*/
static void
match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
List **orderby_clauses_p,
List **clause_columns_p)
{
- List *orderby_clauses = NIL;
- List *clause_columns = NIL;
ListCell *lc1;
*orderby_clauses_p = NIL; /* set default results */
@@ -3104,11 +3100,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
@@ -3145,8 +3141,8 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
pathkey->pk_opfamily);
if (expr)
{
- orderby_clauses = lappend(orderby_clauses, expr);
- clause_columns = lappend_int(clause_columns, indexcol);
+ *orderby_clauses_p = lappend(*orderby_clauses_p, expr);
+ *clause_columns_p = lappend_int(*clause_columns_p, indexcol);
found = true;
break;
}
@@ -3156,12 +3152,9 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
break;
}
- if (!found) /* fail if no match for this pathkey */
+ if (!found) /* end after first not matching expression */
return;
}
-
- *orderby_clauses_p = orderby_clauses; /* success! */
- *clause_columns_p = clause_columns;
}
/*
diff --git a/src/test/regress/expected/incremental_sort.out b/src/test/regress/expected/incremental_sort.out
index 0a631124c2..dcf2c0762d 100644
--- a/src/test/regress/expected/incremental_sort.out
+++ b/src/test/regress/expected/incremental_sort.out
@@ -1672,3 +1672,33 @@ order by 1, 2;
-> Function Scan on generate_series
(7 rows)
+-- Incremental sort for not ordered indexes, but with AM order operator
+set enable_incremental_sort = on;
+-- table with GiST index supports closest points retrieval,
+-- but has not sorted index
+create table t (a point, b int);
+create index on t using gist(a);
+insert into t select point(mod(i, 7), mod(i, 11)), i from generate_series(1, 1000) s(i);
+analyze t;
+explain (costs off) select a, b, a <-> point(5, 5) dist from t order by dist, b limit 1;
+ QUERY PLAN
+-------------------------------------------------
+ Limit
+ -> Incremental Sort
+ Sort Key: ((a <-> '(5,5)'::point)), b
+ Presorted Key: ((a <-> '(5,5)'::point))
+ -> Index Scan using t_a_idx on t
+ Order By: (a <-> '(5,5)'::point)
+(6 rows)
+
+explain (costs off) select a, b, a <-> point(5, 5) dist from t order by dist, b desc limit 1;
+ QUERY PLAN
+----------------------------------------------------
+ Limit
+ -> Incremental Sort
+ Sort Key: ((a <-> '(5,5)'::point)), b DESC
+ Presorted Key: ((a <-> '(5,5)'::point))
+ -> Index Scan using t_a_idx on t
+ Order By: (a <-> '(5,5)'::point)
+(6 rows)
+
diff --git a/src/test/regress/sql/incremental_sort.sql b/src/test/regress/sql/incremental_sort.sql
index 284a354dbb..5cef0ba12c 100644
--- a/src/test/regress/sql/incremental_sort.sql
+++ b/src/test/regress/sql/incremental_sort.sql
@@ -281,3 +281,14 @@ from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub;
explain (costs off) select sub.unique1, stringu1 || random()::text
from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub
order by 1, 2;
+
+-- Incremental sort for not ordered indexes, but with AM order operator
+set enable_incremental_sort = on;
+-- table with GiST index supports closest points retrieval,
+-- but has not sorted index
+create table t (a point, b int);
+create index on t using gist(a);
+insert into t select point(mod(i, 7), mod(i, 11)), i from generate_series(1, 1000) s(i);
+analyze t;
+explain (costs off) select a, b, a <-> point(5, 5) dist from t order by dist, b limit 1;
+explain (costs off) select a, b, a <-> point(5, 5) dist from t order by dist, b desc limit 1;