summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTatsuo Ishii2013-12-18 14:42:44 +0000
committerTatsuo Ishii2013-12-18 14:42:44 +0000
commit65d6e4cb5c62371dae6c236a7e709d503ae6ddf8 (patch)
tree07fff22fb42940bcf618885589909de0adaa9f9c
parentdba5a9dda9adbda16a72c46e1c012ee6552c248a (diff)
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.
-rw-r--r--doc/src/sgml/config.sgml13
-rw-r--r--doc/src/sgml/ref/allfiles.sgml1
-rw-r--r--doc/src/sgml/ref/alter_system.sgml114
-rw-r--r--doc/src/sgml/reference.sgml1
-rw-r--r--doc/src/sgml/storage.sgml6
-rw-r--r--src/backend/nodes/copyfuncs.c13
-rw-r--r--src/backend/nodes/equalfuncs.c12
-rw-r--r--src/backend/parser/gram.y27
-rw-r--r--src/backend/replication/basebackup.c7
-rw-r--r--src/backend/tcop/utility.c13
-rw-r--r--src/backend/utils/misc/guc-file.l35
-rw-r--r--src/backend/utils/misc/guc.c614
-rw-r--r--src/bin/initdb/initdb.c16
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/parsenodes.h10
-rw-r--r--src/include/pg_config_manual.h7
-rw-r--r--src/include/storage/lwlock.h1
-rw-r--r--src/include/utils/guc.h1
18 files changed, 797 insertions, 95 deletions
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f0794467ba..5575df5160 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -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">
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 5846974feb..ce7a5e3cb6 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -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">
diff --git a/doc/src/sgml/ref/alter_system.sgml b/doc/src/sgml/ref/alter_system.sgml
new file mode 100644
index 0000000000..3ccc6afd51
--- /dev/null
+++ b/doc/src/sgml/ref/alter_system.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>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index d967f666b9..87e8e9ee8f 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -58,6 +58,7 @@
&alterSchema;
&alterSequence;
&alterServer;
+ &alterSystem;
&alterTable;
&alterTableSpace;
&alterTSConfig;
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 09b3f1028c..1f3f1f9bf9 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -126,6 +126,12 @@ Item
</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
last started with</entry>
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index cd8a11b8d5..3e102310c5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6188114060..329755c703 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1547,6 +1547,15 @@ _equalReplicaIdentityStmt(const ReplicaIdentityStmt *a, const ReplicaIdentityStm
}
static bool
+_equalAlterSystemStmt(const AlterSystemStmt * a, const AlterSystemStmt * b)
+{
+ COMPARE_NODE_FIELD(setstmt);
+
+ return true;
+}
+
+
+static bool
_equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
{
COMPARE_NODE_FIELD(sequence);
@@ -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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f9d45777ca..b4e5552636 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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);
@@ -8312,6 +8316,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
*
*****************************************************************************/
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index ba8d173357..244e3b0ab3 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -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
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 7d75b3383f..dca4503471 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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;
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index c5ca4a4074..640899bae5 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -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;
@@ -145,6 +150,26 @@ ProcessConfigFile(GucContext context)
}
/*
+ * 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
* from the file since we last processed it.
@@ -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)));
}
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index b0c14a2dfc..51416f49cd 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -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)
- 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)
{
@@ -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
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 30e3701f92..e6bb132bea 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -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);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ff9af7691c..a68c8ad5ca 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -363,6 +363,7 @@ typedef enum NodeTag
T_AlterEventTrigStmt,
T_RefreshMatViewStmt,
T_ReplicaIdentityStmt,
+ T_AlterSystemStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 0ad7586853..6a5a8c5f2d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2472,6 +2472,16 @@ typedef struct DropdbStmt
} DropdbStmt;
/* ----------------------
+ * Alter System Statement
+ * ----------------------
+ */
+typedef struct AlterSystemStmt
+{
+ NodeTag type;
+ VariableSetStmt *setstmt; /* SET subcommand */
+} AlterSystemStmt;
+
+/* ----------------------
* Cluster Statement (support pbrown's cluster index implementation)
* ----------------------
*/
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 2e6aad1ca5..9d1166305d 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -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"
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 730c47ba68..3e42f6a468 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -81,6 +81,7 @@ typedef enum LWLockId
SyncRepLock,
BackgroundWorkerLock,
DynamicSharedMemoryControlLock,
+ AutoFileLock,
/* Individual lock IDs end here */
FirstBufMappingLock,
FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 3e981b3e94..0a02999e3f 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -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);