On 11.11.24 08:16, Peter Eisentraut wrote:
I implemented a patch along the lines Craig had suggested.  It's a new GUC variable that is a path for extension control files.  It's called extension_control_path, and it works exactly the same way as dynamic_library_path.  Except that the magic token is called $system instead of $libdir.  In fact, most of the patch is refactoring the routines in dfmgr.c to not hardcode dynamic_library_path but allow searching for any file in any path.  Once a control file is found, the other extension support files (script files and auxiliary control files) are looked for in the same directory.

There are some TODOs in the patch.  Some of those are for documentation that needs to be completed.  Others are for functions like pg_available_extensions() that need to be rewritten to be aware of the path.  I think this would be pretty straightforward.

I've made a bit of progress on this patch, filled in some documentation and resolved some TODO markers. Also:

Some open problems or discussion points:

- You can install extensions into alternative directories using PGXS like

     make install datadir=/else/where/share pkglibdir=/else/where/lib

This works.  I was hoping it would work to use

     make install prefix=/else/where

but that doesn't because of some details in Makefile.global.  I think we can tweak that a little bit to make that work too.

This works now.

- The biggest problem is that many extensions set in their control file

     module_pathname = '$libdir/foo'

This disables the use of dynamic_library_path, so this whole idea of installing an extension elsewhere won't work that way.  The obvious solution is that extensions change this to just 'foo'.  But this will require a lot updating work for many extensions, or a lot of patching by packagers.

I have solved this by just stripping off "$libdir/" from the front of the filename. This works for now. We can think about other ways to tweak this, perhaps, but I don't see any drawback to this in practice.

This patch is now complete enough for testing, I think. As I mentioned earlier, I haven't updated pg_available_extensions() etc. to support the path, but that shouldn't prevent some testing.
From fdf1bd8abbb9aaff3747c57d653ef19f8be31fe8 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Thu, 5 Dec 2024 11:49:05 +0100
Subject: [PATCH v1] extension_control_path

The new GUC extension_control_path specifies a path to look for
extension control files.  The default value is $system, which looks in
the compiled-in location, as before.

The path search uses the same code and works in the same way as
dynamic_library_path.

Discussion: 
https://www.postgresql.org/message-id/flat/e7c7bffb-8857-48d4-a71f-88b359fad...@justatheory.com

TODO: Some utility functions such as pg_available_extensions() are not
adjusted to be aware of the path yet.
---
 doc/src/sgml/config.sgml                      | 68 +++++++++++++++
 doc/src/sgml/extend.sgml                      | 19 ++--
 doc/src/sgml/ref/create_extension.sgml        |  6 +-
 src/Makefile.global.in                        | 19 ++--
 src/backend/commands/extension.c              | 87 ++++++++++++++-----
 src/backend/utils/fmgr/dfmgr.c                | 76 ++++++++++------
 src/backend/utils/misc/guc_tables.c           | 13 +++
 src/backend/utils/misc/postgresql.conf.sample |  1 +
 src/include/commands/extension.h              |  2 +
 src/include/fmgr.h                            |  2 +
 10 files changed, 230 insertions(+), 63 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e0c8325a39c..94ef9fb44d7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -10476,6 +10476,74 @@ <title>Other Defaults</title>
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-extension-control-path" 
xreflabel="extension_control_path">
+      <term><varname>extension_control_path</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>extension_control_path</varname> configuration 
parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        A path to search for extensions, specifically extension control files
+        (<filename><replaceable>name</replaceable>.control</filename>).  The
+        remaining extension script and secondary control files are then loaded
+        from the same directory where the primary control file was found.
+        See <xref linkend="extend-extensions-files"/> for details.
+       </para>
+
+       <para>
+        The value for <varname>extension_control_path</varname> must be a
+        list of absolute directory paths separated by colons (or semi-colons
+        on Windows).  If a list element starts
+        with the special string <literal>$system</literal>, the
+        compiled-in <productname>PostgreSQL</productname> extension
+        directory is substituted for <literal>$system</literal>; this
+        is where the extensions provided by the standard
+        <productname>PostgreSQL</productname> distribution are installed.
+        (Use <literal>pg_config --sharedir</literal> to find out the name of
+        this directory.) For example:
+<programlisting>
+extension_control_path = 
'/usr/local/share/postgresql/extension:/home/my_project/share/extension:$system'
+</programlisting>
+        or, in a Windows environment:
+<programlisting>
+extension_control_path = 
'C:\tools\postgresql\extension;H:\my_project\share\extension;$system'
+</programlisting>
+        Note that the path elements should typically end in
+        <literal>extension</literal> if the normal installation layouts are
+        followed.  (The value for <literal>$system</literal> already includes
+        the <literal>extension</literal> suffix.)
+       </para>
+
+       <para>
+        The default value for this parameter is
+        <literal>'$system'</literal>. If the value is set to an empty
+        string, the default <literal>'$system'</literal> is also assumed.
+       </para>
+
+       <para>
+        This parameter can be changed at run time by superusers and users
+        with the appropriate <literal>SET</literal> privilege, but a
+        setting done that way will only persist until the end of the
+        client connection, so this method should be reserved for
+        development purposes. The recommended way to set this parameter
+        is in the <filename>postgresql.conf</filename> configuration
+        file.
+       </para>
+
+       <para>
+        Note that if you set this parameter to be able to load extensions from
+        nonstandard locations, you will most likely also need to set <xref
+        linkend="guc-dynamic-library-path"/> to a correspondent location, for
+        example,
+<programlisting>
+extension_control_path = '/usr/local/share/postgresql/extension:$system'
+dynamic_library_path = '/usr/local/lib/postgresql:$libdir'
+</programlisting>
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-gin-fuzzy-search-limit" 
xreflabel="gin_fuzzy_search_limit">
       <term><varname>gin_fuzzy_search_limit</varname> (<type>integer</type>)
       <indexterm>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 218940ee5ce..0a20d06abfb 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -649,6 +649,11 @@ <title>Extension Files</title>
      control file can specify a different directory for the script file(s).
     </para>
 
+    <para>
+     Additional locations for extension control files can be configured using
+     the parameter <xref linkend="guc-extension-control-path"/>.
+    </para>
+
     <para>
      The file format for an extension control file is the same as for the
      <filename>postgresql.conf</filename> file, namely a list of
@@ -669,9 +674,9 @@ <title>Extension Files</title>
        <para>
         The directory containing the extension's <acronym>SQL</acronym> script
         file(s).  Unless an absolute path is given, the name is relative to
-        the installation's <literal>SHAREDIR</literal> directory.  The
-        default behavior is equivalent to specifying
-        <literal>directory = 'extension'</literal>.
+        the installation's <literal>SHAREDIR</literal> directory.  By default,
+        the script files are looked for in the same directory where the
+        control file was found.
        </para>
       </listitem>
      </varlistentry>
@@ -719,8 +724,8 @@ <title>Extension Files</title>
        <para>
         The value of this parameter will be substituted for each occurrence
         of <literal>MODULE_PATHNAME</literal> in the script file(s).  If it is 
not
-        set, no substitution is made.  Typically, this is set to
-        
<literal>$libdir/<replaceable>shared_library_name</replaceable></literal> and
+        set, no substitution is made.  Typically, this is set to just
+        <literal><replaceable>shared_library_name</replaceable></literal> and
         then <literal>MODULE_PATHNAME</literal> is used in <command>CREATE
         FUNCTION</command> commands for C-language functions, so that the 
script
         files do not need to hard-wire the name of the shared library.
@@ -1808,6 +1813,10 @@ <title>Extension Building Infrastructure</title>
     setting <varname>PG_CONFIG</varname> to point to its
     <command>pg_config</command> program, either within the makefile
     or on the <literal>make</literal> command line.
+    You can also select a separate installation directory for your extension
+    by setting the <literal>make</literal> variable <varname>prefix</varname>
+    on the <literal>make</literal> command line.  (But this will then require
+    additional setup to get the server to find the extension there.)
    </para>
 
    <para>
diff --git a/doc/src/sgml/ref/create_extension.sgml 
b/doc/src/sgml/ref/create_extension.sgml
index ca2b80d669c..713abd9c494 100644
--- a/doc/src/sgml/ref/create_extension.sgml
+++ b/doc/src/sgml/ref/create_extension.sgml
@@ -90,8 +90,10 @@ <title>Parameters</title>
        <para>
         The name of the extension to be
         installed. <productname>PostgreSQL</productname> will create the
-        extension using details from the file
-        <literal>SHAREDIR/extension/</literal><replaceable 
class="parameter">extension_name</replaceable><literal>.control</literal>.
+        extension using details from the file <filename><replaceable
+        class="parameter">extension_name</replaceable>.control</filename>,
+        found via the server's extension control path (set by <xref
+        linkend="guc-extension-control-path"/>.)
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index eac3d001211..7c1bffbea07 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -87,9 +87,19 @@ endif # not PGXS
 #
 # In a PGXS build, we cannot use the values inserted into Makefile.global
 # by configure, since the installation tree may have been relocated.
-# Instead get the path values from pg_config.
+# Instead get the path values from pg_config.  But users can specify
+# prefix explicitly, if they want to select their own installation
+# location.
 
-ifndef PGXS
+ifdef PGXS
+# Extension makefiles should set PG_CONFIG, but older ones might not
+ifndef PG_CONFIG
+PG_CONFIG = pg_config
+endif
+endif
+
+# This means: if ((not PGXS) or prefix)
+ifneq (,$(if $(PGXS),,1)$(prefix))
 
 # Note that prefix, exec_prefix, and datarootdir aren't defined in a PGXS 
build;
 # makefiles may only use the derived variables such as bindir.
@@ -147,11 +157,6 @@ localedir := @localedir@
 
 else # PGXS case
 
-# Extension makefiles should set PG_CONFIG, but older ones might not
-ifndef PG_CONFIG
-PG_CONFIG = pg_config
-endif
-
 bindir := $(shell $(PG_CONFIG) --bindir)
 datadir := $(shell $(PG_CONFIG) --sharedir)
 sysconfdir := $(shell $(PG_CONFIG) --sysconfdir)
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index af6bd8ff426..9115a48d25f 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -69,6 +69,9 @@
 #include "utils/varlena.h"
 
 
+/* GUC */
+char      *Extension_control_path;
+
 /* Globally visible state variables */
 bool           creating_extension = false;
 Oid                    CurrentExtensionObject = InvalidOid;
@@ -79,6 +82,7 @@ Oid                   CurrentExtensionObject = InvalidOid;
 typedef struct ExtensionControlFile
 {
        char       *name;                       /* name of the extension */
+       char       *control_dir;        /* directory where control file was 
found */
        char       *directory;          /* directory for script files */
        char       *default_version;    /* default install target version, if 
any */
        char       *module_pathname;    /* string to substitute for
@@ -328,6 +332,12 @@ is_extension_script_filename(const char *filename)
        return (extension != NULL) && (strcmp(extension, ".sql") == 0);
 }
 
+/*
+ * TODO
+ *
+ * This is now only for finding/listing available extensions.  Rewrite to use
+ * path.  See further TODOs below.
+ */
 static char *
 get_extension_control_directory(void)
 {
@@ -341,16 +351,45 @@ get_extension_control_directory(void)
        return result;
 }
 
+/*
+ * Find control file for extension with name in control->name, looking in the
+ * path.  Return the full file name, or NULL if not found.  If found, the
+ * directory is recorded in control->control_dir.
+ */
 static char *
-get_extension_control_filename(const char *extname)
+find_extension_control_filename(ExtensionControlFile *control)
 {
        char            sharepath[MAXPGPATH];
+       char       *system_dir;
+       char       *basename;
+       char       *ecp;
        char       *result;
 
+       Assert(control->name);
+
        get_share_path(my_exec_path, sharepath);
-       result = (char *) palloc(MAXPGPATH);
-       snprintf(result, MAXPGPATH, "%s/extension/%s.control",
-                        sharepath, extname);
+       system_dir = psprintf("%s/extension", sharepath);
+
+       basename = psprintf("%s.control", control->name);
+
+       /*
+        * find_in_path() does nothing if the path value is empty.  This is the
+        * historical behavior for dynamic_library_path, but it makes no sense 
for
+        * extensions.  So in that case, substitute a default value.
+        */
+       ecp = Extension_control_path;
+       if (strlen(ecp) == 0)
+               ecp = "$system";
+       result = find_in_path(basename, Extension_control_path, 
"extension_control_path", "$system", system_dir);
+
+       if (result)
+       {
+               const char *p;
+
+               p = strrchr(result, '/');
+               Assert(p);
+               control->control_dir = pnstrdup(result, p - result);
+       }
 
        return result;
 }
@@ -366,7 +405,7 @@ get_extension_script_directory(ExtensionControlFile 
*control)
         * installation's share directory.
         */
        if (!control->directory)
-               return get_extension_control_directory();
+               return pstrdup(control->control_dir);
 
        if (is_absolute_path(control->directory))
                return pstrdup(control->directory);
@@ -444,27 +483,25 @@ parse_extension_control_file(ExtensionControlFile 
*control,
        if (version)
                filename = get_extension_aux_control_filename(control, version);
        else
-               filename = get_extension_control_filename(control->name);
+               filename = find_extension_control_filename(control);
+
+       if (!filename)
+       {
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("extension \"%s\" is not available", 
control->name),
+                                errhint("The extension must first be installed 
on the system where PostgreSQL is running.")));
+       }
 
        if ((file = AllocateFile(filename, "r")) == NULL)
        {
-               if (errno == ENOENT)
+               /* no complaint for missing auxiliary file */
+               if (errno == ENOENT && version)
                {
-                       /* no complaint for missing auxiliary file */
-                       if (version)
-                       {
-                               pfree(filename);
-                               return;
-                       }
-
-                       /* missing control file indicates extension is not 
installed */
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("extension \"%s\" is not 
available", control->name),
-                                        errdetail("Could not open extension 
control file \"%s\": %m.",
-                                                          filename),
-                                        errhint("The extension must first be 
installed on the system where PostgreSQL is running.")));
+                       pfree(filename);
+                       return;
                }
+
                ereport(ERROR,
                                (errcode_for_file_access(),
                                 errmsg("could not open extension control file 
\"%s\": %m",
@@ -2114,6 +2151,8 @@ RemoveExtensionById(Oid extId)
  * The system view pg_available_extensions provides a user interface to this
  * SRF, adding information about whether the extensions are installed in the
  * current DB.
+ *
+ * TODO: make aware of path
  */
 Datum
 pg_available_extensions(PG_FUNCTION_ARGS)
@@ -2194,6 +2233,8 @@ pg_available_extensions(PG_FUNCTION_ARGS)
  * The system view pg_available_extension_versions provides a user interface
  * to this SRF, adding information about which versions are installed in the
  * current DB.
+ *
+ * TODO: make aware of path
  */
 Datum
 pg_available_extension_versions(PG_FUNCTION_ARGS)
@@ -2366,6 +2407,8 @@ get_available_versions_for_extension(ExtensionControlFile 
*pcontrol,
  * directory.  That's not a bulletproof check, since the file might be
  * invalid, but this is only used for hints so it doesn't have to be 100%
  * right.
+ *
+ * TODO: make aware of path
  */
 bool
 extension_file_exists(const char *extensionName)
@@ -2445,6 +2488,8 @@ convert_requires_to_datum(List *requires)
 /*
  * This function reports the version update paths that exist for the
  * specified extension.
+ *
+ * TODO: make aware of path
  */
 Datum
 pg_extension_update_paths(PG_FUNCTION_ARGS)
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index 8e81ecc7491..2372d0944c0 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -71,8 +71,7 @@ static void incompatible_module_error(const char *libname,
                                                                          const 
Pg_magic_struct *module_magic_data) pg_attribute_noreturn();
 static char *expand_dynamic_library_name(const char *name);
 static void check_restricted_library_name(const char *name);
-static char *substitute_libpath_macro(const char *name);
-static char *find_in_dynamic_libpath(const char *basename);
+static char *substitute_path_macro(const char *str, const char *macro, const 
char *value);
 
 /* Magic structure that module needs to match to be accepted */
 static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
@@ -398,7 +397,7 @@ incompatible_module_error(const char *libname,
 /*
  * If name contains a slash, check if the file exists, if so return
  * the name.  Else (no slash) try to expand using search path (see
- * find_in_dynamic_libpath below); if that works, return the fully
+ * find_in_path below); if that works, return the fully
  * expanded file name.  If the previous failed, append DLSUFFIX and
  * try again.  If all fails, just return the original name.
  *
@@ -413,17 +412,25 @@ expand_dynamic_library_name(const char *name)
 
        Assert(name);
 
+       /*
+        * If the value starts with "$libdir/", strip that.  This is because 
many
+        * extensions have hardcoded '$libdir/foo' as their library name, which
+        * prevents using the path.
+        */
+       if (strncmp(name, "$libdir/", 8) == 0)
+               name += 8;
+
        have_slash = (first_dir_separator(name) != NULL);
 
        if (!have_slash)
        {
-               full = find_in_dynamic_libpath(name);
+               full = find_in_path(name, Dynamic_library_path, 
"dynamic_library_path", "$libdir", pkglib_path);
                if (full)
                        return full;
        }
        else
        {
-               full = substitute_libpath_macro(name);
+               full = substitute_path_macro(name, "$libdir", pkglib_path);
                if (pg_file_exists(full))
                        return full;
                pfree(full);
@@ -433,14 +440,14 @@ expand_dynamic_library_name(const char *name)
 
        if (!have_slash)
        {
-               full = find_in_dynamic_libpath(new);
+               full = find_in_path(new, Dynamic_library_path, 
"dynamic_library_path", "$libdir", pkglib_path);
                pfree(new);
                if (full)
                        return full;
        }
        else
        {
-               full = substitute_libpath_macro(new);
+               full = substitute_path_macro(new, "$libdir", pkglib_path);
                pfree(new);
                if (pg_file_exists(full))
                        return full;
@@ -475,47 +482,60 @@ check_restricted_library_name(const char *name)
  * Result is always freshly palloc'd.
  */
 static char *
-substitute_libpath_macro(const char *name)
+substitute_path_macro(const char *str, const char *macro, const char *value)
 {
        const char *sep_ptr;
 
-       Assert(name != NULL);
+       Assert(str != NULL);
+       Assert(macro[0] == '$');
 
-       /* Currently, we only recognize $libdir at the start of the string */
-       if (name[0] != '$')
-               return pstrdup(name);
+       /* Currently, we only recognize $macro at the start of the string */
+       if (str[0] != '$')
+               return pstrdup(str);
 
-       if ((sep_ptr = first_dir_separator(name)) == NULL)
-               sep_ptr = name + strlen(name);
+       if ((sep_ptr = first_dir_separator(str)) == NULL)
+               sep_ptr = str + strlen(str);
 
-       if (strlen("$libdir") != sep_ptr - name ||
-               strncmp(name, "$libdir", strlen("$libdir")) != 0)
+       if (strlen(macro) != sep_ptr - str ||
+               strncmp(str, macro, strlen(macro)) != 0)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_NAME),
-                                errmsg("invalid macro name in dynamic library 
path: %s",
-                                               name)));
+                                errmsg("invalid macro name in path: %s",
+                                               str)));
 
-       return psprintf("%s%s", pkglib_path, sep_ptr);
+       return psprintf("%s%s", value, sep_ptr);
 }
 
 
 /*
  * Search for a file called 'basename' in the colon-separated search
- * path Dynamic_library_path.  If the file is found, the full file name
+ * path given.  If the file is found, the full file name
  * is returned in freshly palloc'd memory.  If the file is not found,
  * return NULL.
+ *
+ * path_param is the name of the parameter that path came from, for error
+ * messages.
+ *
+ * macro and macro_val allow substituting a macro; see
+ * substitute_path_macro().
  */
-static char *
-find_in_dynamic_libpath(const char *basename)
+char *
+find_in_path(const char *basename, const char *path, const char *path_param,
+                        const char *macro, const char *macro_val)
 {
        const char *p;
        size_t          baselen;
 
        Assert(basename != NULL);
        Assert(first_dir_separator(basename) == NULL);
-       Assert(Dynamic_library_path != NULL);
+       Assert(path != NULL);
+       Assert(path_param != NULL);
+
+       p = path;
 
-       p = Dynamic_library_path;
+       /*
+        * If the path variable is empty, don't do a path search.
+        */
        if (strlen(p) == 0)
                return NULL;
 
@@ -532,7 +552,7 @@ find_in_dynamic_libpath(const char *basename)
                if (piece == p)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_NAME),
-                                        errmsg("zero-length component in 
parameter \"dynamic_library_path\"")));
+                                        errmsg("zero-length component in 
parameter \"%s\"", path_param)));
 
                if (piece == NULL)
                        len = strlen(p);
@@ -542,7 +562,7 @@ find_in_dynamic_libpath(const char *basename)
                piece = palloc(len + 1);
                strlcpy(piece, p, len + 1);
 
-               mangled = substitute_libpath_macro(piece);
+               mangled = substitute_path_macro(piece, macro, macro_val);
                pfree(piece);
 
                canonicalize_path(mangled);
@@ -551,13 +571,13 @@ find_in_dynamic_libpath(const char *basename)
                if (!is_absolute_path(mangled))
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_NAME),
-                                        errmsg("component in parameter 
\"dynamic_library_path\" is not an absolute path")));
+                                        errmsg("component in parameter \"%s\" 
is not an absolute path", path_param)));
 
                full = palloc(strlen(mangled) + 1 + baselen + 1);
                sprintf(full, "%s/%s", mangled, basename);
                pfree(mangled);
 
-               elog(DEBUG3, "find_in_dynamic_libpath: trying \"%s\"", full);
+               elog(DEBUG3, "%s: trying \"%s\"", __func__, full);
 
                if (pg_file_exists(full))
                        return full;
diff --git a/src/backend/utils/misc/guc_tables.c 
b/src/backend/utils/misc/guc_tables.c
index 8cf1afbad20..5a280f62b77 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -39,6 +39,7 @@
 #include "catalog/namespace.h"
 #include "catalog/storage.h"
 #include "commands/async.h"
+#include "commands/extension.h"
 #include "commands/event_trigger.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
@@ -4252,6 +4253,18 @@ struct config_string ConfigureNamesString[] =
                NULL, NULL, NULL
        },
 
+       {
+               {"extension_control_path", PGC_SUSET, CLIENT_CONN_OTHER,
+                       gettext_noop("Sets the path for extension control 
files."),
+                       gettext_noop("The remaining extension script and 
secondary control files are then loaded "
+                                                "from the same directory where 
the primary control file was found."),
+                       GUC_SUPERUSER_ONLY
+               },
+               &Extension_control_path,
+               "$system",
+               NULL, NULL, NULL
+       },
+
        {
                {"krb_server_keyfile", PGC_SIGHUP, CONN_AUTH_AUTH,
                        gettext_noop("Sets the location of the Kerberos server 
key file."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample 
b/src/backend/utils/misc/postgresql.conf.sample
index a2ac7575ca7..83e72b33fb7 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -774,6 +774,7 @@
 # - Other Defaults -
 
 #dynamic_library_path = '$libdir'
+#extension_control_path = '$system'
 #gin_fuzzy_search_limit = 0
 
 
diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h
index c6f3f867eb7..fe8a97570ea 100644
--- a/src/include/commands/extension.h
+++ b/src/include/commands/extension.h
@@ -17,6 +17,8 @@
 #include "catalog/objectaddress.h"
 #include "parser/parse_node.h"
 
+/* GUC */
+extern PGDLLIMPORT char *Extension_control_path;
 
 /*
  * creating_extension is only true while running a CREATE EXTENSION or ALTER
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 1e3795de4a8..2930b61cee5 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -740,6 +740,8 @@ extern bool CheckFunctionValidatorAccess(Oid validatorOid, 
Oid functionOid);
  */
 extern PGDLLIMPORT char *Dynamic_library_path;
 
+extern char *find_in_path(const char *basename, const char *path, const char 
*path_param,
+                                                 const char *macro, const char 
*macro_val);
 extern void *load_external_function(const char *filename, const char *funcname,
                                                                        bool 
signalNotFound, void **filehandle);
 extern void *lookup_external_function(void *filehandle, const char *funcname);

base-commit: 71cb352904c1833fe067d6f191269710fe2ca06f
-- 
2.47.1

Reply via email to