0001-ALTER-ROLE-.-SET-.-TO-.-USER-SET-v2.patch
application/octet-stream
Filename: 0001-ALTER-ROLE-.-SET-.-TO-.-USER-SET-v2.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0001
Subject: ALTER ROLE ... SET ... TO ... USER SET
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/alter_database.sgml | 14 | 1 |
| doc/src/sgml/ref/alter_role.sgml | 21 | 1 |
| src/backend/catalog/pg_db_role_setting.c | 2 | 2 |
| src/backend/commands/functioncmds.c | 1 | 1 |
| src/backend/parser/gram.y | 20 | 0 |
| src/backend/utils/misc/guc.c | 79 | 32 |
| src/backend/utils/misc/guc_funcs.c | 11 | 1 |
| src/bin/pg_dump/dumputils.c | 15 | 3 |
| src/include/common/guc-common.h | 31 | 0 |
| src/include/nodes/parsenodes.h | 1 | 0 |
| src/include/utils/guc.h | 3 | 1 |
From 662abcc9c3c312955577e02f521576ac8949152f Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Sun, 20 Nov 2022 03:29:09 +0300
Subject: [PATCH] ALTER ROLE ... SET ... TO ... USER SET
---
doc/src/sgml/ref/alter_database.sgml | 15 ++-
doc/src/sgml/ref/alter_role.sgml | 22 ++++-
src/backend/catalog/pg_db_role_setting.c | 4 +-
src/backend/commands/functioncmds.c | 2 +-
src/backend/parser/gram.y | 20 ++++
src/backend/utils/misc/guc.c | 111 ++++++++++++++++-------
src/backend/utils/misc/guc_funcs.c | 12 ++-
src/bin/pg_dump/dumputils.c | 18 +++-
src/include/common/guc-common.h | 31 +++++++
src/include/nodes/parsenodes.h | 1 +
src/include/utils/guc.h | 4 +-
11 files changed, 198 insertions(+), 42 deletions(-)
create mode 100644 src/include/common/guc-common.h
diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml
index 89ed261b4c2..697db7b720f 100644
--- a/doc/src/sgml/ref/alter_database.sgml
+++ b/doc/src/sgml/ref/alter_database.sgml
@@ -37,7 +37,7 @@ ALTER DATABASE <replaceable class="parameter">name</replaceable> SET TABLESPACE
ALTER DATABASE <replaceable class="parameter">name</replaceable> REFRESH COLLATION VERSION
-ALTER DATABASE <replaceable class="parameter">name</replaceable> SET <replaceable>configuration_parameter</replaceable> { TO | = } { <replaceable>value</replaceable> | DEFAULT }
+ALTER DATABASE <replaceable class="parameter">name</replaceable> SET <replaceable>configuration_parameter</replaceable> { TO | = } { <replaceable>value</replaceable> | <replaceable>value</replaceable> USER SET | DEFAULT }
ALTER DATABASE <replaceable class="parameter">name</replaceable> SET <replaceable>configuration_parameter</replaceable> FROM CURRENT
ALTER DATABASE <replaceable class="parameter">name</replaceable> RESET <replaceable>configuration_parameter</replaceable>
ALTER DATABASE <replaceable class="parameter">name</replaceable> RESET ALL
@@ -206,6 +206,19 @@ ALTER DATABASE <replaceable class="parameter">name</replaceable> RESET ALL
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>USER SET</literal></term>
+ <listitem>
+ <para>
+ Specifies that variable should be set on behalf of ordinal role.
+ That lets ordinal role set placeholder variables, which permission
+ requirements is not known yet; see <xref linkend="runtime-config-custom"/>.
+ The value set wouldn't be used if variable finally appear to require
+ superuser privileges.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml
index 5aa5648ae7b..0c51d1d0561 100644
--- a/doc/src/sgml/ref/alter_role.sgml
+++ b/doc/src/sgml/ref/alter_role.sgml
@@ -38,7 +38,7 @@ ALTER ROLE <replaceable class="parameter">role_specification</replaceable> [ WIT
ALTER ROLE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable>new_name</replaceable>
-ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | ALL } [ IN DATABASE <replaceable class="parameter">database_name</replaceable> ] SET <replaceable>configuration_parameter</replaceable> { TO | = } { <replaceable>value</replaceable> | DEFAULT }
+ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | ALL } [ IN DATABASE <replaceable class="parameter">database_name</replaceable> ] SET <replaceable>configuration_parameter</replaceable> { TO | = } { <replaceable>value</replaceable> | <replaceable>value</replaceable> USER SET | DEFAULT }
ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | ALL } [ IN DATABASE <replaceable class="parameter">database_name</replaceable> ] SET <replaceable>configuration_parameter</replaceable> FROM CURRENT
ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | ALL } [ IN DATABASE <replaceable class="parameter">database_name</replaceable> ] RESET <replaceable>configuration_parameter</replaceable>
ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | ALL } [ IN DATABASE <replaceable class="parameter">database_name</replaceable> ] RESET ALL
@@ -234,6 +234,19 @@ ALTER ROLE { <replaceable class="parameter">role_specification</replaceable> | A
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>USER SET</literal></term>
+ <listitem>
+ <para>
+ Specifies that variable should be set on behalf of ordinal role.
+ That lets ordinal role set placeholder variables, which permission
+ requirements is not known yet; see <xref linkend="runtime-config-custom"/>.
+ The value set wouldn't be used if variable finally appear to require
+ superuser privileges.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
@@ -329,6 +342,13 @@ ALTER ROLE worker_bee SET maintenance_work_mem = 100000;
<programlisting>
ALTER ROLE fred IN DATABASE devel SET client_min_messages = DEBUG;
+</programlisting></para>
+
+ <para>
+ Give a role a non-default placeholder setting on behalf of ordinal user.
+
+<programlisting>
+ALTER ROLE fred SET my.param = 'value' USER SET;
</programlisting></para>
</refsect1>
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 42387f4e304..4b9a39a953d 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -115,7 +115,7 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
/* Update (valuestr is NULL in RESET cases) */
if (valuestr)
- a = GUCArrayAdd(a, setstmt->name, valuestr);
+ a = GUCArrayAdd(a, setstmt->name, valuestr, setstmt->user_set);
else
a = GUCArrayDelete(a, setstmt->name);
@@ -141,7 +141,7 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
memset(nulls, false, sizeof(nulls));
- a = GUCArrayAdd(NULL, setstmt->name, valuestr);
+ a = GUCArrayAdd(NULL, setstmt->name, valuestr, setstmt->user_set);
values[Anum_pg_db_role_setting_setdatabase - 1] =
ObjectIdGetDatum(databaseid);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 1f820c93e96..950fd28badc 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -662,7 +662,7 @@ update_proconfig_value(ArrayType *a, List *set_items)
char *valuestr = ExtractSetVariableArgs(sstmt);
if (valuestr)
- a = GUCArrayAdd(a, sstmt->name, valuestr);
+ a = GUCArrayAdd(a, sstmt->name, valuestr, sstmt->user_set);
else /* RESET */
a = GUCArrayDelete(a, sstmt->name);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 737bd2d06d5..c38398d8528 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -1622,6 +1622,26 @@ generic_set:
n->args = $3;
$$ = n;
}
+ | var_name TO var_list USER SET
+ {
+ VariableSetStmt *n = makeNode(VariableSetStmt);
+
+ n->kind = VAR_SET_VALUE;
+ n->name = $1;
+ n->args = $3;
+ n->user_set = true;
+ $$ = n;
+ }
+ | var_name '=' var_list USER SET
+ {
+ VariableSetStmt *n = makeNode(VariableSetStmt);
+
+ n->kind = VAR_SET_VALUE;
+ n->name = $1;
+ n->args = $3;
+ n->user_set = true;
+ $$ = n;
+ }
| var_name TO DEFAULT
{
VariableSetStmt *n = makeNode(VariableSetStmt);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 117a2d26a0e..b311142ff2f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -224,7 +224,6 @@ static bool reporting_enabled; /* true to enable GUC_REPORT */
static int GUCNestLevel = 0; /* 1 when in main transaction */
-
static int guc_var_compare(const void *a, const void *b);
static uint32 guc_name_hash(const void *key, Size keysize);
static int guc_name_match(const void *key1, const void *key2, Size keysize);
@@ -244,7 +243,7 @@ static void reapply_stacked_values(struct config_generic *variable,
GucContext curscontext, GucSource cursource,
Oid cursrole);
static bool validate_option_array_item(const char *name, const char *value,
- bool skipIfNoPermissions);
+ bool user_set, bool skipIfNoPermissions);
static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head);
static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
const char *name, const char *value);
@@ -6159,13 +6158,16 @@ RestoreGUCState(void *gucstate)
/*
* A little "long argument" simulation, although not quite GNU
- * compliant. Takes a string of the form "some-option=some value" and
- * returns name = "some_option" and value = "some value" in palloc'ed
- * storage. Note that '-' is converted to '_' in the option name. If
- * there is no '=' in the input string then value will be NULL.
+ * compliant. Takes a string of the form "some-option=some value" or
+ * "some-option(u)=some value" and returns name = "some_option" and
+ * value = "some value" in palloc'ed storage. If user_set is not null then
+ * the presence of "(u)" flag is stored there. Note that '-' is converted
+ * to '_' in the option name. If there is no '=' in the input string then
+ * value will be NULL.
*/
-void
-ParseLongOption(const char *string, char **name, char **value)
+static void
+ParseLongOptionInternal(const char *string, char **name, char **value,
+ bool *user_set)
{
size_t equal_pos;
char *cp;
@@ -6176,25 +6178,41 @@ ParseLongOption(const char *string, char **name, char **value)
equal_pos = strcspn(string, "=");
- if (string[equal_pos] == '=')
+ if (GUC_ARRAY_IS_USERSET_SIGN_BEFORE(string, string + equal_pos))
+ {
+ *name = palloc(equal_pos - GUC_ARRAY_USERSET_SIGN_LEN + 1);
+ strlcpy(*name, string, equal_pos - GUC_ARRAY_USERSET_SIGN_LEN + 1);
+ if (user_set)
+ *user_set = true;
+ }
+ else
{
*name = palloc(equal_pos + 1);
strlcpy(*name, string, equal_pos + 1);
+ if (user_set)
+ *user_set = false;
+ }
+ if (string[equal_pos] == '=')
*value = pstrdup(&string[equal_pos + 1]);
- }
else
- {
- /* no equal sign in string */
- *name = pstrdup(string);
*value = NULL;
- }
for (cp = *name; *cp; cp++)
if (*cp == '-')
*cp = '_';
}
+/*
+ * The exported version of ParseLongOptionInternal(). Doesn't need user_set
+ * argument since no external users need it.
+ */
+void
+ParseLongOption(const char *string, char **name, char **value)
+{
+ ParseLongOptionInternal(string, name, value, NULL);
+}
+
/*
* Handle options fetched from pg_db_role_setting.setconfig,
@@ -6220,6 +6238,7 @@ ProcessGUCArray(ArrayType *array,
char *s;
char *name;
char *value;
+ bool user_set;
d = array_ref(array, 1, &i,
-1 /* varlenarray */ ,
@@ -6233,7 +6252,7 @@ ProcessGUCArray(ArrayType *array,
s = TextDatumGetCString(d);
- ParseLongOption(s, &name, &value);
+ ParseLongOptionInternal(s, &name, &value, &user_set);
if (!value)
{
ereport(WARNING,
@@ -6245,7 +6264,7 @@ ProcessGUCArray(ArrayType *array,
}
(void) set_config_option(name, value,
- context, source,
+ user_set ? PGC_USERSET : context, source,
action, true, 0, false);
pfree(name);
@@ -6260,7 +6279,8 @@ ProcessGUCArray(ArrayType *array,
* to indicate the current table entry is NULL.
*/
ArrayType *
-GUCArrayAdd(ArrayType *array, const char *name, const char *value)
+GUCArrayAdd(ArrayType *array, const char *name, const char *value,
+ bool user_set)
{
struct config_generic *record;
Datum datum;
@@ -6271,7 +6291,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
Assert(value);
/* test if the option is valid and we're allowed to set it */
- (void) validate_option_array_item(name, value, false);
+ (void) validate_option_array_item(name, value, user_set, false);
/* normalize name (converts obsolete GUC names to modern spellings) */
record = find_option(name, false, true, WARNING);
@@ -6279,7 +6299,11 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
name = record->name;
/* build new item for array */
- newval = psprintf("%s=%s", name, value);
+ if (user_set)
+ newval = psprintf("%s" GUC_ARRAY_USERSET_SIGN "=%s",
+ name, value);
+ else
+ newval = psprintf("%s=%s", name, value);
datum = CStringGetTextDatum(newval);
if (array)
@@ -6309,9 +6333,17 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
continue;
current = TextDatumGetCString(d);
- /* check for match up through and including '=' */
- if (strncmp(current, newval, strlen(name) + 1) == 0)
+ /* check for the name match */
+ if (strncmp(current, newval, strlen(name)) == 0 &&
+ GUC_ARRAY_IS_NAME_BORDER(current + strlen(name)))
{
+ /*
+ * Recheck permissons if we found an option without USER SET
+ * flag while we're setting an optionn with USER SET flag.
+ */
+ if (current[strlen(name)] == '=' && user_set)
+ (void) validate_option_array_item(name, value,
+ false, false);
index = i;
break;
}
@@ -6347,9 +6379,6 @@ GUCArrayDelete(ArrayType *array, const char *name)
Assert(name);
- /* test if the option is valid and we're allowed to set it */
- (void) validate_option_array_item(name, NULL, false);
-
/* normalize name (converts obsolete GUC names to modern spellings) */
record = find_option(name, false, true, WARNING);
if (record)
@@ -6379,9 +6408,15 @@ GUCArrayDelete(ArrayType *array, const char *name)
val = TextDatumGetCString(d);
/* ignore entry if it's what we want to delete */
- if (strncmp(val, name, strlen(name)) == 0
- && val[strlen(name)] == '=')
+ if (strncmp(val, name, strlen(name)) == 0 &&
+ GUC_ARRAY_IS_NAME_BORDER(val + strlen(name)))
+ {
+ /* test if the option is valid and we're allowed to set it */
+ (void) validate_option_array_item(name, NULL,
+ GUC_ARRAY_IS_USERSET_SIGN(val + strlen(name)),
+ false);
continue;
+ }
/* else add it to the output array */
if (newarray)
@@ -6431,6 +6466,7 @@ GUCArrayReset(ArrayType *array)
char *val;
char *eqsgn;
bool isnull;
+ bool user_set = false;
d = array_ref(array, 1, &i,
-1 /* varlenarray */ ,
@@ -6443,10 +6479,18 @@ GUCArrayReset(ArrayType *array)
val = TextDatumGetCString(d);
eqsgn = strchr(val, '=');
- *eqsgn = '\0';
+ if (GUC_ARRAY_IS_USERSET_SIGN_BEFORE(val, eqsgn))
+ {
+ *(eqsgn - GUC_ARRAY_USERSET_SIGN_LEN) = '\0';
+ user_set = true;
+ }
+ else
+ {
+ *eqsgn = '\0';
+ }
/* skip if we have permission to delete it */
- if (validate_option_array_item(val, NULL, true))
+ if (validate_option_array_item(val, NULL, user_set, true))
continue;
/* else add it to the output array */
@@ -6472,15 +6516,16 @@ GUCArrayReset(ArrayType *array)
* Validate a proposed option setting for GUCArrayAdd/Delete/Reset.
*
* name is the option name. value is the proposed value for the Add case,
- * or NULL for the Delete/Reset cases. If skipIfNoPermissions is true, it's
- * not an error to have no permissions to set the option.
+ * or NULL for the Delete/Reset cases. user_set indicates this is the USER SET
+ * option. If skipIfNoPermissions is true, it's not an error to have no
+ * permissions to set the option.
*
* Returns true if OK, false if skipIfNoPermissions is true and user does not
* have permission to change this option (all other error cases result in an
* error being thrown).
*/
static bool
-validate_option_array_item(const char *name, const char *value,
+validate_option_array_item(const char *name, const char *value, bool user_set,
bool skipIfNoPermissions)
{
@@ -6516,8 +6561,10 @@ validate_option_array_item(const char *name, const char *value,
{
/*
* We cannot do any meaningful check on the value, so only permissions
- * are useful to check.
+ * are useful to check. USER SET options are always allowed.
*/
+ if (user_set)
+ return true;
if (superuser() ||
pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK)
return true;
diff --git a/src/backend/utils/misc/guc_funcs.c b/src/backend/utils/misc/guc_funcs.c
index 108b3bd1290..963921710cd 100644
--- a/src/backend/utils/misc/guc_funcs.c
+++ b/src/backend/utils/misc/guc_funcs.c
@@ -166,12 +166,22 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
char *
ExtractSetVariableArgs(VariableSetStmt *stmt)
{
+
switch (stmt->kind)
{
case VAR_SET_VALUE:
return flatten_set_variable_args(stmt->name, stmt->args);
case VAR_SET_CURRENT:
- return GetConfigOptionByName(stmt->name, NULL, false);
+ {
+ struct config_generic *record;
+ char *result;
+
+ result = GetConfigOptionByName(stmt->name, NULL, false);
+ record = find_option(stmt->name, false, false, ERROR);
+ stmt->user_set = (record->scontext == PGC_USERSET);
+
+ return result;
+ }
default:
return NULL;
}
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6e501a54138..4316232d1d1 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -16,6 +16,7 @@
#include <ctype.h>
+#include "common/guc-common.h"
#include "dumputils.h"
#include "fe_utils/string_utils.h"
@@ -804,8 +805,8 @@ SplitGUCList(char *rawstring, char separator,
/*
* Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands.
*
- * Parse the contents of configitem (a "name=value" string), wrap it in
- * a complete ALTER command, and append it to buf.
+ * Parse the contents of configitem (a "name=value" or "name(u)=value" string),
+ * wrap it in a complete ALTER command, and append it to buf.
*
* type is DATABASE or ROLE, and name is the name of the database or role.
* If we need an "IN" clause, type2 and name2 similarly define what to put
@@ -820,6 +821,7 @@ makeAlterConfigCommand(PGconn *conn, const char *configitem,
{
char *mine;
char *pos;
+ bool user_set = false;
/* Parse the configitem. If we can't find an "=", silently do nothing. */
mine = pg_strdup(configitem);
@@ -829,7 +831,13 @@ makeAlterConfigCommand(PGconn *conn, const char *configitem,
pg_free(mine);
return;
}
- *pos++ = '\0';
+ if (GUC_ARRAY_IS_USERSET_SIGN_BEFORE(mine, pos))
+ {
+ user_set = true;
+ *(pos - GUC_ARRAY_USERSET_SIGN_LEN) = '\0';
+ }
+ else
+ *pos++ = '\0';
/* Build the command, with suitable quoting for everything. */
appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
@@ -872,6 +880,10 @@ makeAlterConfigCommand(PGconn *conn, const char *configitem,
else
appendStringLiteralConn(buf, pos, conn);
+ /* Add USER SET flag if specified in the string */
+ if (user_set)
+ appendPQExpBufferStr(buf, "USER SET;\n");
+
appendPQExpBufferStr(buf, ";\n");
pg_free(mine);
diff --git a/src/include/common/guc-common.h b/src/include/common/guc-common.h
new file mode 100644
index 00000000000..8b31953789e
--- /dev/null
+++ b/src/include/common/guc-common.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * guc-common.h
+ * Common declarations for Grand Unified Configuration.
+ *
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/common/guc-common.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef GUC_COMMON_H
+#define GUC_COMMON_H
+
+/*
+ * The designator of USER SET value in GUC array.
+ */
+#define GUC_ARRAY_USERSET_SIGN "(u)"
+#define GUC_ARRAY_USERSET_SIGN_LEN \
+ (sizeof(GUC_ARRAY_USERSET_SIGN) - 1)
+#define GUC_ARRAY_IS_USERSET_SIGN(s) \
+ (strncmp((s), GUC_ARRAY_USERSET_SIGN, GUC_ARRAY_USERSET_SIGN_LEN) == 0)
+#define GUC_ARRAY_IS_USERSET_SIGN_BEFORE(start, eqsign) \
+ ((eqsign) - (start) >= GUC_ARRAY_USERSET_SIGN_LEN && \
+ GUC_ARRAY_IS_USERSET_SIGN((eqsign) - GUC_ARRAY_USERSET_SIGN_LEN))
+#define GUC_ARRAY_IS_NAME_BORDER(s) \
+ ((*(s)) == '=' || GUC_ARRAY_IS_USERSET_SIGN(s))
+
+#endif /* GUC_COMMON_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7e7ad3f7e47..d48acda7c7b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2228,6 +2228,7 @@ typedef struct VariableSetStmt
char *name; /* variable to be set */
List *args; /* List of A_Const nodes */
bool is_local; /* SET LOCAL? */
+ bool user_set;
} VariableSetStmt;
/* ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index b3aaff9665b..9802973f086 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -12,6 +12,7 @@
#ifndef GUC_H
#define GUC_H
+#include "common/guc-common.h"
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
#include "utils/array.h"
@@ -393,7 +394,8 @@ extern char *GetConfigOptionByName(const char *name, const char **varname,
extern void ProcessGUCArray(ArrayType *array,
GucContext context, GucSource source, GucAction action);
-extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value);
+extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name,
+ const char *value, bool user_set);
extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
extern ArrayType *GUCArrayReset(ArrayType *array);
--
2.24.3 (Apple Git-128)