struct config_string *pHolder,
GucStack *stack,
const char *curvalue,
- GucContext curscontext, GucSource cursource);
+ GucContext curscontext, GucSource cursource,
+ Oid cursrole);
static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
static void ShowAllGUCConfig(DestReceiver *dest);
static char *_ShowOption(struct config_generic *record, bool use_units);
check_wal_consistency_checking_deferred = false;
- set_config_option("wal_consistency_checking",
- wal_consistency_checking_string,
- PGC_POSTMASTER, guc->source,
- GUC_ACTION_SET, true, ERROR, false);
+ set_config_option_ext("wal_consistency_checking",
+ wal_consistency_checking_string,
+ guc->scontext, guc->source, guc->srole,
+ GUC_ACTION_SET, true, ERROR, false);
/* checking should not be deferred again */
Assert(!check_wal_consistency_checking_deferred);
gconf->reset_source = PGC_S_DEFAULT;
gconf->scontext = PGC_INTERNAL;
gconf->reset_scontext = PGC_INTERNAL;
+ gconf->srole = BOOTSTRAP_SUPERUSERID;
+ gconf->reset_srole = BOOTSTRAP_SUPERUSERID;
gconf->stack = NULL;
gconf->extra = NULL;
gconf->last_reported = NULL;
gconf->source = gconf->reset_source;
gconf->scontext = gconf->reset_scontext;
+ gconf->srole = gconf->reset_srole;
if (gconf->flags & GUC_REPORT)
{
{
/* SET followed by SET LOCAL, remember SET's value */
stack->masked_scontext = gconf->scontext;
+ stack->masked_srole = gconf->srole;
set_stack_value(gconf, &stack->masked);
stack->state = GUC_SET_LOCAL;
}
}
stack->source = gconf->source;
stack->scontext = gconf->scontext;
+ stack->srole = gconf->srole;
set_stack_value(gconf, &stack->prior);
gconf->stack = stack;
{
/* LOCAL migrates down */
prev->masked_scontext = stack->scontext;
+ prev->masked_srole = stack->srole;
prev->masked = stack->prior;
prev->state = GUC_SET_LOCAL;
}
discard_stack_value(gconf, &stack->prior);
/* copy down the masked state */
prev->masked_scontext = stack->masked_scontext;
+ prev->masked_srole = stack->masked_srole;
if (prev->state == GUC_SET_LOCAL)
discard_stack_value(gconf, &prev->masked);
prev->masked = stack->masked;
config_var_value newvalue;
GucSource newsource;
GucContext newscontext;
+ Oid newsrole;
if (restoreMasked)
{
newvalue = stack->masked;
newsource = PGC_S_SESSION;
newscontext = stack->masked_scontext;
+ newsrole = stack->masked_srole;
}
else
{
newvalue = stack->prior;
newsource = stack->source;
newscontext = stack->scontext;
+ newsrole = stack->srole;
}
switch (gconf->vartype)
/* And restore source information */
gconf->source = newsource;
gconf->scontext = newscontext;
+ gconf->srole = newsrole;
}
/* Finish popping the state stack */
/*
- * Sets option `name' to given value.
+ * set_config_option: sets option `name' to given value.
*
* The value should be a string, which will be parsed and converted to
* the appropriate data type. The context and source parameters indicate
GucContext context, GucSource source,
GucAction action, bool changeVal, int elevel,
bool is_reload)
+{
+ Oid srole;
+
+ /*
+ * Non-interactive sources should be treated as having all privileges,
+ * except for PGC_S_CLIENT. Note in particular that this is true for
+ * pg_db_role_setting sources (PGC_S_GLOBAL etc): we assume a suitable
+ * privilege check was done when the pg_db_role_setting entry was made.
+ */
+ if (source >= PGC_S_INTERACTIVE || source == PGC_S_CLIENT)
+ srole = GetUserId();
+ else
+ srole = BOOTSTRAP_SUPERUSERID;
+
+ return set_config_option_ext(name, value,
+ context, source, srole,
+ action, changeVal, elevel,
+ is_reload);
+}
+
+/*
+ * set_config_option_ext: sets option `name' to given value.
+ *
+ * This API adds the ability to explicitly specify which role OID
+ * is considered to be setting the value. Most external callers can use
+ * set_config_option() and let it determine that based on the GucSource,
+ * but there are a few that are supplying a value that was determined
+ * in some special way and need to override the decision. Also, when
+ * restoring a previously-assigned value, it's important to supply the
+ * same role OID that set the value originally; so all guc.c callers
+ * that are doing that type of thing need to call this directly.
+ *
+ * Generally, srole should be GetUserId() when the source is a SQL operation,
+ * or BOOTSTRAP_SUPERUSERID if the source is a config file or similar.
+ */
+int
+set_config_option_ext(const char *name, const char *value,
+ GucContext context, GucSource source, Oid srole,
+ GucAction action, bool changeVal, int elevel,
+ bool is_reload)
{
struct config_generic *record;
union config_var_val newval_union;
if (context == PGC_BACKEND)
{
/*
- * Check whether the current user has been granted privilege
- * to set this GUC.
+ * Check whether the requesting user has been granted
+ * privilege to set this GUC.
*/
AclResult aclresult;
- aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET);
+ aclresult = pg_parameter_aclcheck(name, srole, ACL_SET);
if (aclresult != ACLCHECK_OK)
{
/* No granted privilege */
if (context == PGC_USERSET || context == PGC_BACKEND)
{
/*
- * Check whether the current user has been granted privilege
- * to set this GUC.
+ * Check whether the requesting user has been granted
+ * privilege to set this GUC.
*/
AclResult aclresult;
- aclresult = pg_parameter_aclcheck(name, GetUserId(), ACL_SET);
+ aclresult = pg_parameter_aclcheck(name, srole, ACL_SET);
if (aclresult != ACLCHECK_OK)
{
/* No granted privilege */
newextra = conf->reset_extra;
source = conf->gen.reset_source;
context = conf->gen.reset_scontext;
+ srole = conf->gen.reset_srole;
}
if (prohibitValueChange)
newextra);
conf->gen.source = source;
conf->gen.scontext = context;
+ conf->gen.srole = srole;
}
if (makeDefault)
{
newextra);
conf->gen.reset_source = source;
conf->gen.reset_scontext = context;
+ conf->gen.reset_srole = srole;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
newextra);
stack->source = source;
stack->scontext = context;
+ stack->srole = srole;
}
}
}
newextra = conf->reset_extra;
source = conf->gen.reset_source;
context = conf->gen.reset_scontext;
+ srole = conf->gen.reset_srole;
}
if (prohibitValueChange)
newextra);
conf->gen.source = source;
conf->gen.scontext = context;
+ conf->gen.srole = srole;
}
if (makeDefault)
{
newextra);
conf->gen.reset_source = source;
conf->gen.reset_scontext = context;
+ conf->gen.reset_srole = srole;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
newextra);
stack->source = source;
stack->scontext = context;
+ stack->srole = srole;
}
}
}
newextra = conf->reset_extra;
source = conf->gen.reset_source;
context = conf->gen.reset_scontext;
+ srole = conf->gen.reset_srole;
}
if (prohibitValueChange)
newextra);
conf->gen.source = source;
conf->gen.scontext = context;
+ conf->gen.srole = srole;
}
if (makeDefault)
{
newextra);
conf->gen.reset_source = source;
conf->gen.reset_scontext = context;
+ conf->gen.reset_srole = srole;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
newextra);
stack->source = source;
stack->scontext = context;
+ stack->srole = srole;
}
}
}
newextra = conf->reset_extra;
source = conf->gen.reset_source;
context = conf->gen.reset_scontext;
+ srole = conf->gen.reset_srole;
}
if (prohibitValueChange)
newextra);
conf->gen.source = source;
conf->gen.scontext = context;
+ conf->gen.srole = srole;
}
if (makeDefault)
newextra);
conf->gen.reset_source = source;
conf->gen.reset_scontext = context;
+ conf->gen.reset_srole = srole;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
newextra);
stack->source = source;
stack->scontext = context;
+ stack->srole = srole;
}
}
}
newextra = conf->reset_extra;
source = conf->gen.reset_source;
context = conf->gen.reset_scontext;
+ srole = conf->gen.reset_srole;
}
if (prohibitValueChange)
newextra);
conf->gen.source = source;
conf->gen.scontext = context;
+ conf->gen.srole = srole;
}
if (makeDefault)
{
newextra);
conf->gen.reset_source = source;
conf->gen.reset_scontext = context;
+ conf->gen.reset_srole = srole;
}
for (stack = conf->gen.stack; stack; stack = stack->prev)
{
newextra);
stack->source = source;
stack->scontext = context;
+ stack->srole = srole;
}
}
}
/* First, apply the reset value if any */
if (pHolder->reset_val)
- (void) set_config_option(name, pHolder->reset_val,
- pHolder->gen.reset_scontext,
- pHolder->gen.reset_source,
- GUC_ACTION_SET, true, WARNING, false);
+ (void) set_config_option_ext(name, pHolder->reset_val,
+ pHolder->gen.reset_scontext,
+ pHolder->gen.reset_source,
+ pHolder->gen.reset_srole,
+ GUC_ACTION_SET, true, WARNING, false);
/* That should not have resulted in stacking anything */
Assert(variable->stack == NULL);
/* Now, apply current and stacked values, in the order they were stacked */
reapply_stacked_values(variable, pHolder, pHolder->gen.stack,
*(pHolder->variable),
- pHolder->gen.scontext, pHolder->gen.source);
+ pHolder->gen.scontext, pHolder->gen.source,
+ pHolder->gen.srole);
/* Also copy over any saved source-location information */
if (pHolder->gen.sourcefile)
struct config_string *pHolder,
GucStack *stack,
const char *curvalue,
- GucContext curscontext, GucSource cursource)
+ GucContext curscontext, GucSource cursource,
+ Oid cursrole)
{
const char *name = variable->name;
GucStack *oldvarstack = variable->stack;
/* First, recurse, so that stack items are processed bottom to top */
reapply_stacked_values(variable, pHolder, stack->prev,
stack->prior.val.stringval,
- stack->scontext, stack->source);
+ stack->scontext, stack->source, stack->srole);
/* See how to apply the passed-in value */
switch (stack->state)
{
case GUC_SAVE:
- (void) set_config_option(name, curvalue,
- curscontext, cursource,
- GUC_ACTION_SAVE, true,
- WARNING, false);
+ (void) set_config_option_ext(name, curvalue,
+ curscontext, cursource, cursrole,
+ GUC_ACTION_SAVE, true,
+ WARNING, false);
break;
case GUC_SET:
- (void) set_config_option(name, curvalue,
- curscontext, cursource,
- GUC_ACTION_SET, true,
- WARNING, false);
+ (void) set_config_option_ext(name, curvalue,
+ curscontext, cursource, cursrole,
+ GUC_ACTION_SET, true,
+ WARNING, false);
break;
case GUC_LOCAL:
- (void) set_config_option(name, curvalue,
- curscontext, cursource,
- GUC_ACTION_LOCAL, true,
- WARNING, false);
+ (void) set_config_option_ext(name, curvalue,
+ curscontext, cursource, cursrole,
+ GUC_ACTION_LOCAL, true,
+ WARNING, false);
break;
case GUC_SET_LOCAL:
/* first, apply the masked value as SET */
- (void) set_config_option(name, stack->masked.val.stringval,
- stack->masked_scontext, PGC_S_SESSION,
- GUC_ACTION_SET, true,
- WARNING, false);
+ (void) set_config_option_ext(name, stack->masked.val.stringval,
+ stack->masked_scontext,
+ PGC_S_SESSION,
+ stack->masked_srole,
+ GUC_ACTION_SET, true,
+ WARNING, false);
/* then apply the current value as LOCAL */
- (void) set_config_option(name, curvalue,
- curscontext, cursource,
- GUC_ACTION_LOCAL, true,
- WARNING, false);
+ (void) set_config_option_ext(name, curvalue,
+ curscontext, cursource, cursrole,
+ GUC_ACTION_LOCAL, true,
+ WARNING, false);
break;
}
*/
if (curvalue != pHolder->reset_val ||
curscontext != pHolder->gen.reset_scontext ||
- cursource != pHolder->gen.reset_source)
+ cursource != pHolder->gen.reset_source ||
+ cursrole != pHolder->gen.reset_srole)
{
- (void) set_config_option(name, curvalue,
- curscontext, cursource,
- GUC_ACTION_SET, true, WARNING, false);
+ (void) set_config_option_ext(name, curvalue,
+ curscontext, cursource, cursrole,
+ GUC_ACTION_SET, true, WARNING, false);
variable->stack = NULL;
}
}
* variable sourceline, integer
* variable source, integer
* variable scontext, integer
+* variable srole, OID
*/
static void
write_one_nondefault_variable(FILE *fp, struct config_generic *gconf)
fwrite(&gconf->sourceline, 1, sizeof(gconf->sourceline), fp);
fwrite(&gconf->source, 1, sizeof(gconf->source), fp);
fwrite(&gconf->scontext, 1, sizeof(gconf->scontext), fp);
+ fwrite(&gconf->srole, 1, sizeof(gconf->srole), fp);
}
void
int varsourceline;
GucSource varsource;
GucContext varscontext;
+ Oid varsrole;
/*
* Open file
elog(FATAL, "invalid format of exec config params file");
if (fread(&varscontext, 1, sizeof(varscontext), fp) != sizeof(varscontext))
elog(FATAL, "invalid format of exec config params file");
+ if (fread(&varsrole, 1, sizeof(varsrole), fp) != sizeof(varsrole))
+ elog(FATAL, "invalid format of exec config params file");
- (void) set_config_option(varname, varvalue,
- varscontext, varsource,
- GUC_ACTION_SET, true, 0, true);
+ (void) set_config_option_ext(varname, varvalue,
+ varscontext, varsource, varsrole,
+ GUC_ACTION_SET, true, 0, true);
if (varsourcefile[0])
set_config_sourcefile(varname, varsourcefile, varsourceline);
size = add_size(size, sizeof(gconf->source));
size = add_size(size, sizeof(gconf->scontext));
+ size = add_size(size, sizeof(gconf->srole));
return size;
}
sizeof(gconf->source));
do_serialize_binary(destptr, maxbytes, &gconf->scontext,
sizeof(gconf->scontext));
+ do_serialize_binary(destptr, maxbytes, &gconf->srole,
+ sizeof(gconf->srole));
}
/*
int varsourceline;
GucSource varsource;
GucContext varscontext;
+ Oid varsrole;
char *srcptr = (char *) gucstate;
char *srcend;
Size len;
&varsource, sizeof(varsource));
read_gucstate_binary(&srcptr, srcend,
&varscontext, sizeof(varscontext));
+ read_gucstate_binary(&srcptr, srcend,
+ &varsrole, sizeof(varsrole));
error_context_name_and_value[0] = varname;
error_context_name_and_value[1] = varvalue;
error_context_callback.arg = &error_context_name_and_value[0];
- result = set_config_option(varname, varvalue, varscontext, varsource,
- GUC_ACTION_SET, true, ERROR, true);
+ result = set_config_option_ext(varname, varvalue,
+ varscontext, varsource, varsrole,
+ GUC_ACTION_SET, true, ERROR, true);
if (result <= 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
/*
* Given a GUC array, delete all settings from it that our permission
* level allows: if superuser, delete them all; if regular user, only
- * those that are PGC_USERSET
+ * those that are PGC_USERSET or we have permission to set
*/
ArrayType *
GUCArrayReset(ArrayType *array)
*
* name is a known GUC variable. Check the value normally, check
* permissions normally (i.e., allow if variable is USERSET, or if it's
- * SUSET and user is superuser).
+ * SUSET and user is superuser or holds ACL_SET permissions).
*
* name is not known, but exists or can be created as a placeholder (i.e.,
* it has a valid custom name). We allow this case if you're a superuser,
* otherwise not. Superusers are assumed to know what they're doing. We
* can't allow it for other users, because when the placeholder is
- * resolved it might turn out to be a SUSET variable;
- * define_custom_variable assumes we checked that.
+ * resolved it might turn out to be a SUSET variable. (With currently
+ * available infrastructure, we can actually handle such cases within the
+ * current session --- but once an entry is made in pg_db_role_setting,
+ * it's assumed to be fully validated.)
*
* name is not known and can't be created as a placeholder. Throw error,
* unless skipIfNoPermissions is true, in which case return false.
* We cannot do any meaningful check on the value, so only permissions
* are useful to check.
*/
- if (superuser())
+ if (superuser() ||
+ pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK)
return true;
if (skipIfNoPermissions)
return false;
/* manual permissions check so we can avoid an error being thrown */
if (gconf->context == PGC_USERSET)
/* ok */ ;
- else if (gconf->context == PGC_SUSET && superuser())
+ else if (gconf->context == PGC_SUSET &&
+ (superuser() ||
+ pg_parameter_aclcheck(name, GetUserId(), ACL_SET) == ACLCHECK_OK))
/* ok */ ;
else if (skipIfNoPermissions)
return false;