public inbox for [email protected]
help / color / mirror / Atom feedFrom: Jonathan Gonzalez V. <[email protected]>
To: Matheus Alcantara <[email protected]>
To: PostgreSQL Hackers <[email protected]>
Subject: Re: pg_upgrade fails when extension_control_path is used
Date: Fri, 06 Mar 2026 13:10:12 +0100
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
<[email protected]>
Hello!
> that the code comment on function.c can be
> similar to what we have on load_external_function():
>
> /*
> * For extensions with hardcoded '$libdir/' library names, we
> * strip the prefix to allow the library search path to be used.
> */
>
> >
I'm attaching a v3 with an improved messages
Also updated the reviewers list on the message
Regards!
>
--
Jonathan Gonzalez V. <[email protected]>
EnterpriseDB
Attachments:
[text/x-patch] v3-0001-Strip-libdir-during-pg_upgrade-starting-on-19.patch (10.9K, 2-v3-0001-Strip-libdir-during-pg_upgrade-starting-on-19.patch)
download | inline diff:
From 347161a6285517755828c51a8a338c8438c3bc63 Mon Sep 17 00:00:00 2001
From: "Jonathan Gonzalez V." <[email protected]>
Date: Mon, 23 Feb 2026 22:27:51 +0100
Subject: [PATCH v3 1/1] Strip `$libdir` during pg_upgrade starting on 19.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
With the commit 4f7f7b03758 extension_control_path GUC was included,
while some test were added, is missing to handle the hardcoded `$libdir/`
path during the execution of `pg_upgrade` for the the installed
extensions using the extension_control_path GUC
An aditional test for `pg_upgrade` is added to test the upgrade with
the extension_control_path in use with a C extension using the
hardcoded `$libdir/` string in the `module_pathname`
Reviewed-by: Niccolò Fei <[email protected]>
Reviewed-by: Matheus Alcantara <[email protected]>
---
src/bin/pg_upgrade/Makefile | 6 +-
src/bin/pg_upgrade/function.c | 8 ++
src/bin/pg_upgrade/meson.build | 21 ++-
.../t/008_extension_control_path.pl | 126 ++++++++++++++++++
src/test/modules/test_extensions/Makefile | 3 +
src/test/modules/test_extensions/meson.build | 13 ++
src/test/modules/test_extensions/test_ext.c | 22 +++
7 files changed, 197 insertions(+), 2 deletions(-)
create mode 100644 src/bin/pg_upgrade/t/008_extension_control_path.pl
create mode 100644 src/test/modules/test_extensions/test_ext.c
diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile
index 726df4b7525..771addb675a 100644
--- a/src/bin/pg_upgrade/Makefile
+++ b/src/bin/pg_upgrade/Makefile
@@ -3,7 +3,7 @@
PGFILEDESC = "pg_upgrade - an in-place binary upgrade utility"
PGAPPICON = win32
-EXTRA_INSTALL=contrib/test_decoding src/test/modules/dummy_seclabel
+EXTRA_INSTALL=contrib/test_decoding src/test/modules/dummy_seclabel src/test/modules/test_extensions
subdir = src/bin/pg_upgrade
top_builddir = ../../..
@@ -38,6 +38,10 @@ LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
REGRESS_SHLIB=$(abs_top_builddir)/src/test/regress/regress$(DLSUFFIX)
export REGRESS_SHLIB
+# required for 008_extension_control_path.pl
+TEST_EXT_LIB=$(abs_top_builddir)/src/test/modules/test_extensions/test_ext$(DLSUFFIX)
+export TEST_EXT_LIB
+
all: pg_upgrade
pg_upgrade: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils
diff --git a/src/bin/pg_upgrade/function.c b/src/bin/pg_upgrade/function.c
index a3184f95665..bc7e8006d82 100644
--- a/src/bin/pg_upgrade/function.c
+++ b/src/bin/pg_upgrade/function.c
@@ -120,6 +120,14 @@ get_loadable_libraries(void)
{
char *lib = PQgetvalue(res, rowno, 0);
+ /*
+ * Starting with version 19, for extensions with hardcoded
+ * '$libdir/' library names, we strip the prefix to allow the
+ * library search path to be used.
+ */
+ if (strncmp(lib, "$libdir/", 8) == 0)
+ lib += 8;
+
os_info.libraries[totaltups].name = pg_strdup(lib);
os_info.libraries[totaltups].dbnum = dbnum;
diff --git a/src/bin/pg_upgrade/meson.build b/src/bin/pg_upgrade/meson.build
index 49b1b624f25..ffbf6ae8d75 100644
--- a/src/bin/pg_upgrade/meson.build
+++ b/src/bin/pg_upgrade/meson.build
@@ -36,13 +36,30 @@ pg_upgrade = executable('pg_upgrade',
)
bin_targets += pg_upgrade
+test_ext_sources = files(
+ '../../test/modules/test_extensions/test_ext.c'
+)
+
+if host_system == 'windows'
+ test_ext_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_ext',
+ '--FILEDESC', 'test_ext - test C extension for pg_upgrade',])
+endif
+
+test_ext = shared_module('test_ext',
+ test_ext_sources,
+ kwargs: pg_test_mod_args,
+)
tests += {
'name': 'pg_upgrade',
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'tap': {
- 'env': {'with_icu': icu.found() ? 'yes' : 'no'},
+ 'env': {
+ 'with_icu': icu.found() ? 'yes' : 'no',
+ 'TEST_EXT_LIB': test_ext.full_path(),
+ },
'tests': [
't/001_basic.pl',
't/002_pg_upgrade.pl',
@@ -51,7 +68,9 @@ tests += {
't/005_char_signedness.pl',
't/006_transfer_modes.pl',
't/007_multixact_conversion.pl',
+ 't/008_extension_control_path.pl',
],
+ 'deps': [test_ext],
'test_kwargs': {'priority': 40}, # pg_upgrade tests are slow
},
}
diff --git a/src/bin/pg_upgrade/t/008_extension_control_path.pl b/src/bin/pg_upgrade/t/008_extension_control_path.pl
new file mode 100644
index 00000000000..b50e92326ca
--- /dev/null
+++ b/src/bin/pg_upgrade/t/008_extension_control_path.pl
@@ -0,0 +1,126 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Test pg_upgrade with the extension_control_path GUC active.
+
+use strict;
+use warnings FATAL => 'all';
+
+use File::Copy;
+use File::Path;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Make sure the extension file .so path is provided
+my $ext_lib_so = $ENV{TEST_EXT_LIB}
+ or die "couldn't get the extension so path";
+
+# Create the custom extension directory layout:
+# $ext_dir/extension/ -- .control and .sql files
+# $ext_dir/lib/ -- .so file
+my $ext_dir = PostgreSQL::Test::Utils::tempdir();
+mkpath("$ext_dir/extension");
+mkpath("$ext_dir/lib");
+my $ext_lib = $ext_dir . '/lib';
+
+# Copy the .so file into the lib/ subdirectory.
+copy($ext_lib_so, $ext_lib)
+ or die "could not copy '$ext_lib_so' to '$ext_lib': $!";
+
+create_extension_files('test_ext', $ext_dir);
+
+my $sep = $windows_os ? ";" : ":";
+my $ext_path = $windows_os ? ($ext_dir =~ s/\\/\\\\/gr) : $ext_dir;
+my $ext_lib_path = $windows_os ? ($ext_lib =~ s/\\/\\\\/gr) : $ext_lib;
+
+my $extension_control_path_conf = qq(
+extension_control_path = '\$system$sep$ext_path'
+dynamic_library_path = '\$libdir$sep$ext_lib_path'
+);
+
+my $old =
+ PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
+$old->init;
+
+# Configure extension_control_path so the .control file is found in our
+# extension/ directory, and dynamic_library_path so the .so is found in lib/.
+$old->append_conf('postgresql.conf', $extension_control_path_conf);
+
+$old->start;
+
+# CREATE EXTENSION 'test_ext'
+$old->safe_psql('postgres', 'CREATE EXTENSION test_ext');
+
+# Verify the extension works before the upgrade.
+my ($code, $stdout, $stderr) = $old->psql('postgres', 'SELECT test_ext()');
+is($code, 0, 'extension works before upgrade');
+like($stderr, qr/NOTICE: running successful/, 'extension working');
+
+$old->stop;
+
+my $new = PostgreSQL::Test::Cluster->new('new');
+$new->init;
+
+# Pre-configure the new cluster with dynamic_library_path and
+# extension_control_path before running pg_upgrade.
+$new->append_conf('postgresql.conf', $extension_control_path_conf);
+
+# In a VPATH build, we'll be started in the source directory, but we want
+# to run pg_upgrade in the build directory so that any files generated finish
+# in it, like delete_old_cluster.{sh,bat}.
+chdir ${PostgreSQL::Test::Utils::tmp_check};
+
+command_ok(
+ [
+ 'pg_upgrade', '--no-sync',
+ '--old-datadir' => $old->data_dir,
+ '--new-datadir' => $new->data_dir,
+ '--old-bindir' => $old->config_data('--bindir'),
+ '--new-bindir' => $new->config_data('--bindir'),
+ '--socketdir' => $new->host,
+ '--old-port' => $old->port,
+ '--new-port' => $new->port,
+ '--copy',
+ ],
+ 'pg_upgrade succeeds with extension installed via extension_control_path'
+);
+
+$new->start;
+
+# Verify the extension still works after the upgrade.
+($code, $stdout, $stderr) = $new->psql('postgres', 'SELECT test_ext()');
+is($code, 0, 'extension works after upgrade');
+like($stderr, qr/NOTICE: running successful/, 'extension working');
+
+$new->stop;
+
+# Write .control and .sql files into $ext_dir/extension/
+# `module_pathname` contains the `$libdir/` to simulate most of the extensions
+# that use it as a prefix in the `module_pathname` by default
+sub create_extension_files
+{
+ my ($ext_name, $ext_dir) = @_;
+
+ open my $cf, '>', "$ext_dir/extension/$ext_name.control"
+ or die "could not create control file: $!";
+ print $cf
+ "comment = 'Test C extension for pg_upgrade + extension_control_path'\n";
+ print $cf "default_version = '1.0'\n";
+ print $cf "module_pathname = '\$libdir/$ext_name'\n";
+ print $cf "relocatable = true\n";
+ close $cf;
+
+ open my $sqlf, '>', "$ext_dir/extension/$ext_name--1.0.sql"
+ or die "could not create SQL file: $!";
+ print $sqlf "/* $ext_name--1.0.sql */\n";
+ print $sqlf
+ "-- complain if script is sourced in psql, rather than via CREATE EXTENSION\n";
+ print $sqlf
+ qq'\\echo Use "CREATE EXTENSION $ext_name" to load this file. \\quit\n';
+ print $sqlf "CREATE FUNCTION test_ext()\n";
+ print $sqlf "RETURNS void AS 'MODULE_PATHNAME'\n";
+ print $sqlf "LANGUAGE C;\n";
+ close $sqlf;
+}
+
+done_testing();
diff --git a/src/test/modules/test_extensions/Makefile b/src/test/modules/test_extensions/Makefile
index a3591bf3d2f..d1b0b81e5fd 100644
--- a/src/test/modules/test_extensions/Makefile
+++ b/src/test/modules/test_extensions/Makefile
@@ -1,6 +1,7 @@
# src/test/modules/test_extensions/Makefile
MODULE = test_extensions
+MODULE_big = test_ext
PGFILEDESC = "test_extensions - regression testing for EXTENSION support"
EXTENSION = test_ext1 test_ext2 test_ext3 test_ext4 test_ext5 test_ext6 \
@@ -11,6 +12,8 @@ EXTENSION = test_ext1 test_ext2 test_ext3 test_ext4 test_ext5 test_ext6 \
test_ext_set_schema \
test_ext_req_schema1 test_ext_req_schema2 test_ext_req_schema3
+OBJS = test_ext.o
+
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 \
diff --git a/src/test/modules/test_extensions/meson.build b/src/test/modules/test_extensions/meson.build
index be9c9ae593f..2c7cea189e2 100644
--- a/src/test/modules/test_extensions/meson.build
+++ b/src/test/modules/test_extensions/meson.build
@@ -46,6 +46,19 @@ test_install_data += files(
'test_ext_set_schema.control',
)
+test_ext_sources = files('test_ext.c')
+
+if host_system == 'windows'
+ test_ext_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_ext',
+ '--FILEDESC', 'test_ext - test C extension for pg_upgrade',])
+endif
+
+test_ext = shared_module('test_ext',
+ test_ext_sources,
+ kwargs: pg_test_mod_args,
+)
+
tests += {
'name': 'test_extensions',
'sd': meson.current_source_dir(),
diff --git a/src/test/modules/test_extensions/test_ext.c b/src/test/modules/test_extensions/test_ext.c
new file mode 100644
index 00000000000..a23165ba67a
--- /dev/null
+++ b/src/test/modules/test_extensions/test_ext.c
@@ -0,0 +1,22 @@
+/*
+ * test_ext.c
+ *
+ * Dummy C extension for testing extension_control_path with pg_upgrade
+ *
+ * Portions Copyright (c) 2026, PostgreSQL Global Development Group
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_ext);
+
+Datum
+test_ext(PG_FUNCTION_ARGS)
+{
+ ereport(NOTICE,
+ (errmsg("running successful")));
+ PG_RETURN_VOID();
+}
--
2.51.0
[application/pgp-signature] signature.asc (833B, 3-signature.asc)
download
view thread (4+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: pg_upgrade fails when extension_control_path is used
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox