Thread

  1. [PATCH v1] Allow wildcard (%) in extension upgrade paths

    Sandro Santilli <strk@kbt.io> — 2022-09-14T09:10:10Z

    A wildcard character "%" will be accepted in the
    "source" side of the upgrade script and be considered
    usable to upgrade any version to the "target" side.
    
    Includes regression test and documentation.
    ---
     doc/src/sgml/extend.sgml                      |  8 ++++
     src/backend/commands/extension.c              | 42 ++++++++++++++++---
     src/test/modules/test_extensions/Makefile     |  6 ++-
     .../expected/test_extensions.out              | 15 +++++++
     src/test/modules/test_extensions/meson.build  |  3 ++
     .../test_extensions/sql/test_extensions.sql   |  7 ++++
     .../test_ext_wildcard1--%--2.0.sql            |  6 +++
     .../test_ext_wildcard1--1.0.sql               |  6 +++
     .../test_ext_wildcard1.control                |  3 ++
     9 files changed, 88 insertions(+), 8 deletions(-)
     create mode 100644 src/test/modules/test_extensions/test_ext_wildcard1--%--2.0.sql
     create mode 100644 src/test/modules/test_extensions/test_ext_wildcard1--1.0.sql
     create mode 100644 src/test/modules/test_extensions/test_ext_wildcard1.control
    
    diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
    index b70cbe83ae..f1f0ae1244 100644
    --- a/doc/src/sgml/extend.sgml
    +++ b/doc/src/sgml/extend.sgml
    @@ -1081,6 +1081,14 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr
          <literal>1.1</literal>).
         </para>
     
    +    <para>
    +     The literal value <literal>%</literal> can be used as the
    +     <replaceable>old_version</replaceable> component in an extension
    +     update script for it to match any version. Such wildcard update
    +     scripts will only be used when no explicit path is found from
    +     old to target version.
    +    </para>
    +
         <para>
          Given that a suitable update script is available, the command
          <command>ALTER EXTENSION UPDATE</command> will update an installed extension
    diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
    index 02ff4a9a7f..6df0fd403a 100644
    --- a/src/backend/commands/extension.c
    +++ b/src/backend/commands/extension.c
    @@ -130,6 +130,7 @@ static void ApplyExtensionUpdates(Oid extensionOid,
     								  bool cascade,
     								  bool is_create);
     static char *read_whole_file(const char *filename, int *length);
    +static bool file_exists(const char *name);
     
     
     /*
    @@ -893,7 +894,14 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
     	if (from_version == NULL)
     		elog(DEBUG1, "executing extension script for \"%s\" version '%s'", control->name, version);
     	else
    +	{
    +		if ( ! file_exists(filename) )
    +		{
    +			/* if filename does not exist, try wildcard */
    +			filename = get_extension_script_filename(control, "%", version);
    +		}
     		elog(DEBUG1, "executing extension script for \"%s\" update from version '%s' to '%s'", control->name, from_version, version);
    +	}
     
     	/*
     	 * If installing a trusted extension on behalf of a non-superuser, become
    @@ -1217,14 +1225,19 @@ identify_update_path(ExtensionControlFile *control,
     
     	/* Find shortest path */
     	result = find_update_path(evi_list, evi_start, evi_target, false, false);
    +	if (result != NIL)
    +		return result;
     
    -	if (result == NIL)
    -		ereport(ERROR,
    -				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    -				 errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
    -						control->name, oldVersion, newVersion)));
    +	/* Find wildcard path, if no explicit path was found */
    +	evi_start = get_ext_ver_info("%", &evi_list);
    +	result = find_update_path(evi_list, evi_start, evi_target, false, false);
    +	if (result != NIL)
    +		return result;
     
    -	return result;
    +	ereport(ERROR,
    +			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    +			 errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"",
    +					control->name, oldVersion, newVersion)));
     }
     
     /*
    @@ -3395,3 +3408,20 @@ read_whole_file(const char *filename, int *length)
     	buf[*length] = '\0';
     	return buf;
     }
    +
    +static bool
    +file_exists(const char *name)
    +{
    +	struct stat st;
    +
    +	Assert(name != NULL);
    +
    +	if (stat(name, &st) == 0)
    +		return !S_ISDIR(st.st_mode);
    +	else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
    +		ereport(ERROR,
    +				(errcode_for_file_access(),
    +				 errmsg("could not access file \"%s\": %m", name)));
    +
    +	return false;
    +}
    diff --git a/src/test/modules/test_extensions/Makefile b/src/test/modules/test_extensions/Makefile
    index c3139ab0fc..4fe2d82b6e 100644
    --- a/src/test/modules/test_extensions/Makefile
    +++ b/src/test/modules/test_extensions/Makefile
    @@ -6,14 +6,16 @@ PGFILEDESC = "test_extensions - regression testing for EXTENSION support"
     EXTENSION = test_ext1 test_ext2 test_ext3 test_ext4 test_ext5 test_ext6 \
                 test_ext7 test_ext8 test_ext_cine test_ext_cor \
                 test_ext_cyclic1 test_ext_cyclic2 \
    -            test_ext_evttrig
    +            test_ext_evttrig test_ext_wildcard1
     DATA = test_ext1--1.0.sql test_ext2--1.0.sql test_ext3--1.0.sql \
            test_ext4--1.0.sql test_ext5--1.0.sql test_ext6--1.0.sql \
            test_ext7--1.0.sql test_ext7--1.0--2.0.sql test_ext8--1.0.sql \
            test_ext_cine--1.0.sql test_ext_cine--1.0--1.1.sql \
            test_ext_cor--1.0.sql \
            test_ext_cyclic1--1.0.sql test_ext_cyclic2--1.0.sql \
    -       test_ext_evttrig--1.0.sql test_ext_evttrig--1.0--2.0.sql
    +       test_ext_evttrig--1.0.sql test_ext_evttrig--1.0--2.0.sql \
    +       test_ext_wildcard1--1.0.sql test_ext_wildcard1--%--2.0.sql \
    +
     
     REGRESS = test_extensions test_extdepend
     
    diff --git a/src/test/modules/test_extensions/expected/test_extensions.out b/src/test/modules/test_extensions/expected/test_extensions.out
    index 821fed38d1..1c4dc5be42 100644
    --- a/src/test/modules/test_extensions/expected/test_extensions.out
    +++ b/src/test/modules/test_extensions/expected/test_extensions.out
    @@ -312,3 +312,18 @@ Objects in extension "test_ext_cine"
      table ext_cine_tab3
     (9 rows)
     
    +CREATE EXTENSION test_ext_wildcard1;
    +SELECT ext_wildcard1_version();
    + ext_wildcard1_version 
    +-----------------------
    + 1.0
    +(1 row)
    +
    +ALTER EXTENSION test_ext_wildcard1 UPDATE TO '2.0';
    +SELECT ext_wildcard1_version();
    + ext_wildcard1_version 
    +-----------------------
    + 2.0
    +(1 row)
    +
    +DROP EXTENSION test_ext_wildcard1;
    diff --git a/src/test/modules/test_extensions/meson.build b/src/test/modules/test_extensions/meson.build
    index c3af3e1721..026be6a879 100644
    --- a/src/test/modules/test_extensions/meson.build
    +++ b/src/test/modules/test_extensions/meson.build
    @@ -30,6 +30,9 @@ test_install_data += files(
       'test_ext_evttrig--1.0--2.0.sql',
       'test_ext_evttrig--1.0.sql',
       'test_ext_evttrig.control',
    +  'test_ext_wildcard1--1.0.sql',
    +  'test_ext_wildcard1--%--2.0.sql',
    +  'test_ext_wildcard1.control',
     )
     
     tests += {
    diff --git a/src/test/modules/test_extensions/sql/test_extensions.sql b/src/test/modules/test_extensions/sql/test_extensions.sql
    index 41b6cddf0b..071845e8df 100644
    --- a/src/test/modules/test_extensions/sql/test_extensions.sql
    +++ b/src/test/modules/test_extensions/sql/test_extensions.sql
    @@ -209,3 +209,10 @@ CREATE EXTENSION test_ext_cine;
     ALTER EXTENSION test_ext_cine UPDATE TO '1.1';
     
     \dx+ test_ext_cine
    +
    +
    +CREATE EXTENSION test_ext_wildcard1;
    +SELECT ext_wildcard1_version();
    +ALTER EXTENSION test_ext_wildcard1 UPDATE TO '2.0';
    +SELECT ext_wildcard1_version();
    +DROP EXTENSION test_ext_wildcard1;
    diff --git a/src/test/modules/test_extensions/test_ext_wildcard1--%--2.0.sql b/src/test/modules/test_extensions/test_ext_wildcard1--%--2.0.sql
    new file mode 100644
    index 0000000000..75154e5c55
    --- /dev/null
    +++ b/src/test/modules/test_extensions/test_ext_wildcard1--%--2.0.sql
    @@ -0,0 +1,6 @@
    +/* src/test/modules/test_extensions/test_ext_wildcard1--%--2.0.sql */
    +-- complain if script is sourced in psql, rather than via ALTER EXTENSION
    +\echo Use "ALTER EXTENSION test_ext_wildcard1 UPDATE TO '2.0'" to load this file. \quit
    +
    +CREATE OR REPLACE FUNCTION ext_wildcard1_version() returns TEXT
    +AS 'SELECT 2.0' LANGUAGE 'sql';
    diff --git a/src/test/modules/test_extensions/test_ext_wildcard1--1.0.sql b/src/test/modules/test_extensions/test_ext_wildcard1--1.0.sql
    new file mode 100644
    index 0000000000..a69e791fda
    --- /dev/null
    +++ b/src/test/modules/test_extensions/test_ext_wildcard1--1.0.sql
    @@ -0,0 +1,6 @@
    +/* src/test/modules/test_extensions/test_ext_wildcard1--1.0.sql */
    +-- complain if script is sourced in psql, rather than via ALTER EXTENSION
    +\echo Use "CREATE EXTENSION test_ext_wildcard1" to load this file. \quit
    +
    +CREATE FUNCTION ext_wildcard1_version() returns TEXT
    +AS 'SELECT 1.0' LANGUAGE 'sql';
    diff --git a/src/test/modules/test_extensions/test_ext_wildcard1.control b/src/test/modules/test_extensions/test_ext_wildcard1.control
    new file mode 100644
    index 0000000000..0c2fc6fca6
    --- /dev/null
    +++ b/src/test/modules/test_extensions/test_ext_wildcard1.control
    @@ -0,0 +1,3 @@
    +comment = 'Test extension wildcard 1'
    +default_version = '1.0'
    +relocatable = true
    -- 
    2.34.1
    
    
    --bhtumk242qxsvzi5--