Skip to content

[PATCH v1] extension_control_path #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions doc/src/sgml/config.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -10539,6 +10539,74 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</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>
Expand Down
19 changes: 14 additions & 5 deletions doc/src/sgml/extend.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,11 @@ RETURNS anycompatible AS ...
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
Expand All @@ -669,9 +674,9 @@ RETURNS anycompatible AS ...
<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>
Expand Down Expand Up @@ -719,8 +724,8 @@ RETURNS anycompatible AS ...
<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.
Expand Down Expand Up @@ -1804,6 +1809,10 @@ include $(PGXS)
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>
Expand Down
6 changes: 4 additions & 2 deletions doc/src/sgml/ref/create_extension.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ CREATE EXTENSION [ IF NOT EXISTS ] <replaceable class="parameter">extension_name
<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>
Expand Down
19 changes: 12 additions & 7 deletions src/Makefile.global.in
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
87 changes: 66 additions & 21 deletions src/backend/commands/extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@
#include "utils/varlena.h"


/* GUC */
char *Extension_control_path;

/* Globally visible state variables */
bool creating_extension = false;
Oid CurrentExtensionObject = InvalidOid;
Expand All @@ -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
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading