diff options
Diffstat (limited to 'src/backend/utils/misc')
-rw-r--r-- | src/backend/utils/misc/Makefile | 19 | ||||
-rw-r--r-- | src/backend/utils/misc/guc-file.l | 282 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 692 | ||||
-rw-r--r-- | src/backend/utils/misc/trace.c | 511 |
4 files changed, 989 insertions, 515 deletions
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index 0afdb0a63bc..de0a0487351 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -4,27 +4,38 @@ # Makefile for utils/misc # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/utils/misc/Makefile,v 1.13 2000/05/29 05:45:37 tgl Exp $ +# $Header: /cvsroot/pgsql/src/backend/utils/misc/Makefile,v 1.14 2000/05/31 00:28:34 petere Exp $ # #------------------------------------------------------------------------- SRCDIR = ../../.. include ../../../Makefile.global -OBJS = database.o superuser.o trace.o +OBJS = database.o superuser.o guc.o guc-file.o all: SUBSYS.o SUBSYS.o: $(OBJS) $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS) +.SECONDARY: guc-file.c +.INTERMEDIATE: lex.yy.c + +guc-file.c: lex.yy.c + sed -e 's/lex\.yy\.c/guc-file\.c/g' \ + -e 's/^yy/GUC_yy/g' \ + -e 's/\([^a-zA-Z0-9_]\)yy/\1GUC_yy/g' < $< > $@ + +lex.yy.c: guc-file.l + $(LEX) $(LFLAGS) $< + + depend dep: $(CC) -MM $(CFLAGS) *.c >depend clean: - rm -f SUBSYS.o $(OBJS) + rm -f SUBSYS.o $(OBJS) lex.yy.c ifeq (depend,$(wildcard depend)) include depend endif - diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l new file mode 100644 index 00000000000..2d48ef31ef6 --- /dev/null +++ b/src/backend/utils/misc/guc-file.l @@ -0,0 +1,282 @@ +/* -*-pgsql-c-*- */ +/* + * Scanner for the configuration file + * + * Copyright 2000 by PostgreSQL Global Development Group + * + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc-file.l,v 1.1 2000/05/31 00:28:34 petere Exp $ + */ + +%{ + +#include "postgres.h" + +#include <string.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> + +#include "miscadmin.h" +#include "storage/fd.h" +#include "utils/elog.h" +#include "utils/guc.h" + +static unsigned ConfigFileLineno; + +enum { + GUC_ID = 1, + GUC_STRING = 2, + GUC_INTEGER = 3, + GUC_REAL = 4, + GUC_EQUALS = 5, + GUC_EOL = 99, + GUC_ERROR = 100, +}; + +#if defined(yywrap) +#undef yywrap +#endif /* yywrap */ + +#define YY_USER_INIT (ConfigFileLineno = 1) +#define YY_NO_UNPUT + +%} + +SIGN ("-"|"+") +DIGIT [0-9] +HEXDIGIT [0-9a-fA-F] + +INTEGER {SIGN}?({DIGIT}+|0x{HEXDIGIT}+) + +EXPONENT [Ee]{SIGN}?{DIGIT}+ +REAL {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}? + +LETTER [A-Za-z_\200-\377] +LETTER_OR_DIGIT [A-Za-z_0-9\200-\377] + +ID {LETTER}{LETTER_OR_DIGIT}* +/* + * FIXME: This string syntax is nice and all but of course the quotes + * need to be stripped before we can make any use of the string value. + * There is a function in parser/scansup.c that does this but it uses + * palloc and there might be a little more magic needed to get it to + * work right. Now there are no string options, and if there were then + * the unquoted (`ID') tokens should still work. Of course this only + * affects the configuration file. + */ +STRING \'([^'\n]|\\.)*' + +%% + +\n ConfigFileLineno++; return GUC_EOL; +[ \t\r]+ /* eat whitespace */ +#.*$ /* eat comment */ + +{ID} return GUC_ID; +{STRING} return GUC_STRING; +{INTEGER} return GUC_INTEGER; +{REAL} return GUC_REAL; += return GUC_EQUALS; + +. return GUC_ERROR; + +%% + + +struct name_value_pair +{ + char *name; + char *value; + struct name_value_pair *next; +}; + + + +/* + * Free a list of name/value pairs, including the names and the values + */ +static void +free_name_value_list(struct name_value_pair * list) +{ + struct name_value_pair *item; + + item = list; + while (item) + { + struct name_value_pair *save; + + save = item->next; + free(item->name); + free(item->value); + free(item); + item = save; + } +} + + +/* + * Official function to read and process the configuration file. The + * parameter indicates in what context the file is being read + * (postmaster startup, backend startup, or SIGHUP). All options + * mentioned in the configuration file are set to new values. This + * function does not return if an error occurs. If an error occurs, no + * values will be changed. + */ +void +ProcessConfigFile(unsigned int context) +{ + int token, parse_state; + char *opt_name, *opt_value; + char *filename; + struct stat stat_buf; + struct name_value_pair *item, *head, *tail; + int elevel; + FILE * fp; + + Assert(context == PGC_POSTMASTER || context == PGC_BACKEND || context == PGC_SIGHUP); + Assert(DataDir); + elevel = (context == PGC_SIGHUP) ? DEBUG : ERROR; + + /* + * Open file + */ + filename = malloc(strlen(DataDir) + 16); + if (filename == NULL) + { + elog(elevel, "out of memory"); + return; + } + sprintf(filename, "%s/configuration", DataDir); + + fp = AllocateFile(filename, "r"); + if (!fp) + { + free(filename); + /* File not found is fine */ + if (errno != ENOENT) + elog(elevel, "could not read configuration: %s", strerror(errno)); + return; + } + + /* + * Check if the file is group or world writeable. If so, reject. + */ + if (fstat(fileno(fp), &stat_buf) == -1) + { + FreeFile(fp); + free(filename); + elog(elevel, "could not stat configuration file: %s", strerror(errno)); + return; + } + + if (stat_buf.st_mode & (S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH)) + { + FreeFile(fp); + free(filename); + elog(elevel, "configuration file has wrong permissions"); + return; + } + + /* + * Parse + */ + yyin = fp; + parse_state = 0; + head = tail = NULL; + opt_name = opt_value = NULL; + + while((token = yylex())) + switch(parse_state) + { + case 0: /* no previous input */ + if (token == GUC_EOL) /* empty line */ + continue; + if (token != GUC_ID) + goto parse_error; + opt_name = strdup(yytext); + if (opt_name == NULL) + goto out_of_memory; + parse_state = 1; + break; + + case 1: /* found name */ + /* ignore equals sign */ + if (token == GUC_EQUALS) + token = yylex(); + + if (token != GUC_ID && token != GUC_STRING && token != GUC_INTEGER && token != GUC_REAL) + goto parse_error; + opt_value = strdup(yytext); + if (opt_value == NULL) + goto out_of_memory; + parse_state = 2; + break; + + case 2: /* now we'd like an end of line */ + if (token != GUC_EOL) + goto parse_error; + + /* append to list */ + item = malloc(sizeof *item); + if (item == NULL) + goto out_of_memory; + item->name = opt_name; + item->value = opt_value; + item->next = NULL; + + if (!head) + tail = head = item; + else + { + tail->next = item; + tail = item; + } + + parse_state = 0; + break; + } + + FreeFile(fp); + free(filename); + + /* + * Check if all options are valid + */ + for(item = head; item; item=item->next) + { + if (!set_config_option(item->name, item->value, context, false)) + goto cleanup_exit; + } + + /* If we got here all the options parsed okay. */ + for(item = head; item; item=item->next) + set_config_option(item->name, item->value, context, true); + + cleanup_exit: + free_name_value_list(head); + return; + + parse_error: + FreeFile(fp); + free(filename); + free_name_value_list(head); + elog(elevel, "%s:%u: syntax error (ps:%d, t:%d)", filename, + ConfigFileLineno, parse_state, token); + return; + + out_of_memory: + FreeFile(fp); + free(filename); + free_name_value_list(head); + elog(elevel, "out of memory"); + return; +} + + + +int +yywrap(void) +{ + return 1; +} diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c new file mode 100644 index 00000000000..7382c677028 --- /dev/null +++ b/src/backend/utils/misc/guc.c @@ -0,0 +1,692 @@ +/*-------------------------------------------------------------------- + * 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.1 2000/05/31 00:28:34 petere Exp $ + * + * Copyright 2000 by PostgreSQL Global Development Group + * Written by Peter Eisentraut <peter_e@gmx.net>. + *-------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include <errno.h> +#include <float.h> +#include <limits.h> +#include <unistd.h> + +#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_POSTMASTER, &enableFsync, true}, + + {"log_connections", PGC_POSTMASTER, &Log_connections, 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; +} diff --git a/src/backend/utils/misc/trace.c b/src/backend/utils/misc/trace.c deleted file mode 100644 index 4412923c1e3..00000000000 --- a/src/backend/utils/misc/trace.c +++ /dev/null @@ -1,511 +0,0 @@ -/*------------------------------------------------------------------------- - * - * trace.c - * - * Conditional trace and logging functions. - * - * Massimo Dal Zotto <dz@cs.unitn.it> - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include <unistd.h> -#include <signal.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#ifdef USE_SYSLOG -#include <syslog.h> -#endif - -#include "miscadmin.h" -#include "utils/trace.h" - -#include <ctype.h> - -#ifdef MULTIBYTE -#include "mb/pg_wchar.h" -#endif - -/* - * We could support trace messages of indefinite length, as elog() does, - * but it's probably not worth the trouble. Instead limit trace message - * length to this. - */ -#define TRACEMSG_MAXLEN 4096 - -#ifdef USE_SYSLOG -/* - * Global option to control the use of syslog(3) for logging: - * - * 0 stdout/stderr only - * 1 stdout/stderr + syslog - * 2 syslog only - */ -#define UseSyslog pg_options[OPT_SYSLOG] -#define PG_LOG_FACILITY LOG_LOCAL0 -#define PG_LOG_IDENT "postgres" -#else -#define UseSyslog 0 -#endif - -/* - * Trace option names, must match the constants in trace_opts[]. - */ -static char *opt_names[] = { - "all", /* 0=trace some, 1=trace all, -1=trace - * none */ - "verbose", - "query", - "plan", - "parse", - "rewritten", - "pretty_plan", - "pretty_parse", - "pretty_rewritten", - "parserstats", - "plannerstats", - "executorstats", - "shortlocks", /* currently unused but needed, see lock.c */ - "locks", - "userlocks", - "spinlocks", - "notify", - "malloc", - "palloc", - "lock_debug_oidmin", - "lock_debug_relid", - "lock_read_priority", /* lock priority, see lock.c */ - "deadlock_timeout", /* deadlock timeout, see proc.c */ - "nofsync", /* turn fsync off */ - "syslog", /* use syslog for error messages */ - "hostlookup", /* enable hostname lookup in ps_status */ - "showportnumber", /* show port number in ps_status */ - - /* NUM_PG_OPTIONS *//* must be the last item of enum */ -}; - -/* - * Array of trace flags which can be set or reset independently. - */ -int pg_options[NUM_PG_OPTIONS] = {0}; - -/* - * Print a timestamp and a message to stdout if the trace flag - * indexed by the flag value is set. - */ -int -tprintf(int flag, const char *fmt,...) -{ - va_list ap; - char line[TRACEMSG_MAXLEN + TIMESTAMP_SIZE + 1]; - -#ifdef USE_SYSLOG - int log_level; - -#endif - - if ((flag == TRACE_ALL) || (pg_options[TRACE_ALL] > 0)) - { - /* unconditional trace or trace all option set */ - } - else if (pg_options[TRACE_ALL] == 0) - { - if ((flag < 0) || (flag >= NUM_PG_OPTIONS) || (!pg_options[flag])) - return 0; - } - else if (pg_options[TRACE_ALL] < 0) - return 0; - -#ifdef ELOG_TIMESTAMPS - strcpy(line, tprintf_timestamp()); -#endif - va_start(ap, fmt); - vsnprintf(line + TIMESTAMP_SIZE, TRACEMSG_MAXLEN, fmt, ap); - va_end(ap); - -#ifdef USE_SYSLOG - log_level = ((flag == TRACE_ALL) ? LOG_INFO : LOG_DEBUG); - write_syslog(log_level, line + TIMESTAMP_SIZE); -#endif - - if (UseSyslog <= 1) - { - puts(line); - fflush(stdout); - } - - return 1; -} - -/* - * Print a timestamp and a message to stdout or to syslog. - */ -#ifdef NOT_USED -int -tprintf1(const char *fmt,...) -{ - va_list ap; - char line[TRACEMSG_MAXLEN + TIMESTAMP_SIZE + 1]; - -#ifdef ELOG_TIMESTAMPS - strcpy(line, tprintf_timestamp()); -#endif - va_start(ap, fmt); - vsnprintf(line + TIMESTAMP_SIZE, TRACEMSG_MAXLEN, fmt, ap); - va_end(ap); - -#ifdef USE_SYSLOG - write_syslog(LOG_INFO, line + TIMESTAMP_SIZE); -#endif - - if (UseSyslog <= 1) - { - puts(line); - fflush(stdout); - } - - return 1; -} - -#endif - -/* - * Print a timestamp and a message to stderr. - */ -int -eprintf(const char *fmt,...) -{ - va_list ap; - char line[TRACEMSG_MAXLEN + TIMESTAMP_SIZE + 1]; - -#ifdef ELOG_TIMESTAMPS - strcpy(line, tprintf_timestamp()); -#endif - va_start(ap, fmt); - vsnprintf(line + TIMESTAMP_SIZE, TRACEMSG_MAXLEN, fmt, ap); - va_end(ap); - -#ifdef USE_SYSLOG - write_syslog(LOG_ERR, line + TIMESTAMP_SIZE); -#endif - - if (UseSyslog <= 1) - { - fputs(line, stderr); - fputc('\n', stderr); - fflush(stderr); - } - - return 1; -} - -#ifdef USE_SYSLOG -/* - * Write a message line to syslog if the syslog option is set. - */ -void -write_syslog(int level, char *line) -{ -#ifndef PG_SYSLOG_LIMIT -#define PG_SYSLOG_LIMIT 128 -#endif /* PG_SYSLOG_LIMIT */ - - static int openlog_done = 0; - static char buf[PG_SYSLOG_LIMIT+1]; - static int logid = 0; - - if (UseSyslog >= 1) - { - int len = strlen(line); - int buflen; - int seq = 0; - int l; - int i; - - if (!openlog_done) - { - openlog_done = 1; - openlog(PG_LOG_IDENT, LOG_PID | LOG_NDELAY, PG_LOG_FACILITY); - } - - /* divide into multiple syslog() calls if message is - * too long - */ - if (len > PG_SYSLOG_LIMIT) - { - logid++; - - while (len > 0) - { - strncpy(buf, line, PG_SYSLOG_LIMIT); - buf[PG_SYSLOG_LIMIT] = '\0'; - - l = strlen(buf); -#ifdef MULTIBYTE - /* trim to multibyte letter boundary */ - buflen = pg_mbcliplen(buf, l, l); - buf[buflen] = '\0'; - l = strlen(buf); -#endif - /* already word boundary? */ - if (isspace(line[l]) || line[l] == '\0') - { - buflen = l; - } - else - { - /* try to divide in word boundary */ - i = l - 1; - while(i > 0 && !isspace(buf[i])) - { - i--; - } - if (i <= 0) /* couldn't divide word boundary */ - { - buflen = l; - } - else - { - buflen = i; - buf[i] = '\0'; - } - } - - seq++; - /* - * Here we actually call syslog(). - * For segmented messages, we add logid - * (incremented at each write_syslog call), - * and seq (incremented at each syslog call - * within a write_syslog call). - * This will prevent syslog to surpress - * "same" messages... - */ - syslog(level, "[%d-%d] %s", logid, seq, buf); - line += buflen; - len -= buflen; - } - } - else - { - syslog(level, "%s", line); - } - } -} - -#endif - -#ifdef ELOG_TIMESTAMPS -/* - * Return a timestamp string like "980119.17:25:59.902 [21974] " - */ -char * -tprintf_timestamp() -{ - struct timeval tv; - struct timezone tz = {0, 0}; - struct tm *time; - time_t tm; - static char timestamp[32], - pid[8]; - - gettimeofday(&tv, &tz); - tm = tv.tv_sec; - time = localtime(&tm); - - sprintf(pid, "[%d]", MyProcPid); - sprintf(timestamp, "%02d%02d%02d.%02d:%02d:%02d.%03d %7s ", - time->tm_year % 100, time->tm_mon + 1, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec, - (int) (tv.tv_usec / 1000), pid); - - return timestamp; -} - -#endif - -#ifdef NOT_USED -static int -option_flag(int flag) -{ - if ((flag < 0) || (flag >= NUM_PG_OPTIONS)) - return 0; - return pg_options[flag]; -} - -int -set_option_flag(int flag, int value) -{ - if ((flag < 0) || (flag >= NUM_PG_OPTIONS)) - return -1; - - pg_options[flag] = value; - return value; -} - -#endif - -/* - * Parse an option string like "name,name+,name-,name=value". - * Single options are delimited by ',',space,tab,newline or cr. - * - * If 'secure' is false, the option string came from a remote client via - * connection "debug options" field --- do not obey any requests that - * might potentially be security loopholes. - */ -void -parse_options(char *str, bool secure) -{ - char *s, - *name; - int i, - len, - val, - is_comment; - - Assert((sizeof(opt_names) / sizeof(char *)) == NUM_PG_OPTIONS); - - str = strdup(str); - for (s = str; *s;) - { - is_comment = 0; - name = s; - val = 1; - for (; *s; s++) - { - switch (*s) - { - case '#': - is_comment = 1; - break; - case '=': - *s++ = '\0'; - val = strtol(s, &s, 10); - goto setval; - case '-': - *s++ = '\0'; - val = 0; - goto setval; - case '+': - *s++ = '\0'; - val = 1; - goto setval; - case ' ': - case ',': - case '\t': - case '\n': - case '\r': - *s = ','; - val = 1; - goto setval; - } - } -setval: - for (; *s; s++) - { - if (*s == ',') - { - *s++ = '\0'; - break; - } - } - len = strlen(name); - if (len == 0) - continue; - for (i = 0; i < NUM_PG_OPTIONS; i++) - { - if (strncmp(name, opt_names[i], len) == 0) - { - pg_options[i] = val; - break; - } - } - if (!is_comment && (i >= NUM_PG_OPTIONS)) - fprintf(stderr, "invalid option: %s\n", name); - } - free(str); -} - -#define BUF_SIZE 4096 - -void -read_pg_options(SIGNAL_ARGS) -{ - int fd; - int n; - int verbose; - char buffer[BUF_SIZE]; - char c; - char *s, - *p; - - if (!DataDir) - { - fprintf(stderr, "read_pg_options: DataDir not defined\n"); - return; - } - - snprintf(buffer, BUF_SIZE - 1, "%s/%s", DataDir, "pg_options"); -#ifndef __CYGWIN32__ - if ((fd = open(buffer, O_RDONLY)) < 0) -#else - if ((fd = open(buffer, O_RDONLY | O_BINARY)) < 0) -#endif - return; - - if ((n = read(fd, buffer, BUF_SIZE - 1)) > 0) - { - /* collpse buffer in place removing comments and spaces */ - for (s = buffer, p = buffer, c = '\0'; s < (buffer + n);) - { - switch (*s) - { - case '#': - while ((s < (buffer + n)) && (*s++ != '\n')); - break; - case ' ': - case '\t': - case '\n': - case '\r': - if (c != ',') - c = *p++ = ','; - s++; - break; - default: - c = *p++ = *s++; - break; - } - } - if (c == ',') - p--; - *p = '\0'; - verbose = pg_options[TRACE_VERBOSE]; - parse_options(buffer, true); - verbose |= pg_options[TRACE_VERBOSE]; - if (verbose || postgres_signal_arg == SIGHUP) - tprintf(TRACE_ALL, "read_pg_options: %s", buffer); - } - - close(fd); -} - -void -show_options(void) -{ - int i; - - for (i = 0; i < NUM_PG_OPTIONS; i++) - elog(NOTICE, "%s=%d", opt_names[i], pg_options[i]); -} - -/* - * Local variables: - * tab-width: 4 - * c-indent-level: 4 - * c-basic-offset: 4 - * End: - */ |