set_persistent_v13.patch

application/octet-stream

Filename: set_persistent_v13.patch
Type: application/octet-stream
Part: 1
Message: Re: Re: Proposal for Allow postgresql.conf values to be changed via SQL [review] (Fix memory growth)

Patch

Format: context
Series: patch v13
File+
doc/src/sgml/config.sgml 13 0
doc/src/sgml/keywords.sgml 7 0
doc/src/sgml/ref/set.sgml 26 0
doc/src/sgml/storage.sgml 12 0
src/backend/access/transam/xlog.c 1 0
src/backend/commands/extension.c 1 0
src/backend/nodes/copyfuncs.c 1 0
src/backend/nodes/equalfuncs.c 1 0
src/backend/parser/gram.y 55 0
src/backend/replication/basebackup.c 6 0
src/backend/tcop/utility.c 8 0
src/backend/utils/misc/guc.c 569 0
src/backend/utils/misc/guc-file.l 58 0
src/backend/utils/misc/postgresql.conf.sample 9 0
src/bin/initdb/initdb.c 39 0
src/include/nodes/parsenodes.h 1 0
src/include/parser/kwlist.h 1 0
src/include/pg_config_manual.h 7 0
src/include/storage/lwlock.h 1 0
src/include/utils/guc.h 6 0
src/include/utils/guc_tables.h 4 0
src/test/regress/expected/persistent.out 474 0
src/test/regress/parallel_schedule 4 0
src/test/regress/serial_schedule 1 0
src/test/regress/sql/persistent.sql 190 0
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 158,163 **** SET ENABLE_SEQSCAN TO OFF;
--- 158,176 ----
       require superuser permission to change via <command>SET</command> or
       <command>ALTER</>.
      </para>
+ 
+     <para>
+      Another way to change configuration parameters persistently is by
+      use of <xref linkend="SQL-SET">
+      command, for example:
+ <screen>
+ SET PERSISTENT max_connections To 10;
+ </screen>
+      This command will allow users to change values persistently
+      through SQL command. The values will be effective at the end of
+      the command. The effect of this command is similar to when user
+      manually changes values in <filename>postgresql.conf</filename>.
+     </para>
     </sect2>
  
     <sect2 id="config-setting-examining">
*** a/doc/src/sgml/keywords.sgml
--- b/doc/src/sgml/keywords.sgml
***************
*** 3388,3393 ****
--- 3388,3400 ----
      <entry></entry>
     </row>
     <row>
+     <entry><token>PERSISTENT</token></entry>
+     <entry>non-reserved</entry>
+     <entry></entry>
+     <entry></entry>
+     <entry></entry>
+    </row>
+    <row>
      <entry><token>PLACING</token></entry>
      <entry>reserved</entry>
      <entry>non-reserved</entry>
*** a/doc/src/sgml/ref/set.sgml
--- b/doc/src/sgml/ref/set.sgml
***************
*** 21,28 **** PostgreSQL documentation
  
   <refsynopsisdiv>
  <synopsis>
! SET [ SESSION | LOCAL ] <replaceable class="PARAMETER">configuration_parameter</replaceable> { TO | = } { <replaceable class="PARAMETER">value</replaceable> | '<replaceable class="PARAMETER">value</replaceable>' | DEFAULT }
! SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</replaceable> | LOCAL | DEFAULT }
  </synopsis>
   </refsynopsisdiv>
  
--- 21,28 ----
  
   <refsynopsisdiv>
  <synopsis>
! SET [ SESSION | LOCAL | PERSISTENT ] <replaceable class="PARAMETER">configuration_parameter</replaceable> { TO | = } { <replaceable class="PARAMETER">value</replaceable> | '<replaceable class="PARAMETER">value</replaceable>' | DEFAULT }
! SET [ SESSION | LOCAL | PERSISTENT ] TIME ZONE { <replaceable class="PARAMETER">timezone</replaceable> | LOCAL | DEFAULT }
  </synopsis>
   </refsynopsisdiv>
  
***************
*** 50,55 **** SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
--- 50,63 ----
    </para>
  
    <para>
+    <command>SET PERSISTENT</command> writes the configuration parameter
+    values to the <filename>persistent.auto.conf</filename> file. With
+    <literal>DEFAULT</literal>, it removes a configuration entry from
+    <filename>persistent.auto.conf</filename> file. The values will be
+    effective at the end of command.
+   </para>
+ 
+   <para>
     The effects of <command>SET LOCAL</command> last only till the end of
     the current transaction, whether committed or not.  A special case is
     <command>SET</command> followed by <command>SET LOCAL</command> within
***************
*** 98,105 **** SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
      <listitem>
       <para>
        Specifies that the command takes effect for the current session.
!       (This is the default if neither <literal>SESSION</> nor
!       <literal>LOCAL</> appears.)
       </para>
      </listitem>
     </varlistentry>
--- 106,113 ----
      <listitem>
       <para>
        Specifies that the command takes effect for the current session.
!       (This is the default option, if any of <literal>SESSION</>,
!       <literal>LOCAL</> or <literal>PERSISTENT</> are not provided.)
       </para>
      </listitem>
     </varlistentry>
***************
*** 119,124 **** SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
--- 127,143 ----
     </varlistentry>
  
     <varlistentry>
+     <term><literal>PERSISTENT</></term>
+     <listitem>
+      <para>
+       Writes updated configuration parameters to <filename>persistent.auto.conf</filename>.
+       These configuration parameters takes effect at the end of the command based on
+       the type of configuration parameter modified.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
      <term><replaceable class="PARAMETER">configuration_parameter</replaceable></term>
      <listitem>
       <para>
***************
*** 154,160 **** SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
      <varlistentry>
       <term><literal>SCHEMA</literal></term>
       <listitem>
!       <para><literal>SET SCHEMA '<replaceable>value</>'</> is an alias for
         <literal>SET search_path TO <replaceable>value</></>.  Only one
         schema can be specified using this syntax.
        </para>
--- 173,179 ----
      <varlistentry>
       <term><literal>SCHEMA</literal></term>
       <listitem>
!       <para><literal>SET [ PERSISTENT ] SCHEMA '<replaceable>value</>'</> is an alias for
         <literal>SET search_path TO <replaceable>value</></>.  Only one
         schema can be specified using this syntax.
        </para>
***************
*** 164,170 **** SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
      <varlistentry>
       <term><literal>NAMES</literal></term>
       <listitem>
!       <para><literal>SET NAMES <replaceable>value</></> is an alias for
         <literal>SET client_encoding TO <replaceable>value</></>.
        </para>
       </listitem>
--- 183,189 ----
      <varlistentry>
       <term><literal>NAMES</literal></term>
       <listitem>
!       <para><literal>SET [ PERSISTENT ] NAMES <replaceable>value</></> is an alias for
         <literal>SET client_encoding TO <replaceable>value</></>.
        </para>
       </listitem>
***************
*** 192,198 **** SELECT setseed(<replaceable>value</replaceable>);
      <varlistentry>
       <term><literal>TIME ZONE</literal></term>
       <listitem>
!       <para><literal>SET TIME ZONE <replaceable>value</></> is an alias
         for <literal>SET timezone TO <replaceable>value</></>.  The
         syntax <literal>SET TIME ZONE</literal> allows special syntax
         for the time zone specification.  Here are examples of valid
--- 211,217 ----
      <varlistentry>
       <term><literal>TIME ZONE</literal></term>
       <listitem>
!       <para><literal>SET [ PERSISTENT ] TIME ZONE <replaceable>value</></> is an alias
         for <literal>SET timezone TO <replaceable>value</></>.  The
         syntax <literal>SET TIME ZONE</literal> allows special syntax
         for the time zone specification.  Here are examples of valid
*** a/doc/src/sgml/storage.sgml
--- b/doc/src/sgml/storage.sgml
***************
*** 34,40 **** these required items, the cluster configuration files
  <filename>postgresql.conf</filename>, <filename>pg_hba.conf</filename>, and
  <filename>pg_ident.conf</filename> are traditionally stored in
  <varname>PGDATA</> (although in <productname>PostgreSQL</productname> 8.0 and
! later, it is possible to place them elsewhere).
  </para>
  
  <table tocentry="1" id="pgdata-contents-table">
--- 34,41 ----
  <filename>postgresql.conf</filename>, <filename>pg_hba.conf</filename>, and
  <filename>pg_ident.conf</filename> are traditionally stored in
  <varname>PGDATA</> (although in <productname>PostgreSQL</productname> 8.0 and
! later, it is possible to place them elsewhere). By default the directory config is stored
! in <varname>PGDATA</>, however it should be kept along with <filename>postgresql.conf</filename>.
  </para>
  
  <table tocentry="1" id="pgdata-contents-table">
***************
*** 57,62 **** Item
--- 58,68 ----
  </row>
  
  <row>
+  <entry><filename>config</></entry>
+  <entry>Subdirectory containing automatically generated configuration files</entry>
+ </row>
+ 
+ <row>
   <entry><filename>base</></entry>
   <entry>Subdirectory containing per-database subdirectories</entry>
  </row>
***************
*** 253,258 **** where <replaceable>PPP</> is the PID of the owning backend and
--- 259,269 ----
  <replaceable>NNN</> distinguishes different temporary files of that backend.
  </para>
  
+ <para>
+ Configuration variables changed by command <acronym>SET PERSISTENT</> will be stored in 
+ <varname>PGDATA</><filename>/config</>.
+ </para>
+ 
  </sect1>
  
  <sect1 id="storage-toast">
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 4150,4156 **** readRecoveryCommandFile(void)
  	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's
  	 * no need to check the return value.
  	 */
! 	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
  
  	FreeFile(fd);
  
--- 4150,4156 ----
  	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's
  	 * no need to check the return value.
  	 */
! 	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail, NULL);
  
  	FreeFile(fd);
  
*** a/src/backend/commands/extension.c
--- b/src/backend/commands/extension.c
***************
*** 478,484 **** 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);
  
  	FreeFile(file);
  
--- 478,484 ----
  	 * 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, NULL);
  
  	FreeFile(file);
  
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 3281,3286 **** _copyVariableSetStmt(const VariableSetStmt *from)
--- 3281,3287 ----
  	COPY_STRING_FIELD(name);
  	COPY_NODE_FIELD(args);
  	COPY_SCALAR_FIELD(is_local);
+ 	COPY_SCALAR_FIELD(is_persistent);
  
  	return newnode;
  }
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 1568,1573 **** _equalVariableSetStmt(const VariableSetStmt *a, const VariableSetStmt *b)
--- 1568,1574 ----
  	COMPARE_STRING_FIELD(name);
  	COMPARE_NODE_FIELD(args);
  	COMPARE_SCALAR_FIELD(is_local);
+ 	COMPARE_SCALAR_FIELD(is_persistent);
  
  	return true;
  }
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 404,410 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  
  %type <istmt>	insert_rest
  
! %type <vsetstmt> set_rest set_rest_more SetResetClause FunctionSetResetClause
  
  %type <node>	TableElement TypedTableElement ConstraintElem TableFuncElement
  %type <node>	columnDef columnOptions
--- 404,410 ----
  
  %type <istmt>	insert_rest
  
! %type <vsetstmt> set_rest set_rest_more set_persistent SetResetClause FunctionSetResetClause
  
  %type <node>	TableElement TypedTableElement ConstraintElem TableFuncElement
  %type <node>	columnDef columnOptions
***************
*** 573,579 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  	OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
  	ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
  
! 	PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POSITION
  	PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
  	PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM
  
--- 573,579 ----
  	OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
  	ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
  
! 	PARSER PARTIAL PARTITION PASSING PASSWORD PERSISTENT PLACING PLANS POSITION
  	PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
  	PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM
  
***************
*** 1312,1317 **** VariableSetStmt:
--- 1312,1323 ----
  					n->is_local = false;
  					$$ = (Node *) n;
  				}
+ 			| SET PERSISTENT set_persistent
+ 				{
+ 					VariableSetStmt *n = $3;
+ 					n->is_persistent = true;
+ 					$$ = (Node *) n;
+ 				}
  		;
  
  set_rest:
***************
*** 1335,1340 **** set_rest:
--- 1341,1391 ----
  			;
  
  set_rest_more:	/* Generic SET syntaxes: */
+ 			CATALOG_P Sconst
+ 				{
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 							 errmsg("current database cannot be changed"),
+ 							 parser_errposition(@2)));
+ 					$$ = NULL; /*not reached*/
+ 				}
+ 			| ROLE ColId_or_Sconst
+ 				{
+ 					VariableSetStmt *n = makeNode(VariableSetStmt);
+ 					n->kind = VAR_SET_VALUE;
+ 					n->name = "role";
+ 					n->args = list_make1(makeStringConst($2, @2));
+ 					$$ = n;
+ 				}
+ 			| SESSION AUTHORIZATION ColId_or_Sconst
+ 				{
+ 					VariableSetStmt *n = makeNode(VariableSetStmt);
+ 					n->kind = VAR_SET_VALUE;
+ 					n->name = "session_authorization";
+ 					n->args = list_make1(makeStringConst($3, @3));
+ 					$$ = n;
+ 				}
+ 			| SESSION AUTHORIZATION DEFAULT
+ 				{
+ 					VariableSetStmt *n = makeNode(VariableSetStmt);
+ 					n->kind = VAR_SET_DEFAULT;
+ 					n->name = "session_authorization";
+ 					$$ = n;
+ 				}
+ 			/* Special syntaxes invented by PostgreSQL: */
+ 			| TRANSACTION SNAPSHOT Sconst
+ 				{
+ 					VariableSetStmt *n = makeNode(VariableSetStmt);
+ 					n->kind = VAR_SET_MULTI;
+ 					n->name = "TRANSACTION SNAPSHOT";
+ 					n->args = list_make1(makeStringConst($3, @3));
+ 					$$ = n;
+ 				}
+ 			| set_persistent
+ 			;
+ 
+ /* common SET/SET PERSISTENT syntaxes */
+ set_persistent:
  			var_name TO var_list
  				{
  					VariableSetStmt *n = makeNode(VariableSetStmt);
***************
*** 1384,1397 **** set_rest_more:	/* Generic SET syntaxes: */
  						n->kind = VAR_SET_DEFAULT;
  					$$ = n;
  				}
- 			| CATALOG_P Sconst
- 				{
- 					ereport(ERROR,
- 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- 							 errmsg("current database cannot be changed"),
- 							 parser_errposition(@2)));
- 					$$ = NULL; /*not reached*/
- 				}
  			| SCHEMA Sconst
  				{
  					VariableSetStmt *n = makeNode(VariableSetStmt);
--- 1435,1440 ----
***************
*** 1411,1439 **** set_rest_more:	/* Generic SET syntaxes: */
  						n->kind = VAR_SET_DEFAULT;
  					$$ = n;
  				}
- 			| ROLE ColId_or_Sconst
- 				{
- 					VariableSetStmt *n = makeNode(VariableSetStmt);
- 					n->kind = VAR_SET_VALUE;
- 					n->name = "role";
- 					n->args = list_make1(makeStringConst($2, @2));
- 					$$ = n;
- 				}
- 			| SESSION AUTHORIZATION ColId_or_Sconst
- 				{
- 					VariableSetStmt *n = makeNode(VariableSetStmt);
- 					n->kind = VAR_SET_VALUE;
- 					n->name = "session_authorization";
- 					n->args = list_make1(makeStringConst($3, @3));
- 					$$ = n;
- 				}
- 			| SESSION AUTHORIZATION DEFAULT
- 				{
- 					VariableSetStmt *n = makeNode(VariableSetStmt);
- 					n->kind = VAR_SET_DEFAULT;
- 					n->name = "session_authorization";
- 					$$ = n;
- 				}
  			| XML_P OPTION document_or_content
  				{
  					VariableSetStmt *n = makeNode(VariableSetStmt);
--- 1454,1459 ----
***************
*** 1442,1457 **** set_rest_more:	/* Generic SET syntaxes: */
  					n->args = list_make1(makeStringConst($3 == XMLOPTION_DOCUMENT ? "DOCUMENT" : "CONTENT", @3));
  					$$ = n;
  				}
! 			/* Special syntaxes invented by PostgreSQL: */
! 			| TRANSACTION SNAPSHOT Sconst
! 				{
! 					VariableSetStmt *n = makeNode(VariableSetStmt);
! 					n->kind = VAR_SET_MULTI;
! 					n->name = "TRANSACTION SNAPSHOT";
! 					n->args = list_make1(makeStringConst($3, @3));
! 					$$ = n;
! 				}
! 		;
  
  var_name:	ColId								{ $$ = $1; }
  			| var_name '.' ColId
--- 1462,1468 ----
  					n->args = list_make1(makeStringConst($3 == XMLOPTION_DOCUMENT ? "DOCUMENT" : "CONTENT", @3));
  					$$ = n;
  				}
! 			;
  
  var_name:	ColId								{ $$ = $1; }
  			| var_name '.' ColId
***************
*** 12826,12831 **** unreserved_keyword:
--- 12837,12843 ----
  			| PARTITION
  			| PASSING
  			| PASSWORD
+ 			| PERSISTENT
  			| PLANS
  			| PRECEDING
  			| PREPARE
*** a/src/backend/replication/basebackup.c
--- b/src/backend/replication/basebackup.c
***************
*** 755,760 **** sendDir(char *path, int basepathlen, bool sizeonly)
--- 755,766 ----
  					strlen(PG_TEMP_FILE_PREFIX)) == 0)
  			continue;
  
+ 		/* skip auto conf temporary file */
+ 		if (strncmp(de->d_name,
+ 					PG_AUTOCONF_FILENAME ".",
+ 					sizeof(PG_AUTOCONF_FILENAME)) == 0)
+ 			continue;
+ 
  		/*
  		 * If there's a backup_label file, it belongs to a backup started by
  		 * the user with pg_start_backup(). It is *not* correct for this
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 1175,1181 **** standard_ProcessUtility(Node *parsetree,
  			break;
  
  		case T_VariableSetStmt:
! 			ExecSetVariableStmt((VariableSetStmt *) parsetree);
  			break;
  
  		case T_VariableShowStmt:
--- 1175,1188 ----
  			break;
  
  		case T_VariableSetStmt:
! 			{
! 				VariableSetStmt *stmt = (VariableSetStmt *) parsetree;
! 
! 				if (stmt->is_persistent)
! 					PreventTransactionChain(isTopLevel, "SET PERSISTENT");
! 
! 				ExecSetVariableStmt(stmt);
! 			}
  			break;
  
  		case T_VariableShowStmt:
*** a/src/backend/utils/misc/guc-file.l
--- b/src/backend/utils/misc/guc-file.l
***************
*** 120,125 **** ProcessConfigFile(GucContext context)
--- 120,129 ----
  				   *head,
  				   *tail;
  	int			i;
+ 	struct stat st;
+ 	bool        is_config_dir_parsed = false;
+ 	char		ConfigFileDir[MAXPGPATH];
+ 	char		AutoConfFileName[MAXPGPATH];
  
  	/*
  	 * Config files are processed on startup (by the postmaster only)
***************
*** 137,149 **** ProcessConfigFile(GucContext context)
  	/* Parse the file into a list of option names and values */
  	head = tail = NULL;
  
! 	if (!ParseConfigFile(ConfigFileName, NULL, true, 0, elevel, &head, &tail))
  	{
  		/* Syntax error(s) detected in the file, so bail out */
  		error = true;
  		goto cleanup_list;
  	}
  
  	/*
  	 * Mark all extant GUC variables as not present in the config file.
  	 * We need this so that we can tell below which ones have been removed
--- 141,173 ----
  	/* Parse the file into a list of option names and values */
  	head = tail = NULL;
  
! 	if (!ParseConfigFile(ConfigFileName, NULL, true, 0, elevel, &head, &tail, &is_config_dir_parsed))
  	{
  		/* Syntax error(s) detected in the file, so bail out */
  		error = true;
  		goto cleanup_list;
  	}
  
+ 	if (!is_config_dir_parsed && context != PGC_SIGHUP)
+ 	{
+   	    StrNCpy(ConfigFileDir, ConfigFileName, sizeof(ConfigFileDir));
+ 		get_parent_directory(ConfigFileDir);
+ 
+ 
+ 		snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s/%s/%s",
+ 				 ConfigFileDir,
+ 				 PG_AUTOCONF_DIR,
+ 			 	PG_AUTOCONF_FILENAME);
+  	    if ((stat(AutoConfFileName, &st) == -1))
+ 		   	ereport(elevel,
+ 				(errmsg("Configuration parameters changed with SET PERSISTENT command before start of server "
+ 				        "will not be effective as \"%s\"  file is not accessible.", PG_AUTOCONF_FILENAME)));
+ 		else
+ 		    ereport(elevel,
+ 			    (errmsg("Configuration parameters changed with SET PERSISTENT command before start of server "
+ 				        "will not be effective as default include directive for  \"%s\" folder is not present in postgresql.conf", PG_AUTOCONF_DIR)));
+ 	}
+ 
  	/*
  	 * Mark all extant GUC variables as not present in the config file.
  	 * We need this so that we can tell below which ones have been removed
***************
*** 402,416 **** AbsoluteConfigLocation(const char *location, const char *calling_file)
   * See ParseConfigFp for details.  This one merely adds opening the
   * file rather than working from a caller-supplied file descriptor,
   * and absolute-ifying the path name if necessary.
   */
  bool
  ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
  				int depth, int elevel,
  				ConfigVariable **head_p,
! 				ConfigVariable **tail_p)
  {
  	bool		OK = true;
  	FILE	   *fp;
  
  	/*
  	 * Reject too-deep include nesting depth.  This is just a safety check
--- 426,447 ----
   * See ParseConfigFp for details.  This one merely adds opening the
   * file rather than working from a caller-supplied file descriptor,
   * and absolute-ifying the path name if necessary.
+  *
+  * While parsing, it records if it has parsed persistent.auto.conf file.
+  * This information can be used by the callers to ensure if the parameters
+  * set by SET PERSISTENT command will be effective.
   */
  bool
  ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
  				int depth, int elevel,
  				ConfigVariable **head_p,
! 				ConfigVariable **tail_p,
! 				bool *is_config_dir_parsed)
  {
  	bool		OK = true;
  	FILE	   *fp;
+ 	char 	   *ConfigAutoFileName;
+ 	char		Filename[MAXPGPATH];
  
  	/*
  	 * Reject too-deep include nesting depth.  This is just a safety check
***************
*** 427,432 **** ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
--- 458,474 ----
  	}
  
  	config_file = AbsoluteConfigLocation(config_file,calling_file);
+ 
+     /*
+      * record if the file currently being parsed is persistent.auto.conf,
+      * so that it can be later used to give warning if it doesn't parse
+      * it.
+     */
+ 	snprintf(Filename,sizeof(Filename),"%s/%s", PG_AUTOCONF_DIR, PG_AUTOCONF_FILENAME);
+ 	ConfigAutoFileName = AbsoluteConfigLocation(Filename, ConfigFileName);
+ 	if (depth == 1 && strcmp(ConfigAutoFileName, config_file) == 0)
+ 		*is_config_dir_parsed = true;
+ 
  	fp = AllocateFile(config_file, "r");
  	if (!fp)
  	{
***************
*** 445,451 **** ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
  		return OK;
  	}
  
! 	OK = ParseConfigFp(fp, config_file, depth, elevel, head_p, tail_p);
  
  	FreeFile(fp);
  
--- 487,493 ----
  		return OK;
  	}
  
! 	OK = ParseConfigFp(fp, config_file, depth, elevel, head_p, tail_p, is_config_dir_parsed);
  
  	FreeFile(fp);
  
***************
*** 493,499 **** GUC_flex_fatal(const char *msg)
   */
  bool
  ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
! 			  ConfigVariable **head_p, ConfigVariable **tail_p)
  {
  	volatile bool OK = true;
  	unsigned int save_ConfigFileLineno = ConfigFileLineno;
--- 535,542 ----
   */
  bool
  ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
! 			  ConfigVariable **head_p, ConfigVariable **tail_p,
! 			  bool *is_config_dir_parsed)
  {
  	volatile bool OK = true;
  	unsigned int save_ConfigFileLineno = ConfigFileLineno;
***************
*** 579,585 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
  			 */
  			if (!ParseConfigDirectory(opt_value, config_file,
  								 depth + 1, elevel,
! 								 head_p, tail_p))
  				OK = false;
  			yy_switch_to_buffer(lex_buffer);
  			ConfigFileLineno = save_ConfigFileLineno;
--- 622,628 ----
  			 */
  			if (!ParseConfigDirectory(opt_value, config_file,
  								 depth + 1, elevel,
! 								 head_p, tail_p,is_config_dir_parsed))
  				OK = false;
  			yy_switch_to_buffer(lex_buffer);
  			ConfigFileLineno = save_ConfigFileLineno;
***************
*** 594,600 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
  			 */
  			if (!ParseConfigFile(opt_value, config_file, false,
  								 depth + 1, elevel,
! 								 head_p, tail_p))
  				OK = false;
  				yy_switch_to_buffer(lex_buffer);
  				pfree(opt_name);
--- 637,643 ----
  			 */
  			if (!ParseConfigFile(opt_value, config_file, false,
  								 depth + 1, elevel,
! 								 head_p, tail_p, is_config_dir_parsed))
  				OK = false;
  				yy_switch_to_buffer(lex_buffer);
  				pfree(opt_name);
***************
*** 608,614 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
  			 */
  			if (!ParseConfigFile(opt_value, config_file, true,
  								 depth + 1, elevel,
! 								 head_p, tail_p))
  				OK = false;
  			yy_switch_to_buffer(lex_buffer);
  			pfree(opt_name);
--- 651,657 ----
  			 */
  			if (!ParseConfigFile(opt_value, config_file, true,
  								 depth + 1, elevel,
! 								 head_p, tail_p, is_config_dir_parsed))
  				OK = false;
  			yy_switch_to_buffer(lex_buffer);
  			pfree(opt_name);
***************
*** 616,622 **** ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
  		}
  		else
  		{
! 			/* ordinary variable, append to list */
  			item = palloc(sizeof *item);
  			item->name = opt_name;
  			item->value = opt_value;
--- 659,669 ----
  		}
  		else
  		{
! 			/*
! 			 * ordinary variable, append to list.
! 			 * mark string variables by setting is_quoted, so that it can be
! 			 * used in write_auto_conf_file to quote the string variables.
! 			 */
  			item = palloc(sizeof *item);
  			item->name = opt_name;
  			item->value = opt_value;
***************
*** 696,702 **** ParseConfigDirectory(const char *includedir,
  					 const char *calling_file,
  					 int depth, int elevel,
  					 ConfigVariable **head_p,
! 					 ConfigVariable **tail_p)
  {
  	char	   *directory;
  	DIR		   *d;
--- 743,750 ----
  					 const char *calling_file,
  					 int depth, int elevel,
  					 ConfigVariable **head_p,
! 					 ConfigVariable **tail_p,
! 					 bool *is_config_dir_parsed)
  {
  	char	   *directory;
  	DIR		   *d;
***************
*** 780,786 **** ParseConfigDirectory(const char *includedir,
  		for (i = 0; i < num_filenames; i++)
  		{
  			if (!ParseConfigFile(filenames[i], NULL, true,
! 								 depth, elevel, head_p, tail_p))
  			{
  				status = false;
  				goto cleanup;
--- 828,834 ----
  		for (i = 0; i < num_filenames; i++)
  		{
  			if (!ParseConfigFile(filenames[i], NULL, true,
! 								 depth, elevel, head_p, tail_p, is_config_dir_parsed))
  			{
  				status = false;
  				goto cleanup;
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 57,68 ****
--- 57,70 ----
  #include "postmaster/postmaster.h"
  #include "postmaster/syslogger.h"
  #include "postmaster/walwriter.h"
+ #include "port.h"
  #include "replication/syncrep.h"
  #include "replication/walreceiver.h"
  #include "replication/walsender.h"
  #include "storage/bufmgr.h"
  #include "storage/standby.h"
  #include "storage/fd.h"
+ #include "storage/lwlock.h"
  #include "storage/proc.h"
  #include "storage/predicate.h"
  #include "tcop/tcopprot.h"
***************
*** 3349,3354 **** static void ShowAllGUCConfig(DestReceiver *dest);
--- 3351,3361 ----
  static char *_ShowOption(struct config_generic * record, bool use_units);
  static bool validate_option_array_item(const char *name, const char *value,
  						   bool skipIfNoPermissions);
+ static void write_auto_conf_file(int fd, const char *filename,
+ 					 ConfigVariable **head_p);
+ static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
+ 						  char *config_file, char *name, char *value);
+ static void set_config_file(VariableSetStmt *setstmt);
  
  
  /*
***************
*** 5095,5100 **** config_enum_get_options(struct config_enum * record, const char *prefix,
--- 5102,5320 ----
  	return retstr.data;
  }
  
+ /*
+  * Validates configuration parameter and value, by calling check hook functions
+  * depending on record's vartype. It validates if the parameter
+  * value given is in range of expected predefined value for that parameter.
+  *
+  * freemem - true indicates memory for newval and newextra will be
+  *			 freed in this function, false indicates it will be freed
+  *			 by caller.
+  * Return value:
+  *	1: the value is valid
+  *	0: the name or value is invalid
+  */
+ int
+ validate_conf_option(struct config_generic * record,
+ 					 const char *name, const char *value, int elevel,
+ 					 bool freemem, void *newval, void **newextra)
+ {
+ 	/*
+ 	 * Validate the value for the passed record, to ensure it is in expected
+ 	 * range.
+ 	 */
+ 	switch (record->vartype)
+ 	{
+ 
+ 		case PGC_BOOL:
+ 			{
+ 				struct config_bool *conf = (struct config_bool *) record;
+ 				bool		tmpnewval;
+ 
+ 				if (newval == NULL)
+ 					newval = &tmpnewval;
+ 
+ 				if (value != NULL)
+ 				{
+ 					if (!parse_bool(value, newval))
+ 					{
+ 						ereport(elevel,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						  errmsg("parameter \"%s\" requires a Boolean value",
+ 								 name)));
+ 						return 0;
+ 					}
+ 
+ 					if (!call_bool_check_hook(conf, newval, newextra,
+ 											  PGC_S_FILE, elevel))
+ 						return 0;
+ 
+ 					if (*newextra && freemem)
+ 						free(*newextra);
+ 				}
+ 			}
+ 			break;
+ 		case PGC_INT:
+ 			{
+ 				struct config_int *conf = (struct config_int *) record;
+ 				int			tmpnewval;
+ 
+ 				if (newval == NULL)
+ 					newval = &tmpnewval;
+ 
+ 				if (value != NULL)
+ 				{
+ 					const char *hintmsg;
+ 
+ 					if (!parse_int(value, newval, conf->gen.flags, &hintmsg))
+ 					{
+ 						ereport(elevel,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						 errmsg("invalid value for parameter \"%s\": \"%s\"",
+ 								name, value),
+ 								 hintmsg ? errhint("%s", _(hintmsg)) : 0));
+ 						return 0;
+ 					}
+ 
+ 					if (*((int *) newval) < conf->min || *((int *) newval) > conf->max)
+ 					{
+ 						ereport(elevel,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 								 errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
+ 							*((int *) newval), name, conf->min, conf->max)));
+ 						return 0;
+ 					}
+ 
+ 					if (!call_int_check_hook(conf, newval, newextra,
+ 											 PGC_S_FILE, elevel))
+ 						return 0;
+ 
+ 					if (*newextra && freemem)
+ 						free(*newextra);
+ 				}
+ 			}
+ 			break;
+ 		case PGC_REAL:
+ 			{
+ 				struct config_real *conf = (struct config_real *) record;
+ 				double		tmpnewval;
+ 
+ 				if (newval == NULL)
+ 					newval = &tmpnewval;
+ 
+ 				if (value != NULL)
+ 				{
+ 					if (!parse_real(value, newval))
+ 					{
+ 						ereport(elevel,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						  errmsg("parameter \"%s\" requires a numeric value",
+ 								 name)));
+ 						return 0;
+ 					}
+ 
+ 					if (*((double *) newval) < conf->min || *((double *) newval) > conf->max)
+ 					{
+ 						ereport(elevel,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 								 errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
+ 						 *((double *) newval), name, conf->min, conf->max)));
+ 						return 0;
+ 					}
+ 
+ 					if (!call_real_check_hook(conf, newval, newextra,
+ 											  PGC_S_FILE, elevel))
+ 						return 0;
+ 
+ 					if (*newextra && freemem)
+ 						free(*newextra);
+ 				}
+ 			}
+ 			break;
+ 		case PGC_STRING:
+ 			{
+ 				struct config_string *conf = (struct config_string *) record;
+ 				char	   *tempPtr;
+ 				char	  **tmpnewval = newval;
+ 
+ 				if (newval == NULL)
+ 					tmpnewval = &tempPtr;
+ 
+ 				if (value != NULL)
+ 				{
+ 					/*
+ 					 * The value passed by the caller could be transient, so
+ 					 * we always strdup it.
+ 					 */
+ 					*tmpnewval = guc_strdup(elevel, value);
+ 					if (*tmpnewval == NULL)
+ 						return 0;
+ 
+ 					/*
+ 					 * The only built-in "parsing" check we have is to apply
+ 					 * truncation if GUC_IS_NAME.
+ 					 */
+ 					if (conf->gen.flags & GUC_IS_NAME)
+ 						truncate_identifier(*tmpnewval, strlen(*tmpnewval), true);
+ 
+ 					if (!call_string_check_hook(conf, tmpnewval, newextra,
+ 												PGC_S_FILE, elevel))
+ 					{
+ 						free(*tmpnewval);
+ 						return 0;
+ 					}
+ 
+ 					/* Free the malloc'd data if any */
+ 					if (freemem)
+ 					{
+ 						if (*tmpnewval != NULL)
+ 							free(*tmpnewval);
+ 						if (*newextra != NULL)
+ 							free(*newextra);
+ 					}
+ 				}
+ 			}
+ 			break;
+ 		case PGC_ENUM:
+ 			{
+ 				struct config_enum *conf = (struct config_enum *) record;
+ 				int			tmpnewval;
+ 
+ 				if (newval == NULL)
+ 					newval = &tmpnewval;
+ 
+ 				if (value != NULL)
+ 				{
+ 					if (!config_enum_lookup_by_name(conf, value, newval))
+ 					{
+ 						char	   *hintmsg;
+ 
+ 						hintmsg = config_enum_get_options(conf,
+ 														"Available values: ",
+ 														  ".", ", ");
+ 
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 						 errmsg("invalid value for parameter \"%s\": \"%s\"",
+ 								name, value),
+ 								 hintmsg ? errhint("%s", _(hintmsg)) : 0));
+ 
+ 						if (hintmsg != NULL)
+ 							pfree(hintmsg);
+ 						return 0;
+ 					}
+ 					if (!call_enum_check_hook(conf, newval, newextra,
+ 											  PGC_S_FILE, LOG))
+ 						return 0;
+ 
+ 					if (*newextra && freemem)
+ 						free(*newextra);
+ 				}
+ 			}
+ 			break;
+ 	}
+ 	return 1;
+ }
  
  /*
   * Sets option `name' to given value.
***************
*** 5343,5359 **** set_config_option(const char *name, const char *value,
  
  				if (value)
  				{
! 					if (!parse_bool(value, &newval))
! 					{
! 						ereport(elevel,
! 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 						  errmsg("parameter \"%s\" requires a Boolean value",
! 								 name)));
! 						return 0;
! 					}
! 					if (!call_bool_check_hook(conf, &newval, &newextra,
! 											  source, elevel))
! 						return 0;
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
--- 5563,5570 ----
  
  				if (value)
  				{
! 					validate_conf_option(record, name, value, elevel,
! 										 false, &newval, &newextra);
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
***************
*** 5436,5463 **** set_config_option(const char *name, const char *value,
  
  				if (value)
  				{
! 					const char *hintmsg;
  
- 					if (!parse_int(value, &newval, conf->gen.flags, &hintmsg))
- 					{
- 						ereport(elevel,
- 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- 						 errmsg("invalid value for parameter \"%s\": \"%s\"",
- 								name, value),
- 								 hintmsg ? errhint("%s", _(hintmsg)) : 0));
- 						return 0;
- 					}
- 					if (newval < conf->min || newval > conf->max)
- 					{
- 						ereport(elevel,
- 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- 								 errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)",
- 										newval, name, conf->min, conf->max)));
- 						return 0;
- 					}
- 					if (!call_int_check_hook(conf, &newval, &newextra,
- 											 source, elevel))
- 						return 0;
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
--- 5647,5655 ----
  
  				if (value)
  				{
! 					validate_conf_option(record, name, value, elevel,
! 										 false, &newval, &newextra);
  
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
***************
*** 5540,5564 **** set_config_option(const char *name, const char *value,
  
  				if (value)
  				{
! 					if (!parse_real(value, &newval))
! 					{
! 						ereport(elevel,
! 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 						  errmsg("parameter \"%s\" requires a numeric value",
! 								 name)));
! 						return 0;
! 					}
! 					if (newval < conf->min || newval > conf->max)
! 					{
! 						ereport(elevel,
! 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 								 errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)",
! 										newval, name, conf->min, conf->max)));
! 						return 0;
! 					}
! 					if (!call_real_check_hook(conf, &newval, &newextra,
! 											  source, elevel))
! 						return 0;
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
--- 5732,5740 ----
  
  				if (value)
  				{
! 					validate_conf_option(record, name, value, elevel,
! 										 false, &newval, &newextra);
! 
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
***************
*** 5641,5667 **** set_config_option(const char *name, const char *value,
  
  				if (value)
  				{
! 					/*
! 					 * The value passed by the caller could be transient, so
! 					 * we always strdup it.
! 					 */
! 					newval = guc_strdup(elevel, value);
! 					if (newval == NULL)
! 						return 0;
! 
! 					/*
! 					 * The only built-in "parsing" check we have is to apply
! 					 * truncation if GUC_IS_NAME.
! 					 */
! 					if (conf->gen.flags & GUC_IS_NAME)
! 						truncate_identifier(newval, strlen(newval), true);
! 
! 					if (!call_string_check_hook(conf, &newval, &newextra,
! 												source, elevel))
! 					{
! 						free(newval);
! 						return 0;
! 					}
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
--- 5817,5824 ----
  
  				if (value)
  				{
! 					validate_conf_option(record, name, value, elevel,
! 										 false, &newval, &newextra);
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
***************
*** 5767,5793 **** set_config_option(const char *name, const char *value,
  
  				if (value)
  				{
! 					if (!config_enum_lookup_by_name(conf, value, &newval))
! 					{
! 						char	   *hintmsg;
! 
! 						hintmsg = config_enum_get_options(conf,
! 														"Available values: ",
! 														  ".", ", ");
! 
! 						ereport(elevel,
! 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 						 errmsg("invalid value for parameter \"%s\": \"%s\"",
! 								name, value),
! 								 hintmsg ? errhint("%s", _(hintmsg)) : 0));
! 
! 						if (hintmsg)
! 							pfree(hintmsg);
! 						return 0;
! 					}
! 					if (!call_enum_check_hook(conf, &newval, &newextra,
! 											  source, elevel))
! 						return 0;
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
--- 5924,5931 ----
  
  				if (value)
  				{
! 					validate_conf_option(record, name, value, elevel,
! 										 false, &newval, &newextra);
  				}
  				else if (source == PGC_S_DEFAULT)
  				{
***************
*** 5898,5903 **** set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
--- 6036,6364 ----
  }
  
  /*
+  * Writes updated configuration parameter values into
+  * persistent.auto.conf.temp file. It traverses the list of parameters
+  * and quote the string values before writing them to temporaray file.
+  */
+ static void
+ write_auto_conf_file(int fd, const char *filename, ConfigVariable **head_p)
+ {
+ 	ConfigVariable *item;
+ 	StringInfoData buf;
+ 
+ 	initStringInfo(&buf);
+ 	appendStringInfoString(&buf, "# Do not edit this file manually! "
+ 					   "It is overwritten by the SET PERSISTENT command \n");
+ 
+ 	/*
+ 	 * traverse the list of parameters, quote the string parameter and write
+ 	 * it file. Once all parameters are written fsync the file.
+ 	 */
+ 
+ 	for (item = *head_p; item != NULL; item = item->next)
+ 	{
+ 		char	   *escaped;
+ 
+ 		appendStringInfoString(&buf, item->name);
+ 		appendStringInfoString(&buf, " = ");
+ 
+ 		appendStringInfoString(&buf, "\'");
+ 		escaped = escape_single_quotes_ascii(item->value);
+ 		appendStringInfoString(&buf, escaped);
+ 		free(escaped);
+ 		appendStringInfoString(&buf, "\'");
+ 
+ 		appendStringInfoString(&buf, "\n");
+ 
+ 		if (write(fd, buf.data, buf.len) < 0)
+ 			ereport(ERROR,
+ 					(errmsg("failed to write to \"%s\" file", filename)));
+ 		resetStringInfo(&buf);
+ 	}
+ 
+ 	if (pg_fsync(fd) != 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("could not fsync file \"%s\": %m", filename)));
+ 
+ 	pfree(buf.data);
+ }
+ 
+ 
+ /*
+  * This function takes list of all configuration parameters in persistent.auto.conf
+  * and parameter to be updated as input arguments and replace the updated
+  * configuration parameter value in a list.
+  * If the parameter to be updated is new then it is appended to the
+  * list of parameters.
+  */
+ static void
+ replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
+ 						  char *config_file,
+ 						  char *name, char *value)
+ {
+ 	ConfigVariable *item,
+ 			   *prev = NULL;
+ 
+ 	if (*head_p != NULL)
+ 	{
+ 		for (item = *head_p; item != NULL; item = item->next)
+ 		{
+ 			if (strcmp(item->name, name) == 0)
+ 			{
+ 				pfree(item->value);
+ 				if (value != NULL)
+ 					/* update the parameter value */
+ 					item->value = pstrdup(value);
+ 				else
+ 				{
+ 					/* delete the configuration parameter from list */
+ 					if (*head_p == item)
+ 						*head_p = item->next;
+ 					else
+ 						prev->next = item->next;
+ 
+ 					if (*tail_p == item)
+ 						*tail_p = prev;
+ 
+ 					pfree(item->name);
+ 					pfree(item->filename);
+ 					pfree(item);
+ 				}
+ 				return;
+ 			}
+ 			prev = item;
+ 		}
+ 	}
+ 
+ 	if (value == NULL)
+ 		return;
+ 
+ 	item = palloc(sizeof *item);
+ 	item->name = pstrdup(name);
+ 	item->value = pstrdup(value);
+ 	item->filename = pstrdup(config_file);
+ 	item->next = NULL;
+ 
+ 	if (*head_p == NULL)
+ 	{
+ 		item->sourceline = 1;
+ 		*head_p = item;
+ 	}
+ 	else
+ 	{
+ 		item->sourceline = (*tail_p)->sourceline + 1;
+ 		(*tail_p)->next = item;
+ 	}
+ 
+ 	*tail_p = item;
+ 
+ 	return;
+ }
+ 
+ 
+ /*
+  * Persist the configuration parameter value.
+  *
+  * This function takes all previously set persistent
+  * configuration parameters and the currently set ones
+  * and write them all to the automatic configuration file.
+  *
+  * The configuration parameters are written to a temporary
+  * file then renamed to the final name. The template for the
+  * temporary file is persistent.auto.conf.temp.
+  *
+  * An LWLock is used to serialize writing to the same file.
+  *
+  * In case of an error, we leave the original automatic
+  * configuration file (persistent.auto.conf) intact.
+  * A stale temporary file may be left behind in case we crash.
+  * Such files are removed on the next server restart.
+  */
+ static void
+ set_config_file(VariableSetStmt *setstmt)
+ {
+ 	char	   *name;
+ 	char	   *value;
+ 	int			Tmpfd = -1;
+ 	FILE	   *infile;
+ 	struct config_generic *record;
+ 	ConfigVariable *head = NULL;
+ 	ConfigVariable *tail = NULL;
+ 	char		ConfigFileDir[MAXPGPATH];
+ 	char		AutoConfFileName[MAXPGPATH];
+ 	char		AutoConfTmpFileName[MAXPGPATH];
+ 	struct stat st;
+ 	void	   *newextra = NULL;
+ 	bool		is_config_dir_parsed = false;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 		   (errmsg("must be superuser to execute SET PERSISTENT command"))));
+ 
+ 	/*
+ 	 * Validate the name and arguments [value1, value2 ... ].
+ 	 */
+ 	name = setstmt->name;
+ 
+ 	switch (setstmt->kind)
+ 	{
+ 		case VAR_SET_VALUE:
+ 		case VAR_SET_CURRENT:
+ 			value = ExtractSetVariableArgs(setstmt);
+ 			break;
+ 
+ 		case VAR_SET_DEFAULT:
+ 			value = NULL;
+ 			break;
+ 		default:
+ 			elog(ERROR, "unrecognized set persistent stmt type: %d",
+ 				 setstmt->kind);
+ 			break;
+ 	}
+ 
+ 	record = find_option(name, true, LOG);
+ 	if (record == NULL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 			   errmsg("unrecognized configuration parameter \"%s\"", name)));
+ 
+ 	if ((record->context == PGC_INTERNAL) ||
+ 		(record->context == PGC_POSTMASTER) ||
+ 		(record->flags & GUC_DISALLOW_IN_FILE))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+ 				 errmsg("parameter \"%s\" cannot be changed",
+ 						name)));
+ 
+ 	if (!validate_conf_option(record, name, value, ERROR,
+ 							  true, NULL, &newextra))
+ 		ereport(ERROR,
+ 		(errmsg("invalid value for parameter \"%s\": \"%s\"", name, value)));
+ 
+ 
+ 	/*
+ 	 * The "config" directory should follow the postgresql.conf file directory
+ 	 * not the data_directory. The same is validated while parsing the
+ 	 * postgresql.conf configuration file. So while framing the name for
+ 	 * persistent.auto.conf and it's temp file we need to follow the
+ 	 * postgresql.conf file directory.
+ 	 */
+ 	StrNCpy(ConfigFileDir, ConfigFileName, sizeof(ConfigFileDir));
+ 	get_parent_directory(ConfigFileDir);
+ 
+ 	/* Frame config dir, auto conf file and auto conf sample temp file names */
+ 	snprintf(AutoConfFileName, sizeof(AutoConfFileName), "%s/%s/%s",
+ 			 ConfigFileDir,
+ 			 PG_AUTOCONF_DIR,
+ 			 PG_AUTOCONF_FILENAME);
+ 	snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s/%s/%s.%s",
+ 			 ConfigFileDir,
+ 			 PG_AUTOCONF_DIR,
+ 			 PG_AUTOCONF_FILENAME,
+ 			 "temp");
+ 
+ 	/*
+ 	 * Verify if include_dir for persistent.auto.conf file exists in
+ 	 * postgresql.conf. If it doesn't exist then warn the user that parameters
+ 	 * changed with this command will not be effective.
+ 	 */
+ 
+ 	if (!ParseConfigFile(ConfigFileName, NULL, false, 0, LOG, &head, &tail,
+ 						 &is_config_dir_parsed))
+ 		ereport(ERROR,
+ 				(errmsg("configuration file \"%s\" contains errors; parameters changed by "
+ 					"this command will not be effective.", ConfigFileName)));
+ 
+ 	/*
+ 	 * ignore the list of options as we are intersted to just ensure the
+ 	 * existence of include directive for config folder.
+ 	 */
+ 	head = tail = NULL;
+ 
+ 	if (!is_config_dir_parsed && stat(AutoConfFileName, &st) == 0)
+ 		ereport(WARNING,
+ 				(errmsg("Configuration parameters changed by this command will not be effective  "
+ 						"as default include directive for  \"%s\" folder is not present in postgresql.conf", PG_AUTOCONF_DIR)));
+ 
+ 	/*
+ 	 * one backend is allowed to operate on .auto.conf file, to ensure that we
+ 	 * need to update the contents of the file with SetPersistentLock. To
+ 	 * ensure crash safety, first the contents are written to temporary file
+ 	 * and then rename it to persistent.auto.conf
+ 	 */
+ 
+ 	LWLockAcquire(SetPersistentLock, LW_EXCLUSIVE);
+ 
+ 	Tmpfd = open(AutoConfTmpFileName, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
+ 	if (Tmpfd < 0)
+ 		ereport(ERROR,
+ 				(errcode_for_file_access(),
+ 				 errmsg("Failed to open auto conf temp file \"%s\": %m ",
+ 						AutoConfTmpFileName)));
+ 
+ 	PG_TRY();
+ 	{
+ 		if (stat(AutoConfFileName, &st) == 0)
+ 		{
+ 			/* open persistent.auto.conf file */
+ 			infile = AllocateFile(AutoConfFileName, "r");
+ 			if (infile == NULL)
+ 				ereport(ERROR,
+ 						(errmsg("Failed to open auto conf file \"%s\": %m ",
+ 								AutoConfFileName)));
+ 
+ 			/* Parse the persistent.auto.conf file */
+ 			ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail, NULL);
+ 
+ 			FreeFile(infile);
+ 		}
+ 
+ 		/*
+ 		 * replace with new value if the configuration parameter already
+ 		 * exists OR add it as a new cofiguration parameter in the file.
+ 		 */
+ 		replace_auto_config_value(&head, &tail, AutoConfFileName, name, value);
+ 
+ 		/* Write and sync the New contents to persistent.auto.conf.temp file */
+ 		write_auto_conf_file(Tmpfd, AutoConfTmpFileName, &head);
+ 
+ 		close(Tmpfd);
+ 		Tmpfd = -1;
+ 
+ 		/*
+ 		 * As the rename is atomic operation, if any problem occurs after this
+ 		 * at max it can lead to either lost the last set persistent command.
+ 		 */
+ 		if (rename(AutoConfTmpFileName, AutoConfFileName) < 0)
+ 			ereport(ERROR,
+ 					(errcode_for_file_access(),
+ 					 errmsg("could not rename file \"%s\" to \"%s\" : %m",
+ 							AutoConfTmpFileName, AutoConfFileName)));
+ 	}
+ 	PG_CATCH();
+ 	{
+ 		if (Tmpfd >= 0)
+ 			close(Tmpfd);
+ 
+ 		unlink(AutoConfTmpFileName);
+ 		PG_RE_THROW();
+ 	}
+ 	PG_END_TRY();
+ 
+ 	LWLockRelease(SetPersistentLock);
+ 
+ 	/* Send signal to postmaster to reload the configuration data */
+ 	if (kill(PostmasterPid, SIGHUP))
+ 		ereport(WARNING,
+ 				(errmsg("failed to send signal to postmaster: %m")));
+ 
+ 	return;
+ }
+ 
+ 
+ /*
   * Set a config option to the given value.
   *
   * See also set_config_option; this is just the wrapper to be called from
***************
*** 6169,6181 **** ExecSetVariableStmt(VariableSetStmt *stmt)
  	{
  		case VAR_SET_VALUE:
  		case VAR_SET_CURRENT:
! 			(void) set_config_option(stmt->name,
! 									 ExtractSetVariableArgs(stmt),
  									 (superuser() ? PGC_SUSET : PGC_USERSET),
! 									 PGC_S_SESSION,
! 									 action,
! 									 true,
! 									 0);
  			break;
  		case VAR_SET_MULTI:
  
--- 6630,6645 ----
  	{
  		case VAR_SET_VALUE:
  		case VAR_SET_CURRENT:
! 			if (stmt->is_persistent)
! 				set_config_file(stmt);
! 			else
! 				(void) set_config_option(stmt->name,
! 										 ExtractSetVariableArgs(stmt),
  									 (superuser() ? PGC_SUSET : PGC_USERSET),
! 										 PGC_S_SESSION,
! 										 action,
! 										 true,
! 										 0);
  			break;
  		case VAR_SET_MULTI:
  
***************
*** 6247,6252 **** ExecSetVariableStmt(VariableSetStmt *stmt)
--- 6711,6722 ----
  					 stmt->name);
  			break;
  		case VAR_SET_DEFAULT:
+ 			if (stmt->is_persistent)
+ 			{
+ 				set_config_file(stmt);
+ 				break;
+ 			}
+ 			/* FALLTHROUGH */
  		case VAR_RESET:
  			(void) set_config_option(stmt->name,
  									 NULL,
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 581,586 ****
--- 581,595 ----
  #include_if_exists = 'exists.conf'	# include file only if it exists
  #include = 'special.conf'		# include file
  
+ # This includes the default configuration directory included to support
+ # SET PERSISTENT statement.
+ #
+ # WARNING: User should not remove below include_dir or directory config, 
+ #          as both are required to make SET PERSISTENT command work.
+ #          Any configuration parameter values specified after this line 
+ #          will override the values set by SET PERSISTENT statement.
+ #include_dir = 'config'
+ 
  #------------------------------------------------------------------------------
  # CUSTOMIZED OPTIONS
  #------------------------------------------------------------------------------
*** a/src/bin/initdb/initdb.c
--- b/src/bin/initdb/initdb.c
***************
*** 193,199 **** const char *subdirs[] = {
  	"base/1",
  	"pg_tblspc",
  	"pg_stat",
! 	"pg_stat_tmp"
  };
  
  
--- 193,200 ----
  	"base/1",
  	"pg_tblspc",
  	"pg_stat",
! 	"pg_stat_tmp",
! 	PG_AUTOCONF_DIR
  };
  
  
***************
*** 1180,1185 **** setup_config(void)
--- 1181,1187 ----
  	char		repltok[MAXPGPATH];
  	char		path[MAXPGPATH];
  	const char *default_timezone;
+ 	FILE	   *autofile;
  
  	fputs(_("creating configuration files ... "), stdout);
  	fflush(stdout);
***************
*** 1262,1272 **** setup_config(void)
--- 1264,1310 ----
  		conflines = replace_token(conflines, "#log_timezone = 'GMT'", repltok);
  	}
  
+ 	snprintf(repltok, sizeof(repltok), "include_dir = '" PG_AUTOCONF_DIR "'");
+ 	conflines = replace_token(conflines, "#include_dir = 'config'", repltok);
+ 
  	snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
  
  	writefile(path, conflines);
  	chmod(path, S_IRUSR | S_IWUSR);
  
+ 	/*
+ 	 * create the automatic configuration file to store the configuration
+ 	 * parameters set by SET PERSISTENT command. This file will be included in
+ 	 * postgresql.conf and the parameters present in this file will override
+ 	 * the values of parameters that exists before include of this file.
+ 	 */
+ 	sprintf(path, "%s/%s/%s", pg_data, PG_AUTOCONF_DIR, PG_AUTOCONF_FILENAME);
+ 	autofile = fopen(path, PG_BINARY_W);
+ 	if (autofile == NULL)
+ 	{
+ 		fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
+ 				progname, path, strerror(errno));
+ 		exit_nicely();
+ 	}
+ 
+ 	if (fputs("# Do not edit this file manually! It is overwritten by the SET PERSISTENT command \n",
+ 			 autofile) < 0)
+ 	{
+ 		fprintf(stderr, _("%s: could not write file \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		exit_nicely();
+ 	}
+ 
+ 	if (fclose(autofile))
+ 	{
+ 		fprintf(stderr, _("%s: could not close file \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		exit_nicely();
+ 	}
+ 
+ 	chmod(path, S_IRUSR | S_IWUSR);
+ 
+ 
  	free(conflines);
  
  
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1438,1443 **** typedef struct VariableSetStmt
--- 1438,1444 ----
  	char	   *name;			/* variable to be set */
  	List	   *args;			/* List of A_Const nodes */
  	bool		is_local;		/* SET LOCAL? */
+ 	bool		is_persistent;	/* SET PERSISTENT? */
  } VariableSetStmt;
  
  /* ----------------------
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 280,285 **** PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD)
--- 280,286 ----
  PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD)
  PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD)
  PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD)
+ PG_KEYWORD("persistent", PERSISTENT, UNRESERVED_KEYWORD)
  PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD)
  PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD)
  PG_KEYWORD("position", POSITION, COL_NAME_KEYWORD)
*** a/src/include/pg_config_manual.h
--- b/src/include/pg_config_manual.h
***************
*** 266,268 ****
--- 266,275 ----
  /* #define HEAPDEBUGALL */
  /* #define ACLDEBUG */
  /* #define RTDEBUG */
+ 
+ /*
+  * Automatic configuration directory and file name
+  * for SET PERSISTENT.
+  */
+ #define PG_AUTOCONF_DIR			"config"
+ #define PG_AUTOCONF_FILENAME		"persistent.auto.conf"
*** a/src/include/storage/lwlock.h
--- b/src/include/storage/lwlock.h
***************
*** 79,84 **** typedef enum LWLockId
--- 79,85 ----
  	SerializablePredicateLockListLock,
  	OldSerXidLock,
  	SyncRepLock,
+ 	SetPersistentLock,
  	/* Individual lock IDs end here */
  	FirstBufMappingLock,
  	FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
*** a/src/include/utils/guc.h
--- b/src/include/utils/guc.h
***************
*** 113,127 **** typedef struct ConfigVariable
  
  extern bool ParseConfigFile(const char *config_file, const char *calling_file,
  				bool strict, int depth, int elevel,
! 				ConfigVariable **head_p, ConfigVariable **tail_p);
  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);
  
  /*
--- 113,130 ----
  
  extern bool ParseConfigFile(const char *config_file, const char *calling_file,
  				bool strict, int depth, int elevel,
! 				ConfigVariable **head_p, ConfigVariable **tail_p,
! 				bool *is_config_dir_parsed);
  extern bool ParseConfigFp(FILE *fp, const char *config_file,
  			  int depth, int elevel,
! 			  ConfigVariable **head_p, ConfigVariable **tail_p,
! 			  bool *is_config_dir_parsed);
  extern bool ParseConfigDirectory(const char *includedir,
  					 const char *calling_file,
  					 int depth, int elevel,
  					 ConfigVariable **head_p,
! 					 ConfigVariable **tail_p,
! 					 bool *is_config_dir_parsed);
  extern void FreeConfigVariables(ConfigVariable *list);
  
  /*
*** a/src/include/utils/guc_tables.h
--- b/src/include/utils/guc_tables.h
***************
*** 260,265 **** extern void build_guc_variables(void);
  extern const char *config_enum_lookup_by_value(struct config_enum * record, int val);
  extern bool config_enum_lookup_by_name(struct config_enum * record,
  						   const char *value, int *retval);
! 
  
  #endif   /* GUC_TABLES_H */
--- 260,268 ----
  extern const char *config_enum_lookup_by_value(struct config_enum * record, int val);
  extern bool config_enum_lookup_by_name(struct config_enum * record,
  						   const char *value, int *retval);
! extern int
! validate_conf_option(struct config_generic *record,
! 	                        const char *name, const char *value, int elevel, 
! 	                        bool freemem, void *newval, void **newextra);
  
  #endif   /* GUC_TABLES_H */
*** /dev/null
--- b/src/test/regress/expected/persistent.out
***************
*** 0 ****
--- 1,474 ----
+ -- parameter which cannot be set after connection start.
+ set persistent log_connections=on;
+ -- parameter can only be set at during server start.
+ set persistent max_connections To 10;
+ ERROR:  parameter "max_connections" cannot be changed
+ set persistent wal_level = hot_standby;
+ ERROR:  parameter "wal_level" cannot be changed
+ -- parameter can set by the backend
+ set persistent authentication_timeout = 10;
+ set persistent vacuum_cost_delay To 200;
+ ERROR:  200 is outside the valid range for parameter "vacuum_cost_delay" (0 .. 100)
+ set persistent bgwriter_lru_multiplier = 3.2;
+ set persistent full_page_writes = off;
+ set persistent synchronous_commit To off;
+ set persistent synchronous_standby_names = 'standby1, standby2';
+ set persistent seq_page_cost = 0.9;
+ set persistent autovacuum_naptime To 30;
+ set persistent search_path = 'public';
+ set persistent default_tablespace = '';
+ set persistent checkpoint_timeout = 1;
+ ERROR:  1 is outside the valid range for parameter "checkpoint_timeout" (30 .. 3600)
+ set persistent checkpoint_timeout = 3700;
+ ERROR:  3700 is outside the valid range for parameter "checkpoint_timeout" (30 .. 3600)
+ set persistent checkpoint_timeout = 90;
+ set persistent restart_after_crash TO on;
+ set persistent work_mem = '2 MB';
+ -- check the new configurations.
+ select pg_sleep(1);
+  pg_sleep 
+ ----------
+  
+ (1 row)
+ 
+ show log_connections;
+  log_connections 
+ -----------------
+  off
+ (1 row)
+ 
+ show max_connections;
+  max_connections 
+ -----------------
+  100
+ (1 row)
+ 
+ show wal_level;
+  wal_level 
+ -----------
+  minimal
+ (1 row)
+ 
+ show authentication_timeout;
+  authentication_timeout 
+ ------------------------
+  10s
+ (1 row)
+ 
+ show vacuum_cost_delay;
+  vacuum_cost_delay 
+ -------------------
+  0
+ (1 row)
+ 
+ show bgwriter_lru_multiplier;
+  bgwriter_lru_multiplier 
+ -------------------------
+  3.2
+ (1 row)
+ 
+ show full_page_writes;
+  full_page_writes 
+ ------------------
+  off
+ (1 row)
+ 
+ show synchronous_commit;
+  synchronous_commit 
+ --------------------
+  off
+ (1 row)
+ 
+ show synchronous_standby_names;
+  synchronous_standby_names 
+ ---------------------------
+  standby1, standby2
+ (1 row)
+ 
+ show seq_page_cost;
+  seq_page_cost 
+ ---------------
+  0.9
+ (1 row)
+ 
+ show autovacuum_naptime;
+  autovacuum_naptime 
+ --------------------
+  30s
+ (1 row)
+ 
+ show search_path;
+  search_path 
+ -------------
+  public
+ (1 row)
+ 
+ show default_tablespace;
+  default_tablespace 
+ --------------------
+  
+ (1 row)
+ 
+ show checkpoint_timeout;
+  checkpoint_timeout 
+ --------------------
+  90s
+ (1 row)
+ 
+ show restart_after_crash;
+  restart_after_crash 
+ ---------------------
+  on
+ (1 row)
+ 
+ show work_mem;
+  work_mem 
+ ----------
+  2MB
+ (1 row)
+ 
+ -- reconnect to see the new configurations
+ \c
+ select name, setting from pg_settings where name = 'log_connections';
+       name       | setting 
+ -----------------+---------
+  log_connections | on
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'max_connections';
+       name       | setting 
+ -----------------+---------
+  max_connections | 100
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'wal_level';
+    name    | setting 
+ -----------+---------
+  wal_level | minimal
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'authentication_timeout';
+           name          | setting 
+ ------------------------+---------
+  authentication_timeout | 10
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'vacuum_cost_delay';
+        name        | setting 
+ -------------------+---------
+  vacuum_cost_delay | 0
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'bgwriter_lru_multiplier';
+           name           | setting 
+ -------------------------+---------
+  bgwriter_lru_multiplier | 3.2
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'full_page_writes';
+        name       | setting 
+ ------------------+---------
+  full_page_writes | off
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'synchronous_commit';
+         name        | setting 
+ --------------------+---------
+  synchronous_commit | off
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'synchronous_standby_names';
+            name            |      setting       
+ ---------------------------+--------------------
+  synchronous_standby_names | standby1, standby2
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'seq_page_cost';
+      name      | setting 
+ ---------------+---------
+  seq_page_cost | 0.9
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'autovacuum_naptime';
+         name        | setting 
+ --------------------+---------
+  autovacuum_naptime | 30
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'search_path';
+     name     | setting 
+ -------------+---------
+  search_path | public
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'default_tablespace';
+         name        | setting 
+ --------------------+---------
+  default_tablespace | 
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'checkpoint_timeout';
+         name        | setting 
+ --------------------+---------
+  checkpoint_timeout | 90
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'restart_after_crash';
+         name         | setting 
+ ---------------------+---------
+  restart_after_crash | on
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'work_mem';
+    name   | setting 
+ ----------+---------
+  work_mem | 2048
+ (1 row)
+ 
+ -- Reset the some of the parameters which are set in above
+ set persistent log_connections = default;
+ set persistent max_connections To default;
+ ERROR:  parameter "max_connections" cannot be changed
+ set persistent authentication_timeout To default;
+ set persistent synchronous_commit = default;
+ set persistent restart_after_crash To default;
+ -- reload the new configurations.
+ select pg_sleep(1);
+  pg_sleep 
+ ----------
+  
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'log_connections';
+       name       | setting 
+ -----------------+---------
+  log_connections | on
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'max_connections';
+       name       | setting 
+ -----------------+---------
+  max_connections | 100
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'authentication_timeout';
+           name          | setting 
+ ------------------------+---------
+  authentication_timeout | 60
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'seq_page_cost';
+      name      | setting 
+ ---------------+---------
+  seq_page_cost | 0.9
+ (1 row)
+ 
+ select name, setting from pg_settings where name = 'restart_after_crash';
+         name         | setting 
+ ---------------------+---------
+  restart_after_crash | on
+ (1 row)
+ 
+ -- reconnect to see the new configurations
+ \c
+ show log_connections;
+  log_connections 
+ -----------------
+  off
+ (1 row)
+ 
+ show max_connections;
+  max_connections 
+ -----------------
+  100
+ (1 row)
+ 
+ show authentication_timeout;
+  authentication_timeout 
+ ------------------------
+  1min
+ (1 row)
+ 
+ show seq_page_cost;
+  seq_page_cost 
+ ---------------
+  0.9
+ (1 row)
+ 
+ show restart_after_crash;
+  restart_after_crash 
+ ---------------------
+  on
+ (1 row)
+ 
+ -- set the parameters again which are reset above.
+ set persistent log_connections To yes;
+ set persistent max_connections = 50;
+ ERROR:  parameter "max_connections" cannot be changed
+ set persistent authentication_timeout = 20;
+ set persistent synchronous_commit To off;
+ set persistent restart_after_crash To yes;
+ -- reload the new configurations.
+ select pg_sleep(1);
+  pg_sleep 
+ ----------
+  
+ (1 row)
+ 
+ show log_connections;
+  log_connections 
+ -----------------
+  off
+ (1 row)
+ 
+ show max_connections;
+  max_connections 
+ -----------------
+  100
+ (1 row)
+ 
+ show authentication_timeout;
+  authentication_timeout 
+ ------------------------
+  20s
+ (1 row)
+ 
+ show seq_page_cost;
+  seq_page_cost 
+ ---------------
+  0.9
+ (1 row)
+ 
+ show restart_after_crash;
+  restart_after_crash 
+ ---------------------
+  on
+ (1 row)
+ 
+ -- reconnect to see the new configurations
+ \c
+ show log_connections;
+  log_connections 
+ -----------------
+  on
+ (1 row)
+ 
+ show max_connections;
+  max_connections 
+ -----------------
+  100
+ (1 row)
+ 
+ show authentication_timeout;
+  authentication_timeout 
+ ------------------------
+  20s
+ (1 row)
+ 
+ show seq_page_cost;
+  seq_page_cost 
+ ---------------
+  0.9
+ (1 row)
+ 
+ show restart_after_crash;
+  restart_after_crash 
+ ---------------------
+  on
+ (1 row)
+ 
+ -- set a configuration param inside a function
+ create or replace function persistent_func() returns integer as
+ $$
+ begin
+ set persistent enable_hashagg=off;
+ return 1;
+ end;
+ $$ Language plpgsql;
+ select persistent_func();
+ ERROR:  SET PERSISTENT cannot be executed from a function or multi-command string
+ CONTEXT:  SQL statement "set persistent enable_hashagg=off"
+ PL/pgSQL function persistent_func() line 3 at SQL statement
+ show enable_hashagg;
+  enable_hashagg 
+ ----------------
+  on
+ (1 row)
+ 
+ select pg_sleep(1);
+  pg_sleep 
+ ----------
+  
+ (1 row)
+ 
+ show enable_hashagg;
+  enable_hashagg 
+ ----------------
+  on
+ (1 row)
+ 
+ create or replace function persistent_func() returns integer as
+ $$
+ begin
+ set persistent enable_hashagg=default;
+ return 1;
+ end;
+ $$ Language plpgsql;
+ select persistent_func();
+ ERROR:  SET PERSISTENT cannot be executed from a function or multi-command string
+ CONTEXT:  SQL statement "set persistent enable_hashagg=default"
+ PL/pgSQL function persistent_func() line 3 at SQL statement
+ select pg_sleep(1);
+  pg_sleep 
+ ----------
+  
+ (1 row)
+ 
+ show enable_hashagg;
+  enable_hashagg 
+ ----------------
+  on
+ (1 row)
+ 
+ Drop function persistent_func();
+ -- Reset all parameters which are set at above.
+ set persistent log_connections To default;
+ set persistent max_connections = default;
+ ERROR:  parameter "max_connections" cannot be changed
+ set persistent wal_level To default;
+ ERROR:  parameter "wal_level" cannot be changed
+ set persistent authentication_timeout To default;
+ set persistent vacuum_cost_delay = default;
+ set persistent bgwriter_lru_multiplier To default;
+ set persistent full_page_writes To default;
+ set persistent synchronous_commit = default;
+ set persistent synchronous_standby_names To default;
+ set persistent seq_page_cost To default;
+ set persistent autovacuum_naptime = default;
+ set persistent search_path To default;
+ set persistent default_tablespace To default;
+ set persistent checkpoint_timeout To default;
+ set persistent restart_after_crash = default;
+ set persistent work_mem = default;
+ select pg_sleep(1);
+  pg_sleep 
+ ----------
+  
+ (1 row)
+ 
+ -- Normal user tries to set
+ create user test password 'test';
+ SET SESSION AUTHORIZATION test;
+ set persistent enable_seqscan=off;
+ ERROR:  must be superuser to execute SET PERSISTENT command
+ \c -
+ drop user test;
+ -- In a transaction block tries to set
+ start transaction;
+ set persistent enable_hashagg=off;
+ ERROR:  SET PERSISTENT cannot run inside a transaction block
+ commit;
+ start transaction;
+ set persistent enable_hashagg=default;
+ ERROR:  SET PERSISTENT cannot run inside a transaction block
+ commit;
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
***************
*** 108,111 **** test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
  test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
  
  # run stats by itself because its delay may be insufficient under heavy load
! test: stats
--- 108,114 ----
  test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
  
  # run stats by itself because its delay may be insufficient under heavy load
! test: stats 
! 
! # run persistent by itself because it modifies the configuration parameters
! test: persistent
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
***************
*** 137,139 **** test: largeobject
--- 137,140 ----
  test: with
  test: xml
  test: stats
+ test: persistent
*** /dev/null
--- b/src/test/regress/sql/persistent.sql
***************
*** 0 ****
--- 1,190 ----
+ -- parameter which cannot be set after connection start.
+ set persistent log_connections=on;
+ 
+ -- parameter can only be set at during server start.
+ set persistent max_connections To 10;
+ set persistent wal_level = hot_standby;
+ 
+ -- parameter can set by the backend
+ set persistent authentication_timeout = 10;
+ set persistent vacuum_cost_delay To 200;
+ set persistent bgwriter_lru_multiplier = 3.2;
+ set persistent full_page_writes = off;
+ set persistent synchronous_commit To off;
+ set persistent synchronous_standby_names = 'standby1, standby2';
+ set persistent seq_page_cost = 0.9;
+ set persistent autovacuum_naptime To 30;
+ set persistent search_path = 'public';
+ set persistent default_tablespace = '';
+ set persistent checkpoint_timeout = 1;
+ set persistent checkpoint_timeout = 3700;
+ set persistent checkpoint_timeout = 90;
+ set persistent restart_after_crash TO on;
+ set persistent work_mem = '2 MB';
+ 
+ -- check the new configurations.
+ select pg_sleep(1);
+ show log_connections;
+ 
+ show max_connections;
+ show wal_level;
+ 
+ show authentication_timeout;
+ show vacuum_cost_delay;
+ show bgwriter_lru_multiplier;
+ show full_page_writes;
+ show synchronous_commit;
+ show synchronous_standby_names;
+ show seq_page_cost;
+ show autovacuum_naptime;
+ show search_path;
+ show default_tablespace;
+ show checkpoint_timeout;
+ show restart_after_crash;
+ show work_mem;
+ 
+ 
+ -- reconnect to see the new configurations
+ \c
+ 
+ select name, setting from pg_settings where name = 'log_connections';
+ 
+ select name, setting from pg_settings where name = 'max_connections';
+ select name, setting from pg_settings where name = 'wal_level';
+ 
+ select name, setting from pg_settings where name = 'authentication_timeout';
+ select name, setting from pg_settings where name = 'vacuum_cost_delay';
+ select name, setting from pg_settings where name = 'bgwriter_lru_multiplier';
+ select name, setting from pg_settings where name = 'full_page_writes';
+ select name, setting from pg_settings where name = 'synchronous_commit';
+ select name, setting from pg_settings where name = 'synchronous_standby_names';
+ select name, setting from pg_settings where name = 'seq_page_cost';
+ select name, setting from pg_settings where name = 'autovacuum_naptime';
+ select name, setting from pg_settings where name = 'search_path';
+ select name, setting from pg_settings where name = 'default_tablespace';
+ select name, setting from pg_settings where name = 'checkpoint_timeout';
+ select name, setting from pg_settings where name = 'restart_after_crash';
+ select name, setting from pg_settings where name = 'work_mem';
+ 
+ -- Reset the some of the parameters which are set in above
+ set persistent log_connections = default;
+ 
+ set persistent max_connections To default;
+ 
+ set persistent authentication_timeout To default;
+ set persistent synchronous_commit = default;
+ set persistent restart_after_crash To default;
+ 
+ -- reload the new configurations.
+ select pg_sleep(1);
+ 
+ select name, setting from pg_settings where name = 'log_connections';
+ select name, setting from pg_settings where name = 'max_connections';
+ select name, setting from pg_settings where name = 'authentication_timeout';
+ select name, setting from pg_settings where name = 'seq_page_cost';
+ select name, setting from pg_settings where name = 'restart_after_crash';
+ 
+ -- reconnect to see the new configurations
+ \c
+ 
+ show log_connections;
+ show max_connections;
+ show authentication_timeout;
+ show seq_page_cost;
+ show restart_after_crash;
+ 
+ -- set the parameters again which are reset above.
+ set persistent log_connections To yes;
+ 
+ set persistent max_connections = 50;
+ 
+ set persistent authentication_timeout = 20;
+ set persistent synchronous_commit To off;
+ set persistent restart_after_crash To yes;
+ 
+ -- reload the new configurations.
+ select pg_sleep(1);
+ 
+ show log_connections;
+ show max_connections;
+ show authentication_timeout;
+ show seq_page_cost;
+ show restart_after_crash;
+ 
+ -- reconnect to see the new configurations
+ \c
+ show log_connections;
+ show max_connections;
+ show authentication_timeout;
+ show seq_page_cost;
+ show restart_after_crash;
+ 
+ -- set a configuration param inside a function
+ create or replace function persistent_func() returns integer as
+ $$
+ begin
+ set persistent enable_hashagg=off;
+ return 1;
+ end;
+ $$ Language plpgsql;
+ 
+ select persistent_func();
+ 
+ show enable_hashagg;
+ select pg_sleep(1);
+ show enable_hashagg;
+ 
+ create or replace function persistent_func() returns integer as
+ $$
+ begin
+ set persistent enable_hashagg=default;
+ return 1;
+ end;
+ $$ Language plpgsql;
+ 
+ select persistent_func();
+ 
+ select pg_sleep(1);
+ show enable_hashagg;
+ 
+ Drop function persistent_func();
+ 
+ -- Reset all parameters which are set at above.
+ set persistent log_connections To default;
+ 
+ set persistent max_connections = default;
+ set persistent wal_level To default;
+ 
+ set persistent authentication_timeout To default;
+ set persistent vacuum_cost_delay = default;
+ set persistent bgwriter_lru_multiplier To default;
+ set persistent full_page_writes To default;
+ set persistent synchronous_commit = default;
+ set persistent synchronous_standby_names To default;
+ set persistent seq_page_cost To default;
+ set persistent autovacuum_naptime = default;
+ set persistent search_path To default;
+ set persistent default_tablespace To default;
+ set persistent checkpoint_timeout To default;
+ set persistent restart_after_crash = default;
+ set persistent work_mem = default;
+ 
+ select pg_sleep(1);
+ 
+ 
+ -- Normal user tries to set
+ create user test password 'test';
+ SET SESSION AUTHORIZATION test;
+ set persistent enable_seqscan=off;
+ \c -
+ drop user test;
+ 
+ -- In a transaction block tries to set
+ start transaction;
+ set persistent enable_hashagg=off;
+ commit;
+ 
+ start transaction;
+ set persistent enable_hashagg=default;
+ commit;
+