set_persistent_v14.patch
application/octet-stream
Filename: set_persistent_v14.patch
Type: application/octet-stream
Part: 0
Patch
Format: context
Series: patch v14
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 13 | 0 |
| doc/src/sgml/keywords.sgml | 7 | 0 |
| doc/src/sgml/ref/set.sgml | 27 | 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 | 562 | 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 | 600 | 0 |
| src/test/regress/parallel_schedule | 4 | 0 |
| src/test/regress/serial_schedule | 1 | 0 |
| src/test/regress/sql/persistent.sql | 220 | 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 after reload of 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">
*** 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 after reload of server configuration (SIGHUP) or server startup.
+ </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,144 ----
</varlistentry>
<varlistentry>
+ <term><literal>PERSISTENT</></term>
+ <listitem>
+ <para>
+ Writes updated configuration parameters to <filename>persistent.auto.conf</filename>.
+ These configuration parameters takes effect after reload of
+ 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,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>
--- 174,180 ----
<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>
--- 184,190 ----
<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
--- 212,218 ----
<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
***************
*** 4178,4184 **** 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);
--- 4178,4184 ----
* 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
***************
*** 12803,12808 **** unreserved_keyword:
--- 12814,12820 ----
| 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"
***************
*** 3376,3381 **** static void ShowAllGUCConfig(DestReceiver *dest);
--- 3378,3388 ----
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);
/*
***************
*** 5122,5127 **** config_enum_get_options(struct config_enum * record, const char *prefix,
--- 5129,5347 ----
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.
***************
*** 5370,5386 **** 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)
{
--- 5590,5597 ----
if (value)
{
! validate_conf_option(record, name, value, elevel,
! false, &newval, &newextra);
}
else if (source == PGC_S_DEFAULT)
{
***************
*** 5463,5490 **** 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)
{
--- 5674,5682 ----
if (value)
{
! validate_conf_option(record, name, value, elevel,
! false, &newval, &newextra);
}
else if (source == PGC_S_DEFAULT)
{
***************
*** 5567,5591 **** 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)
{
--- 5759,5767 ----
if (value)
{
! validate_conf_option(record, name, value, elevel,
! false, &newval, &newextra);
!
}
else if (source == PGC_S_DEFAULT)
{
***************
*** 5668,5694 **** 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)
{
--- 5844,5851 ----
if (value)
{
! validate_conf_option(record, name, value, elevel,
! false, &newval, &newextra);
}
else if (source == PGC_S_DEFAULT)
{
***************
*** 5794,5820 **** 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)
{
--- 5951,5958 ----
if (value)
{
! validate_conf_option(record, name, value, elevel,
! false, &newval, &newextra);
}
else if (source == PGC_S_DEFAULT)
{
***************
*** 5925,5930 **** set_config_sourcefile(const char *name, char *sourcefile, int sourceline)
--- 6063,6384 ----
}
/*
+ * 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->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);
+ return;
+ }
+
+
+ /*
* Set a config option to the given value.
*
* See also set_config_option; this is just the wrapper to be called from
***************
*** 6196,6208 **** 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:
--- 6650,6665 ----
{
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:
***************
*** 6274,6279 **** ExecSetVariableStmt(VariableSetStmt *stmt)
--- 6731,6742 ----
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
***************
*** 582,587 ****
--- 582,596 ----
#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,600 ----
+ -- 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;
+ 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';
+ 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
+ ------------------------
+ 1min
+ (1 row)
+
+ show vacuum_cost_delay;
+ vacuum_cost_delay
+ -------------------
+ 0
+ (1 row)
+
+ show bgwriter_lru_multiplier;
+ bgwriter_lru_multiplier
+ -------------------------
+ 2
+ (1 row)
+
+ show full_page_writes;
+ full_page_writes
+ ------------------
+ on
+ (1 row)
+
+ show synchronous_commit;
+ synchronous_commit
+ --------------------
+ on
+ (1 row)
+
+ show synchronous_standby_names;
+ synchronous_standby_names
+ ---------------------------
+
+ (1 row)
+
+ show seq_page_cost;
+ seq_page_cost
+ ---------------
+ 1
+ (1 row)
+
+ show autovacuum_naptime;
+ autovacuum_naptime
+ --------------------
+ 1min
+ (1 row)
+
+ show search_path;
+ search_path
+ ----------------
+ "$user",public
+ (1 row)
+
+ show default_tablespace;
+ default_tablespace
+ --------------------
+
+ (1 row)
+
+ show checkpoint_timeout;
+ checkpoint_timeout
+ --------------------
+ 5min
+ (1 row)
+
+ show restart_after_crash;
+ restart_after_crash
+ ---------------------
+ on
+ (1 row)
+
+ show work_mem;
+ work_mem
+ ----------
+ 1MB
+ (1 row)
+
+ -- reload the new configurations.
+ select pg_reload_conf();
+ pg_reload_conf
+ ----------------
+ t
+ (1 row)
+
+ 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;
+ set persistent authentication_timeout To default;
+ set persistent synchronous_commit = default;
+ set persistent restart_after_crash To default;
+ -- reload the new configurations.
+ select pg_reload_conf();
+ pg_reload_conf
+ ----------------
+ t
+ (1 row)
+
+ 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;
+ set persistent authentication_timeout = 20;
+ set persistent synchronous_commit To off;
+ set persistent restart_after_crash To yes;
+ -- reload the new configurations.
+ select pg_reload_conf();
+ pg_reload_conf
+ ----------------
+ t
+ (1 row)
+
+ 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_reload_conf();
+ pg_reload_conf
+ ----------------
+ t
+ (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_reload_conf();
+ pg_reload_conf
+ ----------------
+ t
+ (1 row)
+
+ 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;
+ 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_reload_conf();
+ pg_reload_conf
+ ----------------
+ t
+ (1 row)
+
+ 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,220 ----
+ -- 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';
+
+
+ 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;
+
+ -- reload the new configurations.
+ select pg_reload_conf();
+ 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_reload_conf();
+ 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_reload_conf();
+ 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_reload_conf();
+ 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_reload_conf();
+ 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_reload_conf();
+ 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;
+