Re: extension_control_path

Поиск
Список
Период
Сортировка
От Dimitri Fontaine
Тема Re: extension_control_path
Дата
Msg-id m238iq1mdd.fsf@2ndQuadrant.fr
обсуждение исходный текст
Ответ на Re: extension_control_path  (Peter Eisentraut <peter_e@gmx.net>)
Ответы Re: extension_control_path  (Peter Eisentraut <peter_e@gmx.net>)
Список pgsql-hackers
Peter Eisentraut <peter_e@gmx.net> writes:
> Aside from those details, it seems clear that any reasonably complete
> move-extensions-elsewhere feature will need some kind of build system
> support.  I have various ideas on that and would gladly contribute some
> of them, but it's not going to happen within two weeks.

+1

Note that I am currently working on such a build system, so feel free to
send me off-list emails about your thoughs, I'm interested and could
integrate them into what I'm building.

> At this point I suggest that we work toward the minimum viable product:
> the extension_control_path feature as originally proposed (plus the
> crash fixes), and let the field work out best practices.  As you
> describe, you can work around all the other issues by patching various
> text files, but you currently cannot move the extension control file in
> any way, and that's a real deficiency.  (I once experimented with bind
> mounts to work around that -- a real mess ;-) )

Please find attached the v2 version of the patch, including fixes for
the crash and documentation aspects you've listed before.

Regards,
--
Dimitri Fontaine                                        06 63 07 10 78
http://2ndQuadrant.fr     PostgreSQL : Expertise, Formation et Support

*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 6008,6013 **** dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
--- 6008,6068 ----
        </listitem>
       </varlistentry>

+      <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>
+         Note that unless using the <literal>directory</literal> control file
+         parameter, the extension scripts and auxilliary files are searched in
+         the <varname>extension_control_path</varname> too.
+        </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-gin-fuzzy-search-limit" xreflabel="gin_fuzzy_search_limit">
        <term><varname>gin_fuzzy_search_limit</varname> (<type>integer</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,684 ----
      if (version)
          filename = get_extension_aux_control_filename(control, version);
      else
!         filename = get_extension_control_filename(control->name, directory);
!
!     if (filename == NULL)
!         ereport(ERROR,
!                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
!                  errmsg("extension \"%s\" is not available", control->name),
!                  errdetail("Couldn't find \"%s.control\" anywhere in extension_control_path",
!                            control->name)));

      if ((file = AllocateFile(filename, "r")) == NULL)
      {
***************
*** 482,487 **** parse_extension_control_file(ExtensionControlFile *control,
--- 702,709 ----

      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;

--- 800,811 ----

  /*
   * 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;
  }
--- 821,864 ----
      /*
       * 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 (path && 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;
  }
--- 884,890 ----
      /*
       * Parse the auxiliary control file, overwriting struct fields
       */
!     parse_extension_control_file(acontrol, version, NULL);

      return acontrol;
  }
***************
*** 1229,1235 **** 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
--- 1491,1497 ----
       * 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
***************
*** 1283,1288 **** CreateExtension(CreateExtensionStmt *stmt)
--- 1545,1558 ----
      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.
       */
***************
*** 1619,1671 **** 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);

      /*
--- 1889,1904 ----
  }

  /*
!  * 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);

      /*
***************
*** 1696,1702 **** 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));
--- 1929,1935 ----
              if (strstr(extname, "--"))
                  continue;

!             control = read_extension_control_file(extname, location);

              memset(values, 0, sizeof(values));
              memset(nulls, 0, sizeof(nulls));
***************
*** 1717,1725 **** pg_available_extensions(PG_FUNCTION_ARGS)

              tuplestore_putvalues(tupstore, tupdesc, values, nulls);
          }
-
          FreeDir(dir);
      }

      /* clean up and return the tuplestore */
      tuplestore_donestoring(tupstore);
--- 1950,2011 ----

              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);
***************
*** 1744,1750 **** pg_available_extension_versions(PG_FUNCTION_ARGS)
      Tuplestorestate *tupstore;
      MemoryContext per_query_ctx;
      MemoryContext oldcontext;
!     char       *location;
      DIR           *dir;
      struct dirent *de;

--- 2030,2037 ----
      Tuplestorestate *tupstore;
      MemoryContext per_query_ctx;
      MemoryContext oldcontext;
!     List *extension_control_paths = list_extension_control_paths();
!     ListCell *lc;
      DIR           *dir;
      struct dirent *de;

***************
*** 1774,1816 **** 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 */
--- 2061,2107 ----

      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 */
***************
*** 1968,1974 **** 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);
--- 2259,2265 ----
      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);
***************
*** 2653,2659 **** 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
--- 2944,2951 ----
       * 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"
***************
*** 2792,2797 **** static struct config_string ConfigureNamesString[] =
--- 2793,2811 ----
      },

      {
+         {"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
***************
*** 547,552 ****
--- 547,553 ----
  # - 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);


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Kyotaro HORIGUCHI
Дата:
Сообщение: Re: inherit support for foreign tables
Следующее
От: "MauMau"
Дата:
Сообщение: Re: [bug fix] pg_ctl always uses the same event source