[pgpool-hackers: 4001] Proposal: Allow pgpool.conf to include files

Takuma Hoshiai hoshiai.takuma at nttcom.co.jp
Tue Aug 24 14:21:36 JST 2021


Hi,

I propose to be able to break down pgpool.conf files into subfiles. On
pgpool.conf, we can include the subfiles by specifying the following

  include = '/dir/path/sub.conf'


If the file name is not an absolute path, it is taken as relative to the
directory containing the referencing configuration file. 'include' can
be nested.

This patch is especially useful if you would like to separate specific 
environment parameters.


Best Regards,

-- 
Takuma Hoshiai <hoshiai.takuma at nttcom.co.jp>
-------------- next part --------------
diff --git a/doc.ja/src/sgml/config.sgml b/doc.ja/src/sgml/config.sgml
index a2a3ea2..d885682 100644
--- a/doc.ja/src/sgml/config.sgml
+++ b/doc.ja/src/sgml/config.sgml
@@ -229,6 +229,34 @@
    </para>
   </sect2>
 
+  <sect2 id="config-setting-includes">
+   <!--
+   <title>Managing Configuration File Contents</title>
+   -->
+   <title>設定ファイルの内容の管理</title>
+
+   <para>
+    <!--
+    <productname>Pgpool-II</productname> provides a feature for breaking down
+    <filename>pgpool.conf</filename> files into sub-files. This feature is
+    especially useful when managing multiple servers or dividing by features.
+    <literal>include</literal> directive can be used like this:
+    -->
+    <productname>Pgpool-II</productname>は<filename>pgpool.conf</filename>ファイルを複数のファイルに分割して記述する方法を提供しています。
+    機能やサーバ固有のパラメータの設定を分割したい場合に有用です。
+    次のように<literal>include</literal>ディレクティブを追加します。
+    <programlisting>
+     include = 'filename'
+    </programlisting>
+    <!--
+    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.
+    -->
+    ファイル名が絶対パスでない場合、参照する設定ファイルを含むディレクトリからの相対パスであると受け取られます。includeコマンドは入れ子にすることができます。
+   </para>
+  </sect2>
+
   <sect2 id="config-setting-sql-command-interaction">
    <!--
    <title>Parameter Interaction via SQL Clients</title>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 730cf7e..542217d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -146,6 +146,25 @@
    </para>
   </sect2>
 
+  <sect2 id="config-setting-includes">
+   <title>Managing Configuration File Contents</title>
+
+   <para>
+    <productname>Pgpool-II</productname> provides a feature for breaking down
+    <filename>pgpool.conf</filename> files into sub-files. This feature is
+    especially useful when managing multiple servers or dividing by features.
+    <literal>include</literal> directive can be used like this:
+
+    <programlisting>
+     include = 'filename'
+    </programlisting>
+
+    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.
+   </para>
+  </sect2>
+
   <sect2 id="config-setting-sql-command-interaction">
    <title>Parameter Interaction via SQL Clients</title>
 
diff --git a/src/config/pool_config.c b/src/config/pool_config.c
index a238874..5b1cd2c 100644
--- a/src/config/pool_config.c
+++ b/src/config/pool_config.c
@@ -550,8 +550,8 @@ typedef enum {
 
 static char *extract_string(char *value, POOL_TOKEN token);
 static void FreeConfigVariable(ConfigVariable *item);
-static bool ParseConfigFile( const char *config_file, int elevel,
-			ConfigVariable **head_p, ConfigVariable **tail_p);
+static bool ParseConfigFile(const char *config_file, const char *calling_file,
+				int depth, int elevel, ConfigVariable **head_p, ConfigVariable **tail_p);
 
 #define YY_NO_INPUT 1
 #line 558 "config/pool_config.c"
@@ -2042,6 +2042,9 @@ FreeConfigVariables(ConfigVariable *list)
  *
  * Input parameters:
  *  config_file: absolute or relative path name of the configuration file
+ *  calling_file: absolute path of file containing the "include" directive,
+ *    or NULL at outer level (config_file must be absolute at outer level)
+ *  depth: recursion depth (used only to prevent infinite recursion)
  *  elevel: error logging level to use
  * Input/Output parameters:
  *  head_p, tail_p: head and tail of linked list of name/value pairs
@@ -2055,34 +2058,66 @@ FreeConfigVariables(ConfigVariable *list)
  *
  */
 static bool
-ParseConfigFile(const char *config_file, int elevel,
-				ConfigVariable **head_p, ConfigVariable **tail_p)
+ParseConfigFile(const char *config_file, const char *calling_file,
+				int depth, int elevel, ConfigVariable **head_p,
+				ConfigVariable **tail_p)
 {
 
 	FILE *fd;
+	YY_BUFFER_STATE lex_buffer;
 	int token;
 	char *key;
 	char *val;
 	ConfigVariable *item;
 	char buf[POOLMAXPATHLEN + 1];
+	char *config_filepath;
+
+	/*
+	 * Reject too-deep include nesting depth.
+	 */
+	if (depth > 10)
+		ereport(elevel,
+				(errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
+								config_file)));
+
+	if (depth == 0)
+	{
+		/* get directory path of config file pgpool.conf */
+		strlcpy(buf, config_file, sizeof(buf));
+		get_parent_directory(buf);
+		config_file_dir = buf;
+	}
 
-	*head_p = NULL;
 
-	/* get directory path of config file pgpool.conf */
-	strlcpy(buf, config_file, sizeof(buf));
-	get_parent_directory(buf);
-	config_file_dir = buf;
+	if (calling_file == NULL || is_absolute_path(config_file))
+	{
+		/* absolute path is taken as-is */
+		config_filepath = pstrdup(config_file);
+	}
+	else
+	{
+		/* relative path is relative to dir of calling file */
+		config_filepath = (char *) palloc(strlen(config_file) + 1 +
+									   strlen(calling_file) + 1);
+		strcpy(config_filepath, calling_file);
+		get_parent_directory(config_filepath);
+		join_path_components(config_filepath, config_filepath, config_file);
+		canonicalize_path(config_filepath);
+	}
 
 	/* open config file */
-	fd = fopen(config_file, "r");
+	fd = fopen(config_filepath, "r");
 	if (!fd)
 	{
 		ereport(WARNING,
-			(errmsg("could not open configuration file: \"%s\"",config_file),
+			(errmsg("could not open configuration file: \"%s\"",config_filepath),
 				errdetail("using default configuration parameter values")));
 		return false;
 	}
 
+	lex_buffer = yy_create_buffer(fd,YY_BUF_SIZE);
+	yy_switch_to_buffer(lex_buffer);
+
 	yyin = fd;
 	Lineno = 1;
 
@@ -2115,17 +2150,36 @@ ParseConfigFile(const char *config_file, int elevel,
 		ereport(DEBUG5,
 			(errmsg("key: \"%s\" value: \"%s\" kind: %d",key, val, token)));
 
-		/* Add this to the list */
-		item = palloc(sizeof(ConfigVariable));
-		item->name = key;
-		item->value = val;
-		item->sourceline = Lineno;
-		item->next = NULL;
-		if (*head_p == NULL)
-			*head_p = item;
+		if (strcmp(key, "include") == 0)
+		{
+			/*
+			 * An include directive isn't a variable and should be processed
+			 * immediately.
+			 */
+			unsigned save_Lineno = Lineno;
+
+			if (!ParseConfigFile(val, config_filepath, depth + 1, elevel, head_p, tail_p))
+			{
+				goto parse_error;
+			}
+			yy_switch_to_buffer(lex_buffer);
+			Lineno = save_Lineno;
+		}
 		else
-			(*tail_p)->next = item;
-		*tail_p = item;
+		{
+			/* Add this to the list */
+			item = palloc(sizeof(ConfigVariable));
+			item->name = key;
+			item->value = val;
+			item->sourceline = Lineno;
+			item->next = NULL;
+
+			if (*head_p == NULL)
+				*head_p = item;
+			else
+				(*tail_p)->next = item;
+			*tail_p = item;
+		}
 	}
 
 	fclose(fd);
@@ -2138,7 +2192,7 @@ parse_error:
 	*head_p = NULL;
 	*tail_p = NULL;
 	ereport(elevel,
-		(errmsg("syntax error in configuration file \"%s\"",config_file),
+		(errmsg("syntax error in configuration file \"%s\"", config_filepath),
 			errdetail("parse error at line %d '%s' token = %d", Lineno, yytext,token)));
 
 	return false;
@@ -2154,7 +2208,7 @@ bool pool_get_config(const char *config_file, ConfigContext context)
 	bool res;
 	int elevel = (context == CFGCXT_INIT)?FATAL:WARNING;
 
-	res = ParseConfigFile(config_file, elevel, &head_p, &tail_p);
+	res = ParseConfigFile(config_file, NULL, 0, elevel, &head_p, &tail_p);
 	if (res == false || head_p == NULL)
 		return false;
 
diff --git a/src/config/pool_config.l b/src/config/pool_config.l
index 3451279..1cbe22e 100644
--- a/src/config/pool_config.l
+++ b/src/config/pool_config.l
@@ -60,8 +60,8 @@ typedef enum {
 
 static char *extract_string(char *value, POOL_TOKEN token);
 static void FreeConfigVariable(ConfigVariable *item);
-static bool ParseConfigFile( const char *config_file, int elevel,
-			ConfigVariable **head_p, ConfigVariable **tail_p);
+static bool ParseConfigFile(const char *config_file, const char *calling_file,
+				int depth, int elevel, ConfigVariable **head_p, ConfigVariable **tail_p);
 
 %}
 
@@ -311,6 +311,9 @@ FreeConfigVariables(ConfigVariable *list)
  *
  * Input parameters:
  *  config_file: absolute or relative path name of the configuration file
+ *  calling_file: absolute path of file containing the "include" directive,
+ *    or NULL at outer level (config_file must be absolute at outer level)
+ *  depth: recursion depth (used only to prevent infinite recursion)
  *  elevel: error logging level to use
  * Input/Output parameters:
  *  head_p, tail_p: head and tail of linked list of name/value pairs
@@ -324,34 +327,66 @@ FreeConfigVariables(ConfigVariable *list)
  *
  */
 static bool
-ParseConfigFile(const char *config_file, int elevel,
-				ConfigVariable **head_p, ConfigVariable **tail_p)
+ParseConfigFile(const char *config_file, const char *calling_file,
+				int depth, int elevel, ConfigVariable **head_p,
+				ConfigVariable **tail_p)
 {
 
 	FILE *fd;
+	YY_BUFFER_STATE lex_buffer;
 	int token;
 	char *key;
 	char *val;
 	ConfigVariable *item;
 	char buf[POOLMAXPATHLEN + 1];
+	char *config_filepath;
 
-	*head_p = NULL;
+	/*
+	 * Reject too-deep include nesting depth.
+	 */
+	if (depth > 10)
+		ereport(elevel,
+				(errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
+								config_file)));
+
+	if (depth == 0)
+	{
+		/* get directory path of config file pgpool.conf */
+		strlcpy(buf, config_file, sizeof(buf));
+		get_parent_directory(buf);
+		config_file_dir = buf;
+	}
 
-	/* get directory path of config file pgpool.conf */
-	strlcpy(buf, config_file, sizeof(buf));
-	get_parent_directory(buf);
-	config_file_dir = buf;
+
+	if (calling_file == NULL || is_absolute_path(config_file))
+	{
+		/* absolute path is taken as-is */
+		config_filepath = pstrdup(config_file);
+	}
+	else
+	{
+		/* relative path is relative to dir of calling file */
+		config_filepath = (char *) palloc(strlen(config_file) + 1 +
+									   strlen(calling_file) + 1);
+		strcpy(config_filepath, calling_file);
+		get_parent_directory(config_filepath);
+		join_path_components(config_filepath, config_filepath, config_file);
+		canonicalize_path(config_filepath);
+	}
 
 	/* open config file */
-	fd = fopen(config_file, "r");
+	fd = fopen(config_filepath, "r");
 	if (!fd)
 	{
 		ereport(WARNING,
-			(errmsg("could not open configuration file: \"%s\"",config_file),
+			(errmsg("could not open configuration file: \"%s\"",config_filepath),
 				errdetail("using default configuration parameter values")));
 		return false;
 	}
 
+	lex_buffer = yy_create_buffer(fd, YY_BUF_SIZE);
+	yy_switch_to_buffer(lex_buffer);
+
 	yyin = fd;
 	Lineno = 1;
 
@@ -384,17 +419,36 @@ ParseConfigFile(const char *config_file, int elevel,
 		ereport(DEBUG5,
 			(errmsg("key: \"%s\" value: \"%s\" kind: %d",key, val, token)));
 
-		/* Add this to the list */
-		item = palloc(sizeof(ConfigVariable));
-		item->name = key;
-		item->value = val;
-		item->sourceline = Lineno;
-		item->next = NULL;
-		if (*head_p == NULL)
-			*head_p = item;
+		if (strcmp(key, "include") == 0)
+		{
+			/*
+			 * An include directive isn't a variable and should be processed
+			 * immediately.
+			 */
+			unsigned save_Lineno = Lineno;
+
+			if (!ParseConfigFile(val, config_filepath, depth + 1, elevel, head_p, tail_p))
+			{
+				goto parse_error;
+			}
+			yy_switch_to_buffer(lex_buffer);
+			Lineno = save_Lineno;
+		}
 		else
-			(*tail_p)->next = item;
-		*tail_p = item;
+		{
+			/* Add this to the list */
+			item = palloc(sizeof(ConfigVariable));
+			item->name = key;
+			item->value = val;
+			item->sourceline = Lineno;
+			item->next = NULL;
+
+			if (*head_p == NULL)
+				*head_p = item;
+			else
+				(*tail_p)->next = item;
+			*tail_p = item;
+		}
 	}
 
 	fclose(fd);
@@ -407,7 +461,7 @@ parse_error:
 	*head_p = NULL;
 	*tail_p = NULL;
 	ereport(elevel,
-		(errmsg("syntax error in configuration file \"%s\"",config_file),
+		(errmsg("syntax error in configuration file \"%s\"", config_filepath),
 			errdetail("parse error at line %d '%s' token = %d", Lineno, yytext,token)));
 
 	return false;
@@ -423,7 +477,7 @@ bool pool_get_config(const char *config_file, ConfigContext context)
 	bool res;
 	int elevel = (context == CFGCXT_INIT)?FATAL:WARNING;
 
-	res = ParseConfigFile(config_file, elevel, &head_p, &tail_p);
+	res = ParseConfigFile(config_file, NULL, 0, elevel, &head_p, &tail_p);
 	if (res == false || head_p == NULL)
 		return false;
 


More information about the pgpool-hackers mailing list