diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index d1e628f..5df214e 100644
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
*************** shared_buffers = 128MB
*** 74,96 ****
- include>
- in configuration file
-
- In addition to parameter settings, the postgresql.conf>
- file can contain include directives>, which specify
- another file to read and process as if it were inserted into the
- configuration file at this point. Include directives simply look like:
-
- include 'filename'
-
- If the file name is not an absolute path, it is taken as relative to
- the directory containing the referencing configuration file.
- Inclusions can be nested.
-
-
-
-
SIGHUP
The configuration file is reread whenever the main server process receives a
--- 74,79 ----
*************** SET ENABLE_SEQSCAN TO OFF;
*** 178,184 ****
any desired selection condition. It also contains more information about
what values are allowed for the parameters.
!
File Locations
--- 161,273 ----
any desired selection condition. It also contains more information about
what values are allowed for the parameters.
!
!
! Configuration file includes
!
!
! include>
! in configuration file
!
! In addition to parameter settings, the postgresql.conf>
! file can contain include directives>, which specify
! another file to read and process as if it were inserted into the
! configuration file at this point. Include directives simply look like:
!
! include 'filename'
!
! If the file name is not an absolute path, it is taken as relative to
! the directory containing the referencing configuration file.
! All types of inclusion directives can be nested.
!
!
!
!
! include_dir>
! in configuration file
!
! The postgresql.conf> file can also contain
! include_dir directives>, which specify an entire directory
! of configuration files to include. It is used similarly:
!
! include_dir 'directory'
!
! Non-absolute directory names follow the same rules as single file include
! directives: they are relative to the directory containing the referencing
! configuration file. Within that directory, only files whose names end
! with the suffix .conf will be included. File names
! that start with the . character are also excluded,
! to prevent mistakes as they are hidden on some platforms. Multiple files
! within an include directory are ordered by an alphanumeric sorting, so
! that ones beginning with numbers are considered before those starting
! with letters.
!
!
!
! Include files or directories can be used to logically separate portions
! of the database configuration, rather than having a single large
! postgresql.conf> file. Consider a company that has two
! database servers, each with a different amount of memory. There are likely
! elements of the configuration both will share, for things such as logging.
! But memory-related parameters on the server will vary between the two. And
! there might be server specific customizations too. One way to manage this
! situation is to break the custom configuration changes for your site into
! three files. You could add this to the end of your
! postgresql.conf> file to include them:
!
! include 'shared.conf'
! include 'memory.conf'
! include 'server.conf'
!
! All systems would have the same shared.conf>. Each server
! with a particular amount of memory could share the same
! memory.conf>; you might have one for all servers with 8GB of RAM,
! another for those having 16GB. And finally server.conf> could
! have truly server-specific configuration information in it.
!
!
!
! Another possibility for this same sort of organization is to create a
! configuration file directory and put this information into files there.
! Other programs such as Apache use a
! conf.d> directory for this purpose. And using numbered names
! to enforce an ordering is common in UNIX startup script directories named
! like /etc/rcN.d, where N> gives the
! associated system runlevel. Both of these ideas might be combined by
! adding a conf.d> directory where the
! postgresql.conf> file is at, then referencing it at the end:
!
! include_dir 'conf.d'
!
! Then you could name the files in the conf.d> directory like this:
!
! include '00shared.conf'
! include '01memory.conf'
! include '02server.conf'
!
! This shows a clear order in which these files will be loaded. This is
! important because only the last setting encountered when the server is
! reading its configuration will be used. Something set in
! conf.d/02server.conf> in this example would override a value
! set in conf.d/01memory.conf>.
!
!
!
! You might instead use this configuration directory approach while naming
! these files more descriptively:
!
! 00shared.conf
! 01memory-8GB.conf
! 02server-foo.conf
!
! This sort of arrangement gives a unique name for each configuration file
! variation. This can help eliminate ambiguity when several servers have
! their configurations all stored in one place, such as in a version
! control repository. (Storing database configuration files under version
! control is another good practice to consider).
!
!
!
File Locations
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index a094c7a..ae33fdd 100644
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
*************** ProcessConfigFile(GucContext context)
*** 355,360 ****
--- 355,396 ----
}
/*
+ * Given a configuration file or directory location that may be a relative
+ * path, return an absolute one. We consider the location to be relative to
+ * the directory holding the calling file.
+ */
+ static char *
+ AbsoluteConfigLocation(const char *location,
+ const char *calling_file)
+ {
+ char abs_path[MAXPGPATH];
+
+ if (is_absolute_path(location))
+ {
+ return pstrdup(location);
+ }
+ else
+ {
+ if (calling_file != NULL)
+ {
+ strlcpy(abs_path, calling_file, sizeof(abs_path));
+ get_parent_directory(abs_path);
+ join_path_components(abs_path, abs_path, location);
+ canonicalize_path(abs_path);
+ }
+ else
+ {
+ /*
+ * calling_file is NULL, we make an absolute path from $PGDATA
+ */
+ join_path_components(abs_path, data_directory, location);
+ canonicalize_path(abs_path);
+ }
+ return pstrdup(abs_path);
+ }
+ }
+
+ /*
* Read and parse a single configuration file. This function recurses
* to handle "include" directives.
*
*************** ParseConfigFile(const char *config_file,
*** 370,376 ****
{
bool OK = true;
FILE *fp;
- char abs_path[MAXPGPATH];
/*
* Reject too-deep include nesting depth. This is just a safety check
--- 406,411 ----
*************** ParseConfigFile(const char *config_file,
*** 386,416 ****
return false;
}
! /*
! * If config_file is a relative path, convert to absolute. We consider
! * it to be relative to the directory holding the calling file.
! */
! if (!is_absolute_path(config_file))
! {
! if (calling_file != NULL)
! {
! strlcpy(abs_path, calling_file, sizeof(abs_path));
! get_parent_directory(abs_path);
! join_path_components(abs_path, abs_path, config_file);
! canonicalize_path(abs_path);
! config_file = abs_path;
! }
! else
! {
! /*
! * calling_file is NULL, we make an absolute path from $PGDATA
! */
! join_path_components(abs_path, data_directory, config_file);
! canonicalize_path(abs_path);
! config_file = abs_path;
! }
! }
!
fp = AllocateFile(config_file, "r");
if (!fp)
{
--- 421,427 ----
return false;
}
! config_file=AbsoluteConfigLocation(config_file,calling_file);
fp = AllocateFile(config_file, "r");
if (!fp)
{
*************** ParseConfigFp(FILE *fp, const char *conf
*** 512,518 ****
}
/* OK, process the option name and value */
! if (guc_name_compare(opt_name, "include") == 0)
{
/*
* An include directive isn't a variable and should be processed
--- 523,546 ----
}
/* OK, process the option name and value */
! if (guc_name_compare(opt_name, "include_dir") == 0)
! {
! /*
! * An include_dir directive isn't a variable and should be processed
! * immediately.
! */
! unsigned int save_ConfigFileLineno = ConfigFileLineno;
!
! if (!ParseConfigDirectory(opt_value, config_file,
! depth + 1, elevel,
! head_p, tail_p))
! OK = false;
! yy_switch_to_buffer(lex_buffer);
! ConfigFileLineno = save_ConfigFileLineno;
! pfree(opt_name);
! pfree(opt_value);
! }
! else if (guc_name_compare(opt_name, "include") == 0)
{
/*
* An include directive isn't a variable and should be processed
*************** ParseConfigFp(FILE *fp, const char *conf
*** 599,604 ****
--- 627,747 ----
return OK;
}
+ /*
+ * String comparison code for use by qsort. Borrowed from
+ * src/backend/tsearch/ts_utils.c
+ */
+ static int
+ comparestr(const void *a, const void *b)
+ {
+ return strcmp(*(char **) a, *(char **) b);
+ }
+
+ /*
+ * Read and parse all config files in a subdirectory in alphabetical order
+ */
+ bool
+ ParseConfigDirectory(const char *includedir,
+ const char *calling_file,
+ int depth, int elevel,
+ ConfigVariable **head_p,
+ ConfigVariable **tail_p)
+ {
+ char *directory;
+ DIR *d;
+ struct dirent *de;
+ char **filenames = NULL;
+ int num_filenames = 0;
+ int size_filenames = 0;
+ bool status;
+
+ directory=AbsoluteConfigLocation(includedir,calling_file);
+ d = AllocateDir(directory);
+ if (d == NULL)
+ {
+ ereport(elevel,
+ (errcode_for_file_access(),
+ errmsg("could not open configuration directory \"%s\": %m",
+ directory)));
+ return false;
+
+ }
+
+ /*
+ * Read the directory and put the filenames in an array, so we can sort
+ * them prior to processing the contents.
+ */
+ while ((de = ReadDir(d, directory)) != NULL)
+ {
+ struct stat st;
+ char filename[MAXPGPATH];
+
+ /*
+ * Only parse files with names ending in ".conf". Explicitly reject
+ * files starting with ".". This excludes things like "." and "..",
+ * as well as typical hidden files, backup files, and editor debris.
+ */
+ if (strlen(de->d_name) < 6)
+ continue;
+ if (strncmp(de->d_name, ".", 1) == 0)
+ continue;
+ if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
+ continue;
+
+ join_path_components(filename, directory, de->d_name);
+ if (stat(filename, &st) == 0)
+ {
+ if (!S_ISDIR(st.st_mode))
+ {
+ /* Add file to list, increasing its size in blocks of 32 */
+ if (num_filenames == size_filenames)
+ {
+ size_filenames += 32;
+ if (num_filenames == 0)
+ /* Must initialize, repalloc won't take NULL input */
+ filenames = palloc(size_filenames * sizeof(char *));
+ else
+ filenames = repalloc(filenames, size_filenames * sizeof(char *));
+ }
+ filenames[num_filenames] = pstrdup(filename);
+ num_filenames++;
+ }
+ else
+ /*
+ * stat does not care about permissions, so the most likely reason
+ * a file can't be accessed now is if it was removed between the
+ * directory listing and now.
+ */
+ {
+ ereport(elevel,
+ (errcode_for_file_access(),
+ errmsg("configuration file \"%s\" can no longer be accessed: %m",
+ filename)));
+ return false;
+ }
+ }
+ }
+
+ if (num_filenames > 0)
+ {
+ int i;
+ qsort(filenames, num_filenames, sizeof(char *), comparestr);
+ for (i = 0; i < num_filenames; i++)
+ {
+ if (!ParseConfigFile(filenames[i], NULL,
+ 0, elevel,head_p, tail_p))
+ {
+ status = false;
+ goto cleanup;
+ }
+ }
+ }
+ status = true;
+
+ cleanup:
+ FreeDir(d);
+ return status;
+ }
/*
* Free a list of ConfigVariables, including the names and the values
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 8e3057a..2005ce9 100644
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
*************** extern bool ParseConfigFile(const char *
*** 116,121 ****
--- 116,126 ----
extern bool ParseConfigFp(FILE *fp, const char *config_file,
int depth, int elevel,
ConfigVariable **head_p, ConfigVariable **tail_p);
+ extern bool ParseConfigDirectory(const char *includedir,
+ const char *calling_file,
+ int depth, int elevel,
+ ConfigVariable **head_p,
+ ConfigVariable **tail_p);
extern void FreeConfigVariables(ConfigVariable *list);
/*