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.
pull/6/head
Tatsuo Ishii 12 years ago
parent dba5a9dda9
commit 65d6e4cb5c
  1. 13
      doc/src/sgml/config.sgml
  2. 1
      doc/src/sgml/ref/allfiles.sgml
  3. 114
      doc/src/sgml/ref/alter_system.sgml
  4. 1
      doc/src/sgml/reference.sgml
  5. 6
      doc/src/sgml/storage.sgml
  6. 13
      src/backend/nodes/copyfuncs.c
  7. 12
      src/backend/nodes/equalfuncs.c
  8. 27
      src/backend/parser/gram.y
  9. 7
      src/backend/replication/basebackup.c
  10. 13
      src/backend/tcop/utility.c
  11. 35
      src/backend/utils/misc/guc-file.l
  12. 614
      src/backend/utils/misc/guc.c
  13. 16
      src/bin/initdb/initdb.c
  14. 1
      src/include/nodes/nodes.h
  15. 10
      src/include/nodes/parsenodes.h
  16. 7
      src/include/pg_config_manual.h
  17. 1
      src/include/storage/lwlock.h
  18. 1
      src/include/utils/guc.h

@ -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">

@ -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">

@ -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>

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

@ -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

@ -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;

@ -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;

@ -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

@ -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

@ -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;

@ -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)));
}
}

@ -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,28 +5975,11 @@ 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);
if (!validate_conf_option(record, name, value, source,
elevel, false, &newval,
&newextra))
return 0;
}
}
else if (source == PGC_S_DEFAULT)
{
/* non-NULL boot_val must always get strdup'd */
@ -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

@ -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);

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

@ -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)
* ----------------------

@ -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"

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

@ -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);

Loading…
Cancel
Save