set_persistent_one_setting_per_file.patch

text/x-patch

Filename: set_persistent_one_setting_per_file.patch
Type: text/x-patch
Part: 0
Message: Re: Re: Proposal for Allow postgresql.conf values to be changed via SQL [review]

Patch

Format: unified
File+
doc/src/sgml/config.sgml 16 0
doc/src/sgml/keywords.sgml 7 0
doc/src/sgml/ref/set.sgml 32 7
doc/src/sgml/storage.sgml 12 1
src/backend/commands/extension.c 6 3
src/backend/nodes/copyfuncs.c 1 0
src/backend/nodes/equalfuncs.c 1 0
src/backend/parser/gram.y 63 51
src/backend/utils/adt/ri_triggers.c 2 1
src/backend/utils/misc/guc.c 223 14
src/backend/utils/misc/guc-file.l 2 2
src/backend/utils/misc/postgresql.conf.sample 9 0
src/bin/initdb/initdb.c 5 1
src/include/nodes/parsenodes.h 1 0
src/include/parser/kwlist.h 1 0
src/include/pg_config_manual.h 5 0
src/include/utils/guc.h 6 2
src/include/utils/guc_tables.h 5 1
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ae6ee60..8713fd8 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -158,6 +158,22 @@ SET ENABLE_SEQSCAN TO OFF;
      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 new value will be effective immediately in
+     the session and the last persistent value for a transaction is
+     committed into its own configuration file. It becomes effective
+     cluster-wide after reloading the server configuration (<acronym>SIGHUP</>)
+     or server startup. 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">
diff --git a/doc/src/sgml/keywords.sgml b/doc/src/sgml/keywords.sgml
index 576fd65..ed19784 100644
--- a/doc/src/sgml/keywords.sgml
+++ b/doc/src/sgml/keywords.sgml
@@ -3388,6 +3388,13 @@
     <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>
diff --git a/doc/src/sgml/ref/set.sgml b/doc/src/sgml/ref/set.sgml
index 21745db..5449362 100644
--- a/doc/src/sgml/ref/set.sgml
+++ b/doc/src/sgml/ref/set.sgml
@@ -21,8 +21,8 @@ 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 }
+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,6 +50,18 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
   </para>
 
   <para>
+   <command>SET PERSISTENT</command> writes the configuration parameter
+   value to the <filename><replaceable class="PARAMETER">configuration_parameter</replaceable>.conf</filename>
+   file. The new value will be effective immediately in the session and
+   is transactional. The last persistent value will be written to the
+   configuration file. The value written to the file will be effective
+   after reloading the server configuration (SIGHUP) or server startup.
+   With <literal>DEFAULT</literal>, it removes the <filename><replaceable class="PARAMETER">configuration_parameter</replaceable>.conf</filename>
+   file but the new effective value for the session will be what the
+   default was at the last server startup or configuration reloading.
+  </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,8 +110,8 @@ 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.)
+      (This is the default option, if any of <literal>SESSION</>,
+      <literal>LOCAL</> or <literal>PERSISTENT</> are not provided.)
      </para>
     </listitem>
    </varlistentry>
@@ -119,6 +131,19 @@ SET [ SESSION | LOCAL ] TIME ZONE { <replaceable class="PARAMETER">timezone</rep
    </varlistentry>
 
    <varlistentry>
+    <term><literal>PERSISTENT</></term>
+    <listitem>
+     <para>
+      Writes updated configuration parameters to
+      <filename><replaceable class="PARAMETER">configuration_parameter</replaceable>.conf</filename>
+      These configuration parameters take effect after reloading the
+      configuration file (SIGHUP) or in next server start based on
+      the type of configuration parameter modified.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><replaceable class="PARAMETER">configuration_parameter</replaceable></term>
     <listitem>
      <para>
@@ -154,7 +179,7 @@ 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
+      <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,7 +189,7 @@ 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
+      <para><literal>SET [ PERSISTENT ] NAMES <replaceable>value</></> is an alias for
        <literal>SET client_encoding TO <replaceable>value</></>.
       </para>
      </listitem>
@@ -192,7 +217,7 @@ SELECT setseed(<replaceable>value</replaceable>);
     <varlistentry>
      <term><literal>TIME ZONE</literal></term>
      <listitem>
-      <para><literal>SET TIME ZONE <replaceable>value</></> is an alias
+      <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
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index e0a93c1..ceb5a5f 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -34,7 +34,8 @@ 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).
+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,6 +58,11 @@ Item
 </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,6 +259,11 @@ where <replaceable>PPP</> is the PID of the owning backend and
 <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">
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 6b7cb5b..80d8e5b 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -818,11 +818,13 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 	if (client_min_messages < WARNING)
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
-								 GUC_ACTION_SAVE, true, 0);
+								 GUC_ACTION_SAVE, GUC_ACTION_NONE,
+								 true, 0);
 	if (log_min_messages < WARNING)
 		(void) set_config_option("log_min_messages", "warning",
 								 PGC_SUSET, PGC_S_SESSION,
-								 GUC_ACTION_SAVE, true, 0);
+								 GUC_ACTION_SAVE, GUC_ACTION_NONE,
+								 true, 0);
 
 	/*
 	 * Set up the search path to contain the target schema, then the schemas
@@ -847,7 +849,8 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 
 	(void) set_config_option("search_path", pathbuf.data,
 							 PGC_USERSET, PGC_S_SESSION,
-							 GUC_ACTION_SAVE, true, 0);
+							 GUC_ACTION_SAVE, GUC_ACTION_NONE,
+							 true, 0);
 
 	/*
 	 * Set creating_extension and related variables so that
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index fd3823a..ffcb037 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3281,6 +3281,7 @@ _copyVariableSetStmt(const VariableSetStmt *from)
 	COPY_STRING_FIELD(name);
 	COPY_NODE_FIELD(args);
 	COPY_SCALAR_FIELD(is_local);
+	COPY_SCALAR_FIELD(is_persistent);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 085cd5b..d3dab8f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1568,6 +1568,7 @@ _equalVariableSetStmt(const VariableSetStmt *a, const VariableSetStmt *b)
 	COMPARE_STRING_FIELD(name);
 	COMPARE_NODE_FIELD(args);
 	COMPARE_SCALAR_FIELD(is_local);
+	COMPARE_SCALAR_FIELD(is_persistent);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9d07f30..8cb388c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -404,7 +404,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <istmt>	insert_rest
 
-%type <vsetstmt> set_rest set_rest_more SetResetClause FunctionSetResetClause
+%type <vsetstmt> set_rest set_rest_more set_persistent SetResetClause FunctionSetResetClause
 
 %type <node>	TableElement TypedTableElement ConstraintElem TableFuncElement
 %type <node>	columnDef columnOptions
@@ -573,7 +573,7 @@ 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
+	PARSER PARTIAL PARTITION PASSING PASSWORD PERSISTENT PLACING PLANS POSITION
 	PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
 	PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM
 
@@ -1312,6 +1312,12 @@ VariableSetStmt:
 					n->is_local = false;
 					$$ = (Node *) n;
 				}
+			| SET PERSISTENT set_persistent
+				{
+					VariableSetStmt *n = $3;
+					n->is_persistent = true;
+					$$ = (Node *) n;
+				}
 		;
 
 set_rest:
@@ -1335,6 +1341,51 @@ set_rest:
 			;
 
 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);
@@ -1373,6 +1424,14 @@ set_rest_more:	/* Generic SET syntaxes: */
 					$$ = n;
 				}
 			/* Special syntaxes mandated by SQL standard: */
+			| SCHEMA Sconst
+				{
+					VariableSetStmt *n = makeNode(VariableSetStmt);
+					n->kind = VAR_SET_VALUE;
+					n->name = "search_path";
+					n->args = list_make1(makeStringConst($2, @2));
+					$$ = n;
+				}
 			| TIME ZONE zone_value
 				{
 					VariableSetStmt *n = makeNode(VariableSetStmt);
@@ -1384,22 +1443,6 @@ 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);
-					n->kind = VAR_SET_VALUE;
-					n->name = "search_path";
-					n->args = list_make1(makeStringConst($2, @2));
-					$$ = n;
-				}
 			| NAMES opt_encoding
 				{
 					VariableSetStmt *n = makeNode(VariableSetStmt);
@@ -1411,29 +1454,6 @@ 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);
@@ -1442,16 +1462,7 @@ 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
@@ -12826,6 +12837,7 @@ unreserved_keyword:
 			| PARTITION
 			| PASSING
 			| PASSWORD
+			| PERSISTENT
 			| PLANS
 			| PRECEDING
 			| PREPARE
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 4322844..2cbeb2d 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2408,7 +2408,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
 	snprintf(workmembuf, sizeof(workmembuf), "%d", maintenance_work_mem);
 	(void) set_config_option("work_mem", workmembuf,
 							 PGC_USERSET, PGC_S_SESSION,
-							 GUC_ACTION_SAVE, true, 0);
+							 GUC_ACTION_SAVE, GUC_ACTION_NONE,
+							 true, 0);
 
 	if (SPI_connect() != SPI_OK_CONNECT)
 		elog(ERROR, "SPI_connect failed");
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index d3565a5..b9c0aab 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -246,7 +246,7 @@ ProcessConfigFile(GucContext context)
 		/* Now we can re-apply the wired-in default (i.e., the boot_val) */
 		if (set_config_option(gconf->name, NULL,
 							  context, PGC_S_DEFAULT,
-							  GUC_ACTION_SET, true, 0) > 0)
+							  GUC_ACTION_SET, GUC_ACTION_NONE, true, 0) > 0)
 		{
 			/* Log the change if appropriate */
 			if (context == PGC_SIGHUP)
@@ -301,7 +301,7 @@ ProcessConfigFile(GucContext context)
 
 		scres = set_config_option(item->name, item->value,
 								  context, PGC_S_FILE,
-								  GUC_ACTION_SET, true, 0);
+								  GUC_ACTION_SET, GUC_ACTION_NONE, true, 0);
 		if (scres > 0)
 		{
 			/* variable was updated, so log the change if appropriate */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 98149fc..73794cd 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -138,6 +138,8 @@ extern bool optimize_bounded_sort;
 
 static int	GUC_check_errcode_value;
 
+static char	   *GUC_autodir = NULL;
+
 /* global variables for check hook support */
 char	   *GUC_check_errmsg_string;
 char	   *GUC_check_errdetail_string;
@@ -4335,6 +4337,10 @@ push_old_value(struct config_generic * gconf, GucAction action)
 		Assert(stack->nest_level == GUCNestLevel);
 		switch (action)
 		{
+			case GUC_ACTION_NONE:
+				/* push_old_value must not be called with this action */
+				Assert(action != GUC_ACTION_NONE);
+				break;
 			case GUC_ACTION_SET:
 				/* SET overrides any prior action at same nest level */
 				if (stack->state == GUC_SET_LOCAL)
@@ -4358,6 +4364,17 @@ push_old_value(struct config_generic * gconf, GucAction action)
 				/* Could only have a prior SAVE of same variable */
 				Assert(stack->state == GUC_SAVE);
 				break;
+			case GUC_ACTION_PERSISTENT:
+				stack->remove_persistent = false;
+				stack->has_persistent = true;
+				set_stack_value(gconf, &stack->persistent);
+				break;
+			case GUC_ACTION_REMOVE_PERSISTENT:
+				if (stack->has_persistent)
+					discard_stack_value(gconf, &stack->persistent);
+				stack->remove_persistent = true;
+				stack->has_persistent = false;
+				break;
 		}
 		Assert(guc_dirty);		/* must be set already */
 		return;
@@ -4375,19 +4392,38 @@ push_old_value(struct config_generic * gconf, GucAction action)
 	stack->nest_level = GUCNestLevel;
 	switch (action)
 	{
+		case GUC_ACTION_NONE:
+			/* push_old_value must not be called with this action */
+			Assert(action != GUC_ACTION_NONE);
+			break;
 		case GUC_ACTION_SET:
 			stack->state = GUC_SET;
 			break;
 		case GUC_ACTION_LOCAL:
 			stack->state = GUC_LOCAL;
 			break;
+		case GUC_ACTION_PERSISTENT:
+		case GUC_ACTION_REMOVE_PERSISTENT:
+			stack->state = GUC_PERSISTENT;
+			break;
 		case GUC_ACTION_SAVE:
 			stack->state = GUC_SAVE;
 			break;
 	}
 	stack->source = gconf->source;
 	stack->scontext = gconf->scontext;
-	set_stack_value(gconf, &stack->prior);
+	if (stack->state == GUC_PERSISTENT)
+	{
+		if (action == GUC_ACTION_PERSISTENT)
+		{
+			set_stack_value(gconf, &stack->persistent);
+			stack->has_persistent = true;
+		}
+		else
+			stack->remove_persistent = true;
+	}
+	else
+		set_stack_value(gconf, &stack->prior);
 
 	gconf->stack = stack;
 
@@ -4425,6 +4461,108 @@ NewGUCNestLevel(void)
 	return ++GUCNestLevel;
 }
 
+static void
+SetPersistentDir(void)
+{
+	if (GUC_autodir == NULL)
+	{
+		char	config_dir[MAXPGPATH];
+
+		strncpy(config_dir, ConfigFileName, sizeof(config_dir));
+		get_parent_directory(config_dir);
+		strcat(config_dir, "/");
+		strcat(config_dir, PG_AUTOCONF_DIR);
+		GUC_autodir = (char *) MemoryContextAllocZero(TopMemoryContext, MAXPGPATH);
+		strcpy(GUC_autodir, config_dir);
+	}
+}
+
+/*
+ * Write a persistent GUC into its own file and write only warnings
+ * when the file cannot be written. This is because we are COMMITting
+ * the transaction, we cannot rollback by throwing an elog(ERROR).
+ * Returns false if an error happened, to allow warning once.
+ */
+static bool
+WritePersistentGUC(struct config_generic *gconf, config_var_value *value, bool warn)
+{
+	char		tmp_file[MAXPGPATH];
+	char		config_file[MAXPGPATH];
+	int		fd, retval;
+	StringInfoData	buf;
+	char	   *escaped;
+
+	SetPersistentDir();
+
+	sprintf(tmp_file, "%s/%s.conf.%d", GUC_autodir, gconf->name, (int) getpid());
+
+	fd = open(tmp_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
+	if (fd == -1)
+	{
+		if (warn)
+			ereport(WARNING,
+					(errcode_for_file_access(),
+					 errmsg("Failed to open temporary file: %m")));
+		return false;
+	}
+
+	initStringInfo(&buf);
+	switch (gconf->vartype)
+	{
+		case PGC_BOOL:
+			appendStringInfo(&buf, "%s = %s\n", gconf->name, (value->val.boolval ? "on" : "off"));
+			break;
+		case PGC_INT:
+			appendStringInfo(&buf, "%s = %d\n", gconf->name, value->val.intval);
+			break;
+		case PGC_REAL:
+			appendStringInfo(&buf, "%s = %lf\n", gconf->name, value->val.realval);
+			break;
+		case PGC_STRING:
+			escaped = escape_single_quotes_ascii(value->val.stringval);
+			appendStringInfo(&buf, "%s = '%s'\n", gconf->name, escaped);
+			free(escaped);
+			break;
+		case PGC_ENUM:
+			appendStringInfo(&buf, "%s = '%s'\n", gconf->name, 
+						config_enum_lookup_by_value((struct config_enum *)gconf,
+												value->val.enumval));
+			break;
+	}
+
+	retval = write(fd, buf.data, buf.len);
+
+	pfree(buf.data);
+
+	if (retval != buf.len)
+	{
+		if (warn)
+			ereport(WARNING,
+					(errcode_for_file_access(),
+					 errmsg("Failed to open temporary file: %m")));
+		close(fd);
+		unlink(tmp_file);
+		return false;
+	}
+
+	close(fd);
+
+	sprintf(config_file, "%s/%s.conf", GUC_autodir, gconf->name);
+	rename(tmp_file, config_file);
+
+	return true;
+}
+
+static void
+RemovePersistentGUC(struct config_generic *gconf)
+{
+	char		config_file[MAXPGPATH];
+
+	SetPersistentDir();
+	sprintf(config_file, "%s/%s.conf", GUC_autodir, gconf->name);
+	unlink(config_file);
+}
+
 /*
  * Do GUC processing at transaction or subtransaction commit or abort, or
  * when exiting a function that has proconfig settings, or when undoing a
@@ -4437,6 +4575,7 @@ void
 AtEOXact_GUC(bool isCommit, int nestLevel)
 {
 	bool		still_dirty;
+	bool		warn_persistent = true;
 	int			i;
 
 	/*
@@ -4491,10 +4630,18 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
 				/* transaction commit */
 				if (stack->state == GUC_SET_LOCAL)
 					restoreMasked = true;
-				else if (stack->state == GUC_SET)
+				else if (stack->state == GUC_SET || stack->state == GUC_PERSISTENT)
 				{
 					/* we keep the current active value */
 					discard_stack_value(gconf, &stack->prior);
+					if (stack->has_persistent)
+					{
+						bool	retval = WritePersistentGUC(gconf, &stack->persistent,
+												warn_persistent);
+						warn_persistent = warn_persistent && retval;
+					}
+					if (stack->remove_persistent)
+						RemovePersistentGUC(gconf);
 				}
 				else	/* must be GUC_LOCAL */
 					restorePrior = true;
@@ -4522,6 +4669,14 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
 						discard_stack_value(gconf, &stack->prior);
 						if (prev->state == GUC_SET_LOCAL)
 							discard_stack_value(gconf, &prev->masked);
+						if (stack->has_persistent || stack->remove_persistent)
+						{
+							if (prev->has_persistent)
+								discard_stack_value(gconf, &prev->persistent);
+							prev->remove_persistent = stack->remove_persistent;
+							prev->has_persistent = stack->has_persistent;
+							prev->persistent = stack->persistent;
+						}
 						prev->state = GUC_SET;
 						break;
 
@@ -4548,8 +4703,22 @@ AtEOXact_GUC(bool isCommit, int nestLevel)
 						if (prev->state == GUC_SET_LOCAL)
 							discard_stack_value(gconf, &prev->masked);
 						prev->masked = stack->masked;
+						if (stack->has_persistent || stack->remove_persistent)
+						{
+							if (prev->has_persistent)
+								discard_stack_value(gconf, &prev->persistent);
+							prev->remove_persistent = stack->remove_persistent;
+							prev->has_persistent = stack->has_persistent;
+							prev->persistent = stack->persistent;
+						}
 						prev->state = GUC_SET_LOCAL;
 						break;
+
+					case GUC_PERSISTENT:
+						prev->remove_persistent = stack->remove_persistent;
+						prev->has_persistent = stack->has_persistent;
+						prev->persistent = stack->persistent;
+						break;
 				}
 			}
 
@@ -5134,7 +5303,8 @@ config_enum_get_options(struct config_enum * record, const char *prefix,
 int
 set_config_option(const char *name, const char *value,
 				  GucContext context, GucSource source,
-				  GucAction action, bool changeVal, int elevel)
+				  GucAction action, GucAction action2,
+				  bool changeVal, int elevel)
 {
 	struct config_generic *record;
 	bool		prohibitValueChange = false;
@@ -5396,6 +5566,9 @@ set_config_option(const char *name, const char *value,
 									newextra);
 					conf->gen.source = source;
 					conf->gen.scontext = context;
+
+					if (!makeDefault && (action2 == GUC_ACTION_PERSISTENT))
+						push_old_value(&conf->gen, GUC_ACTION_PERSISTENT);
 				}
 				if (makeDefault)
 				{
@@ -5421,6 +5594,8 @@ set_config_option(const char *name, const char *value,
 						}
 					}
 				}
+				if (action2 == GUC_ACTION_REMOVE_PERSISTENT)
+					push_old_value(&conf->gen, GUC_ACTION_REMOVE_PERSISTENT);
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
@@ -5500,6 +5675,9 @@ set_config_option(const char *name, const char *value,
 									newextra);
 					conf->gen.source = source;
 					conf->gen.scontext = context;
+
+					if (!makeDefault && action2 == GUC_ACTION_PERSISTENT)
+						push_old_value(&conf->gen, GUC_ACTION_PERSISTENT);
 				}
 				if (makeDefault)
 				{
@@ -5525,6 +5703,8 @@ set_config_option(const char *name, const char *value,
 						}
 					}
 				}
+				if (action2 == GUC_ACTION_REMOVE_PERSISTENT)
+					push_old_value(&conf->gen, GUC_ACTION_REMOVE_PERSISTENT);
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
@@ -5601,6 +5781,9 @@ set_config_option(const char *name, const char *value,
 									newextra);
 					conf->gen.source = source;
 					conf->gen.scontext = context;
+
+					if (!makeDefault && action2 == GUC_ACTION_PERSISTENT)
+						push_old_value(&conf->gen, GUC_ACTION_PERSISTENT);
 				}
 				if (makeDefault)
 				{
@@ -5626,6 +5809,8 @@ set_config_option(const char *name, const char *value,
 						}
 					}
 				}
+				if (action2 == GUC_ACTION_REMOVE_PERSISTENT)
+					push_old_value(&conf->gen, GUC_ACTION_REMOVE_PERSISTENT);
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
@@ -5722,6 +5907,9 @@ set_config_option(const char *name, const char *value,
 									newextra);
 					conf->gen.source = source;
 					conf->gen.scontext = context;
+
+					if (!makeDefault && action2 == GUC_ACTION_PERSISTENT)
+						push_old_value(&conf->gen, GUC_ACTION_PERSISTENT);
 				}
 
 				if (makeDefault)
@@ -5749,6 +5937,8 @@ set_config_option(const char *name, const char *value,
 						}
 					}
 				}
+				if (action2 == GUC_ACTION_REMOVE_PERSISTENT)
+					push_old_value(&conf->gen, GUC_ACTION_REMOVE_PERSISTENT);
 
 				/* Perhaps we didn't install newval anywhere */
 				if (newval && !string_field_used(conf, newval))
@@ -5830,6 +6020,9 @@ set_config_option(const char *name, const char *value,
 									newextra);
 					conf->gen.source = source;
 					conf->gen.scontext = context;
+
+					if (!makeDefault && action2 == GUC_ACTION_PERSISTENT)
+						push_old_value(&conf->gen, GUC_ACTION_PERSISTENT);
 				}
 				if (makeDefault)
 				{
@@ -5855,6 +6048,8 @@ set_config_option(const char *name, const char *value,
 						}
 					}
 				}
+				if (action2 == GUC_ACTION_REMOVE_PERSISTENT)
+					push_old_value(&conf->gen, GUC_ACTION_REMOVE_PERSISTENT);
 
 				/* Perhaps we didn't install newextra anywhere */
 				if (newextra && !extra_field_used(&conf->gen, newextra))
@@ -5912,7 +6107,7 @@ SetConfigOption(const char *name, const char *value,
 				GucContext context, GucSource source)
 {
 	(void) set_config_option(name, value, context, source,
-							 GUC_ACTION_SET, true, 0);
+						 GUC_ACTION_SET, GUC_ACTION_NONE, true, 0);
 }
 
 
@@ -6174,6 +6369,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt)
 									 (superuser() ? PGC_SUSET : PGC_USERSET),
 									 PGC_S_SESSION,
 									 action,
+									 (stmt->is_persistent ? GUC_ACTION_PERSISTENT : GUC_ACTION_NONE),
 									 true,
 									 0);
 			break;
@@ -6253,6 +6449,7 @@ ExecSetVariableStmt(VariableSetStmt *stmt)
 									 (superuser() ? PGC_SUSET : PGC_USERSET),
 									 PGC_S_SESSION,
 									 action,
+									 (stmt->is_persistent ? GUC_ACTION_REMOVE_PERSISTENT : GUC_ACTION_NONE),
 									 true,
 									 0);
 			break;
@@ -6299,6 +6496,7 @@ SetPGVariable(const char *name, List *args, bool is_local)
 							 (superuser() ? PGC_SUSET : PGC_USERSET),
 							 PGC_S_SESSION,
 							 is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
+							 GUC_ACTION_NONE,
 							 true,
 							 0);
 }
@@ -6343,6 +6541,7 @@ set_config_by_name(PG_FUNCTION_ARGS)
 							 (superuser() ? PGC_SUSET : PGC_USERSET),
 							 PGC_S_SESSION,
 							 is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
+							 GUC_ACTION_NONE,
 							 true,
 							 0);
 
@@ -6465,7 +6664,8 @@ define_custom_variable(struct config_generic * variable)
 		(void) set_config_option(name, pHolder->reset_val,
 								 pHolder->gen.reset_scontext,
 								 pHolder->gen.reset_source,
-								 GUC_ACTION_SET, true, WARNING);
+								 GUC_ACTION_SET, GUC_ACTION_NONE,
+								 true, WARNING);
 	/* That should not have resulted in stacking anything */
 	Assert(variable->stack == NULL);
 
@@ -6521,30 +6721,36 @@ reapply_stacked_values(struct config_generic * variable,
 			case GUC_SAVE:
 				(void) set_config_option(name, curvalue,
 										 curscontext, cursource,
-										 GUC_ACTION_SAVE, true, WARNING);
+										 GUC_ACTION_SAVE, GUC_ACTION_NONE,
+										 true, WARNING);
 				break;
 
 			case GUC_SET:
+			case GUC_PERSISTENT:
 				(void) set_config_option(name, curvalue,
 										 curscontext, cursource,
-										 GUC_ACTION_SET, true, WARNING);
+										 GUC_ACTION_SET, GUC_ACTION_NONE,
+										 true, WARNING);
 				break;
 
 			case GUC_LOCAL:
 				(void) set_config_option(name, curvalue,
 										 curscontext, cursource,
-										 GUC_ACTION_LOCAL, true, WARNING);
+										 GUC_ACTION_LOCAL, GUC_ACTION_NONE,
+										 true, WARNING);
 				break;
 
 			case GUC_SET_LOCAL:
 				/* first, apply the masked value as SET */
 				(void) set_config_option(name, stack->masked.val.stringval,
 									   stack->masked_scontext, PGC_S_SESSION,
-										 GUC_ACTION_SET, true, WARNING);
+										 GUC_ACTION_SET, GUC_ACTION_NONE,
+										 true, WARNING);
 				/* then apply the current value as LOCAL */
 				(void) set_config_option(name, curvalue,
 										 curscontext, cursource,
-										 GUC_ACTION_LOCAL, true, WARNING);
+										 GUC_ACTION_LOCAL, GUC_ACTION_NONE,
+										 true, WARNING);
 				break;
 		}
 
@@ -6568,7 +6774,8 @@ reapply_stacked_values(struct config_generic * variable,
 		{
 			(void) set_config_option(name, curvalue,
 									 curscontext, cursource,
-									 GUC_ACTION_SET, true, WARNING);
+									 GUC_ACTION_SET, GUC_ACTION_NONE,
+									 true, WARNING);
 			variable->stack = NULL;
 		}
 	}
@@ -7681,7 +7888,8 @@ read_nondefault_variables(void)
 
 		(void) set_config_option(varname, varvalue,
 								 varscontext, varsource,
-								 GUC_ACTION_SET, true, 0);
+								 GUC_ACTION_SET, GUC_ACTION_NONE,
+								 true, 0);
 		if (varsourcefile[0])
 			set_config_sourcefile(varname, varsourcefile, varsourceline);
 
@@ -7784,7 +7992,8 @@ ProcessGUCArray(ArrayType *array,
 
 		(void) set_config_option(name, value,
 								 context, source,
-								 action, true, 0);
+								 action, GUC_ACTION_NONE,
+								 true, 0);
 
 		free(name);
 		if (value)
@@ -8088,7 +8297,7 @@ validate_option_array_item(const char *name, const char *value,
 	/* test for permissions and valid option value */
 	(void) set_config_option(name, value,
 							 superuser() ? PGC_SUSET : PGC_USERSET,
-							 PGC_S_TEST, GUC_ACTION_SET, false, 0);
+							 PGC_S_TEST, GUC_ACTION_SET, GUC_ACTION_NONE, false, 0);
 
 	return true;
 }
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 62aea2f..144639b 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -581,6 +581,15 @@
 #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
 #------------------------------------------------------------------------------
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index b0b346f..2e190ba 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -193,7 +193,8 @@ const char *subdirs[] = {
 	"base/1",
 	"pg_tblspc",
 	"pg_stat",
-	"pg_stat_tmp"
+	"pg_stat_tmp",
+	PG_AUTOCONF_DIR
 };
 
 
@@ -1262,6 +1263,9 @@ setup_config(void)
 		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);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2229ef0..2861706 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1438,6 +1438,7 @@ typedef struct VariableSetStmt
 	char	   *name;			/* variable to be set */
 	List	   *args;			/* List of A_Const nodes */
 	bool		is_local;		/* SET LOCAL? */
+	bool		is_persistent;	/* SET PERSISTENT? */
 } VariableSetStmt;
 
 /* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 68a13b7..bd9d338 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -280,6 +280,7 @@ PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD)
 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)
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 02bcd92..92083f1 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -266,3 +266,8 @@
 /* #define HEAPDEBUGALL */
 /* #define ACLDEBUG */
 /* #define RTDEBUG */
+
+/*
+ * Automatic configuration directory for SET PERSISTENT.
+ */
+#define PG_AUTOCONF_DIR			"config"
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index d497b1f..e35dc36 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -159,9 +159,12 @@ typedef const char *(*GucShowHook) (void);
 typedef enum
 {
 	/* Types of set_config_option actions */
+	GUC_ACTION_NONE,			/* dummy action */
 	GUC_ACTION_SET,				/* regular SET command */
 	GUC_ACTION_LOCAL,			/* SET LOCAL command */
-	GUC_ACTION_SAVE				/* function SET option, or temp assignment */
+	GUC_ACTION_SAVE,			/* function SET option, or temp assignment */
+	GUC_ACTION_PERSISTENT,			/* SET PERSISTENT command */
+	GUC_ACTION_REMOVE_PERSISTENT		/* SET PERSISTENT TO DEFAULT command */
 } GucAction;
 
 #define GUC_QUALIFIER_SEPARATOR '.'
@@ -321,7 +324,8 @@ extern bool parse_int(const char *value, int *result, int flags,
 extern bool parse_real(const char *value, double *result);
 extern int set_config_option(const char *name, const char *value,
 				  GucContext context, GucSource source,
-				  GucAction action, bool changeVal, int elevel);
+				  GucAction action, GucAction action2,
+				  bool changeVal, int elevel);
 extern char *GetConfigOptionByName(const char *name, const char **varname);
 extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
 extern int	GetNumConfigOptions(void);
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 7360ce3..6e7985a 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -109,7 +109,8 @@ typedef enum
 	GUC_SAVE,					/* entry caused by function SET option */
 	GUC_SET,					/* entry caused by plain SET command */
 	GUC_LOCAL,					/* entry caused by SET LOCAL command */
-	GUC_SET_LOCAL				/* entry caused by SET then SET LOCAL */
+	GUC_SET_LOCAL,				/* entry caused by SET then SET LOCAL */
+	GUC_PERSISTENT					/* entry caused by SET PERSISTENT */
 } GucStackState;
 
 typedef struct guc_stack
@@ -123,6 +124,9 @@ typedef struct guc_stack
 	GucContext	masked_scontext;	/* context that set the masked value */
 	config_var_value prior;		/* previous value of variable */
 	config_var_value masked;	/* SET value in a GUC_SET_LOCAL entry */
+	bool		remove_persistent;
+	bool		has_persistent;	/* the value below is valid */
+	config_var_value persistent;	/* SET value in a GUC_SET_PERSISTENT entry */
 } GucStack;
 
 /*