1
0
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:
Heikki Linnakangas
2015-03-26 19:12:00 +02:00
parent 8fa393a6d7
commit d04c8ed904
24 changed files with 575 additions and 73 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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;