/*-------------------------------------------------------------------- * guc.c * * Support for grand unified configuration scheme, including SET * command, configuration file, and command line options. * * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.2 2000/06/04 15:06:30 petere Exp $ * * Copyright 2000 by PostgreSQL Global Development Group * Written by Peter Eisentraut . *-------------------------------------------------------------------- */ #include "postgres.h" #include #include #include #include #include "utils/guc.h" #include "access/transam.h" #include "commands/async.h" #include "miscadmin.h" #include "optimizer/cost.h" #include "optimizer/geqo.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" #include "parser/parse_expr.h" #include "storage/fd.h" #include "storage/lock.h" #include "storage/proc.h" #include "storage/spin.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/elog.h" /* XXX should be in a header file */ extern bool Log_connections; /* * Debugging options */ bool Debug_print_query = false; bool Debug_print_plan = false; bool Debug_print_parse = false; bool Debug_print_rewritten = false; bool Debug_pretty_print = false; bool Show_parser_stats = false; bool Show_planner_stats = false; bool Show_executor_stats = false; bool Show_query_stats = false; /* this is sort of all three above together */ bool Show_btree_build_stats = false; enum config_type { PGC_NONE = 0, PGC_BOOL, PGC_INT, PGC_REAL, PGC_STRING }; struct config_generic { const char *name; GucContext context; void *variable; }; struct config_bool { const char *name; GucContext context; bool *variable; bool default_val; }; struct config_int { const char *name; GucContext context; int *variable; int default_val; int min; int max; }; struct config_real { const char *name; GucContext context; double *variable; double default_val; double min; double max; }; /* * String value options are allocated with strdup, not with the * pstrdup/palloc mechanisms. That is because configuration settings * are already in place before the memory subsystem is up. It would * perhaps be an idea to change that sometime. */ struct config_string { const char *name; GucContext context; char *variable; const char *default_val; bool (*parse_hook)(const char *); }; /******** option names follow ********/ static struct config_bool ConfigureNamesBool[] = { {"enable_seqscan", PGC_USERSET, &enable_seqscan, true}, {"enable_indexscan", PGC_USERSET, &enable_indexscan, true}, {"enable_tidscan", PGC_USERSET, &enable_tidscan, true}, {"enable_sort", PGC_USERSET, &enable_sort, true}, {"enable_nestloop", PGC_USERSET, &enable_nestloop, true}, {"enable_mergejoin", PGC_USERSET, &enable_mergejoin, true}, {"enable_hashjoin", PGC_USERSET, &enable_hashjoin, true}, {"ksqo", PGC_USERSET, &_use_keyset_query_optimizer, false}, {"geqo", PGC_USERSET, &enable_geqo, true}, {"net_server", PGC_POSTMASTER, &NetServer, false}, {"fsync", PGC_BACKEND, &enableFsync, true}, {"log_connections", PGC_POSTMASTER, &Log_connections, false}, {"log_timestamp", PGC_BACKEND, &Log_timestamp, false}, {"log_pid", PGC_BACKEND, &Log_pid, false}, {"debug_print_query", PGC_SUSET, &Debug_print_query, false}, {"debug_print_parse", PGC_SUSET, &Debug_print_parse, false}, {"debug_print_rewritten", PGC_SUSET, &Debug_print_rewritten, false}, {"debug_print_plan", PGC_SUSET, &Debug_print_plan, false}, {"debug_pretty_print", PGC_SUSET, &Debug_pretty_print, false}, {"show_parser_stats", PGC_SUSET, &Show_parser_stats, false}, {"show_planner_stats", PGC_SUSET, &Show_planner_stats, false}, {"show_executor_stats", PGC_SUSET, &Show_executor_stats, false}, {"show_query_stats", PGC_SUSET, &Show_query_stats, false}, #ifdef BTREE_BUILD_STATS {"show_btree_build_stats", PGC_SUSET, &Show_btree_build_stats, false}, #endif {"trace_notify", PGC_SUSET, &Trace_notify, false}, #ifdef LOCK_DEBUG {"trace_locks", PGC_SUSET, &Trace_locks, false}, {"trace_userlocks", PGC_SUSET, &Trace_userlocks, false}, {"trace_spinlocks", PGC_SUSET, &Trace_spinlocks, false}, {"debug_deadlocks", PGC_SUSET, &Debug_deadlocks, false}, #endif {"hostlookup", PGC_POSTMASTER, &HostnameLookup, false}, {"showportnumber", PGC_POSTMASTER, &ShowPortNumber, false}, {NULL, 0, NULL, false} }; static struct config_int ConfigureNamesInt[] = { {"geqo_rels", PGC_USERSET, &geqo_rels, DEFAULT_GEQO_RELS, 2, INT_MAX}, {"geqo_pool_size", PGC_USERSET, &Geqo_pool_size, DEFAULT_GEQO_POOL_SIZE, 0, MAX_GEQO_POOL_SIZE}, {"geqo_effort", PGC_USERSET, &Geqo_effort, 1, 1, INT_MAX}, {"geqo_generations", PGC_USERSET, &Geqo_generations, 0, 0, INT_MAX}, {"geqo_random_seed", PGC_USERSET, &Geqo_random_seed, -1, INT_MIN, INT_MAX}, {"deadlock_timeout", PGC_POSTMASTER, &DeadlockTimeout, 1000, 0, INT_MAX}, #ifdef ENABLE_SYSLOG {"syslog", PGC_POSTMASTER, &Use_syslog, 0, 0, 2}, #endif /* * 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. */ {"max_backends", PGC_POSTMASTER, &MaxBackends, DEF_MAXBACKENDS, 1, MAXBACKENDS}, {"shmem_buffers", PGC_POSTMASTER, &NBuffers, DEF_NBUFFERS, 16, INT_MAX}, {"port", PGC_POSTMASTER, &PostPortName, DEF_PGPORT, 1, 65535}, /* XXX Is this really changeable at runtime? */ {"sort_mem", PGC_SUSET, &SortMem, 512, 1, INT_MAX}, {"debug_level", PGC_SUSET, &DebugLvl, 0, 0, 16}, #ifdef LOCK_DEBUG {"trace_lock_oidmin", PGC_SUSET, &Trace_lock_oidmin, BootstrapObjectIdData, 1, INT_MAX}, {"trace_lock_table", PGC_SUSET, &Trace_lock_table, 0, 0, INT_MAX}, #endif {"max_expr_depth", PGC_USERSET, &max_expr_depth, DEFAULT_MAX_EXPR_DEPTH, 10, INT_MAX}, {NULL, 0, NULL, 0, 0, 0} }; static struct config_real ConfigureNamesReal[] = { {"effective_cache_size", PGC_USERSET, &effective_cache_size, DEFAULT_EFFECTIVE_CACHE_SIZE, 0, DBL_MAX}, {"random_page_cost", PGC_USERSET, &random_page_cost, DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX}, {"cpu_tuple_cost", PGC_USERSET, &cpu_tuple_cost, DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX}, {"cpu_index_tuple_cost", PGC_USERSET, &cpu_index_tuple_cost, DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX}, {"cpu_operator_cost", PGC_USERSET, &cpu_operator_cost, DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX}, {"geqo_selection_bias", PGC_USERSET, &Geqo_selection_bias, DEFAULT_GEQO_SELECTION_BIAS, MIN_GEQO_SELECTION_BIAS, MAX_GEQO_SELECTION_BIAS}, {NULL, 0, NULL, 0.0, 0.0, 0.0} }; static struct config_string ConfigureNamesString[] = { /* none so far */ {NULL, 0, NULL, NULL, NULL} }; /******** end of options list ********/ /* * Look up option NAME. If it exists, return it's data type, else * PGC_NONE (zero). If record is not NULL, store the description of * the option there. */ static enum config_type find_option(const char * name, struct config_generic ** record) { int i; Assert(name); for (i = 0; ConfigureNamesBool[i].name; i++) if (strcasecmp(ConfigureNamesBool[i].name, name)==0) { if (record) *record = (struct config_generic *)&ConfigureNamesBool[i]; return PGC_BOOL; } for (i = 0; ConfigureNamesInt[i].name; i++) if (strcasecmp(ConfigureNamesInt[i].name, name)==0) { if (record) *record = (struct config_generic *)&ConfigureNamesInt[i]; return PGC_INT; } for (i = 0; ConfigureNamesReal[i].name; i++) if (strcasecmp(ConfigureNamesReal[i].name, name)==0) { if (record) *record = (struct config_generic *)&ConfigureNamesReal[i]; return PGC_REAL; } for (i = 0; ConfigureNamesString[i].name; i++) if (strcasecmp(ConfigureNamesString[i].name, name)==0) { if (record) *record = (struct config_generic *)&ConfigureNamesString[i]; return PGC_REAL; } return PGC_NONE; } /* * Reset all options to their specified default values. Should only be * called at program startup. */ void ResetAllOptions(void) { int i; for (i = 0; ConfigureNamesBool[i].name; i++) *(ConfigureNamesBool[i].variable) = ConfigureNamesBool[i].default_val; for (i = 0; ConfigureNamesInt[i].name; i++) *(ConfigureNamesInt[i].variable) = ConfigureNamesInt[i].default_val; for (i = 0; ConfigureNamesReal[i].name; i++) *(ConfigureNamesReal[i].variable) = ConfigureNamesReal[i].default_val; for (i = 0; ConfigureNamesString[i].name; i++) { char * str = NULL; if (ConfigureNamesString[i].default_val) { str = strdup(ConfigureNamesString[i].default_val); if (str == NULL) elog(ERROR, "out of memory"); } ConfigureNamesString[i].variable = str; } } /* * Try to interpret value as boolean value. Valid values are: true, * false, yes, no, on, off, 1, 0. If the string parses okay, return * true, else false. If result is not NULL, return the parsing result * there. */ static bool parse_bool(const char * value, bool * result) { size_t len = strlen(value); if (strncasecmp(value, "true", len)==0) { if (result) *result = true; } else if (strncasecmp(value, "false", len)==0) { if (result) *result = false; } else if (strncasecmp(value, "yes", len)==0) { if (result) *result = true; } else if (strncasecmp(value, "no", len)==0) { if (result) *result = false; } else if (strcasecmp(value, "on")==0) { if (result) *result = true; } else if (strcasecmp(value, "off")==0) { if (result) *result = false; } else if (strcasecmp(value, "1")==0) { if (result) *result = true; } else if (strcasecmp(value, "0")==0) { if (result) *result = false; } else return false; return true; } /* * Try to parse value as an integer. The accepted formats are the * usual decimal, octal, or hexadecimal formats. If the string parses * okay, return true, else false. If result is not NULL, return the * value there. */ static bool parse_int(const char * value, int * result) { long val; char * endptr; errno = 0; val = strtol(value, &endptr, 0); if (endptr == value || *endptr != '\0' || errno == ERANGE) return false; if (result) *result = (int)val; return true; } /* * Try to parse value as a floating point constant in the usual * format. If the value parsed okay return true, else false. If * result is not NULL, return the semantic value there. */ static bool parse_real(const char * value, double * result) { double val; char * endptr; errno = 0; val = strtod(value, &endptr); if (endptr == value || *endptr != '\0' || errno == ERANGE) return false; if (result) *result = val; return true; } /* * Sets option `name' to given value. The value should be a string * which is going to be parsed and converted to the appropriate data * type. Parameter context should indicate in which context this * 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 DoIt 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 * elog(ERROR) is thrown *unless* this is called as part of the * configuration file re-read in the SIGHUP handler, in which case we * simply write the error message via elog(DEBUG) and return false. In * all other cases the function returns true. This is working around * the deficiencies in the elog mechanism, so don't blame me. * * See also SetConfigOption for an external interface. */ bool set_config_option(const char * name, const char * value, GucContext context, bool DoIt) { struct config_generic * record; enum config_type type; int elevel; elevel = (context == PGC_SIGHUP) ? DEBUG : ERROR; type = find_option(name, &record); if (type == PGC_NONE) { elog(elevel, "not a valid option name: %s", name); return false; } if (record->context < context) { /* can't set option right now */ switch (context) { case PGC_USERSET: elog(ERROR, "permission denied"); /*NORETURN*/ case PGC_SUSET: elog(ERROR, "%s can only be set at startup", name); /*NORETURN*/ case PGC_SIGHUP: /* ignore the option */ return true; case PGC_BACKEND: /* ignore; is this the right thing to do? */ return true; default: elog(FATAL, "%s:%d: internal error", __FILE__, __LINE__); /*NORETURN*/ } } switch(type) { case PGC_BOOL: { struct config_bool * conf = (struct config_bool *)record; if (value) { bool boolval; if (!parse_bool(value, &boolval)) { elog(elevel, "expected boolean value for option %s", name); return false; } if (DoIt) *conf->variable = boolval; } else if (DoIt) *conf->variable = conf->default_val; break; } case PGC_INT: { struct config_int * conf = (struct config_int *)record; if (value) { int intval; if (!parse_int(value, &intval)) { elog(elevel, "expected integer value for option %s", name); return false; } if (intval < conf->min || intval > conf->max) { elog(elevel, "value out of permissible range %d .. %d", conf->min, conf->max); return false; } if (DoIt) *conf->variable = intval; } else if (DoIt) *conf->variable = conf->default_val; break; } case PGC_REAL: { struct config_real * conf = (struct config_real *)record; if (value) { double dval; if (!parse_real(value, &dval)) { elog(elevel, "expected real number for option %s", name); return false; } if (dval < conf->min || dval > conf->max) { elog(elevel, "value out of permissible range %g .. %g", conf->min, conf->max); return false; } if (DoIt) *conf->variable = dval; } else if (DoIt) *conf->variable = conf->default_val; break; } case PGC_STRING: { struct config_string * conf = (struct config_string *)record; if (value) { if (conf->parse_hook && !(conf->parse_hook)(value)) { elog(elevel, "value '%s' not accepted for option %s", value, name); return false; } if (DoIt) { char * str; str = strdup(value); if (str == NULL) { elog(elevel, "out of memory"); return false; } free(conf->variable); conf->variable = str; } } else if (DoIt) { char * str; str = strdup(conf->default_val); if (str == NULL) { elog(elevel, "out of memory"); return false; } free(conf->variable); conf->variable = str; } break; } default: ; } return true; } /* * Set a config option to the given value. See also set_config_option, * this is just the wrapper to be called from the outside. */ void SetConfigOption(const char * name, const char * value, GucContext context) { (void)set_config_option(name, value, context, true); } /* * This is more or less the SHOW command. It returns a string with the * value of the option `name'. If the option doesn't exist, throw an * elog and don't return. issuper should be true if and only if the * current user is a superuser. Normal users don't have read * permission on all options. * * The string is *not* allocated for modification and is really only * valid until the next call to configuration related functions. */ const char * GetConfigOption(const char * name, bool issuper) { struct config_generic * record; static char buffer[256]; enum config_type opttype; opttype = find_option(name, &record); if (opttype == PGC_NONE) elog(ERROR, "not a valid option name: %s", name); if (record->context < PGC_USERSET && !issuper) elog(ERROR, "permission denied"); switch(opttype) { case PGC_BOOL: return *((struct config_bool *)record)->variable ? "true" : "false"; case PGC_INT: snprintf(buffer, 256, "%d", *((struct config_int *)record)->variable); return buffer; case PGC_REAL: snprintf(buffer, 256, "%g", *((struct config_real *)record)->variable); return buffer; case PGC_STRING: return ((struct config_string *)record)->variable; default: ; } return NULL; }