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"<[email protected]> writes:
On Monday, September 8, 2025, Matheus Alcantara<[email protected]>
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.
From a0064ef26339482c8982c4b299f221316fa5a2d8 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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 <[email protected]>
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

Reply via email to