mirror of
https://github.com/postgres/postgres.git
synced 2025-05-02 11:44:50 +03:00
Fix planning of btree index scans using ScalarArrayOpExpr quals.
In commit 9e8da0f75731aaa7605cf4656c21ea09e84d2eb1, I improved btree to handle ScalarArrayOpExpr quals natively, so that constructs like "indexedcol IN (list)" could be supported by index-only scans. Using such a qual results in multiple scans of the index, under-the-hood. I went to some lengths to ensure that this still produces rows in index order ... but I failed to recognize that if a higher-order index column is lacking an equality constraint, rescans can produce out-of-order data from that column. Tweak the planner to not expect sorted output in that case. Per trouble report from Robert McGehee.
This commit is contained in:
parent
66762ce5fa
commit
3cccc6990b
@ -756,6 +756,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
List *index_pathkeys;
|
List *index_pathkeys;
|
||||||
List *useful_pathkeys;
|
List *useful_pathkeys;
|
||||||
bool found_clause;
|
bool found_clause;
|
||||||
|
bool found_lower_saop_clause;
|
||||||
bool pathkeys_possibly_useful;
|
bool pathkeys_possibly_useful;
|
||||||
bool index_is_ordered;
|
bool index_is_ordered;
|
||||||
bool index_only_scan;
|
bool index_only_scan;
|
||||||
@ -793,12 +794,20 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
* if saop_control is SAOP_REQUIRE, it has to be a ScalarArrayOpExpr
|
* if saop_control is SAOP_REQUIRE, it has to be a ScalarArrayOpExpr
|
||||||
* clause.
|
* clause.
|
||||||
*
|
*
|
||||||
|
* found_lower_saop_clause is set true if there's a ScalarArrayOpExpr
|
||||||
|
* index clause for a non-first index column. This prevents us from
|
||||||
|
* assuming that the scan result is ordered. (Actually, the result is
|
||||||
|
* still ordered if there are equality constraints for all earlier
|
||||||
|
* columns, but it seems too expensive and non-modular for this code to be
|
||||||
|
* aware of that refinement.)
|
||||||
|
*
|
||||||
* We also build a Relids set showing which outer rels are required by the
|
* We also build a Relids set showing which outer rels are required by the
|
||||||
* selected clauses.
|
* selected clauses.
|
||||||
*/
|
*/
|
||||||
index_clauses = NIL;
|
index_clauses = NIL;
|
||||||
clause_columns = NIL;
|
clause_columns = NIL;
|
||||||
found_clause = false;
|
found_clause = false;
|
||||||
|
found_lower_saop_clause = false;
|
||||||
outer_relids = NULL;
|
outer_relids = NULL;
|
||||||
for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
|
for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
|
||||||
{
|
{
|
||||||
@ -814,6 +823,8 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
if (saop_control == SAOP_PER_AM && !index->amsearcharray)
|
if (saop_control == SAOP_PER_AM && !index->amsearcharray)
|
||||||
continue;
|
continue;
|
||||||
found_clause = true;
|
found_clause = true;
|
||||||
|
if (indexcol > 0)
|
||||||
|
found_lower_saop_clause = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -850,9 +861,11 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
/*
|
/*
|
||||||
* 2. Compute pathkeys describing index's ordering, if any, then see how
|
* 2. Compute pathkeys describing index's ordering, if any, then see how
|
||||||
* many of them are actually useful for this query. This is not relevant
|
* many of them are actually useful for this query. This is not relevant
|
||||||
* if we are only trying to build bitmap indexscans.
|
* if we are only trying to build bitmap indexscans, nor if we have to
|
||||||
|
* assume the scan is unordered.
|
||||||
*/
|
*/
|
||||||
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
|
pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&
|
||||||
|
!found_lower_saop_clause &&
|
||||||
has_useful_pathkeys(root, rel));
|
has_useful_pathkeys(root, rel));
|
||||||
index_is_ordered = (index->sortopfamily != NULL);
|
index_is_ordered = (index->sortopfamily != NULL);
|
||||||
if (index_is_ordered && pathkeys_possibly_useful)
|
if (index_is_ordered && pathkeys_possibly_useful)
|
||||||
|
@ -2676,3 +2676,48 @@ SELECT count(*) FROM dupindexcols
|
|||||||
97
|
97
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Check ordering of =ANY indexqual results (bug in 9.2.0)
|
||||||
|
--
|
||||||
|
vacuum analyze tenk1; -- ensure we get consistent plans here
|
||||||
|
explain (costs off)
|
||||||
|
SELECT unique1 FROM tenk1
|
||||||
|
WHERE unique1 IN (1,42,7)
|
||||||
|
ORDER BY unique1;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------------------
|
||||||
|
Index Only Scan using tenk1_unique1 on tenk1
|
||||||
|
Index Cond: (unique1 = ANY ('{1,42,7}'::integer[]))
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT unique1 FROM tenk1
|
||||||
|
WHERE unique1 IN (1,42,7)
|
||||||
|
ORDER BY unique1;
|
||||||
|
unique1
|
||||||
|
---------
|
||||||
|
1
|
||||||
|
7
|
||||||
|
42
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
SELECT thousand, tenthous FROM tenk1
|
||||||
|
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||||
|
ORDER BY thousand;
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: thousand
|
||||||
|
-> Index Only Scan using tenk1_thous_tenthous on tenk1
|
||||||
|
Index Cond: ((thousand < 2) AND (tenthous = ANY ('{1001,3000}'::integer[])))
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT thousand, tenthous FROM tenk1
|
||||||
|
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||||
|
ORDER BY thousand;
|
||||||
|
thousand | tenthous
|
||||||
|
----------+----------
|
||||||
|
0 | 3000
|
||||||
|
1 | 1001
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
@ -888,3 +888,27 @@ EXPLAIN (COSTS OFF)
|
|||||||
WHERE f1 > 'WA' and id < 1000 and f1 ~<~ 'YX';
|
WHERE f1 > 'WA' and id < 1000 and f1 ~<~ 'YX';
|
||||||
SELECT count(*) FROM dupindexcols
|
SELECT count(*) FROM dupindexcols
|
||||||
WHERE f1 > 'WA' and id < 1000 and f1 ~<~ 'YX';
|
WHERE f1 > 'WA' and id < 1000 and f1 ~<~ 'YX';
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Check ordering of =ANY indexqual results (bug in 9.2.0)
|
||||||
|
--
|
||||||
|
|
||||||
|
vacuum analyze tenk1; -- ensure we get consistent plans here
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
SELECT unique1 FROM tenk1
|
||||||
|
WHERE unique1 IN (1,42,7)
|
||||||
|
ORDER BY unique1;
|
||||||
|
|
||||||
|
SELECT unique1 FROM tenk1
|
||||||
|
WHERE unique1 IN (1,42,7)
|
||||||
|
ORDER BY unique1;
|
||||||
|
|
||||||
|
explain (costs off)
|
||||||
|
SELECT thousand, tenthous FROM tenk1
|
||||||
|
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||||
|
ORDER BY thousand;
|
||||||
|
|
||||||
|
SELECT thousand, tenthous FROM tenk1
|
||||||
|
WHERE thousand < 2 AND tenthous IN (1001,3000)
|
||||||
|
ORDER BY thousand;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user