Re: extension_control_path - Mailing list pgsql-hackers
From | Dimitri Fontaine |
---|---|
Subject | Re: extension_control_path |
Date | |
Msg-id | m238iq1mdd.fsf@2ndQuadrant.fr Whole thread Raw |
In response to | Re: extension_control_path (Peter Eisentraut <peter_e@gmx.net>) |
Responses |
Re: extension_control_path
|
List | 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 by date: