public inbox for [email protected]
help / color / mirror / Atom feedFrom: Ayush Tiwari <[email protected]>
To: [email protected]
To: [email protected]
To: Peter Eisentraut <[email protected]>
Subject: Re: BUG #19506: LOAD '$libdir/...' inside extension scripts ignores dynamic_library_path with extension_control_path
Date: Fri, 5 Jun 2026 09:57:16 +0530
Message-ID: <CAJTYsWXftZbLOd=f7kGWA6xP6tC7neeP82PCtui=b9Rwv1Z3uA@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
Hi,
On Thu, 4 Jun 2026 at 21:23, PG Bug reporting form <[email protected]>
wrote:
> The following bug has been logged on the website:
>
> Bug reference: 19506
> Logged by: Gabriele Bartolini
> Email address: [email protected]
> PostgreSQL version: 18.4
> Operating system: Linux (reproduced under CloudNativePG/Kubernetes)
> Description:
>
> When an extension is installed in a location reached via
> `extension_control_path` / `dynamic_library_path` (rather than the
> compiled-in package library directory), a LOAD '$libdir/foo' hardcoded
> inside an extension's SQL script fails to find the library. PostGIS does
> this in its upgrade scripts, so a PostGIS upgrade fails:
>
> ```
> app=# SELECT postgis_extensions_upgrade();
> NOTICE: Updating extension postgis 3.6.1
> ERROR: could not access file "$libdir/postgis-3": No such file or
> directory
> CONTEXT: SQL statement "LOAD '$libdir/postgis-3'"
> extension script file "postgis--ANY--3.6.3.sql", near line 1530
> ```
>
> This is a side effect of the fix for bug #18920 (commit f777d773878).
> Commit
> 4f7f7b03758 (`extension_control_path`) made the feature work by stripping
> the '$libdir/' prefix so that dynamic_library_path is consulted. #18920
> then
> restricted that stripping to the function-load path so that a user-issued
> `LOAD` keeps the literal '$libdir/' prefix. As a result, a `LOAD` inside an
> extension script now also keeps the literal prefix, so
> `dynamic_library_path` is never consulted, and the library cannot be found.
>
> A `LOAD` running inside an extension script should behave like the
> extension's function loads (strip '$libdir/'), while a LOAD issued directly
> by a user should keep it (the #18920 behaviour). The two can be
> distinguished by `creating_extension`.
>
> Reproduced with the CloudNativePG operator on Kubernetes, but it applies to
> any setup using `extension_control_path` / `dynamic_library_path`.
>
Thanks for the report and the very clear diagnosis, I could
reproduce the issue and your analysis matches what I see.
The attached patch implements exactly what you suggested: a
LOAD running while creating_extension is true strips the simple
"$libdir/" prefix (so dynamic_library_path is consulted, like
the extension's own function loads do), while a user-issued
LOAD keeps the literal prefix and therefore preserves the
#18920 behaviour.
To avoid duplicating the existing prefix-stripping logic in
load_external_function(), I factored it out into a small
static helper and reused it from load_file(). Nested paths
(e.g. "$libdir/foo/bar") are still left untouched and continue
to be expanded by expand_dynamic_library_name() as before.
Verified locally that the PostGIS-style reproducer now succeeds
when the extension is installed via extension_control_path /
dynamic_library_path, that a plain user-issued
LOAD '$libdir/foo' still behaves as on HEAD, and that the
existing extension regression suites still pass.
Regards,
Ayush
Attachments:
[application/octet-stream] v1-0001-dfmgr-let-extension-script-LOAD-use-dynamic_lib.patch (4.2K, 3-v1-0001-dfmgr-let-extension-script-LOAD-use-dynamic_lib.patch)
download | inline diff:
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ayush Tiwari <[email protected]>
Date: Fri, 5 Jun 2026 12:00:00 +0530
Subject: [PATCH v1] dfmgr: let extension-script LOAD use dynamic_library_path
Commit f777d773878 (fix for bug #18920) restricted the simple "$libdir/"
prefix stripping to load_external_function() so that a user-issued
LOAD '$libdir/foo' would keep the literal "$libdir/" prefix and not
consult dynamic_library_path.
A side effect is that a LOAD '$libdir/foo' executed *inside* an
extension script also keeps the literal prefix, so a hardcoded
"$libdir/" library reference in an extension's SQL script can no
longer be resolved through dynamic_library_path. This breaks
extensions installed via extension_control_path /
dynamic_library_path (for example PostGIS, whose upgrade scripts do
LOAD '$libdir/postgis-3').
A LOAD running inside an extension script should behave like the
extension's function loads: strip the simple "$libdir/" prefix and
let dynamic_library_path be consulted. A LOAD issued directly by a
user should keep the literal prefix. The two are distinguished by
creating_extension.
Factor the existing prefix stripping into a small helper and use it
from load_external_function() unchanged and from load_file() only
while an extension script is being executed.
Reported-by: Gabriele Bartolini
Discussion: https://postgr.es/m/[email protected]
---
src/backend/utils/fmgr/dfmgr.c | 38 ++++++++++++++++++++++------------
1 file changed, 25 insertions(+), 13 deletions(-)
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index e636cc81cf8..14b6e5b5c4c 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -20,6 +20,7 @@
#include <dlfcn.h>
#endif /* !WIN32 */
+#include "commands/extension.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
@@ -71,6 +72,7 @@ char *Dynamic_library_path;
static void *internal_load_library(const char *libname);
pg_noreturn static void incompatible_module_error(const char *libname,
const Pg_abi_values *module_magic_data);
+static const char *strip_libdir_prefix(const char *filename);
static char *expand_dynamic_library_name(const char *name);
static void check_restricted_library_name(const char *name);
@@ -99,20 +101,7 @@ load_external_function(const char *filename, const char *funcname,
void *lib_handle;
void *retval;
- /*
- * For extensions with hardcoded '$libdir/' library names, we strip the
- * prefix to allow the library search path to be used. This is done only
- * for simple names (e.g., "$libdir/foo"), not for nested paths (e.g.,
- * "$libdir/foo/bar").
- *
- * For nested paths, 'expand_dynamic_library_name' directly expands the
- * '$libdir' macro, so we leave them untouched.
- */
- if (strncmp(filename, "$libdir/", 8) == 0)
- {
- if (first_dir_separator(filename + 8) == NULL)
- filename += 8;
- }
+ filename = strip_libdir_prefix(filename);
/* Expand the possibly-abbreviated filename to an exact path name */
fullname = expand_dynamic_library_name(filename);
@@ -150,6 +139,9 @@ load_file(const char *filename, bool restricted)
{
char *fullname;
+ if (creating_extension)
+ filename = strip_libdir_prefix(filename);
+
/* Apply security restriction if requested */
if (restricted)
check_restricted_library_name(filename);
@@ -309,6 +301,24 @@ internal_load_library(const char *libname)
return file_scanner->handle;
}
+/*
+ * For extensions with hardcoded '$libdir/' library names, strip the prefix to
+ * allow the library search path to be used. This is done only for simple names
+ * (e.g., "$libdir/foo"), not for nested paths (e.g., "$libdir/foo/bar").
+ *
+ * For nested paths, expand_dynamic_library_name() directly expands the
+ * '$libdir' macro, so leave them untouched.
+ */
+static const char *
+strip_libdir_prefix(const char *filename)
+{
+ if (strncmp(filename, "$libdir/", 8) == 0 &&
+ first_dir_separator(filename + 8) == NULL)
+ filename += 8;
+
+ return filename;
+}
+
/*
* Report a suitable error for an incompatible magic block.
*/
--
2.43.0
base-commit: 7598b5383b16a562bd7e7732eda7d783b34f4615
view thread (6+ 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], [email protected], [email protected]
Subject: Re: BUG #19506: LOAD '$libdir/...' inside extension scripts ignores dynamic_library_path with extension_control_path
In-Reply-To: <CAJTYsWXftZbLOd=f7kGWA6xP6tC7neeP82PCtui=b9Rwv1Z3uA@mail.gmail.com>
* 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