Preparatory refactoring for compiling guc-file.c standalone
Mostly this involves moving ProcessConfigFileInternal() to guc.c and fixing the shared API to match. Reviewed by Andres Freund Discussion: https://www.postgresql.org/message-id/20220810171935.7k5zgnjwqzalzmtm%40awork3.anarazel.de Discussion: https://www.postgresql.org/message-id/CAFBsxsF8Gc2StS3haXofshHCzqNMRXiSxvQEYGwnFsTmsdwNeg@mail.gmail.com
This commit is contained in:
parent
73b9d051c6
commit
1b188ea792
|
@ -49,12 +49,6 @@ static sigjmp_buf *GUC_flex_fatal_jmp;
|
||||||
|
|
||||||
static void FreeConfigVariable(ConfigVariable *item);
|
static void FreeConfigVariable(ConfigVariable *item);
|
||||||
|
|
||||||
static void record_config_file_error(const char *errmsg,
|
|
||||||
const char *config_file,
|
|
||||||
int lineno,
|
|
||||||
ConfigVariable **head_p,
|
|
||||||
ConfigVariable **tail_p);
|
|
||||||
|
|
||||||
static int GUC_flex_fatal(const char *msg);
|
static int GUC_flex_fatal(const char *msg);
|
||||||
|
|
||||||
/* LCOV_EXCL_START */
|
/* LCOV_EXCL_START */
|
||||||
|
@ -160,358 +154,6 @@ ProcessConfigFile(GucContext context)
|
||||||
MemoryContextDelete(config_cxt);
|
MemoryContextDelete(config_cxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This function handles both actual config file (re)loads and execution of
|
|
||||||
* show_all_file_settings() (i.e., the pg_file_settings view). In the latter
|
|
||||||
* case we don't apply any of the settings, but we make all the usual validity
|
|
||||||
* checks, and we return the ConfigVariable list so that it can be printed out
|
|
||||||
* by show_all_file_settings().
|
|
||||||
*/
|
|
||||||
static ConfigVariable *
|
|
||||||
ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
|
|
||||||
{
|
|
||||||
bool error = false;
|
|
||||||
bool applying = false;
|
|
||||||
const char *ConfFileWithError;
|
|
||||||
ConfigVariable *item,
|
|
||||||
*head,
|
|
||||||
*tail;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Parse the main config file into a list of option names and values */
|
|
||||||
ConfFileWithError = ConfigFileName;
|
|
||||||
head = tail = NULL;
|
|
||||||
|
|
||||||
if (!ParseConfigFile(ConfigFileName, true,
|
|
||||||
NULL, 0, 0, elevel,
|
|
||||||
&head, &tail))
|
|
||||||
{
|
|
||||||
/* Syntax error(s) detected in the file, so bail out */
|
|
||||||
error = true;
|
|
||||||
goto bail_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse the PG_AUTOCONF_FILENAME file, if present, after the main file to
|
|
||||||
* replace any parameters set by ALTER SYSTEM command. Because this file
|
|
||||||
* is in the data directory, we can't read it until the DataDir has been
|
|
||||||
* set.
|
|
||||||
*/
|
|
||||||
if (DataDir)
|
|
||||||
{
|
|
||||||
if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
|
|
||||||
NULL, 0, 0, elevel,
|
|
||||||
&head, &tail))
|
|
||||||
{
|
|
||||||
/* Syntax error(s) detected in the file, so bail out */
|
|
||||||
error = true;
|
|
||||||
ConfFileWithError = PG_AUTOCONF_FILENAME;
|
|
||||||
goto bail_out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If DataDir is not set, the PG_AUTOCONF_FILENAME file cannot be
|
|
||||||
* read. In this case, we don't want to accept any settings but
|
|
||||||
* data_directory from postgresql.conf, because they might be
|
|
||||||
* overwritten with settings in the PG_AUTOCONF_FILENAME file which
|
|
||||||
* will be read later. OTOH, since data_directory isn't allowed in the
|
|
||||||
* PG_AUTOCONF_FILENAME file, it will never be overwritten later.
|
|
||||||
*/
|
|
||||||
ConfigVariable *newlist = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prune all items except the last "data_directory" from the list.
|
|
||||||
*/
|
|
||||||
for (item = head; item; item = item->next)
|
|
||||||
{
|
|
||||||
if (!item->ignore &&
|
|
||||||
strcmp(item->name, "data_directory") == 0)
|
|
||||||
newlist = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newlist)
|
|
||||||
newlist->next = NULL;
|
|
||||||
head = tail = newlist;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Quick exit if data_directory is not present in file.
|
|
||||||
*
|
|
||||||
* We need not do any further processing, in particular we don't set
|
|
||||||
* PgReloadTime; that will be set soon by subsequent full loading of
|
|
||||||
* the config file.
|
|
||||||
*/
|
|
||||||
if (head == NULL)
|
|
||||||
goto bail_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Mark all extant GUC variables as not present in the config file. We
|
|
||||||
* need this so that we can tell below which ones have been removed from
|
|
||||||
* the file since we last processed it.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < num_guc_variables; i++)
|
|
||||||
{
|
|
||||||
struct config_generic *gconf = guc_variables[i];
|
|
||||||
|
|
||||||
gconf->status &= ~GUC_IS_IN_FILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if all the supplied option names are valid, as an additional
|
|
||||||
* quasi-syntactic check on the validity of the config file. It is
|
|
||||||
* important that the postmaster and all backends agree on the results of
|
|
||||||
* this phase, else we will have strange inconsistencies about which
|
|
||||||
* processes accept a config file update and which don't. Hence, unknown
|
|
||||||
* custom variable names have to be accepted without complaint. For the
|
|
||||||
* same reason, we don't attempt to validate the options' values here.
|
|
||||||
*
|
|
||||||
* In addition, the GUC_IS_IN_FILE flag is set on each existing GUC
|
|
||||||
* variable mentioned in the file; and we detect duplicate entries in the
|
|
||||||
* file and mark the earlier occurrences as ignorable.
|
|
||||||
*/
|
|
||||||
for (item = head; item; item = item->next)
|
|
||||||
{
|
|
||||||
struct config_generic *record;
|
|
||||||
|
|
||||||
/* Ignore anything already marked as ignorable */
|
|
||||||
if (item->ignore)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to find the variable; but do not create a custom placeholder if
|
|
||||||
* it's not there already.
|
|
||||||
*/
|
|
||||||
record = find_option(item->name, false, true, elevel);
|
|
||||||
|
|
||||||
if (record)
|
|
||||||
{
|
|
||||||
/* If it's already marked, then this is a duplicate entry */
|
|
||||||
if (record->status & GUC_IS_IN_FILE)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Mark the earlier occurrence(s) as dead/ignorable. We could
|
|
||||||
* avoid the O(N^2) behavior here with some additional state,
|
|
||||||
* but it seems unlikely to be worth the trouble.
|
|
||||||
*/
|
|
||||||
ConfigVariable *pitem;
|
|
||||||
|
|
||||||
for (pitem = head; pitem != item; pitem = pitem->next)
|
|
||||||
{
|
|
||||||
if (!pitem->ignore &&
|
|
||||||
strcmp(pitem->name, item->name) == 0)
|
|
||||||
pitem->ignore = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Now mark it as present in file */
|
|
||||||
record->status |= GUC_IS_IN_FILE;
|
|
||||||
}
|
|
||||||
else if (!valid_custom_variable_name(item->name))
|
|
||||||
{
|
|
||||||
/* Invalid non-custom variable, so complain */
|
|
||||||
ereport(elevel,
|
|
||||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
||||||
errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %d",
|
|
||||||
item->name,
|
|
||||||
item->filename, item->sourceline)));
|
|
||||||
item->errmsg = pstrdup("unrecognized configuration parameter");
|
|
||||||
error = true;
|
|
||||||
ConfFileWithError = item->filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we've detected any errors so far, we don't want to risk applying any
|
|
||||||
* changes.
|
|
||||||
*/
|
|
||||||
if (error)
|
|
||||||
goto bail_out;
|
|
||||||
|
|
||||||
/* Otherwise, set flag that we're beginning to apply changes */
|
|
||||||
applying = true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for variables having been removed from the config file, and
|
|
||||||
* revert their reset values (and perhaps also effective values) to the
|
|
||||||
* boot-time defaults. If such a variable can't be changed after startup,
|
|
||||||
* report that and continue.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < num_guc_variables; i++)
|
|
||||||
{
|
|
||||||
struct config_generic *gconf = guc_variables[i];
|
|
||||||
GucStack *stack;
|
|
||||||
|
|
||||||
if (gconf->reset_source != PGC_S_FILE ||
|
|
||||||
(gconf->status & GUC_IS_IN_FILE))
|
|
||||||
continue;
|
|
||||||
if (gconf->context < PGC_SIGHUP)
|
|
||||||
{
|
|
||||||
/* The removal can't be effective without a restart */
|
|
||||||
gconf->status |= GUC_PENDING_RESTART;
|
|
||||||
ereport(elevel,
|
|
||||||
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
|
|
||||||
errmsg("parameter \"%s\" cannot be changed without restarting the server",
|
|
||||||
gconf->name)));
|
|
||||||
record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
|
|
||||||
gconf->name),
|
|
||||||
NULL, 0,
|
|
||||||
&head, &tail);
|
|
||||||
error = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No more to do if we're just doing show_all_file_settings() */
|
|
||||||
if (!applySettings)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reset any "file" sources to "default", else set_config_option will
|
|
||||||
* not override those settings.
|
|
||||||
*/
|
|
||||||
if (gconf->reset_source == PGC_S_FILE)
|
|
||||||
gconf->reset_source = PGC_S_DEFAULT;
|
|
||||||
if (gconf->source == PGC_S_FILE)
|
|
||||||
gconf->source = PGC_S_DEFAULT;
|
|
||||||
for (stack = gconf->stack; stack; stack = stack->prev)
|
|
||||||
{
|
|
||||||
if (stack->source == PGC_S_FILE)
|
|
||||||
stack->source = PGC_S_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now we can re-apply the wired-in default (i.e., the boot_val) */
|
|
||||||
if (set_config_option(gconf->name, NULL,
|
|
||||||
context, PGC_S_DEFAULT,
|
|
||||||
GUC_ACTION_SET, true, 0, false) > 0)
|
|
||||||
{
|
|
||||||
/* Log the change if appropriate */
|
|
||||||
if (context == PGC_SIGHUP)
|
|
||||||
ereport(elevel,
|
|
||||||
(errmsg("parameter \"%s\" removed from configuration file, reset to default",
|
|
||||||
gconf->name)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Restore any variables determined by environment variables or
|
|
||||||
* dynamically-computed defaults. This is a no-op except in the case
|
|
||||||
* where one of these had been in the config file and is now removed.
|
|
||||||
*
|
|
||||||
* In particular, we *must not* do this during the postmaster's initial
|
|
||||||
* loading of the file, since the timezone functions in particular should
|
|
||||||
* be run only after initialization is complete.
|
|
||||||
*
|
|
||||||
* XXX this is an unmaintainable crock, because we have to know how to set
|
|
||||||
* (or at least what to call to set) every non-PGC_INTERNAL variable that
|
|
||||||
* could potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
|
|
||||||
*/
|
|
||||||
if (context == PGC_SIGHUP && applySettings)
|
|
||||||
{
|
|
||||||
InitializeGUCOptionsFromEnvironment();
|
|
||||||
pg_timezone_abbrev_initialize();
|
|
||||||
/* this selects SQL_ASCII in processes not connected to a database */
|
|
||||||
SetConfigOption("client_encoding", GetDatabaseEncodingName(),
|
|
||||||
PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now apply the values from the config file.
|
|
||||||
*/
|
|
||||||
for (item = head; item; item = item->next)
|
|
||||||
{
|
|
||||||
char *pre_value = NULL;
|
|
||||||
int scres;
|
|
||||||
|
|
||||||
/* Ignore anything marked as ignorable */
|
|
||||||
if (item->ignore)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* In SIGHUP cases in the postmaster, we want to report changes */
|
|
||||||
if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
|
|
||||||
{
|
|
||||||
const char *preval = GetConfigOption(item->name, true, false);
|
|
||||||
|
|
||||||
/* If option doesn't exist yet or is NULL, treat as empty string */
|
|
||||||
if (!preval)
|
|
||||||
preval = "";
|
|
||||||
/* must dup, else might have dangling pointer below */
|
|
||||||
pre_value = pstrdup(preval);
|
|
||||||
}
|
|
||||||
|
|
||||||
scres = set_config_option(item->name, item->value,
|
|
||||||
context, PGC_S_FILE,
|
|
||||||
GUC_ACTION_SET, applySettings, 0, false);
|
|
||||||
if (scres > 0)
|
|
||||||
{
|
|
||||||
/* variable was updated, so log the change if appropriate */
|
|
||||||
if (pre_value)
|
|
||||||
{
|
|
||||||
const char *post_value = GetConfigOption(item->name, true, false);
|
|
||||||
|
|
||||||
if (!post_value)
|
|
||||||
post_value = "";
|
|
||||||
if (strcmp(pre_value, post_value) != 0)
|
|
||||||
ereport(elevel,
|
|
||||||
(errmsg("parameter \"%s\" changed to \"%s\"",
|
|
||||||
item->name, item->value)));
|
|
||||||
}
|
|
||||||
item->applied = true;
|
|
||||||
}
|
|
||||||
else if (scres == 0)
|
|
||||||
{
|
|
||||||
error = true;
|
|
||||||
item->errmsg = pstrdup("setting could not be applied");
|
|
||||||
ConfFileWithError = item->filename;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* no error, but variable's active value was not changed */
|
|
||||||
item->applied = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We should update source location unless there was an error, since
|
|
||||||
* even if the active value didn't change, the reset value might have.
|
|
||||||
* (In the postmaster, there won't be a difference, but it does matter
|
|
||||||
* in backends.)
|
|
||||||
*/
|
|
||||||
if (scres != 0 && applySettings)
|
|
||||||
set_config_sourcefile(item->name, item->filename,
|
|
||||||
item->sourceline);
|
|
||||||
|
|
||||||
if (pre_value)
|
|
||||||
pfree(pre_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remember when we last successfully loaded the config file. */
|
|
||||||
if (applySettings)
|
|
||||||
PgReloadTime = GetCurrentTimestamp();
|
|
||||||
|
|
||||||
bail_out:
|
|
||||||
if (error && applySettings)
|
|
||||||
{
|
|
||||||
/* During postmaster startup, any error is fatal */
|
|
||||||
if (context == PGC_POSTMASTER)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
||||||
errmsg("configuration file \"%s\" contains errors",
|
|
||||||
ConfFileWithError)));
|
|
||||||
else if (applying)
|
|
||||||
ereport(elevel,
|
|
||||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
||||||
errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
|
|
||||||
ConfFileWithError)));
|
|
||||||
else
|
|
||||||
ereport(elevel,
|
|
||||||
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
|
||||||
errmsg("configuration file \"%s\" contains errors; no changes were applied",
|
|
||||||
ConfFileWithError)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Successful or otherwise, return the collected data list */
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a configuration file or directory location that may be a relative
|
* Given a configuration file or directory location that may be a relative
|
||||||
* path, return an absolute one. We consider the location to be relative to
|
* path, return an absolute one. We consider the location to be relative to
|
||||||
|
@ -660,7 +302,7 @@ cleanup:
|
||||||
* Capture an error message in the ConfigVariable list returned by
|
* Capture an error message in the ConfigVariable list returned by
|
||||||
* config file parsing.
|
* config file parsing.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
record_config_file_error(const char *errmsg,
|
record_config_file_error(const char *errmsg,
|
||||||
const char *config_file,
|
const char *config_file,
|
||||||
int lineno,
|
int lineno,
|
||||||
|
|
|
@ -243,10 +243,6 @@ static void assign_recovery_target_lsn(const char *newval, void *extra);
|
||||||
static bool check_primary_slot_name(char **newval, void **extra, GucSource source);
|
static bool check_primary_slot_name(char **newval, void **extra, GucSource source);
|
||||||
static bool check_default_with_oids(bool *newval, void **extra, GucSource source);
|
static bool check_default_with_oids(bool *newval, void **extra, GucSource source);
|
||||||
|
|
||||||
/* Private functions in guc-file.l that need to be called from guc.c */
|
|
||||||
static ConfigVariable *ProcessConfigFileInternal(GucContext context,
|
|
||||||
bool applySettings, int elevel);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Track whether there were any deferred checks for custom resource managers
|
* Track whether there were any deferred checks for custom resource managers
|
||||||
* specified in wal_consistency_checking.
|
* specified in wal_consistency_checking.
|
||||||
|
@ -5160,8 +5156,8 @@ static bool report_needed; /* true if any GUC_REPORT reports are needed */
|
||||||
static int GUCNestLevel = 0; /* 1 when in main transaction */
|
static int GUCNestLevel = 0; /* 1 when in main transaction */
|
||||||
|
|
||||||
|
|
||||||
|
static struct config_generic *find_option(const char *name, bool create_placeholders, bool skip_errors, int elevel);
|
||||||
static int guc_var_compare(const void *a, const void *b);
|
static int guc_var_compare(const void *a, const void *b);
|
||||||
static int guc_name_compare(const char *namea, const char *nameb);
|
|
||||||
static void InitializeGUCOptionsFromEnvironment(void);
|
static void InitializeGUCOptionsFromEnvironment(void);
|
||||||
static void InitializeOneGUCOption(struct config_generic *gconf);
|
static void InitializeOneGUCOption(struct config_generic *gconf);
|
||||||
static void push_old_value(struct config_generic *gconf, GucAction action);
|
static void push_old_value(struct config_generic *gconf, GucAction action);
|
||||||
|
@ -5180,7 +5176,359 @@ static bool validate_option_array_item(const char *name, const char *value,
|
||||||
static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head_p);
|
static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head_p);
|
||||||
static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
|
static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
|
||||||
const char *name, const char *value);
|
const char *name, const char *value);
|
||||||
|
static bool valid_custom_variable_name(const char *name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function handles both actual config file (re)loads and execution of
|
||||||
|
* show_all_file_settings() (i.e., the pg_file_settings view). In the latter
|
||||||
|
* case we don't apply any of the settings, but we make all the usual validity
|
||||||
|
* checks, and we return the ConfigVariable list so that it can be printed out
|
||||||
|
* by show_all_file_settings().
|
||||||
|
*/
|
||||||
|
ConfigVariable *
|
||||||
|
ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
|
||||||
|
{
|
||||||
|
bool error = false;
|
||||||
|
bool applying = false;
|
||||||
|
const char *ConfFileWithError;
|
||||||
|
ConfigVariable *item,
|
||||||
|
*head,
|
||||||
|
*tail;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Parse the main config file into a list of option names and values */
|
||||||
|
ConfFileWithError = ConfigFileName;
|
||||||
|
head = tail = NULL;
|
||||||
|
|
||||||
|
if (!ParseConfigFile(ConfigFileName, true,
|
||||||
|
NULL, 0, 0, elevel,
|
||||||
|
&head, &tail))
|
||||||
|
{
|
||||||
|
/* Syntax error(s) detected in the file, so bail out */
|
||||||
|
error = true;
|
||||||
|
goto bail_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse the PG_AUTOCONF_FILENAME file, if present, after the main file to
|
||||||
|
* replace any parameters set by ALTER SYSTEM command. Because this file
|
||||||
|
* is in the data directory, we can't read it until the DataDir has been
|
||||||
|
* set.
|
||||||
|
*/
|
||||||
|
if (DataDir)
|
||||||
|
{
|
||||||
|
if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
|
||||||
|
NULL, 0, 0, elevel,
|
||||||
|
&head, &tail))
|
||||||
|
{
|
||||||
|
/* Syntax error(s) detected in the file, so bail out */
|
||||||
|
error = true;
|
||||||
|
ConfFileWithError = PG_AUTOCONF_FILENAME;
|
||||||
|
goto bail_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If DataDir is not set, the PG_AUTOCONF_FILENAME file cannot be
|
||||||
|
* read. In this case, we don't want to accept any settings but
|
||||||
|
* data_directory from postgresql.conf, because they might be
|
||||||
|
* overwritten with settings in the PG_AUTOCONF_FILENAME file which
|
||||||
|
* will be read later. OTOH, since data_directory isn't allowed in the
|
||||||
|
* PG_AUTOCONF_FILENAME file, it will never be overwritten later.
|
||||||
|
*/
|
||||||
|
ConfigVariable *newlist = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prune all items except the last "data_directory" from the list.
|
||||||
|
*/
|
||||||
|
for (item = head; item; item = item->next)
|
||||||
|
{
|
||||||
|
if (!item->ignore &&
|
||||||
|
strcmp(item->name, "data_directory") == 0)
|
||||||
|
newlist = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newlist)
|
||||||
|
newlist->next = NULL;
|
||||||
|
head = tail = newlist;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quick exit if data_directory is not present in file.
|
||||||
|
*
|
||||||
|
* We need not do any further processing, in particular we don't set
|
||||||
|
* PgReloadTime; that will be set soon by subsequent full loading of
|
||||||
|
* the config file.
|
||||||
|
*/
|
||||||
|
if (head == NULL)
|
||||||
|
goto bail_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark all extant GUC variables as not present in the config file. We
|
||||||
|
* need this so that we can tell below which ones have been removed from
|
||||||
|
* the file since we last processed it.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < num_guc_variables; i++)
|
||||||
|
{
|
||||||
|
struct config_generic *gconf = guc_variables[i];
|
||||||
|
|
||||||
|
gconf->status &= ~GUC_IS_IN_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if all the supplied option names are valid, as an additional
|
||||||
|
* quasi-syntactic check on the validity of the config file. It is
|
||||||
|
* important that the postmaster and all backends agree on the results of
|
||||||
|
* this phase, else we will have strange inconsistencies about which
|
||||||
|
* processes accept a config file update and which don't. Hence, unknown
|
||||||
|
* custom variable names have to be accepted without complaint. For the
|
||||||
|
* same reason, we don't attempt to validate the options' values here.
|
||||||
|
*
|
||||||
|
* In addition, the GUC_IS_IN_FILE flag is set on each existing GUC
|
||||||
|
* variable mentioned in the file; and we detect duplicate entries in the
|
||||||
|
* file and mark the earlier occurrences as ignorable.
|
||||||
|
*/
|
||||||
|
for (item = head; item; item = item->next)
|
||||||
|
{
|
||||||
|
struct config_generic *record;
|
||||||
|
|
||||||
|
/* Ignore anything already marked as ignorable */
|
||||||
|
if (item->ignore)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to find the variable; but do not create a custom placeholder if
|
||||||
|
* it's not there already.
|
||||||
|
*/
|
||||||
|
record = find_option(item->name, false, true, elevel);
|
||||||
|
|
||||||
|
if (record)
|
||||||
|
{
|
||||||
|
/* If it's already marked, then this is a duplicate entry */
|
||||||
|
if (record->status & GUC_IS_IN_FILE)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Mark the earlier occurrence(s) as dead/ignorable. We could
|
||||||
|
* avoid the O(N^2) behavior here with some additional state,
|
||||||
|
* but it seems unlikely to be worth the trouble.
|
||||||
|
*/
|
||||||
|
ConfigVariable *pitem;
|
||||||
|
|
||||||
|
for (pitem = head; pitem != item; pitem = pitem->next)
|
||||||
|
{
|
||||||
|
if (!pitem->ignore &&
|
||||||
|
strcmp(pitem->name, item->name) == 0)
|
||||||
|
pitem->ignore = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Now mark it as present in file */
|
||||||
|
record->status |= GUC_IS_IN_FILE;
|
||||||
|
}
|
||||||
|
else if (!valid_custom_variable_name(item->name))
|
||||||
|
{
|
||||||
|
/* Invalid non-custom variable, so complain */
|
||||||
|
ereport(elevel,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %d",
|
||||||
|
item->name,
|
||||||
|
item->filename, item->sourceline)));
|
||||||
|
item->errmsg = pstrdup("unrecognized configuration parameter");
|
||||||
|
error = true;
|
||||||
|
ConfFileWithError = item->filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we've detected any errors so far, we don't want to risk applying any
|
||||||
|
* changes.
|
||||||
|
*/
|
||||||
|
if (error)
|
||||||
|
goto bail_out;
|
||||||
|
|
||||||
|
/* Otherwise, set flag that we're beginning to apply changes */
|
||||||
|
applying = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for variables having been removed from the config file, and
|
||||||
|
* revert their reset values (and perhaps also effective values) to the
|
||||||
|
* boot-time defaults. If such a variable can't be changed after startup,
|
||||||
|
* report that and continue.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < num_guc_variables; i++)
|
||||||
|
{
|
||||||
|
struct config_generic *gconf = guc_variables[i];
|
||||||
|
GucStack *stack;
|
||||||
|
|
||||||
|
if (gconf->reset_source != PGC_S_FILE ||
|
||||||
|
(gconf->status & GUC_IS_IN_FILE))
|
||||||
|
continue;
|
||||||
|
if (gconf->context < PGC_SIGHUP)
|
||||||
|
{
|
||||||
|
/* The removal can't be effective without a restart */
|
||||||
|
gconf->status |= GUC_PENDING_RESTART;
|
||||||
|
ereport(elevel,
|
||||||
|
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
|
||||||
|
errmsg("parameter \"%s\" cannot be changed without restarting the server",
|
||||||
|
gconf->name)));
|
||||||
|
record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
|
||||||
|
gconf->name),
|
||||||
|
NULL, 0,
|
||||||
|
&head, &tail);
|
||||||
|
error = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No more to do if we're just doing show_all_file_settings() */
|
||||||
|
if (!applySettings)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset any "file" sources to "default", else set_config_option will
|
||||||
|
* not override those settings.
|
||||||
|
*/
|
||||||
|
if (gconf->reset_source == PGC_S_FILE)
|
||||||
|
gconf->reset_source = PGC_S_DEFAULT;
|
||||||
|
if (gconf->source == PGC_S_FILE)
|
||||||
|
gconf->source = PGC_S_DEFAULT;
|
||||||
|
for (stack = gconf->stack; stack; stack = stack->prev)
|
||||||
|
{
|
||||||
|
if (stack->source == PGC_S_FILE)
|
||||||
|
stack->source = PGC_S_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we can re-apply the wired-in default (i.e., the boot_val) */
|
||||||
|
if (set_config_option(gconf->name, NULL,
|
||||||
|
context, PGC_S_DEFAULT,
|
||||||
|
GUC_ACTION_SET, true, 0, false) > 0)
|
||||||
|
{
|
||||||
|
/* Log the change if appropriate */
|
||||||
|
if (context == PGC_SIGHUP)
|
||||||
|
ereport(elevel,
|
||||||
|
(errmsg("parameter \"%s\" removed from configuration file, reset to default",
|
||||||
|
gconf->name)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore any variables determined by environment variables or
|
||||||
|
* dynamically-computed defaults. This is a no-op except in the case
|
||||||
|
* where one of these had been in the config file and is now removed.
|
||||||
|
*
|
||||||
|
* In particular, we *must not* do this during the postmaster's initial
|
||||||
|
* loading of the file, since the timezone functions in particular should
|
||||||
|
* be run only after initialization is complete.
|
||||||
|
*
|
||||||
|
* XXX this is an unmaintainable crock, because we have to know how to set
|
||||||
|
* (or at least what to call to set) every non-PGC_INTERNAL variable that
|
||||||
|
* could potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
|
||||||
|
*/
|
||||||
|
if (context == PGC_SIGHUP && applySettings)
|
||||||
|
{
|
||||||
|
InitializeGUCOptionsFromEnvironment();
|
||||||
|
pg_timezone_abbrev_initialize();
|
||||||
|
/* this selects SQL_ASCII in processes not connected to a database */
|
||||||
|
SetConfigOption("client_encoding", GetDatabaseEncodingName(),
|
||||||
|
PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now apply the values from the config file.
|
||||||
|
*/
|
||||||
|
for (item = head; item; item = item->next)
|
||||||
|
{
|
||||||
|
char *pre_value = NULL;
|
||||||
|
int scres;
|
||||||
|
|
||||||
|
/* Ignore anything marked as ignorable */
|
||||||
|
if (item->ignore)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* In SIGHUP cases in the postmaster, we want to report changes */
|
||||||
|
if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
|
||||||
|
{
|
||||||
|
const char *preval = GetConfigOption(item->name, true, false);
|
||||||
|
|
||||||
|
/* If option doesn't exist yet or is NULL, treat as empty string */
|
||||||
|
if (!preval)
|
||||||
|
preval = "";
|
||||||
|
/* must dup, else might have dangling pointer below */
|
||||||
|
pre_value = pstrdup(preval);
|
||||||
|
}
|
||||||
|
|
||||||
|
scres = set_config_option(item->name, item->value,
|
||||||
|
context, PGC_S_FILE,
|
||||||
|
GUC_ACTION_SET, applySettings, 0, false);
|
||||||
|
if (scres > 0)
|
||||||
|
{
|
||||||
|
/* variable was updated, so log the change if appropriate */
|
||||||
|
if (pre_value)
|
||||||
|
{
|
||||||
|
const char *post_value = GetConfigOption(item->name, true, false);
|
||||||
|
|
||||||
|
if (!post_value)
|
||||||
|
post_value = "";
|
||||||
|
if (strcmp(pre_value, post_value) != 0)
|
||||||
|
ereport(elevel,
|
||||||
|
(errmsg("parameter \"%s\" changed to \"%s\"",
|
||||||
|
item->name, item->value)));
|
||||||
|
}
|
||||||
|
item->applied = true;
|
||||||
|
}
|
||||||
|
else if (scres == 0)
|
||||||
|
{
|
||||||
|
error = true;
|
||||||
|
item->errmsg = pstrdup("setting could not be applied");
|
||||||
|
ConfFileWithError = item->filename;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* no error, but variable's active value was not changed */
|
||||||
|
item->applied = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We should update source location unless there was an error, since
|
||||||
|
* even if the active value didn't change, the reset value might have.
|
||||||
|
* (In the postmaster, there won't be a difference, but it does matter
|
||||||
|
* in backends.)
|
||||||
|
*/
|
||||||
|
if (scres != 0 && applySettings)
|
||||||
|
set_config_sourcefile(item->name, item->filename,
|
||||||
|
item->sourceline);
|
||||||
|
|
||||||
|
if (pre_value)
|
||||||
|
pfree(pre_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remember when we last successfully loaded the config file. */
|
||||||
|
if (applySettings)
|
||||||
|
PgReloadTime = GetCurrentTimestamp();
|
||||||
|
|
||||||
|
bail_out:
|
||||||
|
if (error && applySettings)
|
||||||
|
{
|
||||||
|
/* During postmaster startup, any error is fatal */
|
||||||
|
if (context == PGC_POSTMASTER)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||||
|
errmsg("configuration file \"%s\" contains errors",
|
||||||
|
ConfFileWithError)));
|
||||||
|
else if (applying)
|
||||||
|
ereport(elevel,
|
||||||
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||||
|
errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
|
||||||
|
ConfFileWithError)));
|
||||||
|
else
|
||||||
|
ereport(elevel,
|
||||||
|
(errcode(ERRCODE_CONFIG_FILE_ERROR),
|
||||||
|
errmsg("configuration file \"%s\" contains errors; no changes were applied",
|
||||||
|
ConfFileWithError)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Successful or otherwise, return the collected data list */
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some infrastructure for checking malloc/strdup/realloc calls
|
* Some infrastructure for checking malloc/strdup/realloc calls
|
||||||
|
@ -5737,7 +6085,7 @@ guc_var_compare(const void *a, const void *b)
|
||||||
/*
|
/*
|
||||||
* the bare comparison function for GUC names
|
* the bare comparison function for GUC names
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
guc_name_compare(const char *namea, const char *nameb)
|
guc_name_compare(const char *namea, const char *nameb)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -442,6 +442,15 @@ extern void GUC_check_errcode(int sqlerrcode);
|
||||||
pre_format_elog_string(errno, TEXTDOMAIN), \
|
pre_format_elog_string(errno, TEXTDOMAIN), \
|
||||||
GUC_check_errhint_string = format_elog_string
|
GUC_check_errhint_string = format_elog_string
|
||||||
|
|
||||||
|
/* functions shared between guc.c and guc-file.l */
|
||||||
|
extern int guc_name_compare(const char *namea, const char *nameb);
|
||||||
|
extern ConfigVariable *ProcessConfigFileInternal(GucContext context,
|
||||||
|
bool applySettings, int elevel);
|
||||||
|
extern void record_config_file_error(const char *errmsg,
|
||||||
|
const char *config_file,
|
||||||
|
int lineno,
|
||||||
|
ConfigVariable **head_p,
|
||||||
|
ConfigVariable **tail_p);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following functions are not in guc.c, but are declared here to avoid
|
* The following functions are not in guc.c, but are declared here to avoid
|
||||||
|
|
Loading…
Reference in New Issue