0001-ALTER-ROLE-.-SET-.-TO-.-USER-SET-v1.patch
application/octet-stream
Filename: 0001-ALTER-ROLE-.-SET-.-TO-.-USER-SET-v1.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 v1-0001
Subject: ALTER ROLE ... SET ... TO ... USER SET
| File | + | − |
|---|---|---|
| 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 | 72 | 18 |
| src/bin/pg_dump/dumputils.c | 10 | 1 |
| src/include/nodes/parsenodes.h | 1 | 0 |
| src/include/utils/guc.h | 2 | 1 |
From 37838f16be7549936457ce265ab290a76928fd72 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
---
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 | 90 +++++++++++++++++++-----
src/bin/pg_dump/dumputils.c | 11 ++-
src/include/nodes/parsenodes.h | 1 +
src/include/utils/guc.h | 3 +-
7 files changed, 108 insertions(+), 23 deletions(-)
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..c7af6305306 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -244,7 +244,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 skipIfNoPermissions, bool user_set);
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);
@@ -6164,8 +6164,9 @@ RestoreGUCState(void *gucstate)
* storage. 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;
@@ -6178,8 +6179,20 @@ ParseLongOption(const char *string, char **name, char **value)
if (string[equal_pos] == '=')
{
- *name = palloc(equal_pos + 1);
- strlcpy(*name, string, equal_pos + 1);
+ if (equal_pos >= 3 && string[equal_pos - 1] == ')')
+ {
+ *name = palloc(equal_pos - 2);
+ strlcpy(*name, string, equal_pos - 2);
+ if (user_set)
+ *user_set = true;
+ }
+ else
+ {
+ *name = palloc(equal_pos + 1);
+ strlcpy(*name, string, equal_pos + 1);
+ if (user_set)
+ *user_set = false;
+ }
*value = pstrdup(&string[equal_pos + 1]);
}
@@ -6188,6 +6201,9 @@ ParseLongOption(const char *string, char **name, char **value)
/* no equal sign in string */
*name = pstrdup(string);
*value = NULL;
+
+ if (user_set)
+ *user_set = false;
}
for (cp = *name; *cp; cp++)
@@ -6195,6 +6211,19 @@ ParseLongOption(const char *string, char **name, char **value)
*cp = '_';
}
+/*
+ * 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.
+ */
+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 +6249,7 @@ ProcessGUCArray(ArrayType *array,
char *s;
char *name;
char *value;
+ bool user_set;
d = array_ref(array, 1, &i,
-1 /* varlenarray */ ,
@@ -6233,7 +6263,7 @@ ProcessGUCArray(ArrayType *array,
s = TextDatumGetCString(d);
- ParseLongOption(s, &name, &value);
+ ParseLongOptionInternal(s, &name, &value, &user_set);
if (!value)
{
ereport(WARNING,
@@ -6245,7 +6275,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 +6290,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 +6302,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, false, user_set);
/* normalize name (converts obsolete GUC names to modern spellings) */
record = find_option(name, false, true, WARNING);
@@ -6279,7 +6310,10 @@ 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(u)=%s", name, value);
+ else
+ newval = psprintf("%s=%s", name, value);
datum = CStringGetTextDatum(newval);
if (array)
@@ -6310,8 +6344,15 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
current = TextDatumGetCString(d);
/* check for match up through and including '=' */
- if (strncmp(current, newval, strlen(name) + 1) == 0)
+ if (strncmp(current, newval, strlen(name)) == 0 &&
+ (current[strlen(name)] == '=' || current[strlen(name)] == '('))
{
+ /*
+ * recheck permissons if there is not USER SET option, while
+ * we were looking for USER SET
+ */
+ if (current[strlen(name)] == '=' && user_set)
+ (void) validate_option_array_item(name, value, false, false);
index = i;
break;
}
@@ -6347,9 +6388,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)
@@ -6380,8 +6418,13 @@ GUCArrayDelete(ArrayType *array, const char *name)
/* ignore entry if it's what we want to delete */
if (strncmp(val, name, strlen(name)) == 0
- && val[strlen(name)] == '=')
+ && (val[strlen(name)] == '=' || val[strlen(name)] == '('))
+ {
+ /* test if the option is valid and we're allowed to set it */
+ (void) validate_option_array_item(name, NULL, false,
+ val[strlen(name)] == '(');
continue;
+ }
/* else add it to the output array */
if (newarray)
@@ -6431,6 +6474,7 @@ GUCArrayReset(ArrayType *array)
char *val;
char *eqsgn;
bool isnull;
+ bool user_set = false;
d = array_ref(array, 1, &i,
-1 /* varlenarray */ ,
@@ -6443,10 +6487,18 @@ GUCArrayReset(ArrayType *array)
val = TextDatumGetCString(d);
eqsgn = strchr(val, '=');
- *eqsgn = '\0';
+ if (eqsgn - val >= 3 && eqsgn[-1] == ')')
+ {
+ eqsgn[-3] = '\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, true, user_set))
continue;
/* else add it to the output array */
@@ -6481,7 +6533,7 @@ GUCArrayReset(ArrayType *array)
*/
static bool
validate_option_array_item(const char *name, const char *value,
- bool skipIfNoPermissions)
+ bool skipIfNoPermissions, bool user_set)
{
struct config_generic *gconf;
@@ -6518,6 +6570,8 @@ 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.
*/
+ if (user_set)
+ return true;
if (superuser() ||
pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK)
return true;
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 6e501a54138..3aea75296dc 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -820,6 +820,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 +830,13 @@ makeAlterConfigCommand(PGconn *conn, const char *configitem,
pg_free(mine);
return;
}
- *pos++ = '\0';
+ if (pos - mine >= 3 && pos[-1] == ')')
+ {
+ user_set = true;
+ pos[-3] = '\0';
+ }
+ else
+ *pos++ = '\0';
/* Build the command, with suitable quoting for everything. */
appendPQExpBuffer(buf, "ALTER %s %s ", type, fmtId(name));
@@ -872,6 +879,8 @@ makeAlterConfigCommand(PGconn *conn, const char *configitem,
else
appendStringLiteralConn(buf, pos, conn);
+ if (user_set)
+ appendPQExpBufferStr(buf, "USER SET;\n");
appendPQExpBufferStr(buf, ";\n");
pg_free(mine);
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..95b605f9b1e 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -393,7 +393,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)