X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Futils%2Fmisc%2Fguc.c;h=49958cb4e7f3ab74c62a82ecf89b6fb80294fe11;hb=23c356ccecaf10665777c05fac414466b7d5793d;hp=a05b3be3937c2f1e67cc78403520beb15d1faa38;hpb=fd801f4faa8e0f00bc314b16549e3d8e8aa1b653;p=postgresql diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a05b3be393..49958cb4e7 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -6,11 +6,11 @@ * See src/backend/utils/misc/README for more information. * * - * Copyright (c) 2000-2007, PostgreSQL Global Development Group + * Copyright (c) 2000-2008, PostgreSQL Global Development Group * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.413 2007/08/19 01:41:25 adunstan Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.436 2008/03/10 12:39:23 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -25,7 +25,6 @@ #include #endif - #include "access/gin.h" #include "access/transam.h" #include "access/twophase.h" @@ -58,6 +57,7 @@ #include "storage/fd.h" #include "storage/freespace.h" #include "tcop/tcopprot.h" +#include "tsearch/ts_cache.h" #include "utils/builtins.h" #include "utils/guc_tables.h" #include "utils/memutils.h" @@ -110,6 +110,7 @@ extern int CommitDelay; extern int CommitSiblings; extern char *default_tablespace; extern char *temp_tablespaces; +extern bool synchronize_seqscans; extern bool fullPageWrites; #ifdef TRACE_SORT @@ -142,7 +143,7 @@ static const char *assign_syslog_ident(const char *ident, static const char *assign_defaultxactisolevel(const char *newval, bool doit, GucSource source); static const char *assign_session_replication_role(const char *newval, bool doit, - GucSource source); + GucSource source); static const char *assign_log_min_messages(const char *newval, bool doit, GucSource source); static const char *assign_client_min_messages(const char *newval, @@ -169,7 +170,7 @@ static const char *assign_backslash_quote(const char *newval, bool doit, GucSour static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source); static const char *assign_xmlbinary(const char *newval, bool doit, GucSource source); static const char *assign_xmloption(const char *newval, bool doit, GucSource source); - +static const char *show_archive_command(void); static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source); static bool assign_tcp_keepalives_interval(int newval, bool doit, GucSource source); static bool assign_tcp_keepalives_count(int newval, bool doit, GucSource source); @@ -208,7 +209,7 @@ bool SQL_inheritance = true; bool Password_encryption = true; int log_min_error_statement = ERROR; -int log_min_messages = NOTICE; +int log_min_messages = WARNING; int client_min_messages = NOTICE; int log_min_duration_statement = -1; int log_temp_files = -1; @@ -422,9 +423,9 @@ const char *const config_type_names[] = * 4. Add a record below. * * 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if - * appropriate + * appropriate. * - * 6. Don't forget to document the option. + * 6. Don't forget to document the option (at least in config.sgml). * * 7. If it's a new GUC_LIST option you must edit pg_dumpall.c to ensure * it is not single quoted at dump time. @@ -560,8 +561,7 @@ static struct config_bool ConfigureNamesBool[] = NULL }, &XactSyncCommit, - /* XXX TEMPORARY FOR TESTING: sync commit should default to TRUE! */ - false, NULL, NULL + true, NULL, NULL }, { {"zero_damaged_pages", PGC_SUSET, DEVELOPER_OPTIONS, @@ -737,47 +737,23 @@ static struct config_bool ConfigureNamesBool[] = &Explain_pretty_print, true, NULL, NULL }, + { - {"stats_start_collector", PGC_POSTMASTER, STATS_COLLECTOR, - gettext_noop("Starts the server statistics-collection subprocess."), - NULL - }, - &pgstat_collect_startcollector, - true, NULL, NULL - }, - { - {"stats_reset_on_server_start", PGC_POSTMASTER, STATS_COLLECTOR, - gettext_noop("Zeroes collected statistics on server restart."), - NULL - }, - &pgstat_collect_resetonpmstart, - false, NULL, NULL - }, - { - {"stats_row_level", PGC_SUSET, STATS_COLLECTOR, - gettext_noop("Collects row-level statistics on database activity."), - NULL + {"track_activities", PGC_SUSET, STATS_COLLECTOR, + gettext_noop("Collects information about executing commands."), + gettext_noop("Enables the collection of information on the currently " + "executing command of each session, along with " + "the time at which that command began execution.") }, - &pgstat_collect_tuplelevel, + &pgstat_track_activities, true, NULL, NULL }, { - {"stats_block_level", PGC_SUSET, STATS_COLLECTOR, - gettext_noop("Collects block-level statistics on database activity."), + {"track_counts", PGC_SUSET, STATS_COLLECTOR, + gettext_noop("Collects statistics on database activity."), NULL }, - &pgstat_collect_blocklevel, - false, NULL, NULL - }, - - { - {"stats_command_string", PGC_SUSET, STATS_COLLECTOR, - gettext_noop("Collects information about executing commands."), - gettext_noop("Enables the collection of information on the currently " - "executing command of each session, along with the time " - "at which that command began execution.") - }, - &pgstat_collect_querystring, + &pgstat_track_counts, true, NULL, NULL }, @@ -1077,6 +1053,24 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, + { + {"synchronize_seqscans", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, + gettext_noop("Enable synchronized sequential scans."), + NULL + }, + &synchronize_seqscans, + true, NULL, NULL + }, + + { + {"archive_mode", PGC_POSTMASTER, WAL_SETTINGS, + gettext_noop("Allows archiving of WAL files using archive_command."), + NULL + }, + &XLogArchiveMode, + false, NULL, NULL + }, + { {"allow_system_table_mods", PGC_POSTMASTER, DEVELOPER_OPTIONS, gettext_noop("Allows modifications of the structure of system tables."), @@ -1197,20 +1191,14 @@ static struct config_int ConfigureNamesInt[] = GUC_UNIT_MS }, &DeadlockTimeout, - 1000, 1, INT_MAX/1000, NULL, NULL + 1000, 1, INT_MAX / 1000, NULL, NULL }, /* - * Note: There is some postprocessing done in PostmasterMain() to make - * sure the buffers are at least twice the number of backends, so the - * constraints here are partially unused. Similarly, the superuser - * reserved number is checked to ensure it is less than the max backends - * number. - * - * MaxBackends is limited to INT_MAX/4 because some places compute - * 4*MaxBackends without any overflow check. This check is made on - * assign_maxconnections, since MaxBackends is computed as MaxConnections + - * autovacuum_max_workers. + * Note: MaxBackends is limited to INT_MAX/4 because some places compute + * 4*MaxBackends without any overflow check. This check is made in + * assign_maxconnections, since MaxBackends is computed as MaxConnections + * plus autovacuum_max_workers. * * Likewise we have to limit NBuffers to INT_MAX/2. */ @@ -1563,9 +1551,9 @@ static struct config_int ConfigureNamesInt[] = { {"log_min_duration_statement", PGC_SUSET, LOGGING_WHEN, - gettext_noop("Sets the minimum execution time above which statements will " - "be logged."), - gettext_noop("Zero prints all queries. The default is -1 (turning this feature off)."), + gettext_noop("Sets the minimum execution time above which " + "statements will be logged."), + gettext_noop("Zero prints all queries. -1 turns this feature off."), GUC_UNIT_MS }, &log_min_duration_statement, @@ -1573,13 +1561,13 @@ static struct config_int ConfigureNamesInt[] = }, { - {"log_autovacuum", PGC_BACKEND, LOGGING_WHAT, - gettext_noop("Sets the minimum execution time above which autovacuum actions " - "will be logged."), - gettext_noop("Zero prints all actions. The default is -1 (turning this feature off)."), + {"log_autovacuum_min_duration", PGC_SIGHUP, LOGGING_WHAT, + gettext_noop("Sets the minimum execution time above which " + "autovacuum actions will be logged."), + gettext_noop("Zero prints all actions. -1 turns autovacuum logging off."), GUC_UNIT_MS }, - &Log_autovacuum, + &Log_autovacuum_min_duration, -1, -1, INT_MAX / 1000, NULL, NULL }, @@ -1599,7 +1587,7 @@ static struct config_int ConfigureNamesInt[] = NULL }, &bgwriter_lru_maxpages, - 5, 0, 1000, NULL, NULL + 100, 0, 1000, NULL, NULL }, { @@ -1846,12 +1834,12 @@ static struct config_real ConfigureNamesReal[] = }, { - {"bgwriter_lru_percent", PGC_SIGHUP, RESOURCES, - gettext_noop("Background writer percentage of LRU buffers to flush per round."), + {"bgwriter_lru_multiplier", PGC_SIGHUP, RESOURCES, + gettext_noop("Multiple of the average buffer usage to free per round."), NULL }, - &bgwriter_lru_percent, - 1.0, 0.0, 100.0, NULL, NULL + &bgwriter_lru_multiplier, + 2.0, 0.0, 10.0, NULL, NULL }, { @@ -1861,7 +1849,7 @@ static struct config_real ConfigureNamesReal[] = GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &phony_random_seed, - 0.5, 0.0, 1.0, assign_random_seed, show_random_seed + 0.0, -1.0, 1.0, assign_random_seed, show_random_seed }, { @@ -1905,7 +1893,7 @@ static struct config_string ConfigureNamesString[] = NULL }, &XLogArchiveCommand, - "", NULL, NULL + "", NULL, show_archive_command }, { @@ -1947,7 +1935,7 @@ static struct config_string ConfigureNamesString[] = "includes all the levels that follow it.") }, &log_min_messages_str, - "notice", assign_log_min_messages, NULL + "warning", assign_log_min_messages, NULL }, { @@ -2038,9 +2026,9 @@ static struct config_string ConfigureNamesString[] = { {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT, - gettext_noop("Sets the sessions behavior for triggers and rewrite rules."), + gettext_noop("Sets the session's behavior for triggers and rewrite rules."), gettext_noop("Each session can be either" - " \"origin\", \"replica\" or \"local\".") + " \"origin\", \"replica\", or \"local\".") }, &session_replication_role_string, "origin", assign_session_replication_role, NULL @@ -2059,6 +2047,16 @@ static struct config_string ConfigureNamesString[] = "$libdir", NULL, NULL }, + { + {"krb_realm", PGC_POSTMASTER, CONN_AUTH_SECURITY, + gettext_noop("Sets realm to match Kerberos and GSSAPI users against."), + NULL, + GUC_SUPERUSER_ONLY + }, + &pg_krb_realm, + NULL, NULL, NULL + }, + { {"krb_server_keyfile", PGC_POSTMASTER, CONN_AUTH_SECURITY, gettext_noop("Sets the location of the Kerberos server key file."), @@ -2240,8 +2238,9 @@ static struct config_string ConfigureNamesString[] = { {"log_destination", PGC_SIGHUP, LOGGING_WHERE, gettext_noop("Sets the destination for server log output."), - gettext_noop("Valid values are combinations of \"stderr\", \"syslog\", " - " \"csvlog\" and \"eventlog\", depending on the platform."), + gettext_noop("Valid values are combinations of \"stderr\", " + "\"syslog\", \"csvlog\", and \"eventlog\", " + "depending on the platform."), GUC_LIST_INPUT }, &log_destination_string, @@ -2434,6 +2433,15 @@ static struct config_string ConfigureNamesString[] = "content", assign_xmloption, NULL }, + { + {"default_text_search_config", PGC_USERSET, CLIENT_CONN_LOCALE, + gettext_noop("Sets default text search configuration."), + NULL + }, + &TSCurrentConfig, + "pg_catalog.simple", assignTSCurrentConfig, NULL + }, + #ifdef USE_SSL { {"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY, @@ -2444,7 +2452,7 @@ static struct config_string ConfigureNamesString[] = &SSLCipherSuites, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH", NULL, NULL }, -#endif /* USE_SSL */ +#endif /* USE_SSL */ /* End-of-list marker */ { @@ -2485,10 +2493,12 @@ static bool guc_dirty; /* TRUE if need to do commit/abort work */ 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 int guc_name_compare(const char *namea, const char *nameb); -static void push_old_value(struct config_generic * gconf); +static void push_old_value(struct config_generic * gconf, GucAction action); static void ReportGUCOption(struct config_generic * record); static void ShowGUCConfigOption(const char *name, DestReceiver *dest); static void ShowAllGUCConfig(DestReceiver *dest); @@ -2557,13 +2567,12 @@ set_string_field(struct config_string * conf, char **field, char *newval) if (oldval == NULL || oldval == *(conf->variable) || oldval == conf->reset_val || - oldval == conf->tentative_val || oldval == conf->boot_val) return; for (stack = conf->gen.stack; stack; stack = stack->prev) { - if (oldval == stack->tentative_val.stringval || - oldval == stack->value.stringval) + if (oldval == stack->prior.stringval || + oldval == stack->masked.stringval) return; } @@ -2581,19 +2590,71 @@ string_field_used(struct config_string * conf, char *strval) if (strval == *(conf->variable) || strval == conf->reset_val || - strval == conf->tentative_val || strval == conf->boot_val) return true; for (stack = conf->gen.stack; stack; stack = stack->prev) { - if (strval == stack->tentative_val.stringval || - strval == stack->value.stringval) + if (strval == stack->prior.stringval || + strval == stack->masked.stringval) return true; } return false; } +/* + * Support for copying a variable's active value into a stack entry + */ +static void +set_stack_value(struct config_generic * gconf, union config_var_value * val) +{ + switch (gconf->vartype) + { + case PGC_BOOL: + val->boolval = + *((struct config_bool *) gconf)->variable; + break; + case PGC_INT: + val->intval = + *((struct config_int *) gconf)->variable; + break; + case PGC_REAL: + val->realval = + *((struct config_real *) gconf)->variable; + break; + case PGC_STRING: + /* we assume stringval is NULL if not valid */ + set_string_field((struct config_string *) gconf, + &(val->stringval), + *((struct config_string *) gconf)->variable); + break; + } +} + +/* + * Support for discarding a no-longer-needed value in a stack entry + */ +static void +discard_stack_value(struct config_generic * gconf, union config_var_value * val) +{ + switch (gconf->vartype) + { + case PGC_BOOL: + case PGC_INT: + case PGC_REAL: + /* no need to do anything */ + break; + case PGC_STRING: + set_string_field((struct config_string *) gconf, + &(val->stringval), + NULL); + break; + } +} + +/* + * Fetch the sorted array pointer (exported for help_config.c's use ONLY) + */ struct config_generic ** get_guc_variables(void) { @@ -2678,40 +2739,6 @@ build_guc_variables(void) sizeof(struct config_generic *), guc_var_compare); } -static bool -is_custom_class(const char *name, int dotPos) -{ - /* - * assign_custom_variable_classes() has made sure no empty identifiers or - * whitespace exists in the variable - */ - bool result = false; - const char *ccs = GetConfigOption("custom_variable_classes"); - - if (ccs != NULL) - { - const char *start = ccs; - - for (;; ++ccs) - { - int c = *ccs; - - if (c == 0 || c == ',') - { - if (dotPos == ccs - start && strncmp(start, name, dotPos) == 0) - { - result = true; - break; - } - if (c == 0) - break; - start = ccs + 1; - } - } - } - return result; -} - /* * Add a new GUC variable to the list of known variables. The * list is expanded if needed. @@ -2755,7 +2782,7 @@ add_guc_variable(struct config_generic * var, int elevel) * Create and add a placeholder variable. It's presumed to belong * to a valid custom variable class at this point. */ -static struct config_string * +static struct config_generic * add_placeholder_variable(const char *name, int elevel) { size_t sz = sizeof(struct config_string) + sizeof(char *); @@ -2765,9 +2792,8 @@ add_placeholder_variable(const char *name, int elevel) var = (struct config_string *) guc_malloc(elevel, sz); if (var == NULL) return NULL; - - gen = &var->gen; memset(var, 0, sz); + gen = &var->gen; gen->name = guc_strdup(elevel, name); if (gen->name == NULL) @@ -2784,7 +2810,8 @@ add_placeholder_variable(const char *name, int elevel) /* * The char* is allocated at the end of the struct since we have no - * 'static' place to point to. + * 'static' place to point to. Note that the current value, as well as + * the boot and reset values, start out NULL. */ var->variable = (char **) (var + 1); @@ -2795,17 +2822,53 @@ add_placeholder_variable(const char *name, int elevel) return NULL; } - return var; + return gen; +} + +/* + * Detect whether the portion of "name" before dotPos matches any custom + * variable class name listed in custom_var_classes. The latter must be + * formatted the way that assign_custom_variable_classes does it, ie, + * no whitespace. NULL is valid for custom_var_classes. + */ +static bool +is_custom_class(const char *name, int dotPos, const char *custom_var_classes) +{ + bool result = false; + const char *ccs = custom_var_classes; + + if (ccs != NULL) + { + const char *start = ccs; + + for (;; ++ccs) + { + char c = *ccs; + + if (c == '\0' || c == ',') + { + if (dotPos == ccs - start && strncmp(start, name, dotPos) == 0) + { + result = true; + break; + } + if (c == '\0') + break; + start = ccs + 1; + } + } + } + return result; } /* - * Look up option NAME. If it exists, return a pointer to its record, - * else return NULL. + * Look up option NAME. If it exists, return a pointer to its record, + * else return NULL. If create_placeholders is TRUE, we'll create a + * placeholder record for a valid-looking custom variable name. */ static struct config_generic * -find_option(const char *name, int elevel) +find_option(const char *name, bool create_placeholders, int elevel) { - const char *dot; const char **key = &name; struct config_generic **res; int i; @@ -2832,17 +2895,21 @@ find_option(const char *name, int elevel) for (i = 0; map_old_guc_names[i] != NULL; i += 2) { if (guc_name_compare(name, map_old_guc_names[i]) == 0) - return find_option(map_old_guc_names[i + 1], elevel); + return find_option(map_old_guc_names[i + 1], false, elevel); } - /* - * Check if the name is qualified, and if so, check if the qualifier maps - * to a custom variable class. - */ - dot = strchr(name, GUC_QUALIFIER_SEPARATOR); - if (dot != NULL && is_custom_class(name, dot - name)) - /* Add a placeholder variable for this name */ - return (struct config_generic *) add_placeholder_variable(name, elevel); + if (create_placeholders) + { + /* + * Check if the name is qualified, and if so, check if the qualifier + * matches any custom variable class. If so, add a placeholder. + */ + const char *dot = strchr(name, GUC_QUALIFIER_SEPARATOR); + + if (dot != NULL && + is_custom_class(name, dot - name, custom_variable_classes)) + return add_placeholder_variable(name, elevel); + } /* Unknown name */ return NULL; @@ -2861,7 +2928,9 @@ guc_var_compare(const void *a, const void *b) return guc_name_compare(confa->name, confb->name); } - +/* + * the bare comparison function for GUC names + */ static int guc_name_compare(const char *namea, const char *nameb) { @@ -2890,30 +2959,6 @@ guc_name_compare(const char *namea, const char *nameb) } -static int -guc_get_index(const char *name) -{ - const char **key = &name; - struct config_generic **res; - - Assert(name); - - /* - * By equating const char ** with struct config_generic *, we are assuming - * the name field is first in config_generic. - */ - res = (struct config_generic **) bsearch((void *) &key, - (void *) guc_variables, - num_guc_variables, - sizeof(struct config_generic *), - guc_var_compare); - if (!res) - return -1; - - return res - guc_variables; -} - - /* * Initialize GUC options during program startup. * @@ -2928,8 +2973,8 @@ InitializeGUCOptions(void) long stack_rlimit; /* - * Before log_line_prefix could possibly receive a nonempty setting, - * make sure that timezone processing is minimally alive (see elog.c). + * Before log_line_prefix could possibly receive a nonempty setting, make + * sure that timezone processing is minimally alive (see elog.c). */ pg_timezone_pre_initialize(); @@ -2948,7 +2993,6 @@ InitializeGUCOptions(void) gconf->status = 0; gconf->reset_source = PGC_S_DEFAULT; - gconf->tentative_source = PGC_S_DEFAULT; gconf->source = PGC_S_DEFAULT; gconf->stack = NULL; @@ -3001,11 +3045,10 @@ InitializeGUCOptions(void) *conf->variable = NULL; conf->reset_val = NULL; - conf->tentative_val = NULL; if (conf->boot_val == NULL) { - /* Cannot set value yet */ + /* leave the value NULL, do not call assign hook */ break; } @@ -3055,7 +3098,8 @@ InitializeGUCOptions(void) /* * For historical reasons, some GUC parameters can receive defaults from - * environment variables. Process those settings. + * environment variables. Process those settings. NB: if you add or + * remove anything here, see also ProcessConfigFile(). */ env = getenv("PGPORT"); @@ -3078,11 +3122,11 @@ InitializeGUCOptions(void) stack_rlimit = get_stack_depth_rlimit(); if (stack_rlimit > 0) { - int new_limit = (stack_rlimit - STACK_DEPTH_SLOP) / 1024L; + int new_limit = (stack_rlimit - STACK_DEPTH_SLOP) / 1024L; if (new_limit > 100) { - char limbuf[16]; + char limbuf[16]; new_limit = Min(new_limit, 2048); sprintf(limbuf, "%d", new_limit); @@ -3266,7 +3310,7 @@ ResetAllOptions(void) continue; /* Save old value to support transaction abort */ - push_old_value(gconf); + push_old_value(gconf, GUC_ACTION_SET); switch (gconf->vartype) { @@ -3279,11 +3323,7 @@ ResetAllOptions(void) PGC_S_SESSION)) elog(ERROR, "failed to reset %s", conf->gen.name); *conf->variable = conf->reset_val; - conf->tentative_val = conf->reset_val; conf->gen.source = conf->gen.reset_source; - conf->gen.tentative_source = conf->gen.reset_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; break; } case PGC_INT: @@ -3295,11 +3335,7 @@ ResetAllOptions(void) PGC_S_SESSION)) elog(ERROR, "failed to reset %s", conf->gen.name); *conf->variable = conf->reset_val; - conf->tentative_val = conf->reset_val; conf->gen.source = conf->gen.reset_source; - conf->gen.tentative_source = conf->gen.reset_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; break; } case PGC_REAL: @@ -3311,11 +3347,7 @@ ResetAllOptions(void) PGC_S_SESSION)) elog(ERROR, "failed to reset %s", conf->gen.name); *conf->variable = conf->reset_val; - conf->tentative_val = conf->reset_val; conf->gen.source = conf->gen.reset_source; - conf->gen.tentative_source = conf->gen.reset_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; break; } case PGC_STRING: @@ -3323,16 +3355,10 @@ ResetAllOptions(void) struct config_string *conf = (struct config_string *) gconf; char *str; - if (conf->reset_val == NULL) - { - /* Nothing to reset to, as yet; so do nothing */ - break; - } - /* We need not strdup here */ str = conf->reset_val; - if (conf->assign_hook) + if (conf->assign_hook && str) { const char *newstr; @@ -3350,11 +3376,7 @@ ResetAllOptions(void) } set_string_field(conf, conf->variable, str); - set_string_field(conf, &conf->tentative_val, str); conf->gen.source = conf->gen.reset_source; - conf->gen.tentative_source = conf->gen.reset_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; break; } } @@ -3367,339 +3389,368 @@ ResetAllOptions(void) /* * push_old_value - * Push previous state during first assignment to a GUC variable - * within a particular transaction. - * - * We have to be willing to "back-fill" the state stack if the first - * assignment occurs within a subtransaction nested several levels deep. - * This ensures that if an intermediate transaction aborts, it will have - * the proper value available to restore the setting to. + * Push previous state during transactional assignment to a GUC variable. */ static void -push_old_value(struct config_generic * gconf) +push_old_value(struct config_generic * gconf, GucAction action) { - int my_level = GetCurrentTransactionNestLevel(); GucStack *stack; - /* If we're not inside a transaction, do nothing */ - if (my_level == 0) + /* If we're not inside a nest level, do nothing */ + if (GUCNestLevel == 0) return; - for (;;) + /* Do we already have a stack entry of the current nest level? */ + stack = gconf->stack; + if (stack && stack->nest_level >= GUCNestLevel) { - /* Done if we already pushed it at this nesting depth */ - if (gconf->stack && gconf->stack->nest_level >= my_level) - return; - - /* - * We keep all the stack entries in TopTransactionContext so as to - * avoid allocation problems when a subtransaction back-fills stack - * entries for upper transaction levels. - */ - stack = (GucStack *) MemoryContextAlloc(TopTransactionContext, - sizeof(GucStack)); - - stack->prev = gconf->stack; - stack->nest_level = stack->prev ? stack->prev->nest_level + 1 : 1; - stack->status = gconf->status; - stack->tentative_source = gconf->tentative_source; - stack->source = gconf->source; - - switch (gconf->vartype) + /* Yes, so adjust its state if necessary */ + Assert(stack->nest_level == GUCNestLevel); + switch (action) { - case PGC_BOOL: - stack->tentative_val.boolval = - ((struct config_bool *) gconf)->tentative_val; - stack->value.boolval = - *((struct config_bool *) gconf)->variable; - break; - - case PGC_INT: - stack->tentative_val.intval = - ((struct config_int *) gconf)->tentative_val; - stack->value.intval = - *((struct config_int *) gconf)->variable; + case GUC_ACTION_SET: + /* SET overrides any prior action at same nest level */ + if (stack->state == GUC_SET_LOCAL) + { + /* must discard old masked value */ + discard_stack_value(gconf, &stack->masked); + } + stack->state = GUC_SET; break; - - case PGC_REAL: - stack->tentative_val.realval = - ((struct config_real *) gconf)->tentative_val; - stack->value.realval = - *((struct config_real *) gconf)->variable; + case GUC_ACTION_LOCAL: + if (stack->state == GUC_SET) + { + /* SET followed by SET LOCAL, remember SET's value */ + set_stack_value(gconf, &stack->masked); + stack->state = GUC_SET_LOCAL; + } + /* in all other cases, no change to stack entry */ break; - - case PGC_STRING: - stack->tentative_val.stringval = - ((struct config_string *) gconf)->tentative_val; - stack->value.stringval = - *((struct config_string *) gconf)->variable; + case GUC_ACTION_SAVE: + /* Could only have a prior SAVE of same variable */ + Assert(stack->state == GUC_SAVE); break; } + Assert(guc_dirty); /* must be set already */ + return; + } - gconf->stack = stack; - - /* Set state to indicate nothing happened yet within this level */ - gconf->status = GUC_HAVE_STACK; + /* + * Push a new stack entry + * + * We keep all the stack entries in TopTransactionContext for simplicity. + */ + stack = (GucStack *) MemoryContextAllocZero(TopTransactionContext, + sizeof(GucStack)); - /* Ensure we remember to pop at end of xact */ - guc_dirty = true; + stack->prev = gconf->stack; + stack->nest_level = GUCNestLevel; + switch (action) + { + case GUC_ACTION_SET: + stack->state = GUC_SET; + break; + case GUC_ACTION_LOCAL: + stack->state = GUC_LOCAL; + break; + case GUC_ACTION_SAVE: + stack->state = GUC_SAVE; + break; } + stack->source = gconf->source; + set_stack_value(gconf, &stack->prior); + + gconf->stack = stack; + + /* Ensure we remember to pop at end of xact */ + guc_dirty = true; +} + + +/* + * Do GUC processing at main transaction start. + */ +void +AtStart_GUC(void) +{ + /* + * The nest level should be 0 between transactions; if it isn't, somebody + * didn't call AtEOXact_GUC, or called it with the wrong nestLevel. We + * throw a warning but make no other effort to clean up. + */ + if (GUCNestLevel != 0) + elog(WARNING, "GUC nest level = %d at transaction start", + GUCNestLevel); + GUCNestLevel = 1; +} + +/* + * Enter a new nesting level for GUC values. This is called at subtransaction + * start and when entering a function that has proconfig settings. NOTE that + * we must not risk error here, else subtransaction start will be unhappy. + */ +int +NewGUCNestLevel(void) +{ + return ++GUCNestLevel; } /* - * Do GUC processing at transaction or subtransaction commit or abort. + * Do GUC processing at transaction or subtransaction commit or abort, or + * when exiting a function that has proconfig settings. (The name is thus + * a bit of a misnomer; perhaps it should be ExitGUCNestLevel or some such.) + * During abort, we discard all GUC settings that were applied at nesting + * levels >= nestLevel. nestLevel == 1 corresponds to the main transaction. */ void -AtEOXact_GUC(bool isCommit, bool isSubXact) +AtEOXact_GUC(bool isCommit, int nestLevel) { - int my_level; + bool still_dirty; int i; + Assert(nestLevel > 0 && nestLevel <= GUCNestLevel); + /* Quick exit if nothing's changed in this transaction */ if (!guc_dirty) + { + GUCNestLevel = nestLevel - 1; return; + } - my_level = GetCurrentTransactionNestLevel(); - Assert(isSubXact ? (my_level > 1) : (my_level == 1)); - + still_dirty = false; for (i = 0; i < num_guc_variables; i++) { struct config_generic *gconf = guc_variables[i]; - int my_status = gconf->status; - GucStack *stack = gconf->stack; - bool useTentative; - bool changed; + GucStack *stack; /* - * Skip if nothing's happened to this var in this transaction + * Process and pop each stack entry within the nest level. To + * simplify fmgr_security_definer(), we allow failure exit from a + * function-with-SET-options to be recovered at the surrounding + * transaction or subtransaction abort; so there could be more than + * one stack entry to pop. */ - if (my_status == 0) + while ((stack = gconf->stack) != NULL && + stack->nest_level >= nestLevel) { - Assert(stack == NULL); - continue; - } - /* Assert that we stacked old value before changing it */ - Assert(stack != NULL && (my_status & GUC_HAVE_STACK)); - /* However, the last change may have been at an outer xact level */ - if (stack->nest_level < my_level) - continue; - Assert(stack->nest_level == my_level); - - /* - * We will pop the stack entry. Start by restoring outer xact status - * (since we may want to modify it below). Be careful to use - * my_status to reference the inner xact status below this point... - */ - gconf->status = stack->status; - - /* - * We have two cases: - * - * If commit and HAVE_TENTATIVE, set actual value to tentative (this - * is to override a SET LOCAL if one occurred later than SET). We keep - * the tentative value and propagate HAVE_TENTATIVE to the parent - * status, allowing the SET's effect to percolate up. (But if we're - * exiting the outermost transaction, we'll drop the HAVE_TENTATIVE - * bit below.) - * - * Otherwise, we have a transaction that aborted or executed only SET - * LOCAL (or no SET at all). In either case it should have no further - * effect, so restore both tentative and actual values from the stack - * entry. - */ + GucStack *prev = stack->prev; + bool restorePrior = false; + bool restoreMasked = false; + bool changed; - useTentative = isCommit && (my_status & GUC_HAVE_TENTATIVE) != 0; - changed = false; - - switch (gconf->vartype) - { - case PGC_BOOL: + /* + * In this next bit, if we don't set either restorePrior or + * restoreMasked, we must "discard" any unwanted fields of the + * stack entries to avoid leaking memory. If we do set one of + * those flags, unused fields will be cleaned up after restoring. + */ + if (!isCommit) /* if abort, always restore prior value */ + restorePrior = true; + else if (stack->state == GUC_SAVE) + restorePrior = true; + else if (stack->nest_level == 1) + { + /* transaction commit */ + if (stack->state == GUC_SET_LOCAL) + restoreMasked = true; + else if (stack->state == GUC_SET) { - struct config_bool *conf = (struct config_bool *) gconf; - bool newval; - GucSource newsource; - - if (useTentative) - { - newval = conf->tentative_val; - newsource = conf->gen.tentative_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - } - else - { - newval = stack->value.boolval; - newsource = stack->source; - conf->tentative_val = stack->tentative_val.boolval; - conf->gen.tentative_source = stack->tentative_source; - } - - if (*conf->variable != newval) - { - if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, - true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s", - conf->gen.name); - *conf->variable = newval; - changed = true; - } - conf->gen.source = newsource; - break; + /* we keep the current active value */ + discard_stack_value(gconf, &stack->prior); } - case PGC_INT: + else /* must be GUC_LOCAL */ + restorePrior = true; + } + else if (prev == NULL || + prev->nest_level < stack->nest_level - 1) + { + /* decrement entry's level and do not pop it */ + stack->nest_level--; + continue; + } + else + { + /* + * We have to merge this stack entry into prev. See README for + * discussion of this bit. + */ + switch (stack->state) { - struct config_int *conf = (struct config_int *) gconf; - int newval; - GucSource newsource; + case GUC_SAVE: + Assert(false); /* can't get here */ + + case GUC_SET: + /* next level always becomes SET */ + discard_stack_value(gconf, &stack->prior); + if (prev->state == GUC_SET_LOCAL) + discard_stack_value(gconf, &prev->masked); + prev->state = GUC_SET; + break; - if (useTentative) - { - newval = conf->tentative_val; - newsource = conf->gen.tentative_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - } - else - { - newval = stack->value.intval; - newsource = stack->source; - conf->tentative_val = stack->tentative_val.intval; - conf->gen.tentative_source = stack->tentative_source; - } + case GUC_LOCAL: + if (prev->state == GUC_SET) + { + /* LOCAL migrates down */ + prev->masked = stack->prior; + prev->state = GUC_SET_LOCAL; + } + else + { + /* else just forget this stack level */ + discard_stack_value(gconf, &stack->prior); + } + break; - if (*conf->variable != newval) - { - if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, - true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s", - conf->gen.name); - *conf->variable = newval; - changed = true; - } - conf->gen.source = newsource; - break; + case GUC_SET_LOCAL: + /* prior state at this level no longer wanted */ + discard_stack_value(gconf, &stack->prior); + /* copy down the masked state */ + if (prev->state == GUC_SET_LOCAL) + discard_stack_value(gconf, &prev->masked); + prev->masked = stack->masked; + prev->state = GUC_SET_LOCAL; + break; } - case PGC_REAL: - { - struct config_real *conf = (struct config_real *) gconf; - double newval; - GucSource newsource; + } - if (useTentative) - { - newval = conf->tentative_val; - newsource = conf->gen.tentative_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - } - else - { - newval = stack->value.realval; - newsource = stack->source; - conf->tentative_val = stack->tentative_val.realval; - conf->gen.tentative_source = stack->tentative_source; - } + changed = false; - if (*conf->variable != newval) - { - if (conf->assign_hook) - if (!(*conf->assign_hook) (newval, - true, PGC_S_OVERRIDE)) - elog(LOG, "failed to commit %s", - conf->gen.name); - *conf->variable = newval; - changed = true; - } - conf->gen.source = newsource; - break; + if (restorePrior || restoreMasked) + { + /* Perform appropriate restoration of the stacked value */ + union config_var_value newvalue; + GucSource newsource; + + if (restoreMasked) + { + newvalue = stack->masked; + newsource = PGC_S_SESSION; } - case PGC_STRING: + else { - struct config_string *conf = (struct config_string *) gconf; - char *newval; - GucSource newsource; + newvalue = stack->prior; + newsource = stack->source; + } - if (useTentative) - { - newval = conf->tentative_val; - newsource = conf->gen.tentative_source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - } - else - { - newval = stack->value.stringval; - newsource = stack->source; - set_string_field(conf, &conf->tentative_val, - stack->tentative_val.stringval); - conf->gen.tentative_source = stack->tentative_source; - } + switch (gconf->vartype) + { + case PGC_BOOL: + { + struct config_bool *conf = (struct config_bool *) gconf; + bool newval = newvalue.boolval; - if (*conf->variable != newval) - { - if (conf->assign_hook) + if (*conf->variable != newval) + { + if (conf->assign_hook) + if (!(*conf->assign_hook) (newval, + true, PGC_S_OVERRIDE)) + elog(LOG, "failed to commit %s", + conf->gen.name); + *conf->variable = newval; + changed = true; + } + break; + } + case PGC_INT: { - const char *newstr; - - newstr = (*conf->assign_hook) (newval, true, - PGC_S_OVERRIDE); - if (newstr == NULL) - elog(LOG, "failed to commit %s", - conf->gen.name); - else if (newstr != newval) + struct config_int *conf = (struct config_int *) gconf; + int newval = newvalue.intval; + + if (*conf->variable != newval) { - /* - * If newval should now be freed, it'll be - * taken care of below. - * - * See notes in set_config_option about - * casting - */ - newval = (char *) newstr; + if (conf->assign_hook) + if (!(*conf->assign_hook) (newval, + true, PGC_S_OVERRIDE)) + elog(LOG, "failed to commit %s", + conf->gen.name); + *conf->variable = newval; + changed = true; } + break; } + case PGC_REAL: + { + struct config_real *conf = (struct config_real *) gconf; + double newval = newvalue.realval; - set_string_field(conf, conf->variable, newval); - changed = true; - } - conf->gen.source = newsource; - /* Release stacked values if not used anymore */ - set_string_field(conf, &stack->value.stringval, - NULL); - set_string_field(conf, &stack->tentative_val.stringval, - NULL); - /* Don't store tentative value separately after commit */ - if (!isSubXact) - set_string_field(conf, &conf->tentative_val, NULL); - break; + if (*conf->variable != newval) + { + if (conf->assign_hook) + if (!(*conf->assign_hook) (newval, + true, PGC_S_OVERRIDE)) + elog(LOG, "failed to commit %s", + conf->gen.name); + *conf->variable = newval; + changed = true; + } + break; + } + case PGC_STRING: + { + struct config_string *conf = (struct config_string *) gconf; + char *newval = newvalue.stringval; + + if (*conf->variable != newval) + { + if (conf->assign_hook && newval) + { + const char *newstr; + + newstr = (*conf->assign_hook) (newval, true, + PGC_S_OVERRIDE); + if (newstr == NULL) + elog(LOG, "failed to commit %s", + conf->gen.name); + else if (newstr != newval) + { + /* + * If newval should now be freed, + * it'll be taken care of below. + * + * See notes in set_config_option + * about casting + */ + newval = (char *) newstr; + } + } + + set_string_field(conf, conf->variable, newval); + changed = true; + } + + /* + * Release stacked values if not used anymore. We + * could use discard_stack_value() here, but since + * we have type-specific code anyway, might as + * well inline it. + */ + set_string_field(conf, &stack->prior.stringval, NULL); + set_string_field(conf, &stack->masked.stringval, NULL); + break; + } } - } - /* Finish popping the state stack */ - gconf->stack = stack->prev; - pfree(stack); + gconf->source = newsource; + } - /* - * If we're now out of all xact levels, forget TENTATIVE status bit; - * there's nothing tentative about the value anymore. - */ - if (!isSubXact) - { - Assert(gconf->stack == NULL); - gconf->status = 0; - } + /* Finish popping the state stack */ + gconf->stack = prev; + pfree(stack); - /* Report new value if we changed it */ - if (changed && (gconf->flags & GUC_REPORT)) - ReportGUCOption(gconf); + /* Report new value if we changed it */ + if (changed && (gconf->flags & GUC_REPORT)) + ReportGUCOption(gconf); + } /* end of stack-popping loop */ + + if (stack != NULL) + still_dirty = true; } - /* - * If we're now out of all xact levels, we can clear guc_dirty. (Note: we - * cannot reset guc_dirty when exiting a subtransaction, because we know - * that all outer transaction levels will have stacked values to deal - * with.) - */ - if (!isSubXact) - guc_dirty = false; + /* If there are no remaining stack entries, we can reset guc_dirty */ + guc_dirty = still_dirty; + + /* Update nesting level */ + GUCNestLevel = nestLevel - 1; } @@ -4098,14 +4149,19 @@ call_string_assign_hook(GucStringAssignHook assign_hook, * function is being called so it can apply the access restrictions * properly. * - * If value is NULL, set the option to its default value. If the - * parameter changeVal is false then don't really set the option but do all + * If value is NULL, set the option to its default value (normally the + * reset_val, but if source == PGC_S_DEFAULT we instead use the boot_val). + * + * action indicates whether to set the value globally in the session, locally + * to the current top transaction, or just for the duration of a function call. + * + * If changeVal is false then don't really set the option but do all * the checks to see if it would work. * * If there is an error (non-existing option, invalid value) then an * ereport(ERROR) is thrown *unless* this is called in a context where we * don't want to ereport (currently, startup or SIGHUP config file reread). - * In that case we write a suitable error message via ereport(DEBUG) and + * In that case we write a suitable error message via ereport(LOG) and * return false. This is working around the deficiencies in the ereport * mechanism, so don't blame me. In all other cases, the function * returns true, including cases where the input is valid but we chose @@ -4116,7 +4172,7 @@ call_string_assign_hook(GucStringAssignHook assign_hook, bool set_config_option(const char *name, const char *value, GucContext context, GucSource source, - bool isLocal, bool changeVal) + GucAction action, bool changeVal) { struct config_generic *record; int elevel; @@ -4128,14 +4184,14 @@ set_config_option(const char *name, const char *value, * To avoid cluttering the log, only the postmaster bleats loudly * about problems with the config file. */ - elevel = IsUnderPostmaster ? DEBUG2 : LOG; + elevel = IsUnderPostmaster ? DEBUG3 : LOG; } else if (source == PGC_S_DATABASE || source == PGC_S_USER) elevel = INFO; else elevel = ERROR; - record = find_option(name, elevel); + record = find_option(name, true, elevel); if (record == NULL) { ereport(elevel, @@ -4145,11 +4201,13 @@ set_config_option(const char *name, const char *value, } /* - * Do not replace a value that has been set on the command line by a SIGHUP - * reload + * If source is postgresql.conf, mark the found record with + * GUC_IS_IN_FILE. This is for the convenience of ProcessConfigFile. Note + * that we do it even if changeVal is false, since ProcessConfigFile wants + * the marking to occur during its testing pass. */ - if (context == PGC_SIGHUP && record->source == PGC_S_ARGV) - return true; + if (source == PGC_S_FILE) + record->status |= GUC_IS_IN_FILE; /* * Check if the option can be set at this time. See guc.h for the precise @@ -4173,12 +4231,17 @@ set_config_option(const char *name, const char *value, case PGC_POSTMASTER: if (context == PGC_SIGHUP) { + /* + * We are reading a PGC_POSTMASTER var from postgresql.conf. + * We can't change the setting, so give a warning if the DBA + * tries to change it. (Throwing an error would be more + * consistent, but seems overly rigid.) + */ if (changeVal && !is_newvalue_equal(record, value)) ereport(elevel, (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM), errmsg("parameter \"%s\" cannot be changed after server start; configuration file change ignored", name))); - return true; } if (context != PGC_POSTMASTER) @@ -4246,27 +4309,22 @@ set_config_option(const char *name, const char *value, } /* - * Should we set reset/stacked values? (If so, the behavior is not - * transactional.) This is done either when we get a default - * value from the database's/user's/client's default settings or - * when we reset a value to its default. + * Should we set reset/stacked values? (If so, the behavior is not + * transactional.) This is done either when we get a default value from + * the database's/user's/client's default settings or when we reset a + * value to its default. */ - makeDefault = changeVal && (source <= PGC_S_OVERRIDE) - && ((value != NULL) || source == PGC_S_DEFAULT); + makeDefault = changeVal && (source <= PGC_S_OVERRIDE) && + ((value != NULL) || source == PGC_S_DEFAULT); /* - * Ignore attempted set if overridden by previously processed - * setting. However, if changeVal is false then plow ahead anyway - * since we are trying to find out if the value is potentially - * good, not actually use it. Also keep going if makeDefault is - * true, since we may want to set the reset/stacked values even if - * we can't set the variable itself. There's one exception to - * this rule: if we want to apply the default value to variables - * that were removed from the configuration file. This is - * indicated by source == PGC_S_DEFAULT and context == PGC_SIGHUP. + * Ignore attempted set if overridden by previously processed setting. + * However, if changeVal is false then plow ahead anyway since we are + * trying to find out if the value is potentially good, not actually use + * it. Also keep going if makeDefault is true, since we may want to set + * the reset/stacked values even if we can't set the variable itself. */ - if (record->source > source - && !(source == PGC_S_DEFAULT && context == PGC_SIGHUP)) + if (record->source > source) { if (changeVal && !makeDefault) { @@ -4298,14 +4356,8 @@ set_config_option(const char *name, const char *value, return false; } } - /* - * If value == NULL and source == PGC_S_DEFAULT then - * we reset some value to its default (removed from - * configuration file). - */ else if (source == PGC_S_DEFAULT) newval = conf->boot_val; - /* else we handle a "RESET varname" command */ else { newval = conf->reset_val; @@ -4326,7 +4378,7 @@ set_config_option(const char *name, const char *value, { /* Save old value to support transaction abort */ if (!makeDefault) - push_old_value(&conf->gen); + push_old_value(&conf->gen, action); if (changeVal) { *conf->variable = newval; @@ -4345,23 +4397,11 @@ set_config_option(const char *name, const char *value, { if (stack->source <= source) { - stack->value.boolval = newval; + stack->prior.boolval = newval; stack->source = source; } } } - else if (isLocal) - { - conf->gen.status |= GUC_HAVE_LOCAL; - guc_dirty = true; - } - else - { - conf->tentative_val = newval; - conf->gen.tentative_source = source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; - } } break; } @@ -4379,8 +4419,8 @@ set_config_option(const char *name, const char *value, { ereport(elevel, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for parameter \"%s\": \"%s\"", - name, value), + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, value), hintmsg ? errhint(hintmsg) : 0)); return false; } @@ -4393,14 +4433,8 @@ set_config_option(const char *name, const char *value, return false; } } - /* - * If value == NULL and source == PGC_S_DEFAULT then - * we reset some value to its default (removed from - * configuration file). - */ else if (source == PGC_S_DEFAULT) newval = conf->boot_val; - /* else we handle a "RESET varname" command */ else { newval = conf->reset_val; @@ -4421,7 +4455,7 @@ set_config_option(const char *name, const char *value, { /* Save old value to support transaction abort */ if (!makeDefault) - push_old_value(&conf->gen); + push_old_value(&conf->gen, action); if (changeVal) { *conf->variable = newval; @@ -4440,23 +4474,11 @@ set_config_option(const char *name, const char *value, { if (stack->source <= source) { - stack->value.intval = newval; + stack->prior.intval = newval; stack->source = source; } } } - else if (isLocal) - { - conf->gen.status |= GUC_HAVE_LOCAL; - guc_dirty = true; - } - else - { - conf->tentative_val = newval; - conf->gen.tentative_source = source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; - } } break; } @@ -4485,14 +4507,8 @@ set_config_option(const char *name, const char *value, return false; } } - /* - * If value == NULL and source == PGC_S_DEFAULT then - * we reset some value to its default (removed from - * configuration file). - */ else if (source == PGC_S_DEFAULT) newval = conf->boot_val; - /* else we handle a "RESET varname" command */ else { newval = conf->reset_val; @@ -4513,7 +4529,7 @@ set_config_option(const char *name, const char *value, { /* Save old value to support transaction abort */ if (!makeDefault) - push_old_value(&conf->gen); + push_old_value(&conf->gen, action); if (changeVal) { *conf->variable = newval; @@ -4532,23 +4548,11 @@ set_config_option(const char *name, const char *value, { if (stack->source <= source) { - stack->value.realval = newval; + stack->prior.realval = newval; stack->source = source; } } } - else if (isLocal) - { - conf->gen.status |= GUC_HAVE_LOCAL; - guc_dirty = true; - } - else - { - conf->tentative_val = newval; - conf->gen.tentative_source = source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; - } } break; } @@ -4571,11 +4575,6 @@ set_config_option(const char *name, const char *value, if (conf->gen.flags & GUC_IS_NAME) truncate_identifier(newval, strlen(newval), true); } - /* - * If value == NULL and source == PGC_S_DEFAULT then - * we reset some value to its default (removed from - * configuration file). - */ else if (source == PGC_S_DEFAULT) { if (conf->boot_val == NULL) @@ -4587,25 +4586,25 @@ set_config_option(const char *name, const char *value, return false; } } - /* else we handle a "RESET varname" command */ - else if (conf->reset_val) + else { /* * We could possibly avoid strdup here, but easier to make - * this case work the same as the normal assignment case. + * this case work the same as the normal assignment case; + * note the possible free of newval below. */ - newval = guc_strdup(elevel, conf->reset_val); - if (newval == NULL) - return false; + if (conf->reset_val == NULL) + newval = NULL; + else + { + newval = guc_strdup(elevel, conf->reset_val); + if (newval == NULL) + return false; + } source = conf->gen.reset_source; } - else - { - /* Nothing to reset to, as yet; so do nothing */ - break; - } - if (conf->assign_hook) + if (conf->assign_hook && newval) { const char *hookresult; @@ -4646,7 +4645,7 @@ set_config_option(const char *name, const char *value, { /* Save old value to support transaction abort */ if (!makeDefault) - push_old_value(&conf->gen); + push_old_value(&conf->gen, action); if (changeVal) { set_string_field(conf, conf->variable, newval); @@ -4665,29 +4664,17 @@ set_config_option(const char *name, const char *value, { if (stack->source <= source) { - set_string_field(conf, &stack->value.stringval, + set_string_field(conf, &stack->prior.stringval, newval); stack->source = source; } } /* Perhaps we didn't install newval anywhere */ - if (!string_field_used(conf, newval)) + if (newval && !string_field_used(conf, newval)) free(newval); } - else if (isLocal) - { - conf->gen.status |= GUC_HAVE_LOCAL; - guc_dirty = true; - } - else - { - set_string_field(conf, &conf->tentative_val, newval); - conf->gen.tentative_source = source; - conf->gen.status |= GUC_HAVE_TENTATIVE; - guc_dirty = true; - } } - else + else if (newval) free(newval); break; } @@ -4709,7 +4696,8 @@ void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source) { - (void) set_config_option(name, value, context, source, false, true); + (void) set_config_option(name, value, context, source, + GUC_ACTION_SET, true); } @@ -4727,7 +4715,7 @@ GetConfigOption(const char *name) struct config_generic *record; static char buffer[256]; - record = find_option(name, ERROR); + record = find_option(name, false, ERROR); if (record == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -4760,6 +4748,10 @@ GetConfigOption(const char *name) /* * Get the RESET value associated with the given option. + * + * Note: this is not re-entrant, due to use of static result buffer; + * not to mention that a string variable could have its reset_val changed. + * Beware of assuming the result value is good for very long. */ const char * GetConfigOptionResetString(const char *name) @@ -4767,7 +4759,7 @@ GetConfigOptionResetString(const char *name) struct config_generic *record; static char buffer[256]; - record = find_option(name, ERROR); + record = find_option(name, false, ERROR); if (record == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -4807,7 +4799,7 @@ IsSuperuserConfigOption(const char *name) { struct config_generic *record; - record = find_option(name, ERROR); + record = find_option(name, false, ERROR); /* On an unrecognized name, don't error, just return false. */ if (record == NULL) return false; @@ -4815,6 +4807,51 @@ IsSuperuserConfigOption(const char *name) } +/* + * GUC_complaint_elevel + * Get the ereport error level to use in an assign_hook's error report. + * + * This should be used by assign hooks that want to emit a custom error + * report (in addition to the generic "invalid value for option FOO" that + * guc.c will provide). Note that the result might be ERROR or a lower + * level, so the caller must be prepared for control to return from ereport, + * or not. If control does return, return false/NULL from the hook function. + * + * At some point it'd be nice to replace this with a mechanism that allows + * the custom message to become the DETAIL line of guc.c's generic message. + */ +int +GUC_complaint_elevel(GucSource source) +{ + int elevel; + + if (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_OVERRIDE) + { + /* + * If we're a postmaster child, this is probably "undo" during + * transaction abort, so we don't want to clutter the log. There's + * a small chance of a real problem with an OVERRIDE setting, + * though, so suppressing the message entirely wouldn't be desirable. + */ + elevel = IsUnderPostmaster ? DEBUG5 : LOG; + } + else if (source < PGC_S_INTERACTIVE) + elevel = LOG; + else + elevel = ERROR; + + return elevel; +} + + /* * flatten_set_variable_args * Given a parsenode List as emitted by the grammar for SET, @@ -4823,10 +4860,10 @@ IsSuperuserConfigOption(const char *name) * We need to be told the name of the variable the args are for, because * the flattening rules vary (ugh). * - * The result is NULL if input is NIL (ie, SET ... TO DEFAULT), otherwise + * The result is NULL if args is NIL (ie, SET ... TO DEFAULT), otherwise * a palloc'd string. */ -char * +static char * flatten_set_variable_args(const char *name, List *args) { struct config_generic *record; @@ -4834,15 +4871,12 @@ flatten_set_variable_args(const char *name, List *args) StringInfoData buf; ListCell *l; - /* - * Fast path if just DEFAULT. We do not check the variable name in this - * case --- necessary for RESET ALL to work correctly. - */ + /* Fast path if just DEFAULT */ if (args == NIL) return NULL; /* Else get flags for the variable */ - record = find_option(name, ERROR); + record = find_option(name, true, ERROR); if (record == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -4888,11 +4922,13 @@ flatten_set_variable_args(const char *name, List *args) * to interval and back to normalize the value and account * for any typmod. */ + Oid typoid; int32 typmod; Datum interval; char *intervalout; - typmod = typenameTypeMod(NULL, arg->typename, INTERVALOID); + typoid = typenameTypeId(NULL, arg->typename, &typmod); + Assert(typoid == INTERVALOID); interval = DirectFunctionCall3(interval_in, @@ -4932,6 +4968,111 @@ flatten_set_variable_args(const char *name, List *args) * SET command */ void +ExecSetVariableStmt(VariableSetStmt *stmt) +{ + GucAction action = stmt->is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET; + + switch (stmt->kind) + { + case VAR_SET_VALUE: + case VAR_SET_CURRENT: + set_config_option(stmt->name, + ExtractSetVariableArgs(stmt), + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + action, + true); + break; + case VAR_SET_MULTI: + + /* + * Special case for special SQL syntax that effectively sets more + * than one variable per statement. + */ + if (strcmp(stmt->name, "TRANSACTION") == 0) + { + ListCell *head; + + foreach(head, stmt->args) + { + DefElem *item = (DefElem *) lfirst(head); + + if (strcmp(item->defname, "transaction_isolation") == 0) + SetPGVariable("transaction_isolation", + list_make1(item->arg), stmt->is_local); + else if (strcmp(item->defname, "transaction_read_only") == 0) + SetPGVariable("transaction_read_only", + list_make1(item->arg), stmt->is_local); + else + elog(ERROR, "unexpected SET TRANSACTION element: %s", + item->defname); + } + } + else if (strcmp(stmt->name, "SESSION CHARACTERISTICS") == 0) + { + ListCell *head; + + foreach(head, stmt->args) + { + DefElem *item = (DefElem *) lfirst(head); + + if (strcmp(item->defname, "transaction_isolation") == 0) + SetPGVariable("default_transaction_isolation", + list_make1(item->arg), stmt->is_local); + else if (strcmp(item->defname, "transaction_read_only") == 0) + SetPGVariable("default_transaction_read_only", + list_make1(item->arg), stmt->is_local); + else + elog(ERROR, "unexpected SET SESSION element: %s", + item->defname); + } + } + else + elog(ERROR, "unexpected SET MULTI element: %s", + stmt->name); + break; + case VAR_SET_DEFAULT: + case VAR_RESET: + set_config_option(stmt->name, + NULL, + (superuser() ? PGC_SUSET : PGC_USERSET), + PGC_S_SESSION, + action, + true); + break; + case VAR_RESET_ALL: + ResetAllOptions(); + break; + } +} + +/* + * Get the value to assign for a VariableSetStmt, or NULL if it's RESET. + * The result is palloc'd. + * + * This is exported for use by actions such as ALTER ROLE SET. + */ +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); + default: + return NULL; + } +} + +/* + * SetPGVariable - SET command exported as an easily-C-callable function. + * + * This provides access to SET TO value, as well as SET TO DEFAULT (expressed + * by passing args == NIL), but not SET FROM CURRENT functionality. + */ +void SetPGVariable(const char *name, List *args, bool is_local) { char *argstring = flatten_set_variable_args(name, args); @@ -4941,7 +5082,7 @@ SetPGVariable(const char *name, List *args, bool is_local) argstring, (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION, - is_local, + is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET, true); } @@ -4985,7 +5126,7 @@ set_config_by_name(PG_FUNCTION_ARGS) value, (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION, - is_local, + is_local ? GUC_ACTION_LOCAL : GUC_ACTION_SET, true); /* get the new current value */ @@ -4998,6 +5139,38 @@ set_config_by_name(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(result_text); } + +/* + * Common code for DefineCustomXXXVariable subroutines: allocate the + * new variable's config struct and fill in generic fields. + */ +static struct config_generic * +init_custom_variable(const char *name, + const char *short_desc, + const char *long_desc, + GucContext context, + enum config_type type, + size_t sz) +{ + struct config_generic *gen; + + gen = (struct config_generic *) guc_malloc(ERROR, sz); + memset(gen, 0, sz); + + gen->name = guc_strdup(ERROR, name); + gen->context = context; + gen->group = CUSTOM_OPTIONS; + gen->short_desc = short_desc; + gen->long_desc = long_desc; + gen->vartype = type; + + return gen; +} + +/* + * Common code for DefineCustomXXXVariable subroutines: insert the new + * variable into the GUC variable array, replacing any placeholder. + */ static void define_custom_variable(struct config_generic * variable) { @@ -5005,15 +5178,16 @@ define_custom_variable(struct config_generic * variable) const char **nameAddr = &name; const char *value; struct config_string *pHolder; - struct config_generic **res = (struct config_generic **) bsearch( - (void *) &nameAddr, - (void *) guc_variables, - num_guc_variables, - sizeof(struct config_generic *), - guc_var_compare); + struct config_generic **res; + res = (struct config_generic **) bsearch((void *) &nameAddr, + (void *) guc_variables, + num_guc_variables, + sizeof(struct config_generic *), + guc_var_compare); if (res == NULL) { + /* No placeholder to replace, so just add it */ add_guc_variable(variable, ERROR); return; } @@ -5027,13 +5201,14 @@ define_custom_variable(struct config_generic * variable) errmsg("attempt to redefine parameter \"%s\"", name))); Assert((*res)->vartype == PGC_STRING); - pHolder = (struct config_string *) * res; + pHolder = (struct config_string *) (*res); - /* We have the same name, no sorting is necessary */ + /* + * Replace the placeholder. We aren't changing the name, so no re-sorting + * is necessary + */ *res = variable; - value = *pHolder->variable; - /* * Assign the string value stored in the placeholder to the real variable. * @@ -5041,9 +5216,12 @@ define_custom_variable(struct config_generic * variable) * assignment, since we don't want it to roll back if the current xact * fails later. */ - set_config_option(name, value, - pHolder->gen.context, pHolder->gen.source, - false, true); + value = *pHolder->variable; + + if (value) + set_config_option(name, value, + pHolder->gen.context, pHolder->gen.source, + GUC_ACTION_SET, true); /* * Free up as much as we conveniently can of the placeholder structure @@ -5051,27 +5229,10 @@ define_custom_variable(struct config_generic * variable) */ set_string_field(pHolder, pHolder->variable, NULL); set_string_field(pHolder, &pHolder->reset_val, NULL); - set_string_field(pHolder, &pHolder->tentative_val, NULL); free(pHolder); } -static void -init_custom_variable(struct config_generic * gen, - const char *name, - const char *short_desc, - const char *long_desc, - GucContext context, - enum config_type type) -{ - gen->name = guc_strdup(ERROR, name); - gen->context = context; - gen->group = CUSTOM_OPTIONS; - gen->short_desc = short_desc; - gen->long_desc = long_desc; - gen->vartype = type; -} - void DefineCustomBoolVariable(const char *name, const char *short_desc, @@ -5081,13 +5242,13 @@ DefineCustomBoolVariable(const char *name, GucBoolAssignHook assign_hook, GucShowHook show_hook) { - size_t sz = sizeof(struct config_bool); - struct config_bool *var = (struct config_bool *) guc_malloc(ERROR, sz); - - memset(var, 0, sz); - init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_BOOL); + struct config_bool *var; + var = (struct config_bool *) + init_custom_variable(name, short_desc, long_desc, context, + PGC_BOOL, sizeof(struct config_bool)); var->variable = valueAddr; + var->boot_val = *valueAddr; var->reset_val = *valueAddr; var->assign_hook = assign_hook; var->show_hook = show_hook; @@ -5105,13 +5266,13 @@ DefineCustomIntVariable(const char *name, GucIntAssignHook assign_hook, GucShowHook show_hook) { - size_t sz = sizeof(struct config_int); - struct config_int *var = (struct config_int *) guc_malloc(ERROR, sz); - - memset(var, 0, sz); - init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_INT); + struct config_int *var; + var = (struct config_int *) + init_custom_variable(name, short_desc, long_desc, context, + PGC_INT, sizeof(struct config_int)); var->variable = valueAddr; + var->boot_val = *valueAddr; var->reset_val = *valueAddr; var->min = minValue; var->max = maxValue; @@ -5131,13 +5292,13 @@ DefineCustomRealVariable(const char *name, GucRealAssignHook assign_hook, GucShowHook show_hook) { - size_t sz = sizeof(struct config_real); - struct config_real *var = (struct config_real *) guc_malloc(ERROR, sz); - - memset(var, 0, sz); - init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_REAL); + struct config_real *var; + var = (struct config_real *) + init_custom_variable(name, short_desc, long_desc, context, + PGC_REAL, sizeof(struct config_real)); var->variable = valueAddr; + var->boot_val = *valueAddr; var->reset_val = *valueAddr; var->min = minValue; var->max = maxValue; @@ -5155,14 +5316,16 @@ DefineCustomStringVariable(const char *name, GucStringAssignHook assign_hook, GucShowHook show_hook) { - size_t sz = sizeof(struct config_string); - struct config_string *var = (struct config_string *) guc_malloc(ERROR, sz); - - memset(var, 0, sz); - init_custom_variable(&var->gen, name, short_desc, long_desc, context, PGC_STRING); + struct config_string *var; + var = (struct config_string *) + init_custom_variable(name, short_desc, long_desc, context, + PGC_STRING, sizeof(struct config_string)); var->variable = valueAddr; - var->reset_val = *valueAddr; + var->boot_val = *valueAddr; + /* we could probably do without strdup, but keep it like normal case */ + if (var->boot_val) + var->reset_val = guc_strdup(ERROR, var->boot_val); var->assign_hook = assign_hook; var->show_hook = show_hook; define_custom_variable(&var->gen); @@ -5198,7 +5361,7 @@ EmitWarningsOnPlaceholders(const char *className) void GetPGVariable(const char *name, DestReceiver *dest) { - if (pg_strcasecmp(name, "all") == 0) + if (guc_name_compare(name, "all") == 0) ShowAllGUCConfig(dest); else ShowGUCConfigOption(name, dest); @@ -5209,7 +5372,7 @@ GetPGVariableResultDesc(const char *name) { TupleDesc tupdesc; - if (pg_strcasecmp(name, "all") == 0) + if (guc_name_compare(name, "all") == 0) { /* need a tuple descriptor representing three TEXT columns */ tupdesc = CreateTemplateTupleDesc(3, false); @@ -5236,23 +5399,6 @@ GetPGVariableResultDesc(const char *name) return tupdesc; } -/* - * RESET command - */ -void -ResetPGVariable(const char *name, bool isTopLevel) -{ - if (pg_strcasecmp(name, "all") == 0) - ResetAllOptions(); - else - set_config_option(name, - NULL, - (superuser() ? PGC_SUSET : PGC_USERSET), - PGC_S_SESSION, - false, - true); -} - /* * SHOW command @@ -5340,7 +5486,7 @@ GetConfigOptionByName(const char *name, const char **varname) { struct config_generic *record; - record = find_option(name, ERROR); + record = find_option(name, false, ERROR); if (record == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -5797,9 +5943,19 @@ _ShowOption(struct config_generic * record, bool use_units) } +/* + * Attempt (badly) to detect if a proposed new GUC setting is the same + * as the current value. + * + * XXX this does not really work because it doesn't account for the + * effects of canonicalization of string values by assign_hooks. + */ static bool is_newvalue_equal(struct config_generic * record, const char *newvalue) { + /* newvalue == NULL isn't supported */ + Assert(newvalue != NULL); + switch (record->vartype) { case PGC_BOOL: @@ -5830,7 +5986,8 @@ is_newvalue_equal(struct config_generic * record, const char *newvalue) { struct config_string *conf = (struct config_string *) record; - return strcmp(*conf->variable, newvalue) == 0; + return *conf->variable != NULL && + strcmp(*conf->variable, newvalue) == 0; } } @@ -6010,7 +6167,7 @@ read_nondefault_variables(void) if ((varname = read_string_with_null(fp)) == NULL) break; - if ((record = find_option(varname, FATAL)) == NULL) + if ((record = find_option(varname, true, FATAL)) == NULL) elog(FATAL, "failed to locate variable %s in exec config params file", varname); if ((varvalue = read_string_with_null(fp)) == NULL) elog(FATAL, "invalid format of exec config params file"); @@ -6018,7 +6175,7 @@ read_nondefault_variables(void) elog(FATAL, "invalid format of exec config params file"); (void) set_config_option(varname, varvalue, record->context, - varsource, false, true); + varsource, GUC_ACTION_SET, true); free(varname); free(varvalue); } @@ -6068,11 +6225,14 @@ ParseLongOption(const char *string, char **name, char **value) /* - * Handle options fetched from pg_database.datconfig or pg_authid.rolconfig. + * Handle options fetched from pg_database.datconfig, pg_authid.rolconfig, + * pg_proc.proconfig, etc. Caller must specify proper context/source/action. + * * The array parameter must be an array of TEXT (it must not be NULL). */ void -ProcessGUCArray(ArrayType *array, GucSource source) +ProcessGUCArray(ArrayType *array, + GucContext context, GucSource source, GucAction action) { int i; @@ -6080,7 +6240,6 @@ ProcessGUCArray(ArrayType *array, GucSource source) Assert(ARR_ELEMTYPE(array) == TEXTOID); Assert(ARR_NDIM(array) == 1); Assert(ARR_LBOUND(array)[0] == 1); - Assert(source == PGC_S_DATABASE || source == PGC_S_USER); for (i = 1; i <= ARR_DIMS(array)[0]; i++) { @@ -6107,17 +6266,13 @@ ProcessGUCArray(ArrayType *array, GucSource source) { ereport(WARNING, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("could not parse setting for parameter \"%s\"", name))); + errmsg("could not parse setting for parameter \"%s\"", + name))); free(name); continue; } - /* - * We process all these options at SUSET level. We assume that the - * right to insert an option into pg_database or pg_authid was checked - * when it was inserted. - */ - SetConfigOption(name, value, PGC_SUSET, source); + (void) set_config_option(name, value, context, source, action, true); free(name); if (value) @@ -6144,7 +6299,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value) /* test if the option is valid */ set_config_option(name, value, superuser() ? PGC_SUSET : PGC_USERSET, - PGC_S_TEST, false, false); + PGC_S_TEST, GUC_ACTION_SET, false); /* convert name to canonical spelling, so we can use plain strcmp */ (void) GetConfigOptionByName(name, &varname); @@ -6222,7 +6377,7 @@ GUCArrayDelete(ArrayType *array, const char *name) /* test if the option is valid */ set_config_option(name, NULL, superuser() ? PGC_SUSET : PGC_USERSET, - PGC_S_TEST, false, false); + PGC_S_TEST, GUC_ACTION_SET, false); /* convert name to canonical spelling, so we can use plain strcmp */ (void) GetConfigOptionByName(name, &varname); @@ -6280,7 +6435,7 @@ GUCArrayDelete(ArrayType *array, const char *name) /* - * assign_hook subroutines + * assign_hook and show_hook subroutines */ static const char * @@ -6300,9 +6455,8 @@ assign_log_destination(const char *value, bool doit, GucSource source) /* syntax error in list */ pfree(rawstring); list_free(elemlist); - if (source >= PGC_S_INTERACTIVE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid list syntax for parameter \"log_destination\""))); return NULL; } @@ -6314,7 +6468,7 @@ assign_log_destination(const char *value, bool doit, GucSource source) if (pg_strcasecmp(tok, "stderr") == 0) newlogdest |= LOG_DESTINATION_STDERR; else if (pg_strcasecmp(tok, "csvlog") == 0) - newlogdest |= LOG_DESTINATION_CSVLOG; + newlogdest |= LOG_DESTINATION_CSVLOG; #ifdef HAVE_SYSLOG else if (pg_strcasecmp(tok, "syslog") == 0) newlogdest |= LOG_DESTINATION_SYSLOG; @@ -6325,9 +6479,8 @@ assign_log_destination(const char *value, bool doit, GucSource source) #endif else { - if (source >= PGC_S_INTERACTIVE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized \"log_destination\" key word: \"%s\"", tok))); pfree(rawstring); @@ -6423,7 +6576,7 @@ assign_defaultxactisolevel(const char *newval, bool doit, GucSource source) static const char * assign_session_replication_role(const char *newval, bool doit, GucSource source) { - int newrole; + int newrole; if (pg_strcasecmp(newval, "origin") == 0) newrole = SESSION_REPLICATION_ROLE_ORIGIN; @@ -6448,8 +6601,7 @@ assign_session_replication_role(const char *newval, bool doit, GucSource source) } static const char * -assign_log_min_messages(const char *newval, - bool doit, GucSource source) +assign_log_min_messages(const char *newval, bool doit, GucSource source) { return (assign_msglvl(&log_min_messages, newval, doit, source)); } @@ -6614,10 +6766,9 @@ assign_phony_autocommit(bool newval, bool doit, GucSource source) { if (!newval) { - if (doit && source >= PGC_S_INTERACTIVE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SET AUTOCOMMIT TO OFF is no longer supported"))); + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SET AUTOCOMMIT TO OFF is no longer supported"))); return false; } return true; @@ -6628,23 +6779,16 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) { /* * Check syntax. newval must be a comma separated list of identifiers. - * Whitespace is allowed but skipped. + * Whitespace is allowed but removed from the result. */ bool hasSpaceAfterToken = false; const char *cp = newval; int symLen = 0; - int c; + char c; StringInfoData buf; - /* - * Resetting custom_variable_classes by removing it from the - * configuration file will lead to newval = NULL - */ - if (newval == NULL) - return guc_strdup(ERROR, ""); - initStringInfo(&buf); - while ((c = *cp++) != 0) + while ((c = *cp++) != '\0') { if (isspace((unsigned char) c)) { @@ -6655,12 +6799,12 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) if (c == ',') { - hasSpaceAfterToken = false; - if (symLen > 0) + if (symLen > 0) /* terminate identifier */ { - symLen = 0; appendStringInfoChar(&buf, ','); + symLen = 0; } + hasSpaceAfterToken = false; continue; } @@ -6670,24 +6814,19 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) * Syntax error due to token following space after token or non * alpha numeric character */ - ereport(LOG, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("invalid syntax for \"custom_variable_classes\": \"%s\"", newval))); pfree(buf.data); return NULL; } + appendStringInfoChar(&buf, c); symLen++; - appendStringInfoChar(&buf, (char) c); } /* Remove stray ',' at end */ if (symLen == 0 && buf.len > 0) buf.data[--buf.len] = '\0'; - if (buf.len == 0) - newval = NULL; - else if (doit) - newval = guc_strdup(ERROR, buf.data); + /* GUC wants the result malloc'd */ + newval = guc_strdup(LOG, buf.data); pfree(buf.data); return newval; @@ -6698,9 +6837,12 @@ assign_debug_assertions(bool newval, bool doit, GucSource source) { #ifndef USE_ASSERT_CHECKING if (newval) - ereport(ERROR, + { + ereport(GUC_complaint_elevel(source), (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("assertion checking is not supported by this build"))); + return false; + } #endif return true; } @@ -6710,9 +6852,12 @@ assign_ssl(bool newval, bool doit, GucSource source) { #ifndef USE_SSL if (newval) - ereport(ERROR, + { + ereport(GUC_complaint_elevel(source), (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("SSL is not supported by this build"))); + return false; + } #endif return true; } @@ -6722,12 +6867,11 @@ assign_stage_log_stats(bool newval, bool doit, GucSource source) { if (newval && log_statement_stats) { - if (source >= PGC_S_INTERACTIVE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot enable parameter when \"log_statement_stats\" is true"))); + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot enable parameter when \"log_statement_stats\" is true"))); /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - else if (source != PGC_S_OVERRIDE) + if (source != PGC_S_OVERRIDE) return false; } return true; @@ -6739,14 +6883,13 @@ assign_log_stats(bool newval, bool doit, GucSource source) if (newval && (log_parser_stats || log_planner_stats || log_executor_stats)) { - if (source >= PGC_S_INTERACTIVE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot enable \"log_statement_stats\" when " - "\"log_parser_stats\", \"log_planner_stats\", " - "or \"log_executor_stats\" is true"))); + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot enable \"log_statement_stats\" when " + "\"log_parser_stats\", \"log_planner_stats\", " + "or \"log_executor_stats\" is true"))); /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - else if (source != PGC_S_OVERRIDE) + if (source != PGC_S_OVERRIDE) return false; } return true; @@ -6758,12 +6901,11 @@ assign_transaction_read_only(bool newval, bool doit, GucSource source) /* Can't go to r/w mode inside a r/o transaction */ if (newval == false && XactReadOnly && IsSubTransaction()) { - if (source >= PGC_S_INTERACTIVE) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot set transaction read-write mode inside a read-only transaction"))); + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot set transaction read-write mode inside a read-only transaction"))); /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - else if (source != PGC_S_OVERRIDE) + if (source != PGC_S_OVERRIDE) return false; } return true; @@ -6841,7 +6983,7 @@ assign_timezone_abbreviations(const char *newval, bool doit, GucSource source) * and we use WARNING message level. */ if (source == PGC_S_FILE) - elevel = IsUnderPostmaster ? DEBUG2 : LOG; + elevel = IsUnderPostmaster ? DEBUG3 : LOG; else elevel = WARNING; if (!load_tzoffsets(newval, doit, elevel)) @@ -6902,6 +7044,15 @@ assign_xmloption(const char *newval, bool doit, GucSource source) return newval; } +static const char * +show_archive_command(void) +{ + if (XLogArchiveMode) + return XLogArchiveCommand; + else + return "(disabled)"; +} + static bool assign_tcp_keepalives_idle(int newval, bool doit, GucSource source) {