From 37838f16be7549936457ce265ab290a76928fd72 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov 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)