Hi,
Sergey Muraviov <[email protected]> writes:
> Now patch applies cleanly and works. :-)
Cool ;-)
> But I have some notes:
>
> 1. There is an odd underscore character in functions
> find_in_extension_control_path and list_extension_control_paths:
> \"extension_control__path\""
Fixed in the new version of the patch, attached.
> 2. If we have several versions of one extension in different directories
> (which are listed in extension_control_path parameter) then we
> get strange output from pg_available_extensions and
> pg_available_extension_versions views (Information about extension, whose
> path is at the beginning of the list, is duplicated). And only one version
> of the extension can be created.
Fixed.
> 3. It would be fine to see an extension control path
> in pg_available_extensions and pg_available_extension_versions views (in
> separate column or within of extension name).
I think the on-disk location is an implementation detail and decided in
the attached version not to change those system view definitions.
> 4. Perhaps the CREATE EXTENSION command should be improved to allow
> creation of the required version of the extension.
> So we can use different versions of extensions in different databases.
Fixed in the attached.
I also fixed ALTER EXTENSION UPDATE to search for udpate scripts in the
same directory where the main control file is found, but I suspect this
part requires more thinking.
When we ALTER EXTENSION UPDATE we might now have several places where we
find extname.control files, with possibly differents default_version
properties.
In the attached, we select the directory containing the control file
where default_version matches the already installed extension version.
That matches with a model where the new version of the extension changes
the default_version in an auxiliary file.
We might want to instead match on the default_version in the control
file to match with the new version we are asked to upgrade to.
Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 5773,5778 **** SET XML OPTION { DOCUMENT | CONTENT };
--- 5773,5827 ----
<variablelist>
+ <varlistentry id="guc-extension-control-path" xreflabel="extension_control_path">
+ <term><varname>extension_control_path</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>extension_control_path</> configuration parameter</primary>
+ </indexterm>
+ <indexterm><primary>extension packaging</></>
+ <listitem>
+ <para>
+ The command <command>CREATE EXTENSION</> searches for the extension
+ control file in order to install it. The value
+ for <varname>extension_control_path</varname> is used to search for
+ the <literal>name.control</literal> files.
+ </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>$extdir</literal>, the
+ compiled-in <productname>PostgreSQL</productname> package extension
+ directory is substituted for <literal>$extdir</literal>; this is where
+ the extensions provided by the standard
+ <productname>PostgreSQL</productname> distribution are installed.
+ (Use <literal>pg_config --extdir</literal> to find out the name of
+ this directory.) For example:
+ <programlisting>
+ extension_control_path = '/usr/local/postgresql/extension:/home/my_project:$extdir'
+ </programlisting>
+ or, in a Windows environment:
+ <programlisting>
+ extension_control_path = 'C:\tools\postgresql;H:\my_project\lib;$extdir'
+ </programlisting>
+ </para>
+
+ <para>
+ The default value for this parameter is <literal>'$extdir'</literal>.
+ </para>
+
+ <para>
+ This parameter can be changed at run time by superusers, 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>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-dynamic-library-path" xreflabel="dynamic_library_path">
<term><varname>dynamic_library_path</varname> (<type>string</type>)</term>
<indexterm>
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 25,30 ****
--- 25,31 ----
#include <dirent.h>
#include <limits.h>
+ #include <sys/stat.h>
#include <unistd.h>
#include "access/htup_details.h"
***************
*** 60,71 ****
--- 61,76 ----
bool creating_extension = false;
Oid CurrentExtensionObject = InvalidOid;
+ /* GUC extension_control_path */
+ char *Extension_control_path;
+
/*
* Internal data structure to hold the results of parsing a control file
*/
typedef struct ExtensionControlFile
{
char *name; /* name of the extension */
+ char *filename; /* full path of the extension control file */
char *directory; /* directory for script files */
char *default_version; /* default install target version, if any */
char *module_pathname; /* string to substitute for MODULE_PATHNAME */
***************
*** 342,397 **** is_extension_script_filename(const char *filename)
return (extension != NULL) && (strcmp(extension, ".sql") == 0);
}
static char *
! get_extension_control_directory(void)
{
char sharepath[MAXPGPATH];
! char *result;
get_share_path(my_exec_path, sharepath);
! result = (char *) palloc(MAXPGPATH);
! snprintf(result, MAXPGPATH, "%s/extension", sharepath);
! return result;
}
static char *
! get_extension_control_filename(const char *extname)
{
- char sharepath[MAXPGPATH];
char *result;
! get_share_path(my_exec_path, sharepath);
result = (char *) palloc(MAXPGPATH);
! snprintf(result, MAXPGPATH, "%s/extension/%s.control",
! sharepath, extname);
return result;
}
static char *
get_extension_script_directory(ExtensionControlFile *control)
{
! char sharepath[MAXPGPATH];
char *result;
/*
* The directory parameter can be omitted, absolute, or relative to the
! * installation's share directory.
*/
if (!control->directory)
! return get_extension_control_directory();
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);
return result;
}
static char *
get_extension_aux_control_filename(ExtensionControlFile *control,
const char *version)
--- 347,604 ----
return (extension != NULL) && (strcmp(extension, ".sql") == 0);
}
+ /*
+ * Substitute for any macros appearing in the given string.
+ * Result is always freshly palloc'd.
+ */
static char *
! substitute_extension_control_path_macro(const char *name)
{
char sharepath[MAXPGPATH];
! const char *sep_ptr;
!
! AssertArg(name != NULL);
!
! /* Currently, we only recognize $extdir at the start of the string */
! if (name[0] != '$')
! return pstrdup(name);
!
! if ((sep_ptr = first_dir_separator(name)) == NULL)
! sep_ptr = name + strlen(name);
!
! if (strlen("$extdir") != sep_ptr - name ||
! strncmp(name, "$extdir", strlen("$extdir")) != 0)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_NAME),
! errmsg("invalid macro name in extension control path: %s",
! name)));
get_share_path(my_exec_path, sharepath);
! return psprintf("%s/extension%s", sharepath, sep_ptr);
! }
! /*
! * XXX: move that function to somewhere both src/backend/utils/fmgr/dfmgr.c
! * and this file can use it.
! */
! static bool
! file_exists(const char *name)
! {
! struct stat st;
!
! AssertArg(name != NULL);
!
! if (stat(name, &st) == 0)
! return S_ISDIR(st.st_mode) ? false : true;
! else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not access file \"%s\": %m", name)));
!
! return false;
}
+ /*
+ * Search for a file called 'basename' in the colon-separated search
+ * path Extension_control_path. If the file is found, the full file name
+ * is returned in freshly palloc'd memory. If the file is not found,
+ * return NULL.
+ */
static char *
! find_in_extension_control_path(const char *basename)
! {
! const char *p;
! size_t baselen;
!
! AssertArg(basename != NULL);
! AssertArg(first_dir_separator(basename) == NULL);
! AssertState(Extension_control_path != NULL);
!
! p = Extension_control_path;
! if (strlen(p) == 0)
! return NULL;
!
! baselen = strlen(basename);
!
! for (;;)
! {
! size_t len;
! char *piece;
! char *mangled;
! char *full;
!
! piece = first_path_var_separator(p);
! if (piece == p)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_NAME),
! errmsg("zero-length component in parameter \"extension_control_path\"")));
!
! if (piece == NULL)
! len = strlen(p);
! else
! len = piece - p;
!
! piece = palloc(len + 1);
! strlcpy(piece, p, len + 1);
!
! mangled = substitute_extension_control_path_macro(piece);
! pfree(piece);
!
! canonicalize_path(mangled);
!
! /* only absolute paths */
! if (!is_absolute_path(mangled))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_NAME),
! errmsg("component in parameter \"extension_control_path\" is not an absolute path")));
!
! /* don't forget to count for ".control", 8 chars */
! full = palloc(strlen(mangled) + 1 + baselen + 8 + 1);
! sprintf(full, "%s/%s.control", mangled, basename);
! pfree(mangled);
!
! elog(DEBUG3, "find_in_extension_control_path: trying \"%s\"", full);
!
! if (file_exists(full))
! return full;
!
! pfree(full);
!
! if (p[len] == '\0')
! break;
! else
! p += len + 1;
! }
!
! return NULL;
! }
!
! /*
! * Return the current list of extension_control_path directories, with $extdir
! * macro expanded.
! */
! static List *
! list_extension_control_paths()
! {
! List *paths = NIL;
! const char *p;
!
! AssertState(Extension_control_path != NULL);
!
! p = Extension_control_path;
! if (strlen(p) == 0)
! return NULL;
!
! for (;;)
! {
! size_t len;
! char *piece;
! char *mangled;
!
! piece = first_path_var_separator(p);
! if (piece == p)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_NAME),
! errmsg("zero-length component in parameter \"extension_control_path\"")));
!
! if (piece == NULL)
! len = strlen(p);
! else
! len = piece - p;
!
! piece = palloc(len + 1);
! strlcpy(piece, p, len + 1);
!
! mangled = substitute_extension_control_path_macro(piece);
! pfree(piece);
!
! canonicalize_path(mangled);
!
! /* only absolute paths */
! if (!is_absolute_path(mangled))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_NAME),
! errmsg("component in parameter \"extension_control_path\" is not an absolute path")));
!
! paths = lappend(paths, mangled);
!
! if (p[len] == '\0')
! break;
! else
! p += len + 1;
! }
!
! return paths;
! }
!
! /*
! * When this function is called, the control file has already been opened and
! * its true name is registered in control->filename. We don't need to find the
! * control file in extension_control_path anymore.
! */
! static char *
! get_extension_control_directory(ExtensionControlFile *control)
! {
! char *filename = pstrdup(control->filename);
!
! get_parent_directory(filename);
!
! return filename;
! }
!
! /*
! * We need to either open directory/extname.control or go find the control
! * file for extname in extension_control_path, depending on being given a
! * directory where to look into.
! */
! static char *
! get_extension_control_filename(const char *extname, const char *directory)
{
char *result;
! if (directory == NULL)
! return find_in_extension_control_path(extname);
!
result = (char *) palloc(MAXPGPATH);
! snprintf(result, MAXPGPATH, "%s/%s.control", directory, extname);
return result;
}
+ /*
+ * When this function is called, the control file has already been opened and
+ * its true name is registered in control->filename. We don't need to find the
+ * control file in extension_control_path anymore.
+ */
static char *
get_extension_script_directory(ExtensionControlFile *control)
{
! char *directory;
char *result;
/*
* The directory parameter can be omitted, absolute, or relative to the
! * extension's main control file's parent directory.
*/
if (!control->directory)
! return get_extension_control_directory(control);
if (is_absolute_path(control->directory))
return pstrdup(control->directory);
! /* control->directory is relative to control->filename parent's directory */
! directory = get_extension_control_directory(control);
result = (char *) palloc(MAXPGPATH);
! snprintf(result, MAXPGPATH, "%s/%s", directory, control->directory);
return result;
}
+ /*
+ * When this function is called, the control file has already been opened and
+ * its true name is registered in control->filename. We don't need to find the
+ * control file in extension_control_path anymore.
+ */
static char *
get_extension_aux_control_filename(ExtensionControlFile *control,
const char *version)
***************
*** 410,415 **** get_extension_aux_control_filename(ExtensionControlFile *control,
--- 617,627 ----
return result;
}
+ /*
+ * When this function is called, the control file has already been opened and
+ * its true name is registered in control->filename. We don't need to find the
+ * control file in extension_control_path anymore.
+ */
static char *
get_extension_script_filename(ExtensionControlFile *control,
const char *from_version, const char *version)
***************
*** 444,450 **** get_extension_script_filename(ExtensionControlFile *control,
*/
static void
parse_extension_control_file(ExtensionControlFile *control,
! const char *version)
{
char *filename;
FILE *file;
--- 656,663 ----
*/
static void
parse_extension_control_file(ExtensionControlFile *control,
! const char *version,
! const char *directory)
{
char *filename;
FILE *file;
***************
*** 458,464 **** parse_extension_control_file(ExtensionControlFile *control,
if (version)
filename = get_extension_aux_control_filename(control, version);
else
! filename = get_extension_control_filename(control->name);
if ((file = AllocateFile(filename, "r")) == NULL)
{
--- 671,677 ----
if (version)
filename = get_extension_aux_control_filename(control, version);
else
! filename = get_extension_control_filename(control->name, directory);
if ((file = AllocateFile(filename, "r")) == NULL)
{
***************
*** 482,487 **** parse_extension_control_file(ExtensionControlFile *control,
--- 695,702 ----
FreeFile(file);
+ control->filename = pstrdup(filename);
+
/*
* Convert the ConfigVariable list into ExtensionControlFile entries.
*/
***************
*** 578,586 **** parse_extension_control_file(ExtensionControlFile *control,
/*
* Read the primary control file for the specified extension.
*/
static ExtensionControlFile *
! read_extension_control_file(const char *extname)
{
ExtensionControlFile *control;
--- 793,804 ----
/*
* Read the primary control file for the specified extension.
+ *
+ * When directory is not NULL, then instead of trying to find the extension
+ * control file in extension_control_path, we just open the file in directory.
*/
static ExtensionControlFile *
! read_extension_control_file(const char *extname, const char *directory)
{
ExtensionControlFile *control;
***************
*** 596,602 **** read_extension_control_file(const char *extname)
/*
* Parse the primary control file.
*/
! parse_extension_control_file(control, NULL);
return control;
}
--- 814,857 ----
/*
* Parse the primary control file.
*/
! parse_extension_control_file(control, NULL, directory);
!
! return control;
! }
!
! /*
! * Find the control file where default_version is the same as given version.
! * To be able to check that we've found the right control file, we need to
! * parse it.
! */
! static ExtensionControlFile *
! find_extension_control_file_for_version(const char *extname, const char *version)
! {
! ExtensionControlFile *control = NULL;
! List *extension_control_paths = list_extension_control_paths();
! ListCell *lc;
!
! foreach(lc, extension_control_paths)
! {
! char *location = (char *) lfirst(lc);
! char *path = get_extension_control_filename(extname, location);
!
! if (access(path, R_OK) == 0)
! {
! control = read_extension_control_file(extname, location);
!
! if (strcmp(control->default_version, version) == 0)
! break;
! else
! control = NULL;
! }
! }
! if (control == NULL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("invalid extension version: \"%s\"", version),
! errdetail("Extension '%s' has no control file for version '%s'.",
! extname, version)));
return control;
}
***************
*** 622,628 **** read_extension_aux_control_file(const ExtensionControlFile *pcontrol,
/*
* Parse the auxiliary control file, overwriting struct fields
*/
! parse_extension_control_file(acontrol, version);
return acontrol;
}
--- 877,883 ----
/*
* Parse the auxiliary control file, overwriting struct fields
*/
! parse_extension_control_file(acontrol, version, NULL);
return acontrol;
}
***************
*** 1233,1239 **** CreateExtension(CreateExtensionStmt *stmt)
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
*/
! pcontrol = read_extension_control_file(stmt->extname);
/*
* Read the statement option list
--- 1488,1494 ----
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
*/
! pcontrol = read_extension_control_file(stmt->extname, NULL);
/*
* Read the statement option list
***************
*** 1287,1292 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1542,1555 ----
check_valid_version_name(versionName);
/*
+ * We might need to read another control file given a version number that
+ * is not the default one.
+ */
+ if (strcmp(versionName, pcontrol->default_version) != 0)
+ pcontrol =
+ find_extension_control_file_for_version(stmt->extname, versionName);
+
+ /*
* Determine the (unpackaged) version to update from, if any, and then
* figure out what sequence of update scripts we need to apply.
*/
***************
*** 1623,1675 **** RemoveExtensionById(Oid extId)
}
/*
! * This function lists the available extensions (one row per primary control
! * file in the control directory). We parse each control file and report the
! * interesting fields.
! *
! * 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.
*/
! Datum
! pg_available_extensions(PG_FUNCTION_ARGS)
{
- ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
- TupleDesc tupdesc;
- Tuplestorestate *tupstore;
- MemoryContext per_query_ctx;
- MemoryContext oldcontext;
- char *location;
DIR *dir;
struct dirent *de;
- /* check to see if caller supports us returning a tuplestore */
- if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that cannot accept a set")));
- if (!(rsinfo->allowedModes & SFRM_Materialize))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("materialize mode required, but it is not " \
- "allowed in this context")));
-
- /* Build a tuple descriptor for our result type */
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- elog(ERROR, "return type must be a row type");
-
- /* Build tuplestore to hold the result rows */
- per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
- oldcontext = MemoryContextSwitchTo(per_query_ctx);
-
- tupstore = tuplestore_begin_heap(true, false, work_mem);
- rsinfo->returnMode = SFRM_Materialize;
- rsinfo->setResult = tupstore;
- rsinfo->setDesc = tupdesc;
-
- MemoryContextSwitchTo(oldcontext);
-
- location = get_extension_control_directory();
dir = AllocateDir(location);
/*
--- 1886,1901 ----
}
/*
! * Add available extensions informations (from the control files found in
! * location) to the pg_available_extension tuple store.
*/
! static void
! list_available_extensions(TupleDesc tupdesc, Tuplestorestate *tupstore,
! const char *location)
{
DIR *dir;
struct dirent *de;
dir = AllocateDir(location);
/*
***************
*** 1700,1706 **** pg_available_extensions(PG_FUNCTION_ARGS)
if (strstr(extname, "--"))
continue;
! control = read_extension_control_file(extname);
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
--- 1926,1932 ----
if (strstr(extname, "--"))
continue;
! control = read_extension_control_file(extname, location);
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
***************
*** 1721,1729 **** pg_available_extensions(PG_FUNCTION_ARGS)
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
-
FreeDir(dir);
}
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
--- 1947,2008 ----
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
FreeDir(dir);
}
+ }
+
+ /*
+ * This function lists the available extensions (one row per primary control
+ * file in the control directory). We parse each control file and report the
+ * interesting fields.
+ *
+ * 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.
+ */
+ Datum
+ pg_available_extensions(PG_FUNCTION_ARGS)
+ {
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ TupleDesc tupdesc;
+ Tuplestorestate *tupstore;
+ MemoryContext per_query_ctx;
+ MemoryContext oldcontext;
+ List *extension_control_paths = list_extension_control_paths();
+ ListCell *lc;
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not " \
+ "allowed in this context")));
+
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ /* Build tuplestore to hold the result rows */
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+ tupstore = tuplestore_begin_heap(true, false, work_mem);
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ foreach(lc, extension_control_paths)
+ {
+ char *location = (char *) lfirst(lc);
+
+ list_available_extensions(tupdesc, tupstore, location);
+ }
/* clean up and return the tuplestore */
tuplestore_donestoring(tupstore);
***************
*** 1748,1754 **** pg_available_extension_versions(PG_FUNCTION_ARGS)
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
! char *location;
DIR *dir;
struct dirent *de;
--- 2027,2034 ----
Tuplestorestate *tupstore;
MemoryContext per_query_ctx;
MemoryContext oldcontext;
! List *extension_control_paths = list_extension_control_paths();
! ListCell *lc;
DIR *dir;
struct dirent *de;
***************
*** 1778,1820 **** pg_available_extension_versions(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(oldcontext);
! location = get_extension_control_directory();
! dir = AllocateDir(location);
!
! /*
! * If the control directory doesn't exist, we want to silently return an
! * empty set. Any other error will be reported by ReadDir.
! */
! if (dir == NULL && errno == ENOENT)
! {
! /* do nothing */
! }
! else
{
! while ((de = ReadDir(dir, location)) != NULL)
{
! ExtensionControlFile *control;
! char *extname;
! if (!is_extension_control_filename(de->d_name))
! continue;
! /* extract extension name from 'name.control' filename */
! extname = pstrdup(de->d_name);
! *strrchr(extname, '.') = '\0';
! /* ignore it if it's an auxiliary control file */
! if (strstr(extname, "--"))
! continue;
! /* read the control file */
! control = read_extension_control_file(extname);
! /* scan extension's script directory for install scripts */
! get_available_versions_for_extension(control, tupstore, tupdesc);
! }
! FreeDir(dir);
}
/* clean up and return the tuplestore */
--- 2058,2104 ----
MemoryContextSwitchTo(oldcontext);
! foreach(lc, extension_control_paths)
{
! char *location = (char *) lfirst(lc);
!
! dir = AllocateDir(location);
!
! /*
! * If the control directory doesn't exist, we want to silently return an
! * empty set. Any other error will be reported by ReadDir.
! */
! if (dir == NULL && errno == ENOENT)
{
! /* do nothing */
! }
! else
! {
! while ((de = ReadDir(dir, location)) != NULL)
! {
! ExtensionControlFile *control;
! char *extname;
! if (!is_extension_control_filename(de->d_name))
! continue;
! /* extract extension name from 'name.control' filename */
! extname = pstrdup(de->d_name);
! *strrchr(extname, '.') = '\0';
! /* ignore it if it's an auxiliary control file */
! if (strstr(extname, "--"))
! continue;
! /* read the control file */
! control = read_extension_control_file(extname, location);
! /* scan extension's script directory for install scripts */
! get_available_versions_for_extension(control, tupstore, tupdesc);
! }
! FreeDir(dir);
! }
}
/* clean up and return the tuplestore */
***************
*** 1972,1978 **** pg_extension_update_paths(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(oldcontext);
/* Read the extension's control file */
! control = read_extension_control_file(NameStr(*extname));
/* Extract the version update graph from the script directory */
evi_list = get_ext_ver_list(control);
--- 2256,2262 ----
MemoryContextSwitchTo(oldcontext);
/* Read the extension's control file */
! control = read_extension_control_file(NameStr(*extname), NULL);
/* Extract the version update graph from the script directory */
evi_list = get_ext_ver_list(control);
***************
*** 2657,2663 **** ExecAlterExtensionStmt(AlterExtensionStmt *stmt)
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
*/
! control = read_extension_control_file(stmt->extname);
/*
* Read the statement option list
--- 2941,2948 ----
* any non-ASCII data, so there is no need to worry about encoding at this
* point.
*/
! control = find_extension_control_file_for_version(stmt->extname,
! oldVersionName);
/*
* Read the statement option list
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 32,37 ****
--- 32,38 ----
#include "access/xact.h"
#include "catalog/namespace.h"
#include "commands/async.h"
+ #include "commands/extension.h"
#include "commands/prepare.h"
#include "commands/vacuum.h"
#include "commands/variable.h"
***************
*** 2726,2731 **** static struct config_string ConfigureNamesString[] =
--- 2727,2745 ----
},
{
+ {"extension_control_path", PGC_SUSET, CLIENT_CONN_OTHER,
+ gettext_noop("Sets the path for extension control files."),
+ gettext_noop("If an extension control file needs to be opened "
+ "the system will search this path for "
+ "the specified file."),
+ GUC_SUPERUSER_ONLY
+ },
+ &Extension_control_path,
+ "$extdir",
+ NULL, NULL, NULL
+ },
+
+ {
{"krb_server_keyfile", PGC_SIGHUP, CONN_AUTH_SECURITY,
gettext_noop("Sets the location of the Kerberos server key file."),
NULL,
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 537,542 ****
--- 537,543 ----
# - Other Defaults -
#dynamic_library_path = '$libdir'
+ #extension_control_path = '$extdir'
#local_preload_libraries = ''
#session_preload_libraries = ''
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 146,151 **** show_includedir_server(bool all)
--- 146,163 ----
}
static void
+ show_extdir(bool all)
+ {
+ char path[MAXPGPATH];
+
+ if (all)
+ printf("EXTDIR = ");
+ get_share_path(mypath, path);
+ cleanup_path(path);
+ printf("%s/extension\n", path);
+ }
+
+ static void
show_libdir(bool all)
{
char path[MAXPGPATH];
***************
*** 402,407 **** static const InfoItem info_items[] = {
--- 414,420 ----
{"--pkgincludedir", show_pkgincludedir},
{"--includedir-server", show_includedir_server},
{"--libdir", show_libdir},
+ {"--extdir", show_extdir},
{"--pkglibdir", show_pkglibdir},
{"--localedir", show_localedir},
{"--mandir", show_mandir},
***************
*** 436,441 **** help(void)
--- 449,455 ----
" interfaces\n"));
printf(_(" --pkgincludedir show location of other C header files\n"));
printf(_(" --includedir-server show location of C header files for the server\n"));
+ printf(_(" --extdir show location of extension control files\n"));
printf(_(" --libdir show location of object code libraries\n"));
printf(_(" --pkglibdir show location of dynamically loadable modules\n"));
printf(_(" --localedir show location of locale support files\n"));
*** a/src/include/commands/extension.h
--- b/src/include/commands/extension.h
***************
*** 26,31 ****
--- 26,33 ----
extern bool creating_extension;
extern Oid CurrentExtensionObject;
+ /* GUC extension_control_path */
+ extern char *Extension_control_path;
extern Oid CreateExtension(CreateExtensionStmt *stmt);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers