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:
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;
+