Hi,

Sergey Muraviov <sergey.k.murav...@gmail.com> 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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to