mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-30 07:57:20 +02:00
b1577a7c78
accesses versus sequential accesses, a (very crude) estimate of the effects of caching on random page accesses, and cost to evaluate WHERE- clause expressions. Export critical parameters for this model as SET variables. Also, create SET variables for the planner's enable flags (enable_seqscan, enable_indexscan, etc) so that these can be controlled more conveniently than via PGOPTIONS. Planner now estimates both startup cost (cost before retrieving first tuple) and total cost of each path, so it can optimize queries with LIMIT on a reasonable basis by interpolating between these costs. Same facility is a win for EXISTS(...) subqueries and some other cases. Redesign pathkey representation to achieve a major speedup in planning (I saw as much as 5X on a 10-way join); also minor changes in planner to reduce memory consumption by recycling discarded Path nodes and not constructing unnecessary lists. Minor cleanups to display more-plausible costs in some cases in EXPLAIN output. Initdb forced by change in interface to index cost estimation functions.
1119 lines
20 KiB
C
1119 lines
20 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* variable.c
|
|
* Routines for handling of 'SET var TO',
|
|
* 'SHOW var' and 'RESET var' statements.
|
|
*
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.29 2000/02/15 20:49:08 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/xact.h"
|
|
#include "catalog/pg_shadow.h"
|
|
#include "commands/variable.h"
|
|
#include "miscadmin.h"
|
|
#include "optimizer/cost.h"
|
|
#include "optimizer/paths.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/tqual.h"
|
|
#include "utils/trace.h"
|
|
|
|
#ifdef MULTIBYTE
|
|
#include "mb/pg_wchar.h"
|
|
#endif
|
|
|
|
|
|
/* XXX should be in a header file */
|
|
extern bool _use_keyset_query_optimizer;
|
|
|
|
|
|
static bool show_date(void);
|
|
static bool reset_date(void);
|
|
static bool parse_date(const char *);
|
|
static bool show_timezone(void);
|
|
static bool reset_timezone(void);
|
|
static bool parse_timezone(const char *);
|
|
static bool show_effective_cache_size(void);
|
|
static bool reset_effective_cache_size(void);
|
|
static bool parse_effective_cache_size(const char *);
|
|
static bool show_random_page_cost(void);
|
|
static bool reset_random_page_cost(void);
|
|
static bool parse_random_page_cost(const char *);
|
|
static bool show_cpu_tuple_cost(void);
|
|
static bool reset_cpu_tuple_cost(void);
|
|
static bool parse_cpu_tuple_cost(const char *);
|
|
static bool show_cpu_index_tuple_cost(void);
|
|
static bool reset_cpu_index_tuple_cost(void);
|
|
static bool parse_cpu_index_tuple_cost(const char *);
|
|
static bool show_cpu_operator_cost(void);
|
|
static bool reset_cpu_operator_cost(void);
|
|
static bool parse_cpu_operator_cost(const char *);
|
|
static bool reset_enable_seqscan(void);
|
|
static bool show_enable_seqscan(void);
|
|
static bool parse_enable_seqscan(const char *);
|
|
static bool reset_enable_indexscan(void);
|
|
static bool show_enable_indexscan(void);
|
|
static bool parse_enable_indexscan(const char *);
|
|
static bool reset_enable_tidscan(void);
|
|
static bool show_enable_tidscan(void);
|
|
static bool parse_enable_tidscan(const char *);
|
|
static bool reset_enable_sort(void);
|
|
static bool show_enable_sort(void);
|
|
static bool parse_enable_sort(const char *);
|
|
static bool reset_enable_nestloop(void);
|
|
static bool show_enable_nestloop(void);
|
|
static bool parse_enable_nestloop(const char *);
|
|
static bool reset_enable_mergejoin(void);
|
|
static bool show_enable_mergejoin(void);
|
|
static bool parse_enable_mergejoin(const char *);
|
|
static bool reset_enable_hashjoin(void);
|
|
static bool show_enable_hashjoin(void);
|
|
static bool parse_enable_hashjoin(const char *);
|
|
static bool reset_geqo(void);
|
|
static bool show_geqo(void);
|
|
static bool parse_geqo(const char *);
|
|
static bool show_ksqo(void);
|
|
static bool reset_ksqo(void);
|
|
static bool parse_ksqo(const char *);
|
|
static bool show_XactIsoLevel(void);
|
|
static bool reset_XactIsoLevel(void);
|
|
static bool parse_XactIsoLevel(const char *);
|
|
|
|
/*
|
|
*
|
|
* Get_Token
|
|
*
|
|
*/
|
|
static const char *
|
|
get_token(char **tok, char **val, const char *str)
|
|
{
|
|
const char *start;
|
|
int len = 0;
|
|
|
|
*tok = NULL;
|
|
if (val != NULL)
|
|
*val = NULL;
|
|
|
|
if (!(*str))
|
|
return NULL;
|
|
|
|
/* skip white spaces */
|
|
while (isspace(*str))
|
|
str++;
|
|
if (*str == ',' || *str == '=')
|
|
elog(ERROR, "Syntax error near (%s): empty setting", str);
|
|
|
|
/* end of string? then return NULL */
|
|
if (!(*str))
|
|
return NULL;
|
|
|
|
/* OK, at beginning of non-NULL string... */
|
|
start = str;
|
|
|
|
/*
|
|
* count chars in token until we hit white space or comma or '=' or
|
|
* end of string
|
|
*/
|
|
while (*str && (!isspace(*str))
|
|
&& *str != ',' && *str != '=')
|
|
{
|
|
str++;
|
|
len++;
|
|
}
|
|
|
|
*tok = (char *) palloc(len + 1);
|
|
StrNCpy(*tok, start, len + 1);
|
|
|
|
/* skip white spaces */
|
|
while (isspace(*str))
|
|
str++;
|
|
|
|
/* end of string? */
|
|
if (!(*str))
|
|
{
|
|
return str;
|
|
|
|
/* delimiter? */
|
|
}
|
|
else if (*str == ',')
|
|
{
|
|
return ++str;
|
|
|
|
}
|
|
else if ((val == NULL) || (*str != '='))
|
|
{
|
|
elog(ERROR, "Syntax error near (%s)", str);
|
|
};
|
|
|
|
str++; /* '=': get value */
|
|
len = 0;
|
|
|
|
/* skip white spaces */
|
|
while (isspace(*str))
|
|
str++;
|
|
|
|
if (*str == ',' || !(*str))
|
|
elog(ERROR, "Syntax error near (=%s)", str);
|
|
|
|
start = str;
|
|
|
|
/*
|
|
* count chars in token's value until we hit white space or comma or
|
|
* end of string
|
|
*/
|
|
while (*str && (!isspace(*str)) && *str != ',')
|
|
{
|
|
str++;
|
|
len++;
|
|
}
|
|
|
|
*val = (char *) palloc(len + 1);
|
|
StrNCpy(*val, start, len + 1);
|
|
|
|
/* skip white spaces */
|
|
while (isspace(*str))
|
|
str++;
|
|
|
|
if (!(*str))
|
|
return NULL;
|
|
if (*str == ',')
|
|
return ++str;
|
|
|
|
elog(ERROR, "Syntax error near (%s)", str);
|
|
|
|
return str;
|
|
}
|
|
|
|
/*
|
|
* Generic parse routine for boolean ON/OFF variables
|
|
*/
|
|
static bool
|
|
parse_boolean_var(const char *value,
|
|
bool *variable, const char *varname, bool defaultval)
|
|
{
|
|
if (value == NULL)
|
|
{
|
|
*variable = defaultval;
|
|
return TRUE;
|
|
}
|
|
|
|
if (strcasecmp(value, "on") == 0)
|
|
*variable = true;
|
|
else if (strcasecmp(value, "off") == 0)
|
|
*variable = false;
|
|
else
|
|
elog(ERROR, "Bad value for %s (%s)", varname, value);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ENABLE_SEQSCAN
|
|
*/
|
|
static bool
|
|
parse_enable_seqscan(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &enable_seqscan,
|
|
"ENABLE_SEQSCAN", true);
|
|
}
|
|
|
|
static bool
|
|
show_enable_seqscan()
|
|
{
|
|
elog(NOTICE, "ENABLE_SEQSCAN is %s",
|
|
enable_seqscan ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_enable_seqscan()
|
|
{
|
|
enable_seqscan = true;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ENABLE_INDEXSCAN
|
|
*/
|
|
static bool
|
|
parse_enable_indexscan(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &enable_indexscan,
|
|
"ENABLE_INDEXSCAN", true);
|
|
}
|
|
|
|
static bool
|
|
show_enable_indexscan()
|
|
{
|
|
elog(NOTICE, "ENABLE_INDEXSCAN is %s",
|
|
enable_indexscan ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_enable_indexscan()
|
|
{
|
|
enable_indexscan = true;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ENABLE_TIDSCAN
|
|
*/
|
|
static bool
|
|
parse_enable_tidscan(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &enable_tidscan,
|
|
"ENABLE_TIDSCAN", true);
|
|
}
|
|
|
|
static bool
|
|
show_enable_tidscan()
|
|
{
|
|
elog(NOTICE, "ENABLE_TIDSCAN is %s",
|
|
enable_tidscan ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_enable_tidscan()
|
|
{
|
|
enable_tidscan = true;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ENABLE_SORT
|
|
*/
|
|
static bool
|
|
parse_enable_sort(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &enable_sort,
|
|
"ENABLE_SORT", true);
|
|
}
|
|
|
|
static bool
|
|
show_enable_sort()
|
|
{
|
|
elog(NOTICE, "ENABLE_SORT is %s",
|
|
enable_sort ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_enable_sort()
|
|
{
|
|
enable_sort = true;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ENABLE_NESTLOOP
|
|
*/
|
|
static bool
|
|
parse_enable_nestloop(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &enable_nestloop,
|
|
"ENABLE_NESTLOOP", true);
|
|
}
|
|
|
|
static bool
|
|
show_enable_nestloop()
|
|
{
|
|
elog(NOTICE, "ENABLE_NESTLOOP is %s",
|
|
enable_nestloop ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_enable_nestloop()
|
|
{
|
|
enable_nestloop = true;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ENABLE_MERGEJOIN
|
|
*/
|
|
static bool
|
|
parse_enable_mergejoin(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &enable_mergejoin,
|
|
"ENABLE_MERGEJOIN", true);
|
|
}
|
|
|
|
static bool
|
|
show_enable_mergejoin()
|
|
{
|
|
elog(NOTICE, "ENABLE_MERGEJOIN is %s",
|
|
enable_mergejoin ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_enable_mergejoin()
|
|
{
|
|
enable_mergejoin = true;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* ENABLE_HASHJOIN
|
|
*/
|
|
static bool
|
|
parse_enable_hashjoin(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &enable_hashjoin,
|
|
"ENABLE_HASHJOIN", true);
|
|
}
|
|
|
|
static bool
|
|
show_enable_hashjoin()
|
|
{
|
|
elog(NOTICE, "ENABLE_HASHJOIN is %s",
|
|
enable_hashjoin ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_enable_hashjoin()
|
|
{
|
|
enable_hashjoin = true;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* GEQO
|
|
*
|
|
*/
|
|
static bool
|
|
parse_geqo(const char *value)
|
|
{
|
|
const char *rest;
|
|
char *tok,
|
|
*val;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_geqo();
|
|
return TRUE;
|
|
}
|
|
|
|
rest = get_token(&tok, &val, value);
|
|
if (tok == NULL)
|
|
elog(ERROR, "Value undefined");
|
|
|
|
if ((rest) && (*rest != '\0'))
|
|
elog(ERROR, "Unable to parse '%s'", value);
|
|
|
|
if (strcasecmp(tok, "on") == 0)
|
|
{
|
|
int new_geqo_rels = GEQO_RELS;
|
|
|
|
if (val != NULL)
|
|
{
|
|
new_geqo_rels = pg_atoi(val, sizeof(int), '\0');
|
|
if (new_geqo_rels <= 1)
|
|
elog(ERROR, "Bad value for # of relations (%s)", val);
|
|
pfree(val);
|
|
}
|
|
enable_geqo = true;
|
|
geqo_rels = new_geqo_rels;
|
|
}
|
|
else if (strcasecmp(tok, "off") == 0)
|
|
{
|
|
if ((val != NULL) && (*val != '\0'))
|
|
elog(ERROR, "%s does not allow a parameter", tok);
|
|
enable_geqo = false;
|
|
}
|
|
else
|
|
elog(ERROR, "Bad value for GEQO (%s)", value);
|
|
|
|
pfree(tok);
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_geqo()
|
|
{
|
|
if (enable_geqo)
|
|
elog(NOTICE, "GEQO is ON beginning with %d relations", geqo_rels);
|
|
else
|
|
elog(NOTICE, "GEQO is OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_geqo(void)
|
|
{
|
|
#ifdef GEQO
|
|
enable_geqo = true;
|
|
#else
|
|
enable_geqo = false;
|
|
#endif
|
|
geqo_rels = GEQO_RELS;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* EFFECTIVE_CACHE_SIZE
|
|
*/
|
|
static bool
|
|
parse_effective_cache_size(const char *value)
|
|
{
|
|
float64 res;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_effective_cache_size();
|
|
return TRUE;
|
|
}
|
|
|
|
res = float8in((char *) value);
|
|
effective_cache_size = *res;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_effective_cache_size()
|
|
{
|
|
elog(NOTICE, "EFFECTIVE_CACHE_SIZE is %g (%dK pages)",
|
|
effective_cache_size, BLCKSZ/1024);
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_effective_cache_size()
|
|
{
|
|
effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* RANDOM_PAGE_COST
|
|
*/
|
|
static bool
|
|
parse_random_page_cost(const char *value)
|
|
{
|
|
float64 res;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_random_page_cost();
|
|
return TRUE;
|
|
}
|
|
|
|
res = float8in((char *) value);
|
|
random_page_cost = *res;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_random_page_cost()
|
|
{
|
|
elog(NOTICE, "RANDOM_PAGE_COST is %g", random_page_cost);
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_random_page_cost()
|
|
{
|
|
random_page_cost = DEFAULT_RANDOM_PAGE_COST;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CPU_TUPLE_COST
|
|
*/
|
|
static bool
|
|
parse_cpu_tuple_cost(const char *value)
|
|
{
|
|
float64 res;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_cpu_tuple_cost();
|
|
return TRUE;
|
|
}
|
|
|
|
res = float8in((char *) value);
|
|
cpu_tuple_cost = *res;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_cpu_tuple_cost()
|
|
{
|
|
elog(NOTICE, "CPU_TUPLE_COST is %g", cpu_tuple_cost);
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_cpu_tuple_cost()
|
|
{
|
|
cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CPU_INDEX_TUPLE_COST
|
|
*/
|
|
static bool
|
|
parse_cpu_index_tuple_cost(const char *value)
|
|
{
|
|
float64 res;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_cpu_index_tuple_cost();
|
|
return TRUE;
|
|
}
|
|
|
|
res = float8in((char *) value);
|
|
cpu_index_tuple_cost = *res;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_cpu_index_tuple_cost()
|
|
{
|
|
elog(NOTICE, "CPU_INDEX_TUPLE_COST is %g", cpu_index_tuple_cost);
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_cpu_index_tuple_cost()
|
|
{
|
|
cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CPU_OPERATOR_COST
|
|
*/
|
|
static bool
|
|
parse_cpu_operator_cost(const char *value)
|
|
{
|
|
float64 res;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_cpu_operator_cost();
|
|
return TRUE;
|
|
}
|
|
|
|
res = float8in((char *) value);
|
|
cpu_operator_cost = *res;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_cpu_operator_cost()
|
|
{
|
|
elog(NOTICE, "CPU_OPERATOR_COST is %g", cpu_operator_cost);
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_cpu_operator_cost()
|
|
{
|
|
cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* DATE_STYLE
|
|
*
|
|
*/
|
|
static bool
|
|
parse_date(const char *value)
|
|
{
|
|
char *tok;
|
|
int dcnt = 0,
|
|
ecnt = 0;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_date();
|
|
return TRUE;
|
|
}
|
|
|
|
while ((value = get_token(&tok, NULL, value)) != 0)
|
|
{
|
|
/* Ugh. Somebody ought to write a table driven version -- mjl */
|
|
|
|
if (!strcasecmp(tok, "ISO"))
|
|
{
|
|
DateStyle = USE_ISO_DATES;
|
|
dcnt++;
|
|
}
|
|
else if (!strcasecmp(tok, "SQL"))
|
|
{
|
|
DateStyle = USE_SQL_DATES;
|
|
dcnt++;
|
|
}
|
|
else if (!strcasecmp(tok, "POSTGRES"))
|
|
{
|
|
DateStyle = USE_POSTGRES_DATES;
|
|
dcnt++;
|
|
}
|
|
else if (!strcasecmp(tok, "GERMAN"))
|
|
{
|
|
DateStyle = USE_GERMAN_DATES;
|
|
dcnt++;
|
|
EuroDates = TRUE;
|
|
if ((ecnt > 0) && (!EuroDates))
|
|
ecnt++;
|
|
}
|
|
else if (!strncasecmp(tok, "EURO", 4))
|
|
{
|
|
EuroDates = TRUE;
|
|
if ((dcnt <= 0) || (DateStyle != USE_GERMAN_DATES))
|
|
ecnt++;
|
|
}
|
|
else if ((!strcasecmp(tok, "US"))
|
|
|| (!strncasecmp(tok, "NONEURO", 7)))
|
|
{
|
|
EuroDates = FALSE;
|
|
if ((dcnt <= 0) || (DateStyle == USE_GERMAN_DATES))
|
|
ecnt++;
|
|
}
|
|
else if (!strcasecmp(tok, "DEFAULT"))
|
|
{
|
|
DateStyle = USE_POSTGRES_DATES;
|
|
EuroDates = FALSE;
|
|
ecnt++;
|
|
}
|
|
else
|
|
elog(ERROR, "Bad value for date style (%s)", tok);
|
|
pfree(tok);
|
|
}
|
|
|
|
if (dcnt > 1 || ecnt > 1)
|
|
elog(NOTICE, "Conflicting settings for date");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_date()
|
|
{
|
|
char buf[64];
|
|
|
|
strcpy(buf, "DateStyle is ");
|
|
switch (DateStyle)
|
|
{
|
|
case USE_ISO_DATES:
|
|
strcat(buf, "ISO");
|
|
break;
|
|
case USE_SQL_DATES:
|
|
strcat(buf, "SQL");
|
|
break;
|
|
case USE_GERMAN_DATES:
|
|
strcat(buf, "German");
|
|
break;
|
|
default:
|
|
strcat(buf, "Postgres");
|
|
break;
|
|
};
|
|
strcat(buf, " with ");
|
|
strcat(buf, ((EuroDates) ? "European" : "US (NonEuropean)"));
|
|
strcat(buf, " conventions");
|
|
|
|
elog(NOTICE, buf, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_date()
|
|
{
|
|
DateStyle = USE_POSTGRES_DATES;
|
|
EuroDates = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Timezone support
|
|
* Working storage for strings is allocated with an arbitrary size of 64 bytes.
|
|
*/
|
|
|
|
static char *defaultTZ = NULL;
|
|
static char TZvalue[64];
|
|
static char tzbuf[64];
|
|
|
|
/*
|
|
*
|
|
* TIMEZONE
|
|
*
|
|
*/
|
|
/* parse_timezone()
|
|
* Handle SET TIME ZONE...
|
|
* Try to save existing TZ environment variable for later use in RESET TIME ZONE.
|
|
* - thomas 1997-11-10
|
|
*/
|
|
static bool
|
|
parse_timezone(const char *value)
|
|
{
|
|
char *tok;
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_timezone();
|
|
return TRUE;
|
|
}
|
|
|
|
while ((value = get_token(&tok, NULL, value)) != 0)
|
|
{
|
|
/* Not yet tried to save original value from environment? */
|
|
if (defaultTZ == NULL)
|
|
{
|
|
/* found something? then save it for later */
|
|
if ((defaultTZ = getenv("TZ")) != NULL)
|
|
strcpy(TZvalue, defaultTZ);
|
|
|
|
/* found nothing so mark with an invalid pointer */
|
|
else
|
|
defaultTZ = (char *) -1;
|
|
}
|
|
|
|
strcpy(tzbuf, "TZ=");
|
|
strcat(tzbuf, tok);
|
|
if (putenv(tzbuf) != 0)
|
|
elog(ERROR, "Unable to set TZ environment variable to %s", tok);
|
|
|
|
tzset();
|
|
pfree(tok);
|
|
}
|
|
|
|
return TRUE;
|
|
} /* parse_timezone() */
|
|
|
|
static bool
|
|
show_timezone()
|
|
{
|
|
char *tz;
|
|
|
|
tz = getenv("TZ");
|
|
|
|
elog(NOTICE, "Time zone is %s", ((tz != NULL) ? tz : "unknown"));
|
|
|
|
return TRUE;
|
|
} /* show_timezone() */
|
|
|
|
/* reset_timezone()
|
|
* Set TZ environment variable to original value.
|
|
* Note that if TZ was originally not set, TZ should be cleared.
|
|
* unsetenv() works fine, but is BSD, not POSIX, and is not available
|
|
* under Solaris, among others. Apparently putenv() called as below
|
|
* clears the process-specific environment variables.
|
|
* Other reasonable arguments to putenv() (e.g. "TZ=", "TZ", "") result
|
|
* in a core dump (under Linux anyway).
|
|
* - thomas 1998-01-26
|
|
*/
|
|
static bool
|
|
reset_timezone()
|
|
{
|
|
/* no time zone has been set in this session? */
|
|
if (defaultTZ == NULL)
|
|
{
|
|
}
|
|
|
|
/* time zone was set and original explicit time zone available? */
|
|
else if (defaultTZ != (char *) -1)
|
|
{
|
|
strcpy(tzbuf, "TZ=");
|
|
strcat(tzbuf, TZvalue);
|
|
if (putenv(tzbuf) != 0)
|
|
elog(ERROR, "Unable to set TZ environment variable to %s", TZvalue);
|
|
tzset();
|
|
}
|
|
|
|
/*
|
|
* otherwise, time zone was set but no original explicit time zone
|
|
* available
|
|
*/
|
|
else
|
|
{
|
|
strcpy(tzbuf, "=");
|
|
if (putenv(tzbuf) != 0)
|
|
elog(ERROR, "Unable to clear TZ environment variable");
|
|
tzset();
|
|
}
|
|
|
|
return TRUE;
|
|
} /* reset_timezone() */
|
|
|
|
/*-----------------------------------------------------------------------
|
|
KSQO code will one day be unnecessary when the optimizer makes use of
|
|
indexes when multiple ORs are specified in the where clause.
|
|
See optimizer/prep/prepkeyset.c for more on this.
|
|
daveh@insightdist.com 6/16/98
|
|
-----------------------------------------------------------------------*/
|
|
static bool
|
|
parse_ksqo(const char *value)
|
|
{
|
|
return parse_boolean_var(value, &_use_keyset_query_optimizer,
|
|
"KSQO", false);
|
|
}
|
|
|
|
static bool
|
|
show_ksqo()
|
|
{
|
|
elog(NOTICE, "KSQO is %s",
|
|
_use_keyset_query_optimizer ? "ON" : "OFF");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_ksqo()
|
|
{
|
|
_use_keyset_query_optimizer = false;
|
|
return TRUE;
|
|
}
|
|
|
|
/* SET TRANSACTION */
|
|
|
|
static bool
|
|
parse_XactIsoLevel(const char *value)
|
|
{
|
|
|
|
if (value == NULL)
|
|
{
|
|
reset_XactIsoLevel();
|
|
return TRUE;
|
|
}
|
|
|
|
if (SerializableSnapshot != NULL)
|
|
{
|
|
elog(ERROR, "SET TRANSACTION ISOLATION LEVEL must be called before any query");
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
if (strcasecmp(value, "SERIALIZABLE") == 0)
|
|
XactIsoLevel = XACT_SERIALIZABLE;
|
|
else if (strcasecmp(value, "COMMITTED") == 0)
|
|
XactIsoLevel = XACT_READ_COMMITTED;
|
|
else
|
|
elog(ERROR, "Bad TRANSACTION ISOLATION LEVEL (%s)", value);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
show_XactIsoLevel()
|
|
{
|
|
|
|
if (XactIsoLevel == XACT_SERIALIZABLE)
|
|
elog(NOTICE, "TRANSACTION ISOLATION LEVEL is SERIALIZABLE");
|
|
else
|
|
elog(NOTICE, "TRANSACTION ISOLATION LEVEL is READ COMMITTED");
|
|
return TRUE;
|
|
}
|
|
|
|
static bool
|
|
reset_XactIsoLevel()
|
|
{
|
|
|
|
if (SerializableSnapshot != NULL)
|
|
{
|
|
elog(ERROR, "SET TRANSACTION ISOLATION LEVEL must be called before any query");
|
|
return TRUE;
|
|
}
|
|
|
|
XactIsoLevel = DefaultXactIsoLevel;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Pg_options
|
|
*/
|
|
static bool
|
|
parse_pg_options(const char *value)
|
|
{
|
|
if (!superuser()) {
|
|
elog(ERROR, "Only users with superuser privilege can set pg_options");
|
|
}
|
|
parse_options((char *) value, TRUE);
|
|
return (TRUE);
|
|
}
|
|
|
|
static bool
|
|
show_pg_options(void)
|
|
{
|
|
show_options();
|
|
return (TRUE);
|
|
}
|
|
|
|
static bool
|
|
reset_pg_options(void)
|
|
{
|
|
if (!superuser()) {
|
|
elog(ERROR, "Only users with superuser privilege can set pg_options");
|
|
}
|
|
read_pg_options(0);
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
struct VariableParsers
|
|
{
|
|
const char *name;
|
|
bool (*parser) (const char *);
|
|
bool (*show) ();
|
|
bool (*reset) ();
|
|
} VariableParsers[] =
|
|
|
|
{
|
|
{
|
|
"datestyle", parse_date, show_date, reset_date
|
|
},
|
|
{
|
|
"timezone", parse_timezone, show_timezone, reset_timezone
|
|
},
|
|
{
|
|
"effective_cache_size", parse_effective_cache_size,
|
|
show_effective_cache_size, reset_effective_cache_size
|
|
},
|
|
{
|
|
"random_page_cost", parse_random_page_cost,
|
|
show_random_page_cost, reset_random_page_cost
|
|
},
|
|
{
|
|
"cpu_tuple_cost", parse_cpu_tuple_cost,
|
|
show_cpu_tuple_cost, reset_cpu_tuple_cost
|
|
},
|
|
{
|
|
"cpu_index_tuple_cost", parse_cpu_index_tuple_cost,
|
|
show_cpu_index_tuple_cost, reset_cpu_index_tuple_cost
|
|
},
|
|
{
|
|
"cpu_operator_cost", parse_cpu_operator_cost,
|
|
show_cpu_operator_cost, reset_cpu_operator_cost
|
|
},
|
|
{
|
|
"enable_seqscan", parse_enable_seqscan,
|
|
show_enable_seqscan, reset_enable_seqscan
|
|
},
|
|
{
|
|
"enable_indexscan", parse_enable_indexscan,
|
|
show_enable_indexscan, reset_enable_indexscan
|
|
},
|
|
{
|
|
"enable_tidscan", parse_enable_tidscan,
|
|
show_enable_tidscan, reset_enable_tidscan
|
|
},
|
|
{
|
|
"enable_sort", parse_enable_sort,
|
|
show_enable_sort, reset_enable_sort
|
|
},
|
|
{
|
|
"enable_nestloop", parse_enable_nestloop,
|
|
show_enable_nestloop, reset_enable_nestloop
|
|
},
|
|
{
|
|
"enable_mergejoin", parse_enable_mergejoin,
|
|
show_enable_mergejoin, reset_enable_mergejoin
|
|
},
|
|
{
|
|
"enable_hashjoin", parse_enable_hashjoin,
|
|
show_enable_hashjoin, reset_enable_hashjoin
|
|
},
|
|
{
|
|
"geqo", parse_geqo, show_geqo, reset_geqo
|
|
},
|
|
#ifdef MULTIBYTE
|
|
{
|
|
"client_encoding", parse_client_encoding, show_client_encoding, reset_client_encoding
|
|
},
|
|
{
|
|
"server_encoding", parse_server_encoding, show_server_encoding, reset_server_encoding
|
|
},
|
|
#endif
|
|
{
|
|
"ksqo", parse_ksqo, show_ksqo, reset_ksqo
|
|
},
|
|
{
|
|
"XactIsoLevel", parse_XactIsoLevel, show_XactIsoLevel, reset_XactIsoLevel
|
|
},
|
|
{
|
|
"pg_options", parse_pg_options, show_pg_options, reset_pg_options
|
|
},
|
|
{
|
|
NULL, NULL, NULL, NULL
|
|
}
|
|
};
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
bool
|
|
SetPGVariable(const char *name, const char *value)
|
|
{
|
|
struct VariableParsers *vp;
|
|
|
|
for (vp = VariableParsers; vp->name; vp++)
|
|
{
|
|
if (!strcasecmp(vp->name, name))
|
|
return (vp->parser) (value);
|
|
}
|
|
|
|
elog(NOTICE, "Unrecognized variable %s", name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
bool
|
|
GetPGVariable(const char *name)
|
|
{
|
|
struct VariableParsers *vp;
|
|
|
|
for (vp = VariableParsers; vp->name; vp++)
|
|
{
|
|
if (!strcasecmp(vp->name, name))
|
|
return (vp->show) ();
|
|
}
|
|
|
|
elog(NOTICE, "Unrecognized variable %s", name);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
bool
|
|
ResetPGVariable(const char *name)
|
|
{
|
|
struct VariableParsers *vp;
|
|
|
|
for (vp = VariableParsers; vp->name; vp++)
|
|
{
|
|
if (!strcasecmp(vp->name, name))
|
|
return (vp->reset) ();
|
|
}
|
|
|
|
elog(NOTICE, "Unrecognized variable %s", name);
|
|
|
|
return TRUE;
|
|
}
|