if (client_min_messages < WARNING)
(void) set_config_option("client_min_messages", "warning",
PGC_USERSET, PGC_S_SESSION,
- GUC_ACTION_LOCAL, true);
+ GUC_ACTION_LOCAL, true, 0);
save_log_min_messages =
pstrdup(GetConfigOption("log_min_messages", false, false));
if (log_min_messages < WARNING)
(void) set_config_option("log_min_messages", "warning",
PGC_SUSET, PGC_S_SESSION,
- GUC_ACTION_LOCAL, true);
+ GUC_ACTION_LOCAL, true, 0);
/*
* Set up the search path to contain the target schema, then the schemas
(void) set_config_option("search_path", pathbuf.data,
PGC_USERSET, PGC_S_SESSION,
- GUC_ACTION_LOCAL, true);
+ GUC_ACTION_LOCAL, true, 0);
/*
* Set creating_extension and related variables so that
*/
(void) set_config_option("search_path", save_search_path,
PGC_USERSET, PGC_S_SESSION,
- GUC_ACTION_LOCAL, true);
+ GUC_ACTION_LOCAL, true, 0);
(void) set_config_option("client_min_messages", save_client_min_messages,
PGC_USERSET, PGC_S_SESSION,
- GUC_ACTION_LOCAL, true);
+ GUC_ACTION_LOCAL, true, 0);
(void) set_config_option("log_min_messages", save_log_min_messages,
PGC_SUSET, PGC_S_SESSION,
- GUC_ACTION_LOCAL, true);
+ GUC_ACTION_LOCAL, true, 0);
}
/*
static void InitializeOneGUCOption(struct config_generic * gconf);
static void push_old_value(struct config_generic * gconf, GucAction action);
static void ReportGUCOption(struct config_generic * record);
+static void reapply_stacked_values(struct config_generic * variable,
+ struct config_string *pHolder,
+ GucStack *stack,
+ const char *curvalue,
+ GucContext curscontext, GucSource cursource);
static void ShowGUCConfigOption(const char *name, DestReceiver *dest);
static void ShowAllGUCConfig(DestReceiver *dest);
static char *_ShowOption(struct config_generic * record, bool use_units);
* If changeVal is false then don't really set the option but do all
* the checks to see if it would work.
*
+ * elevel should normally be passed as zero, allowing this function to make
+ * its standard choice of ereport level. However some callers need to be
+ * able to override that choice; they should pass the ereport level to use.
+ *
* Return value:
* +1: the value is valid and was successfully applied.
* 0: the name or value is invalid (but see below).
* If there is an error (non-existing option, invalid value) then an
* ereport(ERROR) is thrown *unless* this is called for a source for which
* we don't want an ERROR (currently, those are defaults, the config file,
- * and per-database or per-user settings). In those cases we write a
- * suitable error message via ereport() and return 0.
+ * and per-database or per-user settings, as well as callers who specify
+ * a less-than-ERROR elevel). In those cases we write a suitable error
+ * message via ereport() and return 0.
*
* See also SetConfigOption for an external interface.
*/
int
set_config_option(const char *name, const char *value,
GucContext context, GucSource source,
- GucAction action, bool changeVal)
+ GucAction action, bool changeVal, int elevel)
{
struct config_generic *record;
- int elevel;
bool prohibitValueChange = false;
bool makeDefault;
- if (source == PGC_S_DEFAULT || source == PGC_S_FILE)
+ if (elevel == 0)
{
- /*
- * To avoid cluttering the log, only the postmaster bleats loudly
- * about problems with the config file.
- */
- elevel = IsUnderPostmaster ? DEBUG3 : LOG;
+ if (source == PGC_S_DEFAULT || source == PGC_S_FILE)
+ {
+ /*
+ * To avoid cluttering the log, only the postmaster bleats loudly
+ * about problems with the config file.
+ */
+ elevel = IsUnderPostmaster ? DEBUG3 : LOG;
+ }
+ else if (source == PGC_S_DATABASE || source == PGC_S_USER ||
+ source == PGC_S_DATABASE_USER)
+ elevel = WARNING;
+ else
+ elevel = ERROR;
}
- else if (source == PGC_S_DATABASE || source == PGC_S_USER ||
- source == PGC_S_DATABASE_USER)
- elevel = WARNING;
- else
- elevel = ERROR;
record = find_option(name, true, elevel);
if (record == NULL)
}
/*
- * Set a config option to the given value. See also set_config_option,
- * this is just the wrapper to be called from outside GUC. NB: this
- * is used only for non-transactional operations.
+ * Set a config option to the given value.
+ *
+ * See also set_config_option; this is just the wrapper to be called from
+ * outside GUC. (This function should be used when possible, because its API
+ * is more stable than set_config_option's.)
*
* Note: there is no support here for setting source file/line, as it
* is currently not needed.
GucContext context, GucSource source)
{
(void) set_config_option(name, value, context, source,
- GUC_ACTION_SET, true);
+ GUC_ACTION_SET, true, 0);
}
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
action,
- true);
+ true,
+ 0);
break;
case VAR_SET_MULTI:
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
action,
- true);
+ true,
+ 0);
break;
case VAR_RESET_ALL:
ResetAllOptions();
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
- true);
+ true,
+ 0);
}
/*
(superuser() ? PGC_SUSET : PGC_USERSET),
PGC_S_SESSION,
is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET,
- true);
+ true,
+ 0);
/* get the new current value */
new_value = GetConfigOptionByName(name, NULL);
{
const char *name = variable->name;
const char **nameAddr = &name;
- const char *value;
struct config_string *pHolder;
struct config_generic **res;
*res = variable;
/*
- * Assign the string value stored in the placeholder to the real variable.
+ * Assign the string value(s) stored in the placeholder to the real
+ * variable. Essentially, we need to duplicate all the active and stacked
+ * values, but with appropriate validation and datatype adjustment.
*
- * XXX this is not really good enough --- it should be a nontransactional
- * assignment, since we don't want it to roll back if the current xact
- * fails later. (Or do we?)
+ * If an assignment fails, we report a WARNING and keep going. We don't
+ * want to throw ERROR for bad values, because it'd bollix the add-on
+ * module that's presumably halfway through getting loaded. In such cases
+ * the default or previous state will become active instead.
*/
- value = *pHolder->variable;
- if (value)
- {
- if (set_config_option(name, value,
- pHolder->gen.scontext, pHolder->gen.source,
- GUC_ACTION_SET, true) != 0)
- {
- /* Also copy over any saved source-location information */
- if (pHolder->gen.sourcefile)
- set_config_sourcefile(name, pHolder->gen.sourcefile,
- pHolder->gen.sourceline);
- }
- }
+ /* 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);
+ /* 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);
+
+ /* Also copy over any saved source-location information */
+ if (pHolder->gen.sourcefile)
+ set_config_sourcefile(name, pHolder->gen.sourcefile,
+ pHolder->gen.sourceline);
/*
- * Free up as much as we conveniently can of the placeholder structure
- * (this neglects any stack items...)
+ * Free up as much as we conveniently can of the placeholder structure.
+ * (This neglects any stack items, so it's possible for some memory to be
+ * leaked. Since this can only happen once per session per variable, it
+ * doesn't seem worth spending much code on.)
*/
set_string_field(pHolder, pHolder->variable, NULL);
set_string_field(pHolder, &pHolder->reset_val, NULL);
free(pHolder);
}
+/*
+ * Recursive subroutine for define_custom_variable: reapply non-reset values
+ *
+ * We recurse so that the values are applied in the same order as originally.
+ * At each recursion level, apply the upper-level value (passed in) in the
+ * fashion implied by the stack entry.
+ */
+static void
+reapply_stacked_values(struct config_generic * variable,
+ struct config_string *pHolder,
+ GucStack *stack,
+ const char *curvalue,
+ GucContext curscontext, GucSource cursource)
+{
+ const char *name = variable->name;
+ GucStack *oldvarstack = variable->stack;
+
+ if (stack != NULL)
+ {
+ /* 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);
+
+ /* 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);
+ break;
+
+ case GUC_SET:
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_SET, true, WARNING);
+ break;
+
+ case GUC_LOCAL:
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_LOCAL, true, WARNING);
+ 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);
+ /* then apply the current value as LOCAL */
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_LOCAL, true, WARNING);
+ break;
+ }
+
+ /* If we successfully made a stack entry, adjust its nest level */
+ if (variable->stack != oldvarstack)
+ variable->stack->nest_level = stack->nest_level;
+ }
+ else
+ {
+ /*
+ * We are at the end of the stack. If the active/previous value is
+ * different from the reset value, it must represent a previously
+ * committed session value. Apply it, and then drop the stack entry
+ * that set_config_option will have created under the impression that
+ * this is to be just a transactional assignment. (We leak the stack
+ * entry.)
+ */
+ if (curvalue != pHolder->reset_val ||
+ curscontext != pHolder->gen.reset_scontext ||
+ cursource != pHolder->gen.reset_source)
+ {
+ (void) set_config_option(name, curvalue,
+ curscontext, cursource,
+ GUC_ACTION_SET, true, WARNING);
+ variable->stack = NULL;
+ }
+ }
+}
+
void
DefineCustomBoolVariable(const char *name,
const char *short_desc,
(void) set_config_option(varname, varvalue,
varscontext, varsource,
- GUC_ACTION_SET, true);
+ GUC_ACTION_SET, true, 0);
if (varsourcefile[0])
set_config_sourcefile(varname, varsourcefile, varsourceline);
continue;
}
- (void) set_config_option(name, value, context, source, action, true);
+ (void) set_config_option(name, value,
+ context, source,
+ action, true, 0);
free(name);
if (value)
/* test for permissions and valid option value */
(void) set_config_option(name, value,
superuser() ? PGC_SUSET : PGC_USERSET,
- PGC_S_TEST, GUC_ACTION_SET, false);
+ PGC_S_TEST, GUC_ACTION_SET, false, 0);
return true;
}