Versioning

Fork/mirror of https://gitlab.com/depesz/Versioning
git clone https://ccx.te2000.cz/git/Versioning
Log | Files | Refs | README | LICENSE

commit 2e936d77580bfcb0a4d26b75c5a5e2636dc96cc5
parent 829ee00e6efbd34faf34ce1901b822de38ad75c9
Author: Hubert depesz Lubaczewski <depesz@depesz.com>
Date:   Tue, 18 Feb 2020 12:53:54 +0100

Add try_register_patch function

Fixes https://gitlab.com/depesz/Versioning/issues/2 - reported by Zach
Levow.

Diffstat:
MREADME.md | 22++++++++--------------
Minstall.versioning.sql | 24++++++++++++++++++++++++
At/03-try.sql | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 84 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md @@ -63,24 +63,18 @@ This will make sure that patch 001-users can only be applied after # AVAILABLE FUNCTIONS -## \_v.register_patch( TEXT ) - -Registers named patch, or dies if it is already registered. - -Returns integer which is id of patch in \_v.patches table - only if it -succeeded. - -## \_v.register_patch( TEXT, TEXT[] ) +## \_v.register_patch( TEXT, TEXT[], TEXT[] ) -Same as \_v.register_patch( TEXT ), but checks is all given patches (given as -array in second argument) are already registered. +Registers named patch (first argument), checking if all required patches (2nd +argument) are installed, and that no conflicting patches (3rd argument) are +installed. -## \_v.register_patch( TEXT, TEXT[], TEXT[] ) +2nd and 3rd arguments default to NULL/empty array. -Same as \_v.register_patch( TEXT, TEXT[] ), but also checks if there are no conflicts with preexisting patches. +## \_v.try_register_patch( TEXT, TEXT[], TEXT[] ) -Third argument is array of names of patches that conflict with current one. So -if any of them is installed - register_patch will error out. +Works just like \_v.register_patch(), but instead of raising exception it +returns true if it worked, and false if it didn't. ## \_v.unregister_patch( TEXT ) diff --git a/install.versioning.sql b/install.versioning.sql @@ -9,6 +9,7 @@ BEGIN; CREATE SCHEMA IF NOT EXISTS _v; COMMENT ON SCHEMA _v IS 'Schema for versioning data and functionality.'; + CREATE TABLE IF NOT EXISTS _v.patches ( patch_name TEXT PRIMARY KEY, applied_tsz TIMESTAMPTZ NOT NULL DEFAULT now(), @@ -23,6 +24,7 @@ COMMENT ON COLUMN _v.patches.applied_by IS 'Who applied this patch (PostgreSQL COMMENT ON COLUMN _v.patches.requires IS 'List of patches that are required for given patch.'; COMMENT ON COLUMN _v.patches.conflicts IS 'List of patches that conflict with given patch.'; + CREATE OR REPLACE FUNCTION _v.register_patch( IN in_patch_name TEXT, IN in_requirements TEXT[] DEFAULT NULL, in_conflicts TEXT[] DEFAULT NULL, OUT versioning INT4 ) RETURNS setof INT4 AS $$ DECLARE t_text TEXT; @@ -61,6 +63,23 @@ END; $$ language plpgsql; COMMENT ON FUNCTION _v.register_patch( TEXT, TEXT[], TEXT[] ) IS 'Function to register patches in database. Raises exception if there are conflicts, prerequisites are not installed or the migration has already been installed.'; + +CREATE OR REPLACE FUNCTION _v.try_register_patch( IN in_patch_name TEXT, IN in_requirements TEXT[] DEFAULT NULL, in_conflicts TEXT[] DEFAULT NULL ) RETURNS BOOL AS $$ +declare + temptext TEXT; +BEGIN + PERFORM _v.register_patch( in_patch_name, in_requirements, in_conflicts ); + RETURN true; +EXCEPTION + WHEN raise_exception THEN + GET STACKED DIAGNOSTICS temptext := MESSAGE_TEXT; + RAISE WARNING '%', temptext; + RETURN false; +END; +$$ language plpgsql; +COMMENT ON FUNCTION _v.try_register_patch( TEXT, TEXT[], TEXT[] ) IS 'Function to register patches in database, returns true if it worked, false otherwise.'; + + CREATE OR REPLACE FUNCTION _v.unregister_patch( IN in_patch_name TEXT, OUT versioning INT4 ) RETURNS setof INT4 AS $$ DECLARE i INT4; @@ -85,6 +104,7 @@ END; $$ language plpgsql; COMMENT ON FUNCTION _v.unregister_patch( TEXT ) IS 'Function to unregister patches in database. Dies if the patch is not registered, or if unregistering it would break dependencies.'; + CREATE OR REPLACE FUNCTION _v.assert_patch_is_applied( IN in_patch_name TEXT ) RETURNS TEXT as $$ DECLARE t_text TEXT; @@ -98,6 +118,7 @@ END; $$ language plpgsql; COMMENT ON FUNCTION _v.assert_patch_is_applied( TEXT ) IS 'Function that can be used to make sure that patch has been applied.'; + CREATE OR REPLACE FUNCTION _v.assert_user_is_superuser() RETURNS TEXT as $$ DECLARE v_super bool; @@ -111,6 +132,7 @@ END; $$ language plpgsql; COMMENT ON FUNCTION _v.assert_user_is_superuser() IS 'Function that can be used to make sure that patch is being applied using superuser account.'; + CREATE OR REPLACE FUNCTION _v.assert_user_is_not_superuser() RETURNS TEXT as $$ DECLARE v_super bool; @@ -124,6 +146,7 @@ END; $$ language plpgsql; COMMENT ON FUNCTION _v.assert_user_is_not_superuser() IS 'Function that can be used to make sure that patch is being applied using normal (not superuser) account.'; + CREATE OR REPLACE FUNCTION _v.assert_user_is_one_of(VARIADIC p_acceptable_users TEXT[] ) RETURNS TEXT as $$ DECLARE BEGIN @@ -135,4 +158,5 @@ END; $$ language plpgsql; COMMENT ON FUNCTION _v.assert_user_is_one_of(TEXT[]) IS 'Function that can be used to make sure that patch is being applied by one of defined users.'; + COMMIT; diff --git a/t/03-try.sql b/t/03-try.sql @@ -0,0 +1,52 @@ +BEGIN; + -- Disable printing warning messages + SET client_min_messages = ERROR; + + -- load pgtap - change next line to point to correct path for your system! + \i t/00-load.sql.inc + + + SELECT plan(7); + + SELECT is( ( SELECT count(*) FROM _v.patches ), 0::bigint, 'When running tests _v.patches table should be empty to prevent bad interactions between patches and tests.' ); + + SELECT is( + _v.try_register_patch( 'first_patch' ), + true, + 'Installation of patch without dependencies and conflicts.' + ); + + SELECT is( + _v.try_register_patch( 'first_patch' ), + false, + 'Reinstallation of patch without dependencies and conflicts.' + ); + + SELECT is( + _v.try_register_patch( 'second_patch', ARRAY['first_patch'] ), + true, + 'Installation of patch with correct dependency.' + ); + + SELECT is( + _v.try_register_patch( 'third_patch', ARRAY['bad_patch'] ), + false, + 'Installation of patch with bad dependency.' + ); + + SELECT is( + _v.try_register_patch( 'fourth_patch', NULL, ARRAY['bad_patch'] ), + true, + 'Installation of patch with and correct conflict.' + ); + + SELECT is( + _v.try_register_patch( 'fifth_patch', NULL, ARRAY['first_patch'] ), + false, + 'Installation of patch with bad conflict.' + ); + + SELECT * FROM finish(); + +ROLLBACK; +