From d4b9fe8b2d199c471dd238e6fe5f8238881ff598 Mon Sep 17 00:00:00 2001 From: "Chao Li (Evan)" Date: Tue, 3 Nov 2026 09:56:50 +0800 Subject: [PATCH v1 2/2] Deduplicate extension control search directories Compare directories after macro expansion and path canonicalization, retaining the first occurrence and its display metadata. This avoids rescanning repeated directories while preserving search precedence. Keep extension-name deduplication for matching names in distinct directories, and test that repeated directories preserve the reported location. Author: Chao Li --- src/backend/commands/extension.c | 40 +++++++++++++------ .../t/001_extension_control_path.pl | 36 +++++++++++++++++ 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 6ef4bdb3faf..01d8b855285 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -533,6 +533,8 @@ get_extension_control_directories(void) } else { + List *found_paths = NIL; + /* Duplicate the string so we can modify it */ ecp = pstrdup(Extension_control_path); @@ -541,7 +543,8 @@ get_extension_control_directories(void) int len; char *mangled; char *piece = first_path_var_separator(ecp); - ExtensionLocation *location; + String *path_str; + bool is_system; /* Get the length of the next path on ecp */ if (piece == NULL) @@ -558,8 +561,6 @@ get_extension_control_directories(void) continue; } - location = palloc_object(ExtensionLocation); - /* Copy the next path found on ecp */ piece = palloc(len + 1); strlcpy(piece, ecp, len + 1); @@ -568,22 +569,32 @@ get_extension_control_directories(void) * Substitute the path macro if needed or append "extension" * suffix if it is a custom extension control path. */ - if (strcmp(piece, EXTENSION_SYSTEM_MACRO) == 0) - { - location->macro = pstrdup(piece); + is_system = strcmp(piece, EXTENSION_SYSTEM_MACRO) == 0; + if (is_system) mangled = substitute_path_macro(piece, EXTENSION_SYSTEM_MACRO, system_dir); - } else - { - location->macro = NULL; mangled = psprintf("%s/extension", piece); - } pfree(piece); - /* Canonicalize the path based on the OS and add to the list */ + /* Compare paths after macro expansion and canonicalization. */ canonicalize_path(mangled); - location->loc = mangled; - paths = lappend(paths, location); + path_str = makeString(mangled); + + /* Keep the first occurrence, including its macro for display. */ + if (list_member(found_paths, path_str)) + { + pfree(path_str); + pfree(mangled); + } + else + { + ExtensionLocation *location = palloc_object(ExtensionLocation); + + location->macro = is_system ? pstrdup(EXTENSION_SYSTEM_MACRO) : NULL; + location->loc = mangled; + found_paths = lappend(found_paths, path_str); + paths = lappend(paths, location); + } /* Break if ecp is empty or move to the next path on ecp */ if (ecp[len] == '\0') @@ -591,6 +602,9 @@ get_extension_control_directories(void) else ecp += len + 1; } + + /* Free the String nodes, but retain the strings used by locations. */ + list_free_deep(found_paths); } foreach_ptr(ExtensionLocation, location, paths) 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 7c42561f5bc..e2c0230d67e 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 @@ -139,6 +139,42 @@ for my $path ( "empty elements should not search /extension with path '$path'"); } +# Repeated directories, including differently spelled paths that canonicalize +# to the same directory, must be searched only once and preserve the first +# location for each extension. +my $ext_dir2_canonicalized = $ext_dir2; +$ext_dir2_canonicalized =~ s!\\!/!g if $windows_os; +for my $dirs ( + [ $ext_dir_canonicalized, $ext_dir2_canonicalized ], + [ $ext_dir2_canonicalized, $ext_dir_canonicalized ]) +{ + my ($first, $second) = @$dirs; + my $path = join($sep, $first, "$first/.", $second, $first); + for my $view (qw(pg_available_extensions pg_available_extension_versions)) + { + my $stderr; + $node->psql( + 'postgres', + "set client_min_messages = debug3; + set extension_control_path = '$path'; + select location from $view where name = '$ext_name'", + stdout => \$ret, + stderr => \$stderr, + on_error_die => 1); + is($ret, "$first/extension", + "duplicate directories preserve the first location in $view with path '$path'"); + my $count = () = $stderr =~ /$path_message "\Q$first\E\/extension"/g; + is($count, 1, + "duplicate directory is searched only once in $view with path '$path'"); + } +} + +$ret = $node->safe_psql( + 'postgres', + "set extension_control_path = '\$system$sep\$system'; + select location from pg_available_extensions where name = 'plpgsql'"); +is($ret, '$system', 'repeated system directory preserves the macro location'); + # Test with an extension that does not exists my ($code, $stdout, $stderr) = $node->psql('postgres', "CREATE EXTENSION invalid"); -- 2.50.1 (Apple Git-155)