Only one version can be installed when using extension_control_path

Started by Pierrick4 months ago13 messages
#1Pierrick
pierrick.chovelon@dalibo.com

If the same extension is in two different paths mentioned in
extension_control_path,
it is impossible to install the version of the last one.

postgres=# show extension_control_path ;
                           extension_control_path
-----------------------------------------------------------------------------
 $system:/extensions/meilleureextension/share:/extensions/monextension/share
(1 row)

postgres=# select * from pg_available_extensions where name like '%mon%';
     name     | default_version | installed_version |    comment
--------------+-----------------+-------------------+---------------
 monextension | 2.0             |                   | Meilleure
 monextension | 1.0             |                   | Mon extension

(2 rows)

postgres=# create extension monextension version "1.0" ;
ERROR:  extension "monextension" has no installation script nor update path for version "1.0"

postgres=# create extension monextension ;
CREATE EXTENSION
postgres=# select * from pg_available_extensions where name like '%mon%';
name | default_version | installed_version | comment
--------------+-----------------+-------------------+---------------
monextension | 2.0 | 2.0 | Meilleure
monextension | 1.0 | 2.0 | Mon extension

Don't you think that the command

create extension monextension version "1.0" ;

should be accepted by checking in all paths ?

Pierrick,

#2Matheus Alcantara
matheusssilv97@gmail.com
In reply to: Pierrick (#1)
Re: Only one version can be installed when using extension_control_path

On Fri Sep 5, 2025 at 11:22 AM -03, Pierrick wrote:

If the same extension is in two different paths mentioned in
extension_control_path,
it is impossible to install the version of the last one.

postgres=# show extension_control_path ;
                           extension_control_path
-----------------------------------------------------------------------------
 $system:/extensions/meilleureextension/share:/extensions/monextension/share
(1 row)

postgres=# select * from pg_available_extensions where name like '%mon%';
     name     | default_version | installed_version |    comment
--------------+-----------------+-------------------+---------------
 monextension | 2.0             |                   | Meilleure
 monextension | 1.0             |                   | Mon extension

(2 rows)

postgres=# create extension monextension version "1.0" ;
ERROR:  extension "monextension" has no installation script nor update path for version "1.0"

postgres=# create extension monextension ;
CREATE EXTENSION
postgres=# select * from pg_available_extensions where name like '%mon%';
name | default_version | installed_version | comment
--------------+-----------------+-------------------+---------------
monextension | 2.0 | 2.0 | Meilleure
monextension | 1.0 | 2.0 | Mon extension

Don't you think that the command

create extension monextension version "1.0" ;

should be accepted by checking in all paths ?

This is working as expected. The current behavior is that at the first
step of CREATE EXTENSION command it will try to find the primary
.control file of the extension. On this step it will search the .control
file on paths at extension_control_path in order and it will use the
first one that it finds and based on the .control file found it will
install the extension using the version specified on the command.

--
Matheus Alcantara

#3David G. Johnston
david.g.johnston@gmail.com
In reply to: Matheus Alcantara (#2)
Re: Only one version can be installed when using extension_control_path

On Monday, September 8, 2025, Matheus Alcantara <matheusssilv97@gmail.com>
wrote:

On Fri Sep 5, 2025 at 11:22 AM -03, Pierrick wrote:

If the same extension is in two different paths mentioned in
extension_control_path,
it is impossible to install the version of the last one.

postgres=# show extension_control_path ;
extension_control_path
------------------------------------------------------------

-----------------

$system:/extensions/meilleureextension/share:/

extensions/monextension/share

(1 row)

postgres=# select * from pg_available_extensions where name like '%mon%';
name | default_version | installed_version | comment
--------------+-----------------+-------------------+---------------
monextension | 2.0 | | Meilleure
monextension | 1.0 | | Mon extension

On this step it will search the .control
file on paths at extension_control_path in order and it will use the
first one that it finds and based on the .control file found it will
install the extension using the version specified on the command.

Then pg_available_extensions seems broken - it lists extensions as
available that are not accessible to the user due to this policy. Maybe we
need to add something indicating that certain rows are hidden behind the
present path setting which would need to be changed if one wishes to
install them. Also seems like the extension location should be part of the
output too.

David J.

#4Tom Lane
tgl@sss.pgh.pa.us
In reply to: David G. Johnston (#3)
Re: Only one version can be installed when using extension_control_path

"David G. Johnston" <david.g.johnston@gmail.com> writes:

On Monday, September 8, 2025, Matheus Alcantara <matheusssilv97@gmail.com>
wrote:

On this step it will search the .control
file on paths at extension_control_path in order and it will use the
first one that it finds and based on the .control file found it will
install the extension using the version specified on the command.

Then pg_available_extensions seems broken - it lists extensions as
available that are not accessible to the user due to this policy. Maybe we
need to add something indicating that certain rows are hidden behind the
present path setting which would need to be changed if one wishes to
install them. Also seems like the extension location should be part of the
output too.

Between this and previously-identified problems (commits 81eaaa2c4,
f777d7738), it seems clear that extension_control_path (which is a new
thing in v18) was very poorly thought out. I wonder if it's too late
to revert it so that we can redesign it more carefully.

regards, tom lane

#5Pierrick
pierrick.chovelon@dalibo.com
In reply to: Tom Lane (#4)
Re: Only one version can be installed when using extension_control_path

On 08/09/2025 17:35, Tom Lane wrote:

"David G. Johnston" <david.g.johnston@gmail.com> writes:

On Monday, September 8, 2025, Matheus Alcantara <matheusssilv97@gmail.com>
wrote:

On this step it will search the .control
file on paths at extension_control_path in order and it will use the
first one that it finds and based on the .control file found it will
install the extension using the version specified on the command.

Then pg_available_extensions seems broken - it lists extensions as
available that are not accessible to the user due to this policy. Maybe we
need to add something indicating that certain rows are hidden behind the
present path setting which would need to be changed if one wishes to
install them. Also seems like the extension location should be part of the
output too.

I agree. If an extension is the list, I expect to be able to install it.

Either :
- we hide the latest version found in paths in pg_available_extensions ;
- we give the possibility to install all listed versions.
The second option seems more relevant to me.

Adding extension location is a good idea.

Show quoted text

Between this and previously-identified problems (commits 81eaaa2c4,
f777d7738), it seems clear that extension_control_path (which is a new
thing in v18) was very poorly thought out. I wonder if it's too late
to revert it so that we can redesign it more carefully.

regards, tom lane

#6Matheus Alcantara
matheusssilv97@gmail.com
In reply to: Pierrick (#5)
Re: Only one version can be installed when using extension_control_path

On Tue Sep 9, 2025 at 4:53 AM -03, Pierrick wrote:

On 08/09/2025 17:35, Tom Lane wrote:

"David G. Johnston" <david.g.johnston@gmail.com> writes:

On Monday, September 8, 2025, Matheus Alcantara <matheusssilv97@gmail.com>
wrote:

On this step it will search the .control
file on paths at extension_control_path in order and it will use the
first one that it finds and based on the .control file found it will
install the extension using the version specified on the command.

Then pg_available_extensions seems broken - it lists extensions as
available that are not accessible to the user due to this policy. Maybe we
need to add something indicating that certain rows are hidden behind the
present path setting which would need to be changed if one wishes to
install them. Also seems like the extension location should be part of the
output too.

I agree. If an extension is the list, I expect to be able to install it.

I definitely agree that pg_available_extensions is wrong but I think
that a fix for this should be to don't allow extensions with the same
name on different paths.

The current behavior is to stop the searching when the first extension
name matches with the CREATE EXTENSION input, this works in the same way
when for example we have the same command on multiple paths on PATH.
Changing the behavior to search on all paths to find the extension that
matches the name and possibly the version seems complicated and
confusing to me TBH.

To have multiple versions of the same extension, the .sql files for all
versions should be put on the same path all together as already is
today. Enabling adding the same extension on multiple paths doesn't seem
right to me IMHO.

Any thoughts? I'm missing something here?

Adding extension location is a good idea.

This looks like a good idea to me too.

Thanks for all the comments!

--
Matheus Alcantara

#7Pierrick
pierrick.chovelon@dalibo.com
In reply to: Matheus Alcantara (#6)
Re: Only one version can be installed when using extension_control_path

On 09/09/2025 16:18, Matheus Alcantara wrote:

On Tue Sep 9, 2025 at 4:53 AM -03, Pierrick wrote:

On 08/09/2025 17:35, Tom Lane wrote:

"David G. Johnston"<david.g.johnston@gmail.com> writes:

On Monday, September 8, 2025, Matheus Alcantara<matheusssilv97@gmail.com>
wrote:

On this step it will search the .control
file on paths at extension_control_path in order and it will use the
first one that it finds and based on the .control file found it will
install the extension using the version specified on the command.

Then pg_available_extensions seems broken - it lists extensions as
available that are not accessible to the user due to this policy. Maybe we
need to add something indicating that certain rows are hidden behind the
present path setting which would need to be changed if one wishes to
install them. Also seems like the extension location should be part of the
output too.

I agree. If an extension is the list, I expect to be able to install it.

I definitely agree that pg_available_extensions is wrong but I think
that a fix for this should be to don't allow extensions with the same
name on different paths.

The current behavior is to stop the searching when the first extension
name matches with the CREATE EXTENSION input, this works in the same way
when for example we have the same command on multiple paths on PATH.

If so, then only one line should be printed in pg_available_extensions.
This could be the fix :).

Changing the behavior to search on all paths to find the extension that
matches the name and possibly the version seems complicated and
confusing to me TBH.

To have multiple versions of the same extension, the .sql files for all
versions should be put on the same path all together as already is
today. Enabling adding the same extension on multiple paths doesn't seem
right to me IMHO.

Any thoughts? I'm missing something here?

Well, I don't really have a strong argument, but adding this new setting
allows
people to work with extensions differently. Having different folders for
different
versions of the same extension might be a new approach.

Adding extension location is a good idea.

This looks like a good idea to me too.

Thanks for all the comments!

You are welcome :)

Show quoted text

--
Matheus Alcantara

#8Michael Paquier
michael@paquier.xyz
In reply to: Tom Lane (#4)
Re: Only one version can be installed when using extension_control_path

On Mon, Sep 08, 2025 at 11:35:42AM -0400, Tom Lane wrote:

Between this and previously-identified problems (commits 81eaaa2c4,
f777d7738), it seems clear that extension_control_path (which is a new
thing in v18) was very poorly thought out. I wonder if it's too late
to revert it so that we can redesign it more carefully.

It would be too late once we are in GA, and we are not yet in GA. At
this point I think that this should be Peter's call.

I am not in his shoes, still I suspect like you that we may have not
seen the end of it yet. If we're confident enough that we can fix
anything, that's fine as a response to me, but discussions are also
involving how available extensions are seen at catalog level. This
bit is concerning for me, but I'm also of a pessimistic nature.
--
Michael

#9Peter Eisentraut
peter@eisentraut.org
In reply to: Pierrick (#7)
1 attachment(s)
Re: Only one version can be installed when using extension_control_path

On 10.09.25 09:48, Pierrick wrote:

On 09/09/2025 16:18, Matheus Alcantara wrote:

On Tue Sep 9, 2025 at 4:53 AM -03, Pierrick wrote:

On 08/09/2025 17:35, Tom Lane wrote:

"David G. Johnston"<david.g.johnston@gmail.com> writes:

On Monday, September 8, 2025, Matheus Alcantara<matheusssilv97@gmail.com>
wrote:

On this step it will search the .control
file on paths at extension_control_path in order and it will use the
first one that it finds and based on the .control file found it will
install the extension using the version specified on the command.

Then pg_available_extensions seems broken - it lists extensions as
available that are not accessible to the user due to this policy. Maybe we
need to add something indicating that certain rows are hidden behind the
present path setting which would need to be changed if one wishes to
install them. Also seems like the extension location should be part of the
output too.

I agree. If an extension is the list, I expect to be able to install it.

I definitely agree that pg_available_extensions is wrong but I think
that a fix for this should be to don't allow extensions with the same
name on different paths.

The current behavior is to stop the searching when the first extension
name matches with the CREATE EXTENSION input, this works in the same way
when for example we have the same command on multiple paths on PATH.

If so, then only one line should be printed in pg_available_extensions.
This could be the fix :).

Here is a patch for that. Please test it against your setup.

Attachments:

0001-Hide-duplicate-names-from-extension-views.patchtext/plain; charset=UTF-8; name=0001-Hide-duplicate-names-from-extension-views.patchDownload
From a0064ef26339482c8982c4b299f221316fa5a2d8 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 11 Sep 2025 15:41:29 +0200
Subject: [PATCH] Hide duplicate names from extension views

If extensions of equal names were installed in different directories
in the path, the views pg_available_extensions and
pg_available_extension_versions would show all of them, even though
only the first one was actually reachable by CREATE EXTENSION.  To
fix, have those views skip extensions found later in the path if they
have names already found earlier.

Also add a bit of documentation that only the first extension in the
path can be used.

Reported-by: Pierrick <pierrick.chovelon@dalibo.com>
Discussion: https://www.postgresql.org/message-id/flat/8f5a0517-1cb8-4085-ae89-77e7454e27ba%40dalibo.com
---
 doc/src/sgml/config.sgml                      |  6 +++++
 src/backend/commands/extension.c              | 24 +++++++++++++++++++
 .../t/001_extension_control_path.pl           |  9 ++++---
 3 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index d1e103ed779..5aea6cc2fe8 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -11057,6 +11057,12 @@ <title>Other Defaults</title>
         string, the default <literal>'$system'</literal> is also assumed.
        </para>
 
+       <para>
+        If extensions with equal names are present in multiple directories in
+        the configured path, only the instance found first in the path will be
+        used.
+       </para>
+
        <para>
         This parameter can be changed at run time by superusers and users
         with the appropriate <literal>SET</literal> privilege, but a
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index e6f9ab6dfd6..93ef1ad106f 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2208,6 +2208,7 @@ pg_available_extensions(PG_FUNCTION_ARGS)
 	List	   *locations;
 	DIR		   *dir;
 	struct dirent *de;
+	List	   *found_ext = NIL;
 
 	/* Build tuplestore to hold the result rows */
 	InitMaterializedSRF(fcinfo, 0);
@@ -2232,6 +2233,7 @@ pg_available_extensions(PG_FUNCTION_ARGS)
 			{
 				ExtensionControlFile *control;
 				char	   *extname;
+				String	   *extname_str;
 				Datum		values[3];
 				bool		nulls[3];
 
@@ -2246,6 +2248,16 @@ pg_available_extensions(PG_FUNCTION_ARGS)
 				if (strstr(extname, "--"))
 					continue;
 
+				/*
+				 * Ignore already-found names.  They are not reachable by the
+				 * path search, so don't shown them.
+				 */
+				extname_str = makeString(extname);
+				if (list_member(found_ext, extname_str))
+					continue;
+				else
+					found_ext = lappend(found_ext, extname_str);
+
 				control = new_ExtensionControlFile(extname);
 				control->control_dir = pstrdup(location);
 				parse_extension_control_file(control, NULL);
@@ -2294,6 +2306,7 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
 	List	   *locations;
 	DIR		   *dir;
 	struct dirent *de;
+	List	   *found_ext = NIL;
 
 	/* Build tuplestore to hold the result rows */
 	InitMaterializedSRF(fcinfo, 0);
@@ -2318,6 +2331,7 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
 			{
 				ExtensionControlFile *control;
 				char	   *extname;
+				String	   *extname_str;
 
 				if (!is_extension_control_filename(de->d_name))
 					continue;
@@ -2330,6 +2344,16 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
 				if (strstr(extname, "--"))
 					continue;
 
+				/*
+				 * Ignore already-found names.  They are not reachable by the
+				 * path search, so don't shown them.
+				 */
+				extname_str = makeString(extname);
+				if (list_member(found_ext, extname_str))
+					continue;
+				else
+					found_ext = lappend(found_ext, extname_str);
+
 				/* read the control file */
 				control = new_ExtensionControlFile(extname);
 				control->control_dir = pstrdup(location);
diff --git a/src/test/modules/test_extensions/t/001_extension_control_path.pl b/src/test/modules/test_extensions/t/001_extension_control_path.pl
index 1a9c97bbf4d..7fbe5bde332 100644
--- a/src/test/modules/test_extensions/t/001_extension_control_path.pl
+++ b/src/test/modules/test_extensions/t/001_extension_control_path.pl
@@ -11,12 +11,15 @@
 
 $node->init;
 
-# Create a temporary directory for the extension control file
+# Create temporary directories for the extension control files
 my $ext_dir = PostgreSQL::Test::Utils::tempdir();
 mkpath("$ext_dir/extension");
+my $ext_dir2 = PostgreSQL::Test::Utils::tempdir();
+mkpath("$ext_dir2/extension");
 
 my $ext_name = "test_custom_ext_paths";
 create_extension($ext_name, $ext_dir);
+create_extension($ext_name, $ext_dir2);
 
 my $ext_name2 = "test_custom_ext_paths_using_directory";
 mkpath("$ext_dir/$ext_name2");
@@ -26,7 +29,7 @@
 my $sep = $windows_os ? ";" : ":";
 $node->append_conf(
 	'postgresql.conf', qq{
-extension_control_path = '\$system$sep@{[ $windows_os ? ($ext_dir =~ s/\\/\\\\/gr) : $ext_dir ]}'
+extension_control_path = '\$system$sep@{[ $windows_os ? ($ext_dir =~ s/\\/\\\\/gr) : $ext_dir ]}$sep@{[ $windows_os ? ($ext_dir2 =~ s/\\/\\\\/gr) : $ext_dir2 ]}'
 });
 
 # Start node
@@ -34,7 +37,7 @@
 
 my $ecp = $node->safe_psql('postgres', 'show extension_control_path;');
 
-is($ecp, "\$system$sep$ext_dir",
+is($ecp, "\$system$sep$ext_dir$sep$ext_dir2",
 	"custom extension control directory path configured");
 
 $node->safe_psql('postgres', "CREATE EXTENSION $ext_name");

base-commit: 3e43a7b1ab971ed22e663891d941d4d00b6dd9fe
-- 
2.51.0

#10Pierrick
pierrick.chovelon@dalibo.com
In reply to: Peter Eisentraut (#9)
Re: Only one version can be installed when using extension_control_path

On 11/09/2025 16:11, Peter Eisentraut wrote:

Here is a patch for that.  Please test it against your setup.

Thanks a lot. Here is a test, and IMHO, it looks good.

-- version 1 of monextension is in /tmp/extensions/share/extension

postgres=# show extension_control_path ;
                extension_control_path
------------------------------------------------------
 $system:/tmp/extensions/share:/tmp/extensions2/share
(1 ligne)

postgres=# select * from pg_available_extensions where name =
'monextension';
     name     | default_version | installed_version |    comment
--------------+-----------------+-------------------+---------------
 monextension | 1.0             |                   | Mon extension
(1 ligne)

-- switching order

postgres=# alter system set extension_control_path =
'$system:/tmp/extensions2/share:/tmp/extensions/share';
ALTER SYSTEM
postgres=# select pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 ligne)

postgres=# show extension_control_path ;
                extension_control_path
------------------------------------------------------
 $system:/tmp/extensions2/share:/tmp/extensions/share
(1 ligne)

-- version 2 of monextension is in /tmp/extensions2/share/extension

postgres=# select * from pg_available_extensions where name =
'monextension';
     name     | default_version | installed_version |    comment
--------------+-----------------+-------------------+---------------
 monextension | 2.0             |                   | Mon extension
(1 ligne)

-- make a mistake in first folder

postgres=# alter system set extension_control_path =
'$system:/tmp/extension_old/share:/tmp/extensions/share';
ALTER SYSTEM
postgres=# select pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 ligne)

-- still version 2.0
postgres=# select * from pg_available_extensions where name =
'monextension';
     name     | default_version | installed_version |    comment
--------------+-----------------+-------------------+---------------
 monextension | 1.0             |                   | Mon extension
(1 ligne)

#11Pierrick
pierrick.chovelon@dalibo.com
In reply to: Pierrick (#10)
Re: Only one version can be installed when using extension_control_path

On 11/09/2025 20:03, Pierrick wrote:

-- make a mistake in first folder

postgres=# alter system set extension_control_path =
'$system:/tmp/extension_old/share:/tmp/extensions/share';
ALTER SYSTEM
postgres=# select pg_reload_conf();
 pg_reload_conf
----------------
 t
(1 ligne)

-- still version 2.0

sorry ... read --still version 1.0 of course

Show quoted text

postgres=# select * from pg_available_extensions where name =
'monextension';
     name     | default_version | installed_version |    comment
--------------+-----------------+-------------------+---------------
 monextension | 1.0             |                   | Mon extension
(1 ligne)

#12Peter Eisentraut
peter@eisentraut.org
In reply to: Pierrick (#10)
Re: Only one version can be installed when using extension_control_path

On 11.09.25 20:03, Pierrick wrote:

On 11/09/2025 16:11, Peter Eisentraut wrote:

Here is a patch for that.  Please test it against your setup.

Thanks a lot. Here is a test, and IMHO, it looks good.

committed

#13Matheus Alcantara
matheusssilv97@gmail.com
In reply to: David G. Johnston (#3)
Re: Only one version can be installed when using extension_control_path

On Mon, Sep 8, 2025 at 12:23 PM David G. Johnston
<david.g.johnston@gmail.com> wrote:

Also seems like the extension location should be part of the output too.

I've posted a patch for this at [1]/messages/by-id/DCTSKQAVOXIG.3A05JM1YORARK@gmail.com

[1]: /messages/by-id/DCTSKQAVOXIG.3A05JM1YORARK@gmail.com

--
Matheus Alcantara