diff --git a/client/Makefile b/client/Makefile index 652539f..1e0696c 100644 --- a/client/Makefile +++ b/client/Makefile @@ -12,15 +12,27 @@ PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) +SOURCES_DATA_DIR = sql/$(EXTVERSION) -SOURCES_DATA = $(wildcard sql/$(EXTVERSION)/*.sql) +# The interface definition is used along with some templates to automatically generate code +RENDERER = ../sql-template-renderer +INTERFACE_FILE = ../interface.yaml +TEMPLATE_DIR = templates +TEMPLATE_FILES = $(wildcard $(TEMPLATE_DIR)/*.erb) +GENERATED_SQL_FILES = $(patsubst $(TEMPLATE_DIR)/%.erb, $(SOURCES_DATA_DIR)/%.sql, $(TEMPLATE_FILES)) + +$(GENERATED_SQL_FILES): $(SOURCES_DATA_DIR)/%.sql: $(TEMPLATE_DIR)/%.erb $(INTERFACE_FILE) $(RENDERER) + $(RENDERER) $(INTERFACE_FILE) $< > $@ + +SOURCES_DATA = $(wildcard $(SOURCES_DATA_DIR)/*.sql) $(GENERATED_SQL_FILES) $(DATA): $(SOURCES_DATA) rm -f $@ - cat $(SOURCES_DATA) >> $@ + cat $(SOURCES_DATA_DIR)/*.sql >> $@ all: $(DATA) # Only meant for development time, do not use once a version is released devclean: rm -f $(DATA) + rm -f $(GENERATED_SQL_FILES) diff --git a/client/expected/40_postalcodes_test.out b/client/expected/40_postalcodes_test.out index 7b3b824..a4477ec 100644 --- a/client/expected/40_postalcodes_test.out +++ b/client/expected/40_postalcodes_test.out @@ -24,7 +24,7 @@ PL/pgSQL function cdb_geocoder_client.geocode_postalcode_polygon(text,text) line (1 row) SELECT cdb_geocoder_client.geocode_postalcode_point('03204', 'Spain'); -NOTICE: cdb_geocoder_client._geocode_postalcode_point(4): [contrib_regression] REMOTE NOTICE: cdb_geocoder_server.geocode_postalcode_polygon invoked with params (postgres, some_transaction_id, 03204, Spain) +NOTICE: cdb_geocoder_client._geocode_postalcode_point(4): [contrib_regression] REMOTE NOTICE: cdb_geocoder_server.geocode_postalcode_point invoked with params (postgres, some_transaction_id, 03204, Spain) CONTEXT: SQL statement "SELECT cdb_geocoder_client._geocode_postalcode_point(session_user, txid_current(), postal_code, country_name)" PL/pgSQL function cdb_geocoder_client.geocode_postalcode_point(text,text) line 5 at SQL statement geocode_postalcode_point diff --git a/client/expected/90_permissions_test.out b/client/expected/90_permissions_test.out index 00124ec..14e78f8 100644 --- a/client/expected/90_permissions_test.out +++ b/client/expected/90_permissions_test.out @@ -66,7 +66,7 @@ PL/pgSQL function cdb_geocoder_client.geocode_postalcode_polygon(text,text) line (1 row) SELECT cdb_geocoder_client.geocode_postalcode_point('03204', 'Spain'); -NOTICE: cdb_geocoder_client._geocode_postalcode_point(4): [contrib_regression] REMOTE NOTICE: cdb_geocoder_server.geocode_postalcode_polygon invoked with params (postgres, some_transaction_id, 03204, Spain) +NOTICE: cdb_geocoder_client._geocode_postalcode_point(4): [contrib_regression] REMOTE NOTICE: cdb_geocoder_server.geocode_postalcode_point invoked with params (postgres, some_transaction_id, 03204, Spain) CONTEXT: SQL statement "SELECT cdb_geocoder_client._geocode_postalcode_point(session_user, txid_current(), postal_code, country_name)" PL/pgSQL function cdb_geocoder_client.geocode_postalcode_point(text,text) line 5 at SQL statement geocode_postalcode_point diff --git a/client/sql/0.0.1/.gitignore b/client/sql/0.0.1/.gitignore new file mode 100644 index 0000000..4cf1e95 --- /dev/null +++ b/client/sql/0.0.1/.gitignore @@ -0,0 +1,3 @@ +20_public_functions.sql +30_plproxy_functions.sql +90_grant_execute.sql diff --git a/client/sql/0.0.1/10_admin0.sql b/client/sql/0.0.1/10_admin0.sql deleted file mode 100644 index 6337496..0000000 --- a/client/sql/0.0.1/10_admin0.sql +++ /dev/null @@ -1,26 +0,0 @@ --- --- Public geocoder API function --- --- These are the only ones with permissions to publicuser role --- and should also be the only ones with SECURITY DEFINER - -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_admin0_polygon(country_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_admin0_polygon(session_user, txid_current(), country_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - - --- TODO: review all permissions stuff [I'd explicitly grant permissions to the public functions] - --------------------------------------------------------------------------------- - -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_admin0_polygon(user_id name, tx_id bigint, country_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_admin0_polygon(user_id, tx_id, country_name); -$$ LANGUAGE plproxy; diff --git a/client/sql/0.0.1/06_geocoder_server_conn.sql b/client/sql/0.0.1/10_geocoder_server_conn.sql similarity index 100% rename from client/sql/0.0.1/06_geocoder_server_conn.sql rename to client/sql/0.0.1/10_geocoder_server_conn.sql diff --git a/client/sql/0.0.1/20_admin1.sql b/client/sql/0.0.1/20_admin1.sql deleted file mode 100644 index f7d0088..0000000 --- a/client/sql/0.0.1/20_admin1.sql +++ /dev/null @@ -1,45 +0,0 @@ --- --- Public geocoder API function --- --- These are the only ones with permissions to publicuser role --- and should also be the only ones with SECURITY DEFINER - ----- geocode_admin1_polygon(admin1_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_admin1_polygon(admin1_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_admin1_polygon(session_user, txid_current(), admin1_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - ----- geocode_admin1_polygon(admin1_name text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_admin1_polygon(admin1_name text, country_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_admin1_polygon(session_user, txid_current(), admin1_name, country_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - --- TODO: review all permissions stuff [I'd explicitly grant permissions to the public functions] - --------------------------------------------------------------------------------- - ----- geocode_admin1_polygon(admin1_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_admin1_polygon(user_id name, tx_id bigint, admin1_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_admin1_polygon(user_id, tx_id, admin1_name); -$$ LANGUAGE plproxy; - ----- geocode_admin1_polygon(admin1_name text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_admin1_polygon(user_id name, tx_id bigint, admin1_name text, country_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_admin1_polygon(user_id, tx_id, admin1_name, country_name); -$$ LANGUAGE plproxy; diff --git a/client/sql/0.0.1/30_namedplaces.sql b/client/sql/0.0.1/30_namedplaces.sql deleted file mode 100644 index 8ac6a9d..0000000 --- a/client/sql/0.0.1/30_namedplaces.sql +++ /dev/null @@ -1,62 +0,0 @@ --- --- Public geocoder API function --- --- These are the only ones with permissions to publicuser role --- and should also be the only ones with SECURITY DEFINER - ----- geocode_namedplace_point(city_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_namedplace_point(city_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_namedplace_point(session_user, txid_current(), city_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - ----- geocode_namedplace_point(city_name text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_namedplace_point(city_name text, country_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_namedplace_point(session_user, txid_current(), city_name, country_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - ----- geocode_namedplace_point(city_name text, admin1_name text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_namedplace_point(city_name text, admin1_name text, country_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_namedplace_point(session_user, txid_current(), city_name, admin1_name, country_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; --- TODO: review all permissions stuff [I'd explicitly grant permissions to the public functions] - --------------------------------------------------------------------------------- - ----- geocode_namedplace_point(city_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_namedplace_point(user_id name, tx_id bigint, city_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_namedplace_point(user_id, tx_id, city_name); -$$ LANGUAGE plproxy; - ----- geocode_namedplace_point(city_name text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_namedplace_point(user_id name, tx_id bigint, city_name text, country_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_namedplace_point(user_id, tx_id, city_name, country_name); -$$ LANGUAGE plproxy; - ----- geocode_namedplace_point(city_name text, admin1_name text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_namedplace_point(user_id name, tx_id bigint, city_name text, admin1_name text, country_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_namedplace_point(user_id, tx_id, city_name, admin1_name, country_name); -$$ LANGUAGE plproxy; \ No newline at end of file diff --git a/client/sql/0.0.1/40_postalcodes.sql b/client/sql/0.0.1/40_postalcodes.sql deleted file mode 100644 index f3afcc8..0000000 --- a/client/sql/0.0.1/40_postalcodes.sql +++ /dev/null @@ -1,45 +0,0 @@ --- --- Public geocoder API function --- --- These are the only ones with permissions to publicuser role --- and should also be the only ones with SECURITY DEFINER - ----- geocode_postalcode_polygon(postal_code text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_postalcode_polygon(postal_code text, country_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_postalcode_polygon(session_user, txid_current(), postal_code, country_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - ----- geocode_postalcode_polygon(postal_code integer, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_postalcode_point(postal_code text, country_name text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_postalcode_point(session_user, txid_current(), postal_code, country_name) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - --- TODO: review all permissions stuff [I'd explicitly grant permissions to the public functions] - --------------------------------------------------------------------------------- - ----- geocode_postalcode_polygon(postal_code text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_postalcode_polygon(user_id name, tx_id bigint, postal_code text, country_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_postalcode_polygon(user_id, tx_id, postal_code, country_name); -$$ LANGUAGE plproxy; - ----- geocode_postalcode_polygon(postal_code text, country_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_postalcode_point(user_id name, tx_id bigint, postal_code text, country_name text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_postalcode_polygon(user_id, tx_id, postal_code, country_name); -$$ LANGUAGE plproxy; diff --git a/client/sql/0.0.1/50_ipaddresses.sql b/client/sql/0.0.1/50_ipaddresses.sql deleted file mode 100644 index 60602ab..0000000 --- a/client/sql/0.0.1/50_ipaddresses.sql +++ /dev/null @@ -1,27 +0,0 @@ --- --- Public geocoder API function --- --- These are the only ones with permissions to publicuser role --- and should also be the only ones with SECURITY DEFINER - ----- geocode_ipaddress_point(city_name text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client.geocode_ipaddress_point(ip_address text) -RETURNS Geometry AS $$ -DECLARE - ret Geometry; -BEGIN - SELECT cdb_geocoder_client._geocode_ipaddress_point(session_user, txid_current(), ip_address) INTO ret; - RETURN ret; -END; -$$ LANGUAGE 'plpgsql' SECURITY DEFINER; - --- TODO: review all permissions stuff [I'd explicitly grant permissions to the public functions] - --------------------------------------------------------------------------------- - ----- geocode_ipaddress_point(ip_address text) -CREATE OR REPLACE FUNCTION cdb_geocoder_client._geocode_ipaddress_point(user_id name, tx_id bigint, ip_address text) -RETURNS Geometry AS $$ - CONNECT cdb_geocoder_client._server_conn_str(); - SELECT cdb_geocoder_server.geocode_ipaddress_point(user_id, tx_id, ip_address); -$$ LANGUAGE plproxy; diff --git a/client/sql/0.0.1/80_permissions.sql b/client/sql/0.0.1/80_permissions.sql new file mode 100644 index 0000000..16fc021 --- /dev/null +++ b/client/sql/0.0.1/80_permissions.sql @@ -0,0 +1,9 @@ +-- Make sure by default there are no permissions for publicuser +-- NOTE: this happens at extension creation time, as part of an implicit transaction. +REVOKE ALL PRIVILEGES ON SCHEMA cdb_geocoder_client FROM PUBLIC, publicuser CASCADE; + +-- Grant permissions on the schema to publicuser (but just the schema) +GRANT USAGE ON SCHEMA cdb_geocoder_client TO publicuser; + +-- Revoke execute permissions on all functions in the schema by default +REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_geocoder_client FROM PUBLIC, publicuser; diff --git a/client/sql/0.0.1/90_permissions.sql b/client/sql/0.0.1/90_permissions.sql deleted file mode 100644 index 9fea793..0000000 --- a/client/sql/0.0.1/90_permissions.sql +++ /dev/null @@ -1,23 +0,0 @@ --- Make sure by default there are no permissions for publicuser --- NOTE: this happens at extension creation time, as part of an implicit transaction. -REVOKE ALL PRIVILEGES ON SCHEMA cdb_geocoder_client FROM PUBLIC, publicuser CASCADE; - --- Grant permissions on the schema to publicuser (but just the schema) -GRANT USAGE ON SCHEMA cdb_geocoder_client TO publicuser; - --- Revoke execute permissions on all functions in the schema by default -REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_geocoder_client FROM PUBLIC, publicuser; - --------------------------------------------------------------------------------- - --- Explicitly grant permissions to public functions --- NOTE: All public functions must be listed below, grating permissions to publicuser -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_admin0_polygon(country_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_admin1_polygon(admin1_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_admin1_polygon(admin1_name text, country_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_namedplace_point(city_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_namedplace_point(city_name text, country_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_namedplace_point(city_name text, admin1_name text, country_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_postalcode_polygon(postal_code text, country_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_postalcode_point(postal_code text, country_name text) TO publicuser; -GRANT EXECUTE ON FUNCTION cdb_geocoder_client.geocode_ipaddress_point(ip_address text) TO publicuser; diff --git a/client/templates/20_public_functions.erb b/client/templates/20_public_functions.erb new file mode 100644 index 0000000..483a3a8 --- /dev/null +++ b/client/templates/20_public_functions.erb @@ -0,0 +1,16 @@ +-- +-- Public geocoder API function +-- +-- These are the only ones with permissions to publicuser role +-- and should also be the only ones with SECURITY DEFINER + +CREATE OR REPLACE FUNCTION <%= GEOCODER_CLIENT_SCHEMA %>.<%= name %> (<%= params_with_type %>) +RETURNS <%= return_type %> AS $$ +DECLARE + ret <%= return_type %>; +BEGIN + SELECT <%= GEOCODER_CLIENT_SCHEMA %>._<%= name %>(session_user, txid_current(), <%= params %>) INTO ret; + RETURN ret; +END; +$$ LANGUAGE 'plpgsql' SECURITY DEFINER; + diff --git a/client/templates/30_plproxy_functions.erb b/client/templates/30_plproxy_functions.erb new file mode 100644 index 0000000..22d6ffc --- /dev/null +++ b/client/templates/30_plproxy_functions.erb @@ -0,0 +1,5 @@ +CREATE OR REPLACE FUNCTION <%= GEOCODER_CLIENT_SCHEMA %>._<%= name %> (user_id name, tx_id bigint, <%= params_with_type %>) +RETURNS Geometry AS $$ + CONNECT <%= GEOCODER_CLIENT_SCHEMA %>._server_conn_str(); + SELECT cdb_geocoder_server.<%= name %> (user_id, tx_id, <%= params %>); +$$ LANGUAGE plproxy; diff --git a/client/templates/90_grant_execute.erb b/client/templates/90_grant_execute.erb new file mode 100644 index 0000000..97415ca --- /dev/null +++ b/client/templates/90_grant_execute.erb @@ -0,0 +1 @@ +GRANT EXECUTE ON FUNCTION <%= GEOCODER_CLIENT_SCHEMA %>.<%= name %>(<%= params_with_type %>) TO publicuser; \ No newline at end of file diff --git a/interface.yaml b/interface.yaml new file mode 100644 index 0000000..7e525a0 --- /dev/null +++ b/interface.yaml @@ -0,0 +1,53 @@ +--- +- name: geocode_admin0_polygon + return_type: Geometry + params: + - { name: country_name, type: text } + +- name: geocode_admin1_polygon + return_type: Geometry + params: + - { name: admin1_name, type: text } + +- name: geocode_admin1_polygon + return_type: Geometry + params: + - { name: admin1_name, type: text } + - { name: country_name, type: text } + +- name: geocode_namedplace_point + return_type: Geometry + params: + - { name: city_name, type: text} + +- name: geocode_namedplace_point + return_type: Geometry + params: + - { name: city_name, type: text} + - { name: country_name, type: text} + +- name: geocode_namedplace_point + return_type: Geometry + params: + - { name: city_name, type: text} + - { name: admin1_name, type: text} + - { name: country_name, type: text} + + +- name: geocode_postalcode_polygon + return_type: Geometry + params: + - { name: postal_code, type: text} + - { name: country_name, type: text} + +- name: geocode_postalcode_point + return_type: Geometry + params: + - { name: postal_code, type: text} + - { name: country_name, type: text} + +- name: geocode_ipaddress_point + return_type: Geometry + params: + - { name: ip_address, type: text} + diff --git a/sql-template-renderer b/sql-template-renderer new file mode 100755 index 0000000..526bd89 --- /dev/null +++ b/sql-template-renderer @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby + +# A script to automatically generate SQL files from an interface definition. +# To be called like this: sql-template-renderer interface.csv templates/sql-template.erb + +require 'yaml' +require 'erb' + +class SqlTemplateRenderer + + GEOCODER_CLIENT_SCHEMA = 'cdb_geocoder_client' + + def initialize(template_file, function_signature) + @f = function_signature + @template = File.read(template_file) + end + + def render + ERB.new(@template).result(binding) + end + + def name + @f['name'] + end + + def return_type + @f['return_type'] + end + + def params + @f['params'].map { |p| p['name'] }.join(', ') + end + + def params_with_type + @f['params'].map { |p| "#{p['name']} #{p['type']}"}.join(', ') + end + +end + + +if ARGV.length != 2 then + puts "Usage: sql-template-renderer " + exit +end + +interface_source_file = ARGV[0] +template_file = ARGV[1] + + +functions = YAML.load(File.open(interface_source_file)) + +functions.each do |f| + puts SqlTemplateRenderer.new(template_file, f).render +end