From 035cfe99a42f771a0d9d01aaea0fd757c05655ac Mon Sep 17 00:00:00 2001 From: Christoph Berg Date: Fri, 3 Nov 2023 10:49:53 +0100 Subject: [PATCH] Hash opclass for spoint Besides hash indexes on spoint, this enables "select distinct spoint from tbl" queries. Close #101. --- Makefile | 4 ++-- doc/indices.sgm | 25 +++++++++++++++++++++++++ expected/index.out | 36 ++++++++++++++++++++++++++++++++++++ pgs_hash.sql.in | 19 +++++++++++++++++++ sql/index.sql | 11 +++++++++++ src/point.c | 11 +++++++++++ src/point.h | 5 +++++ 7 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 pgs_hash.sql.in diff --git a/Makefile b/Makefile index 2a685d33..20abe66c 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ CRUSH_TESTS = init_extended circle_extended PGS_SQL = pgs_types.sql pgs_point.sql pgs_euler.sql pgs_circle.sql \ pgs_line.sql pgs_ellipse.sql pgs_polygon.sql pgs_path.sql \ pgs_box.sql pgs_contains_ops.sql pgs_contains_ops_compat.sql \ - pgs_gist.sql gnomo.sql pgs_brin.sql pgs_circle_sel.sql + pgs_gist.sql gnomo.sql pgs_brin.sql pgs_circle_sel.sql pgs_hash.sql ifneq ($(USE_HEALPIX),0) TESTS += healpix moc moc1 moc100 mocautocast @@ -199,7 +199,7 @@ ifeq ($(has_index_options),y) pg_sphere--1.3.1--1.3.2.sql: pgs_moc_options.sql.in endif endif -pg_sphere--1.3.1--1.3.2.sql: pgs_circle_sel.sql.in +pg_sphere--1.3.1--1.3.2.sql: pgs_circle_sel.sql.in pgs_hash.sql.in cat upgrade_scripts/$@.in $^ > $@ # end of local stuff diff --git a/doc/indices.sgm b/doc/indices.sgm index b3a9c698..998b5461 100644 --- a/doc/indices.sgm +++ b/doc/indices.sgm @@ -153,4 +153,29 @@ + + + Hash index + + + Values of type spoint can be indexed using a + HASH index, supporting the + = operator. + The spoint_hash_ops operator class also enables + DISTINCT queries on spoint. + + + Hash index of spoints + + + + + + + + + + + + diff --git a/expected/index.out b/expected/index.out index eefa5c63..f63cff80 100644 --- a/expected/index.out +++ b/expected/index.out @@ -195,3 +195,39 @@ EXPLAIN (COSTS OFF) SELECT count(*) FROM spheretmp1b WHERE p = spoint '(3.09 , 1 4 (1 row) +-- test hash opclass +CREATE TABLE spheretmp1c AS TABLE spheretmp1; +SELECT p FROM spheretmp1c WHERE p <@ scircle '<(1,1),0.2>' ORDER BY p::text; + p +--------------- + (0.67 , 0.97) + (0.67 , 0.97) + (0.67 , 0.97) + (0.67 , 0.97) + (1.07 , 1.09) + (1.07 , 1.09) + (1.07 , 1.09) + (1.07 , 1.09) + (1.24 , 0.95) + (1.24 , 0.95) + (1.24 , 0.95) + (1.24 , 0.95) +(12 rows) + +WITH points AS (SELECT DISTINCT p FROM spheretmp1c WHERE p <@ scircle '<(1,1),0.2>') + SELECT p FROM points ORDER BY p::text; + p +--------------- + (0.67 , 0.97) + (1.07 , 1.09) + (1.24 , 0.95) +(3 rows) + +CREATE INDEX spheretmp1c_hash_idx ON spheretmp1c USING hash(p); +EXPLAIN (COSTS OFF) SELECT * FROM spheretmp1c WHERE p = '(0.67 , 0.97)'; + QUERY PLAN +------------------------------------------------------ + Index Scan using spheretmp1c_hash_idx on spheretmp1c + Index Cond: (p = '(0.67 , 0.97)'::spoint) +(2 rows) + diff --git a/pgs_hash.sql.in b/pgs_hash.sql.in new file mode 100644 index 00000000..52fd1ed9 --- /dev/null +++ b/pgs_hash.sql.in @@ -0,0 +1,19 @@ +CREATE FUNCTION spoint_hash32 (spoint) + RETURNS int + IMMUTABLE STRICT + PARALLEL SAFE + LANGUAGE C + AS 'MODULE_PATHNAME', 'spherepoint_hash32'; + +UPDATE pg_operator + SET oprcanhash = true + WHERE oprname = '=' AND + oprleft = 'spoint'::regtype AND oprright = 'spoint'::regtype; + +/* PG17: ALTER OPERATOR = (spoint, spoint) SET (HASHES); */ + +CREATE OPERATOR CLASS spoint_hash_ops + DEFAULT FOR TYPE spoint USING hash + AS + OPERATOR 1 = (spoint, spoint), + FUNCTION 1 spoint_hash32(spoint); diff --git a/sql/index.sql b/sql/index.sql index 87131065..19d537b2 100644 --- a/sql/index.sql +++ b/sql/index.sql @@ -76,3 +76,14 @@ EXPLAIN (COSTS OFF) SELECT count(*) FROM spheretmp1b WHERE p <@ scircle '<(1,1), SELECT count(*) FROM spheretmp1b WHERE p <@ scircle '<(1,1),0.3>'; EXPLAIN (COSTS OFF) SELECT count(*) FROM spheretmp1b WHERE p = spoint '(3.09 , 1.25)'; SELECT count(*) FROM spheretmp1b WHERE p = spoint '(3.09 , 1.25)'; + +-- test hash opclass + +CREATE TABLE spheretmp1c AS TABLE spheretmp1; + +SELECT p FROM spheretmp1c WHERE p <@ scircle '<(1,1),0.2>' ORDER BY p::text; +WITH points AS (SELECT DISTINCT p FROM spheretmp1c WHERE p <@ scircle '<(1,1),0.2>') + SELECT p FROM points ORDER BY p::text; + +CREATE INDEX spheretmp1c_hash_idx ON spheretmp1c USING hash(p); +EXPLAIN (COSTS OFF) SELECT * FROM spheretmp1c WHERE p = '(0.67 , 0.97)'; diff --git a/src/point.c b/src/point.c index 498ee5e2..f4df15c0 100644 --- a/src/point.c +++ b/src/point.c @@ -16,6 +16,7 @@ PG_FUNCTION_INFO_V1(spherepoint_y); PG_FUNCTION_INFO_V1(spherepoint_z); PG_FUNCTION_INFO_V1(spherepoint_xyz); PG_FUNCTION_INFO_V1(spherepoint_equal); +PG_FUNCTION_INFO_V1(spherepoint_hash32); static Oid point_id = InvalidOid; @@ -309,3 +310,13 @@ spherepoint_equal(PG_FUNCTION_ARGS) PG_RETURN_BOOL(spoint_eq(p1, p2)); } + +Datum +spherepoint_hash32(PG_FUNCTION_ARGS) +{ + SPoint *p1 = (SPoint *) PG_GETARG_POINTER(0); + Datum h1 = DirectFunctionCall1(hashfloat8, p1->lat); + Datum h2 = DirectFunctionCall1(hashfloat8, p1->lng); + + PG_RETURN_INT32(DatumGetInt32(h1) ^ DatumGetInt32(h2)); +} diff --git a/src/point.h b/src/point.h index 9269cf78..d7af2281 100644 --- a/src/point.h +++ b/src/point.h @@ -102,4 +102,9 @@ Datum spherepoint_xyz(PG_FUNCTION_ARGS); */ Datum spherepoint_equal(PG_FUNCTION_ARGS); +/* + * Compute a 32-bit hash value of a point. + */ +Datum spherepoint_hash32(PG_FUNCTION_ARGS); + #endif