Add ALTER SYSTEM command to edit the server configuration file.

Patch contributed by Amit Kapila. Reviewed by Hari Babu, Masao Fujii,
Boszormenyi Zoltan, Andres Freund, Greg Smith and others.
This commit is contained in:
Tatsuo Ishii 2013-12-18 23:42:44 +09:00
parent dba5a9dda9
commit 65d6e4cb5c
18 changed files with 797 additions and 95 deletions

View File

@ -158,6 +158,19 @@ 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-ALTERSYSTEM">
command, for example:
<screen>
ALTER SYSTEM SET checkpoint_timeout TO 600;
</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">

View File

@ -30,6 +30,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterSchema SYSTEM "alter_schema.sgml">
<!ENTITY alterServer SYSTEM "alter_server.sgml">
<!ENTITY alterSequence SYSTEM "alter_sequence.sgml">
<!ENTITY alterSystem SYSTEM "alter_system.sgml">
<!ENTITY alterTable SYSTEM "alter_table.sgml">
<!ENTITY alterTableSpace SYSTEM "alter_tablespace.sgml">
<!ENTITY alterTSConfig SYSTEM "alter_tsconfig.sgml">

View File

@ -0,0 +1,114 @@
<!--
doc/src/sgml/ref/alter_system.sgml
PostgreSQL documentation
-->
<refentry id="SQL-ALTERSYSTEM">
<refmeta>
<refentrytitle>ALTER SYSTEM</refentrytitle>
<manvolnum>7</manvolnum>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>ALTER SYSTEM</refname>
<refpurpose>change a server configuration parameter</refpurpose>
</refnamediv>
<indexterm zone="sql-altersystem">
<primary>ALTER SYSTEM</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
ALTER SYSTEM SET <replaceable class="PARAMETER">configuration_parameter</replaceable> { TO | = } { <replaceable class="PARAMETER">value</replaceable> | '<replaceable class="PARAMETER">value</replaceable>' | DEFAULT }
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>ALTER SYSTEM</command> writes the configuration parameter
values to the <filename>postgresql.auto.conf</filename> file. With
<literal>DEFAULT</literal>, it removes a configuration entry from
<filename>postgresql.auto.conf</filename> file. The values will be
effective after reload of server configuration (SIGHUP) or in next
server start based on the type of configuration parameter modified.
</para>
<para>
This command is not allowed inside transaction block or function.
</para>
<para>
See <xref linkend="config-setting"> for other ways to set the parameters and
how they become effective.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><replaceable class="parameter">configuration_parameter</replaceable></term>
<listitem>
<para>
Name of a settable run-time parameter. Available parameters are
documented in <xref linkend="runtime-config">.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">value</replaceable></term>
<listitem>
<para>
New value of parameter. Values can be specified as string
constants, identifiers, numbers, or comma-separated lists of
these, as appropriate for the particular parameter.
<literal>DEFAULT</literal> can be written to specify to remove the
parameter and its value from <filename>postgresql.auto.conf</filename>
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Examples</title>
<para>
Set the <literal>wal_level</>:
<programlisting>
ALTER SYSTEM SET wal_level = hot_standby;
</programlisting>
</para>
<para>
Set the <literal>authentication_timeout</>:
<programlisting>
ALTER SYSTEM SET authentication_timeout = 10;
</programlisting></para>
</refsect1>
<refsect1>
<title>Compatibility</title>
<para>
The <command>ALTER SYSTEM</command> statement is a
<productname>PostgreSQL</productname> extension.
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="SQL-SET"></member>
<member><xref linkend="SQL-SHOW"></member>
</simplelist>
</refsect1>
</refentry>

View File

@ -58,6 +58,7 @@
&alterSchema;
&alterSequence;
&alterServer;
&alterSystem;
&alterTable;
&alterTableSpace;
&alterTSConfig;

View File

@ -125,6 +125,12 @@ Item
<entry>Subdirectory containing WAL (Write Ahead Log) files</entry>
</row>
<row>
<entry><filename>postgresql.auto.conf</></entry>
<entry>A file used for storing configuration parameters that are set by
<command>ALTER SYSTEM</command></entry>
</row>
<row>
<entry><filename>postmaster.opts</></entry>
<entry>A file recording the command-line options the server was

View File

@ -3292,6 +3292,16 @@ _copyReplicaIdentityStmt(const ReplicaIdentityStmt *from)
return newnode;
}
static AlterSystemStmt *
_copyAlterSystemStmt(const AlterSystemStmt * from)
{
AlterSystemStmt *newnode = makeNode(AlterSystemStmt);
COPY_NODE_FIELD(setstmt);
return newnode;
}
static CreateSeqStmt *
_copyCreateSeqStmt(const CreateSeqStmt *from)
{
@ -4368,6 +4378,9 @@ copyObject(const void *from)
case T_ReplicaIdentityStmt:
retval = _copyReplicaIdentityStmt(from);
break;
case T_AlterSystemStmt:
retval = _copyAlterSystemStmt(from);
break;
case T_CreateSeqStmt:
retval = _copyCreateSeqStmt(from);
break;

View File

@ -1546,6 +1546,15 @@ _equalReplicaIdentityStmt(const ReplicaIdentityStmt *a, const ReplicaIdentityStm
return true;
}
static bool
_equalAlterSystemStmt(const AlterSystemStmt * a, const AlterSystemStmt * b)
{
COMPARE_NODE_FIELD(setstmt);
return true;
}
static bool
_equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
{
@ -2838,6 +2847,9 @@ equal(const void *a, const void *b)
case T_ReplicaIdentityStmt:
retval = _equalReplicaIdentityStmt(a, b);
break;
case T_AlterSystemStmt:
retval = _equalAlterSystemStmt(a, b);
break;
case T_CreateSeqStmt:
retval = _equalCreateSeqStmt(a, b);
break;

View File

@ -216,7 +216,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
AlterEventTrigStmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt
AlterFdwStmt AlterForeignServerStmt AlterGroupStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterSystemStmt AlterTableStmt
AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt
AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
@ -397,7 +397,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> generic_set set_rest set_rest_more SetResetClause FunctionSetResetClause
%type <node> TableElement TypedTableElement ConstraintElem TableFuncElement
%type <node> columnDef columnOptions
@ -724,6 +724,7 @@ stmt :
| AlterObjectSchemaStmt
| AlterOwnerStmt
| AlterSeqStmt
| AlterSystemStmt
| AlterTableStmt
| AlterCompositeTypeStmt
| AlterRoleSetStmt
@ -1333,7 +1334,7 @@ set_rest:
| set_rest_more
;
set_rest_more: /* Generic SET syntaxes: */
generic_set:
var_name TO var_list
{
VariableSetStmt *n = makeNode(VariableSetStmt);
@ -1364,6 +1365,9 @@ set_rest_more: /* Generic SET syntaxes: */
n->name = $1;
$$ = n;
}
set_rest_more: /* Generic SET syntaxes: */
generic_set {$$ = $1;}
| var_name FROM CURRENT_P
{
VariableSetStmt *n = makeNode(VariableSetStmt);
@ -8310,6 +8314,23 @@ DropdbStmt: DROP DATABASE database_name
;
/*****************************************************************************
*
* ALTER SYSTEM SET
*
* This is used to change configuration parameters persistently.
*****************************************************************************/
AlterSystemStmt:
ALTER SYSTEM_P SET generic_set
{
AlterSystemStmt *n = makeNode(AlterSystemStmt);
n->setstmt = $4;
$$ = (Node *)n;
}
;
/*****************************************************************************
*
* Manipulate a domain

View File

@ -811,6 +811,13 @@ sendDir(char *path, int basepathlen, bool sizeonly)
strlen(PG_TEMP_FILE_PREFIX)) == 0)
continue;
/* skip auto conf temporary file */
if (strncmp(de->d_name,
PG_AUTOCONF_FILENAME ".temp",
sizeof(PG_AUTOCONF_FILENAME) + 4) == 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

View File

@ -687,6 +687,11 @@ standard_ProcessUtility(Node *parsetree,
ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
break;
case T_AlterSystemStmt:
PreventTransactionChain(isTopLevel, "ALTER SYSTEM");
AlterSystemSetConfigFile((AlterSystemStmt *) parsetree);
break;
case T_VariableSetStmt:
ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel);
break;
@ -2157,6 +2162,10 @@ CreateCommandTag(Node *parsetree)
tag = "REFRESH MATERIALIZED VIEW";
break;
case T_AlterSystemStmt:
tag = "ALTER SYSTEM";
break;
case T_VariableSetStmt:
switch (((VariableSetStmt *) parsetree)->kind)
{
@ -2726,6 +2735,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
case T_AlterSystemStmt:
lev = LOGSTMT_ALL;
break;
case T_VariableSetStmt:
lev = LOGSTMT_ALL;
break;

View File

@ -120,6 +120,9 @@ ProcessConfigFile(GucContext context)
*head,
*tail;
int i;
char ConfigAutoFileName[MAXPGPATH];
char *ErrorConfFile;
char *CallingFileName;
/*
* Config files are processed on startup (by the postmaster only)
@ -134,6 +137,8 @@ ProcessConfigFile(GucContext context)
*/
elevel = IsUnderPostmaster ? DEBUG2 : LOG;
ErrorConfFile = ConfigFileName;
/* Parse the file into a list of option names and values */
head = tail = NULL;
@ -144,6 +149,26 @@ ProcessConfigFile(GucContext context)
goto cleanup_list;
}
/*
* Parse postgresql.auto.conf file after postgresql.conf to replace
* parameters set by ALTER SYSTEM command. This file is present in
* data directory, however when called during initdb data directory is not
* set till this point, so use ConfigFile path which will be same.
*/
snprintf(ConfigAutoFileName,sizeof(ConfigAutoFileName),"%s", PG_AUTOCONF_FILENAME);
if (data_directory)
CallingFileName = NULL;
else
CallingFileName = ConfigFileName;
if (!ParseConfigFile(ConfigAutoFileName, CallingFileName, false, 0, elevel, &head, &tail))
{
/* Syntax error(s) detected in the file, so bail out */
error = true;
ErrorConfFile = ConfigAutoFileName;
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
@ -192,6 +217,7 @@ ProcessConfigFile(GucContext context)
item->name,
item->filename, item->sourceline)));
error = true;
ErrorConfFile = item->filename;
}
}
@ -318,7 +344,10 @@ ProcessConfigFile(GucContext context)
}
}
else if (scres == 0)
{
error = true;
ErrorConfFile = item->filename;
}
/* else no error but variable's active value was not changed */
/*
@ -348,17 +377,17 @@ ProcessConfigFile(GucContext context)
ereport(ERROR,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("configuration file \"%s\" contains errors",
ConfigFileName)));
ErrorConfFile)));
else if (apply)
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
ConfigFileName)));
ErrorConfFile)));
else
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("configuration file \"%s\" contains errors; no changes were applied",
ConfigFileName)));
ErrorConfFile)));
}
}

View File

@ -207,6 +207,10 @@ static char *config_enum_get_options(struct config_enum * record,
const char *prefix, const char *suffix,
const char *separator);
static bool validate_conf_option(struct config_generic * record,
const char *name, const char *value, GucSource source,
int elevel, bool freemem, void *newval, void **newextra);
/*
* Options for enum values defined in this module.
@ -3484,6 +3488,9 @@ static void ShowAllGUCConfig(DestReceiver *dest);
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);
/*
@ -5248,6 +5255,220 @@ config_enum_get_options(struct config_enum * record, const char *prefix,
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
*/
bool
validate_conf_option(struct config_generic * record, const char *name,
const char *value, GucSource source, 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,
source, 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,
source, 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,
source, 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,
source, 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,
source, LOG))
return 0;
if (*newextra && freemem)
free(*newextra);
}
}
break;
}
return 1;
}
/*
* Sets option `name' to given value.
@ -5496,16 +5717,9 @@ 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))
if (!validate_conf_option(record, name, value, source,
elevel, false, &newval,
&newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
@ -5589,27 +5803,9 @@ 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))
if (!validate_conf_option(record, name, value, source,
elevel, false, &newval,
&newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
@ -5693,24 +5889,9 @@ 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))
if (!validate_conf_option(record, name, value, source,
elevel, false, &newval,
&newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
@ -5794,27 +5975,10 @@ 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)
if (!validate_conf_option(record, name, value, source,
elevel, false, &newval,
&newextra))
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)
{
@ -5920,26 +6084,9 @@ 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))
if (!validate_conf_option(record, name, value, source,
elevel, false, &newval,
&newextra))
return 0;
}
else if (source == PGC_S_DEFAULT)
@ -6309,6 +6456,295 @@ flatten_set_variable_args(const char *name, List *args)
return buf.data;
}
/*
* Writes updated configuration parameter values into
* postgresql.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! \n");
appendStringInfoString(&buf, "# It will be overwritten by ALTER SYSTEM command. \n");
/*
* write the file header message before contents, so that if there is no
* item it can contain message
*/
if (write(fd, buf.data, buf.len) < 0)
ereport(ERROR,
(errmsg("failed to write to \"%s\" file", filename)));
resetStringInfo(&buf);
/*
* traverse the list of parameters, quote the string parameter and write
* it to 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
* postgresql.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 previous configuration parameters
* set by ALTER SYSTEM command 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 postgresql.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 (postgresql.auto.conf) intact.
*/
void
AlterSystemSetConfigFile(AlterSystemStmt * altersysstmt)
{
char *name;
char *value;
int Tmpfd = -1;
FILE *infile;
struct config_generic *record;
ConfigVariable *head = NULL;
ConfigVariable *tail = NULL;
char AutoConfFileName[MAXPGPATH];
char AutoConfTmpFileName[MAXPGPATH];
struct stat st;
void *newextra = NULL;
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to execute ALTER SYSTEM command"))));
/*
* Validate the name and arguments [value1, value2 ... ].
*/
name = altersysstmt->setstmt->name;
switch (altersysstmt->setstmt->kind)
{
case VAR_SET_VALUE:
value = ExtractSetVariableArgs(altersysstmt->setstmt);
break;
case VAR_SET_DEFAULT:
value = NULL;
break;
default:
elog(ERROR, "unrecognized alter system stmt type: %d",
altersysstmt->setstmt->kind);
break;
}
record = find_option(name, false, 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, PGC_S_FILE,
ERROR, true, NULL,
&newextra))
ereport(ERROR,
(errmsg("invalid value for parameter \"%s\": \"%s\"", name, value)));
/*
* Use data directory as reference path for postgresql.auto.conf and it's
* corresponding temp file
*/
join_path_components(AutoConfFileName, data_directory, PG_AUTOCONF_FILENAME);
canonicalize_path(AutoConfFileName);
snprintf(AutoConfTmpFileName, sizeof(AutoConfTmpFileName), "%s.%s",
AutoConfFileName,
"temp");
/*
* one backend is allowed to operate on postgresql.auto.conf file, to
* ensure that we need to update the contents of the file with
* AutoFileLock. To ensure crash safety, first the contents are written to
* temporary file and then rename it to postgresql.auto.conf. In case
* there exists a temp file from previous crash, that can be reused.
*/
LWLockAcquire(AutoFileLock, 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 postgresql.auto.conf file */
infile = AllocateFile(AutoConfFileName, "r");
if (infile == NULL)
ereport(ERROR,
(errmsg("failed to open auto conf file \"%s\": %m ",
AutoConfFileName)));
/* Parse the postgresql.auto.conf file */
ParseConfigFp(infile, AutoConfFileName, 0, LOG, &head, &tail);
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 postgresql.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 loose the parameters set by last ALTER SYSTEM
* 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);
FreeConfigVariables(head);
PG_RE_THROW();
}
PG_END_TRY();
FreeConfigVariables(head);
LWLockRelease(AutoFileLock);
return;
}
/*
* SET command

View File

@ -1228,6 +1228,7 @@ setup_config(void)
char repltok[MAXPGPATH];
char path[MAXPGPATH];
const char *default_timezone;
char *autoconflines[3];
fputs(_("creating configuration files ... "), stdout);
fflush(stdout);
@ -1320,6 +1321,21 @@ setup_config(void)
writefile(path, conflines);
chmod(path, S_IRUSR | S_IWUSR);
/*
* create the automatic configuration file to store the configuration
* parameters set by ALTER SYSTEM command. The parameters present in this
* file will override the value of parameters that exists before parse of
* this file.
*/
autoconflines[0] = pg_strdup("# Do not edit this file manually! \n");
autoconflines[1] = pg_strdup("# It will be overwritten by the ALTER SYSTEM command. \n");
autoconflines[2] = NULL;
sprintf(path, "%s/%s", pg_data, PG_AUTOCONF_FILENAME);
writefile(path, autoconflines);
chmod(path, S_IRUSR | S_IWUSR);
free(conflines);

View File

@ -363,6 +363,7 @@ typedef enum NodeTag
T_AlterEventTrigStmt,
T_RefreshMatViewStmt,
T_ReplicaIdentityStmt,
T_AlterSystemStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)

View File

@ -2471,6 +2471,16 @@ typedef struct DropdbStmt
bool missing_ok; /* skip error if db is missing? */
} DropdbStmt;
/* ----------------------
* Alter System Statement
* ----------------------
*/
typedef struct AlterSystemStmt
{
NodeTag type;
VariableSetStmt *setstmt; /* SET subcommand */
} AlterSystemStmt;
/* ----------------------
* Cluster Statement (support pbrown's cluster index implementation)
* ----------------------

View File

@ -292,3 +292,10 @@
/* #define HEAPDEBUGALL */
/* #define ACLDEBUG */
/* #define RTDEBUG */
/*
* Automatic configuration file name for ALTER SYSTEM.
* This file will be used to store values of configuration parameters
* set by ALTER SYSTEM command
*/
#define PG_AUTOCONF_FILENAME "postgresql.auto.conf"

View File

@ -81,6 +81,7 @@ typedef enum LWLockId
SyncRepLock,
BackgroundWorkerLock,
DynamicSharedMemoryControlLock,
AutoFileLock,
/* Individual lock IDs end here */
FirstBufMappingLock,
FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,

View File

@ -326,6 +326,7 @@ 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);
extern void AlterSystemSetConfigFile(AlterSystemStmt * setstmt);
extern char *GetConfigOptionByName(const char *name, const char **varname);
extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow);
extern int GetNumConfigOptions(void);