Support index-only scans in contrib/cube and contrib/seg GiST indexes.

To do this, we only have to remove the compress and decompress support
functions, which have never done anything more than detoasting.
In the wake of commit d3a4f89d8, this results in automatically enabling
index-only scans, since the core code will now know that the stored
representation is the same as the original data (up to detoasting).

The only exciting part of this is that ALTER OPERATOR FAMILY lacks
a way to drop a support function that was declared as being part of
an opclass rather than being loose in the family.  For the moment,
we'll hack our way to a solution with a manual update of the pg_depend
entry type, which is what distinguishes the two cases.  Perhaps
someday it'll be worth providing a cleaner way to do that, but for
now it seems like a very niche problem.

Note that the underlying C functions remain, to support use of the shared
libraries with older versions of the modules' SQL declarations.  Someday
we may be able to remove them, but not soon.

Andrey Borodin, reviewed by me

Discussion: https://postgr.es/m/D0F53A05-4F4A-4DEC-8339-3C069FA0EE11@yandex-team.ru
This commit is contained in:
Tom Lane 2017-11-20 20:25:18 -05:00
parent 84669c9b06
commit de1d042f59
12 changed files with 210 additions and 4 deletions

View File

@ -4,7 +4,7 @@ MODULE_big = cube
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
DATA = cube--1.2.sql cube--1.2--1.3.sql \
DATA = cube--1.2.sql cube--1.2--1.3.sql cube--1.3--1.4.sql \
cube--1.1--1.2.sql cube--1.0--1.1.sql \
cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"

View File

@ -0,0 +1,45 @@
/* contrib/cube/cube--1.3--1.4.sql */
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION cube UPDATE TO '1.4'" to load this file. \quit
--
-- Get rid of unnecessary compress and decompress support functions.
--
-- To be allowed to drop the opclass entry for a support function,
-- we must change the entry's dependency type from 'internal' to 'auto',
-- as though it were a loose member of the opfamily rather than being
-- bound into a particular opclass. There's no SQL command for that,
-- so fake it with a manual update on pg_depend.
--
UPDATE pg_catalog.pg_depend
SET deptype = 'a'
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND objid =
(SELECT objid
FROM pg_catalog.pg_depend
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
AND (refobjid = 'g_cube_compress(pg_catalog.internal)'::pg_catalog.regprocedure))
AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
AND deptype = 'i';
ALTER OPERATOR FAMILY gist_cube_ops USING gist drop function 3 (cube);
ALTER EXTENSION cube DROP function g_cube_compress(pg_catalog.internal);
DROP FUNCTION g_cube_compress(pg_catalog.internal);
UPDATE pg_catalog.pg_depend
SET deptype = 'a'
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND objid =
(SELECT objid
FROM pg_catalog.pg_depend
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
AND (refobjid = 'g_cube_decompress(pg_catalog.internal)'::pg_catalog.regprocedure))
AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
AND deptype = 'i';
ALTER OPERATOR FAMILY gist_cube_ops USING gist drop function 4 (cube);
ALTER EXTENSION cube DROP function g_cube_decompress(pg_catalog.internal);
DROP FUNCTION g_cube_decompress(pg_catalog.internal);

View File

@ -1,5 +1,5 @@
# cube extension
comment = 'data type for multidimensional cubes'
default_version = '1.3'
default_version = '1.4'
module_pathname = '$libdir/cube'
relocatable = true

View File

@ -1589,6 +1589,28 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
-- Test index-only scans
SET enable_bitmapscan = false;
EXPLAIN (COSTS OFF)
SELECT c FROM test_cube WHERE c <@ '(3000,1000),(0,0)' ORDER BY c;
QUERY PLAN
--------------------------------------------------------
Sort
Sort Key: c
-> Index Only Scan using test_cube_ix on test_cube
Index Cond: (c <@ '(3000, 1000),(0, 0)'::cube)
(4 rows)
SELECT c FROM test_cube WHERE c <@ '(3000,1000),(0,0)' ORDER BY c;
c
-------------------------
(337, 455),(240, 359)
(759, 187),(662, 163)
(1444, 403),(1346, 344)
(2424, 160),(2424, 81)
(4 rows)
RESET enable_bitmapscan;
-- kNN with index
SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
c | dist

View File

@ -1589,6 +1589,28 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
-- Test index-only scans
SET enable_bitmapscan = false;
EXPLAIN (COSTS OFF)
SELECT c FROM test_cube WHERE c <@ '(3000,1000),(0,0)' ORDER BY c;
QUERY PLAN
--------------------------------------------------------
Sort
Sort Key: c
-> Index Only Scan using test_cube_ix on test_cube
Index Cond: (c <@ '(3000, 1000),(0, 0)'::cube)
(4 rows)
SELECT c FROM test_cube WHERE c <@ '(3000,1000),(0,0)' ORDER BY c;
c
-------------------------
(337, 455),(240, 359)
(759, 187),(662, 163)
(1444, 403),(1346, 344)
(2424, 160),(2424, 81)
(4 rows)
RESET enable_bitmapscan;
-- kNN with index
SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
c | dist

View File

@ -382,6 +382,13 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' ORDER BY c;
-- Test sorting
SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
-- Test index-only scans
SET enable_bitmapscan = false;
EXPLAIN (COSTS OFF)
SELECT c FROM test_cube WHERE c <@ '(3000,1000),(0,0)' ORDER BY c;
SELECT c FROM test_cube WHERE c <@ '(3000,1000),(0,0)' ORDER BY c;
RESET enable_bitmapscan;
-- kNN with index
SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;

View File

@ -4,7 +4,7 @@ MODULE_big = seg
OBJS = seg.o segparse.o $(WIN32RES)
EXTENSION = seg
DATA = seg--1.1.sql seg--1.1--1.2.sql \
DATA = seg--1.1.sql seg--1.1--1.2.sql seg--1.2--1.3.sql \
seg--1.0--1.1.sql seg--unpackaged--1.0.sql
PGFILEDESC = "seg - line segment data type"

View File

@ -930,12 +930,40 @@ SELECT '1'::seg <@ '-1 .. 1'::seg AS bool;
CREATE TABLE test_seg (s seg);
\copy test_seg from 'data/test_seg.data'
CREATE INDEX test_seg_ix ON test_seg USING gist (s);
EXPLAIN (COSTS OFF)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
QUERY PLAN
-------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on test_seg
Recheck Cond: (s @> '1.1e1 .. 11.3'::seg)
-> Bitmap Index Scan on test_seg_ix
Index Cond: (s @> '1.1e1 .. 11.3'::seg)
(5 rows)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
count
-------
143
(1 row)
SET enable_bitmapscan = false;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
QUERY PLAN
-----------------------------------------------------
Aggregate
-> Index Only Scan using test_seg_ix on test_seg
Index Cond: (s @> '1.1e1 .. 11.3'::seg)
(3 rows)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
count
-------
143
(1 row)
RESET enable_bitmapscan;
-- Test sorting
SELECT * FROM test_seg WHERE s @> '11..11.3' GROUP BY s;
s

View File

@ -930,12 +930,40 @@ SELECT '1'::seg <@ '-1 .. 1'::seg AS bool;
CREATE TABLE test_seg (s seg);
\copy test_seg from 'data/test_seg.data'
CREATE INDEX test_seg_ix ON test_seg USING gist (s);
EXPLAIN (COSTS OFF)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
QUERY PLAN
-------------------------------------------------------
Aggregate
-> Bitmap Heap Scan on test_seg
Recheck Cond: (s @> '1.1e1 .. 11.3'::seg)
-> Bitmap Index Scan on test_seg_ix
Index Cond: (s @> '1.1e1 .. 11.3'::seg)
(5 rows)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
count
-------
143
(1 row)
SET enable_bitmapscan = false;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
QUERY PLAN
-----------------------------------------------------
Aggregate
-> Index Only Scan using test_seg_ix on test_seg
Index Cond: (s @> '1.1e1 .. 11.3'::seg)
(3 rows)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
count
-------
143
(1 row)
RESET enable_bitmapscan;
-- Test sorting
SELECT * FROM test_seg WHERE s @> '11..11.3' GROUP BY s;
s

View File

@ -0,0 +1,45 @@
/* contrib/seg/seg--1.2--1.3.sql */
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION seg UPDATE TO '1.3'" to load this file. \quit
--
-- Get rid of unnecessary compress and decompress support functions.
--
-- To be allowed to drop the opclass entry for a support function,
-- we must change the entry's dependency type from 'internal' to 'auto',
-- as though it were a loose member of the opfamily rather than being
-- bound into a particular opclass. There's no SQL command for that,
-- so fake it with a manual update on pg_depend.
--
UPDATE pg_catalog.pg_depend
SET deptype = 'a'
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND objid =
(SELECT objid
FROM pg_catalog.pg_depend
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
AND (refobjid = 'gseg_compress(pg_catalog.internal)'::pg_catalog.regprocedure))
AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
AND deptype = 'i';
ALTER OPERATOR FAMILY gist_seg_ops USING gist drop function 3 (seg);
ALTER EXTENSION seg DROP function gseg_compress(pg_catalog.internal);
DROP function gseg_compress(pg_catalog.internal);
UPDATE pg_catalog.pg_depend
SET deptype = 'a'
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND objid =
(SELECT objid
FROM pg_catalog.pg_depend
WHERE classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass
AND refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
AND (refobjid = 'gseg_decompress(pg_catalog.internal)'::pg_catalog.regprocedure))
AND refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass
AND deptype = 'i';
ALTER OPERATOR FAMILY gist_seg_ops USING gist drop function 4 (seg);
ALTER EXTENSION seg DROP function gseg_decompress(pg_catalog.internal);
DROP function gseg_decompress(pg_catalog.internal);

View File

@ -1,5 +1,5 @@
# seg extension
comment = 'data type for representing line segments or floating-point intervals'
default_version = '1.2'
default_version = '1.3'
module_pathname = '$libdir/seg'
relocatable = true

View File

@ -216,7 +216,16 @@ CREATE TABLE test_seg (s seg);
\copy test_seg from 'data/test_seg.data'
CREATE INDEX test_seg_ix ON test_seg USING gist (s);
EXPLAIN (COSTS OFF)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
SET enable_bitmapscan = false;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
SELECT count(*) FROM test_seg WHERE s @> '11..11.3';
RESET enable_bitmapscan;
-- Test sorting
SELECT * FROM test_seg WHERE s @> '11..11.3' GROUP BY s;