diff options
Diffstat (limited to 'src/backend/utils/misc/guc-file.l')
-rw-r--r-- | src/backend/utils/misc/guc-file.l | 313 |
1 files changed, 171 insertions, 142 deletions
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l index 4327601320e..124dfbf62e1 100644 --- a/src/backend/utils/misc/guc-file.l +++ b/src/backend/utils/misc/guc-file.l @@ -4,7 +4,7 @@ * * Copyright (c) 2000-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.50 2007/04/21 20:02:40 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc-file.l,v 1.51 2007/09/10 00:57:21 tgl Exp $ */ %{ @@ -116,9 +116,10 @@ ProcessConfigFile(GucContext context) { int elevel; struct name_value_pair *item, *head, *tail; + char *cvc = NULL; + struct config_string *cvc_struct; + const char *envvar; int i; - bool *in_conffile = NULL; - const char *var; Assert(context == PGC_POSTMASTER || context == PGC_SIGHUP); @@ -133,6 +134,7 @@ ProcessConfigFile(GucContext context) else elevel = ERROR; + /* Parse the file into a list of option names and values */ head = tail = NULL; if (!ParseConfigFile(ConfigFileName, NULL, @@ -140,162 +142,178 @@ ProcessConfigFile(GucContext context) &head, &tail)) goto cleanup_list; - /* Check if all options are valid */ - for (item = head; item; item = item->next) + /* + * We need the proposed new value of custom_variable_classes to check + * custom variables with. ParseConfigFile ensured that if it's in + * the file, it's first in the list. But first check to see if we + * have an active value from the command line, which should override + * the file in any case. (Since there's no relevant env var, the + * only possible nondefault sources are the file and ARGV.) + */ + cvc_struct = (struct config_string *) + find_option("custom_variable_classes", false, elevel); + if (cvc_struct && cvc_struct->gen.reset_source > PGC_S_FILE) { - char *sep = strchr(item->name, GUC_QUALIFIER_SEPARATOR); - if (sep && !is_custom_class(item->name, sep - item->name)) + cvc = guc_strdup(elevel, cvc_struct->reset_val); + if (cvc == NULL) + goto cleanup_list; + } + else if (head != NULL && + guc_name_compare(head->name, "custom_variable_classes") == 0) + { + /* + * Need to canonicalize the value via the assign hook. Casting away + * const is a bit ugly, but we know the result is malloc'd. + */ + cvc = (char *) assign_custom_variable_classes(head->value, + false, PGC_S_FILE); + if (cvc == NULL) { ereport(elevel, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("unrecognized configuration parameter \"%s\"", - item->name))); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid value for parameter \"%s\": \"%s\"", + head->name, head->value))); goto cleanup_list; } - - if (!set_config_option(item->name, item->value, context, - PGC_S_FILE, false, false)) - goto cleanup_list; } - /* - * Mark all variables as not showing up in the config file. The - * allocation has to take place after ParseConfigFile() since this - * function can change num_guc_variables due to custom variables. - * It would be easier to add a new field or status bit to struct - * conf_generic, but that way we would expose internal information - * that is just needed here in the following few lines. The price - * to pay for this separation are a few more loops over the set of - * configuration options, but those are expected to be rather few - * and we only have to pay the cost at SIGHUP. We initialize - * in_conffile only here because set_config_option() makes - * guc_variables grow with custom variables. + * 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. */ - in_conffile = guc_malloc(elevel, num_guc_variables * sizeof(bool)); - if (!in_conffile) - goto cleanup_list; for (i = 0; i < num_guc_variables; i++) - in_conffile[i] = false; + { + struct config_generic *gconf = guc_variables[i]; + + gconf->status &= ~GUC_IS_IN_FILE; + } + /* + * Check if all options are valid. As a side-effect, the GUC_IS_IN_FILE + * flag is set on each GUC variable mentioned in the list. + */ for (item = head; item; item = item->next) { - /* - * After set_config_option() the variable name item->name is - * known to exist. - */ - Assert(guc_get_index(item->name) >= 0); - in_conffile[guc_get_index(item->name)] = true; + char *sep = strchr(item->name, GUC_QUALIFIER_SEPARATOR); + + if (sep) + { + /* + * We have to consider three cases for custom variables: + * + * 1. The class name is not valid according to the (new) setting + * of custom_variable_classes. If so, reject. We don't care + * which side is at fault. + */ + if (!is_custom_class(item->name, sep - item->name, cvc)) + { + ereport(elevel, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("unrecognized configuration parameter \"%s\"", + item->name))); + goto cleanup_list; + } + /* + * 2. There is no GUC entry. If we called set_config_option then + * it would make a placeholder, which we don't want to do yet, + * since we could still fail further down the list. Do nothing + * (assuming that making the placeholder will succeed later). + */ + if (find_option(item->name, false, elevel) == NULL) + continue; + /* + * 3. There is already a GUC entry (either real or placeholder) for + * the variable. In this case we should let set_config_option + * check it, since the assignment could well fail if it's a real + * entry. + */ + } + + if (!set_config_option(item->name, item->value, context, + PGC_S_FILE, false, false)) + goto cleanup_list; } + /* + * 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, + * just throw a warning and continue. (This is analogous to the fact that + * set_config_option only throws a warning for a new but different value. + * If we wanted to make it a hard error, we'd need an extra pass over the + * list so that we could throw the error before starting to apply + * changes.) + */ for (i = 0; i < num_guc_variables; i++) { struct config_generic *gconf = guc_variables[i]; - if (!in_conffile[i] && gconf->source == PGC_S_FILE) + GucStack *stack; + + if (gconf->reset_source != PGC_S_FILE || + (gconf->status & GUC_IS_IN_FILE)) + continue; + if (gconf->context < PGC_SIGHUP) { - if (gconf->context < PGC_SIGHUP) - ereport(elevel, + ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), - errmsg("parameter \"%s\" cannot be changed after server start; configuration file change ignored", - gconf->name))); - else - { - /* prepare */ - GucStack *stack; - if (gconf->reset_source == PGC_S_FILE) - gconf->reset_source = PGC_S_DEFAULT; - for (stack = gconf->stack; stack; stack = stack->prev) - if (stack->source == PGC_S_FILE) - stack->source = PGC_S_DEFAULT; - /* apply the default */ - set_config_option(gconf->name, NULL, context, - PGC_S_DEFAULT, false, true); - } + errmsg("parameter \"%s\" cannot be changed after server start; configuration file change ignored", + gconf->name))); + continue; } - else if (!in_conffile[i] && gconf->reset_source == PGC_S_FILE) - { - /*------ - * Change the reset_val to default_val. Here's an - * example: In the configuration file we have - * - * seq_page_cost = 3.00 - * - * Now we execute in a session - * - * SET seq_page_cost TO 4.00; - * - * Then we remove this option from the configuration file - * and send SIGHUP. Now when you execute - * - * RESET seq_page_cost; - * - * it should fall back to 1.00 (the default value for - * seq_page_cost) and not to 3.00 (which is the current - * reset_val). - */ - switch (gconf->vartype) - { - case PGC_BOOL: - { - struct config_bool *conf; - conf = (struct config_bool *) gconf; - conf->reset_val = conf->boot_val; - break; - } - case PGC_INT: - { - struct config_int *conf; - conf = (struct config_int *) gconf; - conf->reset_val = conf->boot_val; - break; - } - case PGC_REAL: - { - struct config_real *conf; - conf = (struct config_real *) gconf; - conf->reset_val = conf->boot_val; - break; - } - case PGC_STRING: - { - struct config_string *conf; - conf = (struct config_string *) gconf; - /* - * We can cast away the const here because we - * won't free the address. It is protected by - * set_string_field() and string_field_used(). - */ - conf->reset_val = (char *) conf->boot_val; - break; - } - } + /* + * Reset any "file" sources to "default", else set_config_option + * will not override those settings. tentative_source should + * never be "file". + */ + if (gconf->reset_source == PGC_S_FILE) + gconf->reset_source = PGC_S_DEFAULT; + Assert(gconf->tentative_source != PGC_S_FILE); + if (gconf->source == PGC_S_FILE) + gconf->source = PGC_S_DEFAULT; + for (stack = gconf->stack; stack; stack = stack->prev) + { + Assert(stack->tentative_source != PGC_S_FILE); + if (stack->source == PGC_S_FILE) + stack->source = PGC_S_DEFAULT; } - } - /* If we got here all the options checked out okay, so apply them. */ - for (item = head; item; item = item->next) - set_config_option(item->name, item->value, context, - PGC_S_FILE, false, true); + /* Now we can re-apply the wired-in default */ + set_config_option(gconf->name, NULL, context, PGC_S_DEFAULT, + false, true); + } /* - * Reset variables to the value of environment variables - * (PGC_S_ENV_VAR overrules PGC_S_FILE). PGPORT is ignored, - * because it cannot be changed without restart. + * Restore any variables determined by environment variables. This + * is a no-op except in the case where one of these had been in the + * config file and is now removed. PGC_S_ENV_VAR will override the + * wired-in default we just applied, but cannot override any other source. + * PGPORT can be ignored, because it cannot be changed without restart. + * Keep this list in sync with InitializeGUCOptions()! */ - var = getenv("PGDATESTYLE"); - if (var != NULL) - set_config_option("datestyle", var, context, + envvar = getenv("PGDATESTYLE"); + if (envvar != NULL) + set_config_option("datestyle", envvar, PGC_POSTMASTER, PGC_S_ENV_VAR, false, true); - var = getenv("PGCLIENTENCODING"); - if (var != NULL) - set_config_option("client_encoding", var, context, + envvar = getenv("PGCLIENTENCODING"); + if (envvar != NULL) + set_config_option("client_encoding", envvar, PGC_POSTMASTER, PGC_S_ENV_VAR, false, true); + + /* If we got here all the options checked out okay, so apply them. */ + for (item = head; item; item = item->next) + { + set_config_option(item->name, item->value, context, + PGC_S_FILE, false, true); + } + cleanup_list: - free(in_conffile); free_name_value_list(head); + if (cvc) + free(cvc); } @@ -389,6 +407,7 @@ ParseConfigFile(const char *config_file, const char *calling_file, while ((token = yylex())) { char *opt_name, *opt_value; + struct name_value_pair *item; if (token == GUC_EOL) /* empty or comment line */ continue; @@ -421,7 +440,7 @@ ParseConfigFile(const char *config_file, const char *calling_file, goto parse_error; /* OK, process the option name and value */ - if (pg_strcasecmp(opt_name, "include") == 0) + if (guc_name_compare(opt_name, "include") == 0) { /* * An include directive isn't a variable and should be processed @@ -443,29 +462,39 @@ ParseConfigFile(const char *config_file, const char *calling_file, pfree(opt_name); pfree(opt_value); } - else if (pg_strcasecmp(opt_name, "custom_variable_classes") == 0) + else if (guc_name_compare(opt_name, "custom_variable_classes") == 0) { /* * This variable must be processed first as it controls - * the validity of other variables; so apply immediately. + * the validity of other variables; so it goes at the head + * of the result list. If we already found a value for it, + * replace with this one. */ - if (!set_config_option(opt_name, opt_value, context, - PGC_S_FILE, false, true)) + item = *head_p; + if (item != NULL && + guc_name_compare(item->name, "custom_variable_classes") == 0) { - pfree(opt_name); - pfree(opt_value); - - /* We assume the error message was logged already. */ - OK = false; - goto cleanup_exit; + /* replace existing head item */ + pfree(item->name); + pfree(item->value); + item->name = opt_name; + item->value = opt_value; + } + else + { + /* prepend to list */ + item = palloc(sizeof *item); + item->name = opt_name; + item->value = opt_value; + item->next = *head_p; + *head_p = item; + if (*tail_p == NULL) + *tail_p = item; } } - - if (pg_strcasecmp(opt_name, "include") != 0) + else { - /* append to list */ - struct name_value_pair *item; - + /* ordinary variable, append to list */ item = palloc(sizeof *item); item->name = opt_name; item->value = opt_value; |