mirror of
https://github.com/postgres/postgres.git
synced 2025-11-12 05:01:15 +03:00
Merge the last few variable.c configuration variables into the generic
GUC support. It's now possible to set datestyle, timezone, and client_encoding from postgresql.conf and per-database or per-user settings. Also, implement rollback of SET commands that occur in a transaction that later fails. Create a SET LOCAL var = value syntax that sets the variable only for the duration of the current transaction. All per previous discussions in pghackers.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.89 2002/04/21 19:48:12 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.90 2002/05/17 01:19:18 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -3571,11 +3571,17 @@ EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
|
||||
} /* EncodeInterval() */
|
||||
|
||||
|
||||
void
|
||||
ClearDateCache(bool dummy)
|
||||
/* GUC assign_hook for australian_timezones */
|
||||
bool
|
||||
ClearDateCache(bool newval, bool doit, bool interactive)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAXDATEFIELDS; i++)
|
||||
datecache[i] = NULL;
|
||||
if (doit)
|
||||
{
|
||||
for (i = 0; i < MAXDATEFIELDS; i++)
|
||||
datecache[i] = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* PostgreSQL locale utilities
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.16 2002/04/03 05:39:31 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/pg_locale.c,v 1.17 2002/05/17 01:19:18 tgl Exp $
|
||||
*
|
||||
* Portions Copyright (c) 2002, PostgreSQL Global Development Group
|
||||
*
|
||||
@@ -10,89 +10,73 @@
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "utils/pg_locale.h"
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include "utils/pg_locale.h"
|
||||
|
||||
|
||||
/* GUC storage area */
|
||||
|
||||
char * locale_messages;
|
||||
char * locale_monetary;
|
||||
char * locale_numeric;
|
||||
char * locale_time;
|
||||
char *locale_messages;
|
||||
char *locale_monetary;
|
||||
char *locale_numeric;
|
||||
char *locale_time;
|
||||
|
||||
/* GUC parse hooks */
|
||||
|
||||
bool locale_messages_check(const char *proposed)
|
||||
{
|
||||
#ifdef LC_MESSAGES
|
||||
return chklocale(LC_MESSAGES, proposed);
|
||||
#else
|
||||
/* We return true here so LC_MESSAGES can be set in the
|
||||
configuration file on every system. */
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool locale_monetary_check(const char *proposed)
|
||||
{
|
||||
return chklocale(LC_MONETARY, proposed);
|
||||
}
|
||||
|
||||
bool locale_numeric_check(const char *proposed)
|
||||
{
|
||||
return chklocale(LC_NUMERIC, proposed);
|
||||
}
|
||||
|
||||
bool locale_time_check(const char *proposed)
|
||||
{
|
||||
return chklocale(LC_TIME, proposed);
|
||||
}
|
||||
|
||||
/* GUC assign hooks */
|
||||
|
||||
void locale_messages_assign(const char *value)
|
||||
static const char *
|
||||
locale_xxx_assign(int category, const char *value, bool doit, bool interactive)
|
||||
{
|
||||
if (doit)
|
||||
{
|
||||
if (!setlocale(category, value))
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *save;
|
||||
|
||||
save = setlocale(category, NULL);
|
||||
if (!save)
|
||||
return NULL;
|
||||
|
||||
if (!setlocale(category, value))
|
||||
return NULL;
|
||||
|
||||
setlocale(category, save);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
const char *
|
||||
locale_messages_assign(const char *value, bool doit, bool interactive)
|
||||
{
|
||||
/* LC_MESSAGES category does not exist everywhere, but accept it anyway */
|
||||
#ifdef LC_MESSAGES
|
||||
setlocale(LC_MESSAGES, value);
|
||||
return locale_xxx_assign(LC_MESSAGES, value, doit, interactive);
|
||||
#else
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
void locale_monetary_assign(const char *value)
|
||||
const char *
|
||||
locale_monetary_assign(const char *value, bool doit, bool interactive)
|
||||
{
|
||||
setlocale(LC_MONETARY, value);
|
||||
return locale_xxx_assign(LC_MONETARY, value, doit, interactive);
|
||||
}
|
||||
|
||||
void locale_numeric_assign(const char *value)
|
||||
const char *
|
||||
locale_numeric_assign(const char *value, bool doit, bool interactive)
|
||||
{
|
||||
setlocale(LC_NUMERIC, value);
|
||||
return locale_xxx_assign(LC_NUMERIC, value, doit, interactive);
|
||||
}
|
||||
|
||||
void locale_time_assign(const char *value)
|
||||
const char *
|
||||
locale_time_assign(const char *value, bool doit, bool interactive)
|
||||
{
|
||||
setlocale(LC_TIME, value);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns true if the proposed string represents a valid locale of
|
||||
* the given category. This is probably pretty slow, but it's not
|
||||
* called in critical places.
|
||||
*/
|
||||
bool
|
||||
chklocale(int category, const char *proposed)
|
||||
{
|
||||
char *save;
|
||||
|
||||
save = setlocale(category, NULL);
|
||||
if (!save)
|
||||
return false;
|
||||
|
||||
if (!setlocale(category, proposed))
|
||||
return false;
|
||||
|
||||
setlocale(category, save);
|
||||
return true;
|
||||
return locale_xxx_assign(LC_TIME, value, doit, interactive);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +107,6 @@ lc_collate_is_c(void)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Return the POSIX lconv struct (contains number/money formatting
|
||||
* information) with locale information for all categories.
|
||||
@@ -131,10 +114,11 @@ lc_collate_is_c(void)
|
||||
struct lconv *
|
||||
PGLC_localeconv(void)
|
||||
{
|
||||
struct lconv *extlconv;
|
||||
static bool CurrentLocaleConvValid = false;
|
||||
static struct lconv CurrentLocaleConv;
|
||||
|
||||
struct lconv *extlconv;
|
||||
|
||||
/* Did we do it already? */
|
||||
if (CurrentLocaleConvValid)
|
||||
return &CurrentLocaleConv;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.104 2002/05/12 23:43:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.105 2002/05/17 01:19:18 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -2576,27 +2576,33 @@ quote_identifier(const char *ident)
|
||||
* and contains only lowercase letters, digits, and underscores, *and* is
|
||||
* not any SQL keyword. Otherwise, supply quotes.
|
||||
*/
|
||||
int nquotes = 0;
|
||||
bool safe;
|
||||
const char *ptr;
|
||||
char *result;
|
||||
char *optr;
|
||||
|
||||
/*
|
||||
* would like to use <ctype.h> macros here, but they might yield
|
||||
* unwanted locale-specific results...
|
||||
*/
|
||||
safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
|
||||
if (safe)
|
||||
|
||||
for (ptr = ident; *ptr; ptr++)
|
||||
{
|
||||
const char *ptr;
|
||||
char ch = *ptr;
|
||||
|
||||
for (ptr = ident + 1; *ptr; ptr++)
|
||||
if ((ch >= 'a' && ch <= 'z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
(ch == '_'))
|
||||
{
|
||||
char ch = *ptr;
|
||||
|
||||
safe = ((ch >= 'a' && ch <= 'z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
(ch == '_'));
|
||||
if (!safe)
|
||||
break;
|
||||
/* okay */
|
||||
}
|
||||
else
|
||||
{
|
||||
safe = false;
|
||||
if (ch == '"')
|
||||
nquotes++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2618,8 +2624,21 @@ quote_identifier(const char *ident)
|
||||
if (safe)
|
||||
return ident; /* no change needed */
|
||||
|
||||
result = (char *) palloc(strlen(ident) + 2 + 1);
|
||||
sprintf(result, "\"%s\"", ident);
|
||||
result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
|
||||
|
||||
optr = result;
|
||||
*optr++ = '"';
|
||||
for (ptr = ident; *ptr; ptr++)
|
||||
{
|
||||
char ch = *ptr;
|
||||
|
||||
if (ch == '"')
|
||||
*optr++ = '"';
|
||||
*optr++ = ch;
|
||||
}
|
||||
*optr++ = '"';
|
||||
*optr = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.97 2002/05/05 00:03:29 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.98 2002/05/17 01:19:18 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -841,105 +841,68 @@ elog_message_prefix(int lev)
|
||||
/*
|
||||
* GUC support routines
|
||||
*/
|
||||
|
||||
bool
|
||||
check_server_min_messages(const char *lev)
|
||||
const char *
|
||||
assign_server_min_messages(const char *newval,
|
||||
bool doit, bool interactive)
|
||||
{
|
||||
if (strcasecmp(lev, "debug") == 0 ||
|
||||
strcasecmp(lev, "debug5") == 0 ||
|
||||
strcasecmp(lev, "debug4") == 0 ||
|
||||
strcasecmp(lev, "debug3") == 0 ||
|
||||
strcasecmp(lev, "debug2") == 0 ||
|
||||
strcasecmp(lev, "debug1") == 0 ||
|
||||
strcasecmp(lev, "info") == 0 ||
|
||||
strcasecmp(lev, "notice") == 0 ||
|
||||
strcasecmp(lev, "warning") == 0 ||
|
||||
strcasecmp(lev, "error") == 0 ||
|
||||
strcasecmp(lev, "log") == 0 ||
|
||||
strcasecmp(lev, "fatal") == 0 ||
|
||||
strcasecmp(lev, "panic") == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
assign_server_min_messages(const char *lev)
|
||||
{
|
||||
if (strcasecmp(lev, "debug") == 0)
|
||||
server_min_messages = DEBUG5;
|
||||
else if (strcasecmp(lev, "debug5") == 0)
|
||||
server_min_messages = DEBUG5;
|
||||
else if (strcasecmp(lev, "debug4") == 0)
|
||||
server_min_messages = DEBUG4;
|
||||
else if (strcasecmp(lev, "debug3") == 0)
|
||||
server_min_messages = DEBUG3;
|
||||
else if (strcasecmp(lev, "debug2") == 0)
|
||||
server_min_messages = DEBUG2;
|
||||
else if (strcasecmp(lev, "debug1") == 0)
|
||||
server_min_messages = DEBUG1;
|
||||
else if (strcasecmp(lev, "info") == 0)
|
||||
server_min_messages = INFO;
|
||||
else if (strcasecmp(lev, "notice") == 0)
|
||||
server_min_messages = NOTICE;
|
||||
else if (strcasecmp(lev, "warning") == 0)
|
||||
server_min_messages = WARNING;
|
||||
else if (strcasecmp(lev, "error") == 0)
|
||||
server_min_messages = ERROR;
|
||||
else if (strcasecmp(lev, "log") == 0)
|
||||
server_min_messages = LOG;
|
||||
else if (strcasecmp(lev, "fatal") == 0)
|
||||
server_min_messages = FATAL;
|
||||
else if (strcasecmp(lev, "panic") == 0)
|
||||
server_min_messages = PANIC;
|
||||
if (strcasecmp(newval, "debug") == 0)
|
||||
{ if (doit) server_min_messages = DEBUG1; }
|
||||
else if (strcasecmp(newval, "debug5") == 0)
|
||||
{ if (doit) server_min_messages = DEBUG5; }
|
||||
else if (strcasecmp(newval, "debug4") == 0)
|
||||
{ if (doit) server_min_messages = DEBUG4; }
|
||||
else if (strcasecmp(newval, "debug3") == 0)
|
||||
{ if (doit) server_min_messages = DEBUG3; }
|
||||
else if (strcasecmp(newval, "debug2") == 0)
|
||||
{ if (doit) server_min_messages = DEBUG2; }
|
||||
else if (strcasecmp(newval, "debug1") == 0)
|
||||
{ if (doit) server_min_messages = DEBUG1; }
|
||||
else if (strcasecmp(newval, "info") == 0)
|
||||
{ if (doit) server_min_messages = INFO; }
|
||||
else if (strcasecmp(newval, "notice") == 0)
|
||||
{ if (doit) server_min_messages = NOTICE; }
|
||||
else if (strcasecmp(newval, "warning") == 0)
|
||||
{ if (doit) server_min_messages = WARNING; }
|
||||
else if (strcasecmp(newval, "error") == 0)
|
||||
{ if (doit) server_min_messages = ERROR; }
|
||||
else if (strcasecmp(newval, "log") == 0)
|
||||
{ if (doit) server_min_messages = LOG; }
|
||||
else if (strcasecmp(newval, "fatal") == 0)
|
||||
{ if (doit) server_min_messages = FATAL; }
|
||||
else if (strcasecmp(newval, "panic") == 0)
|
||||
{ if (doit) server_min_messages = PANIC; }
|
||||
else
|
||||
/* Can't get here unless guc.c screwed up */
|
||||
elog(ERROR, "bogus server_min_messages %s", lev);
|
||||
return NULL; /* fail */
|
||||
return newval; /* OK */
|
||||
}
|
||||
|
||||
bool
|
||||
check_client_min_messages(const char *lev)
|
||||
const char *
|
||||
assign_client_min_messages(const char *newval,
|
||||
bool doit, bool interactive)
|
||||
{
|
||||
if (strcasecmp(lev, "debug") == 0 ||
|
||||
strcasecmp(lev, "debug5") == 0 ||
|
||||
strcasecmp(lev, "debug4") == 0 ||
|
||||
strcasecmp(lev, "debug3") == 0 ||
|
||||
strcasecmp(lev, "debug2") == 0 ||
|
||||
strcasecmp(lev, "debug1") == 0 ||
|
||||
strcasecmp(lev, "log") == 0 ||
|
||||
strcasecmp(lev, "info") == 0 ||
|
||||
strcasecmp(lev, "notice") == 0 ||
|
||||
strcasecmp(lev, "warning") == 0 ||
|
||||
strcasecmp(lev, "error") == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
assign_client_min_messages(const char *lev)
|
||||
{
|
||||
if (strcasecmp(lev, "debug") == 0)
|
||||
client_min_messages = DEBUG5;
|
||||
else if (strcasecmp(lev, "debug5") == 0)
|
||||
client_min_messages = DEBUG5;
|
||||
else if (strcasecmp(lev, "debug4") == 0)
|
||||
client_min_messages = DEBUG4;
|
||||
else if (strcasecmp(lev, "debug3") == 0)
|
||||
client_min_messages = DEBUG3;
|
||||
else if (strcasecmp(lev, "debug2") == 0)
|
||||
client_min_messages = DEBUG2;
|
||||
else if (strcasecmp(lev, "debug1") == 0)
|
||||
client_min_messages = DEBUG1;
|
||||
else if (strcasecmp(lev, "log") == 0)
|
||||
client_min_messages = LOG;
|
||||
else if (strcasecmp(lev, "info") == 0)
|
||||
client_min_messages = INFO;
|
||||
else if (strcasecmp(lev, "notice") == 0)
|
||||
client_min_messages = NOTICE;
|
||||
else if (strcasecmp(lev, "warning") == 0)
|
||||
client_min_messages = WARNING;
|
||||
else if (strcasecmp(lev, "error") == 0)
|
||||
client_min_messages = ERROR;
|
||||
if (strcasecmp(newval, "debug") == 0)
|
||||
{ if (doit) client_min_messages = DEBUG1; }
|
||||
else if (strcasecmp(newval, "debug5") == 0)
|
||||
{ if (doit) client_min_messages = DEBUG5; }
|
||||
else if (strcasecmp(newval, "debug4") == 0)
|
||||
{ if (doit) client_min_messages = DEBUG4; }
|
||||
else if (strcasecmp(newval, "debug3") == 0)
|
||||
{ if (doit) client_min_messages = DEBUG3; }
|
||||
else if (strcasecmp(newval, "debug2") == 0)
|
||||
{ if (doit) client_min_messages = DEBUG2; }
|
||||
else if (strcasecmp(newval, "debug1") == 0)
|
||||
{ if (doit) client_min_messages = DEBUG1; }
|
||||
else if (strcasecmp(newval, "log") == 0)
|
||||
{ if (doit) client_min_messages = LOG; }
|
||||
else if (strcasecmp(newval, "info") == 0)
|
||||
{ if (doit) client_min_messages = INFO; }
|
||||
else if (strcasecmp(newval, "notice") == 0)
|
||||
{ if (doit) client_min_messages = NOTICE; }
|
||||
else if (strcasecmp(newval, "warning") == 0)
|
||||
{ if (doit) client_min_messages = WARNING; }
|
||||
else if (strcasecmp(newval, "error") == 0)
|
||||
{ if (doit) client_min_messages = ERROR; }
|
||||
else
|
||||
/* Can't get here unless guc.c screwed up */
|
||||
elog(ERROR, "bogus client_min_messages %s", lev);
|
||||
return NULL; /* fail */
|
||||
return newval; /* OK */
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.90 2002/05/06 19:47:30 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.91 2002/05/17 01:19:18 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -614,6 +614,9 @@ InitializeSessionUserId(const char *username)
|
||||
|
||||
SetSessionUserId(usesysid); /* sets CurrentUserId too */
|
||||
|
||||
/* Record username as a config option too */
|
||||
SetConfigOption("session_authorization", username,
|
||||
PGC_BACKEND, PGC_S_OVERRIDE);
|
||||
|
||||
/*
|
||||
* Set up user-specific configuration variables. This is a good
|
||||
@@ -653,23 +656,16 @@ InitializeSessionUserIdStandalone(void)
|
||||
* Change session auth ID while running
|
||||
*
|
||||
* Only a superuser may set auth ID to something other than himself.
|
||||
*
|
||||
* username == NULL implies reset to default (AuthenticatedUserId).
|
||||
*/
|
||||
void
|
||||
SetSessionAuthorization(const char *username)
|
||||
SetSessionAuthorization(Oid userid)
|
||||
{
|
||||
Oid userid;
|
||||
/* Must have authenticated already, else can't make permission check */
|
||||
AssertState(OidIsValid(AuthenticatedUserId));
|
||||
|
||||
if (username == NULL)
|
||||
userid = AuthenticatedUserId;
|
||||
else
|
||||
{
|
||||
userid = get_usesysid(username);
|
||||
if (userid != AuthenticatedUserId &&
|
||||
!AuthenticatedUserIsSuperuser)
|
||||
elog(ERROR, "permission denied");
|
||||
}
|
||||
if (userid != AuthenticatedUserId &&
|
||||
!AuthenticatedUserIsSuperuser)
|
||||
elog(ERROR, "permission denied");
|
||||
|
||||
SetSessionUserId(userid);
|
||||
SetUserId(userid);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.104 2002/05/05 00:03:29 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.105 2002/05/17 01:19:18 tgl Exp $
|
||||
*
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "catalog/pg_database.h"
|
||||
#include "catalog/pg_shadow.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "commands/variable.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
#include "miscadmin.h"
|
||||
#include "storage/backendid.h"
|
||||
@@ -132,6 +131,9 @@ ReverifyMyDatabase(const char *name)
|
||||
*/
|
||||
#ifdef MULTIBYTE
|
||||
SetDatabaseEncoding(dbform->encoding);
|
||||
/* If we have no other source of client_encoding, use server encoding */
|
||||
SetConfigOption("client_encoding", GetDatabaseEncodingName(),
|
||||
PGC_BACKEND, PGC_S_DEFAULT);
|
||||
#else
|
||||
if (dbform->encoding != PG_SQL_ASCII)
|
||||
elog(FATAL, "database was initialized with MULTIBYTE encoding %d,\n\tbut the backend was compiled without multibyte support.\n\tlooks like you need to initdb or recompile.",
|
||||
@@ -388,11 +390,6 @@ InitPostgres(const char *dbname, const char *username)
|
||||
/* set default namespace search path */
|
||||
InitializeSearchPath();
|
||||
|
||||
#ifdef MULTIBYTE
|
||||
/* set default client encoding --- uses info from ReverifyMyDatabase */
|
||||
set_default_client_encoding();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set up process-exit callback to do pre-shutdown cleanup. This should
|
||||
* be last because we want shmem_exit to call this routine before the exit
|
||||
|
||||
136
src/backend/utils/misc/README
Normal file
136
src/backend/utils/misc/README
Normal file
@@ -0,0 +1,136 @@
|
||||
$Header: /cvsroot/pgsql/src/backend/utils/misc/README,v 1.1 2002/05/17 01:19:18 tgl Exp $
|
||||
|
||||
|
||||
GUC IMPLEMENTATION NOTES
|
||||
|
||||
The GUC (Grand Unified Configuration) module implements configuration
|
||||
variables of multiple types (currently boolean, int, float, and string).
|
||||
Variable settings can come from various places, with a priority ordering
|
||||
determining which setting is used.
|
||||
|
||||
|
||||
PER-VARIABLE HOOKS
|
||||
|
||||
Each variable known to GUC can optionally have an assign_hook and/or
|
||||
a show_hook to provide customized behavior. Assign hooks are used to
|
||||
perform validity checking on variable values (above and beyond what
|
||||
GUC can do). They are also used to update any derived state that needs
|
||||
to change when a GUC variable is set. Show hooks are used to modify
|
||||
the default SHOW display for a variable.
|
||||
|
||||
If an assign_hook is provided, it points to a function of the signature
|
||||
bool assign_hook(newvalue, bool doit, bool interactive)
|
||||
where the type of 'newvalue' matches the kind of variable. This function
|
||||
is called immediately before actually setting the variable's value (so it
|
||||
can look at the actual variable to determine the old value). If the
|
||||
function returns "true" then the assignment is completed; if it returns
|
||||
"false" then newvalue is considered invalid and the assignment is not
|
||||
performed. If "doit" is false then the function should simply check
|
||||
validity of newvalue and not change any derived state. "interactive" is
|
||||
true when we are performing a SET command; in this case it is okay for the
|
||||
assign_hook to raise an error via elog(). If the function returns false
|
||||
for an interactive assignment then guc.c will report a generic "invalid
|
||||
value" error message. (An internal elog() in an assign_hook is only
|
||||
needed if you want to generate a specialized error message.) But when
|
||||
"interactive" is false we are reading a non-interactive option source,
|
||||
such as postgresql.conf. In this case the assign_hook should *not* elog
|
||||
but should just return false if it doesn't like the newvalue. (An
|
||||
elog(LOG) call would be acceptable if you feel a need for a custom
|
||||
complaint in this situation.)
|
||||
|
||||
For string variables, the signature for assign hooks is a bit different:
|
||||
const char *assign_hook(const char *newvalue,
|
||||
bool doit,
|
||||
bool interactive)
|
||||
The meanings of the parameters are the same as for the other types of GUC
|
||||
variables, but the return value is handled differently:
|
||||
NULL --- assignment fails (like returning false for other datatypes)
|
||||
newvalue --- assignment succeeds, assign the newvalue as-is
|
||||
malloc'd (not palloc'd!!!) string --- assign that value instead
|
||||
The third choice is allowed in case the assign_hook wants to return a
|
||||
"canonical" version of the new value. For example, the assign_hook for
|
||||
datestyle always returns a string that includes both basic datestyle and
|
||||
us/euro option, although the input might have specified only one.
|
||||
|
||||
If a show_hook is provided, it points to a function of the signature
|
||||
const char *show_hook(void)
|
||||
This hook allows variable-specific computation of the value displayed
|
||||
by SHOW.
|
||||
|
||||
|
||||
SAVING/RESTORING GUC VARIABLE VALUES
|
||||
|
||||
Prior values of configuration variables must be remembered in order to
|
||||
deal with three special cases: RESET (a/k/a SET TO DEFAULT), rollback of
|
||||
SET on transaction abort, and rollback of SET LOCAL at transaction end
|
||||
(either commit or abort). RESET is defined as selecting the value that
|
||||
would be effective had there never been any SET commands in the current
|
||||
session.
|
||||
|
||||
To handle these cases we must keep track of as many as four distinct
|
||||
values for each variable. They are:
|
||||
|
||||
* actual variable contents always the current effective value
|
||||
|
||||
* reset_value the value to use for RESET
|
||||
|
||||
* session_value the "committed" setting for the session
|
||||
|
||||
* tentative_value the uncommitted result of SET
|
||||
|
||||
During initialization we set the first three of these (actual, reset_value,
|
||||
and session_value) based on whichever non-interactive source has the
|
||||
highest priority. All three will have the same value.
|
||||
|
||||
A SET LOCAL command sets the actual variable (and nothing else). At
|
||||
transaction end, the session_value is used to restore the actual variable
|
||||
to its pre-transaction value.
|
||||
|
||||
A SET (or SET SESSION) command sets the actual variable, and if no error,
|
||||
then sets the tentative_value. If the transaction commits, the
|
||||
tentative_value is assigned to the session_value and the actual variable
|
||||
(which could by now be different, if the SET was followed by SET LOCAL).
|
||||
If the transaction aborts, the tentative_value is discarded and the
|
||||
actual variable is restored from the session_value.
|
||||
|
||||
RESET is executed like a SET, but using the reset_value as the desired new
|
||||
value. (We do not provide a RESET LOCAL command, but SET LOCAL TO DEFAULT
|
||||
has the same behavior that RESET LOCAL would.) The source associated with
|
||||
the reset_value also becomes associated with the actual and session values.
|
||||
|
||||
If SIGHUP is received, the GUC code rereads the postgresql.conf
|
||||
configuration file (this does not happen in the signal handler, but at
|
||||
next return to main loop; note that it can be executed while within a
|
||||
transaction). New values from postgresql.conf are assigned to actual
|
||||
variable, reset_value, and session_value, but only if each of these has a
|
||||
current source priority <= PGC_S_FILE. (It is thus possible for
|
||||
reset_value to track the config-file setting even if there is currently
|
||||
a different interactive value of the actual variable.)
|
||||
|
||||
Note that tentative_value is unused and undefined except between a SET
|
||||
command and the end of the transaction. Also notice that we must track
|
||||
the source associated with each of the four values.
|
||||
|
||||
The assign_hook and show_hook routines work only with the actual variable,
|
||||
and are not directly aware of the additional values maintained by GUC.
|
||||
This is not a problem for normal usage, since we can assign first to the
|
||||
actual variable and then (if that succeeds) to the additional values as
|
||||
needed. However, for SIGHUP rereads we may not want to assign to the
|
||||
actual variable. Our procedure in that case is to call the assign_hook
|
||||
with doit = false so that the value is validated, but no derived state is
|
||||
changed.
|
||||
|
||||
|
||||
STRING MEMORY HANDLING
|
||||
|
||||
String option values are allocated with strdup, not with the
|
||||
pstrdup/palloc mechanisms. We would need to keep them in a permanent
|
||||
context anyway, and strdup gives us more control over handling
|
||||
out-of-memory failures.
|
||||
|
||||
We allow a variable's actual value, reset_val, session_val, and
|
||||
tentative_val to point at the same storage. This makes it slightly harder
|
||||
to free space (must test that the value to be freed isn't equal to any of
|
||||
the other three pointers). The main advantage is that we never need to
|
||||
strdup during transaction commit/abort, so cannot cause an out-of-memory
|
||||
failure there.
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Copyright 2000 by PostgreSQL Global Development Group
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc-file.l,v 1.11 2002/03/02 21:39:33 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/misc/guc-file.l,v 1.12 2002/05/17 01:19:18 tgl Exp $
|
||||
*/
|
||||
|
||||
%{
|
||||
@@ -168,7 +168,7 @@ ProcessConfigFile(GucContext context)
|
||||
head = tail = NULL;
|
||||
opt_name = opt_value = NULL;
|
||||
|
||||
while((token = yylex()))
|
||||
while ((token = yylex()))
|
||||
switch(parse_state)
|
||||
{
|
||||
case 0: /* no previous input */
|
||||
@@ -188,23 +188,22 @@ ProcessConfigFile(GucContext context)
|
||||
token = yylex();
|
||||
|
||||
if (token != GUC_ID && token != GUC_STRING &&
|
||||
token != GUC_INTEGER && token != GUC_REAL &&
|
||||
token != GUC_UNQUOTED_STRING)
|
||||
token != GUC_INTEGER && token != GUC_REAL &&
|
||||
token != GUC_UNQUOTED_STRING)
|
||||
goto parse_error;
|
||||
opt_value = strdup(yytext);
|
||||
if (opt_value == NULL)
|
||||
goto out_of_memory;
|
||||
if (token == GUC_STRING)
|
||||
{
|
||||
/* remove the beginning and ending quote/apostrophe */
|
||||
/* first: shift the whole shooting match down one
|
||||
character */
|
||||
memmove(opt_value,opt_value+1,strlen(opt_value)-1);
|
||||
/* second: null out the 2 characters we shifted */
|
||||
opt_value[strlen(opt_value)-2]='\0';
|
||||
/* do the escape thing. free()'s the strdup above */
|
||||
opt_value=GUC_scanstr(opt_value);
|
||||
}
|
||||
if (token == GUC_STRING)
|
||||
{
|
||||
/* remove the beginning and ending quote/apostrophe */
|
||||
/* first: shift the whole thing down one character */
|
||||
memmove(opt_value,opt_value+1,strlen(opt_value)-1);
|
||||
/* second: null out the 2 characters we shifted */
|
||||
opt_value[strlen(opt_value)-2]='\0';
|
||||
/* do the escape thing. free()'s the strdup above */
|
||||
opt_value=GUC_scanstr(opt_value);
|
||||
}
|
||||
parse_state = 2;
|
||||
break;
|
||||
|
||||
@@ -241,14 +240,14 @@ ProcessConfigFile(GucContext context)
|
||||
for(item = head; item; item=item->next)
|
||||
{
|
||||
if (!set_config_option(item->name, item->value, context,
|
||||
false, PGC_S_INFINITY))
|
||||
PGC_S_FILE, false, 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, PGC_S_FILE);
|
||||
PGC_S_FILE, false, true);
|
||||
|
||||
cleanup_exit:
|
||||
free_name_value_list(head);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -192,7 +192,10 @@
|
||||
#
|
||||
#dynamic_library_path = '$libdir'
|
||||
#search_path = '$user,public'
|
||||
#datestyle = 'iso, us'
|
||||
#timezone = unknown # actually, defaults to TZ environment setting
|
||||
#australian_timezones = false
|
||||
#client_encoding = sql_ascii # actually, defaults to database encoding
|
||||
#authentication_timeout = 60 # min 1, max 600
|
||||
#deadlock_timeout = 1000
|
||||
#default_transaction_isolation = 'read committed'
|
||||
|
||||
Reference in New Issue
Block a user