aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/misc
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/misc')
-rw-r--r--src/backend/utils/misc/Makefile19
-rw-r--r--src/backend/utils/misc/guc-file.l282
-rw-r--r--src/backend/utils/misc/guc.c692
-rw-r--r--src/backend/utils/misc/trace.c511
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:
- */