diff --git a/sql/install/13-opclass_gist.sql b/sql/install/13-opclass_gist.sql new file mode 100644 index 00000000..e754291a --- /dev/null +++ b/sql/install/13-opclass_gist.sql @@ -0,0 +1,53 @@ +/* + * Copyright 2019 Bytes & Brains + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +-- GiST Operator Class (opclass_gist.c) +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- + +-- GiST operator class +CREATE OR REPLACE FUNCTION h3index_gist_consistent(internal, h3index, smallint, oid, internal) RETURNS boolean + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_union(internal, internal) RETURNS h3index + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_compress(internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_decompress(internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_penalty(internal, internal, internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_picksplit(internal, internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_same(h3index, h3index, internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_distance(internal, h3index, smallint, oid, internal) RETURNS integer + AS 'h3' LANGUAGE C STRICT; + +CREATE OPERATOR CLASS gist_h3index_ops DEFAULT FOR TYPE h3index USING gist AS + OPERATOR 3 && , + OPERATOR 6 = , + OPERATOR 7 @> , + OPERATOR 8 <@ , + OPERATOR 15 <-> (h3index, h3index) FOR ORDER BY integer_ops, + + FUNCTION 1 h3index_gist_consistent(internal, h3index, smallint, oid, internal), + FUNCTION 2 h3index_gist_union(internal, internal), + FUNCTION 3 h3index_gist_compress(internal), + FUNCTION 4 h3index_gist_decompress(internal), + FUNCTION 5 h3index_gist_penalty(internal, internal, internal), + FUNCTION 6 h3index_gist_picksplit(internal, internal), + FUNCTION 7 h3index_gist_same(h3index, h3index, internal), + FUNCTION 8 (h3index, h3index) h3index_gist_distance(internal, h3index, smallint, oid, internal); diff --git a/sql/install/14-opclass_spgist.sql b/sql/install/14-opclass_spgist.sql new file mode 100644 index 00000000..edc467e3 --- /dev/null +++ b/sql/install/14-opclass_spgist.sql @@ -0,0 +1,42 @@ +/* + * Copyright 2019 Bytes & Brains + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- +-- SP-GiST Operator Class (opclass_spgist.c) +-- ---------- ---------- ---------- ---------- ---------- ---------- ---------- + +-- SP-GiST operator class +CREATE OR REPLACE FUNCTION h3index_spgist_config(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_choose(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_picksplit(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_inner_consistent(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_leaf_consistent(internal, internal) RETURNS bool + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS spgist_h3index_ops DEFAULT FOR TYPE h3index USING spgist AS + OPERATOR 6 = , + OPERATOR 7 @> , + OPERATOR 8 <@ , + + FUNCTION 1 h3index_spgist_config(internal, internal), + FUNCTION 2 h3index_spgist_choose(internal, internal), + FUNCTION 3 h3index_spgist_picksplit(internal, internal), + FUNCTION 4 h3index_spgist_inner_consistent(internal, internal), + FUNCTION 5 h3index_spgist_leaf_consistent(internal, internal); diff --git a/sql/updates/h3--3.5.0--3.6.0-alpha.sql b/sql/updates/h3--3.5.0--3.6.0-alpha.sql new file mode 100644 index 00000000..b6ff2409 --- /dev/null +++ b/sql/updates/h3--3.5.0--3.6.0-alpha.sql @@ -0,0 +1,130 @@ +/* + * Copyright 2019 Bytes & Brains + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.0-alpha'" to load this file. \quit + +-- Hierarchical grid functions (hierarchy.c) +CREATE OR REPLACE FUNCTION h3_to_center_child(h3index, resolution integer DEFAULT -1) RETURNS h3index + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + COMMENT ON FUNCTION h3_to_parent(h3index, resolution integer) IS +'Returns the center child (finer) index contained by input index at given resolution'; + +-- Miscellaneous H3 functions (miscellaneous.c) +CREATE OR REPLACE FUNCTION h3_get_pentagon_indexes(resolution integer) RETURNS SETOF h3index + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + COMMENT ON FUNCTION h3_get_res_0_indexes() IS +'All the pentagon H3 indexes at the specified resolution.'; + +-- PostgreSQL operators +DROP FUNCTION IF EXISTS h3_to_string(h3index); +DROP FUNCTION IF EXISTS h3_string_to_h3(cstring); + +CREATE OR REPLACE FUNCTION h3index_overlap(h3index, h3index) RETURNS boolean + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_contains(h3index, h3index) RETURNS boolean + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_contained(h3index, h3index) RETURNS boolean + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR <-> ( + LEFTARG = h3index, + RIGHTARG = h3index, + PROCEDURE = h3_distance, + COMMUTATOR = <-> +); +CREATE OPERATOR && ( + LEFTARG = h3index, + RIGHTARG = h3index, + PROCEDURE = h3index_overlap, + COMMUTATOR = &&, + RESTRICT = contsel, + JOIN = contjoinsel +); +CREATE OPERATOR @> ( + LEFTARG = h3index, + RIGHTARG = h3index, + PROCEDURE = h3index_contains, + COMMUTATOR = <@, + RESTRICT = contsel, + JOIN = contjoinsel +); +CREATE OPERATOR <@ ( + LEFTARG = h3index, + RIGHTARG = h3index, + PROCEDURE = h3index_contained, + COMMUTATOR = @>, + NEGATOR = <, + RESTRICT = contsel, + JOIN = contjoinsel +); + +-- GiST operator class +CREATE OR REPLACE FUNCTION h3index_gist_consistent(internal, h3index, smallint, oid, internal) RETURNS boolean + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_union(internal, internal) RETURNS h3index + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_compress(internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_decompress(internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_penalty(internal, internal, internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_picksplit(internal, internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_same(h3index, h3index, internal) RETURNS internal + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_gist_distance(internal, h3index, smallint, oid, internal) RETURNS integer + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS gist_h3index_ops DEFAULT FOR TYPE h3index USING gist AS + OPERATOR 3 && , + OPERATOR 6 = , + OPERATOR 7 @> , + OPERATOR 8 <@ , + OPERATOR 15 <-> (h3index, h3index) FOR ORDER BY integer_ops, + + FUNCTION 1 h3index_gist_consistent(internal, h3index, smallint, oid, internal), + FUNCTION 2 h3index_gist_union(internal, internal), + FUNCTION 3 h3index_gist_compress(internal), + FUNCTION 4 h3index_gist_decompress(internal), + FUNCTION 5 h3index_gist_penalty(internal, internal, internal), + FUNCTION 6 h3index_gist_picksplit(internal, internal), + FUNCTION 7 h3index_gist_same(h3index, h3index, internal), + FUNCTION 8 h3index_gist_distance(internal, h3index, smallint, oid, internal); + +-- SP-GiST operator class +CREATE OR REPLACE FUNCTION h3index_spgist_config(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_choose(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_picksplit(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_inner_consistent(internal, internal) RETURNS void + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3index_spgist_leaf_consistent(internal, internal) RETURNS bool + AS 'h3' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE OPERATOR CLASS spgist_h3index_ops DEFAULT FOR TYPE h3index USING spgist AS + OPERATOR 6 = , + OPERATOR 7 @> , + OPERATOR 8 <@ , + + FUNCTION 1 h3index_spgist_config(internal, internal), + FUNCTION 2 h3index_spgist_choose(internal, internal), + FUNCTION 3 h3index_spgist_picksplit(internal, internal), + FUNCTION 4 h3index_spgist_inner_consistent(internal, internal), + FUNCTION 5 h3index_spgist_leaf_consistent(internal, internal); diff --git a/sql/updates/h3--3.6.0-alpha--3.5.0.sql b/sql/updates/h3--3.6.0-alpha--3.5.0.sql new file mode 100644 index 00000000..adde0737 --- /dev/null +++ b/sql/updates/h3--3.6.0-alpha--3.5.0.sql @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Bytes & Brains + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION h3 UPDATE TO '3.6.0'" to load this file. \quit + +--- JUST REVERSE EVERYTHING FROM ALPHA + +-- Hierarchical grid functions (hierarchy.c) +DROP FUNCTION IF EXISTS h3_to_center_child(h3index, resolution integer); + +-- Miscellaneous H3 functions (miscellaneous.c) +DROP FUNCTION IF EXISTS h3_get_pentagon_indexes(resolution integer); + +-- PostgreSQL operators +CREATE OR REPLACE FUNCTION h3_string_to_h3(cstring) RETURNS h3index + AS 'h3', 'h3index_in' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; +CREATE OR REPLACE FUNCTION h3_to_string(h3index) RETURNS cstring + AS 'h3', 'h3index_out' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +-- GiST operator class +DROP OPERATOR CLASS gist_h3index_ops USING gist; +DROP FUNCTION IF EXISTS h3index_gist_consistent(internal, h3index, smallint, oid, internal); +DROP FUNCTION IF EXISTS h3index_gist_union(internal, internal); +DROP FUNCTION IF EXISTS h3index_gist_compress(internal); +DROP FUNCTION IF EXISTS h3index_gist_decompress(internal); +DROP FUNCTION IF EXISTS h3index_gist_penalty(internal, internal, internal); +DROP FUNCTION IF EXISTS h3index_gist_picksplit(internal, internal); +DROP FUNCTION IF EXISTS h3index_gist_same(h3index, h3index, internal); +DROP FUNCTION IF EXISTS h3index_gist_distance(internal, h3index, smallint, oid, internal); + + +-- SP-GiST operator class +DROP OPERATOR CLASS spgist_h3index_ops USING spgist; +DROP FUNCTION IF EXISTS h3index_spgist_config(internal, internal); +DROP FUNCTION IF EXISTS h3index_spgist_choose(internal, internal); +DROP FUNCTION IF EXISTS h3index_spgist_picksplit(internal, internal); +DROP FUNCTION IF EXISTS h3index_spgist_inner_consistent(internal, internal); +DROP FUNCTION IF EXISTS h3index_spgist_leaf_consistent(internal, internal); + +-- general +DROP OPERATOR <-> (h3index, h3index); +DROP OPERATOR && (h3index, h3index); +DROP OPERATOR @> (h3index, h3index); +DROP OPERATOR <@ (h3index, h3index); + +DROP FUNCTION IF EXISTS h3index_overlap(h3index, h3index); +DROP FUNCTION IF EXISTS h3index_contains(h3index, h3index); +DROP FUNCTION IF EXISTS h3index_contained(h3index, h3index); diff --git a/src/extension.in.h b/src/extension.in.h index cb934cc5..a6f686b3 100644 --- a/src/extension.in.h +++ b/src/extension.in.h @@ -33,7 +33,9 @@ typedef struct /* base type in postgres is Datum, and we cannot fit 8 bytes on all platforms so we use pointers */ #define PG_RETURN_H3_INDEX_P(x) PG_RETURN_POINTER(x) -#define PG_GETARG_H3_INDEX_P(x) (H3Index *)PG_GETARG_POINTER(x) +#define PG_GETARG_H3_INDEX_P(x) (H3Index *) PG_GETARG_POINTER(x) +#define DatumGetH3IndexP(x) (H3Index *) DatumGetPointer(x) +#define H3IndexPGetDatum(x) PointerGetDatum(x) /* helper functions to return sets from user fctx */ Datum srf_return_h3_indexes_from_user_fctx(PG_FUNCTION_ARGS); @@ -63,7 +65,7 @@ Datum srf_return_h3_index_distances_from_user_fctx(PG_FUNCTION_ARGS); ) #define DEBUG(msg, ...) \ - ereport(DEBUG1, ( \ + ereport(WARNING, ( \ errmsg(msg, ##__VA_ARGS__) \ )) diff --git a/src/h3Index.h b/src/h3Index.h new file mode 100644 index 00000000..9784af90 --- /dev/null +++ b/src/h3Index.h @@ -0,0 +1,177 @@ +/* + * Copyright 2016-2018 Uber Technologies, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** @file h3Index.h + * @brief H3Index functions. + */ + +#ifndef H3INDEX_H +#define H3INDEX_H + +/** +#include "faceijk.h" +#include "h3api.h" +**/ + +typedef enum +{ + /** H3 digit in center */ + CENTER_DIGIT = 0, + /** H3 digit in k-axes direction */ + K_AXES_DIGIT = 1, + /** H3 digit in j-axes direction */ + J_AXES_DIGIT = 2, + /** H3 digit in j == k direction */ + JK_AXES_DIGIT = J_AXES_DIGIT | K_AXES_DIGIT, /* 3 */ + /** H3 digit in i-axes direction */ + I_AXES_DIGIT = 4, + /** H3 digit in i == k direction */ + IK_AXES_DIGIT = I_AXES_DIGIT | K_AXES_DIGIT, /* 5 */ + /** H3 digit in i == j direction */ + IJ_AXES_DIGIT = I_AXES_DIGIT | J_AXES_DIGIT, /* 6 */ + /** H3 digit in the invalid direction */ + INVALID_DIGIT = 7, + /** Valid digits will be less than this value. Same value as INVALID_DIGIT. + */ + NUM_DIGITS = INVALID_DIGIT +} Direction; + + +/* define's of constants and macros for bitwise manipulation of H3Index's. */ + +/** The number of bits in an H3 index. */ +#define H3_NUM_BITS 64 + +/** The bit offset of the max resolution digit in an H3 index. */ +#define H3_MAX_OFFSET 63 + +/** The bit offset of the mode in an H3 index. */ +#define H3_MODE_OFFSET 59 + +/** The bit offset of the base cell in an H3 index. */ +#define H3_BC_OFFSET 45 + +/** The bit offset of the resolution in an H3 index. */ +#define H3_RES_OFFSET 52 + +/** The bit offset of the reserved bits in an H3 index. */ +#define H3_RESERVED_OFFSET 56 + +/** The number of bits in a single H3 resolution digit. */ +#define H3_PER_DIGIT_OFFSET 3 + +/** 1's in the 4 mode bits, 0's everywhere else. */ +#define H3_MODE_MASK ((uint64_t)(15) << H3_MODE_OFFSET) + +/** 0's in the 4 mode bits, 1's everywhere else. */ +#define H3_MODE_MASK_NEGATIVE (~H3_MODE_MASK) + +/** 1's in the 7 base cell bits, 0's everywhere else. */ +#define H3_BC_MASK ((uint64_t)(127) << H3_BC_OFFSET) + +/** 0's in the 7 base cell bits, 1's everywhere else. */ +#define H3_BC_MASK_NEGATIVE (~H3_BC_MASK) + +/** 1's in the 4 resolution bits, 0's everywhere else. */ +#define H3_RES_MASK (UINT64_C(15) << H3_RES_OFFSET) + +/** 0's in the 4 resolution bits, 1's everywhere else. */ +#define H3_RES_MASK_NEGATIVE (~H3_RES_MASK) + +/** 1's in the 3 reserved bits, 0's everywhere else. */ +#define H3_RESERVED_MASK ((uint64_t)(7) << H3_RESERVED_OFFSET) + +/** 0's in the 3 reserved bits, 1's everywhere else. */ +#define H3_RESERVED_MASK_NEGATIVE (~H3_RESERVED_MASK) + +/** 1's in the 3 bits of res 15 digit bits, 0's everywhere else. */ +#define H3_DIGIT_MASK ((uint64_t)(7)) + +/** 0's in the 7 base cell bits, 1's everywhere else. */ +#define H3_DIGIT_MASK_NEGATIVE (~H3_DIGIT_MASK_NEGATIVE) + +/** H3 index with mode 0, res 0, base cell 0, and 7 for all index digits. */ +#define H3_INIT (UINT64_C(35184372088831)) + +/** + * Gets the integer mode of h3. + */ +#define H3_GET_MODE(h3) ((int)((((h3)&H3_MODE_MASK) >> H3_MODE_OFFSET))) + +/** + * Sets the integer mode of h3 to v. + */ +#define H3_SET_MODE(h3, v) \ + (h3) = (((h3)&H3_MODE_MASK_NEGATIVE) | (((uint64_t)(v)) << H3_MODE_OFFSET)) + +/** + * Gets the integer base cell of h3. + */ +#define H3_GET_BASE_CELL(h3) ((int)((((h3)&H3_BC_MASK) >> H3_BC_OFFSET))) + +/** + * Sets the integer base cell of h3 to bc. + */ +#define H3_SET_BASE_CELL(h3, bc) \ + (h3) = (((h3)&H3_BC_MASK_NEGATIVE) | (((uint64_t)(bc)) << H3_BC_OFFSET)) + +/** + * Gets the integer resolution of h3. + */ +#define H3_GET_RESOLUTION(h3) ((int)((((h3)&H3_RES_MASK) >> H3_RES_OFFSET))) + +/** + * Sets the integer resolution of h3. + */ +#define H3_SET_RESOLUTION(h3, res) \ + (h3) = (((h3)&H3_RES_MASK_NEGATIVE) | (((uint64_t)(res)) << H3_RES_OFFSET)) + +/** + * Gets the resolution res integer digit (0-7) of h3. + */ +#define H3_GET_INDEX_DIGIT(h3, res) \ + ((Direction)((((h3) >> ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET)) & \ + H3_DIGIT_MASK))) + +/** + * Sets a value in the reserved space. Setting to non-zero may produce invalid + * indexes. + */ +#define H3_SET_RESERVED_BITS(h3, v) \ + (h3) = (((h3)&H3_RESERVED_MASK_NEGATIVE) | \ + (((uint64_t)(v)) << H3_RESERVED_OFFSET)) + +/** + * Gets a value in the reserved space. Should always be zero for valid indexes. + */ +#define H3_GET_RESERVED_BITS(h3) \ + ((int)((((h3)&H3_RESERVED_MASK) >> H3_RESERVED_OFFSET))) + +/** + * Sets the resolution res digit of h3 to the integer digit (0-7) + */ +#define H3_SET_INDEX_DIGIT(h3, res, digit) \ + (h3) = (((h3) & ~((H3_DIGIT_MASK \ + << ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET)))) | \ + (((uint64_t)(digit)) \ + << ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET))) + +/** + * Invalid index used to indicate an error from geoToH3 and related functions. + */ +#define H3_INVALID_INDEX 0 + + +#endif diff --git a/src/opclass_gist.c b/src/opclass_gist.c new file mode 100644 index 00000000..60bd1028 --- /dev/null +++ b/src/opclass_gist.c @@ -0,0 +1,303 @@ +/* + * Copyright 2019 Bytes & Brains + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include // Datum, etc. +#include // PG_FUNCTION_ARGS, etc. +#include // making native points +#include // hash_any +#include "access/gist.h" // GiST + +#include // Main H3 include +#include "extension.h" + +#define H3_ROOT_INDEX -1 + +static int +gist_cmp(H3Index * a, H3Index * b) +{ + int aRes = h3GetResolution(*a); + int bRes = h3GetResolution(*b); + H3Index aParent = h3ToParent(*a, bRes); + H3Index bParent = h3ToParent(*b, aRes); + + /* a contains b */ + if (*a == H3_ROOT_INDEX || *a == bParent) + { + return 1; + } + + /* a contained by b */ + if (*b == H3_ROOT_INDEX || *b == aParent) + { + return -1; + } + + /* no overlap */ + return 0; +} + +/** + * GiST support + */ + +static H3Index +common_ancestor(H3Index a, H3Index b) +{ + H3Index aParent, + bParent; + int res = h3GetResolution(a); + int bRes = h3GetResolution(b); + + if (bRes < res) + res = bRes; + + for (int i = res; i >= 0; i--) + { + aParent = h3ToParent(a, i); + bParent = h3ToParent(b, i); + if (aParent == bParent) + return aParent; + } + + return H3_ROOT_INDEX; +} + +/** + * The GiST Consistent method for H3 indexes + * Should return false if for all data items x below entry, + * the predicate x op query == false, where op is the oper + * corresponding to strategy in the pg_amop table. + */ +PG_FUNCTION_INFO_V1(h3index_gist_consistent); +Datum +h3index_gist_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + H3Index *query = PG_GETARG_H3_INDEX_P(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + H3Index *key = DatumGetH3IndexP(entry->key); + + /* When the result is true, a recheck flag must also be returned. */ + *recheck = true; + + switch (strategy) + { + case RTOverlapStrategyNumber: + PG_RETURN_BOOL(gist_cmp(key, query) != 0); + case RTContainsStrategyNumber: + PG_RETURN_BOOL(gist_cmp(key, query) > 0); + case RTContainedByStrategyNumber: + if (GIST_LEAF(entry)) + { + PG_RETURN_BOOL(gist_cmp(key, query) < 0); + } + /* internal nodes, just check if we overlap */ + PG_RETURN_BOOL(gist_cmp(key, query) != 0); + default: + ereport(ERROR, ( + errcode(ERRCODE_INTERNAL_ERROR), + errmsg("unrecognized StrategyNumber: %d", strategy)) + ); + } +} + +/** + * The GiST Union method for H3 indexes + * returns the minimal H3 index that encloses all the entries in entryvec + */ +PG_FUNCTION_INFO_V1(h3index_gist_union); +Datum +h3index_gist_union(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GISTENTRY *entries = entryvec->vector; + int n = entryvec->n; + H3Index *out, + *tmp; + + out = palloc(sizeof(H3Index)); + tmp = DatumGetH3IndexP(entries[0].key); + *out = *tmp; + + for (int i = 1; i < n; i++) + { + tmp = DatumGetH3IndexP(entries[i].key); + *out = common_ancestor(*out, *tmp); + } + + PG_RETURN_H3_INDEX_P(out); +} + +/** + * GiST Compress and Decompress methods for H3Indexes + * do not do anything. + */ +PG_FUNCTION_INFO_V1(h3index_gist_compress); +Datum +h3index_gist_compress(PG_FUNCTION_ARGS) +{ + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); +} + +PG_FUNCTION_INFO_V1(h3index_gist_decompress); +Datum +h3index_gist_decompress(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); +} + +/* +** The GiST Penalty method for H3 indexes +** We use change resolution as our penalty metric +*/ +PG_FUNCTION_INFO_V1(h3index_gist_penalty); +Datum +h3index_gist_penalty(PG_FUNCTION_ARGS) +{ + GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); + float *penalty = (float *) PG_GETARG_POINTER(2); + + H3Index *orig = DatumGetH3IndexP(origentry->key); + H3Index *new = DatumGetH3IndexP(newentry->key); + + H3Index ancestor = common_ancestor(*orig, *new); + + *penalty = (float) h3GetResolution(*orig) - h3GetResolution(ancestor); + + PG_RETURN_POINTER(penalty); +} + +/** + * The GiST PickSplit method for H3 indexes + */ +PG_FUNCTION_INFO_V1(h3index_gist_picksplit); +Datum +h3index_gist_picksplit(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + + OffsetNumber maxoff = entryvec->n - 1; + GISTENTRY *ent = entryvec->vector; + int i, + nbytes; + OffsetNumber *left, + *right; + H3Index *tmp_union, + *unionL, + *unionR; + GISTENTRY **raw_entryvec; + + nbytes = (maxoff + 1) * sizeof(OffsetNumber); + + v->spl_left = (OffsetNumber *) palloc(nbytes); + left = v->spl_left; + v->spl_nleft = 0; + + v->spl_right = (OffsetNumber *) palloc(nbytes); + right = v->spl_right; + v->spl_nright = 0; + + unionL = NULL; + unionR = NULL; + + /* Initialize the raw entry vector. */ + raw_entryvec = (GISTENTRY **) malloc(entryvec->n * sizeof(void *)); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + raw_entryvec[i] = &(ent[i]); + + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) + { + int real_index = raw_entryvec[i] - ent; + + tmp_union = DatumGetH3IndexP(ent[real_index].key); + Assert(tmp_union != NULL); + + /* + * Choose where to put the index entries and update unionL and unionR + * accordingly. Append the entries to either v_spl_left or + * v_spl_right, and care about the counters. + */ + + if (v->spl_nleft < v->spl_nright) + { + if (unionL == NULL) + unionL = tmp_union; + else + *unionL = common_ancestor(*unionL, *tmp_union); + + *left = real_index; + ++left; + ++(v->spl_nleft); + } + else + { + if (unionR == NULL) + unionR = tmp_union; + else + *unionR = common_ancestor(*unionR, *tmp_union); + + *right = real_index; + ++right; + ++(v->spl_nright); + } + } + + v->spl_ldatum = PointerGetDatum(unionL); + v->spl_rdatum = PointerGetDatum(unionR); + PG_RETURN_POINTER(v); +} + +PG_FUNCTION_INFO_V1(h3index_gist_same); +Datum +h3index_gist_same(PG_FUNCTION_ARGS) +{ + H3Index *a = PG_GETARG_H3_INDEX_P(0); + H3Index *b = PG_GETARG_H3_INDEX_P(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + + *result = *a == *b; + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(h3index_gist_distance); +Datum +h3index_gist_distance(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + H3Index *query = PG_GETARG_H3_INDEX_P(1); + + /* StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); */ + /* Oid subtype = PG_GETARG_OID(3); */ + /* bool *recheck = (bool *) PG_GETARG_POINTER(4); */ + + H3Index *key = DatumGetH3IndexP(entry->key); + + /* + * int aRes = h3GetResolution(*query); int bRes = + * h3GetResolution(*key); H3Index aParent = h3ToCenterChild(*query, + * bRes); H3Index bParent = h3ToCenterChild(*key, aRes); + */ + + int distance = h3Distance(*query, *key); + + DEBUG(" dist %i", distance); + PG_RETURN_INT32(distance); +} diff --git a/src/opclass_spgist.c b/src/opclass_spgist.c new file mode 100644 index 00000000..5c32bc50 --- /dev/null +++ b/src/opclass_spgist.c @@ -0,0 +1,426 @@ +/* + * Copyright 2019 Bytes & Brains + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include // Datum, etc. +#include // PG_FUNCTION_ARGS, etc. +#include // SP-GiST +#include "catalog/pg_type.h" + +#include // Main H3 include +#include "h3Index.h" +#include "extension.h" + +#include "inttypes.h" + +PG_FUNCTION_INFO_V1(h3index_spgist_config); +PG_FUNCTION_INFO_V1(h3index_spgist_choose); +PG_FUNCTION_INFO_V1(h3index_spgist_picksplit); +PG_FUNCTION_INFO_V1(h3index_spgist_inner_consistent); +PG_FUNCTION_INFO_V1(h3index_spgist_leaf_consistent); + +#define H3_ROOT_INDEX -1 +#define NUM_BASE_CELLS 122 +#define MAX_H3_RES 15 + +#define H3_NUM_CHILDREN 7 + +static int +spgist_cmp(H3Index * a, H3Index * b) +{ + int aRes = h3GetResolution(*a); + int bRes = h3GetResolution(*b); + H3Index aParent = h3ToParent(*a, bRes); + H3Index bParent = h3ToParent(*b, aRes); + + /* a contains b */ + if (*a == H3_ROOT_INDEX || *a == bParent) + { + return 1; + } + + /* a contained by b */ + if (*b == H3_ROOT_INDEX || *b == aParent) + { + return -1; + } + + /* no overlap */ + return 0; +} + +/* + * h3index_spgist_config + * Returns static information about the index implementation + * + * Including the data type OIDs of the prefix and node label data types + */ +Datum +h3index_spgist_config(PG_FUNCTION_ARGS) +{ + /*-------------------------------------------------------------------------- + * Oid attType data type to be indexed + *-------------------------------------------------------------------------- + */ + spgConfigIn *in = (spgConfigIn *) PG_GETARG_POINTER(0); + + /*-------------------------------------------------------------------------- + * Oid prefixType data type of inner-tupleprefixes + * Oid labelType data type of inner-tuple node labels + * Oid leafType data type of leaf-tuple values + * bool canReturnData opclass can reconstruct original data + * bool longValuesOK opclass can cope with values > 1 page + *-------------------------------------------------------------------------- + */ + spgConfigOut *out = (spgConfigOut *) PG_GETARG_POINTER(1); + + /* prefix is parent H3 index */ + out->prefixType = in->attType; + /* no need for labels */ + out->labelType = VOIDOID; + + out->canReturnData = true; + out->longValuesOK = false; + + PG_RETURN_VOID(); +} + +/* + * h3index_spgist_choose + * Chooses a method for inserting a new value into an inner tuple + * + * The choose function can determine either that the new value matches one of + * the existing child nodes, or that a new child node must be added, or that the + * new value is inconsistent with the tuple prefix and so the inner tuple must + * be split to create a less restrictive prefix. + * + * NOTE: When working with an inner tuple having unlabeled nodes, it is an error + * for choose to return spgAddNode, since the set of nodes is supposed to be + * fixed in such cases. Also, there is no provision for generating an unlabeled + * node in spgSplitTuple actions, since it is expected that an spgAddNode action + * will be needed as well. + */ +Datum +h3index_spgist_choose(PG_FUNCTION_ARGS) +{ + /*-------------------------------------------------------------------------- + * Datum datum original datum to be indexed + * Datum leafDatum current datum to be stored at leaf + * int level current level (counting from zero) + * + * (data from current inner tuple) + * bool allTheSame tuple is marked all-the-same? + * bool hasPrefix tuple has a prefix? + * Datum prefixDatum if so, the prefix value + * int nNodes number of nodes in the inner tuple + * Datum* nodeLabels node label values (NULL if none) + *-------------------------------------------------------------------------- + */ + spgChooseIn *in = (spgChooseIn *) PG_GETARG_POINTER(0); + + /*-------------------------------------------------------------------------- + * spgChooseResultType resultType action code, see below + * + * -- results for spgMatchNode -- + * int nodeN descend to this node (index from 0) + * int levelAdd increment level by this much + * Datum restDatum new leaf datum + * + * -- results for spgAddNode -- + * Datum nodeLabel new node's label + * int nodeN where to insert it (index from 0) + * + * -- results for spgSplitTuple -- + * (info to form new upper-level inner tuple with one child tuple) + * bool prefixHasPrefix tuple should have a prefix? + * Datum prefixPrefixDatum if so, its value + * int prefixNNodes number of nodes + * Datum* prefixNodeLabels their labels (or NULL for no labels) + * int childNodeN which node gets child tuple + * (info to form new lower-level inner tuple with all old nodes) + * bool postfixHasPrefix tuple should have a prefix? + * Datum postfixPrefixDatum if so, its value + *-------------------------------------------------------------------------- + */ + spgChooseOut *out = (spgChooseOut *) PG_GETARG_POINTER(1); + + int resolution = in->level; + H3Index *insert = DatumGetH3IndexP(in->datum); + int node; + + out->resultType = spgMatchNode; + out->result.matchNode.levelAdd = 1; + out->result.matchNode.restDatum = H3IndexPGetDatum(insert); + + if (!in->allTheSame) + { + if (resolution == 0) + { + node = h3GetBaseCell(*insert); + } + else + { + node = H3_GET_INDEX_DIGIT(*insert, resolution); + } + + out->result.matchNode.nodeN = node; + } + + PG_RETURN_VOID(); +} + +/** + * Decides how to create a new inner tuple over a set of leaf tuples. + * + * An inner tuple contains a set of one or more nodes, + * which represent groups of similar leaf values. + * + * struct spgPickSplitIn + * int nTuples number of leaf tuples + * Datum *datums their datums (array of length nTuples) + * int level current level (counting from zero) + * + * struct spgPickSplitOut + * bool hasPrefix new inner tuple should have a prefix? + * Datum prefixDatum if so, its value + * int nNodes number of nodes for new inner tuple + * Datum *nodeLabels their labels (or NULL for no labels) + * int *mapTuplesToNodes node index for each leaf tuple + * Datum *leafTupleDatums datum to store in each new leaf tuple + */ +Datum +h3index_spgist_picksplit(PG_FUNCTION_ARGS) +{ + spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); + spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); + int resolution = in->level; + + /* we don't need node labels */ + out->nodeLabels = NULL; + out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); + out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); + + if (resolution == 0) + { + /* at resolution 0 there is one node per base cell */ + out->nNodes = NUM_BASE_CELLS; + out->hasPrefix = false; + } + else + { + /* at finer resolutions there is exactly 7 nodes, one per child */ + H3Index *parent = palloc(sizeof(H3Index)); + H3Index *first = DatumGetH3IndexP(in->datums[0]); + + *parent = h3ToParent(*first, resolution); + + /* + * TODO: consider decreasing nNodes for pentagons which only have 6 + * children? + */ + out->nNodes = H3_NUM_CHILDREN; + out->hasPrefix = true; + out->prefixDatum = H3IndexPGetDatum(parent); + } + + /* map each leaf tuple to node in the new inner tuple */ + for (int i = 0; i < in->nTuples; i++) + { + H3Index *insert = DatumGetH3IndexP(in->datums[i]); + int node; + + if (resolution == 0) + { + /* first resolution is base cells */ + node = h3GetBaseCell(*insert); + } + else if (h3GetResolution(*insert) >= resolution) + { + /* finer resolutions use index digit 0-6 */ + node = H3_GET_INDEX_DIGIT(*insert, resolution); + } + else + { + /* coarse indexes are put into center node */ + node = 0; + } + + out->leafTupleDatums[i] = H3IndexPGetDatum(insert); + out->mapTuplesToNodes[i] = node; + } + + PG_RETURN_VOID(); +} + +/** + * Returns set of nodes (branches) to follow during tree search. + * + * Each query is a single H3 index to be checked against the parent prefix + * + * We either return all or none (except for res 0) + */ +Datum +h3index_spgist_inner_consistent(PG_FUNCTION_ARGS) +{ + spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); + spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); + H3Index *parent = NULL; + int bc, + i; + bool stop; + int innerNodes = in->nNodes; + + if (in->hasPrefix) + { + parent = DatumGetH3IndexP(in->prefixDatum); + } + + if (in->allTheSame) + { + /* Report that all nodes should be visited */ + out->nNodes = in->nNodes; + out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); + for (i = 0; i < in->nNodes; i++) + { + out->nodeNumbers[i] = i; + } + PG_RETURN_VOID(); + } + + out->levelAdds = palloc(sizeof(int) * innerNodes); + for (i = 0; i < innerNodes; ++i) + out->levelAdds[i] = 1; + + /* We must descend into the quadrant(s) identified by which */ + out->nodeNumbers = (int *) palloc(sizeof(int) * innerNodes); + out->nNodes = 0; + + /* "which" is a bitmask of child nodes that satisfy all constraints */ + bc = -1; + stop = false; + for (i = 0; i < in->nkeys; i++) + { + /* each scankey is a constraint to be checked against */ + StrategyNumber strategy = in->scankeys[i].sk_strategy; + H3Index *query = DatumGetH3IndexP(in->scankeys[i].sk_argument); + + if (parent == NULL) + { + if (bc > -1) + { + stop = true; + } + bc = h3GetBaseCell(*query); + } + else + { + switch (strategy) + { + case RTSameStrategyNumber: + if (spgist_cmp(parent, query) == 0) + stop = true; + break; + case RTContainsStrategyNumber: + if (spgist_cmp(parent, query) == 0) + stop = true; + /* no overlap */ + break; + case RTContainedByStrategyNumber: + if (spgist_cmp(parent, query) == 0) + stop = true; + /* no overlap */ + break; + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + break; + } + } + + if (stop) + break; /* no need to consider remaining conditions */ + } + + if (!stop) + { + if (bc > -1) + { + out->nodeNumbers[out->nNodes] = bc; + out->nNodes++; + } + else + { + for (i = 0; i < innerNodes; i++) + { + out->nodeNumbers[out->nNodes] = i; + out->nNodes++; + } + } + } + + PG_RETURN_VOID(); +} + +/** + * Returns true if a leaf satisfies a query. + * + * To satisfy the query, the leaf must satisfy all the conditions described by scankeys. + */ +Datum +h3index_spgist_leaf_consistent(PG_FUNCTION_ARGS) +{ + spgLeafConsistentIn *in = (spgLeafConsistentIn *) PG_GETARG_POINTER(0); + spgLeafConsistentOut *out = (spgLeafConsistentOut *) PG_GETARG_POINTER(1); + + H3Index *leaf = DatumGetH3IndexP(in->leafDatum); + bool retval = true; + + out->leafValue = in->leafDatum; + /* leafDatum is what it is... */ + out->recheck = false; + /* all tests are exact */ + + /* Perform the required comparison(s) */ + for (int i = 0; i < in->nkeys; i++) + { + StrategyNumber strategy = in->scankeys[i].sk_strategy; + H3Index *query = DatumGetH3IndexP(in->scankeys[i].sk_argument); + + switch (strategy) + { + case RTSameStrategyNumber: + /* leaf is equal to query */ + retval = (*leaf == *query); + break; + case RTContainsStrategyNumber: + /* leaf contains the query */ + retval = (spgist_cmp(leaf, query) > 0); + break; + case RTContainedByStrategyNumber: + /* leaf is contained by the query */ + retval = (spgist_cmp(leaf, query) < 0); + break; + default: + ereport(ERROR, ( + errcode(ERRCODE_INTERNAL_ERROR), + errmsg("unrecognized StrategyNumber: %d", strategy)) + ); + } + + if (!retval) + break; + } + + PG_RETURN_BOOL(retval); +} diff --git a/test/expected/opclass_gist.out b/test/expected/opclass_gist.out new file mode 100644 index 00000000..329392f4 --- /dev/null +++ b/test/expected/opclass_gist.out @@ -0,0 +1,16 @@ +\pset tuples_only on +\set hexagon '\'831c02fffffffff\'::h3index' +CREATE TABLE h3_test_gist (hex h3index); +CREATE INDEX GIST_IDX ON h3_test_gist USING gist(hex); +INSERT INTO h3_test_gist (hex) SELECT h3_to_parent(:hexagon); +INSERT INTO h3_test_gist (hex) SELECT h3_to_children(:hexagon); +INSERT INTO h3_test_gist (hex) SELECT h3_to_center_child(:hexagon, 15); +-- +-- TEST GiST +-- +SELECT COUNT(*) = 1 FROM h3_test_gist WHERE hex @> :hexagon; + t + +SELECT COUNT(*) = 8 FROM h3_test_gist WHERE hex <@ :hexagon; + t + diff --git a/test/expected/opclass_spgist.out b/test/expected/opclass_spgist.out new file mode 100644 index 00000000..83335bcd --- /dev/null +++ b/test/expected/opclass_spgist.out @@ -0,0 +1,22 @@ +\pset tuples_only on +\set hexagon '\'831c02fffffffff\'::h3index' +CREATE TABLE h3_test_spgist (hex h3index); +CREATE INDEX SPGIST_IDX ON h3_test_spgist USING spgist(hex); +INSERT INTO h3_test_spgist (hex) SELECT h3_to_parent(:hexagon); +INSERT INTO h3_test_spgist (hex) SELECT h3_to_children(:hexagon); +INSERT INTO h3_test_spgist (hex) SELECT h3_to_center_child(:hexagon, 15); +-- +-- TEST SP-GiST +-- +SELECT COUNT(*) = 1 FROM h3_test_spgist WHERE hex @> :hexagon; + t + +SELECT COUNT(*) = 8 FROM h3_test_spgist WHERE hex <@ :hexagon; + t + +-- +TRUNCATE TABLE h3_test_spgist; +INSERT INTO h3_test_spgist (hex) SELECT h3_to_children(h3_to_center_child(:hexagon, 10), 15); +SELECT COUNT(*) = 16807 FROM h3_test_spgist WHERE hex <@ :hexagon; + t + diff --git a/test/sql/opclass_gist.sql b/test/sql/opclass_gist.sql new file mode 100644 index 00000000..9e7d5a09 --- /dev/null +++ b/test/sql/opclass_gist.sql @@ -0,0 +1,14 @@ +\pset tuples_only on +\set hexagon '\'831c02fffffffff\'::h3index' + +CREATE TABLE h3_test_gist (hex h3index); +CREATE INDEX GIST_IDX ON h3_test_gist USING gist(hex); +INSERT INTO h3_test_gist (hex) SELECT h3_to_parent(:hexagon); +INSERT INTO h3_test_gist (hex) SELECT h3_to_children(:hexagon); +INSERT INTO h3_test_gist (hex) SELECT h3_to_center_child(:hexagon, 15); + +-- +-- TEST GiST +-- +SELECT COUNT(*) = 1 FROM h3_test_gist WHERE hex @> :hexagon; +SELECT COUNT(*) = 8 FROM h3_test_gist WHERE hex <@ :hexagon; diff --git a/test/sql/opclass_spgist.sql b/test/sql/opclass_spgist.sql new file mode 100644 index 00000000..35d0a2fd --- /dev/null +++ b/test/sql/opclass_spgist.sql @@ -0,0 +1,20 @@ +\pset tuples_only on +\set hexagon '\'831c02fffffffff\'::h3index' + +CREATE TABLE h3_test_spgist (hex h3index); +CREATE INDEX SPGIST_IDX ON h3_test_spgist USING spgist(hex); +INSERT INTO h3_test_spgist (hex) SELECT h3_to_parent(:hexagon); +INSERT INTO h3_test_spgist (hex) SELECT h3_to_children(:hexagon); +INSERT INTO h3_test_spgist (hex) SELECT h3_to_center_child(:hexagon, 15); + +-- +-- TEST SP-GiST +-- +SELECT COUNT(*) = 1 FROM h3_test_spgist WHERE hex @> :hexagon; +SELECT COUNT(*) = 8 FROM h3_test_spgist WHERE hex <@ :hexagon; + +-- + +TRUNCATE TABLE h3_test_spgist; +INSERT INTO h3_test_spgist (hex) SELECT h3_to_children(h3_to_center_child(:hexagon, 10), 15); +SELECT COUNT(*) = 16807 FROM h3_test_spgist WHERE hex <@ :hexagon; \ No newline at end of file