mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
The heralded `Grand Unified Configuration scheme' (GUC)
That means you can now set your options in either or all of $PGDATA/configuration, some postmaster option (--enable-fsync=off), or set a SET command. The list of options is in backend/utils/misc/guc.c, documentation will be written post haste. pg_options is gone, so is that pq_geqo config file. Also removed were backend -K, -Q, and -T options (no longer applicable, although -d0 does the same as -Q). Added to configure an --enable-syslog option. changed all callers from TPRINTF to elog(DEBUG)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
282
src/backend/utils/misc/guc-file.l
Normal file
282
src/backend/utils/misc/guc-file.l
Normal file
@@ -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;
|
||||
}
|
||||
692
src/backend/utils/misc/guc.c
Normal file
692
src/backend/utils/misc/guc.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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:
|
||||
*/
|
||||
Reference in New Issue
Block a user