Per discussion at the developer meeting back in Ottawa, attached is an
initial patch that implements reading a directory of configuration
files instead of just one. The idea being that something like a tuning
tool, or pgadmin, for example can drop and modify files in this
directory instead of modifying the main config file (which can be very
hard to machine-parse). The idea is the same as other software like
apache that parses multiple files.

Files are parsed in alphabetical order so it's predictable, and you
can make sure some files override others etc.

Comments, before I go do the final polishing? :-)

-- 
 Magnus Hagander
 Me: http://www.hagander.net/
 Work: http://www.redpill-linpro.com/
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 53,58 **** static bool ParseConfigFile(const char *config_file, const char *calling_file,
--- 53,62 ----
  							int depth, GucContext context, int elevel,
  							struct name_value_pair **head_p,
  							struct name_value_pair **tail_p);
+ static bool ParseConfigDirectory(const char *configdir,
+ 							GucContext context, int elevel,
+ 							struct name_value_pair **head_p,
+ 							struct name_value_pair **tail_p);
  static void free_name_value_list(struct name_value_pair * list);
  static char *GUC_scanstr(const char *s);
  
***************
*** 146,151 **** ProcessConfigFile(GucContext context)
--- 150,163 ----
  		goto cleanup_list;
  
  	/*
+ 	 * Parse all config files in subdirectory, in alphabetical order, and
+ 	 * append them to the list of option names and values.
+ 	 */
+ 	if (!ParseConfigDirectory(ConfigDir, context, elevel,
+ 							  &head, &tail))
+ 		goto cleanup_list;
+ 
+ 	/*
  	 * We need the proposed new value of custom_variable_classes to check
  	 * custom variables with.  ParseConfigFile ensured that if it's in
  	 * the file, it's first in the list.  But first check to see if we
***************
*** 571,576 **** cleanup_exit:
--- 583,693 ----
  	return OK;
  }
  
+ 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
+  */
+ static bool
+ ParseConfigDirectory(const char *configdir,
+ 					GucContext context, int elevel,
+ 					struct name_value_pair **head_p,
+ 					struct name_value_pair **tail_p)
+ {
+ 	DIR *d;
+ 	struct dirent *de;
+ 	char directory[MAXPGPATH];
+ 	char **filenames = NULL;
+ 	int num_filenames = 0;
+ 	int size_filenames = 0;
+ 	bool status;
+ 
+ 	sprintf(directory, "%s/pg_config", configdir);
+ 	d = AllocateDir(directory);
+ 	if (d == NULL)
+ 	{
+ 		/*
+ 		 * Not finding the configuration directory is not fatal, because we
+ 		 * still have the main postgresql.conf file. Return true so the
+ 		 * complete config parsing doesn't fail in this case. Also avoid
+ 		 * logging this, since it can be a normal situtation.
+ 		 */
+ 		return true;
+ 	}
+ 
+ 	/*
+ 	 * 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".
+ 		 * This automtically excludes things like "." and ".."
+ 		 */
+ 		if (strlen(de->d_name) < 6)
+ 			continue;
+ 		if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
+ 			continue;
+ 
+ 		snprintf(filename, MAXPGPATH, "%s/%s", directory, de->d_name);
+ 		if (stat(filename, &st) == 0)
+ 		{
+ 			if (!S_ISDIR(st.st_mode))
+ 			{
+ 				/* Add file to list */
+ 				if (num_filenames == size_filenames)
+ 				{
+ 					/* Increase size of array in blocks of 32 */
+ 					size_filenames += 32;
+ 					filenames = guc_realloc(elevel, filenames, size_filenames * sizeof(char *));
+ 				}
+ 				filenames[num_filenames] = strdup(filename);
+ 				num_filenames++;
+ 			}
+ 		}
+ 	}
+ 	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, context, elevel,
+ 								 head_p, tail_p))
+ 			{
+ 				status = false;
+ 				goto cleanup;
+ 			}
+ 		}
+ 	}
+ 	status = true;
+ 
+ cleanup:
+ 	if (num_filenames > 0)
+ 	{
+ 		int i;
+ 
+ 		for (i = 0; i < num_filenames; i++)
+ 		{
+ 			free(filenames[i]);
+ 		}
+ 		free(filenames);
+ 	}
+ 	FreeDir(d);
+ 	return status;
+ }
  
  /*
   * Free a list of name/value pairs, including the names and the values
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 370,375 **** int			log_temp_files = -1;
--- 370,376 ----
  int			num_temp_buffers = 1000;
  
  char	   *ConfigFileName;
+ char	   *ConfigDir;
  char	   *HbaFileName;
  char	   *IdentFileName;
  char	   *external_pid_file;
***************
*** 2465,2470 **** static struct config_string ConfigureNamesString[] =
--- 2466,2481 ----
  	},
  
  	{
+ 		{"config_dir", PGC_POSTMASTER, FILE_LOCATIONS,
+ 			gettext_noop("Sets the servers main configuration directory."),
+ 			NULL,
+ 			GUC_DISALLOW_IN_FILE | GUC_SUPERUSER_ONLY
+ 		},
+ 		&ConfigDir,
+ 		NULL, NULL, NULL
+ 	},
+ 
+ 	{
  		{"hba_file", PGC_POSTMASTER, FILE_LOCATIONS,
  			gettext_noop("Sets the server's \"hba\" configuration file."),
  			NULL,
***************
*** 2754,2760 **** static bool is_newvalue_equal(struct config_generic * record, const char *newval
  /*
   * Some infrastructure for checking malloc/strdup/realloc calls
   */
! static void *
  guc_malloc(int elevel, size_t size)
  {
  	void	   *data;
--- 2765,2771 ----
  /*
   * Some infrastructure for checking malloc/strdup/realloc calls
   */
! void *
  guc_malloc(int elevel, size_t size)
  {
  	void	   *data;
***************
*** 2767,2773 **** guc_malloc(int elevel, size_t size)
  	return data;
  }
  
! static void *
  guc_realloc(int elevel, void *old, size_t size)
  {
  	void	   *data;
--- 2778,2784 ----
  	return data;
  }
  
! void *
  guc_realloc(int elevel, void *old, size_t size)
  {
  	void	   *data;
***************
*** 3468,3478 **** SelectConfigFiles(const char *userDoption, const char *progname)
  	}
  
  	/*
! 	 * Set the ConfigFileName GUC variable to its final value, ensuring that
! 	 * it can't be overridden later.
  	 */
  	SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
  	free(fname);
  
  	/*
  	 * Now read the config file for the first time.
--- 3479,3491 ----
  	}
  
  	/*
! 	 * Set the ConfigFileName  and ConfigDir GUC variables to their final values,
! 	 * ensuring that they can't be overridden later.
  	 */
  	SetConfigOption("config_file", fname, PGC_POSTMASTER, PGC_S_OVERRIDE);
+ 	SetConfigOption("config_dir", configdir, PGC_POSTMASTER, PGC_S_OVERRIDE);
  	free(fname);
+ 	free(configdir);
  
  	/*
  	 * Now read the config file for the first time.
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
***************
*** 2483,2489 **** main(int argc, char *argv[])
  		"base",
  		"base/1",
  		"pg_tblspc",
! 		"pg_stat_tmp"
  	};
  
  	progname = get_progname(argv[0]);
--- 2483,2490 ----
  		"base",
  		"base/1",
  		"pg_tblspc",
! 		"pg_stat_tmp",
! 		"pg_config"
  	};
  
  	progname = get_progname(argv[0]);
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 177,182 **** extern int	log_temp_files;
--- 177,183 ----
  extern int	num_temp_buffers;
  
  extern char *ConfigFileName;
+ extern char *ConfigDir;
  extern char *HbaFileName;
  extern char *IdentFileName;
  extern char *external_pid_file;
***************
*** 295,300 **** extern void read_nondefault_variables(void);
--- 296,307 ----
  #endif
  
  /*
+  * Functions used for memory allocation in guc.
+  */
+ void *guc_malloc(int elevel, size_t size);
+ void *guc_realloc(int elevel, void *old, size_t size);
+ 
+ /*
   * The following functions are not in guc.c, but are declared here to avoid
   * having to include guc.h in some widely used headers that it really doesn't
   * belong in.
-- 
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