From cdb8ed9c61432d76d93fc274304ae6f6f19a70b2 Mon Sep 17 00:00:00 2001 From: Sven Thelemann Date: Thu, 16 Jan 2025 17:44:29 +0100 Subject: [PATCH 1/9] feat(location): add location data A new table `location` is created to accommodate location data of an account or an event based on GPS coordinates. The new function `distance' calculates the linear distance between two locations. --- src/deploy/function_distance.sql | 33 +++++++ src/deploy/table_account_private.sql | 4 +- src/deploy/table_event.sql | 2 + src/deploy/table_location.sql | 50 ++++++++++ src/revert/function_distance.sql | 5 + src/revert/table_location.sql | 7 ++ src/sqitch.plan | 2 + src/verify/function_distance.sql | 9 ++ src/verify/table_account_private.sql | 3 +- src/verify/table_event.sql | 3 +- src/verify/table_location.sql | 28 ++++++ test/schema/schema.definition.sql | 136 +++++++++++++++++++++++++++ 12 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 src/deploy/function_distance.sql create mode 100644 src/deploy/table_location.sql create mode 100644 src/revert/function_distance.sql create mode 100644 src/revert/table_location.sql create mode 100644 src/verify/function_distance.sql create mode 100644 src/verify/table_location.sql diff --git a/src/deploy/function_distance.sql b/src/deploy/function_distance.sql new file mode 100644 index 00000000..05e152aa --- /dev/null +++ b/src/deploy/function_distance.sql @@ -0,0 +1,33 @@ +BEGIN; + +CREATE FUNCTION maevsi.distance( + lat1 DOUBLE PRECISION, lon1 DOUBLE PRECISION, + lat2 DOUBLE PRECISION, lon2 DOUBLE PRECISION +) RETURNS DOUBLE PRECISION AS $$ +DECLARE + earthRadius DOUBLE PRECISION; + r DOUBLE PRECISION; + hLat DOUBLE PRECISION; + hLon DOUBLE PRECISION; + a DOUBLE PRECISION; + distance DOUBLE PRECISION; +BEGIN + earthRadius := 6371; + r := pi() / 180; + hLat := sin((lat2-lat1)*r/2.0); + hLon := sin((lon2-lon1)*r/2.0); + a := hLat*hLat + hLon*hLon*cos(lat1*r)*cos(lat2*r); -- Haversine + distance := earthRadius * 2 * atan2(sqrt(a), sqrt(1-a)); + -- If atan2 is not available, use asin(least(1, sqrt(a)) (including protection against rounding errors). + -- distance := earthRadius * 2 * asin(least(1, sqrt(a))) + RETURN distance; +END; +$$ LANGUAGE PLPGSQL IMMUTABLE; + +COMMENT ON FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Calculate the distance between to locations given their GPS coordinates.'; + +GRANT EXECUTE ON FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; + +COMMIT; + + diff --git a/src/deploy/table_account_private.sql b/src/deploy/table_account_private.sql index b79c2f61..7d80e306 100644 --- a/src/deploy/table_account_private.sql +++ b/src/deploy/table_account_private.sql @@ -12,7 +12,8 @@ CREATE TABLE maevsi_private.account ( password_hash TEXT NOT NULL, password_reset_verification UUID, password_reset_verification_valid_until TIMESTAMP, - upload_quota_bytes BIGINT NOT NULL DEFAULT 10485760 -- 10 mebibyte + upload_quota_bytes BIGINT NOT NULL DEFAULT 10485760, -- 10 mebibyte + location_id UUID REFERENCES maevsi.location(id) ); COMMENT ON TABLE maevsi_private.account IS 'Private account data.'; @@ -27,6 +28,7 @@ COMMENT ON COLUMN maevsi_private.account.password_hash IS 'The account''s passwo COMMENT ON COLUMN maevsi_private.account.password_reset_verification IS 'The UUID used to reset a password, or null if there is no pending reset request.'; COMMENT ON COLUMN maevsi_private.account.password_reset_verification_valid_until IS 'The timestamp until which a password reset is valid.'; COMMENT ON COLUMN maevsi_private.account.upload_quota_bytes IS 'The account''s upload quota in bytes.'; +COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the account''s location data.'; CREATE FUNCTION maevsi_private.account_email_address_verification_valid_until() RETURNS TRIGGER AS $$ BEGIN diff --git a/src/deploy/table_event.sql b/src/deploy/table_event.sql index 8c219528..6dfac8a0 100644 --- a/src/deploy/table_event.sql +++ b/src/deploy/table_event.sql @@ -17,6 +17,7 @@ CREATE TABLE maevsi.event ( start TIMESTAMP WITH TIME ZONE NOT NULL, url TEXT CHECK (char_length("url") < 300 AND "url" ~ '^https:\/\/'), visibility maevsi.event_visibility NOT NULL, + location_id UUID REFERENCES maevsi.location(id), UNIQUE (author_account_id, slug) ); @@ -37,6 +38,7 @@ COMMENT ON COLUMN maevsi.event.slug IS 'The event''s name, slugified.'; COMMENT ON COLUMN maevsi.event.start IS 'The event''s start date and time, with timezone.'; COMMENT ON COLUMN maevsi.event.url IS 'The event''s unified resource locator.'; COMMENT ON COLUMN maevsi.event.visibility IS 'The event''s visibility.'; +COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the event''s location data.'; -- GRANTs, RLS and POLICYs are specified in 'table_event_policy`. diff --git a/src/deploy/table_location.sql b/src/deploy/table_location.sql new file mode 100644 index 00000000..123bc3bc --- /dev/null +++ b/src/deploy/table_location.sql @@ -0,0 +1,50 @@ +BEGIN; + +CREATE TABLE maevsi.location ( + id UUID PRIMARY KEY, + location_type CHAR(1) NOT NULL, + latitude DOUBLE PRECISION NOT NULL, + longitude DOUBLE PRECISION NOT NULL, + -- possible future extension (using PostGIS): + -- geom GEOMETRY + + CHECK (location_type IN ('A', 'E')) +); + +COMMENT ON TABLE maevsi.location IS 'Location data based on GPS coordnates.'; +COMMENT ON COLUMN maevsi.location.id IS 'The locations''s internal id.'; +COMMENT ON COLUMN maevsi.location.location_type IS 'The type of the location (A = account, E = event)'; +COMMENT ON COLUMN maevsi.location.latitude IS 'reference to an account (if not null).'; +COMMENT ON COLUMN maevsi.location.longitude IS 'reference to an account (if not null).'; + +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE maevsi.location TO maevsi_account; +GRANT SELECT ON TABLE maevsi.location TO maevsi_anonymous; + +/* +-- If PostGIS is used: + +-- A spatial index could be used by function ST_DWithin (returning true if distance between +-- two locations location is <= a given distance. +CREATE INDEX maevsi.location_geom_idx ON maevsi.location USING GIST (geom); + +CREATE FUNCTION maevsi.location_update_geom() RETURNS TRIGGER AS $$ + BEGIN + NEW.geom = ST_Point(NEW.longitude, NEW.latitude, 4326); + RETURN NEW; + END; +$$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.location_update_geom() IS 'Sets column geom to a value based on longitude and latitude, using spatial reference id 4326.'; + +GRANT EXECUTE ON FUNCTION maevsi.location_update_geom() TO maevsi_account; + +CREATE TRIGGER maevsi_location_update_geom + BEFORE + INSERT + OR UPDATE OF latitude, longitude + ON maevsi.location + FOR EACH ROW + EXECUTE PROCEDURE maevsi.location_update_geom(); +*/ + +COMMIT; diff --git a/src/revert/function_distance.sql b/src/revert/function_distance.sql new file mode 100644 index 00000000..67e25d0d --- /dev/null +++ b/src/revert/function_distance.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION); + +COMMIT; diff --git a/src/revert/table_location.sql b/src/revert/table_location.sql new file mode 100644 index 00000000..0672b72f --- /dev/null +++ b/src/revert/table_location.sql @@ -0,0 +1,7 @@ +BEGIN; + +--DROP TRIGGER maevsi_location_update_geom ON maevsi.location; +--DROP FUNCTION maevsi.location_update_geom(); +DROP TABLE maevsi.location; + +COMMIT; diff --git a/src/sqitch.plan b/src/sqitch.plan index d678ad56..ab980ff3 100644 --- a/src/sqitch.plan +++ b/src/sqitch.plan @@ -14,6 +14,8 @@ function_invoker_account_id [privilege_execute_revoke schema_public role_account enum_invitation_feedback [schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Possible answers to an invitation: accepted, canceled. enum_event_visibility [schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Possible visibilities of events and event groups: public, private. table_notification [schema_private] 1970-01-01T00:00:00Z Jonas Thelemann # Notifications that are sent via pg_notify. +table_location [schema_public role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # Location data based on GPS coordinates +function_distance [schema_public role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # Calculates distance betwenn two GPS points. table_account_private [schema_private schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Add private table account. table_account_public [schema_public schema_private table_account_private] 1970-01-01T00:00:00Z Jonas Thelemann # Add public table account. table_event_group [schema_public role_account role_anonymous table_account_public enum_event_visibility] 1970-01-01T00:00:00Z Jonas Thelemann # Add table event_group. diff --git a/src/verify/function_distance.sql b/src/verify/function_distance.sql new file mode 100644 index 00000000..b1321735 --- /dev/null +++ b/src/verify/function_distance.sql @@ -0,0 +1,9 @@ +BEGIN; + +DO $$ +BEGIN + ASSERT (SELECT pg_catalog.has_function_privilege('maevsi_account', 'maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION)', 'EXECUTE')); + ASSERT NOT (SELECT pg_catalog.has_function_privilege('maevsi_anonymous', 'maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION)', 'EXECUTE')); +END $$; + +ROLLBACK; diff --git a/src/verify/table_account_private.sql b/src/verify/table_account_private.sql index 173df64e..86caeccd 100644 --- a/src/verify/table_account_private.sql +++ b/src/verify/table_account_private.sql @@ -10,7 +10,8 @@ SELECT id, password_hash, password_reset_verification, password_reset_verification_valid_until, - upload_quota_bytes + upload_quota_bytes, + location_id FROM maevsi_private.account WHERE FALSE; DO $$ diff --git a/src/verify/table_event.sql b/src/verify/table_event.sql index 58f89ce2..fc1ffbc9 100644 --- a/src/verify/table_event.sql +++ b/src/verify/table_event.sql @@ -14,7 +14,8 @@ SELECT id, slug, start, url, - visibility + visibility, + location_id FROM maevsi.event WHERE FALSE; ROLLBACK; diff --git a/src/verify/table_location.sql b/src/verify/table_location.sql new file mode 100644 index 00000000..840ab251 --- /dev/null +++ b/src/verify/table_location.sql @@ -0,0 +1,28 @@ +BEGIN; + +SELECT id, + location_type, + latitude, + longitude +FROM maevsi.location WHERE FALSE; + +DO $$ +BEGIN + ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'SELECT')); + ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'INSERT')); + ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'UPDATE')); + ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'DELETE')); + ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'SELECT')); + ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'INSERT')); + ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'UPDATE')); + ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'DELETE')); + ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'SELECT')); + ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'INSERT')); + ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'UPDATE')); + ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'DELETE')); + +-- ASSERT (SELECT pg_catalog.has_function_privilege('maevsi_account', 'maevsi.location_update_geom()', 'EXECUTE')); + +END $$; + +ROLLBACK; diff --git a/test/schema/schema.definition.sql b/test/schema/schema.definition.sql index 7ef851e8..0cd66c42 100644 --- a/test/schema/schema.definition.sql +++ b/test/schema/schema.definition.sql @@ -704,6 +704,43 @@ ALTER FUNCTION maevsi.authenticate(username text, password text) OWNER TO postgr COMMENT ON FUNCTION maevsi.authenticate(username text, password text) IS 'Creates a JWT token that will securely identify an account and give it certain permissions.'; +-- +-- Name: distance(double precision, double precision, double precision, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) RETURNS double precision + LANGUAGE plpgsql IMMUTABLE + AS $$ +DECLARE + earthRadius DOUBLE PRECISION; + r DOUBLE PRECISION; + hLat DOUBLE PRECISION; + hLon DOUBLE PRECISION; + a DOUBLE PRECISION; + distance DOUBLE PRECISION; +BEGIN + earthRadius := 6371; + r := pi() / 180; + hLat := sin((lat2-lat1)*r/2.0); + hLon := sin((lon2-lon1)*r/2.0); + a := hLat*hLat + hLon*hLon*cos(lat1*r)*cos(lat2*r); -- Haversine + distance := earthRadius * 2 * atan2(sqrt(a), sqrt(1-a)); + -- If atan2 is not available, use asin(least(1, sqrt(a)) (including protection against rounding errors). + -- distance := earthRadius * 2 * asin(least(1, sqrt(a))) + RETURN distance; +END; +$$; + + +ALTER FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) OWNER TO postgres; + +-- +-- Name: FUNCTION distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) IS 'Calculate the distance between to locations given their GPS coordinates.'; + + SET default_tablespace = ''; SET default_table_access_method = heap; @@ -728,6 +765,7 @@ CREATE TABLE maevsi.event ( start timestamp with time zone NOT NULL, url text, visibility maevsi.event_visibility NOT NULL, + location_id uuid, CONSTRAINT event_description_check CHECK (((char_length(description) > 0) AND (char_length(description) < 1000000))), CONSTRAINT event_invitee_count_maximum_check CHECK ((invitee_count_maximum > 0)), CONSTRAINT event_location_check CHECK (((char_length(location) > 0) AND (char_length(location) < 300))), @@ -2564,6 +2602,56 @@ COMMENT ON COLUMN maevsi.legal_term_acceptance.account_id IS 'The user account I COMMENT ON COLUMN maevsi.legal_term_acceptance.legal_term_id IS 'The ID of the legal terms that were accepted. Deletion of these legal terms is restricted while they are still referenced in this table.'; +-- +-- Name: location; Type: TABLE; Schema: maevsi; Owner: postgres +-- + +CREATE TABLE maevsi.location ( + id uuid NOT NULL, + location_type character(1) NOT NULL, + latitude double precision NOT NULL, + longitude double precision NOT NULL, + CONSTRAINT location_location_type_check CHECK ((location_type = ANY (ARRAY['A'::bpchar, 'E'::bpchar]))) +); + + +ALTER TABLE maevsi.location OWNER TO postgres; + +-- +-- Name: TABLE location; Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON TABLE maevsi.location IS 'Location data based on GPS coordnates.'; + + +-- +-- Name: COLUMN location.id; Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON COLUMN maevsi.location.id IS 'The locations''s internal id.'; + + +-- +-- Name: COLUMN location.location_type; Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON COLUMN maevsi.location.location_type IS 'The type of the location (A = account, E = event)'; + + +-- +-- Name: COLUMN location.latitude; Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON COLUMN maevsi.location.latitude IS 'reference to an account (if not null).'; + + +-- +-- Name: COLUMN location.longitude; Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON COLUMN maevsi.location.longitude IS 'reference to an account (if not null).'; + + -- -- Name: profile_picture; Type: TABLE; Schema: maevsi; Owner: postgres -- @@ -2714,6 +2802,7 @@ CREATE TABLE maevsi_private.account ( password_reset_verification uuid, password_reset_verification_valid_until timestamp without time zone, upload_quota_bytes bigint DEFAULT 10485760 NOT NULL, + location_id uuid, CONSTRAINT account_email_address_check CHECK ((char_length(email_address) < 255)) ); @@ -2804,6 +2893,13 @@ COMMENT ON COLUMN maevsi_private.account.password_reset_verification_valid_until COMMENT ON COLUMN maevsi_private.account.upload_quota_bytes IS 'The account''s upload quota in bytes.'; +-- +-- Name: COLUMN account.location_id; Type: COMMENT; Schema: maevsi_private; Owner: postgres +-- + +COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the event''s location data.'; + + -- -- Name: achievement_code; Type: TABLE; Schema: maevsi_private; Owner: postgres -- @@ -3648,6 +3744,14 @@ ALTER TABLE ONLY maevsi.legal_term ADD CONSTRAINT legal_term_pkey PRIMARY KEY (id); +-- +-- Name: location location_pkey; Type: CONSTRAINT; Schema: maevsi; Owner: postgres +-- + +ALTER TABLE ONLY maevsi.location + ADD CONSTRAINT location_pkey PRIMARY KEY (id); + + -- -- Name: profile_picture profile_picture_account_id_key; Type: CONSTRAINT; Schema: maevsi; Owner: postgres -- @@ -4085,6 +4189,14 @@ ALTER TABLE ONLY maevsi.event_grouping ADD CONSTRAINT event_grouping_event_id_fkey FOREIGN KEY (event_id) REFERENCES maevsi.event(id); +-- +-- Name: event event_location_id_fkey; Type: FK CONSTRAINT; Schema: maevsi; Owner: postgres +-- + +ALTER TABLE ONLY maevsi.event + ADD CONSTRAINT event_location_id_fkey FOREIGN KEY (location_id) REFERENCES maevsi.location(id); + + -- -- Name: event_recommendation event_recommendation_account_id_fkey; Type: FK CONSTRAINT; Schema: maevsi; Owner: postgres -- @@ -4213,6 +4325,14 @@ ALTER TABLE ONLY maevsi.upload ADD CONSTRAINT upload_account_id_fkey FOREIGN KEY (account_id) REFERENCES maevsi.account(id); +-- +-- Name: account account_location_id_fkey; Type: FK CONSTRAINT; Schema: maevsi_private; Owner: postgres +-- + +ALTER TABLE ONLY maevsi_private.account + ADD CONSTRAINT account_location_id_fkey FOREIGN KEY (location_id) REFERENCES maevsi.location(id); + + -- -- Name: changes changes_project_fkey; Type: FK CONSTRAINT; Schema: sqitch; Owner: postgres -- @@ -4852,6 +4972,14 @@ REVOKE ALL ON FUNCTION maevsi.digest(bytea, text) FROM PUBLIC; REVOKE ALL ON FUNCTION maevsi.digest(text, text) FROM PUBLIC; +-- +-- Name: FUNCTION distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) TO maevsi_account; + + -- -- Name: FUNCTION encrypt(bytea, bytea, text); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -5366,6 +5494,14 @@ GRANT SELECT ON TABLE maevsi.legal_term TO maevsi_anonymous; GRANT SELECT,INSERT ON TABLE maevsi.legal_term_acceptance TO maevsi_account; +-- +-- Name: TABLE location; Type: ACL; Schema: maevsi; Owner: postgres +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE maevsi.location TO maevsi_account; +GRANT SELECT ON TABLE maevsi.location TO maevsi_anonymous; + + -- -- Name: TABLE profile_picture; Type: ACL; Schema: maevsi; Owner: postgres -- From 6a75845a6d86b07aa9f2932f4aaaafa4f2ca1419 Mon Sep 17 00:00:00 2001 From: Jonas Thelemann Date: Fri, 17 Jan 2025 06:03:29 +0100 Subject: [PATCH 2/9] refactor(location): cleanup --- src/deploy/function_distance.sql | 8 +++---- src/deploy/table_account_private.sql | 6 ++--- src/deploy/table_event.sql | 4 ++-- src/deploy/table_location.sql | 18 ++++++++------- src/verify/table_account_private.sql | 4 ++-- src/verify/table_event.sql | 4 ++-- test/schema/schema.definition.sql | 34 +++++++++++++++++----------- 7 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/deploy/function_distance.sql b/src/deploy/function_distance.sql index 05e152aa..697b0055 100644 --- a/src/deploy/function_distance.sql +++ b/src/deploy/function_distance.sql @@ -1,8 +1,10 @@ BEGIN; CREATE FUNCTION maevsi.distance( - lat1 DOUBLE PRECISION, lon1 DOUBLE PRECISION, - lat2 DOUBLE PRECISION, lon2 DOUBLE PRECISION + lat1 DOUBLE PRECISION, + lon1 DOUBLE PRECISION, + lat2 DOUBLE PRECISION, + lon2 DOUBLE PRECISION ) RETURNS DOUBLE PRECISION AS $$ DECLARE earthRadius DOUBLE PRECISION; @@ -29,5 +31,3 @@ COMMENT ON FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE P GRANT EXECUTE ON FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; COMMIT; - - diff --git a/src/deploy/table_account_private.sql b/src/deploy/table_account_private.sql index 7d80e306..0684dd5f 100644 --- a/src/deploy/table_account_private.sql +++ b/src/deploy/table_account_private.sql @@ -9,11 +9,11 @@ CREATE TABLE maevsi_private.account ( email_address_verification UUID DEFAULT gen_random_uuid(), email_address_verification_valid_until TIMESTAMP, last_activity TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + location_id UUID REFERENCES maevsi.location(id), password_hash TEXT NOT NULL, password_reset_verification UUID, password_reset_verification_valid_until TIMESTAMP, - upload_quota_bytes BIGINT NOT NULL DEFAULT 10485760, -- 10 mebibyte - location_id UUID REFERENCES maevsi.location(id) + upload_quota_bytes BIGINT NOT NULL DEFAULT 10485760 -- 10 mebibyte ); COMMENT ON TABLE maevsi_private.account IS 'Private account data.'; @@ -24,11 +24,11 @@ COMMENT ON COLUMN maevsi_private.account.email_address IS 'The account''s email COMMENT ON COLUMN maevsi_private.account.email_address_verification IS 'The UUID used to verify an email address, or null if already verified.'; COMMENT ON COLUMN maevsi_private.account.email_address_verification_valid_until IS 'The timestamp until which an email address verification is valid.'; COMMENT ON COLUMN maevsi_private.account.last_activity IS 'Timestamp at which the account last requested an access token.'; +COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the account''s location data.'; COMMENT ON COLUMN maevsi_private.account.password_hash IS 'The account''s password, hashed and salted.'; COMMENT ON COLUMN maevsi_private.account.password_reset_verification IS 'The UUID used to reset a password, or null if there is no pending reset request.'; COMMENT ON COLUMN maevsi_private.account.password_reset_verification_valid_until IS 'The timestamp until which a password reset is valid.'; COMMENT ON COLUMN maevsi_private.account.upload_quota_bytes IS 'The account''s upload quota in bytes.'; -COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the account''s location data.'; CREATE FUNCTION maevsi_private.account_email_address_verification_valid_until() RETURNS TRIGGER AS $$ BEGIN diff --git a/src/deploy/table_event.sql b/src/deploy/table_event.sql index 6dfac8a0..ef8f4f83 100644 --- a/src/deploy/table_event.sql +++ b/src/deploy/table_event.sql @@ -12,12 +12,12 @@ CREATE TABLE maevsi.event ( is_in_person BOOLEAN, is_remote BOOLEAN, location TEXT CHECK (char_length("location") > 0 AND char_length("location") < 300), + location_id UUID REFERENCES maevsi.location(id), name TEXT NOT NULL CHECK (char_length("name") > 0 AND char_length("name") < 100), slug TEXT NOT NULL CHECK (char_length(slug) < 100 AND slug ~ '^[-A-Za-z0-9]+$'), start TIMESTAMP WITH TIME ZONE NOT NULL, url TEXT CHECK (char_length("url") < 300 AND "url" ~ '^https:\/\/'), visibility maevsi.event_visibility NOT NULL, - location_id UUID REFERENCES maevsi.location(id), UNIQUE (author_account_id, slug) ); @@ -33,12 +33,12 @@ COMMENT ON COLUMN maevsi.event.is_archived IS 'Indicates whether the event is ar COMMENT ON COLUMN maevsi.event.is_in_person IS 'Indicates whether the event takes place in person.'; COMMENT ON COLUMN maevsi.event.is_remote IS 'Indicates whether the event takes place remotely.'; COMMENT ON COLUMN maevsi.event.location IS 'The event''s location as it can be shown on a map.'; +COMMENT ON COLUMN maevsi.event.location_id IS 'Reference to the event''s location data.'; COMMENT ON COLUMN maevsi.event.name IS 'The event''s name.'; COMMENT ON COLUMN maevsi.event.slug IS 'The event''s name, slugified.'; COMMENT ON COLUMN maevsi.event.start IS 'The event''s start date and time, with timezone.'; COMMENT ON COLUMN maevsi.event.url IS 'The event''s unified resource locator.'; COMMENT ON COLUMN maevsi.event.visibility IS 'The event''s visibility.'; -COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the event''s location data.'; -- GRANTs, RLS and POLICYs are specified in 'table_event_policy`. diff --git a/src/deploy/table_location.sql b/src/deploy/table_location.sql index 123bc3bc..4f9eb142 100644 --- a/src/deploy/table_location.sql +++ b/src/deploy/table_location.sql @@ -1,21 +1,23 @@ BEGIN; CREATE TABLE maevsi.location ( - id UUID PRIMARY KEY, + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + location_type CHAR(1) NOT NULL, - latitude DOUBLE PRECISION NOT NULL, - longitude DOUBLE PRECISION NOT NULL, - -- possible future extension (using PostGIS): - -- geom GEOMETRY + latitude DOUBLE PRECISION NOT NULL, + longitude DOUBLE PRECISION NOT NULL, + + -- -- possible future extension using PostGIS + -- geom GEOMETRY CHECK (location_type IN ('A', 'E')) ); COMMENT ON TABLE maevsi.location IS 'Location data based on GPS coordnates.'; -COMMENT ON COLUMN maevsi.location.id IS 'The locations''s internal id.'; +COMMENT ON COLUMN maevsi.location.id IS E'@omit create,update\nThe locations''s internal id.'; COMMENT ON COLUMN maevsi.location.location_type IS 'The type of the location (A = account, E = event)'; -COMMENT ON COLUMN maevsi.location.latitude IS 'reference to an account (if not null).'; -COMMENT ON COLUMN maevsi.location.longitude IS 'reference to an account (if not null).'; +COMMENT ON COLUMN maevsi.location.latitude IS 'The coordinate''s latitude.'; +COMMENT ON COLUMN maevsi.location.longitude IS 'The coordinate''s longitude.'; GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE maevsi.location TO maevsi_account; GRANT SELECT ON TABLE maevsi.location TO maevsi_anonymous; diff --git a/src/verify/table_account_private.sql b/src/verify/table_account_private.sql index 86caeccd..a2bf881e 100644 --- a/src/verify/table_account_private.sql +++ b/src/verify/table_account_private.sql @@ -7,11 +7,11 @@ SELECT id, email_address_verification, email_address_verification_valid_until, last_activity, + location_id, password_hash, password_reset_verification, password_reset_verification_valid_until, - upload_quota_bytes, - location_id + upload_quota_bytes FROM maevsi_private.account WHERE FALSE; DO $$ diff --git a/src/verify/table_event.sql b/src/verify/table_event.sql index fc1ffbc9..7accec38 100644 --- a/src/verify/table_event.sql +++ b/src/verify/table_event.sql @@ -10,12 +10,12 @@ SELECT id, is_in_person, is_remote, location, + location_id, name, slug, start, url, - visibility, - location_id + visibility FROM maevsi.event WHERE FALSE; ROLLBACK; diff --git a/test/schema/schema.definition.sql b/test/schema/schema.definition.sql index 5829ebb8..7365f997 100644 --- a/test/schema/schema.definition.sql +++ b/test/schema/schema.definition.sql @@ -776,12 +776,12 @@ CREATE TABLE maevsi.event ( is_in_person boolean, is_remote boolean, location text, + location_id uuid, name text NOT NULL, slug text NOT NULL, start timestamp with time zone NOT NULL, url text, visibility maevsi.event_visibility NOT NULL, - location_id uuid, CONSTRAINT event_description_check CHECK (((char_length(description) > 0) AND (char_length(description) < 1000000))), CONSTRAINT event_invitee_count_maximum_check CHECK ((invitee_count_maximum > 0)), CONSTRAINT event_location_check CHECK (((char_length(location) > 0) AND (char_length(location) < 300))), @@ -872,6 +872,13 @@ COMMENT ON COLUMN maevsi.event.is_remote IS 'Indicates whether the event takes p COMMENT ON COLUMN maevsi.event.location IS 'The event''s location as it can be shown on a map.'; +-- +-- Name: COLUMN event.location_id; Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON COLUMN maevsi.event.location_id IS 'Reference to the event''s location data.'; + + -- -- Name: COLUMN event.name; Type: COMMENT; Schema: maevsi; Owner: postgres -- @@ -3113,7 +3120,7 @@ COMMENT ON COLUMN maevsi.legal_term_acceptance.legal_term_id IS 'The ID of the l -- CREATE TABLE maevsi.location ( - id uuid NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL, location_type character(1) NOT NULL, latitude double precision NOT NULL, longitude double precision NOT NULL, @@ -3134,7 +3141,8 @@ COMMENT ON TABLE maevsi.location IS 'Location data based on GPS coordnates.'; -- Name: COLUMN location.id; Type: COMMENT; Schema: maevsi; Owner: postgres -- -COMMENT ON COLUMN maevsi.location.id IS 'The locations''s internal id.'; +COMMENT ON COLUMN maevsi.location.id IS '@omit create,update +The locations''s internal id.'; -- @@ -3148,14 +3156,14 @@ COMMENT ON COLUMN maevsi.location.location_type IS 'The type of the location (A -- Name: COLUMN location.latitude; Type: COMMENT; Schema: maevsi; Owner: postgres -- -COMMENT ON COLUMN maevsi.location.latitude IS 'reference to an account (if not null).'; +COMMENT ON COLUMN maevsi.location.latitude IS 'The coordinate''s latitude.'; -- -- Name: COLUMN location.longitude; Type: COMMENT; Schema: maevsi; Owner: postgres -- -COMMENT ON COLUMN maevsi.location.longitude IS 'reference to an account (if not null).'; +COMMENT ON COLUMN maevsi.location.longitude IS 'The coordinate''s longitude.'; -- @@ -3304,11 +3312,11 @@ CREATE TABLE maevsi_private.account ( email_address_verification uuid DEFAULT gen_random_uuid(), email_address_verification_valid_until timestamp without time zone, last_activity timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + location_id uuid, password_hash text NOT NULL, password_reset_verification uuid, password_reset_verification_valid_until timestamp without time zone, upload_quota_bytes bigint DEFAULT 10485760 NOT NULL, - location_id uuid, CONSTRAINT account_email_address_check CHECK ((char_length(email_address) < 255)) ); @@ -3371,6 +3379,13 @@ COMMENT ON COLUMN maevsi_private.account.email_address_verification_valid_until COMMENT ON COLUMN maevsi_private.account.last_activity IS 'Timestamp at which the account last requested an access token.'; +-- +-- Name: COLUMN account.location_id; Type: COMMENT; Schema: maevsi_private; Owner: postgres +-- + +COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the account''s location data.'; + + -- -- Name: COLUMN account.password_hash; Type: COMMENT; Schema: maevsi_private; Owner: postgres -- @@ -3399,13 +3414,6 @@ COMMENT ON COLUMN maevsi_private.account.password_reset_verification_valid_until COMMENT ON COLUMN maevsi_private.account.upload_quota_bytes IS 'The account''s upload quota in bytes.'; --- --- Name: COLUMN account.location_id; Type: COMMENT; Schema: maevsi_private; Owner: postgres --- - -COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the event''s location data.'; - - -- -- Name: achievement_code; Type: TABLE; Schema: maevsi_private; Owner: postgres -- From db84b7b8dc15f2f85cf3da4a74b8440ccd56d250 Mon Sep 17 00:00:00 2001 From: Sven Thelemann Date: Tue, 21 Jan 2025 12:12:09 +0100 Subject: [PATCH 3/9] feat(location): make use of PostGIS extension Instead of working with the original GPS coordinates we use the data type GEOMETRY from the PostGIS extension. Table `maevs.location`was dropped in favor of GEOMETRY typed columns in tables `maevsi_private.account' and `maevsi.event'. A collection of location related functions was added in a single source code file `function_package_location.sql`similar to the package feature in Oracle which is missing in PostgreSQL. --- src/deploy/function_distance.sql | 33 -- src/deploy/function_package_location.sql | 120 ++++++++ src/deploy/index_account_location.sql | 7 + src/deploy/index_event_location.sql | 7 + src/deploy/table_account_private.sql | 4 +- src/deploy/table_event.sql | 4 +- src/deploy/table_location.sql | 52 ---- src/revert/function_distance.sql | 5 - src/revert/function_package_location.sql | 12 + src/revert/index_account_location.sql | 5 + src/revert/index_event_location.sql | 5 + src/sqitch.plan | 7 +- src/verify/function_distance.sql | 9 - src/verify/function_package_location.sql | 81 +++++ src/verify/index_account_location.sql | 3 + src/verify/index_event_location.sql | 3 + src/verify/table_account_private.sql | 2 +- src/verify/table_event.sql | 2 +- test/schema/schema.definition.sql | 377 +++++++++++++++-------- 19 files changed, 496 insertions(+), 242 deletions(-) delete mode 100644 src/deploy/function_distance.sql create mode 100644 src/deploy/function_package_location.sql create mode 100644 src/deploy/index_account_location.sql create mode 100644 src/deploy/index_event_location.sql delete mode 100644 src/deploy/table_location.sql delete mode 100644 src/revert/function_distance.sql create mode 100644 src/revert/function_package_location.sql create mode 100644 src/revert/index_account_location.sql create mode 100644 src/revert/index_event_location.sql delete mode 100644 src/verify/function_distance.sql create mode 100644 src/verify/function_package_location.sql create mode 100644 src/verify/index_account_location.sql create mode 100644 src/verify/index_event_location.sql diff --git a/src/deploy/function_distance.sql b/src/deploy/function_distance.sql deleted file mode 100644 index 697b0055..00000000 --- a/src/deploy/function_distance.sql +++ /dev/null @@ -1,33 +0,0 @@ -BEGIN; - -CREATE FUNCTION maevsi.distance( - lat1 DOUBLE PRECISION, - lon1 DOUBLE PRECISION, - lat2 DOUBLE PRECISION, - lon2 DOUBLE PRECISION -) RETURNS DOUBLE PRECISION AS $$ -DECLARE - earthRadius DOUBLE PRECISION; - r DOUBLE PRECISION; - hLat DOUBLE PRECISION; - hLon DOUBLE PRECISION; - a DOUBLE PRECISION; - distance DOUBLE PRECISION; -BEGIN - earthRadius := 6371; - r := pi() / 180; - hLat := sin((lat2-lat1)*r/2.0); - hLon := sin((lon2-lon1)*r/2.0); - a := hLat*hLat + hLon*hLon*cos(lat1*r)*cos(lat2*r); -- Haversine - distance := earthRadius * 2 * atan2(sqrt(a), sqrt(1-a)); - -- If atan2 is not available, use asin(least(1, sqrt(a)) (including protection against rounding errors). - -- distance := earthRadius * 2 * asin(least(1, sqrt(a))) - RETURN distance; -END; -$$ LANGUAGE PLPGSQL IMMUTABLE; - -COMMENT ON FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Calculate the distance between to locations given their GPS coordinates.'; - -GRANT EXECUTE ON FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; - -COMMIT; diff --git a/src/deploy/function_package_location.sql b/src/deploy/function_package_location.sql new file mode 100644 index 00000000..e74b9382 --- /dev/null +++ b/src/deploy/function_package_location.sql @@ -0,0 +1,120 @@ +BEGIN; + +CREATE FUNCTION maevsi.event_distances ( + _account_id UUID, + _max_distance DOUBLE PRECISION) +RETURNS TABLE (event_id UUID, distance DOUBLE PRECISION) AS $$ +BEGIN + RETURN QUERY + WITH a AS ( + SELECT location FROM maevsi_private.account WHERE id = _account_id + ) + SELECT e.id as event_id, maevsi.ST_Distance(a.location, e.location_geometry) distance + FROM a, maevsi.event e + WHERE maevsi.ST_DWithin(a.location, e.location_geometry, _max_distance * 1000); +END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.event_distances(UUID, DOUBLE PRECISION) IS 'Returns event locations within a given radius around the location of an account.'; + +GRANT EXECUTE ON FUNCTION maevsi.event_distances(UUID, DOUBLE PRECISION) TO maevsi_account; + +------------------------------------------------- + +CREATE FUNCTION maevsi.account_distances ( + _event_id UUID, + _max_distance DOUBLE PRECISION) +RETURNS TABLE (account_id UUID, distance DOUBLE PRECISION) AS $$ +BEGIN + -- return account locations within a given radius around the location of an event + RETURN QUERY + WITH e AS ( + SELECT location_geometry FROM maevsi.event WHERE id = _event_id + ) + SELECT a.id as account_id, maevsi.ST_Distance(e.location_geometry, a.location) distance + FROM e, maevsi_private.account a + WHERE maevsi.ST_DWithin(e.location_geometry, a.location, _max_distance * 1000); +END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.account_distances(UUID, DOUBLE PRECISION) IS 'Returns account locations within a given radius around the location of an event.'; + +GRANT EXECUTE ON FUNCTION maevsi.account_distances(UUID, DOUBLE PRECISION) TO maevsi_account; + +------------------------------------------------- + +CREATE FUNCTION maevsi.account_location_update ( + _account_id UUID, + _latitude DOUBLE PRECISION, + _longitude DOUBLE PRECISION) +RETURNS VOID AS $$ +BEGIN + -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm + -- SRID 4326: "WGS 84" (default SRID) + UPDATE maevsi_private.account SET + location = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + WHERE id = _account_id; +END; $$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Updates an account''s location based on latitude and longitude (GPS coordinates).'; + +GRANT EXECUTE ON FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; + +------------------------------------------------- + +CREATE FUNCTION maevsi.event_location_update ( + _event_id UUID, + _latitude DOUBLE PRECISION, + _longitude DOUBLE PRECISION) +RETURNS VOID AS $$ +BEGIN + -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm + -- SRID 4326: "WGS 84" (default SRID) + UPDATE maevsi.event SET + location_geometry = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + WHERE id = _event_id; +END; $$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Updates an event''s location based on latitude and longitude (GPS coordinates).'; + +GRANT EXECUTE ON FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; + +------------------------------------------------- + +CREATE FUNCTION maevsi.get_account_location_coordinates ( + _account_id UUID) +RETURNS DOUBLE PRECISION[] AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT maevsi.ST_Y( maevsi.ST_Transform(location, 4326)), maevsi.ST_X( maevsi.ST_Transform(location, 4326)) + INTO _latitude, _longitude + FROM maevsi_private.account + WHERE id = _account_id; + RETURN ARRAY[_latitude, _longitude]; +END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.get_account_location_coordinates(UUID) IS 'Returns an array with latitude and longitude of the account''s current location data'; + +GRANT EXECUTE ON FUNCTION maevsi.get_account_location_coordinates(UUID) TO maevsi_account; + +------------------------------------------------- + +CREATE FUNCTION maevsi.get_event_location_coordinates ( + _event_id UUID) +RETURNS DOUBLE PRECISION[] AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT maevsi.ST_Y( maevsi.ST_Transform(location_geometry, 4326)), maevsi.ST_X( maevsi.ST_Transform(location_geometry, 4326)) + INTO _latitude, _longitude + FROM maevsi.event + WHERE id = _event_id; + RETURN ARRAY[_latitude, _longitude]; +END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.get_event_location_coordinates(UUID) IS 'Returns an array with latitude and longitude of the event''s current location data.'; + +GRANT EXECUTE ON FUNCTION maevsi.get_event_location_coordinates(UUID) TO maevsi_account; + +COMMIT; diff --git a/src/deploy/index_account_location.sql b/src/deploy/index_account_location.sql new file mode 100644 index 00000000..a78b71c4 --- /dev/null +++ b/src/deploy/index_account_location.sql @@ -0,0 +1,7 @@ +BEGIN; + +CREATE INDEX idx_account_location ON maevsi_private.account USING GIST (location); + +COMMENT ON INDEX maevsi_private.idx_account_location IS 'Spatial index on column location in maevsi_private.account.'; + +COMMIT; diff --git a/src/deploy/index_event_location.sql b/src/deploy/index_event_location.sql new file mode 100644 index 00000000..b3ae713d --- /dev/null +++ b/src/deploy/index_event_location.sql @@ -0,0 +1,7 @@ +BEGIN; + +CREATE INDEX idx_event_location ON maevsi.event USING GIST (location_geometry); + +COMMENT ON INDEX maevsi.idx_event_location IS 'Spatial index on column location in maevsi.event.'; + +COMMIT; diff --git a/src/deploy/table_account_private.sql b/src/deploy/table_account_private.sql index fd6f4925..776bc5aa 100644 --- a/src/deploy/table_account_private.sql +++ b/src/deploy/table_account_private.sql @@ -9,7 +9,7 @@ CREATE TABLE maevsi_private.account ( email_address_verification UUID DEFAULT gen_random_uuid(), email_address_verification_valid_until TIMESTAMP WITH TIME ZONE, last_activity TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, - location_id UUID REFERENCES maevsi.location(id), + location maevsi.GEOMETRY(Point, 4839), password_hash TEXT NOT NULL, password_reset_verification UUID, password_reset_verification_valid_until TIMESTAMP WITH TIME ZONE, @@ -24,7 +24,7 @@ COMMENT ON COLUMN maevsi_private.account.email_address IS 'The account''s email COMMENT ON COLUMN maevsi_private.account.email_address_verification IS 'The UUID used to verify an email address, or null if already verified.'; COMMENT ON COLUMN maevsi_private.account.email_address_verification_valid_until IS 'The timestamp until which an email address verification is valid.'; COMMENT ON COLUMN maevsi_private.account.last_activity IS 'Timestamp at which the account last requested an access token.'; -COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the account''s location data.'; +COMMENT ON COLUMN maevsi_private.account.location IS 'The account''s geometric location.'; COMMENT ON COLUMN maevsi_private.account.password_hash IS 'The account''s password, hashed and salted.'; COMMENT ON COLUMN maevsi_private.account.password_reset_verification IS 'The UUID used to reset a password, or null if there is no pending reset request.'; COMMENT ON COLUMN maevsi_private.account.password_reset_verification_valid_until IS 'The timestamp until which a password reset is valid.'; diff --git a/src/deploy/table_event.sql b/src/deploy/table_event.sql index 047e6056..f5a934a5 100644 --- a/src/deploy/table_event.sql +++ b/src/deploy/table_event.sql @@ -12,7 +12,7 @@ CREATE TABLE maevsi.event ( is_in_person BOOLEAN, is_remote BOOLEAN, location TEXT CHECK (char_length("location") > 0 AND char_length("location") < 300), - location_id UUID REFERENCES maevsi.location(id), + location_geometry maevsi.GEOMETRY(Point, 4839), name TEXT NOT NULL CHECK (char_length("name") > 0 AND char_length("name") < 100), slug TEXT NOT NULL CHECK (char_length(slug) < 100 AND slug ~ '^[-A-Za-z0-9]+$'), start TIMESTAMP WITH TIME ZONE NOT NULL, @@ -33,7 +33,7 @@ COMMENT ON COLUMN maevsi.event.is_archived IS 'Indicates whether the event is ar COMMENT ON COLUMN maevsi.event.is_in_person IS 'Indicates whether the event takes place in person.'; COMMENT ON COLUMN maevsi.event.is_remote IS 'Indicates whether the event takes place remotely.'; COMMENT ON COLUMN maevsi.event.location IS 'The event''s location as it can be shown on a map.'; -COMMENT ON COLUMN maevsi.event.location_id IS 'Reference to the event''s location data.'; +COMMENT ON COLUMN maevsi.event.location_geometry IS 'The event''s geometric location.'; COMMENT ON COLUMN maevsi.event.name IS 'The event''s name.'; COMMENT ON COLUMN maevsi.event.slug IS 'The event''s name, slugified.'; COMMENT ON COLUMN maevsi.event.start IS 'The event''s start date and time, with timezone.'; diff --git a/src/deploy/table_location.sql b/src/deploy/table_location.sql deleted file mode 100644 index 4f9eb142..00000000 --- a/src/deploy/table_location.sql +++ /dev/null @@ -1,52 +0,0 @@ -BEGIN; - -CREATE TABLE maevsi.location ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - location_type CHAR(1) NOT NULL, - latitude DOUBLE PRECISION NOT NULL, - longitude DOUBLE PRECISION NOT NULL, - - -- -- possible future extension using PostGIS - -- geom GEOMETRY - - CHECK (location_type IN ('A', 'E')) -); - -COMMENT ON TABLE maevsi.location IS 'Location data based on GPS coordnates.'; -COMMENT ON COLUMN maevsi.location.id IS E'@omit create,update\nThe locations''s internal id.'; -COMMENT ON COLUMN maevsi.location.location_type IS 'The type of the location (A = account, E = event)'; -COMMENT ON COLUMN maevsi.location.latitude IS 'The coordinate''s latitude.'; -COMMENT ON COLUMN maevsi.location.longitude IS 'The coordinate''s longitude.'; - -GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE maevsi.location TO maevsi_account; -GRANT SELECT ON TABLE maevsi.location TO maevsi_anonymous; - -/* --- If PostGIS is used: - --- A spatial index could be used by function ST_DWithin (returning true if distance between --- two locations location is <= a given distance. -CREATE INDEX maevsi.location_geom_idx ON maevsi.location USING GIST (geom); - -CREATE FUNCTION maevsi.location_update_geom() RETURNS TRIGGER AS $$ - BEGIN - NEW.geom = ST_Point(NEW.longitude, NEW.latitude, 4326); - RETURN NEW; - END; -$$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; - -COMMENT ON FUNCTION maevsi.location_update_geom() IS 'Sets column geom to a value based on longitude and latitude, using spatial reference id 4326.'; - -GRANT EXECUTE ON FUNCTION maevsi.location_update_geom() TO maevsi_account; - -CREATE TRIGGER maevsi_location_update_geom - BEFORE - INSERT - OR UPDATE OF latitude, longitude - ON maevsi.location - FOR EACH ROW - EXECUTE PROCEDURE maevsi.location_update_geom(); -*/ - -COMMIT; diff --git a/src/revert/function_distance.sql b/src/revert/function_distance.sql deleted file mode 100644 index 67e25d0d..00000000 --- a/src/revert/function_distance.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -DROP FUNCTION maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION); - -COMMIT; diff --git a/src/revert/function_package_location.sql b/src/revert/function_package_location.sql new file mode 100644 index 00000000..778447ae --- /dev/null +++ b/src/revert/function_package_location.sql @@ -0,0 +1,12 @@ +BEGIN; + +DROP FUNCTION maevsi.event_distances(UUID, DOUBLE PRECISION); +DROP FUNCTION maevsi.account_distances(UUID, DOUBLE PRECISION); + +DROP FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION); +DROP FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION); + +DROP FUNCTION maevsi.get_account_location_coordinates(UUID); +DROP FUNCTION maevsi.get_event_location_coordinates(UUID); + +COMMIT; diff --git a/src/revert/index_account_location.sql b/src/revert/index_account_location.sql new file mode 100644 index 00000000..d4277036 --- /dev/null +++ b/src/revert/index_account_location.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP INDEX maevsi_private.idx_account_location; + +COMMIT; diff --git a/src/revert/index_event_location.sql b/src/revert/index_event_location.sql new file mode 100644 index 00000000..3ad46c6c --- /dev/null +++ b/src/revert/index_event_location.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP INDEX maevsi.idx_event_location; + +COMMIT; diff --git a/src/sqitch.plan b/src/sqitch.plan index ea361ff0..f292ec65 100644 --- a/src/sqitch.plan +++ b/src/sqitch.plan @@ -15,8 +15,7 @@ function_invoker_account_id [privilege_execute_revoke schema_public role_account enum_invitation_feedback [schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Possible answers to an invitation: accepted, canceled. enum_event_visibility [schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Possible visibilities of events and event groups: public, private. table_notification [schema_private] 1970-01-01T00:00:00Z Jonas Thelemann # Notifications that are sent via pg_notify. -table_location [schema_public role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # Location data based on GPS coordinates -function_distance [schema_public role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # Calculates distance betwenn two GPS points. +extension_postgis [schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Add functions to work with geospatial data. table_account_private [schema_private schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Add private table account. table_account_public [schema_public schema_private table_account_private] 1970-01-01T00:00:00Z Jonas Thelemann # Add public table account. table_account_block [schema_public table_account_public] 1970-01-01T00:00:00Z Sven Thelemann # Blocking of an account by another account. @@ -94,4 +93,6 @@ table_event_recommendation_policy [schema_public table_event_recommendation role table_event_favourite [schema_public table_account_public table_event] 1970-01-01T00:00:00Z Sven Thelemann # A table for the user accounts' favourite events. table_event_favourite_policy [schema_public table_account_public table_event role_account] 1970-01-01T00:00:00Z Sven Thelemann # Policy for table event_favourite. test_account_blocking [schema_test] 1970-01-01T00:00:00Z Sven Thelemann # Test cases for account blocking. -extension_postgis [schema_public] 1970-01-01T00:00:00Z Jonas Thelemann # Add functions to work with geospatial data. +index_account_location [schema_private extension_postgis table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location column in table maevsi_private.account. +index_event_location [schema_public extension_postgis table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location_geometry column in table maevsi.event. +function_package_location [schema_public schema_private extension_postgis table_account_private table_event role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # A collection of location related functions. diff --git a/src/verify/function_distance.sql b/src/verify/function_distance.sql deleted file mode 100644 index b1321735..00000000 --- a/src/verify/function_distance.sql +++ /dev/null @@ -1,9 +0,0 @@ -BEGIN; - -DO $$ -BEGIN - ASSERT (SELECT pg_catalog.has_function_privilege('maevsi_account', 'maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION)', 'EXECUTE')); - ASSERT NOT (SELECT pg_catalog.has_function_privilege('maevsi_anonymous', 'maevsi.distance(DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION, DOUBLE PRECISION)', 'EXECUTE')); -END $$; - -ROLLBACK; diff --git a/src/verify/function_package_location.sql b/src/verify/function_package_location.sql new file mode 100644 index 00000000..5843b1e5 --- /dev/null +++ b/src/verify/function_package_location.sql @@ -0,0 +1,81 @@ +BEGIN; + +DO $$ +DECLARE + _account_id UUID; + _event_id UUID; + _coordinates DOUBLE PRECISION[]; + _id UUID; +BEGIN + + _account_id := maevsi.account_registration('username', 'email@example.com', 'password', 'en'); + PERFORM maevsi.account_email_address_verification( + (SELECT email_address_verification FROM maevsi_private.account WHERE id = _account_id) + ); + + SET LOCAL role = 'maevsi_account'; + EXECUTE 'SET LOCAL jwt.claims.account_id = ''' || _account_id || ''''; + + INSERT INTO maevsi.event(author_account_id, name, slug, start, visibility) + VALUES (_account_id, 'event', 'event', CURRENT_TIMESTAMP, 'public'::maevsi.event_visibility) + RETURNING id INTO _event_id; + + ------------------------------------- + + PERFORM maevsi.account_location_update(_account_id, 51.304, 9.476); -- somewhere in Kassel + + _coordinates := maevsi.get_account_location_coordinates(_account_id); + + IF NOT (round(_coordinates[1]::numeric, 3) = 51.304 AND round(_coordinates[2]::numeric, 3) = 9.476) THEN + RAISE EXCEPTION 'wrong account coordinates.'; + END IF; + + ------------------------------------- + + PERFORM maevsi.event_location_update(_event_id, 50.113, 8.650); -- somewhere in Frankfurt + + _coordinates := maevsi.get_event_location_coordinates(_event_id); + + IF NOT (round(_coordinates[1]::numeric, 3) = 50.113 AND round(_coordinates[2]::numeric, 3) = 8.650) THEN + RAISE EXCEPTION 'wrong event coordinates.'; + END IF; + + ------------------------------------- + + SELECT event_id INTO _id + FROM maevsi.maevsi.event_distances(_account_id, 100); + + IF _id IS NOT NULL THEN + RAISE EXCEPTION 'function event_distances with radius 100 km should have returned an empty result.'; + END IF; + + SELECT event_id INTO _id + FROM maevsi.maevsi.event_distances(_account_id, 250); + + IF _id != _event_id THEN + RAISE EXCEPTION 'function event_distances with radius 250 km should have returned _event_id.'; + END IF; + + ------------------------------------- + + SELECT account_id INTO _id + FROM maevsi.maevsi.account_distances(_event_id, 100); + + IF _id IS NOT NULL THEN + RAISE EXCEPTION 'function account_distances with radius 100 km should have returned an empty result.'; + END IF; + + SELECT account_id INTO _id + FROM maevsi.maevsi.account_distances(_event_id, 250); + + IF _id != _account_id THEN + RAISE EXCEPTION 'function account_distances with radius 250 km should have returned _account_id.'; + END IF; + + ------------------------------------- + + RAISE NOTICE 'tests ok'; + +END; $$ LANGUAGE plpgsql; + +ROLLBACK; diff --git a/src/verify/index_account_location.sql b/src/verify/index_account_location.sql new file mode 100644 index 00000000..1137b56b --- /dev/null +++ b/src/verify/index_account_location.sql @@ -0,0 +1,3 @@ +BEGIN; + +ROLLBACK; diff --git a/src/verify/index_event_location.sql b/src/verify/index_event_location.sql new file mode 100644 index 00000000..1137b56b --- /dev/null +++ b/src/verify/index_event_location.sql @@ -0,0 +1,3 @@ +BEGIN; + +ROLLBACK; diff --git a/src/verify/table_account_private.sql b/src/verify/table_account_private.sql index c025f947..d18b3b07 100644 --- a/src/verify/table_account_private.sql +++ b/src/verify/table_account_private.sql @@ -7,7 +7,7 @@ SELECT id, email_address_verification, email_address_verification_valid_until, last_activity, - location_id, + location, password_hash, password_reset_verification, password_reset_verification_valid_until, diff --git a/src/verify/table_event.sql b/src/verify/table_event.sql index 7accec38..817e0bd5 100644 --- a/src/verify/table_event.sql +++ b/src/verify/table_event.sql @@ -10,7 +10,7 @@ SELECT id, is_in_person, is_remote, location, - location_id, + location_geometry, name, slug, start, diff --git a/test/schema/schema.definition.sql b/test/schema/schema.definition.sql index 2edf4581..1ef8e191 100644 --- a/test/schema/schema.definition.sql +++ b/test/schema/schema.definition.sql @@ -308,6 +308,34 @@ ALTER FUNCTION maevsi.account_delete(password text) OWNER TO postgres; COMMENT ON FUNCTION maevsi.account_delete(password text) IS 'Allows to delete an account.'; +-- +-- Name: account_distances(uuid, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) RETURNS TABLE(account_id uuid, distance double precision) + LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER + AS $$ +BEGIN + -- return account locations within a given radius around the location of an event + RETURN QUERY + WITH e AS ( + SELECT location_geometry FROM maevsi.event WHERE id = _event_id + ) + SELECT a.id as account_id, maevsi.ST_Distance(e.location_geometry, a.location) distance + FROM e, maevsi_private.account a + WHERE maevsi.ST_DWithin(e.location_geometry, a.location, _max_distance * 1000); +END; $$; + + +ALTER FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) OWNER TO postgres; + +-- +-- Name: FUNCTION account_distances(_event_id uuid, _max_distance double precision); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) IS 'Returns account locations within a given radius around the location of an event.'; + + -- -- Name: account_email_address_verification(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -347,6 +375,31 @@ ALTER FUNCTION maevsi.account_email_address_verification(code uuid) OWNER TO pos COMMENT ON FUNCTION maevsi.account_email_address_verification(code uuid) IS 'Sets the account''s email address verification code to `NULL` for which the email address verification code equals the one passed and is up to date.'; +-- +-- Name: account_location_update(uuid, double precision, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.account_location_update(_account_id uuid, _latitude double precision, _longitude double precision) RETURNS void + LANGUAGE plpgsql STRICT SECURITY DEFINER + AS $$ +BEGIN + -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm + -- SRID 4326: "WGS 84" (default SRID) + UPDATE maevsi_private.account SET + location = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + WHERE id = _account_id; +END; $$; + + +ALTER FUNCTION maevsi.account_location_update(_account_id uuid, _latitude double precision, _longitude double precision) OWNER TO postgres; + +-- +-- Name: FUNCTION account_location_update(_account_id uuid, _latitude double precision, _longitude double precision); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.account_location_update(_account_id uuid, _latitude double precision, _longitude double precision) IS 'Updates an account''s location based on latitude and longitude (GPS coordinates).'; + + -- -- Name: account_password_change(text, text); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -734,43 +787,6 @@ ALTER FUNCTION maevsi.authenticate(username text, password text) OWNER TO postgr COMMENT ON FUNCTION maevsi.authenticate(username text, password text) IS 'Creates a JWT token that will securely identify an account and give it certain permissions.'; --- --- Name: distance(double precision, double precision, double precision, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres --- - -CREATE FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) RETURNS double precision - LANGUAGE plpgsql IMMUTABLE - AS $$ -DECLARE - earthRadius DOUBLE PRECISION; - r DOUBLE PRECISION; - hLat DOUBLE PRECISION; - hLon DOUBLE PRECISION; - a DOUBLE PRECISION; - distance DOUBLE PRECISION; -BEGIN - earthRadius := 6371; - r := pi() / 180; - hLat := sin((lat2-lat1)*r/2.0); - hLon := sin((lon2-lon1)*r/2.0); - a := hLat*hLat + hLon*hLon*cos(lat1*r)*cos(lat2*r); -- Haversine - distance := earthRadius * 2 * atan2(sqrt(a), sqrt(1-a)); - -- If atan2 is not available, use asin(least(1, sqrt(a)) (including protection against rounding errors). - -- distance := earthRadius * 2 * asin(least(1, sqrt(a))) - RETURN distance; -END; -$$; - - -ALTER FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) OWNER TO postgres; - --- --- Name: FUNCTION distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision); Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) IS 'Calculate the distance between to locations given their GPS coordinates.'; - - SET default_tablespace = ''; SET default_table_access_method = heap; @@ -790,7 +806,7 @@ CREATE TABLE maevsi.event ( is_in_person boolean, is_remote boolean, location text, - location_id uuid, + location_geometry maevsi.geometry(Point,4839), name text NOT NULL, slug text NOT NULL, start timestamp with time zone NOT NULL, @@ -887,10 +903,10 @@ COMMENT ON COLUMN maevsi.event.location IS 'The event''s location as it can be s -- --- Name: COLUMN event.location_id; Type: COMMENT; Schema: maevsi; Owner: postgres +-- Name: COLUMN event.location_geometry; Type: COMMENT; Schema: maevsi; Owner: postgres -- -COMMENT ON COLUMN maevsi.event.location_id IS 'Reference to the event''s location data.'; +COMMENT ON COLUMN maevsi.event.location_geometry IS 'The event''s geometric location.'; -- @@ -970,6 +986,33 @@ ALTER FUNCTION maevsi.event_delete(id uuid, password text) OWNER TO postgres; COMMENT ON FUNCTION maevsi.event_delete(id uuid, password text) IS 'Allows to delete an event.'; +-- +-- Name: event_distances(uuid, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) RETURNS TABLE(event_id uuid, distance double precision) + LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER + AS $$ +BEGIN + RETURN QUERY + WITH a AS ( + SELECT location FROM maevsi_private.account WHERE id = _account_id + ) + SELECT e.id as event_id, maevsi.ST_Distance(a.location, e.location_geometry) distance + FROM a, maevsi.event e + WHERE maevsi.ST_DWithin(a.location, e.location_geometry, _max_distance * 1000); +END; $$; + + +ALTER FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) OWNER TO postgres; + +-- +-- Name: FUNCTION event_distances(_account_id uuid, _max_distance double precision); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) IS 'Returns event locations within a given radius around the location of an account.'; + + -- -- Name: event_invitee_count_maximum(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -1040,6 +1083,31 @@ ALTER FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text) OWNER COMMENT ON FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text) IS 'Shows if an event exists.'; +-- +-- Name: event_location_update(uuid, double precision, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.event_location_update(_event_id uuid, _latitude double precision, _longitude double precision) RETURNS void + LANGUAGE plpgsql STRICT SECURITY DEFINER + AS $$ +BEGIN + -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm + -- SRID 4326: "WGS 84" (default SRID) + UPDATE maevsi.event SET + location_geometry = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + WHERE id = _event_id; +END; $$; + + +ALTER FUNCTION maevsi.event_location_update(_event_id uuid, _latitude double precision, _longitude double precision) OWNER TO postgres; + +-- +-- Name: FUNCTION event_location_update(_event_id uuid, _latitude double precision, _longitude double precision); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.event_location_update(_event_id uuid, _latitude double precision, _longitude double precision) IS 'Updates an event''s location based on latitude and longitude (GPS coordinates).'; + + -- -- Name: event_unlock(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -1140,6 +1208,62 @@ ALTER FUNCTION maevsi.events_organized() OWNER TO postgres; COMMENT ON FUNCTION maevsi.events_organized() IS 'Add a function that returns all event ids for which the invoker is the author.'; +-- +-- Name: get_account_location_coordinates(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) RETURNS double precision[] + LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER + AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT maevsi.ST_Y( maevsi.ST_Transform(location, 4326)), maevsi.ST_X( maevsi.ST_Transform(location, 4326)) + INTO _latitude, _longitude + FROM maevsi_private.account + WHERE id = _account_id; + RETURN ARRAY[_latitude, _longitude]; +END; $$; + + +ALTER FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) OWNER TO postgres; + +-- +-- Name: FUNCTION get_account_location_coordinates(_account_id uuid); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) IS 'Returns an array with latitude and longitude of the account''s current location data'; + + +-- +-- Name: get_event_location_coordinates(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) RETURNS double precision[] + LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER + AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT maevsi.ST_Y( maevsi.ST_Transform(location_geometry, 4326)), maevsi.ST_X( maevsi.ST_Transform(location_geometry, 4326)) + INTO _latitude, _longitude + FROM maevsi.event + WHERE id = _event_id; + RETURN ARRAY[_latitude, _longitude]; +END; $$; + + +ALTER FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) OWNER TO postgres; + +-- +-- Name: FUNCTION get_event_location_coordinates(_event_id uuid); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) IS 'Returns an array with latitude and longitude of the event''s current location data.'; + + -- -- Name: invitation_claim_array(); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -3120,57 +3244,6 @@ COMMENT ON COLUMN maevsi.legal_term_acceptance.account_id IS 'The user account I COMMENT ON COLUMN maevsi.legal_term_acceptance.legal_term_id IS 'The ID of the legal terms that were accepted. Deletion of these legal terms is restricted while they are still referenced in this table.'; --- --- Name: location; Type: TABLE; Schema: maevsi; Owner: postgres --- - -CREATE TABLE maevsi.location ( - id uuid DEFAULT gen_random_uuid() NOT NULL, - location_type character(1) NOT NULL, - latitude double precision NOT NULL, - longitude double precision NOT NULL, - CONSTRAINT location_location_type_check CHECK ((location_type = ANY (ARRAY['A'::bpchar, 'E'::bpchar]))) -); - - -ALTER TABLE maevsi.location OWNER TO postgres; - --- --- Name: TABLE location; Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON TABLE maevsi.location IS 'Location data based on GPS coordnates.'; - - --- --- Name: COLUMN location.id; Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON COLUMN maevsi.location.id IS '@omit create,update -The locations''s internal id.'; - - --- --- Name: COLUMN location.location_type; Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON COLUMN maevsi.location.location_type IS 'The type of the location (A = account, E = event)'; - - --- --- Name: COLUMN location.latitude; Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON COLUMN maevsi.location.latitude IS 'The coordinate''s latitude.'; - - --- --- Name: COLUMN location.longitude; Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON COLUMN maevsi.location.longitude IS 'The coordinate''s longitude.'; - - -- -- Name: profile_picture; Type: TABLE; Schema: maevsi; Owner: postgres -- @@ -3317,7 +3390,7 @@ CREATE TABLE maevsi_private.account ( email_address_verification uuid DEFAULT gen_random_uuid(), email_address_verification_valid_until timestamp with time zone, last_activity timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - location_id uuid, + location maevsi.geometry(Point,4839), password_hash text NOT NULL, password_reset_verification uuid, password_reset_verification_valid_until timestamp with time zone, @@ -3385,10 +3458,10 @@ COMMENT ON COLUMN maevsi_private.account.last_activity IS 'Timestamp at which th -- --- Name: COLUMN account.location_id; Type: COMMENT; Schema: maevsi_private; Owner: postgres +-- Name: COLUMN account.location; Type: COMMENT; Schema: maevsi_private; Owner: postgres -- -COMMENT ON COLUMN maevsi_private.account.location_id IS 'Reference to the account''s location data.'; +COMMENT ON COLUMN maevsi_private.account.location IS 'The account''s geometric location.'; -- @@ -4279,14 +4352,6 @@ ALTER TABLE ONLY maevsi.legal_term ADD CONSTRAINT legal_term_pkey PRIMARY KEY (id); --- --- Name: location location_pkey; Type: CONSTRAINT; Schema: maevsi; Owner: postgres --- - -ALTER TABLE ONLY maevsi.location - ADD CONSTRAINT location_pkey PRIMARY KEY (id); - - -- -- Name: profile_picture profile_picture_account_id_key; Type: CONSTRAINT; Schema: maevsi; Owner: postgres -- @@ -4526,6 +4591,20 @@ CREATE INDEX idx_event_grouping_event_id ON maevsi.event_grouping USING btree (e COMMENT ON INDEX maevsi.idx_event_grouping_event_id IS 'Speeds up reverse foreign key lookups.'; +-- +-- Name: idx_event_location; Type: INDEX; Schema: maevsi; Owner: postgres +-- + +CREATE INDEX idx_event_location ON maevsi.event USING gist (location_geometry); + + +-- +-- Name: INDEX idx_event_location; Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON INDEX maevsi.idx_event_location IS 'Spatial index on column location in maevsi.event.'; + + -- -- Name: idx_invitation_contact_id; Type: INDEX; Schema: maevsi; Owner: postgres -- @@ -4554,6 +4633,20 @@ CREATE INDEX idx_invitation_event_id ON maevsi.invitation USING btree (event_id) COMMENT ON INDEX maevsi.idx_invitation_event_id IS 'Speeds up reverse foreign key lookups.'; +-- +-- Name: idx_account_location; Type: INDEX; Schema: maevsi_private; Owner: postgres +-- + +CREATE INDEX idx_account_location ON maevsi_private.account USING gist (location); + + +-- +-- Name: INDEX idx_account_location; Type: COMMENT; Schema: maevsi_private; Owner: postgres +-- + +COMMENT ON INDEX maevsi_private.idx_account_location IS 'Spatial index on column location in maevsi_private.account.'; + + -- -- Name: invitation maevsi_invitation_update; Type: TRIGGER; Schema: maevsi; Owner: postgres -- @@ -4740,14 +4833,6 @@ ALTER TABLE ONLY maevsi.event_grouping ADD CONSTRAINT event_grouping_event_id_fkey FOREIGN KEY (event_id) REFERENCES maevsi.event(id); --- --- Name: event event_location_id_fkey; Type: FK CONSTRAINT; Schema: maevsi; Owner: postgres --- - -ALTER TABLE ONLY maevsi.event - ADD CONSTRAINT event_location_id_fkey FOREIGN KEY (location_id) REFERENCES maevsi.location(id); - - -- -- Name: event_recommendation event_recommendation_account_id_fkey; Type: FK CONSTRAINT; Schema: maevsi; Owner: postgres -- @@ -4876,14 +4961,6 @@ ALTER TABLE ONLY maevsi.upload ADD CONSTRAINT upload_account_id_fkey FOREIGN KEY (account_id) REFERENCES maevsi.account(id); --- --- Name: account account_location_id_fkey; Type: FK CONSTRAINT; Schema: maevsi_private; Owner: postgres --- - -ALTER TABLE ONLY maevsi_private.account - ADD CONSTRAINT account_location_id_fkey FOREIGN KEY (location_id) REFERENCES maevsi.location(id); - - -- -- Name: changes changes_project_fkey; Type: FK CONSTRAINT; Schema: sqitch; Owner: postgres -- @@ -6145,6 +6222,14 @@ REVOKE ALL ON FUNCTION maevsi.account_delete(password text) FROM PUBLIC; GRANT ALL ON FUNCTION maevsi.account_delete(password text) TO maevsi_account; +-- +-- Name: FUNCTION account_distances(_event_id uuid, _max_distance double precision); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) TO maevsi_account; + + -- -- Name: FUNCTION account_email_address_verification(code uuid); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -6154,6 +6239,14 @@ GRANT ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) TO ma GRANT ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) TO maevsi_anonymous; +-- +-- Name: FUNCTION account_location_update(_account_id uuid, _latitude double precision, _longitude double precision); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.account_location_update(_account_id uuid, _latitude double precision, _longitude double precision) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.account_location_update(_account_id uuid, _latitude double precision, _longitude double precision) TO maevsi_account; + + -- -- Name: FUNCTION account_password_change(password_current text, password_new text); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -6327,14 +6420,6 @@ REVOKE ALL ON FUNCTION maevsi.digest(bytea, text) FROM PUBLIC; REVOKE ALL ON FUNCTION maevsi.digest(text, text) FROM PUBLIC; --- --- Name: FUNCTION distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision); Type: ACL; Schema: maevsi; Owner: postgres --- - -REVOKE ALL ON FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) FROM PUBLIC; -GRANT ALL ON FUNCTION maevsi.distance(lat1 double precision, lon1 double precision, lat2 double precision, lon2 double precision) TO maevsi_account; - - -- -- Name: FUNCTION dropgeometrycolumn(table_name character varying, column_name character varying); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -6414,6 +6499,14 @@ REVOKE ALL ON FUNCTION maevsi.event_delete(id uuid, password text) FROM PUBLIC; GRANT ALL ON FUNCTION maevsi.event_delete(id uuid, password text) TO maevsi_account; +-- +-- Name: FUNCTION event_distances(_account_id uuid, _max_distance double precision); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) TO maevsi_account; + + -- -- Name: FUNCTION event_invitee_count_maximum(event_id uuid); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -6432,6 +6525,14 @@ GRANT ALL ON FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text GRANT ALL ON FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text) TO maevsi_anonymous; +-- +-- Name: FUNCTION event_location_update(_event_id uuid, _latitude double precision, _longitude double precision); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.event_location_update(_event_id uuid, _latitude double precision, _longitude double precision) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.event_location_update(_event_id uuid, _latitude double precision, _longitude double precision) TO maevsi_account; + + -- -- Name: FUNCTION event_unlock(invitation_id uuid); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -7171,6 +7272,22 @@ REVOKE ALL ON FUNCTION maevsi.geomfromewkb(bytea) FROM PUBLIC; REVOKE ALL ON FUNCTION maevsi.geomfromewkt(text) FROM PUBLIC; +-- +-- Name: FUNCTION get_account_location_coordinates(_account_id uuid); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) TO maevsi_account; + + +-- +-- Name: FUNCTION get_event_location_coordinates(_event_id uuid); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) TO maevsi_account; + + -- -- Name: FUNCTION get_proj4_from_srid(integer); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -11638,14 +11755,6 @@ GRANT SELECT ON TABLE maevsi.legal_term TO maevsi_anonymous; GRANT SELECT,INSERT ON TABLE maevsi.legal_term_acceptance TO maevsi_account; --- --- Name: TABLE location; Type: ACL; Schema: maevsi; Owner: postgres --- - -GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE maevsi.location TO maevsi_account; -GRANT SELECT ON TABLE maevsi.location TO maevsi_anonymous; - - -- -- Name: TABLE profile_picture; Type: ACL; Schema: maevsi; Owner: postgres -- From 65198bb89654a6a7fd4cead68f3e58800498988a Mon Sep 17 00:00:00 2001 From: Jonas Thelemann Date: Wed, 22 Jan 2025 06:44:55 +0100 Subject: [PATCH 4/9] feat(location): grant execute permissions on postgis functions Necessary for the Postgraphile PostGIS module. --- src/deploy/extension_postgis.sql | 10 ++++++++++ src/revert/extension_postgis.sql | 10 ++++++++++ src/verify/extension_postgis.sql | 26 ++++++++++++++++++++++++++ test/schema/schema.definition.sql | 14 ++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/src/deploy/extension_postgis.sql b/src/deploy/extension_postgis.sql index 26acfb2d..680942de 100644 --- a/src/deploy/extension_postgis.sql +++ b/src/deploy/extension_postgis.sql @@ -4,4 +4,14 @@ CREATE EXTENSION postgis WITH SCHEMA maevsi; COMMENT ON EXTENSION postgis IS 'Functions to work with geospatial data.'; +GRANT EXECUTE ON FUNCTION + maevsi.geometry(text), + maevsi.geometrytype(maevsi.geometry), + maevsi.postgis_type_name(character varying, integer, boolean), + maevsi.st_asgeojson(maevsi.geometry, integer, integer), + maevsi.st_coorddim(maevsi.geometry), + maevsi.st_srid(maevsi.geometry), + maevsi.text(maevsi.geometry) +TO maevsi_anonymous, maevsi_account; + COMMIT; diff --git a/src/revert/extension_postgis.sql b/src/revert/extension_postgis.sql index 7e80813b..b148472c 100644 --- a/src/revert/extension_postgis.sql +++ b/src/revert/extension_postgis.sql @@ -1,5 +1,15 @@ BEGIN; +REVOKE EXECUTE ON FUNCTION + maevsi.text(maevsi.geometry), + maevsi.st_srid(maevsi.geometry), + maevsi.st_coorddim(maevsi.geometry), + maevsi.st_asgeojson(maevsi.geometry, integer, integer), + maevsi.postgis_type_name(character varying, integer, boolean), + maevsi.geometrytype(maevsi.geometry), + maevsi.geometry(text) +FROM maevsi_anonymous, maevsi_account; + DROP EXTENSION postgis; COMMIT; diff --git a/src/verify/extension_postgis.sql b/src/verify/extension_postgis.sql index d980ba5d..aa2b0716 100644 --- a/src/verify/extension_postgis.sql +++ b/src/verify/extension_postgis.sql @@ -3,4 +3,30 @@ BEGIN; SELECT 1/count(*) FROM pg_extension WHERE extname = 'postgis'; SELECT has_function_privilege('maevsi.ST_DWithin(maevsi.geometry, maevsi.geometry, double precision)', 'EXECUTE'); +SAVEPOINT function_privileges_for_roles; +DO $$ +DECLARE + functions TEXT[] := ARRAY[ + 'maevsi.geometry(TEXT)', + 'maevsi.geometrytype(maevsi.GEOMETRY)', + 'maevsi.postgis_type_name(CHARACTER VARYING, INTEGER, BOOLEAN)', + 'maevsi.st_asgeojson(maevsi.GEOMETRY, INTEGER, INTEGER)', + 'maevsi.st_coorddim(maevsi.GEOMETRY)', + 'maevsi.st_srid(maevsi.GEOMETRY)', + 'maevsi.text(maevsi.GEOMETRY)' + ]; + roles TEXT[] := ARRAY['maevsi_account', 'maevsi_anonymous']; + function TEXT; + role TEXT; +BEGIN + FOREACH role IN ARRAY roles LOOP + FOREACH function IN ARRAY functions LOOP + IF NOT (SELECT pg_catalog.has_function_privilege(role, function, 'EXECUTE')) THEN + RAISE EXCEPTION 'Test function_privileges_for_roles failed: % does not have EXECUTE privilege on function %', role, function; + END IF; + END LOOP; + END LOOP; +END $$; +ROLLBACK TO SAVEPOINT function_privileges_for_roles; + ROLLBACK; diff --git a/test/schema/schema.definition.sql b/test/schema/schema.definition.sql index 1ef8e191..8a86cb17 100644 --- a/test/schema/schema.definition.sql +++ b/test/schema/schema.definition.sql @@ -5862,6 +5862,8 @@ REVOKE ALL ON FUNCTION maevsi.polygon(maevsi.geometry) FROM PUBLIC; -- REVOKE ALL ON FUNCTION maevsi.text(maevsi.geometry) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.text(maevsi.geometry) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.text(maevsi.geometry) TO maevsi_account; -- @@ -5890,6 +5892,8 @@ REVOKE ALL ON FUNCTION maevsi.geometry(polygon) FROM PUBLIC; -- REVOKE ALL ON FUNCTION maevsi.geometry(text) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.geometry(text) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.geometry(text) TO maevsi_account; -- @@ -7256,6 +7260,8 @@ REVOKE ALL ON FUNCTION maevsi.geometrytype(maevsi.geography) FROM PUBLIC; -- REVOKE ALL ON FUNCTION maevsi.geometrytype(maevsi.geometry) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.geometrytype(maevsi.geometry) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.geometrytype(maevsi.geometry) TO maevsi_account; -- @@ -8103,6 +8109,8 @@ REVOKE ALL ON FUNCTION maevsi.postgis_transform_pipeline_geometry(geom maevsi.ge -- REVOKE ALL ON FUNCTION maevsi.postgis_type_name(geomname character varying, coord_dimension integer, use_new_name boolean) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.postgis_type_name(geomname character varying, coord_dimension integer, use_new_name boolean) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.postgis_type_name(geomname character varying, coord_dimension integer, use_new_name boolean) TO maevsi_account; -- @@ -8412,6 +8420,8 @@ REVOKE ALL ON FUNCTION maevsi.st_asgeojson(geog maevsi.geography, maxdecimaldigi -- REVOKE ALL ON FUNCTION maevsi.st_asgeojson(geom maevsi.geometry, maxdecimaldigits integer, options integer) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.st_asgeojson(geom maevsi.geometry, maxdecimaldigits integer, options integer) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.st_asgeojson(geom maevsi.geometry, maxdecimaldigits integer, options integer) TO maevsi_account; -- @@ -8902,6 +8912,8 @@ REVOKE ALL ON FUNCTION maevsi.st_convexhull(maevsi.geometry) FROM PUBLIC; -- REVOKE ALL ON FUNCTION maevsi.st_coorddim(geometry maevsi.geometry) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.st_coorddim(geometry maevsi.geometry) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.st_coorddim(geometry maevsi.geometry) TO maevsi_account; -- @@ -11016,6 +11028,8 @@ REVOKE ALL ON FUNCTION maevsi.st_srid(geog maevsi.geography) FROM PUBLIC; -- REVOKE ALL ON FUNCTION maevsi.st_srid(geom maevsi.geometry) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.st_srid(geom maevsi.geometry) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.st_srid(geom maevsi.geometry) TO maevsi_account; -- From 9e2960484c544d1c94f74e1f4d3dda3bc9a2fb82 Mon Sep 17 00:00:00 2001 From: Jonas Thelemann Date: Wed, 22 Jan 2025 08:19:12 +0100 Subject: [PATCH 5/9] feat(location): switch to geography --- src/deploy/extension_postgis.sql | 2 ++ src/deploy/function_package_location.sql | 19 +++++++------- src/deploy/index_event_location.sql | 2 +- src/deploy/table_account_private.sql | 2 +- src/deploy/table_event.sql | 4 +-- src/revert/extension_postgis.sql | 4 ++- src/sqitch.plan | 2 +- src/verify/extension_postgis.sql | 2 ++ src/verify/table_event.sql | 2 +- test/schema/schema.definition.sql | 33 ++++++++++++++---------- 10 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/deploy/extension_postgis.sql b/src/deploy/extension_postgis.sql index 680942de..ce275fa0 100644 --- a/src/deploy/extension_postgis.sql +++ b/src/deploy/extension_postgis.sql @@ -5,11 +5,13 @@ CREATE EXTENSION postgis WITH SCHEMA maevsi; COMMENT ON EXTENSION postgis IS 'Functions to work with geospatial data.'; GRANT EXECUTE ON FUNCTION + maevsi.geometry(maevsi.geometry, integer, boolean), maevsi.geometry(text), maevsi.geometrytype(maevsi.geometry), maevsi.postgis_type_name(character varying, integer, boolean), maevsi.st_asgeojson(maevsi.geometry, integer, integer), maevsi.st_coorddim(maevsi.geometry), + maevsi.st_geomfromgeojson(text), maevsi.st_srid(maevsi.geometry), maevsi.text(maevsi.geometry) TO maevsi_anonymous, maevsi_account; diff --git a/src/deploy/function_package_location.sql b/src/deploy/function_package_location.sql index e74b9382..9300166a 100644 --- a/src/deploy/function_package_location.sql +++ b/src/deploy/function_package_location.sql @@ -9,9 +9,9 @@ BEGIN WITH a AS ( SELECT location FROM maevsi_private.account WHERE id = _account_id ) - SELECT e.id as event_id, maevsi.ST_Distance(a.location, e.location_geometry) distance + SELECT e.id as event_id, maevsi.ST_Distance(a.location, e.location_geography) distance FROM a, maevsi.event e - WHERE maevsi.ST_DWithin(a.location, e.location_geometry, _max_distance * 1000); + WHERE maevsi.ST_DWithin(a.location, e.location_geography, _max_distance * 1000); END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; COMMENT ON FUNCTION maevsi.event_distances(UUID, DOUBLE PRECISION) IS 'Returns event locations within a given radius around the location of an account.'; @@ -28,11 +28,11 @@ BEGIN -- return account locations within a given radius around the location of an event RETURN QUERY WITH e AS ( - SELECT location_geometry FROM maevsi.event WHERE id = _event_id + SELECT location_geography FROM maevsi.event WHERE id = _event_id ) - SELECT a.id as account_id, maevsi.ST_Distance(e.location_geometry, a.location) distance + SELECT a.id as account_id, maevsi.ST_Distance(e.location_geography, a.location) distance FROM e, maevsi_private.account a - WHERE maevsi.ST_DWithin(e.location_geometry, a.location, _max_distance * 1000); + WHERE maevsi.ST_DWithin(e.location_geography, a.location, _max_distance * 1000); END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; COMMENT ON FUNCTION maevsi.account_distances(UUID, DOUBLE PRECISION) IS 'Returns account locations within a given radius around the location of an event.'; @@ -50,7 +50,7 @@ BEGIN -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm -- SRID 4326: "WGS 84" (default SRID) UPDATE maevsi_private.account SET - location = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + location = maevsi.ST_Point(_longitude, _latitude, 4326) WHERE id = _account_id; END; $$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; @@ -69,7 +69,7 @@ BEGIN -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm -- SRID 4326: "WGS 84" (default SRID) UPDATE maevsi.event SET - location_geometry = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + location_geography = maevsi.ST_Point(_longitude, _latitude, 4326) WHERE id = _event_id; END; $$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; @@ -86,10 +86,11 @@ DECLARE _latitude DOUBLE PRECISION; _longitude DOUBLE PRECISION; BEGIN - SELECT maevsi.ST_Y( maevsi.ST_Transform(location, 4326)), maevsi.ST_X( maevsi.ST_Transform(location, 4326)) + SELECT maevsi.ST_Y(location::maevsi.geometry), maevsi.ST_X(location::maevsi.geometry) INTO _latitude, _longitude FROM maevsi_private.account WHERE id = _account_id; + RETURN ARRAY[_latitude, _longitude]; END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; @@ -106,7 +107,7 @@ DECLARE _latitude DOUBLE PRECISION; _longitude DOUBLE PRECISION; BEGIN - SELECT maevsi.ST_Y( maevsi.ST_Transform(location_geometry, 4326)), maevsi.ST_X( maevsi.ST_Transform(location_geometry, 4326)) + SELECT maevsi.ST_Y(location_geography::maevsi.geometry), maevsi.ST_X(location_geography::maevsi.geometry) INTO _latitude, _longitude FROM maevsi.event WHERE id = _event_id; diff --git a/src/deploy/index_event_location.sql b/src/deploy/index_event_location.sql index b3ae713d..2c221e3f 100644 --- a/src/deploy/index_event_location.sql +++ b/src/deploy/index_event_location.sql @@ -1,6 +1,6 @@ BEGIN; -CREATE INDEX idx_event_location ON maevsi.event USING GIST (location_geometry); +CREATE INDEX idx_event_location ON maevsi.event USING GIST (location_geography); COMMENT ON INDEX maevsi.idx_event_location IS 'Spatial index on column location in maevsi.event.'; diff --git a/src/deploy/table_account_private.sql b/src/deploy/table_account_private.sql index 776bc5aa..c1dfad12 100644 --- a/src/deploy/table_account_private.sql +++ b/src/deploy/table_account_private.sql @@ -9,7 +9,7 @@ CREATE TABLE maevsi_private.account ( email_address_verification UUID DEFAULT gen_random_uuid(), email_address_verification_valid_until TIMESTAMP WITH TIME ZONE, last_activity TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, - location maevsi.GEOMETRY(Point, 4839), + location maevsi.GEOGRAPHY(Point, 4326), password_hash TEXT NOT NULL, password_reset_verification UUID, password_reset_verification_valid_until TIMESTAMP WITH TIME ZONE, diff --git a/src/deploy/table_event.sql b/src/deploy/table_event.sql index f5a934a5..85ccac1b 100644 --- a/src/deploy/table_event.sql +++ b/src/deploy/table_event.sql @@ -12,7 +12,7 @@ CREATE TABLE maevsi.event ( is_in_person BOOLEAN, is_remote BOOLEAN, location TEXT CHECK (char_length("location") > 0 AND char_length("location") < 300), - location_geometry maevsi.GEOMETRY(Point, 4839), + location_geography maevsi.GEOGRAPHY(Point, 4326), name TEXT NOT NULL CHECK (char_length("name") > 0 AND char_length("name") < 100), slug TEXT NOT NULL CHECK (char_length(slug) < 100 AND slug ~ '^[-A-Za-z0-9]+$'), start TIMESTAMP WITH TIME ZONE NOT NULL, @@ -33,7 +33,7 @@ COMMENT ON COLUMN maevsi.event.is_archived IS 'Indicates whether the event is ar COMMENT ON COLUMN maevsi.event.is_in_person IS 'Indicates whether the event takes place in person.'; COMMENT ON COLUMN maevsi.event.is_remote IS 'Indicates whether the event takes place remotely.'; COMMENT ON COLUMN maevsi.event.location IS 'The event''s location as it can be shown on a map.'; -COMMENT ON COLUMN maevsi.event.location_geometry IS 'The event''s geometric location.'; +COMMENT ON COLUMN maevsi.event.location_geography IS 'The event''s geographic location.'; COMMENT ON COLUMN maevsi.event.name IS 'The event''s name.'; COMMENT ON COLUMN maevsi.event.slug IS 'The event''s name, slugified.'; COMMENT ON COLUMN maevsi.event.start IS 'The event''s start date and time, with timezone.'; diff --git a/src/revert/extension_postgis.sql b/src/revert/extension_postgis.sql index b148472c..f31acf97 100644 --- a/src/revert/extension_postgis.sql +++ b/src/revert/extension_postgis.sql @@ -3,11 +3,13 @@ BEGIN; REVOKE EXECUTE ON FUNCTION maevsi.text(maevsi.geometry), maevsi.st_srid(maevsi.geometry), + maevsi.st_geomfromgeojson(text), maevsi.st_coorddim(maevsi.geometry), maevsi.st_asgeojson(maevsi.geometry, integer, integer), maevsi.postgis_type_name(character varying, integer, boolean), maevsi.geometrytype(maevsi.geometry), - maevsi.geometry(text) + maevsi.geometry(text), + maevsi.geometry(maevsi.geometry, integer, boolean) FROM maevsi_anonymous, maevsi_account; DROP EXTENSION postgis; diff --git a/src/sqitch.plan b/src/sqitch.plan index f292ec65..b1d0e03e 100644 --- a/src/sqitch.plan +++ b/src/sqitch.plan @@ -94,5 +94,5 @@ table_event_favourite [schema_public table_account_public table_event] 1970-01-0 table_event_favourite_policy [schema_public table_account_public table_event role_account] 1970-01-01T00:00:00Z Sven Thelemann # Policy for table event_favourite. test_account_blocking [schema_test] 1970-01-01T00:00:00Z Sven Thelemann # Test cases for account blocking. index_account_location [schema_private extension_postgis table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location column in table maevsi_private.account. -index_event_location [schema_public extension_postgis table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location_geometry column in table maevsi.event. +index_event_location [schema_public extension_postgis table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location_geography column in table maevsi.event. function_package_location [schema_public schema_private extension_postgis table_account_private table_event role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # A collection of location related functions. diff --git a/src/verify/extension_postgis.sql b/src/verify/extension_postgis.sql index aa2b0716..08c0468e 100644 --- a/src/verify/extension_postgis.sql +++ b/src/verify/extension_postgis.sql @@ -7,11 +7,13 @@ SAVEPOINT function_privileges_for_roles; DO $$ DECLARE functions TEXT[] := ARRAY[ + 'maevsi.geometry(maevsi.GEOMETRY, INTEGER, BOOLEAN)', 'maevsi.geometry(TEXT)', 'maevsi.geometrytype(maevsi.GEOMETRY)', 'maevsi.postgis_type_name(CHARACTER VARYING, INTEGER, BOOLEAN)', 'maevsi.st_asgeojson(maevsi.GEOMETRY, INTEGER, INTEGER)', 'maevsi.st_coorddim(maevsi.GEOMETRY)', + 'maevsi.st_geomfromgeojson(TEXT)', 'maevsi.st_srid(maevsi.GEOMETRY)', 'maevsi.text(maevsi.GEOMETRY)' ]; diff --git a/src/verify/table_event.sql b/src/verify/table_event.sql index 817e0bd5..cf4ace85 100644 --- a/src/verify/table_event.sql +++ b/src/verify/table_event.sql @@ -10,7 +10,7 @@ SELECT id, is_in_person, is_remote, location, - location_geometry, + location_geography, name, slug, start, diff --git a/test/schema/schema.definition.sql b/test/schema/schema.definition.sql index 8a86cb17..5c38b0f5 100644 --- a/test/schema/schema.definition.sql +++ b/test/schema/schema.definition.sql @@ -319,11 +319,11 @@ BEGIN -- return account locations within a given radius around the location of an event RETURN QUERY WITH e AS ( - SELECT location_geometry FROM maevsi.event WHERE id = _event_id + SELECT location_geography FROM maevsi.event WHERE id = _event_id ) - SELECT a.id as account_id, maevsi.ST_Distance(e.location_geometry, a.location) distance + SELECT a.id as account_id, maevsi.ST_Distance(e.location_geography, a.location) distance FROM e, maevsi_private.account a - WHERE maevsi.ST_DWithin(e.location_geometry, a.location, _max_distance * 1000); + WHERE maevsi.ST_DWithin(e.location_geography, a.location, _max_distance * 1000); END; $$; @@ -386,7 +386,7 @@ BEGIN -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm -- SRID 4326: "WGS 84" (default SRID) UPDATE maevsi_private.account SET - location = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + location = maevsi.ST_Point(_longitude, _latitude, 4326) WHERE id = _account_id; END; $$; @@ -806,7 +806,7 @@ CREATE TABLE maevsi.event ( is_in_person boolean, is_remote boolean, location text, - location_geometry maevsi.geometry(Point,4839), + location_geography maevsi.geography(Point,4326), name text NOT NULL, slug text NOT NULL, start timestamp with time zone NOT NULL, @@ -903,10 +903,10 @@ COMMENT ON COLUMN maevsi.event.location IS 'The event''s location as it can be s -- --- Name: COLUMN event.location_geometry; Type: COMMENT; Schema: maevsi; Owner: postgres +-- Name: COLUMN event.location_geography; Type: COMMENT; Schema: maevsi; Owner: postgres -- -COMMENT ON COLUMN maevsi.event.location_geometry IS 'The event''s geometric location.'; +COMMENT ON COLUMN maevsi.event.location_geography IS 'The event''s geographic location.'; -- @@ -998,9 +998,9 @@ BEGIN WITH a AS ( SELECT location FROM maevsi_private.account WHERE id = _account_id ) - SELECT e.id as event_id, maevsi.ST_Distance(a.location, e.location_geometry) distance + SELECT e.id as event_id, maevsi.ST_Distance(a.location, e.location_geography) distance FROM a, maevsi.event e - WHERE maevsi.ST_DWithin(a.location, e.location_geometry, _max_distance * 1000); + WHERE maevsi.ST_DWithin(a.location, e.location_geography, _max_distance * 1000); END; $$; @@ -1094,7 +1094,7 @@ BEGIN -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm -- SRID 4326: "WGS 84" (default SRID) UPDATE maevsi.event SET - location_geometry = maevsi.ST_Transform( maevsi.ST_Point(_longitude, _latitude, 4326), 4839) + location_geography = maevsi.ST_Point(_longitude, _latitude, 4326) WHERE id = _event_id; END; $$; @@ -1219,10 +1219,11 @@ DECLARE _latitude DOUBLE PRECISION; _longitude DOUBLE PRECISION; BEGIN - SELECT maevsi.ST_Y( maevsi.ST_Transform(location, 4326)), maevsi.ST_X( maevsi.ST_Transform(location, 4326)) + SELECT maevsi.ST_Y(location::maevsi.geometry), maevsi.ST_X(location::maevsi.geometry) INTO _latitude, _longitude FROM maevsi_private.account WHERE id = _account_id; + RETURN ARRAY[_latitude, _longitude]; END; $$; @@ -1247,7 +1248,7 @@ DECLARE _latitude DOUBLE PRECISION; _longitude DOUBLE PRECISION; BEGIN - SELECT maevsi.ST_Y( maevsi.ST_Transform(location_geometry, 4326)), maevsi.ST_X( maevsi.ST_Transform(location_geometry, 4326)) + SELECT maevsi.ST_Y(location_geography::maevsi.geometry), maevsi.ST_X(location_geography::maevsi.geometry) INTO _latitude, _longitude FROM maevsi.event WHERE id = _event_id; @@ -3390,7 +3391,7 @@ CREATE TABLE maevsi_private.account ( email_address_verification uuid DEFAULT gen_random_uuid(), email_address_verification_valid_until timestamp with time zone, last_activity timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - location maevsi.geometry(Point,4839), + location maevsi.geography(Point,4326), password_hash text NOT NULL, password_reset_verification uuid, password_reset_verification_valid_until timestamp with time zone, @@ -4595,7 +4596,7 @@ COMMENT ON INDEX maevsi.idx_event_grouping_event_id IS 'Speeds up reverse foreig -- Name: idx_event_location; Type: INDEX; Schema: maevsi; Owner: postgres -- -CREATE INDEX idx_event_location ON maevsi.event USING gist (location_geometry); +CREATE INDEX idx_event_location ON maevsi.event USING gist (location_geography); -- @@ -5820,6 +5821,8 @@ REVOKE ALL ON FUNCTION maevsi.geography(maevsi.geometry) FROM PUBLIC; -- REVOKE ALL ON FUNCTION maevsi.geometry(maevsi.geometry, integer, boolean) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.geometry(maevsi.geometry, integer, boolean) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.geometry(maevsi.geometry, integer, boolean) TO maevsi_account; -- @@ -9523,6 +9526,8 @@ REVOKE ALL ON FUNCTION maevsi.st_geomfromgeojson(jsonb) FROM PUBLIC; -- REVOKE ALL ON FUNCTION maevsi.st_geomfromgeojson(text) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.st_geomfromgeojson(text) TO maevsi_anonymous; +GRANT ALL ON FUNCTION maevsi.st_geomfromgeojson(text) TO maevsi_account; -- From fc87b977dea45007feab0b7acdfaa7e5b268ad6b Mon Sep 17 00:00:00 2001 From: Jonas Thelemann Date: Wed, 22 Jan 2025 08:55:47 +0100 Subject: [PATCH 6/9] chore(location): remove table --- src/revert/table_location.sql | 7 ------- src/verify/table_location.sql | 28 ---------------------------- 2 files changed, 35 deletions(-) delete mode 100644 src/revert/table_location.sql delete mode 100644 src/verify/table_location.sql diff --git a/src/revert/table_location.sql b/src/revert/table_location.sql deleted file mode 100644 index 0672b72f..00000000 --- a/src/revert/table_location.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - ---DROP TRIGGER maevsi_location_update_geom ON maevsi.location; ---DROP FUNCTION maevsi.location_update_geom(); -DROP TABLE maevsi.location; - -COMMIT; diff --git a/src/verify/table_location.sql b/src/verify/table_location.sql deleted file mode 100644 index 840ab251..00000000 --- a/src/verify/table_location.sql +++ /dev/null @@ -1,28 +0,0 @@ -BEGIN; - -SELECT id, - location_type, - latitude, - longitude -FROM maevsi.location WHERE FALSE; - -DO $$ -BEGIN - ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'SELECT')); - ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'INSERT')); - ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'UPDATE')); - ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_account', 'maevsi.location', 'DELETE')); - ASSERT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'SELECT')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'INSERT')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'UPDATE')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_anonymous', 'maevsi.location', 'DELETE')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'SELECT')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'INSERT')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'UPDATE')); - ASSERT NOT (SELECT pg_catalog.has_table_privilege('maevsi_tusd', 'maevsi.location', 'DELETE')); - --- ASSERT (SELECT pg_catalog.has_function_privilege('maevsi_account', 'maevsi.location_update_geom()', 'EXECUTE')); - -END $$; - -ROLLBACK; From 0bc2686a67f77a0e33731ebcad4215d38fd3030a Mon Sep 17 00:00:00 2001 From: Jonas Thelemann Date: Wed, 22 Jan 2025 09:49:43 +0100 Subject: [PATCH 7/9] fix(postgis): correct execution grant --- src/deploy/extension_postgis.sql | 9 ++++----- src/revert/extension_postgis.sql | 9 ++++----- src/verify/extension_postgis.sql | 9 ++++----- test/schema/schema.definition.sql | 18 ++++++++---------- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/deploy/extension_postgis.sql b/src/deploy/extension_postgis.sql index 0d9af218..8fe34e6c 100644 --- a/src/deploy/extension_postgis.sql +++ b/src/deploy/extension_postgis.sql @@ -5,15 +5,14 @@ CREATE EXTENSION postgis WITH SCHEMA public; COMMENT ON EXTENSION postgis IS 'Functions to work with geospatial data.'; GRANT EXECUTE ON FUNCTION - public.geometry(public.geometry, integer, boolean), + public.geography(public.geometry), public.geometry(text), - public.geometrytype(public.geometry), + public.geometrytype(public.geography), public.postgis_type_name(character varying, integer, boolean), - public.st_asgeojson(public.geometry, integer, integer), + public.st_asgeojson(public.geography, integer, integer), public.st_coorddim(public.geometry), public.st_geomfromgeojson(text), - public.st_srid(public.geometry), - public.text(public.geometry) + public.st_srid(public.geography) TO maevsi_anonymous, maevsi_account; COMMIT; diff --git a/src/revert/extension_postgis.sql b/src/revert/extension_postgis.sql index 9ed72634..194ca495 100644 --- a/src/revert/extension_postgis.sql +++ b/src/revert/extension_postgis.sql @@ -1,15 +1,14 @@ BEGIN; REVOKE EXECUTE ON FUNCTION - public.text(public.geometry), - public.st_srid(public.geometry), + public.st_srid(public.geography), public.st_geomfromgeojson(text), public.st_coorddim(public.geometry), - public.st_asgeojson(public.geometry, integer, integer), + public.st_asgeojson(public.geography, integer, integer), public.postgis_type_name(character varying, integer, boolean), - public.geometrytype(public.geometry), + public.geometrytype(public.geography), public.geometry(text), - public.geometry(public.geometry, integer, boolean) + public.geography(public.geometry) FROM maevsi_anonymous, maevsi_account; DROP EXTENSION postgis; diff --git a/src/verify/extension_postgis.sql b/src/verify/extension_postgis.sql index 42aa4ca6..23bfdc69 100644 --- a/src/verify/extension_postgis.sql +++ b/src/verify/extension_postgis.sql @@ -7,15 +7,14 @@ SAVEPOINT function_privileges_for_roles; DO $$ DECLARE functions TEXT[] := ARRAY[ - 'public.geometry(public.GEOMETRY, INTEGER, BOOLEAN)', + 'public.geography(public.geometry)', 'public.geometry(TEXT)', - 'public.geometrytype(public.GEOMETRY)', + 'public.geometrytype(public.GEOGRAPHY)', 'public.postgis_type_name(CHARACTER VARYING, INTEGER, BOOLEAN)', - 'public.st_asgeojson(public.GEOMETRY, INTEGER, INTEGER)', + 'public.st_asgeojson(public.GEOGRAPHY, INTEGER, INTEGER)', 'public.st_coorddim(public.GEOMETRY)', 'public.st_geomfromgeojson(TEXT)', - 'public.st_srid(public.GEOMETRY)', - 'public.text(public.GEOMETRY)' + 'public.st_srid(public.GEOGRAPHY)' ]; roles TEXT[] := ARRAY['maevsi_account', 'maevsi_anonymous']; function TEXT; diff --git a/test/schema/schema.definition.sql b/test/schema/schema.definition.sql index 465f3404..23b67776 100644 --- a/test/schema/schema.definition.sql +++ b/test/schema/schema.definition.sql @@ -5814,6 +5814,8 @@ REVOKE ALL ON FUNCTION public.bytea(public.geometry) FROM PUBLIC; -- REVOKE ALL ON FUNCTION public.geography(public.geometry) FROM PUBLIC; +GRANT ALL ON FUNCTION public.geography(public.geometry) TO maevsi_anonymous; +GRANT ALL ON FUNCTION public.geography(public.geometry) TO maevsi_account; -- @@ -5821,8 +5823,6 @@ REVOKE ALL ON FUNCTION public.geography(public.geometry) FROM PUBLIC; -- REVOKE ALL ON FUNCTION public.geometry(public.geometry, integer, boolean) FROM PUBLIC; -GRANT ALL ON FUNCTION public.geometry(public.geometry, integer, boolean) TO maevsi_anonymous; -GRANT ALL ON FUNCTION public.geometry(public.geometry, integer, boolean) TO maevsi_account; -- @@ -5865,8 +5865,6 @@ REVOKE ALL ON FUNCTION public.polygon(public.geometry) FROM PUBLIC; -- REVOKE ALL ON FUNCTION public.text(public.geometry) FROM PUBLIC; -GRANT ALL ON FUNCTION public.text(public.geometry) TO maevsi_anonymous; -GRANT ALL ON FUNCTION public.text(public.geometry) TO maevsi_account; -- @@ -7505,6 +7503,8 @@ REVOKE ALL ON FUNCTION public.geometry_within_nd(public.geometry, public.geometr -- REVOKE ALL ON FUNCTION public.geometrytype(public.geography) FROM PUBLIC; +GRANT ALL ON FUNCTION public.geometrytype(public.geography) TO maevsi_anonymous; +GRANT ALL ON FUNCTION public.geometrytype(public.geography) TO maevsi_account; -- @@ -7512,8 +7512,6 @@ REVOKE ALL ON FUNCTION public.geometrytype(public.geography) FROM PUBLIC; -- REVOKE ALL ON FUNCTION public.geometrytype(public.geometry) FROM PUBLIC; -GRANT ALL ON FUNCTION public.geometrytype(public.geometry) TO maevsi_anonymous; -GRANT ALL ON FUNCTION public.geometrytype(public.geometry) TO maevsi_account; -- @@ -8573,6 +8571,8 @@ REVOKE ALL ON FUNCTION public.st_asgeojson(text) FROM PUBLIC; -- REVOKE ALL ON FUNCTION public.st_asgeojson(geog public.geography, maxdecimaldigits integer, options integer) FROM PUBLIC; +GRANT ALL ON FUNCTION public.st_asgeojson(geog public.geography, maxdecimaldigits integer, options integer) TO maevsi_anonymous; +GRANT ALL ON FUNCTION public.st_asgeojson(geog public.geography, maxdecimaldigits integer, options integer) TO maevsi_account; -- @@ -8580,8 +8580,6 @@ REVOKE ALL ON FUNCTION public.st_asgeojson(geog public.geography, maxdecimaldigi -- REVOKE ALL ON FUNCTION public.st_asgeojson(geom public.geometry, maxdecimaldigits integer, options integer) FROM PUBLIC; -GRANT ALL ON FUNCTION public.st_asgeojson(geom public.geometry, maxdecimaldigits integer, options integer) TO maevsi_anonymous; -GRANT ALL ON FUNCTION public.st_asgeojson(geom public.geometry, maxdecimaldigits integer, options integer) TO maevsi_account; -- @@ -11183,6 +11181,8 @@ REVOKE ALL ON FUNCTION public.st_squaregrid(size double precision, bounds public -- REVOKE ALL ON FUNCTION public.st_srid(geog public.geography) FROM PUBLIC; +GRANT ALL ON FUNCTION public.st_srid(geog public.geography) TO maevsi_anonymous; +GRANT ALL ON FUNCTION public.st_srid(geog public.geography) TO maevsi_account; -- @@ -11190,8 +11190,6 @@ REVOKE ALL ON FUNCTION public.st_srid(geog public.geography) FROM PUBLIC; -- REVOKE ALL ON FUNCTION public.st_srid(geom public.geometry) FROM PUBLIC; -GRANT ALL ON FUNCTION public.st_srid(geom public.geometry) TO maevsi_anonymous; -GRANT ALL ON FUNCTION public.st_srid(geom public.geometry) TO maevsi_account; -- From 7091a2e0d372ef76fba685128810bf2f7388a3cd Mon Sep 17 00:00:00 2001 From: Sven Thelemann Date: Wed, 22 Jan 2025 17:26:22 +0100 Subject: [PATCH 8/9] feat(location): add verifications for indexes Queries on dictionary tables were added to the verification scripts for spatial indexes. --- src/verify/index_account_location.sql | 6 ++++++ src/verify/index_event_location.sql | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/verify/index_account_location.sql b/src/verify/index_account_location.sql index 1137b56b..5ae5ec2d 100644 --- a/src/verify/index_account_location.sql +++ b/src/verify/index_account_location.sql @@ -1,3 +1,9 @@ BEGIN; +SELECT 1/COUNT(*) +FROM pg_class c +JOIN pg_namespace n ON n.oid = c.relnamespace +WHERE c.relname = 'idx_account_location' +AND n.nspname = 'maevsi_private'; + ROLLBACK; diff --git a/src/verify/index_event_location.sql b/src/verify/index_event_location.sql index 1137b56b..90251554 100644 --- a/src/verify/index_event_location.sql +++ b/src/verify/index_event_location.sql @@ -1,3 +1,9 @@ BEGIN; +SELECT 1/COUNT(*) +FROM pg_class c +JOIN pg_namespace n ON n.oid = c.relnamespace +WHERE c.relname = 'idx_event_location' +AND n.nspname = 'maevsi'; + ROLLBACK; From c8f1a9f6d19e32e2bda26496449efd5ec8e3e59b Mon Sep 17 00:00:00 2001 From: Jonas Thelemann Date: Sat, 25 Jan 2025 18:54:23 +0100 Subject: [PATCH 9/9] refactor: work in feedback --- src/deploy/extension_postgis.sql | 16 +- src/deploy/function_package_location.sql | 121 ------- src/deploy/index_account_location.sql | 7 - src/deploy/index_account_private_location.sql | 7 + src/deploy/table_account_private.sql | 2 +- src/deploy/table_event.sql | 2 +- src/deploy/test_location.sql | 162 +++++++++ src/revert/extension_postgis.sql | 16 +- src/revert/function_package_location.sql | 12 - src/revert/index_account_location.sql | 5 - src/revert/index_account_private_location.sql | 5 + src/revert/test_location.sql | 12 + src/sqitch.plan | 6 +- src/verify/extension_postgis.sql | 18 +- src/verify/function_package_location.sql | 81 ----- ...sql => index_account_private_location.sql} | 2 +- src/verify/test_location.sql | 73 +++++ test/schema/schema.definition.sql | 308 ++++++++++-------- 18 files changed, 460 insertions(+), 395 deletions(-) delete mode 100644 src/deploy/function_package_location.sql delete mode 100644 src/deploy/index_account_location.sql create mode 100644 src/deploy/index_account_private_location.sql create mode 100644 src/deploy/test_location.sql delete mode 100644 src/revert/function_package_location.sql delete mode 100644 src/revert/index_account_location.sql create mode 100644 src/revert/index_account_private_location.sql create mode 100644 src/revert/test_location.sql delete mode 100644 src/verify/function_package_location.sql rename src/verify/{index_account_location.sql => index_account_private_location.sql} (73%) create mode 100644 src/verify/test_location.sql diff --git a/src/deploy/extension_postgis.sql b/src/deploy/extension_postgis.sql index 8fe34e6c..36c25266 100644 --- a/src/deploy/extension_postgis.sql +++ b/src/deploy/extension_postgis.sql @@ -5,14 +5,14 @@ CREATE EXTENSION postgis WITH SCHEMA public; COMMENT ON EXTENSION postgis IS 'Functions to work with geospatial data.'; GRANT EXECUTE ON FUNCTION - public.geography(public.geometry), - public.geometry(text), - public.geometrytype(public.geography), - public.postgis_type_name(character varying, integer, boolean), - public.st_asgeojson(public.geography, integer, integer), - public.st_coorddim(public.geometry), - public.st_geomfromgeojson(text), - public.st_srid(public.geography) + geography(geometry), + geometry(text), + geometrytype(geography), + postgis_type_name(character varying, integer, boolean), + st_asgeojson(geography, integer, integer), + st_coorddim(geometry), + st_geomfromgeojson(text), + st_srid(geography) TO maevsi_anonymous, maevsi_account; COMMIT; diff --git a/src/deploy/function_package_location.sql b/src/deploy/function_package_location.sql deleted file mode 100644 index b4d8812e..00000000 --- a/src/deploy/function_package_location.sql +++ /dev/null @@ -1,121 +0,0 @@ -BEGIN; - -CREATE FUNCTION maevsi.event_distances ( - _account_id UUID, - _max_distance DOUBLE PRECISION) -RETURNS TABLE (event_id UUID, distance DOUBLE PRECISION) AS $$ -BEGIN - RETURN QUERY - WITH a AS ( - SELECT location FROM maevsi_private.account WHERE id = _account_id - ) - SELECT e.id as event_id, public.ST_Distance(a.location, e.location_geography) distance - FROM a, maevsi.event e - WHERE public.ST_DWithin(a.location, e.location_geography, _max_distance * 1000); -END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; - -COMMENT ON FUNCTION maevsi.event_distances(UUID, DOUBLE PRECISION) IS 'Returns event locations within a given radius around the location of an account.'; - -GRANT EXECUTE ON FUNCTION maevsi.event_distances(UUID, DOUBLE PRECISION) TO maevsi_account; - -------------------------------------------------- - -CREATE FUNCTION maevsi.account_distances ( - _event_id UUID, - _max_distance DOUBLE PRECISION) -RETURNS TABLE (account_id UUID, distance DOUBLE PRECISION) AS $$ -BEGIN - -- return account locations within a given radius around the location of an event - RETURN QUERY - WITH e AS ( - SELECT location_geography FROM maevsi.event WHERE id = _event_id - ) - SELECT a.id as account_id, public.ST_Distance(e.location_geography, a.location) distance - FROM e, maevsi_private.account a - WHERE public.ST_DWithin(e.location_geography, a.location, _max_distance * 1000); -END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; - -COMMENT ON FUNCTION maevsi.account_distances(UUID, DOUBLE PRECISION) IS 'Returns account locations within a given radius around the location of an event.'; - -GRANT EXECUTE ON FUNCTION maevsi.account_distances(UUID, DOUBLE PRECISION) TO maevsi_account; - -------------------------------------------------- - -CREATE FUNCTION maevsi.account_location_update ( - _account_id UUID, - _latitude DOUBLE PRECISION, - _longitude DOUBLE PRECISION) -RETURNS VOID AS $$ -BEGIN - -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm - -- SRID 4326: "WGS 84" (default SRID) - UPDATE maevsi_private.account SET - location = public.ST_Point(_longitude, _latitude, 4326) - WHERE id = _account_id; -END; $$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; - -COMMENT ON FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Updates an account''s location based on latitude and longitude (GPS coordinates).'; - -GRANT EXECUTE ON FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; - -------------------------------------------------- - -CREATE FUNCTION maevsi.event_location_update ( - _event_id UUID, - _latitude DOUBLE PRECISION, - _longitude DOUBLE PRECISION) -RETURNS VOID AS $$ -BEGIN - -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm - -- SRID 4326: "WGS 84" (default SRID) - UPDATE maevsi.event SET - location_geography = public.ST_Point(_longitude, _latitude, 4326) - WHERE id = _event_id; -END; $$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; - -COMMENT ON FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Updates an event''s location based on latitude and longitude (GPS coordinates).'; - -GRANT EXECUTE ON FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; - -------------------------------------------------- - -CREATE FUNCTION maevsi.get_account_location_coordinates ( - _account_id UUID) -RETURNS DOUBLE PRECISION[] AS $$ -DECLARE - _latitude DOUBLE PRECISION; - _longitude DOUBLE PRECISION; -BEGIN - SELECT public.ST_Y(location::public.geometry), public.ST_X(location::public.geometry) - INTO _latitude, _longitude - FROM maevsi_private.account - WHERE id = _account_id; - - RETURN ARRAY[_latitude, _longitude]; -END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; - -COMMENT ON FUNCTION maevsi.get_account_location_coordinates(UUID) IS 'Returns an array with latitude and longitude of the account''s current location data'; - -GRANT EXECUTE ON FUNCTION maevsi.get_account_location_coordinates(UUID) TO maevsi_account; - -------------------------------------------------- - -CREATE FUNCTION maevsi.get_event_location_coordinates ( - _event_id UUID) -RETURNS DOUBLE PRECISION[] AS $$ -DECLARE - _latitude DOUBLE PRECISION; - _longitude DOUBLE PRECISION; -BEGIN - SELECT public.ST_Y(location_geography::public.geometry), public.ST_X(location_geography::public.geometry) - INTO _latitude, _longitude - FROM maevsi.event - WHERE id = _event_id; - RETURN ARRAY[_latitude, _longitude]; -END; $$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; - -COMMENT ON FUNCTION maevsi.get_event_location_coordinates(UUID) IS 'Returns an array with latitude and longitude of the event''s current location data.'; - -GRANT EXECUTE ON FUNCTION maevsi.get_event_location_coordinates(UUID) TO maevsi_account; - -COMMIT; diff --git a/src/deploy/index_account_location.sql b/src/deploy/index_account_location.sql deleted file mode 100644 index a78b71c4..00000000 --- a/src/deploy/index_account_location.sql +++ /dev/null @@ -1,7 +0,0 @@ -BEGIN; - -CREATE INDEX idx_account_location ON maevsi_private.account USING GIST (location); - -COMMENT ON INDEX maevsi_private.idx_account_location IS 'Spatial index on column location in maevsi_private.account.'; - -COMMIT; diff --git a/src/deploy/index_account_private_location.sql b/src/deploy/index_account_private_location.sql new file mode 100644 index 00000000..e78e911d --- /dev/null +++ b/src/deploy/index_account_private_location.sql @@ -0,0 +1,7 @@ +BEGIN; + +CREATE INDEX idx_account_private_location ON maevsi_private.account USING GIST (location); + +COMMENT ON INDEX maevsi_private.idx_account_private_location IS 'Spatial index on column location in maevsi_private.account.'; + +COMMIT; diff --git a/src/deploy/table_account_private.sql b/src/deploy/table_account_private.sql index a379ba1c..c5e05abd 100644 --- a/src/deploy/table_account_private.sql +++ b/src/deploy/table_account_private.sql @@ -7,7 +7,7 @@ CREATE TABLE maevsi_private.account ( email_address TEXT NOT NULL CHECK (char_length(email_address) < 255) UNIQUE, -- no regex check as "a valid email address is one that you can send emails to" (http://www.dominicsayers.com/isemail/) email_address_verification UUID DEFAULT gen_random_uuid(), email_address_verification_valid_until TIMESTAMP WITH TIME ZONE, - location public.GEOGRAPHY(Point, 4326), + location GEOGRAPHY(Point, 4326), password_hash TEXT NOT NULL, password_reset_verification UUID, password_reset_verification_valid_until TIMESTAMP WITH TIME ZONE, diff --git a/src/deploy/table_event.sql b/src/deploy/table_event.sql index bd88161e..cdbc98cf 100644 --- a/src/deploy/table_event.sql +++ b/src/deploy/table_event.sql @@ -12,7 +12,7 @@ CREATE TABLE maevsi.event ( is_remote BOOLEAN, language maevsi.language, location TEXT CHECK (char_length("location") > 0 AND char_length("location") < 300), - location_geography public.GEOGRAPHY(Point, 4326), + location_geography GEOGRAPHY(Point, 4326), name TEXT NOT NULL CHECK (char_length("name") > 0 AND char_length("name") < 100), slug TEXT NOT NULL CHECK (char_length(slug) < 100 AND slug ~ '^[-A-Za-z0-9]+$'), start TIMESTAMP WITH TIME ZONE NOT NULL, diff --git a/src/deploy/test_location.sql b/src/deploy/test_location.sql new file mode 100644 index 00000000..1e9590fa --- /dev/null +++ b/src/deploy/test_location.sql @@ -0,0 +1,162 @@ +BEGIN; + + +CREATE FUNCTION maevsi.account_filter_radius_event( + _event_id UUID, + _distance_max DOUBLE PRECISION +) +RETURNS TABLE ( + account_id UUID, + distance DOUBLE PRECISION +) AS $$ +BEGIN + RETURN QUERY + WITH event AS ( + SELECT location_geography + FROM maevsi.event + WHERE id = _event_id + ) + SELECT + a.id AS account_id, + ST_Distance(e.location_geography, a.location) AS distance + FROM + event e, + maevsi_private.account a + WHERE + ST_DWithin(e.location_geography, a.location, _distance_max * 1000); +END; +$$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.account_filter_radius_event(UUID, DOUBLE PRECISION) IS 'Returns account locations within a given radius around the location of an event.'; + +GRANT EXECUTE ON FUNCTION maevsi.account_filter_radius_event(UUID, DOUBLE PRECISION) TO maevsi_account; + + +CREATE FUNCTION maevsi.event_filter_radius_account( + _account_id UUID, + _distance_max DOUBLE PRECISION +) +RETURNS TABLE ( + event_id UUID, + distance DOUBLE PRECISION +) AS $$ +BEGIN + RETURN QUERY + WITH account AS ( + SELECT location + FROM maevsi_private.account + WHERE id = _account_id + ) + SELECT + e.id AS event_id, + ST_Distance(a.location, e.location_geography) AS distance + FROM + account a, + maevsi.event e + WHERE + ST_DWithin(a.location, e.location_geography, _distance_max * 1000); +END; +$$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.event_filter_radius_account(UUID, DOUBLE PRECISION) IS 'Returns event locations within a given radius around the location of an account.'; + +GRANT EXECUTE ON FUNCTION maevsi.event_filter_radius_account(UUID, DOUBLE PRECISION) TO maevsi_account; + + +CREATE FUNCTION maevsi.account_location_update( + _account_id UUID, + _latitude DOUBLE PRECISION, + _longitude DOUBLE PRECISION +) +RETURNS VOID AS $$ +BEGIN + UPDATE maevsi_private.account + SET + location = ST_Point(_longitude, _latitude, 4326) + WHERE + id = _account_id; +END; +$$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Updates an account''s location based on latitude and longitude (GPS coordinates).'; + +GRANT EXECUTE ON FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; + + +CREATE FUNCTION maevsi.event_location_update( + _event_id UUID, + _latitude DOUBLE PRECISION, + _longitude DOUBLE PRECISION +) +RETURNS VOID AS $$ +BEGIN + UPDATE maevsi.event + SET + location_geography = ST_Point(_longitude, _latitude, 4326) + WHERE + id = _event_id; +END; +$$ LANGUAGE PLPGSQL STRICT SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) IS 'Updates an event''s location based on latitude and longitude (GPS coordinates).'; + +GRANT EXECUTE ON FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION) TO maevsi_account; + + +CREATE FUNCTION maevsi.account_location_coordinates( + _account_id UUID +) +RETURNS DOUBLE PRECISION[] AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT + ST_Y(location::geometry), + ST_X(location::geometry) + INTO + _latitude, + _longitude + FROM + maevsi_private.account + WHERE + id = _account_id; + + RETURN ARRAY[_latitude, _longitude]; +END; +$$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.account_location_coordinates(UUID) IS 'Returns an array with latitude and longitude of the account''s current location data'; + +GRANT EXECUTE ON FUNCTION maevsi.account_location_coordinates(UUID) TO maevsi_account; + + +CREATE FUNCTION maevsi.event_location_coordinates( + _event_id UUID +) +RETURNS DOUBLE PRECISION[] AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT + ST_Y(location_geography::geometry), + ST_X(location_geography::geometry) + INTO + _latitude, + _longitude + FROM + maevsi.event + WHERE + id = _event_id; + + RETURN ARRAY[_latitude, _longitude]; +END; +$$ LANGUAGE PLPGSQL STRICT STABLE SECURITY DEFINER; + +COMMENT ON FUNCTION maevsi.event_location_coordinates(UUID) IS 'Returns an array with latitude and longitude of the event''s current location data.'; + +GRANT EXECUTE ON FUNCTION maevsi.event_location_coordinates(UUID) TO maevsi_account; + + +COMMIT; diff --git a/src/revert/extension_postgis.sql b/src/revert/extension_postgis.sql index 194ca495..aa435f50 100644 --- a/src/revert/extension_postgis.sql +++ b/src/revert/extension_postgis.sql @@ -1,14 +1,14 @@ BEGIN; REVOKE EXECUTE ON FUNCTION - public.st_srid(public.geography), - public.st_geomfromgeojson(text), - public.st_coorddim(public.geometry), - public.st_asgeojson(public.geography, integer, integer), - public.postgis_type_name(character varying, integer, boolean), - public.geometrytype(public.geography), - public.geometry(text), - public.geography(public.geometry) + st_srid(geography), + st_geomfromgeojson(text), + st_coorddim(geometry), + st_asgeojson(geography, integer, integer), + postgis_type_name(character varying, integer, boolean), + geometrytype(geography), + geometry(text), + geography(geometry) FROM maevsi_anonymous, maevsi_account; DROP EXTENSION postgis; diff --git a/src/revert/function_package_location.sql b/src/revert/function_package_location.sql deleted file mode 100644 index 778447ae..00000000 --- a/src/revert/function_package_location.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN; - -DROP FUNCTION maevsi.event_distances(UUID, DOUBLE PRECISION); -DROP FUNCTION maevsi.account_distances(UUID, DOUBLE PRECISION); - -DROP FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION); -DROP FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION); - -DROP FUNCTION maevsi.get_account_location_coordinates(UUID); -DROP FUNCTION maevsi.get_event_location_coordinates(UUID); - -COMMIT; diff --git a/src/revert/index_account_location.sql b/src/revert/index_account_location.sql deleted file mode 100644 index d4277036..00000000 --- a/src/revert/index_account_location.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -DROP INDEX maevsi_private.idx_account_location; - -COMMIT; diff --git a/src/revert/index_account_private_location.sql b/src/revert/index_account_private_location.sql new file mode 100644 index 00000000..3ebd791a --- /dev/null +++ b/src/revert/index_account_private_location.sql @@ -0,0 +1,5 @@ +BEGIN; + +DROP INDEX maevsi_private.idx_account_private_location; + +COMMIT; diff --git a/src/revert/test_location.sql b/src/revert/test_location.sql new file mode 100644 index 00000000..67a1d508 --- /dev/null +++ b/src/revert/test_location.sql @@ -0,0 +1,12 @@ +BEGIN; + +DROP FUNCTION maevsi.account_filter_radius_event(UUID, DOUBLE PRECISION); +DROP FUNCTION maevsi.event_filter_radius_account(UUID, DOUBLE PRECISION); + +DROP FUNCTION maevsi.account_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION); +DROP FUNCTION maevsi.event_location_update(UUID, DOUBLE PRECISION, DOUBLE PRECISION); + +DROP FUNCTION maevsi.account_location_coordinates(UUID); +DROP FUNCTION maevsi.event_location_coordinates(UUID); + +COMMIT; diff --git a/src/sqitch.plan b/src/sqitch.plan index 66400afc..f35d2d03 100644 --- a/src/sqitch.plan +++ b/src/sqitch.plan @@ -95,6 +95,6 @@ table_event_favourite [schema_public table_account_public table_event] 1970-01-0 table_event_favourite_policy [schema_public table_account_public table_event role_account] 1970-01-01T00:00:00Z Sven Thelemann # Policy for table event_favourite. test_account_blocking [schema_test] 1970-01-01T00:00:00Z Sven Thelemann # Test cases for account blocking. function_event_search [privilege_execute_revoke schema_public enum_language schema_private function_language_iso_full_text_search table_event role_account role_anonymous] 1970-01-01T00:00:00Z Jonas Thelemann # Full-text search on events. -index_account_location [schema_private extension_postgis table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location column in table maevsi_private.account. -index_event_location [schema_public extension_postgis table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location_geography column in table maevsi.event. -function_package_location [schema_public schema_private extension_postgis table_account_private table_event role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # A collection of location related functions. +index_account_private_location [schema_private table_account_private] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location column in table maevsi_private.account. +index_event_location [schema_public table_event] 1970-01-01T00:00:00Z Sven Thelemann # Spacial index on location_geography column in table maevsi.event. +test_location [schema_public schema_private extension_postgis table_account_private table_event role_anonymous role_account] 1970-01-01T00:00:00Z Sven Thelemann # A collection of location related functions. diff --git a/src/verify/extension_postgis.sql b/src/verify/extension_postgis.sql index 23bfdc69..74691917 100644 --- a/src/verify/extension_postgis.sql +++ b/src/verify/extension_postgis.sql @@ -1,20 +1,20 @@ BEGIN; SELECT 1/count(*) FROM pg_extension WHERE extname = 'postgis'; -SELECT has_function_privilege('public.ST_DWithin(public.geometry, public.geometry, double precision)', 'EXECUTE'); +SELECT has_function_privilege('ST_DWithin(geometry, geometry, double precision)', 'EXECUTE'); SAVEPOINT function_privileges_for_roles; DO $$ DECLARE functions TEXT[] := ARRAY[ - 'public.geography(public.geometry)', - 'public.geometry(TEXT)', - 'public.geometrytype(public.GEOGRAPHY)', - 'public.postgis_type_name(CHARACTER VARYING, INTEGER, BOOLEAN)', - 'public.st_asgeojson(public.GEOGRAPHY, INTEGER, INTEGER)', - 'public.st_coorddim(public.GEOMETRY)', - 'public.st_geomfromgeojson(TEXT)', - 'public.st_srid(public.GEOGRAPHY)' + 'geography(geometry)', + 'geometry(TEXT)', + 'geometrytype(GEOGRAPHY)', + 'postgis_type_name(CHARACTER VARYING, INTEGER, BOOLEAN)', + 'st_asgeojson(GEOGRAPHY, INTEGER, INTEGER)', + 'st_coorddim(GEOMETRY)', + 'st_geomfromgeojson(TEXT)', + 'st_srid(GEOGRAPHY)' ]; roles TEXT[] := ARRAY['maevsi_account', 'maevsi_anonymous']; function TEXT; diff --git a/src/verify/function_package_location.sql b/src/verify/function_package_location.sql deleted file mode 100644 index 5843b1e5..00000000 --- a/src/verify/function_package_location.sql +++ /dev/null @@ -1,81 +0,0 @@ -BEGIN; - -DO $$ -DECLARE - _account_id UUID; - _event_id UUID; - _coordinates DOUBLE PRECISION[]; - _id UUID; -BEGIN - - _account_id := maevsi.account_registration('username', 'email@example.com', 'password', 'en'); - PERFORM maevsi.account_email_address_verification( - (SELECT email_address_verification FROM maevsi_private.account WHERE id = _account_id) - ); - - SET LOCAL role = 'maevsi_account'; - EXECUTE 'SET LOCAL jwt.claims.account_id = ''' || _account_id || ''''; - - INSERT INTO maevsi.event(author_account_id, name, slug, start, visibility) - VALUES (_account_id, 'event', 'event', CURRENT_TIMESTAMP, 'public'::maevsi.event_visibility) - RETURNING id INTO _event_id; - - ------------------------------------- - - PERFORM maevsi.account_location_update(_account_id, 51.304, 9.476); -- somewhere in Kassel - - _coordinates := maevsi.get_account_location_coordinates(_account_id); - - IF NOT (round(_coordinates[1]::numeric, 3) = 51.304 AND round(_coordinates[2]::numeric, 3) = 9.476) THEN - RAISE EXCEPTION 'wrong account coordinates.'; - END IF; - - ------------------------------------- - - PERFORM maevsi.event_location_update(_event_id, 50.113, 8.650); -- somewhere in Frankfurt - - _coordinates := maevsi.get_event_location_coordinates(_event_id); - - IF NOT (round(_coordinates[1]::numeric, 3) = 50.113 AND round(_coordinates[2]::numeric, 3) = 8.650) THEN - RAISE EXCEPTION 'wrong event coordinates.'; - END IF; - - ------------------------------------- - - SELECT event_id INTO _id - FROM maevsi.maevsi.event_distances(_account_id, 100); - - IF _id IS NOT NULL THEN - RAISE EXCEPTION 'function event_distances with radius 100 km should have returned an empty result.'; - END IF; - - SELECT event_id INTO _id - FROM maevsi.maevsi.event_distances(_account_id, 250); - - IF _id != _event_id THEN - RAISE EXCEPTION 'function event_distances with radius 250 km should have returned _event_id.'; - END IF; - - ------------------------------------- - - SELECT account_id INTO _id - FROM maevsi.maevsi.account_distances(_event_id, 100); - - IF _id IS NOT NULL THEN - RAISE EXCEPTION 'function account_distances with radius 100 km should have returned an empty result.'; - END IF; - - SELECT account_id INTO _id - FROM maevsi.maevsi.account_distances(_event_id, 250); - - IF _id != _account_id THEN - RAISE EXCEPTION 'function account_distances with radius 250 km should have returned _account_id.'; - END IF; - - ------------------------------------- - - RAISE NOTICE 'tests ok'; - -END; $$ LANGUAGE plpgsql; - -ROLLBACK; diff --git a/src/verify/index_account_location.sql b/src/verify/index_account_private_location.sql similarity index 73% rename from src/verify/index_account_location.sql rename to src/verify/index_account_private_location.sql index 5ae5ec2d..16c05d09 100644 --- a/src/verify/index_account_location.sql +++ b/src/verify/index_account_private_location.sql @@ -3,7 +3,7 @@ BEGIN; SELECT 1/COUNT(*) FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace -WHERE c.relname = 'idx_account_location' +WHERE c.relname = 'idx_account_private_location' AND n.nspname = 'maevsi_private'; ROLLBACK; diff --git a/src/verify/test_location.sql b/src/verify/test_location.sql new file mode 100644 index 00000000..bde9573e --- /dev/null +++ b/src/verify/test_location.sql @@ -0,0 +1,73 @@ +BEGIN; + +DO $$ +DECLARE + _account_id UUID; + _event_id UUID; + _coordinates DOUBLE PRECISION[]; + _id UUID; +BEGIN + -- Register account + _account_id := maevsi.account_registration('username', 'email@example.com', 'password', 'en'); + PERFORM maevsi.account_email_address_verification( + (SELECT email_address_verification FROM maevsi_private.account WHERE id = _account_id) + ); + + -- Set account-specific context + SET LOCAL role = 'maevsi_account'; + EXECUTE 'SET LOCAL jwt.claims.account_id = ''' || _account_id || ''''; + + -- Create event + INSERT INTO maevsi.event(author_account_id, name, slug, start, visibility) + VALUES (_account_id, 'event', 'event', CURRENT_TIMESTAMP, 'public'::maevsi.event_visibility) + RETURNING id INTO _event_id; + + -- Update and validate account location + PERFORM maevsi.account_location_update(_account_id, 51.304, 9.476); -- Somewhere in Kassel + _coordinates := maevsi.account_location_coordinates(_account_id); + + IF NOT (round(_coordinates[1]::numeric, 3) = 51.304 AND round(_coordinates[2]::numeric, 3) = 9.476) THEN + RAISE EXCEPTION 'Wrong account coordinates'; + END IF; + + -- Update and validate event location + PERFORM maevsi.event_location_update(_event_id, 50.113, 8.650); -- Somewhere in Frankfurt + _coordinates := maevsi.event_location_coordinates(_event_id); + + IF NOT (round(_coordinates[1]::numeric, 3) = 50.113 AND round(_coordinates[2]::numeric, 3) = 8.650) THEN + RAISE EXCEPTION 'Wrong event coordinates'; + END IF; + + -- Test event filtering by radius from account + SELECT event_id INTO _id + FROM maevsi.maevsi.event_filter_radius_account(_account_id, 100); + + IF _id IS NOT NULL THEN + RAISE EXCEPTION 'Function `event_filter_radius_account` with radius 100 km should have returned an empty result'; + END IF; + + SELECT event_id INTO _id + FROM maevsi.maevsi.event_filter_radius_account(_account_id, 250); + + IF _id != _event_id THEN + RAISE EXCEPTION 'Function `event_filter_radius_account` with radius 250 km should have returned `_event_id`'; + END IF; + + -- Test account filtering by radius from event + SELECT account_id INTO _id + FROM maevsi.maevsi.account_filter_radius_event(_event_id, 100); + + IF _id IS NOT NULL THEN + RAISE EXCEPTION 'Function `account_filter_radius_event` with radius 100 km should have returned an empty result'; + END IF; + + SELECT account_id INTO _id + FROM maevsi.maevsi.account_filter_radius_event(_event_id, 250); + + IF _id != _account_id THEN + RAISE EXCEPTION 'Function `account_filter_radius_event` with radius 250 km should have returned `_account_id`'; + END IF; +END; +$$ LANGUAGE plpgsql; + +ROLLBACK; diff --git a/test/schema/schema.definition.sql b/test/schema/schema.definition.sql index a53329a0..e526f526 100644 --- a/test/schema/schema.definition.sql +++ b/test/schema/schema.definition.sql @@ -309,34 +309,6 @@ ALTER FUNCTION maevsi.account_delete(password text) OWNER TO postgres; COMMENT ON FUNCTION maevsi.account_delete(password text) IS 'Allows to delete an account.'; --- --- Name: account_distances(uuid, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres --- - -CREATE FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) RETURNS TABLE(account_id uuid, distance double precision) - LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER - AS $$ -BEGIN - -- return account locations within a given radius around the location of an event - RETURN QUERY - WITH e AS ( - SELECT location_geography FROM maevsi.event WHERE id = _event_id - ) - SELECT a.id as account_id, public.ST_Distance(e.location_geography, a.location) distance - FROM e, maevsi_private.account a - WHERE public.ST_DWithin(e.location_geography, a.location, _max_distance * 1000); -END; $$; - - -ALTER FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) OWNER TO postgres; - --- --- Name: FUNCTION account_distances(_event_id uuid, _max_distance double precision); Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) IS 'Returns account locations within a given radius around the location of an event.'; - - -- -- Name: account_email_address_verification(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -376,6 +348,77 @@ ALTER FUNCTION maevsi.account_email_address_verification(code uuid) OWNER TO pos COMMENT ON FUNCTION maevsi.account_email_address_verification(code uuid) IS 'Sets the account''s email address verification code to `NULL` for which the email address verification code equals the one passed and is up to date.'; +-- +-- Name: account_filter_radius_event(uuid, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.account_filter_radius_event(_event_id uuid, _distance_max double precision) RETURNS TABLE(account_id uuid, distance double precision) + LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER + AS $$ +BEGIN + RETURN QUERY + WITH event AS ( + SELECT location_geography + FROM maevsi.event + WHERE id = _event_id + ) + SELECT + a.id AS account_id, + ST_Distance(e.location_geography, a.location) AS distance + FROM + event e, + maevsi_private.account a + WHERE + ST_DWithin(e.location_geography, a.location, _distance_max * 1000); +END; +$$; + + +ALTER FUNCTION maevsi.account_filter_radius_event(_event_id uuid, _distance_max double precision) OWNER TO postgres; + +-- +-- Name: FUNCTION account_filter_radius_event(_event_id uuid, _distance_max double precision); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.account_filter_radius_event(_event_id uuid, _distance_max double precision) IS 'Returns account locations within a given radius around the location of an event.'; + + +-- +-- Name: account_location_coordinates(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.account_location_coordinates(_account_id uuid) RETURNS double precision[] + LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER + AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT + ST_Y(location::geometry), + ST_X(location::geometry) + INTO + _latitude, + _longitude + FROM + maevsi_private.account + WHERE + id = _account_id; + + RETURN ARRAY[_latitude, _longitude]; +END; +$$; + + +ALTER FUNCTION maevsi.account_location_coordinates(_account_id uuid) OWNER TO postgres; + +-- +-- Name: FUNCTION account_location_coordinates(_account_id uuid); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.account_location_coordinates(_account_id uuid) IS 'Returns an array with latitude and longitude of the account''s current location data'; + + -- -- Name: account_location_update(uuid, double precision, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -384,12 +427,13 @@ CREATE FUNCTION maevsi.account_location_update(_account_id uuid, _latitude doubl LANGUAGE plpgsql STRICT SECURITY DEFINER AS $$ BEGIN - -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm - -- SRID 4326: "WGS 84" (default SRID) - UPDATE maevsi_private.account SET - location = public.ST_Point(_longitude, _latitude, 4326) - WHERE id = _account_id; -END; $$; + UPDATE maevsi_private.account + SET + location = ST_Point(_longitude, _latitude, 4326) + WHERE + id = _account_id; +END; +$$; ALTER FUNCTION maevsi.account_location_update(_account_id uuid, _latitude double precision, _longitude double precision) OWNER TO postgres; @@ -998,30 +1042,38 @@ COMMENT ON FUNCTION maevsi.event_delete(id uuid, password text) IS 'Allows to de -- --- Name: event_distances(uuid, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- Name: event_filter_radius_account(uuid, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres -- -CREATE FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) RETURNS TABLE(event_id uuid, distance double precision) +CREATE FUNCTION maevsi.event_filter_radius_account(_account_id uuid, _distance_max double precision) RETURNS TABLE(event_id uuid, distance double precision) LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER AS $$ BEGIN RETURN QUERY - WITH a AS ( - SELECT location FROM maevsi_private.account WHERE id = _account_id + WITH account AS ( + SELECT location + FROM maevsi_private.account + WHERE id = _account_id ) - SELECT e.id as event_id, public.ST_Distance(a.location, e.location_geography) distance - FROM a, maevsi.event e - WHERE public.ST_DWithin(a.location, e.location_geography, _max_distance * 1000); -END; $$; + SELECT + e.id AS event_id, + ST_Distance(a.location, e.location_geography) AS distance + FROM + account a, + maevsi.event e + WHERE + ST_DWithin(a.location, e.location_geography, _distance_max * 1000); +END; +$$; -ALTER FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) OWNER TO postgres; +ALTER FUNCTION maevsi.event_filter_radius_account(_account_id uuid, _distance_max double precision) OWNER TO postgres; -- --- Name: FUNCTION event_distances(_account_id uuid, _max_distance double precision); Type: COMMENT; Schema: maevsi; Owner: postgres +-- Name: FUNCTION event_filter_radius_account(_account_id uuid, _distance_max double precision); Type: COMMENT; Schema: maevsi; Owner: postgres -- -COMMENT ON FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) IS 'Returns event locations within a given radius around the location of an account.'; +COMMENT ON FUNCTION maevsi.event_filter_radius_account(_account_id uuid, _distance_max double precision) IS 'Returns event locations within a given radius around the location of an account.'; -- @@ -1094,6 +1146,42 @@ ALTER FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text) OWNER COMMENT ON FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text) IS 'Shows if an event exists.'; +-- +-- Name: event_location_coordinates(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres +-- + +CREATE FUNCTION maevsi.event_location_coordinates(_event_id uuid) RETURNS double precision[] + LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER + AS $$ +DECLARE + _latitude DOUBLE PRECISION; + _longitude DOUBLE PRECISION; +BEGIN + SELECT + ST_Y(location_geography::geometry), + ST_X(location_geography::geometry) + INTO + _latitude, + _longitude + FROM + maevsi.event + WHERE + id = _event_id; + + RETURN ARRAY[_latitude, _longitude]; +END; +$$; + + +ALTER FUNCTION maevsi.event_location_coordinates(_event_id uuid) OWNER TO postgres; + +-- +-- Name: FUNCTION event_location_coordinates(_event_id uuid); Type: COMMENT; Schema: maevsi; Owner: postgres +-- + +COMMENT ON FUNCTION maevsi.event_location_coordinates(_event_id uuid) IS 'Returns an array with latitude and longitude of the event''s current location data.'; + + -- -- Name: event_location_update(uuid, double precision, double precision); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -1102,12 +1190,13 @@ CREATE FUNCTION maevsi.event_location_update(_event_id uuid, _latitude double pr LANGUAGE plpgsql STRICT SECURITY DEFINER AS $$ BEGIN - -- SRID 4839: "ETRS89 / LCC Germany (N-E)", see https://www.crs-geo.eu/crs-pan-european.htm - -- SRID 4326: "WGS 84" (default SRID) - UPDATE maevsi.event SET - location_geography = public.ST_Point(_longitude, _latitude, 4326) - WHERE id = _event_id; -END; $$; + UPDATE maevsi.event + SET + location_geography = ST_Point(_longitude, _latitude, 4326) + WHERE + id = _event_id; +END; +$$; ALTER FUNCTION maevsi.event_location_update(_event_id uuid, _latitude double precision, _longitude double precision) OWNER TO postgres; @@ -1253,63 +1342,6 @@ ALTER FUNCTION maevsi.events_organized() OWNER TO postgres; COMMENT ON FUNCTION maevsi.events_organized() IS 'Add a function that returns all event ids for which the invoker is the author.'; --- --- Name: get_account_location_coordinates(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres --- - -CREATE FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) RETURNS double precision[] - LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER - AS $$ -DECLARE - _latitude DOUBLE PRECISION; - _longitude DOUBLE PRECISION; -BEGIN - SELECT public.ST_Y(location::public.geometry), public.ST_X(location::public.geometry) - INTO _latitude, _longitude - FROM maevsi_private.account - WHERE id = _account_id; - - RETURN ARRAY[_latitude, _longitude]; -END; $$; - - -ALTER FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) OWNER TO postgres; - --- --- Name: FUNCTION get_account_location_coordinates(_account_id uuid); Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) IS 'Returns an array with latitude and longitude of the account''s current location data'; - - --- --- Name: get_event_location_coordinates(uuid); Type: FUNCTION; Schema: maevsi; Owner: postgres --- - -CREATE FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) RETURNS double precision[] - LANGUAGE plpgsql STABLE STRICT SECURITY DEFINER - AS $$ -DECLARE - _latitude DOUBLE PRECISION; - _longitude DOUBLE PRECISION; -BEGIN - SELECT public.ST_Y(location_geography::public.geometry), public.ST_X(location_geography::public.geometry) - INTO _latitude, _longitude - FROM maevsi.event - WHERE id = _event_id; - RETURN ARRAY[_latitude, _longitude]; -END; $$; - - -ALTER FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) OWNER TO postgres; - --- --- Name: FUNCTION get_event_location_coordinates(_event_id uuid); Type: COMMENT; Schema: maevsi; Owner: postgres --- - -COMMENT ON FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) IS 'Returns an array with latitude and longitude of the event''s current location data.'; - - -- -- Name: invitation_claim_array(); Type: FUNCTION; Schema: maevsi; Owner: postgres -- @@ -4769,17 +4801,17 @@ COMMENT ON INDEX maevsi.idx_invitation_event_id IS 'Speeds up reverse foreign ke -- --- Name: idx_account_location; Type: INDEX; Schema: maevsi_private; Owner: postgres +-- Name: idx_account_private_location; Type: INDEX; Schema: maevsi_private; Owner: postgres -- -CREATE INDEX idx_account_location ON maevsi_private.account USING gist (location); +CREATE INDEX idx_account_private_location ON maevsi_private.account USING gist (location); -- --- Name: INDEX idx_account_location; Type: COMMENT; Schema: maevsi_private; Owner: postgres +-- Name: INDEX idx_account_private_location; Type: COMMENT; Schema: maevsi_private; Owner: postgres -- -COMMENT ON INDEX maevsi_private.idx_account_location IS 'Spatial index on column location in maevsi_private.account.'; +COMMENT ON INDEX maevsi_private.idx_account_private_location IS 'Spatial index on column location in maevsi_private.account.'; -- @@ -6047,20 +6079,28 @@ GRANT ALL ON FUNCTION maevsi.account_delete(password text) TO maevsi_account; -- --- Name: FUNCTION account_distances(_event_id uuid, _max_distance double precision); Type: ACL; Schema: maevsi; Owner: postgres +-- Name: FUNCTION account_email_address_verification(code uuid); Type: ACL; Schema: maevsi; Owner: postgres -- -REVOKE ALL ON FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) FROM PUBLIC; -GRANT ALL ON FUNCTION maevsi.account_distances(_event_id uuid, _max_distance double precision) TO maevsi_account; +REVOKE ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) TO maevsi_account; +GRANT ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) TO maevsi_anonymous; -- --- Name: FUNCTION account_email_address_verification(code uuid); Type: ACL; Schema: maevsi; Owner: postgres +-- Name: FUNCTION account_filter_radius_event(_event_id uuid, _distance_max double precision); Type: ACL; Schema: maevsi; Owner: postgres -- -REVOKE ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) FROM PUBLIC; -GRANT ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) TO maevsi_account; -GRANT ALL ON FUNCTION maevsi.account_email_address_verification(code uuid) TO maevsi_anonymous; +REVOKE ALL ON FUNCTION maevsi.account_filter_radius_event(_event_id uuid, _distance_max double precision) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.account_filter_radius_event(_event_id uuid, _distance_max double precision) TO maevsi_account; + + +-- +-- Name: FUNCTION account_location_coordinates(_account_id uuid); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.account_location_coordinates(_account_id uuid) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.account_location_coordinates(_account_id uuid) TO maevsi_account; -- @@ -6156,11 +6196,11 @@ GRANT ALL ON FUNCTION maevsi.event_delete(id uuid, password text) TO maevsi_acco -- --- Name: FUNCTION event_distances(_account_id uuid, _max_distance double precision); Type: ACL; Schema: maevsi; Owner: postgres +-- Name: FUNCTION event_filter_radius_account(_account_id uuid, _distance_max double precision); Type: ACL; Schema: maevsi; Owner: postgres -- -REVOKE ALL ON FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) FROM PUBLIC; -GRANT ALL ON FUNCTION maevsi.event_distances(_account_id uuid, _max_distance double precision) TO maevsi_account; +REVOKE ALL ON FUNCTION maevsi.event_filter_radius_account(_account_id uuid, _distance_max double precision) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.event_filter_radius_account(_account_id uuid, _distance_max double precision) TO maevsi_account; -- @@ -6181,6 +6221,14 @@ GRANT ALL ON FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text GRANT ALL ON FUNCTION maevsi.event_is_existing(author_account_id uuid, slug text) TO maevsi_anonymous; +-- +-- Name: FUNCTION event_location_coordinates(_event_id uuid); Type: ACL; Schema: maevsi; Owner: postgres +-- + +REVOKE ALL ON FUNCTION maevsi.event_location_coordinates(_event_id uuid) FROM PUBLIC; +GRANT ALL ON FUNCTION maevsi.event_location_coordinates(_event_id uuid) TO maevsi_account; + + -- -- Name: FUNCTION event_location_update(_event_id uuid, _latitude double precision, _longitude double precision); Type: ACL; Schema: maevsi; Owner: postgres -- @@ -6216,22 +6264,6 @@ GRANT ALL ON FUNCTION maevsi.events_organized() TO maevsi_account; GRANT ALL ON FUNCTION maevsi.events_organized() TO maevsi_anonymous; --- --- Name: FUNCTION get_account_location_coordinates(_account_id uuid); Type: ACL; Schema: maevsi; Owner: postgres --- - -REVOKE ALL ON FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) FROM PUBLIC; -GRANT ALL ON FUNCTION maevsi.get_account_location_coordinates(_account_id uuid) TO maevsi_account; - - --- --- Name: FUNCTION get_event_location_coordinates(_event_id uuid); Type: ACL; Schema: maevsi; Owner: postgres --- - -REVOKE ALL ON FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) FROM PUBLIC; -GRANT ALL ON FUNCTION maevsi.get_event_location_coordinates(_event_id uuid) TO maevsi_account; - - -- -- Name: FUNCTION invitation_claim_array(); Type: ACL; Schema: maevsi; Owner: postgres --