/* * * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0 */ \pset pager off CREATE EXTENSION pg_tle; -- create semi-privileged role to manipulate pg_tle artifacts CREATE ROLE dbadmin; GRANT pgtle_admin TO dbadmin; -- create unprivileged role to create trusted extensions CREATE ROLE dbstaff; -- create alt unprivileged role to create trusted extensions CREATE ROLE dbstaff2; -- create completely unprivileged role CREATE ROLE dbguest; GRANT CREATE, USAGE ON SCHEMA PUBLIC TO pgtle_admin; GRANT CREATE, USAGE ON SCHEMA PUBLIC TO dbstaff; DO $$ DECLARE objname text; sql text; BEGIN SELECT current_database() INTO objname; EXECUTE format('GRANT CREATE ON DATABASE %I TO dbstaff;', objname); END; $$ LANGUAGE plpgsql; -- create function that can be executed by superuser only CREATE OR REPLACE FUNCTION superuser_only() RETURNS INT AS $$ ( SELECT 51 ) $$ LANGUAGE sql; REVOKE EXECUTE ON FUNCTION superuser_only() FROM PUBLIC; SET search_path TO pgtle,public; -- installation of artifacts requires semi-privileged role SET SESSION AUTHORIZATION dbadmin; SELECT CURRENT_USER; SELECT pgtle.install_extension ( 'test123', '1.0', 'Test TLE Functions', $_pgtle_$ CREATE OR REPLACE FUNCTION test123_func() RETURNS INT AS $$ ( SELECT 42 )$$ LANGUAGE sql; $_pgtle_$ ); -- install a trusted extension that calls functions requiring superuser privilege SELECT pgtle.install_extension ( 'test_no_switch_to_superuser_when_trusted', '1.0', 'Test TLE Functions', $_pgtle_$ SELECT superuser_only(); $_pgtle_$ ); SET search_path TO public; -- superuser can create and use extensions that do not require superuser privilege RESET SESSION AUTHORIZATION; CREATE EXTENSION test123; SELECT test123_func(); DROP EXTENSION test123; SET SESSION AUTHORIZATION dbstaff; SELECT CURRENT_USER; -- unprivileged role can create and use extensions that do not require superuser privilege CREATE EXTENSION test123; SELECT test123_func(); -- unprivileged role can not create a trusted extension that requires superuser privilege -- fails CREATE EXTENSION test_no_switch_to_superuser_when_trusted; -- switch to dbstaff2 SET SESSION AUTHORIZATION dbstaff2; SELECT CURRENT_USER; -- fails DROP EXTENSION test123; -- suceeds SELECT test123_func(); -- fails DROP FUNCTION test123_func(); -- switch to dbguest SET SESSION AUTHORIZATION dbguest; SELECT CURRENT_USER; -- fails DROP EXTENSION test123; -- suceeds SELECT test123_func(); -- fails DROP FUNCTION test123_func(); SET search_path TO pgtle, public; -- installation of artifacts requires semi-privileged role SET SESSION AUTHORIZATION dbadmin; SELECT CURRENT_USER; SELECT pgtle.install_update_path ( 'test123', '1.0', '1.1', $_pgtle_$ CREATE OR REPLACE FUNCTION test123_func_2() RETURNS INT AS $$ ( SELECT 424242 )$$ LANGUAGE sql; $_pgtle_$ ); SET search_path TO public; -- unprivileged role can modify and use trusted extension SET SESSION AUTHORIZATION dbstaff; SELECT CURRENT_USER; ALTER EXTENSION test123 UPDATE TO '1.1'; SELECT test123_func_2(); SELECT * FROM pgtle.extension_update_paths('test123'); SELECT * FROM pgtle.available_extensions() ORDER BY name; SELECT * FROM pgtle.available_extension_versions() ORDER BY name; DROP EXTENSION test123; -- negative tests, run as unprivileged role -- should fail -- attempt to create a function in pgtle directly CREATE OR REPLACE FUNCTION pgtle.foo() RETURNS TEXT AS $$ SELECT 'ok' $$ LANGUAGE sql; -- create a function in public and then attempt alter to pg_tle -- this works CREATE OR REPLACE FUNCTION public.pg_tlefoo() RETURNS TEXT AS $$ SELECT 'ok' $$ LANGUAGE sql; -- but this should fail ALTER FUNCTION public.pg_tlefoo() SET SCHEMA pgtle; -- clean up, should work DROP FUNCTION public.pg_tlefoo(); -- negative tests, run as superuser RESET SESSION AUTHORIZATION; -- attempt to shadow existing file-based extension -- fail SELECT pgtle.install_extension ( 'plpgsql', '1.0', 'Test TLE Functions', $_pgtle_$ CREATE OR REPLACE FUNCTION test123_func() RETURNS INT AS $$ ( SELECT 42 )$$ LANGUAGE sql; $_pgtle_$ ); -- attempt to alter a pg_tle extension function -- fail ALTER FUNCTION pgtle.install_extension ( name text, version text, description text, ext text, requires text[] ) SET search_path TO 'public'; -- test uninstall extensions by a specific version SELECT pgtle.install_extension ( 'new_ext', '1.0', 'Test TLE Functions', $_pgtle_$ CREATE FUNCTION fun() RETURNS INT AS $$ SELECT 1; $$ LANGUAGE SQL; $_pgtle_$ ); SELECT pgtle.install_update_path ( 'new_ext', '1.0', '1.1', $_pgtle_$ CREATE OR REPLACE FUNCTION fun() RETURNS INT AS $$ SELECT 2; $$ LANGUAGE SQL; $_pgtle_$ ); SELECT pgtle.install_update_path ( 'new_ext', '1.1', '1.0', $_pgtle_$ CREATE OR REPLACE FUNCTION fun() RETURNS INT AS $$ SELECT 1; $$ LANGUAGE SQL; $_pgtle_$ ); -- check available extension versions -- should be 1.0 and 1.1 SELECT * FROM pgtle.available_extension_versions() x WHERE x.name = 'new_ext'; -- check avaialble version update paths -- should be 1.0<=>1.1 SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; SELECT pgtle.uninstall_extension('new_ext', '1.1'); -- check avaialble versions, should only be 1.0 SELECT * FROM pgtle.available_extension_versions() x WHERE x.name = 'new_ext'; -- check avaialble version update paths -- should be none SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; -- add the update back in SELECT pgtle.install_update_path ( 'new_ext', '1.0', '1.1', $_pgtle_$ CREATE OR REPLACE FUNCTION fun() RETURNS INT AS $$ SELECT 2; $$ LANGUAGE SQL; $_pgtle_$ ); -- check avaialble version update paths -- should be 1.0=>1.1 SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; -- try to add a duplicate update path -- fail SELECT pgtle.install_update_path ( 'new_ext', '1.0', '1.1', $_pgtle_$ CREATE OR REPLACE FUNCTION fun() RETURNS INT AS $$ SELECT 2; $$ LANGUAGE SQL; $_pgtle_$ ); -- add a downgrade path SELECT pgtle.install_update_path ( 'new_ext', '1.1', '1.0', $_pgtle_$ CREATE OR REPLACE FUNCTION fun() RETURNS INT AS $$ SELECT 1; $$ LANGUAGE SQL; $_pgtle_$ ); -- check avaiable update paths SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; -- only uninstall the downgrade path SELECT pgtle.uninstall_update_path('new_ext', '1.1', '1.0'); -- check avaiable update paths SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; -- try uninstalling again -- fail SELECT pgtle.uninstall_update_path('new_ext', '1.1', '1.0'); -- try ininstall with if exists, see false SELECT pgtle.uninstall_update_path_if_exists('new_ext', '1.1', '1.0'); -- check avaiable update paths SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; -- install again and uninstall with "if exists", see true SELECT pgtle.install_update_path ( 'new_ext', '1.1', '1.0', $_pgtle_$ CREATE OR REPLACE FUNCTION fun() RETURNS INT AS $$ SELECT 1; $$ LANGUAGE SQL; $_pgtle_$ ); SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; SELECT pgtle.uninstall_update_path_if_exists('new_ext', '1.1', '1.0'); -- check avaiable update paths SELECT * FROM pgtle.extension_update_paths('new_ext') x ORDER BY x.source; -- ok, install it again SELECT pgtle.install_update_path ( 'new_ext', '1.1', '1.0', $_pgtle_$ CREATE OR REPLACE FUNCTION fun() RETURNS INT AS $$ SELECT 1; $$ LANGUAGE SQL; $_pgtle_$ ); -- test the default version, should be 1.0 SELECT * FROM pgtle.available_extensions() x WHERE x.name = 'new_ext'; -- set the new default SELECT pgtle.set_default_version('new_ext', '1.1'); -- test the default version, should be 1.1 SELECT * FROM pgtle.available_extensions() x WHERE x.name = 'new_ext'; -- try setting a default version that does not exist -- fail SELECT pgtle.set_default_version('new_ext', '1.2'); -- try setting a default version on an extension that does not exist -- fail SELECT pgtle.set_default_version('bogus', '1.2'); -- uninstall SELECT pgtle.uninstall_extension('new_ext'); -- OK let's try to install an extension with a control file that has errors SELECT pgtle.install_extension ( 'broken_ext', '0.1', $$Distance functions for two points' directory = '/tmp/$$, $_pg_tle_$ CREATE FUNCTION dist(x1 numeric, y1 numeric, x2 numeric, y2 numeric, l numeric) RETURNS numeric AS $$ SELECT ((x2 ^ l - x1 ^ l) ^ (1 / l)) + ((y2 ^ l - y1 ^ l) ^ (1 / l)); $$ LANGUAGE SQL; $_pg_tle_$ ); -- this should lead to a sytnax error that we catch -- fail SELECT * FROM pgtle.available_extensions(); -- shoo shoo and uninstall SELECT pgtle.uninstall_extension('broken_ext'); -- uninstall with a non-existent extension -- error SELECT pgtle.uninstall_extension('bogus'); -- uninstall_if_exists with a non-existent extension -- returns false, no error SELECT pgtle.uninstall_extension_if_exists('bogus'); -- uninstall_if_exists with an extension that exists SELECT pgtle.install_extension ( 'test42', '1.0', 'Test TLE Functions', $_pgtle_$ CREATE OR REPLACE FUNCTION test42_func() RETURNS INT AS $$ ( SELECT 42 )$$ LANGUAGE sql; $_pgtle_$ ); SELECT pgtle.uninstall_extension_if_exists('test42'); -- back to our regular program: these should work -- removal of artifacts requires semi-privileged role SET SESSION AUTHORIZATION dbadmin; SELECT CURRENT_USER; SELECT pgtle.uninstall_extension('test123'); SELECT pgtle.uninstall_extension('test_no_switch_to_superuser_when_trusted'); RESET SESSION AUTHORIZATION; SELECT pgtle.install_extension ( 'test42', '1.0', 'Test TLE Functions', $_pgtle_$ CREATE OR REPLACE FUNCTION test42_func() RETURNS INT AS $$ ( SELECT 42 )$$ LANGUAGE sql; $_pgtle_$ ); SELECT pgtle.available_extension_versions(); SELECT pgtle.install_extension_version_sql ( 'test42', '2.0', $_pgtle_$ CREATE OR REPLACE FUNCTION test42_func() RETURNS INT AS $$ ( SELECT 4242 )$$ LANGUAGE sql; $_pgtle_$ ); SELECT pgtle.available_extension_versions(); -- install an already installed version -- fails SELECT pgtle.install_extension_version_sql ( 'test42', '2.0', $_pgtle_$ CREATE OR REPLACE FUNCTION test42_func() RETURNS INT AS $$ ( SELECT 4242 )$$ LANGUAGE sql; $_pgtle_$ ); SELECT pgtle.available_extension_versions(); -- uninstall default version -- fails SELECT pgtle.uninstall_extension('test42', '1.0'); SELECT pgtle.available_extension_versions(); -- uninstall a version that is not the default version -- succeeds SELECT pgtle.uninstall_extension('test42', '2.0'); SELECT pgtle.available_extension_versions(); -- uninstall non-existent version -- fails SELECT pgtle.uninstall_extension('test42', '3.0'); SELECT pgtle.available_extension_versions(); -- uninstall the only installed and default version SELECT pgtle.uninstall_extension('test42', '1.0'); SELECT pgtle.available_extension_versions(); SELECT pgtle.install_extension ( 'test42', '1.0', 'Test TLE Functions', $_pgtle_$ CREATE OR REPLACE FUNCTION test42_func() RETURNS INT AS $$ ( SELECT 42 )$$ LANGUAGE sql; $_pgtle_$ ); SELECT pgtle.install_extension_version_sql ( 'test42', '2.0', $_pgtle_$ CREATE OR REPLACE FUNCTION test42_func() RETURNS INT AS $$ ( SELECT 4242 )$$ LANGUAGE sql; $_pgtle_$ ); SELECT pgtle.available_extension_versions(); -- uninstall extension with multiple versions SELECT pgtle.uninstall_extension('test42'); SELECT pgtle.available_extension_versions(); -- Skip TransactionStmts BEGIN; SELECT pgtle.available_extension_versions(); SELECT 1/0; SELECT pgtle.available_extension_versions(); ROLLBACK; -- clean up RESET SESSION AUTHORIZATION; DROP FUNCTION superuser_only(); REVOKE CREATE, USAGE ON SCHEMA PUBLIC FROM dbstaff; REVOKE CREATE, USAGE ON SCHEMA PUBLIC FROM pgtle_admin; DROP ROLE dbadmin; DO $$ DECLARE objname text; sql text; BEGIN SELECT current_database() INTO objname; EXECUTE format('REVOKE ALL ON DATABASE %I FROM dbstaff;', objname); END; $$ LANGUAGE plpgsql; DROP ROLE dbstaff; DROP ROLE dbstaff2; DROP ROLE dbguest; DROP EXTENSION pg_tle; DROP SCHEMA pgtle; DROP ROLE pgtle_admin;