From c07bfd1556502592c1a63cceafe0542f28dfe0a4 Mon Sep 17 00:00:00 2001
From: Matheus Alcantara <mths.dev@pm.me>
Date: Wed, 23 Apr 2025 16:11:24 -0300
Subject: [PATCH v1] Make "directory" work with extension control path

Previously extensions installed on a custom path that is available via
extension_control_path GUC that set the "directory" field on .control
file was not being able to CREATE. This was happening because on
get_extension_script_directory was hard coded to search for the script
files only on the share system dir.

This commit fix this issue by using the control->control_dir as a share
dir to return the path of the extension script files.
---
 src/backend/commands/extension.c              | 43 +++++++++++++++++--
 .../t/001_extension_control_path.pl           |  6 ++-
 2 files changed, 43 insertions(+), 6 deletions(-)

diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 180f4af9be3..d68efd59118 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -376,6 +376,14 @@ get_extension_control_directories(void)
 
 			/* Substitute the path macro if needed */
 			mangled = substitute_path_macro(piece, "$system", system_dir);
+
+			/*
+			 * Append "extension" suffix in case is a custom extension control
+			 * path.
+			 */
+			if (strcmp(piece, "$system") != 0)
+				mangled = psprintf("%s/extension", mangled);
+
 			pfree(piece);
 
 			/* Canonicalize the path based on the OS and add to the list */
@@ -422,6 +430,9 @@ find_extension_control_filename(ExtensionControlFile *control)
 	ecp = Extension_control_path;
 	if (strlen(ecp) == 0)
 		ecp = "$system";
+	else if (strcmp(ecp, "$system") != 0)
+		ecp = psprintf("%s/extension", ecp);
+
 	result = find_in_path(basename, ecp, "extension_control_path", "$system", system_dir);
 
 	if (result)
@@ -439,8 +450,11 @@ find_extension_control_filename(ExtensionControlFile *control)
 static char *
 get_extension_script_directory(ExtensionControlFile *control)
 {
-	char		sharepath[MAXPGPATH];
 	char	   *result;
+	int			ctrldir_len;
+	int			prefix_len;
+	int			dir_len;
+	int			suffix_len = strlen("extension");
 
 	/*
 	 * The directory parameter can be omitted, absolute, or relative to the
@@ -452,9 +466,30 @@ get_extension_script_directory(ExtensionControlFile *control)
 	if (is_absolute_path(control->directory))
 		return pstrdup(control->directory);
 
-	get_share_path(my_exec_path, sharepath);
-	result = (char *) palloc(MAXPGPATH);
-	snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory);
+	ctrldir_len = strlen(control->control_dir);
+
+	/*
+	 * Assert that the control->control_dir end with /extension suffix so that
+	 * we can replace with the value from control->directory.
+	 */
+	Assert(ctrldir_len >= suffix_len &&
+		   strcmp(control->control_dir + ctrldir_len - suffix_len, "extension") == 0);
+
+	/*
+	 * At this point we have the control->directory and control->control_dir
+	 * filled. As the extension is using a custom "directory" value we need to
+	 * replace the "/extension" suffix from "control_dir" with the value of
+	 * "directory" so that we can find the correct script files.
+	 */
+	prefix_len = ctrldir_len - suffix_len;
+	dir_len = strlen(control->directory);
+
+	/* don't forget null terminator */
+	result = (char *) palloc(prefix_len + dir_len + 1);
+
+	memcpy(result, control->control_dir, prefix_len);
+	memcpy(result + prefix_len, control->directory, dir_len);
+	result[prefix_len + dir_len] = '\0';	/* don't forget null terminator */
 
 	return result;
 }
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 c186c1470f7..7616011105f 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
@@ -5,6 +5,7 @@ use warnings FATAL => 'all';
 use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::Utils;
 use Test::More;
+use File::Path qw( make_path );
 
 my $node = PostgreSQL::Test::Cluster->new('node');
 
@@ -12,9 +13,10 @@ $node->init;
 
 # Create a temporary directory for the extension control file
 my $ext_dir = PostgreSQL::Test::Utils::tempdir();
+make_path("$ext_dir/extension");
 my $ext_name = "test_custom_ext_paths";
-my $control_file = "$ext_dir/$ext_name.control";
-my $sql_file = "$ext_dir/$ext_name--1.0.sql";
+my $control_file = "$ext_dir/extension/$ext_name.control";
+my $sql_file = "$ext_dir/extension/$ext_name--1.0.sql";
 
 # Create .control .sql file
 open my $cf, '>', $control_file or die "Could not create control file: $!";
-- 
2.39.5 (Apple Git-154)

