commit fc89b8fff6e8c4f9c8247757440f5b9d78b77584
Author: Ian Barwick <barwick@gmail.com>
Date:   Mon Jul 15 15:19:41 2019 +0900

    Track which configuration files have already been included

diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index f7202cc9e7..7e8526617d 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -501,7 +501,7 @@ parse_extension_control_file(ExtensionControlFile *control,
 	 * Parse the file content, using GUC's file parsing code.  We need not
 	 * check the return value since any errors will be thrown at ERROR level.
 	 */
-	(void) ParseConfigFp(file, filename, 0, ERROR, &head, &tail);
+	(void) ParseConfigFp(file, filename, 0, ERROR, &head, &tail, NULL);
 
 	FreeFile(file);
 
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 807d950291..9ebdd62923 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -178,19 +178,25 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 			   *tail;
 	int			i;
 
+	/* Ensure we don't parse the same file more than once */
+	List       *files_seen = NIL;
+
 	/* Parse the main config file into a list of option names and values */
 	ConfFileWithError = ConfigFileName;
 	head = tail = NULL;
 
 	if (!ParseConfigFile(ConfigFileName, true,
 						 NULL, 0, 0, elevel,
-						 &head, &tail))
+						 &head, &tail, &files_seen))
 	{
 		/* Syntax error(s) detected in the file, so bail out */
 		error = true;
 		goto bail_out;
 	}
 
+	if (files_seen)
+		list_free(files_seen);
+
 	/*
 	 * Parse the PG_AUTOCONF_FILENAME file, if present, after the main file to
 	 * replace any parameters set by ALTER SYSTEM command.  Because this file
@@ -201,7 +207,7 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
 	{
 		if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
 							 NULL, 0, 0, elevel,
-							 &head, &tail))
+							 &head, &tail, NULL))
 		{
 			/* Syntax error(s) detected in the file, so bail out */
 			error = true;
@@ -561,7 +567,8 @@ ParseConfigFile(const char *config_file, bool strict,
 				const char *calling_file, int calling_lineno,
 				int depth, int elevel,
 				ConfigVariable **head_p,
-				ConfigVariable **tail_p)
+				ConfigVariable **tail_p,
+				List **files_seen)
 {
 	char	   *abs_path;
 	bool		OK = true;
@@ -585,6 +592,35 @@ ParseConfigFile(const char *config_file, bool strict,
 	}
 
 	abs_path = AbsoluteConfigLocation(config_file, calling_file);
+
+	/*
+	 * If we've seen this file before, emit an error, otherwise
+	 * add it to the list of files seen.
+	 */
+	if (files_seen)
+	{
+		ListCell *lc;
+
+		foreach(lc, *files_seen)
+		{
+			char *file_path = (char *)lfirst(lc);
+			if (strncmp(file_path, abs_path, MAXPGPATH) == 0)
+			{
+				ereport(elevel,
+						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+						 errmsg("configuration file \"%s\" was previously parsed",
+								abs_path)));
+				record_config_file_error(psprintf("configuration file \"%s\" was previously parsed",
+												  abs_path),
+										 calling_file, calling_lineno,
+										 head_p, tail_p);
+				return false;
+			}
+		}
+
+		*files_seen = lappend(*files_seen, pstrdup(abs_path));
+	}
+
 	fp = AllocateFile(abs_path, "r");
 	if (!fp)
 	{
@@ -609,7 +645,7 @@ ParseConfigFile(const char *config_file, bool strict,
 		goto cleanup;
 	}
 
-	OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p);
+	OK = ParseConfigFp(fp, abs_path, depth, elevel, head_p, tail_p, files_seen);
 
 cleanup:
 	if (fp)
@@ -696,7 +732,8 @@ GUC_flex_fatal(const char *msg)
  */
 bool
 ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
-			  ConfigVariable **head_p, ConfigVariable **tail_p)
+			  ConfigVariable **head_p, ConfigVariable **tail_p,
+			  List **files_seen)
 {
 	volatile bool OK = true;
 	unsigned int save_ConfigFileLineno = ConfigFileLineno;
@@ -792,7 +829,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
 			else if (!ParseConfigDirectory(opt_value,
 										   config_file, ConfigFileLineno - 1,
 										   depth + 1, elevel,
-										   head_p, tail_p))
+										   head_p, tail_p, files_seen))
 				OK = false;
 			yy_switch_to_buffer(lex_buffer);
 			pfree(opt_name);
@@ -814,7 +851,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
 			else if (!ParseConfigFile(opt_value, false,
 								 config_file, ConfigFileLineno - 1,
 								 depth + 1, elevel,
-								 head_p, tail_p))
+								 head_p, tail_p, files_seen))
 				OK = false;
 			yy_switch_to_buffer(lex_buffer);
 			pfree(opt_name);
@@ -836,7 +873,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
 			else if (!ParseConfigFile(opt_value, true,
 								 config_file, ConfigFileLineno - 1,
 								 depth + 1, elevel,
-								 head_p, tail_p))
+								 head_p, tail_p, files_seen))
 				OK = false;
 			yy_switch_to_buffer(lex_buffer);
 			pfree(opt_name);
@@ -944,7 +981,8 @@ ParseConfigDirectory(const char *includedir,
 					 const char *calling_file, int calling_lineno,
 					 int depth, int elevel,
 					 ConfigVariable **head_p,
-					 ConfigVariable **tail_p)
+					 ConfigVariable **tail_p,
+					 List **files_seen)
 {
 	char	   *directory;
 	DIR		   *d;
@@ -1042,7 +1080,7 @@ ParseConfigDirectory(const char *includedir,
 			if (!ParseConfigFile(filenames[i], true,
 								 calling_file, calling_lineno,
 								 depth, elevel,
-								 head_p, tail_p))
+								 head_p, tail_p, files_seen))
 			{
 				status = false;
 				goto cleanup;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fc463601ff..aa8361710b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -8060,7 +8060,7 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 								AutoConfFileName)));
 
 			/* parse it */
-			if (!ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail))
+			if (!ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail, NULL))
 				ereport(ERROR,
 						(errcode(ERRCODE_CONFIG_FILE_ERROR),
 						 errmsg("could not parse contents of file \"%s\"",
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index e709177c37..19c2d082d6 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -145,15 +145,18 @@ typedef struct ConfigVariable
 extern bool ParseConfigFile(const char *config_file, bool strict,
 							const char *calling_file, int calling_lineno,
 							int depth, int elevel,
-							ConfigVariable **head_p, ConfigVariable **tail_p);
+							ConfigVariable **head_p, ConfigVariable **tail_p,
+							List **files_seen);
 extern bool ParseConfigFp(FILE *fp, const char *config_file,
 						  int depth, int elevel,
-						  ConfigVariable **head_p, ConfigVariable **tail_p);
+						  ConfigVariable **head_p, ConfigVariable **tail_p,
+						  List **files_seen);
 extern bool ParseConfigDirectory(const char *includedir,
 								 const char *calling_file, int calling_lineno,
 								 int depth, int elevel,
 								 ConfigVariable **head_p,
-								 ConfigVariable **tail_p);
+								 ConfigVariable **tail_p,
+								 List **files_seen);
 extern void FreeConfigVariables(ConfigVariable *list);
 
 /*
