mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Add support for index-only scans in GiST.
This adds a new GiST opclass method, 'fetch', which is used to reconstruct the original Datum from the value stored in the index. Also, the 'canreturn' index AM interface function gains a new 'attno' argument. That makes it possible to use index-only scans on a multi-column index where some of the opclasses support index-only scans but some do not. This patch adds support in the box and point opclasses. Other opclasses can added later as follow-on patches (btree_gist would be particularly interesting). Anastasia Lubennikova, with additional fixes and modifications by me.
This commit is contained in:
@ -384,7 +384,7 @@ SELECT * FROM fast_emp4000
|
||||
----------------------------------------------------------------
|
||||
Sort
|
||||
Sort Key: ((home_base[0])[0])
|
||||
-> Index Scan using grect2ind on fast_emp4000
|
||||
-> Index Only Scan using grect2ind on fast_emp4000
|
||||
Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
|
||||
(4 rows)
|
||||
|
||||
@ -402,7 +402,7 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using grect2ind on fast_emp4000
|
||||
-> Index Only Scan using grect2ind on fast_emp4000
|
||||
Index Cond: (home_base && '(1000,1000),(0,0)'::box)
|
||||
(3 rows)
|
||||
|
||||
@ -414,10 +414,10 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
|
||||
QUERY PLAN
|
||||
--------------------------------------------------
|
||||
QUERY PLAN
|
||||
-------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using grect2ind on fast_emp4000
|
||||
-> Index Only Scan using grect2ind on fast_emp4000
|
||||
Index Cond: (home_base IS NULL)
|
||||
(3 rows)
|
||||
|
||||
@ -501,7 +501,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)';
|
||||
QUERY PLAN
|
||||
----------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl
|
||||
-> Index Only Scan using gpointind on point_tbl
|
||||
Index Cond: (f1 <@ '(100,100),(0,0)'::box)
|
||||
(3 rows)
|
||||
|
||||
@ -516,8 +516,8 @@ SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
|
||||
QUERY PLAN
|
||||
----------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl
|
||||
Index Cond: ('(100,100),(0,0)'::box @> f1)
|
||||
-> Index Only Scan using gpointind on point_tbl
|
||||
Index Cond: (f1 <@ '(100,100),(0,0)'::box)
|
||||
(3 rows)
|
||||
|
||||
SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
|
||||
@ -531,7 +531,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,
|
||||
QUERY PLAN
|
||||
----------------------------------------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl
|
||||
-> Index Only Scan using gpointind on point_tbl
|
||||
Index Cond: (f1 <@ '((0,0),(0,100),(100,100),(50,50),(100,0),(0,0))'::polygon)
|
||||
(3 rows)
|
||||
|
||||
@ -546,7 +546,7 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
|
||||
QUERY PLAN
|
||||
----------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl
|
||||
-> Index Only Scan using gpointind on point_tbl
|
||||
Index Cond: (f1 <@ '<(50,50),50>'::circle)
|
||||
(3 rows)
|
||||
|
||||
@ -558,10 +558,10 @@ SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
|
||||
QUERY PLAN
|
||||
-------------------------------------------------
|
||||
QUERY PLAN
|
||||
------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl p
|
||||
-> Index Only Scan using gpointind on point_tbl p
|
||||
Index Cond: (f1 << '(0,0)'::point)
|
||||
(3 rows)
|
||||
|
||||
@ -573,10 +573,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
|
||||
QUERY PLAN
|
||||
-------------------------------------------------
|
||||
QUERY PLAN
|
||||
------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl p
|
||||
-> Index Only Scan using gpointind on point_tbl p
|
||||
Index Cond: (f1 >> '(0,0)'::point)
|
||||
(3 rows)
|
||||
|
||||
@ -588,10 +588,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
|
||||
QUERY PLAN
|
||||
-------------------------------------------------
|
||||
QUERY PLAN
|
||||
------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl p
|
||||
-> Index Only Scan using gpointind on point_tbl p
|
||||
Index Cond: (f1 <^ '(0,0)'::point)
|
||||
(3 rows)
|
||||
|
||||
@ -603,10 +603,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
|
||||
QUERY PLAN
|
||||
-------------------------------------------------
|
||||
QUERY PLAN
|
||||
------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl p
|
||||
-> Index Only Scan using gpointind on point_tbl p
|
||||
Index Cond: (f1 >^ '(0,0)'::point)
|
||||
(3 rows)
|
||||
|
||||
@ -618,10 +618,10 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
|
||||
QUERY PLAN
|
||||
-------------------------------------------------
|
||||
QUERY PLAN
|
||||
------------------------------------------------------
|
||||
Aggregate
|
||||
-> Index Scan using gpointind on point_tbl p
|
||||
-> Index Only Scan using gpointind on point_tbl p
|
||||
Index Cond: (f1 ~= '(-5,-12)'::point)
|
||||
(3 rows)
|
||||
|
||||
@ -633,9 +633,9 @@ SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
|
||||
QUERY PLAN
|
||||
-----------------------------------------
|
||||
Index Scan using gpointind on point_tbl
|
||||
QUERY PLAN
|
||||
----------------------------------------------
|
||||
Index Only Scan using gpointind on point_tbl
|
||||
Order By: (f1 <-> '(0,1)'::point)
|
||||
(2 rows)
|
||||
|
||||
@ -653,9 +653,9 @@ SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM point_tbl WHERE f1 IS NULL;
|
||||
QUERY PLAN
|
||||
-----------------------------------------
|
||||
Index Scan using gpointind on point_tbl
|
||||
QUERY PLAN
|
||||
----------------------------------------------
|
||||
Index Only Scan using gpointind on point_tbl
|
||||
Index Cond: (f1 IS NULL)
|
||||
(2 rows)
|
||||
|
||||
@ -667,9 +667,9 @@ SELECT * FROM point_tbl WHERE f1 IS NULL;
|
||||
|
||||
EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
|
||||
QUERY PLAN
|
||||
-----------------------------------------
|
||||
Index Scan using gpointind on point_tbl
|
||||
QUERY PLAN
|
||||
----------------------------------------------
|
||||
Index Only Scan using gpointind on point_tbl
|
||||
Index Cond: (f1 IS NOT NULL)
|
||||
Order By: (f1 <-> '(0,1)'::point)
|
||||
(3 rows)
|
||||
@ -689,7 +689,7 @@ EXPLAIN (COSTS OFF)
|
||||
SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
|
||||
QUERY PLAN
|
||||
------------------------------------------------
|
||||
Index Scan using gpointind on point_tbl
|
||||
Index Only Scan using gpointind on point_tbl
|
||||
Index Cond: (f1 <@ '(10,10),(-10,-10)'::box)
|
||||
Order By: (f1 <-> '(0,1)'::point)
|
||||
(3 rows)
|
||||
|
@ -17,3 +17,149 @@ delete from gist_point_tbl where id % 2 = 1;
|
||||
-- would exercise it)
|
||||
delete from gist_point_tbl where id < 10000;
|
||||
vacuum gist_point_tbl;
|
||||
--
|
||||
-- Test Index-only plans on GiST indexes
|
||||
--
|
||||
create table gist_tbl (b box, p point, c circle);
|
||||
insert into gist_tbl
|
||||
select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)),
|
||||
point(0.05*i, 0.05*i),
|
||||
circle(point(0.05*i, 0.05*i), 1.0)
|
||||
from generate_series(0,10000) as i;
|
||||
vacuum analyze;
|
||||
set enable_seqscan=off;
|
||||
set enable_bitmapscan=off;
|
||||
set enable_indexonlyscan=on;
|
||||
-- Test index-only scan with point opclass
|
||||
create index gist_tbl_point_index on gist_tbl using gist (p);
|
||||
-- check that the planner chooses an index-only scan
|
||||
explain (costs off)
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------
|
||||
Index Only Scan using gist_tbl_point_index on gist_tbl
|
||||
Index Cond: (p <@ '(0.5,0.5),(0,0)'::box)
|
||||
(2 rows)
|
||||
|
||||
-- execute the same
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||
p
|
||||
-------------
|
||||
(0,0)
|
||||
(0.05,0.05)
|
||||
(0.1,0.1)
|
||||
(0.15,0.15)
|
||||
(0.2,0.2)
|
||||
(0.25,0.25)
|
||||
(0.3,0.3)
|
||||
(0.35,0.35)
|
||||
(0.4,0.4)
|
||||
(0.45,0.45)
|
||||
(0.5,0.5)
|
||||
(11 rows)
|
||||
|
||||
-- Also test an index-only knn-search
|
||||
explain (costs off)
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||
order by p <-> point(0.2, 0.2);
|
||||
QUERY PLAN
|
||||
--------------------------------------------------------
|
||||
Index Only Scan using gist_tbl_point_index on gist_tbl
|
||||
Index Cond: (p <@ '(0.5,0.5),(0,0)'::box)
|
||||
Order By: (p <-> '(0.2,0.2)'::point)
|
||||
(3 rows)
|
||||
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||
order by p <-> point(0.2, 0.2);
|
||||
p
|
||||
-------------
|
||||
(0.2,0.2)
|
||||
(0.25,0.25)
|
||||
(0.15,0.15)
|
||||
(0.3,0.3)
|
||||
(0.1,0.1)
|
||||
(0.35,0.35)
|
||||
(0.05,0.05)
|
||||
(0,0)
|
||||
(0.4,0.4)
|
||||
(0.45,0.45)
|
||||
(0.5,0.5)
|
||||
(11 rows)
|
||||
|
||||
drop index gist_tbl_point_index;
|
||||
-- Test index-only scan with box opclass
|
||||
create index gist_tbl_box_index on gist_tbl using gist (b);
|
||||
-- check that the planner chooses an index-only scan
|
||||
explain (costs off)
|
||||
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||
QUERY PLAN
|
||||
------------------------------------------------------
|
||||
Index Only Scan using gist_tbl_box_index on gist_tbl
|
||||
Index Cond: (b <@ '(6,6),(5,5)'::box)
|
||||
(2 rows)
|
||||
|
||||
-- execute the same
|
||||
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||
b
|
||||
-------------------------
|
||||
(5,5),(5,5)
|
||||
(5.05,5.05),(5.05,5.05)
|
||||
(5.1,5.1),(5.1,5.1)
|
||||
(5.15,5.15),(5.15,5.15)
|
||||
(5.2,5.2),(5.2,5.2)
|
||||
(5.25,5.25),(5.25,5.25)
|
||||
(5.3,5.3),(5.3,5.3)
|
||||
(5.35,5.35),(5.35,5.35)
|
||||
(5.4,5.4),(5.4,5.4)
|
||||
(5.45,5.45),(5.45,5.45)
|
||||
(5.5,5.5),(5.5,5.5)
|
||||
(5.55,5.55),(5.55,5.55)
|
||||
(5.6,5.6),(5.6,5.6)
|
||||
(5.65,5.65),(5.65,5.65)
|
||||
(5.7,5.7),(5.7,5.7)
|
||||
(5.75,5.75),(5.75,5.75)
|
||||
(5.8,5.8),(5.8,5.8)
|
||||
(5.85,5.85),(5.85,5.85)
|
||||
(5.9,5.9),(5.9,5.9)
|
||||
(5.95,5.95),(5.95,5.95)
|
||||
(6,6),(6,6)
|
||||
(21 rows)
|
||||
|
||||
drop index gist_tbl_box_index;
|
||||
-- Test that an index-only scan is not chosen, when the query involves the
|
||||
-- circle column (the circle opclass does not support index-only scans).
|
||||
create index gist_tbl_multi_index on gist_tbl using gist (p, c);
|
||||
explain (costs off)
|
||||
select p, c from gist_tbl
|
||||
where p <@ box(point(5,5), point(6, 6));
|
||||
QUERY PLAN
|
||||
---------------------------------------------------
|
||||
Index Scan using gist_tbl_multi_index on gist_tbl
|
||||
Index Cond: (p <@ '(6,6),(5,5)'::box)
|
||||
(2 rows)
|
||||
|
||||
-- execute the same
|
||||
select b, p from gist_tbl
|
||||
where b <@ box(point(4.5, 4.5), point(5.5, 5.5))
|
||||
and p <@ box(point(5,5), point(6, 6));
|
||||
b | p
|
||||
-------------------------+-------------
|
||||
(5,5),(5,5) | (5,5)
|
||||
(5.05,5.05),(5.05,5.05) | (5.05,5.05)
|
||||
(5.1,5.1),(5.1,5.1) | (5.1,5.1)
|
||||
(5.15,5.15),(5.15,5.15) | (5.15,5.15)
|
||||
(5.2,5.2),(5.2,5.2) | (5.2,5.2)
|
||||
(5.25,5.25),(5.25,5.25) | (5.25,5.25)
|
||||
(5.3,5.3),(5.3,5.3) | (5.3,5.3)
|
||||
(5.35,5.35),(5.35,5.35) | (5.35,5.35)
|
||||
(5.4,5.4),(5.4,5.4) | (5.4,5.4)
|
||||
(5.45,5.45),(5.45,5.45) | (5.45,5.45)
|
||||
(5.5,5.5),(5.5,5.5) | (5.5,5.5)
|
||||
(11 rows)
|
||||
|
||||
drop index gist_tbl_multi_index;
|
||||
-- Clean up
|
||||
reset enable_seqscan;
|
||||
reset enable_bitmapscan;
|
||||
reset enable_indexonlyscan;
|
||||
drop table gist_tbl;
|
||||
|
@ -23,3 +23,76 @@ delete from gist_point_tbl where id % 2 = 1;
|
||||
delete from gist_point_tbl where id < 10000;
|
||||
|
||||
vacuum gist_point_tbl;
|
||||
|
||||
|
||||
--
|
||||
-- Test Index-only plans on GiST indexes
|
||||
--
|
||||
|
||||
create table gist_tbl (b box, p point, c circle);
|
||||
|
||||
insert into gist_tbl
|
||||
select box(point(0.05*i, 0.05*i), point(0.05*i, 0.05*i)),
|
||||
point(0.05*i, 0.05*i),
|
||||
circle(point(0.05*i, 0.05*i), 1.0)
|
||||
from generate_series(0,10000) as i;
|
||||
|
||||
vacuum analyze;
|
||||
|
||||
set enable_seqscan=off;
|
||||
set enable_bitmapscan=off;
|
||||
set enable_indexonlyscan=on;
|
||||
|
||||
-- Test index-only scan with point opclass
|
||||
create index gist_tbl_point_index on gist_tbl using gist (p);
|
||||
|
||||
-- check that the planner chooses an index-only scan
|
||||
explain (costs off)
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||
|
||||
-- execute the same
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5));
|
||||
|
||||
-- Also test an index-only knn-search
|
||||
explain (costs off)
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||
order by p <-> point(0.2, 0.2);
|
||||
|
||||
select p from gist_tbl where p <@ box(point(0,0), point(0.5, 0.5))
|
||||
order by p <-> point(0.2, 0.2);
|
||||
|
||||
drop index gist_tbl_point_index;
|
||||
|
||||
-- Test index-only scan with box opclass
|
||||
create index gist_tbl_box_index on gist_tbl using gist (b);
|
||||
|
||||
-- check that the planner chooses an index-only scan
|
||||
explain (costs off)
|
||||
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||
|
||||
-- execute the same
|
||||
select b from gist_tbl where b <@ box(point(5,5), point(6,6));
|
||||
|
||||
drop index gist_tbl_box_index;
|
||||
|
||||
-- Test that an index-only scan is not chosen, when the query involves the
|
||||
-- circle column (the circle opclass does not support index-only scans).
|
||||
create index gist_tbl_multi_index on gist_tbl using gist (p, c);
|
||||
|
||||
explain (costs off)
|
||||
select p, c from gist_tbl
|
||||
where p <@ box(point(5,5), point(6, 6));
|
||||
|
||||
-- execute the same
|
||||
select b, p from gist_tbl
|
||||
where b <@ box(point(4.5, 4.5), point(5.5, 5.5))
|
||||
and p <@ box(point(5,5), point(6, 6));
|
||||
|
||||
drop index gist_tbl_multi_index;
|
||||
|
||||
-- Clean up
|
||||
reset enable_seqscan;
|
||||
reset enable_bitmapscan;
|
||||
reset enable_indexonlyscan;
|
||||
|
||||
drop table gist_tbl;
|
||||
|
Reference in New Issue
Block a user