Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_db_role_setting.c46
-rw-r--r--src/backend/catalog/pg_proc.c1
-rw-r--r--src/backend/commands/functioncmds.c4
-rw-r--r--src/backend/parser/gram.y20
-rw-r--r--src/backend/utils/adt/arrayfuncs.c1
-rw-r--r--src/backend/utils/fmgr/fmgr.c1
-rw-r--r--src/backend/utils/misc/guc.c161
-rw-r--r--src/backend/utils/misc/guc_funcs.c12
8 files changed, 217 insertions, 29 deletions
diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c
index 42387f4e304..6572fcd965b 100644
--- a/src/backend/catalog/pg_db_role_setting.c
+++ b/src/backend/catalog/pg_db_role_setting.c
@@ -63,14 +63,23 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
if (HeapTupleIsValid(tuple))
{
ArrayType *new = NULL;
+ ArrayType *usersetArray;
Datum datum;
+ Datum usersetDatum;
bool isnull;
+ bool usersetIsnull;
datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig,
RelationGetDescr(rel), &isnull);
+ usersetDatum = heap_getattr(tuple, Anum_pg_db_role_setting_setuser,
+ RelationGetDescr(rel), &usersetIsnull);
if (!isnull)
- new = GUCArrayReset(DatumGetArrayTypeP(datum));
+ {
+ Assert(!usersetIsnull);
+ usersetArray = DatumGetArrayTypeP(usersetDatum);
+ new = GUCArrayReset(DatumGetArrayTypeP(datum), &usersetArray);
+ }
if (new)
{
@@ -86,6 +95,11 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true;
repl_null[Anum_pg_db_role_setting_setconfig - 1] = false;
+ repl_val[Anum_pg_db_role_setting_setuser - 1] =
+ PointerGetDatum(usersetArray);
+ repl_repl[Anum_pg_db_role_setting_setuser - 1] = true;
+ repl_null[Anum_pg_db_role_setting_setuser - 1] = false;
+
newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
CatalogTupleUpdate(rel, &tuple->t_self, newtuple);
@@ -101,28 +115,39 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
bool repl_repl[Natts_pg_db_role_setting];
HeapTuple newtuple;
Datum datum;
+ Datum usersetDatum;
bool isnull;
+ bool usersetIsnull;
ArrayType *a;
+ ArrayType *usersetArray;
memset(repl_repl, false, sizeof(repl_repl));
repl_repl[Anum_pg_db_role_setting_setconfig - 1] = true;
repl_null[Anum_pg_db_role_setting_setconfig - 1] = false;
+ repl_repl[Anum_pg_db_role_setting_setuser - 1] = true;
+ repl_null[Anum_pg_db_role_setting_setuser - 1] = false;
- /* Extract old value of setconfig */
+ /* Extract old values of setconfig and setuser */
datum = heap_getattr(tuple, Anum_pg_db_role_setting_setconfig,
RelationGetDescr(rel), &isnull);
a = isnull ? NULL : DatumGetArrayTypeP(datum);
+ usersetDatum = heap_getattr(tuple, Anum_pg_db_role_setting_setuser,
+ RelationGetDescr(rel), &usersetIsnull);
+ usersetArray = usersetIsnull ? NULL : DatumGetArrayTypeP(usersetDatum);
+
/* Update (valuestr is NULL in RESET cases) */
if (valuestr)
- a = GUCArrayAdd(a, setstmt->name, valuestr);
+ a = GUCArrayAdd(a, &usersetArray, setstmt->name, valuestr, setstmt->user_set);
else
- a = GUCArrayDelete(a, setstmt->name);
+ a = GUCArrayDelete(a, &usersetArray, setstmt->name);
if (a)
{
repl_val[Anum_pg_db_role_setting_setconfig - 1] =
PointerGetDatum(a);
+ repl_val[Anum_pg_db_role_setting_setuser - 1] =
+ PointerGetDatum(usersetArray);
newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
@@ -137,16 +162,18 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt)
HeapTuple newtuple;
Datum values[Natts_pg_db_role_setting];
bool nulls[Natts_pg_db_role_setting];
- ArrayType *a;
+ ArrayType *a,
+ *usersetArray;
memset(nulls, false, sizeof(nulls));
- a = GUCArrayAdd(NULL, setstmt->name, valuestr);
+ a = GUCArrayAdd(NULL, &usersetArray, setstmt->name, valuestr, setstmt->user_set);
values[Anum_pg_db_role_setting_setdatabase - 1] =
ObjectIdGetDatum(databaseid);
values[Anum_pg_db_role_setting_setrole - 1] = ObjectIdGetDatum(roleid);
values[Anum_pg_db_role_setting_setconfig - 1] = PointerGetDatum(a);
+ values[Anum_pg_db_role_setting_setuser - 1] = PointerGetDatum(usersetArray);
newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
CatalogTupleInsert(rel, newtuple);
@@ -240,20 +267,25 @@ ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid,
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
bool isnull;
+ bool usersetIsnull;
Datum datum;
+ Datum usersetDatum;
datum = heap_getattr(tup, Anum_pg_db_role_setting_setconfig,
RelationGetDescr(relsetting), &isnull);
+ usersetDatum = heap_getattr(tup, Anum_pg_db_role_setting_setuser,
+ RelationGetDescr(relsetting), &usersetIsnull);
if (!isnull)
{
ArrayType *a = DatumGetArrayTypeP(datum);
+ ArrayType *usersetArray = DatumGetArrayTypeP(usersetDatum);
/*
* We process all the options at SUSET level. We assume that the
* right to insert an option into pg_db_role_setting was checked
* when it was inserted.
*/
- ProcessGUCArray(a, PGC_SUSET, source, GUC_ACTION_SET);
+ ProcessGUCArray(a, usersetArray, PGC_SUSET, source, GUC_ACTION_SET);
}
}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 69f43aa0ecb..e3f9d0b5cfe 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -698,6 +698,7 @@ ProcedureCreate(const char *procedureName,
{
save_nestlevel = NewGUCNestLevel();
ProcessGUCArray(set_items,
+ NULL,
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
GUC_ACTION_SAVE);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 57489f65f2e..f020fe5ec85 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -662,9 +662,9 @@ update_proconfig_value(ArrayType *a, List *set_items)
char *valuestr = ExtractSetVariableArgs(sstmt);
if (valuestr)
- a = GUCArrayAdd(a, sstmt->name, valuestr);
+ a = GUCArrayAdd(a, NULL, sstmt->name, valuestr, sstmt->user_set);
else /* RESET */
- a = GUCArrayDelete(a, sstmt->name);
+ a = GUCArrayDelete(a, NULL, sstmt->name);
}
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b1ae5f834cd..adc3f8ced3b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -1621,6 +1621,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/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 495e449a9e9..59a0852d07c 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -3344,6 +3344,7 @@ construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
switch (elmtype)
{
case CHAROID:
+ case BOOLOID:
elmlen = 1;
elmbyval = true;
elmalign = TYPALIGN_CHAR;
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 3c210297aa1..cd0daa7e166 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -706,6 +706,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
if (fcache->proconfig)
{
ProcessGUCArray(fcache->proconfig,
+ NULL,
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
GUC_ACTION_SAVE);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 28313b3a94a..c6326d50532 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -225,7 +225,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);
@@ -245,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 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);
@@ -6182,7 +6181,6 @@ ParseLongOption(const char *string, char **name, char **value)
{
*name = palloc(equal_pos + 1);
strlcpy(*name, string, equal_pos + 1);
-
*value = pstrdup(&string[equal_pos + 1]);
}
else
@@ -6205,7 +6203,7 @@ ParseLongOption(const char *string, char **name, char **value)
* The array parameter must be an array of TEXT (it must not be NULL).
*/
void
-ProcessGUCArray(ArrayType *array,
+ProcessGUCArray(ArrayType *array, ArrayType *usersetArray,
GucContext context, GucSource source, GucAction action)
{
int i;
@@ -6218,6 +6216,7 @@ ProcessGUCArray(ArrayType *array,
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
Datum d;
+ Datum userSetDatum = BoolGetDatum(false);
bool isnull;
char *s;
char *name;
@@ -6246,9 +6245,29 @@ ProcessGUCArray(ArrayType *array,
continue;
}
- (void) set_config_option(name, value,
- context, source,
- action, true, 0, false);
+ if (usersetArray)
+ userSetDatum = array_ref(usersetArray, 1, &i,
+ -1 /* varlenarray */ ,
+ sizeof(bool) /* BOOL's typlen */ ,
+ true /* BOOL's typbyval */ ,
+ TYPALIGN_CHAR /* BOOL's typalign */ ,
+ &isnull);
+ if (isnull)
+ userSetDatum = BoolGetDatum(false);
+
+ /*
+ * USER SET values are appliciable only for PGC_USERSET parameters. We
+ * use InvalidOid as role in order to evade possible privileges of the
+ * current user.
+ */
+ if (!DatumGetBool(userSetDatum))
+ (void) set_config_option(name, value,
+ context, source,
+ action, true, 0, false);
+ else
+ (void) set_config_option_ext(name, value,
+ PGC_USERSET, source, InvalidOid,
+ action, true, 0, false);
pfree(name);
pfree(value);
@@ -6262,7 +6281,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, ArrayType **usersetArray,
+ const char *name, const char *value, bool user_set)
{
struct config_generic *record;
Datum datum;
@@ -6273,7 +6293,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);
@@ -6314,6 +6334,27 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
/* check for match up through and including '=' */
if (strncmp(current, newval, strlen(name) + 1) == 0)
{
+ bool currentUserSet = false;
+
+ if (usersetArray)
+ {
+ currentUserSet = DatumGetBool(array_ref(*usersetArray, 1, &i,
+ -1 /* varlenarray */ ,
+ sizeof(bool) /* BOOL's typlen */ ,
+ true /* BOOL's typbyval */ ,
+ TYPALIGN_CHAR /* BOOL's typalign */ ,
+ &isnull));
+ if (isnull)
+ currentUserSet = false;
+ }
+
+ /*
+ * Recheck permissons if we found an option without USER SET
+ * flag while we're setting an optionn with USER SET flag.
+ */
+ if (!currentUserSet && user_set)
+ (void) validate_option_array_item(name, value,
+ false, false);
index = i;
break;
}
@@ -6326,9 +6367,25 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
TYPALIGN_INT /* TEXT's typalign */ );
+
+ if (usersetArray)
+ *usersetArray = array_set(*usersetArray, 1, &index,
+ BoolGetDatum(user_set),
+ false,
+ -1 /* varlena array */ ,
+ sizeof(bool) /* BOOL's typlen */ ,
+ true /* BOOL's typbyval */ ,
+ TYPALIGN_CHAR /* BOOL's typalign */ );
}
else
+ {
a = construct_array_builtin(&datum, 1, TEXTOID);
+ if (usersetArray)
+ {
+ datum = BoolGetDatum(user_set);
+ *usersetArray = construct_array_builtin(&datum, 1, BOOLOID);
+ }
+ }
return a;
}
@@ -6340,18 +6397,16 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
* is NULL then a null should be stored.
*/
ArrayType *
-GUCArrayDelete(ArrayType *array, const char *name)
+GUCArrayDelete(ArrayType *array, ArrayType **usersetArray, const char *name)
{
struct config_generic *record;
ArrayType *newarray;
+ ArrayType *newUsersetArray;
int i;
int index;
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)
@@ -6362,11 +6417,13 @@ GUCArrayDelete(ArrayType *array, const char *name)
return NULL;
newarray = NULL;
+ newUsersetArray = NULL;
index = 1;
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
Datum d;
+ Datum userSetDatum = BoolGetDatum(false);
char *val;
bool isnull;
@@ -6380,13 +6437,29 @@ GUCArrayDelete(ArrayType *array, const char *name)
continue;
val = TextDatumGetCString(d);
+ if (usersetArray)
+ userSetDatum = array_ref(*usersetArray, 1, &i,
+ -1 /* varlenarray */ ,
+ sizeof(bool) /* BOOL's typlen */ ,
+ true /* BOOL's typbyval */ ,
+ TYPALIGN_CHAR /* BOOL's typalign */ ,
+ &isnull);
+ if (isnull)
+ userSetDatum = BoolGetDatum(false);
+
/* ignore entry if it's what we want to delete */
if (strncmp(val, name, strlen(name)) == 0
&& val[strlen(name)] == '=')
+ {
+ /* test if the option is valid and we're allowed to set it */
+ (void) validate_option_array_item(name, NULL,
+ DatumGetBool(userSetDatum), false);
continue;
+ }
/* else add it to the output array */
if (newarray)
+ {
newarray = array_set(newarray, 1, &index,
d,
false,
@@ -6394,12 +6467,28 @@ GUCArrayDelete(ArrayType *array, const char *name)
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
TYPALIGN_INT /* TEXT's typalign */ );
+ if (usersetArray)
+ newUsersetArray = array_set(newUsersetArray, 1, &index,
+ userSetDatum,
+ false,
+ -1 /* varlena array */ ,
+ sizeof(bool) /* BOOL's typlen */ ,
+ true /* BOOL's typbyval */ ,
+ TYPALIGN_CHAR /* BOOL's typalign */ );
+ }
else
+ {
newarray = construct_array_builtin(&d, 1, TEXTOID);
+ if (usersetArray)
+ newUsersetArray = construct_array_builtin(&d, 1, BOOLOID);
+ }
index++;
}
+ if (usersetArray)
+ *usersetArray = newUsersetArray;
+
return newarray;
}
@@ -6410,9 +6499,10 @@ GUCArrayDelete(ArrayType *array, const char *name)
* those that are PGC_USERSET or we have permission to set
*/
ArrayType *
-GUCArrayReset(ArrayType *array)
+GUCArrayReset(ArrayType *array, ArrayType **usersetArray)
{
ArrayType *newarray;
+ ArrayType *newUsersetArray;
int i;
int index;
@@ -6425,11 +6515,13 @@ GUCArrayReset(ArrayType *array)
return NULL;
newarray = NULL;
+ newUsersetArray = NULL;
index = 1;
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
Datum d;
+ Datum userSetDatum = BoolGetDatum(false);
char *val;
char *eqsgn;
bool isnull;
@@ -6444,15 +6536,27 @@ GUCArrayReset(ArrayType *array)
continue;
val = TextDatumGetCString(d);
+ if (usersetArray)
+ userSetDatum = array_ref(*usersetArray, 1, &i,
+ -1 /* varlenarray */ ,
+ sizeof(bool) /* BOOL's typlen */ ,
+ true /* BOOL's typbyval */ ,
+ TYPALIGN_CHAR /* BOOL's typalign */ ,
+ &isnull);
+ if (isnull)
+ userSetDatum = BoolGetDatum(false);
+
eqsgn = strchr(val, '=');
*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,
+ DatumGetBool(userSetDatum), true))
continue;
/* else add it to the output array */
if (newarray)
+ {
newarray = array_set(newarray, 1, &index,
d,
false,
@@ -6460,13 +6564,29 @@ GUCArrayReset(ArrayType *array)
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
TYPALIGN_INT /* TEXT's typalign */ );
+ if (usersetArray)
+ newUsersetArray = array_set(newUsersetArray, 1, &index,
+ userSetDatum,
+ false,
+ -1 /* varlena array */ ,
+ sizeof(bool) /* BOOL's typlen */ ,
+ true /* BOOL's typbyval */ ,
+ TYPALIGN_CHAR /* BOOL's typalign */ );
+ }
else
+ {
newarray = construct_array_builtin(&d, 1, TEXTOID);
+ if (usersetArray)
+ newUsersetArray = construct_array_builtin(&userSetDatum, 1, BOOLOID);
+ }
index++;
pfree(val);
}
+ if (usersetArray)
+ *usersetArray = newUsersetArray;
+
return newarray;
}
@@ -6474,15 +6594,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)
{
@@ -6518,8 +6639,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..23da603fe76 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;
}