aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/transam/xact.c5
-rw-r--r--src/backend/access/transam/xlog.c39
-rw-r--r--src/backend/bootstrap/bootstrap.c13
-rw-r--r--src/backend/catalog/namespace.c66
-rw-r--r--src/backend/commands/dbcommands.c8
-rw-r--r--src/backend/commands/user.c8
-rw-r--r--src/backend/commands/variable.c1099
-rw-r--r--src/backend/main/main.c4
-rw-r--r--src/backend/nodes/copyfuncs.c3
-rw-r--r--src/backend/nodes/equalfuncs.c4
-rw-r--r--src/backend/parser/gram.y131
-rw-r--r--src/backend/postmaster/postmaster.c13
-rw-r--r--src/backend/tcop/postgres.c17
-rw-r--r--src/backend/tcop/utility.c6
-rw-r--r--src/backend/utils/adt/datetime.c16
-rw-r--r--src/backend/utils/adt/pg_locale.c106
-rw-r--r--src/backend/utils/adt/ruleutils.c45
-rw-r--r--src/backend/utils/error/elog.c155
-rw-r--r--src/backend/utils/init/miscinit.c24
-rw-r--r--src/backend/utils/init/postinit.c11
-rw-r--r--src/backend/utils/misc/README136
-rw-r--r--src/backend/utils/misc/guc-file.l33
-rw-r--r--src/backend/utils/misc/guc.c1895
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample3
24 files changed, 2274 insertions, 1566 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index ca66b0afafa..d6176ec5c47 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.120 2002/04/01 03:34:25 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.121 2002/05/17 01:19:16 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
@@ -173,6 +173,7 @@
#include "storage/proc.h"
#include "storage/sinval.h"
#include "storage/smgr.h"
+#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/portal.h"
@@ -1002,6 +1003,7 @@ CommitTransaction(void)
RelationPurgeLocalRelation(true);
smgrDoPendingDeletes(true);
+ AtEOXact_GUC(true);
AtEOXact_SPI();
AtEOXact_gist();
AtEOXact_hash();
@@ -1104,6 +1106,7 @@ AbortTransaction(void)
RelationPurgeLocalRelation(false);
smgrDoPendingDeletes(false);
+ AtEOXact_GUC(false);
AtEOXact_SPI();
AtEOXact_gist();
AtEOXact_hash();
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index eca4a5de5f8..77ad566ba89 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.94 2002/05/09 13:30:24 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.95 2002/05/17 01:19:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -3272,31 +3272,10 @@ xlog_outrec(char *buf, XLogRecord *record)
/*
- * GUC support routines
+ * GUC support
*/
-
-bool
-check_xlog_sync_method(const char *method)
-{
- if (strcasecmp(method, "fsync") == 0)
- return true;
-#ifdef HAVE_FDATASYNC
- if (strcasecmp(method, "fdatasync") == 0)
- return true;
-#endif
-#ifdef OPEN_SYNC_FLAG
- if (strcasecmp(method, "open_sync") == 0)
- return true;
-#endif
-#ifdef OPEN_DATASYNC_FLAG
- if (strcasecmp(method, "open_datasync") == 0)
- return true;
-#endif
- return false;
-}
-
-void
-assign_xlog_sync_method(const char *method)
+const char *
+assign_xlog_sync_method(const char *method, bool doit, bool interactive)
{
int new_sync_method;
int new_sync_bit;
@@ -3329,12 +3308,12 @@ assign_xlog_sync_method(const char *method)
#endif
else
{
- /* Can't get here unless guc.c screwed up */
- elog(ERROR, "bogus wal_sync_method %s", method);
- new_sync_method = 0; /* keep compiler quiet */
- new_sync_bit = 0;
+ return NULL;
}
+ if (!doit)
+ return method;
+
if (sync_method != new_sync_method || open_sync_bit != new_sync_bit)
{
/*
@@ -3359,6 +3338,8 @@ assign_xlog_sync_method(const char *method)
sync_method = new_sync_method;
open_sync_bit = new_sync_bit;
}
+
+ return method;
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 51f432b1a00..1a13786a04d 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.128 2002/05/05 00:03:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.129 2002/05/17 01:19:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -249,7 +249,7 @@ BootstrapMain(int argc, char *argv[])
dbName = NULL;
if (!IsUnderPostmaster)
{
- ResetAllOptions(true);
+ InitializeGUCOptions();
potential_DataDir = getenv("PGDATA"); /* Null if no PGDATA
* variable */
}
@@ -263,12 +263,13 @@ BootstrapMain(int argc, char *argv[])
break;
case 'd':
{
- /* Turn on debugging for the postmaster. */
+ /* Turn on debugging for the bootstrap process. */
char *debugstr = palloc(strlen("debug") + strlen(optarg) + 1);
sprintf(debugstr, "debug%s", optarg);
- /* We use PGC_S_SESSION because we will reset in backend */
- SetConfigOption("server_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV);
- SetConfigOption("client_min_messages", debugstr, PGC_POSTMASTER, PGC_S_ARGV);
+ SetConfigOption("server_min_messages", debugstr,
+ PGC_POSTMASTER, PGC_S_ARGV);
+ SetConfigOption("client_min_messages", debugstr,
+ PGC_POSTMASTER, PGC_S_ARGV);
pfree(debugstr);
break;
}
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 9e5002e37f1..92c9306f184 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.19 2002/05/12 20:10:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.20 2002/05/17 01:19:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1739,16 +1739,16 @@ RemoveTempRelationsCallback(void)
* Routines for handling the GUC variable 'search_path'.
*/
-/* parse_hook: is proposed value valid? */
-bool
-check_search_path(const char *proposed)
+/* assign_hook: validate new search_path, do extra actions as needed */
+const char *
+assign_search_path(const char *newval, bool doit, bool interactive)
{
char *rawname;
List *namelist;
List *l;
/* Need a modifiable copy of string */
- rawname = pstrdup(proposed);
+ rawname = pstrdup(newval);
/* Parse string into list of identifiers */
if (!SplitIdentifierString(rawname, ',', &namelist))
@@ -1756,59 +1756,45 @@ check_search_path(const char *proposed)
/* syntax error in name list */
pfree(rawname);
freeList(namelist);
- return false;
+ return NULL;
}
/*
* If we aren't inside a transaction, we cannot do database access so
* cannot verify the individual names. Must accept the list on faith.
- * (This case can happen, for example, when the postmaster reads a
- * search_path setting from postgresql.conf.)
- */
- if (!IsTransactionState())
- {
- pfree(rawname);
- freeList(namelist);
- return true;
- }
-
- /*
- * Verify that all the names are either valid namespace names or "$user".
- * We do not require $user to correspond to a valid namespace.
- * We do not check for USAGE rights, either; should we?
*/
- foreach(l, namelist)
+ if (interactive && IsTransactionState())
{
- char *curname = (char *) lfirst(l);
-
- if (strcmp(curname, "$user") == 0)
- continue;
- if (!SearchSysCacheExists(NAMESPACENAME,
- CStringGetDatum(curname),
- 0, 0, 0))
+ /*
+ * Verify that all the names are either valid namespace names or
+ * "$user". We do not require $user to correspond to a valid
+ * namespace. We do not check for USAGE rights, either; should we?
+ */
+ foreach(l, namelist)
{
- pfree(rawname);
- freeList(namelist);
- return false;
+ char *curname = (char *) lfirst(l);
+
+ if (strcmp(curname, "$user") == 0)
+ continue;
+ if (!SearchSysCacheExists(NAMESPACENAME,
+ CStringGetDatum(curname),
+ 0, 0, 0))
+ elog(ERROR, "Namespace \"%s\" does not exist", curname);
}
}
pfree(rawname);
freeList(namelist);
- return true;
-}
-
-/* assign_hook: do extra actions needed when assigning to search_path */
-void
-assign_search_path(const char *newval)
-{
/*
* We mark the path as needing recomputation, but don't do anything until
* it's needed. This avoids trying to do database access during GUC
* initialization.
*/
- namespaceSearchPathValid = false;
+ if (doit)
+ namespaceSearchPathValid = false;
+
+ return newval;
}
/*
@@ -1844,6 +1830,8 @@ InitializeSearchPath(void)
CacheRegisterSyscacheCallback(NAMESPACEOID,
NamespaceCallback,
(Datum) 0);
+ /* Force search path to be recomputed on next use */
+ namespaceSearchPathValid = false;
}
}
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 41f122767ad..bb53ad8b3bb 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.88 2002/04/27 21:24:34 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.89 2002/05/17 01:19:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -458,9 +458,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
char repl_null[Natts_pg_database];
char repl_repl[Natts_pg_database];
- valuestr = (stmt->value
- ? ((A_Const *) lfirst(stmt->value))->val.val.str
- : NULL);
+ valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
rel = heap_openr(DatabaseRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname,
@@ -477,7 +475,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
MemSet(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_database_datconfig-1] = 'r';
- if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
+ if (strcmp(stmt->variable, "all")==0 && valuestr == NULL)
{
/* RESET ALL */
repl_null[Anum_pg_database_datconfig-1] = 'n';
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 405a7ceaa2b..5398d718b63 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.100 2002/04/28 00:36:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.101 2002/05/17 01:19:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -851,9 +851,7 @@ AlterUserSet(AlterUserSetStmt *stmt)
char repl_repl[Natts_pg_shadow];
int i;
- valuestr = (stmt->value
- ? ((A_Const *) lfirst(stmt->value))->val.val.str
- : NULL);
+ valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
/*
* RowExclusiveLock is sufficient, because we don't need to update
@@ -874,7 +872,7 @@ AlterUserSet(AlterUserSetStmt *stmt)
repl_repl[i] = ' ';
repl_repl[Anum_pg_shadow_useconfig-1] = 'r';
- if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
+ if (strcmp(stmt->variable, "all")==0 && valuestr == NULL)
/* RESET ALL */
repl_null[Anum_pg_shadow_useconfig-1] = 'n';
else
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index de42538bd63..03d7a664577 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -1,15 +1,15 @@
/*-------------------------------------------------------------------------
*
* variable.c
- * Routines for handling of 'SET var TO',
- * 'SHOW var' and 'RESET var' statements.
+ * Routines for handling specialized SET variables.
+ *
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.66 2002/05/06 19:47:30 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.67 2002/05/17 01:19:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,15 +21,11 @@
#include "access/xact.h"
#include "catalog/pg_shadow.h"
-#include "catalog/pg_type.h"
#include "commands/variable.h"
#include "miscadmin.h"
-#include "optimizer/cost.h"
-#include "optimizer/paths.h"
-#include "parser/parse_type.h"
#include "utils/builtins.h"
-#include "utils/date.h"
#include "utils/guc.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
#ifdef MULTIBYTE
@@ -41,470 +37,364 @@
#endif
-static bool show_datestyle(void);
-static bool reset_datestyle(void);
-static bool parse_datestyle(List *);
-static bool show_timezone(void);
-static bool reset_timezone(void);
-static bool parse_timezone(List *);
-
-static bool show_XactIsoLevel(void);
-static bool reset_XactIsoLevel(void);
-static bool parse_XactIsoLevel(List *);
-static bool show_random_seed(void);
-static bool reset_random_seed(void);
-static bool parse_random_seed(List *);
-
-static bool show_client_encoding(void);
-static bool reset_client_encoding(void);
-static bool parse_client_encoding(List *);
-static bool show_server_encoding(void);
-static bool reset_server_encoding(void);
-static bool parse_server_encoding(List *);
-
-
/*
- * get_token
- * Obtain the next item in a comma-separated list of items,
- * where each item can be either "word" or "word=word".
- * The "word=word" form is only accepted if 'val' is not NULL.
- * Words are any sequences not containing whitespace, ',', or '='.
- * Whitespace can appear between the words and punctuation.
- *
- * 'tok': receives a pointer to first word of item, or NULL if none.
- * 'val': if not NULL, receives a pointer to second word, or NULL if none.
- * 'str': start of input string.
- *
- * Returns NULL if input string contained no more words, else pointer
- * to just past this item, which can be used as 'str' for next call.
- * (If this is the last item, returned pointer will point at a null char,
- * so caller can alternatively check for that instead of calling again.)
- *
- * NB: input string is destructively modified by placing null characters
- * at ends of words!
- *
- * A former version of this code avoided modifying the input string by
- * returning palloc'd copies of the words. However, we want to use this
- * code early in backend startup to parse the PGDATESTYLE environment var,
- * and palloc/pfree aren't initialized at that point. Cleanest answer
- * seems to be to palloc in SetPGVariable() so that we can treat the string
- * as modifiable here.
+ * DATESTYLE
*/
-static char *
-get_token(char **tok, char **val, char *str)
-{
- char ch;
-
- *tok = NULL;
- if (val != NULL)
- *val = NULL;
-
- if (!str || *str == '\0')
- return NULL;
-
- /* skip leading white space */
- while (isspace((unsigned char) *str))
- str++;
-
- /* end of string? then return NULL */
- if (*str == '\0')
- return NULL;
-
- if (*str == ',' || *str == '=')
- elog(ERROR, "Syntax error near \"%s\": empty setting", str);
-
- /* OK, at beginning of non-empty item */
- *tok = str;
-
- /* Advance to end of word */
- while (*str && !isspace((unsigned char) *str) &&
- *str != ',' && *str != '=')
- str++;
-
- /* Terminate word string for caller */
- ch = *str;
- *str = '\0';
-
- /* Skip any whitespace */
- while (isspace((unsigned char) ch))
- ch = *(++str);
-
- /* end of string? */
- if (ch == '\0')
- return str;
- /* delimiter? */
- if (ch == ',')
- return ++str;
-
- /* Had better be '=', and caller must be expecting it */
- if (val == NULL || ch != '=')
- elog(ERROR, "Syntax error near \"%s\"", str);
-
- /* '=': get the value */
- str++;
-
- /* skip whitespace after '=' */
- while (isspace((unsigned char) *str))
- str++;
-
- if (*str == ',' || *str == '\0')
- elog(ERROR, "Syntax error near \"=%s\"", str);
-
- /* OK, at beginning of non-empty value */
- *val = str;
-
- /* Advance to end of word */
- while (*str && !isspace((unsigned char) *str) && *str != ',')
- str++;
-
- /* Terminate word string for caller */
- ch = *str;
- *str = '\0';
-
- /* Skip any whitespace */
- while (isspace((unsigned char) ch))
- ch = *(++str);
-
- /* end of string? */
- if (ch == '\0')
- return str;
- /* delimiter? */
- if (ch == ',')
- return ++str;
-
- elog(ERROR, "Syntax error near \"%s\"", str);
-
- return str;
-}
-
/*
- * DATESTYLE
- *
- * NOTE: set_default_datestyle() is called during backend startup to check
- * if the PGDATESTYLE environment variable is set. We want the env var
- * to determine the value that "RESET DateStyle" will reset to!
+ * assign_datestyle: GUC assign_hook for datestyle
*/
-
-/* These get initialized from the "master" values in init/globals.c */
-static int DefaultDateStyle;
-static bool DefaultEuroDates;
-
-static bool
-parse_datestyle_internal(char *value)
+const char *
+assign_datestyle(const char *value, bool doit, bool interactive)
{
- char *tok;
+ int newDateStyle = DateStyle;
+ bool newEuroDates = EuroDates;
+ bool ok = true;
int dcnt = 0,
ecnt = 0;
+ char *rawstring;
+ char *result;
+ List *elemlist;
+ List *l;
- if (value == NULL)
- return reset_datestyle();
+ /* Need a modifiable copy of string */
+ rawstring = pstrdup(value);
- while ((value = get_token(&tok, NULL, value)) != 0)
+ /* Parse string into list of identifiers */
+ if (!SplitIdentifierString(rawstring, ',', &elemlist))
{
+ /* syntax error in list */
+ pfree(rawstring);
+ freeList(elemlist);
+ if (interactive)
+ elog(ERROR, "SET DATESTYLE: invalid list syntax");
+ return NULL;
+ }
+
+ foreach(l, elemlist)
+ {
+ char *tok = (char *) lfirst(l);
+
/* Ugh. Somebody ought to write a table driven version -- mjl */
- if (!strcasecmp(tok, "ISO"))
+ if (strcasecmp(tok, "ISO") == 0)
{
- DateStyle = USE_ISO_DATES;
+ newDateStyle = USE_ISO_DATES;
dcnt++;
}
- else if (!strcasecmp(tok, "SQL"))
+ else if (strcasecmp(tok, "SQL") == 0)
{
- DateStyle = USE_SQL_DATES;
+ newDateStyle = USE_SQL_DATES;
dcnt++;
}
- else if (!strncasecmp(tok, "POSTGRESQL", 8))
+ else if (strncasecmp(tok, "POSTGRESQL", 8) == 0)
{
- DateStyle = USE_POSTGRES_DATES;
+ newDateStyle = USE_POSTGRES_DATES;
dcnt++;
}
- else if (!strcasecmp(tok, "GERMAN"))
+ else if (strcasecmp(tok, "GERMAN") == 0)
{
- DateStyle = USE_GERMAN_DATES;
+ newDateStyle = USE_GERMAN_DATES;
dcnt++;
- if ((ecnt > 0) && (!EuroDates))
- ecnt++;
- EuroDates = TRUE;
+ if ((ecnt > 0) && (!newEuroDates))
+ ok = false;
+ newEuroDates = TRUE;
}
- else if (!strncasecmp(tok, "EURO", 4))
+ else if (strncasecmp(tok, "EURO", 4) == 0)
{
- EuroDates = TRUE;
- if ((dcnt <= 0) || (DateStyle != USE_GERMAN_DATES))
- ecnt++;
+ newEuroDates = TRUE;
+ ecnt++;
}
- else if ((!strcasecmp(tok, "US"))
- || (!strncasecmp(tok, "NONEURO", 7)))
+ else if (strcasecmp(tok, "US") == 0
+ || strncasecmp(tok, "NONEURO", 7) == 0)
{
- EuroDates = FALSE;
- if ((dcnt <= 0) || (DateStyle == USE_GERMAN_DATES))
- ecnt++;
+ newEuroDates = FALSE;
+ ecnt++;
+ if ((dcnt > 0) && (newDateStyle == USE_GERMAN_DATES))
+ ok = false;
}
- else if (!strcasecmp(tok, "DEFAULT"))
+ else if (strcasecmp(tok, "DEFAULT") == 0)
{
- DateStyle = DefaultDateStyle;
- EuroDates = DefaultEuroDates;
+ /*
+ * Easiest way to get the current DEFAULT state is to fetch
+ * the DEFAULT string from guc.c and recursively parse it.
+ *
+ * We can't simply "return assign_datestyle(...)" because we
+ * need to handle constructs like "DEFAULT, ISO".
+ */
+ int saveDateStyle = DateStyle;
+ bool saveEuroDates = EuroDates;
+ const char *subval;
+
+ subval = assign_datestyle(GetConfigOptionResetString("datestyle"),
+ true, interactive);
+ newDateStyle = DateStyle;
+ newEuroDates = EuroDates;
+ DateStyle = saveDateStyle;
+ EuroDates = saveEuroDates;
+ if (!subval)
+ {
+ ok = false;
+ break;
+ }
+ /* Here we know that our own return value is always malloc'd */
+ /* when doit is true */
+ free((char *) subval);
+ dcnt++;
ecnt++;
}
else
- elog(ERROR, "SET DATESTYLE bad value (%s)", tok);
+ {
+ if (interactive)
+ elog(ERROR, "SET DATESTYLE: unrecognized keyword %s", tok);
+ ok = false;
+ break;
+ }
}
if (dcnt > 1 || ecnt > 1)
- elog(WARNING, "SET DATESTYLE specified conflicting settings");
-
- return TRUE;
-}
-
-static bool
-parse_datestyle(List *args)
-{
- int rstat = FALSE;
- List *arg;
- char *value;
-
- if (args == NULL)
- return reset_datestyle();
+ ok = false;
- Assert(IsA(args, List));
+ pfree(rawstring);
+ freeList(elemlist);
- foreach(arg, args)
+ if (!ok)
{
- Node *n;
-
- Assert(IsA(arg, List));
- n = lfirst(arg);
-
- /* Require untyped, stringy constants for arguments. */
- if (IsA(n, A_Const))
- {
- A_Const *p = (A_Const *) n;
- TypeName *type = p->typename;
- Value *v = &(p->val);
-
- if (type != NULL)
- {
- Value *s;
- Assert(IsA(type->names, List));
- s = (Value *) lfirst(type->names);
- elog(ERROR, "SET DATESTYLE does not allow input of type %s"
- "\n\tUse an untyped string instead", s->val.str);
- }
+ if (interactive)
+ elog(ERROR, "SET DATESTYLE: conflicting specifications");
+ return NULL;
+ }
- value = v->val.str;
- }
- else
- {
- elog(ERROR, "SET DATESTYLE argument is not valid");
- value = NULL;
- }
+ /*
+ * If we aren't going to do the assignment, just return OK indicator.
+ */
+ if (!doit)
+ return value;
- rstat = parse_datestyle_internal(value);
+ /*
+ * Prepare the canonical string to return. GUC wants it malloc'd.
+ */
+ result = (char *) malloc(32);
+ if (!result)
+ return NULL;
- if (rstat != TRUE)
- return rstat;
+ switch (newDateStyle)
+ {
+ case USE_ISO_DATES:
+ strcpy(result, "ISO");
+ break;
+ case USE_SQL_DATES:
+ strcpy(result, "SQL");
+ break;
+ case USE_GERMAN_DATES:
+ strcpy(result, "GERMAN");
+ break;
+ default:
+ strcpy(result, "POSTGRESQL");
+ break;
}
+ strcat(result, newEuroDates ? ", EURO" : ", US");
+
+ /*
+ * Finally, it's safe to assign to the global variables;
+ * the assignment cannot fail now.
+ */
+ DateStyle = newDateStyle;
+ EuroDates = newEuroDates;
- return rstat;
+ return result;
}
-static bool
+/*
+ * show_datestyle: GUC show_hook for datestyle
+ */
+const char *
show_datestyle(void)
{
- char buf[64];
+ static char buf[64];
- strcpy(buf, "DateStyle is ");
switch (DateStyle)
{
case USE_ISO_DATES:
- strcat(buf, "ISO");
+ strcpy(buf, "ISO");
break;
case USE_SQL_DATES:
- strcat(buf, "SQL");
+ strcpy(buf, "SQL");
break;
case USE_GERMAN_DATES:
- strcat(buf, "German");
+ strcpy(buf, "German");
break;
default:
- strcat(buf, "Postgres");
+ strcpy(buf, "Postgres");
break;
};
strcat(buf, " with ");
strcat(buf, ((EuroDates) ? "European" : "US (NonEuropean)"));
strcat(buf, " conventions");
- elog(INFO, buf, NULL);
-
- return TRUE;
-}
-
-static bool
-reset_datestyle(void)
-{
- DateStyle = DefaultDateStyle;
- EuroDates = DefaultEuroDates;
-
- return TRUE;
-}
-
-void
-set_default_datestyle(void)
-{
- char *DBDate;
-
- /*
- * Initialize from compile-time defaults in init/globals.c. NB: this
- * is a necessary step; consider PGDATESTYLE="DEFAULT".
- */
- DefaultDateStyle = DateStyle;
- DefaultEuroDates = EuroDates;
-
- /* If the environment var is set, override compiled-in values */
- DBDate = getenv("PGDATESTYLE");
- if (DBDate == NULL)
- return;
-
- /*
- * Make a modifiable copy --- overwriting the env var doesn't seem
- * like a good idea, even though we currently won't look at it again.
- * Note that we cannot use palloc at this early stage of
- * initialization.
- */
- DBDate = strdup(DBDate);
-
- /*
- * Parse desired setting into DateStyle/EuroDates Use
- * parse_datestyle_internal() to avoid any palloc() issues per above -
- * thomas 2001-10-15
- */
- parse_datestyle_internal(DBDate);
-
- free(DBDate);
-
- /* And make it the default for future RESETs */
- DefaultDateStyle = DateStyle;
- DefaultEuroDates = EuroDates;
+ return buf;
}
-/* Timezone support
- * Working storage for strings is allocated with an arbitrary size of 64 bytes.
+/*
+ * TIMEZONE
*/
-static char *defaultTZ = NULL;
-static char TZvalue[64];
+/*
+ * Storage for TZ env var is allocated with an arbitrary size of 64 bytes.
+ */
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.
- * Accept an explicit interval per SQL9x, though this is less useful than a full time zone.
- * - thomas 2001-10-11
+ * assign_timezone: GUC assign_hook for timezone
*/
-static bool
-parse_timezone(List *args)
+const char *
+assign_timezone(const char *value, bool doit, bool interactive)
{
- List *arg;
- TypeName *type;
+ char *result;
+ char *endptr;
+ double hours;
- if (args == NULL)
- return reset_timezone();
-
- Assert(IsA(args, List));
-
- foreach(arg, args)
+ /*
+ * Check for INTERVAL 'foo'
+ */
+ if (strncasecmp(value, "interval", 8) == 0)
{
- A_Const *p;
-
- Assert(IsA(arg, List));
- p = lfirst(arg);
- Assert(IsA(p, A_Const));
-
- type = p->typename;
- if (type != NULL)
+ const char *valueptr = value;
+ char *val;
+ Interval *interval;
+
+ valueptr += 8;
+ while (isspace((unsigned char) *valueptr))
+ valueptr++;
+ if (*valueptr++ != '\'')
+ return NULL;
+ val = pstrdup(valueptr);
+ /* Check and remove trailing quote */
+ endptr = strchr(val, '\'');
+ if (!endptr || endptr[1] != '\0')
{
- Oid typeOid = typenameTypeId(type);
-
- if (typeOid == INTERVALOID)
- {
- Interval *interval;
-
- interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
- CStringGetDatum(p->val.val.str),
- ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(type->typmod)));
- if (interval->month != 0)
- elog(ERROR, "SET TIME ZONE illegal INTERVAL; month not allowed");
- CTimeZone = interval->time;
- }
- else if (typeOid == FLOAT8OID)
+ pfree(val);
+ return NULL;
+ }
+ *endptr = '\0';
+ /*
+ * Try to parse it. XXX an invalid interval format will result in
+ * elog, which is not desirable for GUC. We did what we could to
+ * guard against this in flatten_set_variable_args, but a string
+ * coming in from postgresql.conf might contain anything.
+ */
+ interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
+ CStringGetDatum(val),
+ ObjectIdGetDatum(InvalidOid),
+ Int32GetDatum(-1)));
+ pfree(val);
+ if (interval->month != 0)
+ {
+ if (interactive)
+ elog(ERROR, "SET TIME ZONE: illegal INTERVAL; month not allowed");
+ pfree(interval);
+ return NULL;
+ }
+ if (doit)
+ {
+ CTimeZone = interval->time;
+ HasCTZSet = true;
+ }
+ pfree(interval);
+ }
+ else
+ {
+ /*
+ * Try it as a numeric number of hours (possibly fractional).
+ */
+ hours = strtod(value, &endptr);
+ if (endptr != value && *endptr == '\0')
+ {
+ if (doit)
{
- float8 time;
-
- time = DatumGetFloat8(DirectFunctionCall1(float8in, CStringGetDatum(p->val.val.str)));
- CTimeZone = time * 3600;
+ CTimeZone = hours * 3600;
+ HasCTZSet = true;
}
-
+ }
+ else if (strcasecmp(value, "UNKNOWN") == 0)
+ {
/*
- * We do not actually generate an integer constant in gram.y
- * so this is not used...
+ * Clear any TZ value we may have established.
+ *
+ * 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
*/
- else if (typeOid == INT4OID)
- {
- int32 time;
-
- time = p->val.val.ival;
- CTimeZone = time * 3600;
- }
- else
+ if (doit)
{
- elog(ERROR, "Unable to process SET TIME ZONE command; internal coding error");
+ if (tzbuf[0] == 'T')
+ {
+ strcpy(tzbuf, "=");
+ if (putenv(tzbuf) != 0)
+ elog(ERROR, "Unable to clear TZ environment variable");
+ tzset();
+ }
+ HasCTZSet = false;
}
-
- HasCTZSet = true;
}
else
{
- char *tok;
- char *value;
-
- value = p->val.val.str;
-
- while ((value = get_token(&tok, NULL, value)) != 0)
+ /*
+ * Otherwise assume it is a timezone name.
+ *
+ * XXX unfortunately we have no reasonable way to check whether a
+ * timezone name is good, so we have to just assume that it is.
+ */
+ if (doit)
{
- /* 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);
-
+ strncat(tzbuf, value, sizeof(tzbuf)-4);
+ if (putenv(tzbuf) != 0) /* shouldn't happen? */
+ elog(LOG, "assign_timezone: putenv failed");
tzset();
+ HasCTZSet = false;
}
-
- HasCTZSet = false;
}
}
- return TRUE;
-} /* parse_timezone() */
+ /*
+ * If we aren't going to do the assignment, just return OK indicator.
+ */
+ if (!doit)
+ return value;
+
+ /*
+ * Prepare the canonical string to return. GUC wants it malloc'd.
+ */
+ result = (char *) malloc(sizeof(tzbuf));
+ if (!result)
+ return NULL;
+
+ if (HasCTZSet)
+ {
+ snprintf(result, sizeof(tzbuf), "%.5f",
+ (double) CTimeZone / 3600.0);
+ }
+ else if (tzbuf[0] == 'T')
+ {
+ strcpy(result, tzbuf + 3);
+ }
+ else
+ {
+ strcpy(result, "UNKNOWN");
+ }
+
+ return result;
+}
-static bool
+/*
+ * show_timezone: GUC show_hook for timezone
+ */
+const char *
show_timezone(void)
{
char *tzn;
@@ -516,186 +406,68 @@ show_timezone(void)
interval.month = 0;
interval.time = CTimeZone;
- tzn = DatumGetCString(DirectFunctionCall1(interval_out, IntervalPGetDatum(&interval)));
+ tzn = DatumGetCString(DirectFunctionCall1(interval_out,
+ IntervalPGetDatum(&interval)));
}
else
tzn = getenv("TZ");
if (tzn != NULL)
- elog(INFO, "Time zone is '%s'", tzn);
- else
- elog(INFO, "Time zone is unset");
-
- 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(void)
-{
- if (HasCTZSet)
- HasCTZSet = false;
-
- /* no time zone has been set in this session? */
- else 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() */
+ return tzn;
+ return "unknown";
+}
/*
- *
- * SET TRANSACTION
- *
+ * SET TRANSACTION ISOLATION LEVEL
*/
-static bool
-parse_XactIsoLevel(List *args)
+const char *
+assign_XactIsoLevel(const char *value, bool doit, bool interactive)
{
- char *value;
-
- if (args == NULL)
- return reset_XactIsoLevel();
-
- Assert(IsA(args, List));
- Assert(IsA(lfirst(args), A_Const));
- /* Should only get one argument from the parser */
- if (lnext(args) != NIL)
- elog(ERROR, "SET TRANSACTION ISOLATION LEVEL takes only one argument");
-
- Assert(((A_Const *) lfirst(args))->val.type = T_String);
- value = ((A_Const *) lfirst(args))->val.val.str;
-
- if (SerializableSnapshot != NULL)
- {
+ if (doit && interactive && SerializableSnapshot != NULL)
elog(ERROR, "SET TRANSACTION ISOLATION LEVEL must be called before any query");
- return TRUE;
- }
if (strcmp(value, "serializable") == 0)
- XactIsoLevel = XACT_SERIALIZABLE;
+ { if (doit) XactIsoLevel = XACT_SERIALIZABLE; }
else if (strcmp(value, "read committed") == 0)
- XactIsoLevel = XACT_READ_COMMITTED;
+ { if (doit) XactIsoLevel = XACT_READ_COMMITTED; }
+ else if (strcmp(value, "default") == 0)
+ { if (doit) XactIsoLevel = DefaultXactIsoLevel; }
else
- elog(ERROR, "invalid transaction isolation level: %s", value);
+ return NULL;
- return TRUE;
+ return value;
}
-static bool
+const char *
show_XactIsoLevel(void)
{
-
if (XactIsoLevel == XACT_SERIALIZABLE)
- elog(INFO, "TRANSACTION ISOLATION LEVEL is SERIALIZABLE");
+ return "SERIALIZABLE";
else
- elog(INFO, "TRANSACTION ISOLATION LEVEL is READ COMMITTED");
- return TRUE;
-}
-
-static bool
-reset_XactIsoLevel(void)
-{
-
- if (SerializableSnapshot != NULL)
- {
- elog(ERROR, "SET TRANSACTION ISOLATION LEVEL must be called before any query");
- return TRUE;
- }
-
- XactIsoLevel = DefaultXactIsoLevel;
-
- return TRUE;
+ return "READ COMMITTED";
}
/*
* Random number seed
*/
-static bool
-parse_random_seed(List *args)
-{
- A_Const *p;
- double seed = 0;
-
- if (args == NULL)
- return reset_random_seed();
-
- Assert(IsA(args, List));
- /* Should only get one argument from the parser */
- if (lnext(args) != NIL)
- elog(ERROR, "SET SEED takes only one argument");
-
- p = lfirst(args);
- Assert(IsA(p, A_Const));
- if ((p->val.type == T_String)
- || (p->val.type == T_Float))
- {
- seed = DatumGetFloat8(DirectFunctionCall1(float8in, CStringGetDatum(p->val.val.str)));
- }
- else if (p->val.type == T_Integer)
- {
- seed = p->val.val.ival;
- }
- else
- {
- elog(ERROR, "SET SEED internal coding error");
- }
-
- DirectFunctionCall1(setseed, Float8GetDatum(seed));
-
- return (TRUE);
-}
-
-static bool
-show_random_seed(void)
+bool
+assign_random_seed(double value, bool doit, bool interactive)
{
- elog(INFO, "Seed for random number generator is unavailable");
- return (TRUE);
+ /* Can't really roll back on error, so ignore non-interactive setting */
+ if (doit && interactive)
+ DirectFunctionCall1(setseed, Float8GetDatum(value));
+ return true;
}
-static bool
-reset_random_seed(void)
+const char *
+show_random_seed(void)
{
- double seed = 0.5;
-
- DirectFunctionCall1(setseed, Float8GetDatum(seed));
- return (TRUE);
+ return "unavailable";
}
@@ -708,259 +480,108 @@ reset_random_seed(void)
* clients.
*/
-static bool
-parse_client_encoding(List *args)
+const char *
+assign_client_encoding(const char *value, bool doit, bool interactive)
{
- char *value;
-
#ifdef MULTIBYTE
int encoding;
-#endif
-
- if (args == NULL)
- return reset_client_encoding();
-
- if (lnext(args) != NIL)
- elog(ERROR, "SET CLIENT ENCODING takes only one argument");
-
- Assert(IsA(lfirst(args), A_Const));
- if (((A_Const *) lfirst(args))->val.type != T_String)
- {
- elog(ERROR, "SET CLIENT_ENCODING requires an encoding name");
- }
-
- value = ((A_Const *) lfirst(args))->val.val.str;
+ int old_encoding = 0;
-#ifdef MULTIBYTE
encoding = pg_valid_client_encoding(value);
if (encoding < 0)
+ return NULL;
+ /*
+ * Ugly API here ... can't test validity without setting new encoding...
+ */
+ if (!doit)
+ old_encoding = pg_get_client_encoding();
+ if (pg_set_client_encoding(encoding) < 0)
{
- if (value)
- elog(ERROR, "Client encoding '%s' is not supported", value);
- else
- elog(ERROR, "No client encoding is specified");
- }
- else
- {
- if (pg_set_client_encoding(encoding) < 0)
- {
+ if (interactive)
elog(ERROR, "Conversion between %s and %s is not supported",
value, GetDatabaseEncodingName());
- }
+ return NULL;
}
+ if (!doit)
+ pg_set_client_encoding(old_encoding);
#else
- if (value &&
- strcasecmp(value, pg_get_client_encoding_name()) != 0)
- elog(ERROR, "Client encoding %s is not supported", value);
-#endif
- return TRUE;
-}
-
-static bool
-show_client_encoding(void)
-{
- elog(INFO, "Current client encoding is '%s'",
- pg_get_client_encoding_name());
- return TRUE;
-}
-
-static bool
-reset_client_encoding(void)
-{
-#ifdef MULTIBYTE
- int encoding;
- char *env = getenv("PGCLIENTENCODING");
-
- if (env)
- {
- encoding = pg_char_to_encoding(env);
- if (encoding < 0)
- encoding = GetDatabaseEncoding();
- }
- else
- encoding = GetDatabaseEncoding();
-
- pg_set_client_encoding(encoding);
+ if (strcasecmp(value, pg_get_client_encoding_name()) != 0)
+ return NULL;
#endif
- return TRUE;
-}
-/* Called during MULTIBYTE backend startup ... */
-void
-set_default_client_encoding(void)
-{
- reset_client_encoding();
+ return value;
}
-static bool
-parse_server_encoding(List *args)
+const char *
+assign_server_encoding(const char *value, bool doit, bool interactive)
{
- elog(INFO, "SET SERVER_ENCODING is not supported");
- return TRUE;
+ if (interactive)
+ elog(ERROR, "SET SERVER_ENCODING is not supported");
+ /* Pretend never to fail in noninteractive case */
+ return value;
}
-static bool
+const char *
show_server_encoding(void)
{
- elog(INFO, "Current server encoding is '%s'", GetDatabaseEncodingName());
- return TRUE;
-}
-
-static bool
-reset_server_encoding(void)
-{
- elog(INFO, "RESET SERVER_ENCODING is not supported");
- return TRUE;
+ return GetDatabaseEncodingName();
}
-static bool
-show_session_authorization(void)
+/*
+ * SET SESSION AUTHORIZATION
+ *
+ * Note: when resetting session auth after an error, we can't expect to do
+ * catalog lookups. Hence, the stored form of the value is always a numeric
+ * userid that can be re-used directly.
+ */
+const char *
+assign_session_authorization(const char *value, bool doit, bool interactive)
{
- elog(INFO, "Current session authorization is '%s'",
- GetUserName(GetSessionUserId()));
- return TRUE;
-}
+ Oid usesysid;
+ char *endptr;
+ char *result;
+ usesysid = (Oid) strtoul(value, &endptr, 10);
-
-/* SetPGVariable()
- * Dispatcher for handling SET commands.
- * Special cases ought to be removed and handled separately by TCOP
- */
-void
-SetPGVariable(const char *name, List *args)
-{
- if (strcasecmp(name, "datestyle") == 0)
- parse_datestyle(args);
- else if (strcasecmp(name, "timezone") == 0)
- parse_timezone(args);
- else if (strcasecmp(name, "XactIsoLevel") == 0)
- parse_XactIsoLevel(args);
- else if (strcasecmp(name, "client_encoding") == 0)
- parse_client_encoding(args);
- else if (strcasecmp(name, "server_encoding") == 0)
- parse_server_encoding(args);
- else if (strcasecmp(name, "seed") == 0)
- parse_random_seed(args);
+ if (endptr != value && *endptr == '\0' && OidIsValid(usesysid))
+ {
+ /* use the numeric user ID */
+ }
else
{
- /*
- * For routines defined somewhere else, go ahead and extract the
- * string argument to match the original interface definition.
- * Later, we can change this code too...
- */
- char *value;
+ HeapTuple userTup;
- if (args != NULL)
+ userTup = SearchSysCache(SHADOWNAME,
+ PointerGetDatum(value),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(userTup))
{
- A_Const *n;
-
- /* Ensure one argument only... */
- if (lnext(args) != NIL)
- elog(ERROR, "SET %s takes only one argument", name);
-
- n = (A_Const *) lfirst(args);
- if ((n->val.type == T_String)
- || (n->val.type == T_Float))
- {
- value = n->val.val.str;
- }
- else if (n->val.type == T_Integer)
- {
- /* We should convert back to a string. */
- value = DatumGetCString(DirectFunctionCall1(int4out, Int32GetDatum(n->val.val.ival)));
- }
- else
- {
- elog(ERROR, "SET %s accepts a string argument for this parameter"
- "\n\tInternal coding error: report to thomas@fourpalms.org",
- name);
- value = NULL;
- }
- }
- else
- {
- value = NULL;
+ if (interactive)
+ elog(ERROR, "user \"%s\" does not exist", value);
+ return NULL;
}
- if (strcasecmp(name, "session_authorization") == 0)
- SetSessionAuthorization(value);
- else
- SetConfigOption(name,
- value,
- (superuser() ? PGC_SUSET : PGC_USERSET),
- PGC_S_SESSION);
- }
- return;
-}
+ usesysid = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid;
-void
-GetPGVariable(const char *name)
-{
- if (strcasecmp(name, "datestyle") == 0)
- show_datestyle();
- else if (strcasecmp(name, "timezone") == 0)
- show_timezone();
- else if (strcasecmp(name, "XactIsoLevel") == 0)
- show_XactIsoLevel();
- else if (strcasecmp(name, "client_encoding") == 0)
- show_client_encoding();
- else if (strcasecmp(name, "server_encoding") == 0)
- show_server_encoding();
- else if (strcasecmp(name, "seed") == 0)
- show_random_seed();
- else if (strcasecmp(name, "session_authorization") == 0)
- show_session_authorization();
- else if (strcasecmp(name, "all") == 0)
- {
- ShowAllGUCConfig();
- show_datestyle();
- show_timezone();
- show_XactIsoLevel();
- show_client_encoding();
- show_server_encoding();
- show_random_seed();
+ ReleaseSysCache(userTup);
}
- else
- {
- const char *val = GetConfigOption(name);
- elog(INFO, "%s is %s", name, val);
- }
+ if (doit)
+ SetSessionAuthorization(usesysid);
+
+ result = (char *) malloc(32);
+ if (!result)
+ return NULL;
+
+ snprintf(result, 32, "%lu", (unsigned long) usesysid);
+
+ return result;
}
-void
-ResetPGVariable(const char *name)
+const char *
+show_session_authorization(void)
{
- if (strcasecmp(name, "datestyle") == 0)
- reset_datestyle();
- else if (strcasecmp(name, "timezone") == 0)
- reset_timezone();
- else if (strcasecmp(name, "XactIsoLevel") == 0)
- reset_XactIsoLevel();
- else if (strcasecmp(name, "client_encoding") == 0)
- reset_client_encoding();
- else if (strcasecmp(name, "server_encoding") == 0)
- reset_server_encoding();
- else if (strcasecmp(name, "seed") == 0)
- reset_random_seed();
- else if (strcasecmp(name, "session_authorization") == 0)
- SetSessionAuthorization(NULL);
- else if (strcasecmp(name, "all") == 0)
- {
- reset_random_seed();
- /* reset_server_encoding(); */
- reset_client_encoding();
- reset_datestyle();
- reset_timezone();
- /* should we reset session authorization here? */
-
- ResetAllOptions(false);
- }
- else
- SetConfigOption(name, NULL,
- superuser() ? PGC_SUSET : PGC_USERSET,
- PGC_S_SESSION);
+ return GetUserName(GetSessionUserId());
}
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index 9c79e7b78d9..227fcc35e5e 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.50 2002/04/03 05:39:29 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.51 2002/05/17 01:19:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -125,7 +125,7 @@ main(int argc, char *argv[])
* and COLLATE will be overridden later from pg_control if we are
* in an already-initialized database. We set them here so that
* they will be available to fill pg_control during initdb. The
- * other ones will get reset later in ResetAllOptions, but we set
+ * other ones will get reset later in InitializeGUCOptions, but we set
* them here to get already localized behavior during startup
* (e.g., error messages).
*/
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a3751d36984..909847dc856 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.185 2002/05/13 20:39:43 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.186 2002/05/17 01:19:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2342,6 +2342,7 @@ _copyVariableSetStmt(VariableSetStmt *from)
if (from->name)
newnode->name = pstrdup(from->name);
Node_Copy(from, newnode, args);
+ newnode->is_local = from->is_local;
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index cf203243f11..1f0d175326b 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.132 2002/05/12 23:43:02 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.133 2002/05/17 01:19:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1173,6 +1173,8 @@ _equalVariableSetStmt(VariableSetStmt *a, VariableSetStmt *b)
return false;
if (!equal(a->args, b->args))
return false;
+ if (a->is_local != b->is_local)
+ return false;
return true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index bd3a7a0832b..0009b9df2f0 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.315 2002/05/13 17:45:30 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.316 2002/05/17 01:19:17 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -128,6 +128,7 @@ static void doNegateFloat(Value *v);
PrivTarget *privtarget;
InsertStmt *istmt;
+ VariableSetStmt *vsetstmt;
}
%type <node> stmt, schema_stmt,
@@ -246,6 +247,8 @@ static void doNegateFloat(Value *v);
%type <istmt> insert_rest
+%type <vsetstmt> set_rest
+
%type <node> OptTableElement, ConstraintElem
%type <node> columnDef
%type <defelt> def_elem
@@ -563,12 +566,12 @@ AlterUserStmt: ALTER USER UserId OptUserList
;
-AlterUserSetStmt: ALTER USER UserId VariableSetStmt
+AlterUserSetStmt: ALTER USER UserId SET set_rest
{
AlterUserSetStmt *n = makeNode(AlterUserSetStmt);
n->user = $3;
- n->variable = ((VariableSetStmt *)$4)->name;
- n->value = ((VariableSetStmt *)$4)->args;
+ n->variable = $5->name;
+ n->value = $5->args;
$$ = (Node *)n;
}
| ALTER USER UserId VariableResetStmt
@@ -576,7 +579,7 @@ AlterUserSetStmt: ALTER USER UserId VariableSetStmt
AlterUserSetStmt *n = makeNode(AlterUserSetStmt);
n->user = $3;
n->variable = ((VariableResetStmt *)$4)->name;
- n->value = NULL;
+ n->value = NIL;
$$ = (Node *)n;
}
;
@@ -834,63 +837,83 @@ schema_stmt: CreateStmt
*
*****************************************************************************/
-VariableSetStmt: SET ColId TO var_list_or_default
+VariableSetStmt: SET set_rest
{
- VariableSetStmt *n = makeNode(VariableSetStmt);
- n->name = $2;
- n->args = $4;
+ VariableSetStmt *n = $2;
+ n->is_local = false;
$$ = (Node *) n;
}
- | SET ColId '=' var_list_or_default
+ | SET LOCAL set_rest
{
- VariableSetStmt *n = makeNode(VariableSetStmt);
- n->name = $2;
- n->args = $4;
+ VariableSetStmt *n = $3;
+ n->is_local = true;
$$ = (Node *) n;
}
- | SET TIME ZONE zone_value
+ | SET SESSION set_rest
{
- VariableSetStmt *n = makeNode(VariableSetStmt);
- n->name = "timezone";
- if ($4 != NULL)
- n->args = makeList1($4);
+ VariableSetStmt *n = $3;
+ n->is_local = false;
$$ = (Node *) n;
}
- | SET TRANSACTION ISOLATION LEVEL opt_level
+ ;
+
+set_rest: ColId TO var_list_or_default
{
VariableSetStmt *n = makeNode(VariableSetStmt);
- n->name = "XactIsoLevel";
- n->args = makeList1(makeStringConst($5, NULL));
- $$ = (Node *) n;
+ n->name = $1;
+ n->args = $3;
+ $$ = n;
}
- | SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL opt_level
+ | ColId '=' var_list_or_default
{
VariableSetStmt *n = makeNode(VariableSetStmt);
- n->name = "default_transaction_isolation";
- n->args = makeList1(makeStringConst($8, NULL));
- $$ = (Node *) n;
+ n->name = $1;
+ n->args = $3;
+ $$ = n;
}
- | SET NAMES opt_encoding
+ | TIME ZONE zone_value
{
VariableSetStmt *n = makeNode(VariableSetStmt);
- n->name = "client_encoding";
+ n->name = "timezone";
if ($3 != NULL)
- n->args = makeList1(makeStringConst($3, NULL));
- $$ = (Node *) n;
+ n->args = makeList1($3);
+ $$ = n;
}
- | SET SESSION AUTHORIZATION ColId_or_Sconst
+ | TRANSACTION ISOLATION LEVEL opt_level
{
VariableSetStmt *n = makeNode(VariableSetStmt);
- n->name = "session_authorization";
+ n->name = "TRANSACTION ISOLATION LEVEL";
n->args = makeList1(makeStringConst($4, NULL));
- $$ = (Node *) n;
+ $$ = n;
+ }
+ | SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL opt_level
+ {
+ VariableSetStmt *n = makeNode(VariableSetStmt);
+ n->name = "default_transaction_isolation";
+ n->args = makeList1(makeStringConst($7, NULL));
+ $$ = n;
+ }
+ | NAMES opt_encoding
+ {
+ VariableSetStmt *n = makeNode(VariableSetStmt);
+ n->name = "client_encoding";
+ if ($2 != NULL)
+ n->args = makeList1(makeStringConst($2, NULL));
+ $$ = n;
+ }
+ | SESSION AUTHORIZATION ColId_or_Sconst
+ {
+ VariableSetStmt *n = makeNode(VariableSetStmt);
+ n->name = "session_authorization";
+ n->args = makeList1(makeStringConst($3, NULL));
+ $$ = n;
}
- | SET SESSION AUTHORIZATION DEFAULT
+ | SESSION AUTHORIZATION DEFAULT
{
VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "session_authorization";
n->args = NIL;
- $$ = (Node *) n;
+ $$ = n;
}
;
@@ -926,10 +949,10 @@ opt_boolean: TRUE_P { $$ = "true"; }
/* Timezone values can be:
* - a string such as 'pst8pdt'
- * - a column identifier such as "pst8pdt"
+ * - an identifier such as "pst8pdt"
* - an integer or floating point number
* - a time interval per SQL99
- * ConstInterval and ColId give shift/reduce errors,
+ * ColId gives reduce/reduce errors against ConstInterval and LOCAL,
* so use IDENT and reject anything which is a reserved word.
*/
zone_value: Sconst
@@ -988,25 +1011,31 @@ ColId_or_Sconst: ColId { $$ = $1; }
VariableShowStmt: SHOW ColId
{
VariableShowStmt *n = makeNode(VariableShowStmt);
- n->name = $2;
+ n->name = $2;
$$ = (Node *) n;
}
| SHOW TIME ZONE
{
VariableShowStmt *n = makeNode(VariableShowStmt);
- n->name = "timezone";
+ n->name = "timezone";
$$ = (Node *) n;
}
- | SHOW ALL
+ | SHOW TRANSACTION ISOLATION LEVEL
{
VariableShowStmt *n = makeNode(VariableShowStmt);
- n->name = "all";
+ n->name = "TRANSACTION ISOLATION LEVEL";
$$ = (Node *) n;
}
- | SHOW TRANSACTION ISOLATION LEVEL
+ | SHOW SESSION AUTHORIZATION
+ {
+ VariableShowStmt *n = makeNode(VariableShowStmt);
+ n->name = "session_authorization";
+ $$ = (Node *) n;
+ }
+ | SHOW ALL
{
VariableShowStmt *n = makeNode(VariableShowStmt);
- n->name = "XactIsoLevel";
+ n->name = "all";
$$ = (Node *) n;
}
;
@@ -1014,19 +1043,19 @@ VariableShowStmt: SHOW ColId
VariableResetStmt: RESET ColId
{
VariableResetStmt *n = makeNode(VariableResetStmt);
- n->name = $2;
+ n->name = $2;
$$ = (Node *) n;
}
| RESET TIME ZONE
{
VariableResetStmt *n = makeNode(VariableResetStmt);
- n->name = "timezone";
+ n->name = "timezone";
$$ = (Node *) n;
}
| RESET TRANSACTION ISOLATION LEVEL
{
VariableResetStmt *n = makeNode(VariableResetStmt);
- n->name = "XactIsoLevel";
+ n->name = "TRANSACTION ISOLATION LEVEL";
$$ = (Node *) n;
}
| RESET SESSION AUTHORIZATION
@@ -1038,7 +1067,7 @@ VariableResetStmt: RESET ColId
| RESET ALL
{
VariableResetStmt *n = makeNode(VariableResetStmt);
- n->name = "all";
+ n->name = "all";
$$ = (Node *) n;
}
;
@@ -3329,12 +3358,12 @@ opt_equal: '=' { $$ = TRUE; }
*
*****************************************************************************/
-AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt
+AlterDatabaseSetStmt: ALTER DATABASE database_name SET set_rest
{
AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt);
n->dbname = $3;
- n->variable = ((VariableSetStmt *)$4)->name;
- n->value = ((VariableSetStmt *)$4)->args;
+ n->variable = $5->name;
+ n->value = $5->args;
$$ = (Node *)n;
}
| ALTER DATABASE database_name VariableResetStmt
@@ -3342,7 +3371,7 @@ AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt
AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt);
n->dbname = $3;
n->variable = ((VariableResetStmt *)$4)->name;
- n->value = NULL;
+ n->value = NIL;
$$ = (Node *)n;
}
;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index a3a8f2521e5..1513f13878d 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.273 2002/05/05 00:03:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.274 2002/05/17 01:19:17 tgl Exp $
*
* NOTES
*
@@ -404,12 +404,7 @@ PostmasterMain(int argc, char *argv[])
/*
* Options setup
*/
- ResetAllOptions(true);
-
- /* PGPORT environment variable, if set, overrides GUC setting */
- if (getenv("PGPORT"))
- SetConfigOption("port", getenv("PGPORT"),
- PGC_POSTMASTER, PGC_S_ARGV/*sortof*/);
+ InitializeGUCOptions();
potential_DataDir = getenv("PGDATA"); /* default value */
@@ -443,8 +438,8 @@ PostmasterMain(int argc, char *argv[])
/* Turn on debugging for the postmaster. */
char *debugstr = palloc(strlen("debug") + strlen(optarg) + 1);
sprintf(debugstr, "debug%s", optarg);
- /* We use PGC_S_SESSION because we will reset in backend */
- SetConfigOption("server_min_messages", debugstr, PGC_POSTMASTER, PGC_S_SESSION);
+ SetConfigOption("server_min_messages", debugstr,
+ PGC_POSTMASTER, PGC_S_ARGV);
pfree(debugstr);
break;
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index eaf626ace95..53bf45dce9c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.264 2002/05/10 20:22:13 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.265 2002/05/17 01:19:18 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -37,7 +37,6 @@
#include "access/xlog.h"
#include "commands/async.h"
#include "commands/trigger.h"
-#include "commands/variable.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
@@ -1184,13 +1183,10 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
- ResetAllOptions(true);
+ InitializeGUCOptions();
potential_DataDir = getenv("PGDATA");
}
- /* Check for PGDATESTYLE environment variable */
- set_default_datestyle();
-
/* ----------------
* parse command line arguments
*
@@ -1273,9 +1269,10 @@ PostgresMain(int argc, char *argv[], const char *username)
else
/*
* -d 0 allows user to prevent postmaster debug from
- * propogating to backend.
+ * propagating to backend.
*/
- SetConfigOption("server_min_messages", "notice", PGC_POSTMASTER, PGC_S_ARGV);
+ SetConfigOption("server_min_messages", "notice",
+ ctx, gucsource);
}
break;
@@ -1292,7 +1289,7 @@ PostgresMain(int argc, char *argv[], const char *username)
/*
* Use european date formats.
*/
- EuroDates = true;
+ SetConfigOption("datestyle", "euro", ctx, gucsource);
break;
case 'F':
@@ -1691,7 +1688,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.264 $ $Date: 2002/05/10 20:22:13 $\n");
+ puts("$Revision: 1.265 $ $Date: 2002/05/17 01:19:18 $\n");
}
/*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 9a9c062559e..e72d8dfccf7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.153 2002/04/30 01:26:26 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.154 2002/05/17 01:19:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,7 +36,6 @@
#include "commands/trigger.h"
#include "commands/user.h"
#include "commands/vacuum.h"
-#include "commands/variable.h"
#include "commands/view.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -48,6 +47,7 @@
#include "rewrite/rewriteRemove.h"
#include "tcop/utility.h"
#include "utils/acl.h"
+#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "access/xlog.h"
@@ -718,7 +718,7 @@ ProcessUtility(Node *parsetree,
{
VariableSetStmt *n = (VariableSetStmt *) parsetree;
- SetPGVariable(n->name, n->args);
+ SetPGVariable(n->name, n->args, n->is_local);
}
break;
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index 1a908d9d6b7..d6e0358e817 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -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;
}
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index c5c8d312d3f..ba962ac8b15 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -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
-}
+/* GUC assign hooks */
-bool locale_monetary_check(const char *proposed)
+static const char *
+locale_xxx_assign(int category, const char *value, bool doit, bool interactive)
{
- return chklocale(LC_MONETARY, proposed);
-}
+ if (doit)
+ {
+ if (!setlocale(category, value))
+ return NULL;
+ }
+ else
+ {
+ char *save;
-bool locale_numeric_check(const char *proposed)
-{
- return chklocale(LC_NUMERIC, proposed);
-}
+ save = setlocale(category, NULL);
+ if (!save)
+ return NULL;
-bool locale_time_check(const char *proposed)
-{
- return chklocale(LC_TIME, proposed);
-}
+ if (!setlocale(category, value))
+ return NULL;
-/* GUC assign hooks */
+ setlocale(category, save);
+ }
+ return value;
+}
-void locale_messages_assign(const char *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)
-{
- setlocale(LC_MONETARY, value);
-}
-
-void locale_numeric_assign(const char *value)
+const char *
+locale_monetary_assign(const char *value, bool doit, bool interactive)
{
- setlocale(LC_NUMERIC, value);
+ return locale_xxx_assign(LC_MONETARY, value, doit, interactive);
}
-void locale_time_assign(const char *value)
+const char *
+locale_numeric_assign(const char *value, bool doit, bool interactive)
{
- setlocale(LC_TIME, value);
+ return locale_xxx_assign(LC_NUMERIC, value, doit, interactive);
}
-
-/*
- * 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)
+const char *
+locale_time_assign(const char *value, bool doit, bool interactive)
{
- 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;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 78cb6306e02..88528b9a065 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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;
}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 572e21ba831..a1813b67b83 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -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)
-{
- 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)
+const char *
+assign_server_min_messages(const char *newval,
+ bool doit, bool interactive)
{
- 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)
-{
- 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)
+const char *
+assign_client_min_messages(const char *newval,
+ bool doit, bool interactive)
{
- 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 */
}
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 4cc9d396c70..fd3f191d5cd 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -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);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 95974c6d6a6..817c87c80a0 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -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
diff --git a/src/backend/utils/misc/README b/src/backend/utils/misc/README
new file mode 100644
index 00000000000..fe252d534de
--- /dev/null
+++ b/src/backend/utils/misc/README
@@ -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.
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index fe6cf89ac0c..2f50b4b55c6 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -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);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index f917af9a422..f1d4e8ee291 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3,8 +3,9 @@
*
* Support for grand unified configuration scheme, including SET
* command, configuration file, and command line options.
+ * See src/backend/utils/misc/README for more information.
*
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.67 2002/05/14 13:05:43 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.68 2002/05/17 01:19:18 tgl Exp $
*
* Copyright 2000 by PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -23,6 +24,7 @@
#include "access/xlog.h"
#include "catalog/namespace.h"
#include "commands/async.h"
+#include "commands/variable.h"
#include "fmgr.h"
#include "libpq/auth.h"
#include "libpq/pqcomm.h"
@@ -57,13 +59,10 @@ extern bool FixBTree;
#ifdef HAVE_SYSLOG
extern char *Syslog_facility;
extern char *Syslog_ident;
-static bool check_facility(const char *facility);
-#endif
-
-static char *default_iso_level_string;
-static bool check_defaultxactisolevel(const char *value);
-static void assign_defaultxactisolevel(const char *value);
+static const char *assign_facility(const char *facility,
+ bool doit, bool interactive);
+#endif
/*
* Debugging options
@@ -96,87 +95,167 @@ bool Password_encryption = false;
#define PG_KRB_SRVTAB ""
#endif
-static bool guc_session_init = false; /* XXX mildly bogus */
+/*
+ * These variables are all dummies that don't do anything, except in some
+ * cases provide the value for SHOW to display. The real state is elsewhere
+ * and is kept in sync by assign_hooks.
+ */
+static double phony_random_seed;
+static char *client_encoding_string;
+static char *datestyle_string;
+static char *default_iso_level_string;
+static char *server_encoding_string;
+static char *session_authorization_string;
+static char *timezone_string;
+static char *XactIsoLevel_string;
+
+static const char *assign_defaultxactisolevel(const char *newval,
+ bool doit, bool interactive);
+
/*
* Declarations for GUC tables
+ *
+ * See src/backend/utils/misc/README for design notes.
*/
enum config_type
{
- PGC_NONE = 0,
PGC_BOOL,
PGC_INT,
PGC_REAL,
PGC_STRING
};
-
+/* Generic fields applicable to all types of variables */
struct config_generic
{
- const char *name;
- GucContext context;
- GucSource source;
- void *variable;
+ /* constant fields, must be set correctly in initial value: */
+ const char *name; /* name of variable - MUST BE FIRST */
+ GucContext context; /* context required to set the variable */
+ int flags; /* flag bits, see below */
+ /* variable fields, initialized at runtime: */
+ enum config_type vartype; /* type of variable (set only at startup) */
+ int status; /* status bits, see below */
+ GucSource reset_source; /* source of the reset_value */
+ GucSource session_source; /* source of the session_value */
+ GucSource tentative_source; /* source of the tentative_value */
+ GucSource source; /* source of the current actual value */
};
+/* bit values in flags field */
+#define GUC_LIST_INPUT 0x0001 /* input can be list format */
+#define GUC_LIST_QUOTE 0x0002 /* double-quote list elements */
+#define GUC_NO_SHOW_ALL 0x0004 /* exclude from SHOW ALL */
+#define GUC_NO_RESET_ALL 0x0008 /* exclude from RESET ALL */
+
+/* bit values in status field */
+#define GUC_HAVE_TENTATIVE 0x0001 /* tentative value is defined */
+#define GUC_HAVE_LOCAL 0x0002 /* a SET LOCAL has been executed */
+
+
+/* GUC records for specific variable types */
struct config_bool
{
- const char *name;
- GucContext context;
- GucSource source;
+ struct config_generic gen;
+ /* these fields must be set correctly in initial value: */
+ /* (all but reset_val are constants) */
bool *variable;
- bool default_val;
- /* No need for parse_hook ... presumably both values are legal */
- void (*assign_hook) (bool newval);
+ bool reset_val;
+ bool (*assign_hook) (bool newval, bool doit, bool interactive);
+ const char *(*show_hook) (void);
+ /* variable fields, initialized at runtime: */
+ bool session_val;
+ bool tentative_val;
};
-
struct config_int
{
- const char *name;
- GucContext context;
- GucSource source;
+ struct config_generic gen;
+ /* these fields must be set correctly in initial value: */
+ /* (all but reset_val are constants) */
int *variable;
- int default_val;
+ int reset_val;
int min;
int max;
- bool (*parse_hook) (int proposed);
- void (*assign_hook) (int newval);
+ bool (*assign_hook) (int newval, bool doit, bool interactive);
+ const char *(*show_hook) (void);
+ /* variable fields, initialized at runtime: */
+ int session_val;
+ int tentative_val;
};
-
struct config_real
{
- const char *name;
- GucContext context;
- GucSource source;
+ struct config_generic gen;
+ /* these fields must be set correctly in initial value: */
+ /* (all but reset_val are constants) */
double *variable;
- double default_val;
+ double reset_val;
double min;
double max;
- bool (*parse_hook) (double proposed);
- void (*assign_hook) (double newval);
+ bool (*assign_hook) (double newval, bool doit, bool interactive);
+ const char *(*show_hook) (void);
+ /* variable fields, initialized at runtime: */
+ double session_val;
+ double tentative_val;
};
-/*
- * 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;
- GucSource source;
+ struct config_generic gen;
+ /* these fields must be set correctly in initial value: */
+ /* (all are constants) */
char **variable;
- const char *boot_default_val;
- bool (*parse_hook) (const char *proposed);
- void (*assign_hook) (const char *newval);
- char *default_val;
+ const char *boot_val;
+ const char *(*assign_hook) (const char *newval, bool doit, bool interactive);
+ const char *(*show_hook) (void);
+ /* variable fields, initialized at runtime: */
+ char *reset_val;
+ char *session_val;
+ char *tentative_val;
};
+/* Macros for freeing malloc'd pointers only if appropriate to do so */
+/* Some of these tests are probably redundant, but be safe ... */
+#define SET_STRING_VARIABLE(rec, newval) \
+ do { \
+ if (*(rec)->variable && \
+ *(rec)->variable != (rec)->reset_val && \
+ *(rec)->variable != (rec)->session_val && \
+ *(rec)->variable != (rec)->tentative_val) \
+ free(*(rec)->variable); \
+ *(rec)->variable = (newval); \
+ } while (0)
+#define SET_STRING_RESET_VAL(rec, newval) \
+ do { \
+ if ((rec)->reset_val && \
+ (rec)->reset_val != *(rec)->variable && \
+ (rec)->reset_val != (rec)->session_val && \
+ (rec)->reset_val != (rec)->tentative_val) \
+ free((rec)->reset_val); \
+ (rec)->reset_val = (newval); \
+ } while (0)
+#define SET_STRING_SESSION_VAL(rec, newval) \
+ do { \
+ if ((rec)->session_val && \
+ (rec)->session_val != *(rec)->variable && \
+ (rec)->session_val != (rec)->reset_val && \
+ (rec)->session_val != (rec)->tentative_val) \
+ free((rec)->session_val); \
+ (rec)->session_val = (newval); \
+ } while (0)
+#define SET_STRING_TENTATIVE_VAL(rec, newval) \
+ do { \
+ if ((rec)->tentative_val && \
+ (rec)->tentative_val != *(rec)->variable && \
+ (rec)->tentative_val != (rec)->reset_val && \
+ (rec)->tentative_val != (rec)->session_val) \
+ free((rec)->tentative_val); \
+ (rec)->tentative_val = (newval); \
+ } while (0)
+
+
/*
* TO ADD AN OPTION:
@@ -200,167 +279,212 @@ struct config_string
*/
-/******** option names follow ********/
+/******** option records follow ********/
static struct config_bool
ConfigureNamesBool[] =
{
{
- "enable_seqscan", PGC_USERSET, PGC_S_DEFAULT, &enable_seqscan, true, NULL
+ { "enable_seqscan", PGC_USERSET }, &enable_seqscan,
+ true, NULL, NULL
},
{
- "enable_indexscan", PGC_USERSET, PGC_S_DEFAULT, &enable_indexscan, true, NULL
+ { "enable_indexscan", PGC_USERSET }, &enable_indexscan,
+ true, NULL, NULL
},
{
- "enable_tidscan", PGC_USERSET, PGC_S_DEFAULT, &enable_tidscan, true, NULL
+ { "enable_tidscan", PGC_USERSET }, &enable_tidscan,
+ true, NULL, NULL
},
{
- "enable_sort", PGC_USERSET, PGC_S_DEFAULT, &enable_sort, true, NULL
+ { "enable_sort", PGC_USERSET }, &enable_sort,
+ true, NULL, NULL
},
{
- "enable_nestloop", PGC_USERSET, PGC_S_DEFAULT, &enable_nestloop, true, NULL
+ { "enable_nestloop", PGC_USERSET }, &enable_nestloop,
+ true, NULL, NULL
},
{
- "enable_mergejoin", PGC_USERSET, PGC_S_DEFAULT, &enable_mergejoin, true, NULL
+ { "enable_mergejoin", PGC_USERSET }, &enable_mergejoin,
+ true, NULL, NULL
},
{
- "enable_hashjoin", PGC_USERSET, PGC_S_DEFAULT, &enable_hashjoin, true, NULL
+ { "enable_hashjoin", PGC_USERSET }, &enable_hashjoin,
+ true, NULL, NULL
},
{
- "ksqo", PGC_USERSET, PGC_S_DEFAULT, &_use_keyset_query_optimizer, false, NULL
+ { "ksqo", PGC_USERSET }, &_use_keyset_query_optimizer,
+ false, NULL, NULL
},
{
- "geqo", PGC_USERSET, PGC_S_DEFAULT, &enable_geqo, true, NULL
+ { "geqo", PGC_USERSET }, &enable_geqo,
+ true, NULL, NULL
},
{
- "tcpip_socket", PGC_POSTMASTER, PGC_S_DEFAULT, &NetServer, false, NULL
+ { "tcpip_socket", PGC_POSTMASTER }, &NetServer,
+ false, NULL, NULL
},
{
- "ssl", PGC_POSTMASTER, PGC_S_DEFAULT, &EnableSSL, false, NULL
+ { "ssl", PGC_POSTMASTER }, &EnableSSL,
+ false, NULL, NULL
},
{
- "fsync", PGC_SIGHUP, PGC_S_DEFAULT, &enableFsync, true, NULL
+ { "fsync", PGC_SIGHUP }, &enableFsync,
+ true, NULL, NULL
},
{
- "silent_mode", PGC_POSTMASTER, PGC_S_DEFAULT, &SilentMode, false, NULL
+ { "silent_mode", PGC_POSTMASTER }, &SilentMode,
+ false, NULL, NULL
},
{
- "log_connections", PGC_BACKEND, PGC_S_DEFAULT, &Log_connections, false, NULL
+ { "log_connections", PGC_BACKEND }, &Log_connections,
+ false, NULL, NULL
},
{
- "log_timestamp", PGC_SIGHUP, PGC_S_DEFAULT, &Log_timestamp, false, NULL
+ { "log_timestamp", PGC_SIGHUP }, &Log_timestamp,
+ false, NULL, NULL
},
{
- "log_pid", PGC_SIGHUP, PGC_S_DEFAULT, &Log_pid, false, NULL
+ { "log_pid", PGC_SIGHUP }, &Log_pid,
+ false, NULL, NULL
},
#ifdef USE_ASSERT_CHECKING
{
- "debug_assertions", PGC_USERSET, PGC_S_DEFAULT, &assert_enabled, true, NULL
+ { "debug_assertions", PGC_USERSET }, &assert_enabled,
+ true, NULL, NULL
},
#endif
{
- "debug_print_query", PGC_USERSET, PGC_S_DEFAULT, &Debug_print_query, false, NULL
+ { "debug_print_query", PGC_USERSET }, &Debug_print_query,
+ false, NULL, NULL
},
{
- "debug_print_parse", PGC_USERSET, PGC_S_DEFAULT, &Debug_print_parse, false, NULL
+ { "debug_print_parse", PGC_USERSET }, &Debug_print_parse,
+ false, NULL, NULL
},
{
- "debug_print_rewritten", PGC_USERSET, PGC_S_DEFAULT, &Debug_print_rewritten, false, NULL
+ { "debug_print_rewritten", PGC_USERSET }, &Debug_print_rewritten,
+ false, NULL, NULL
},
{
- "debug_print_plan", PGC_USERSET, PGC_S_DEFAULT, &Debug_print_plan, false, NULL
+ { "debug_print_plan", PGC_USERSET }, &Debug_print_plan,
+ false, NULL, NULL
},
{
- "debug_pretty_print", PGC_USERSET, PGC_S_DEFAULT, &Debug_pretty_print, false, NULL
+ { "debug_pretty_print", PGC_USERSET }, &Debug_pretty_print,
+ false, NULL, NULL
},
{
- "show_parser_stats", PGC_USERSET, PGC_S_DEFAULT, &Show_parser_stats, false, NULL
+ { "show_parser_stats", PGC_USERSET }, &Show_parser_stats,
+ false, NULL, NULL
},
{
- "show_planner_stats", PGC_USERSET, PGC_S_DEFAULT, &Show_planner_stats, false, NULL
+ { "show_planner_stats", PGC_USERSET }, &Show_planner_stats,
+ false, NULL, NULL
},
{
- "show_executor_stats", PGC_USERSET, PGC_S_DEFAULT, &Show_executor_stats, false, NULL
+ { "show_executor_stats", PGC_USERSET }, &Show_executor_stats,
+ false, NULL, NULL
},
{
- "show_query_stats", PGC_USERSET, PGC_S_DEFAULT, &Show_query_stats, false, NULL
+ { "show_query_stats", PGC_USERSET }, &Show_query_stats,
+ false, NULL, NULL
},
#ifdef BTREE_BUILD_STATS
{
- "show_btree_build_stats", PGC_SUSET, PGC_S_DEFAULT, &Show_btree_build_stats, false, NULL
+ { "show_btree_build_stats", PGC_SUSET }, &Show_btree_build_stats,
+ false, NULL, NULL
},
#endif
{
- "explain_pretty_print", PGC_USERSET, PGC_S_DEFAULT, &Explain_pretty_print, true, NULL
+ { "explain_pretty_print", PGC_USERSET }, &Explain_pretty_print,
+ true, NULL, NULL
},
{
- "stats_start_collector", PGC_POSTMASTER, PGC_S_DEFAULT, &pgstat_collect_startcollector, true, NULL
+ { "stats_start_collector", PGC_POSTMASTER }, &pgstat_collect_startcollector,
+ true, NULL, NULL
},
{
- "stats_reset_on_server_start", PGC_POSTMASTER, PGC_S_DEFAULT, &pgstat_collect_resetonpmstart, true, NULL
+ { "stats_reset_on_server_start", PGC_POSTMASTER }, &pgstat_collect_resetonpmstart,
+ true, NULL, NULL
},
{
- "stats_command_string", PGC_SUSET, PGC_S_DEFAULT, &pgstat_collect_querystring, false, NULL
+ { "stats_command_string", PGC_SUSET }, &pgstat_collect_querystring,
+ false, NULL, NULL
},
{
- "stats_row_level", PGC_SUSET, PGC_S_DEFAULT, &pgstat_collect_tuplelevel, false, NULL
+ { "stats_row_level", PGC_SUSET }, &pgstat_collect_tuplelevel,
+ false, NULL, NULL
},
{
- "stats_block_level", PGC_SUSET, PGC_S_DEFAULT, &pgstat_collect_blocklevel, false, NULL
+ { "stats_block_level", PGC_SUSET }, &pgstat_collect_blocklevel,
+ false, NULL, NULL
},
{
- "trace_notify", PGC_USERSET, PGC_S_DEFAULT, &Trace_notify, false, NULL
+ { "trace_notify", PGC_USERSET }, &Trace_notify,
+ false, NULL, NULL
},
#ifdef LOCK_DEBUG
{
- "trace_locks", PGC_SUSET, PGC_S_DEFAULT, &Trace_locks, false, NULL
+ { "trace_locks", PGC_SUSET }, &Trace_locks,
+ false, NULL, NULL
},
{
- "trace_userlocks", PGC_SUSET, PGC_S_DEFAULT, &Trace_userlocks, false, NULL
+ { "trace_userlocks", PGC_SUSET }, &Trace_userlocks,
+ false, NULL, NULL
},
{
- "trace_lwlocks", PGC_SUSET, PGC_S_DEFAULT, &Trace_lwlocks, false, NULL
+ { "trace_lwlocks", PGC_SUSET }, &Trace_lwlocks,
+ false, NULL, NULL
},
{
- "debug_deadlocks", PGC_SUSET, PGC_S_DEFAULT, &Debug_deadlocks, false, NULL
+ { "debug_deadlocks", PGC_SUSET }, &Debug_deadlocks,
+ false, NULL, NULL
},
#endif
{
- "hostname_lookup", PGC_SIGHUP, PGC_S_DEFAULT, &HostnameLookup, false, NULL
+ { "hostname_lookup", PGC_SIGHUP }, &HostnameLookup,
+ false, NULL, NULL
},
{
- "show_source_port", PGC_SIGHUP, PGC_S_DEFAULT, &ShowPortNumber, false, NULL
+ { "show_source_port", PGC_SIGHUP }, &ShowPortNumber,
+ false, NULL, NULL
},
{
- "sql_inheritance", PGC_USERSET, PGC_S_DEFAULT, &SQL_inheritance, true, NULL
+ { "sql_inheritance", PGC_USERSET }, &SQL_inheritance,
+ true, NULL, NULL
},
{
- "australian_timezones", PGC_USERSET, PGC_S_DEFAULT, &Australian_timezones, false, ClearDateCache
+ { "australian_timezones", PGC_USERSET }, &Australian_timezones,
+ false, ClearDateCache, NULL
},
{
- "fixbtree", PGC_POSTMASTER, PGC_S_DEFAULT, &FixBTree, true, NULL
+ { "fixbtree", PGC_POSTMASTER }, &FixBTree,
+ true, NULL, NULL
},
{
- "password_encryption", PGC_USERSET, PGC_S_DEFAULT, &Password_encryption, false, NULL
+ { "password_encryption", PGC_USERSET }, &Password_encryption,
+ false, NULL, NULL
},
{
- "transform_null_equals", PGC_USERSET, PGC_S_DEFAULT, &Transform_null_equals, false, NULL
+ { "transform_null_equals", PGC_USERSET }, &Transform_null_equals,
+ false, NULL, NULL
},
{
- NULL, 0, 0, NULL, false, NULL
+ { NULL, 0 }, NULL, false, NULL, NULL
}
};
@@ -369,34 +493,34 @@ static struct config_int
ConfigureNamesInt[] =
{
{
- "geqo_threshold", PGC_USERSET, PGC_S_DEFAULT, &geqo_rels,
+ { "geqo_threshold", PGC_USERSET }, &geqo_rels,
DEFAULT_GEQO_RELS, 2, INT_MAX, NULL, NULL
},
{
- "geqo_pool_size", PGC_USERSET, PGC_S_DEFAULT, &Geqo_pool_size,
+ { "geqo_pool_size", PGC_USERSET }, &Geqo_pool_size,
DEFAULT_GEQO_POOL_SIZE, 0, MAX_GEQO_POOL_SIZE, NULL, NULL
},
{
- "geqo_effort", PGC_USERSET, PGC_S_DEFAULT, &Geqo_effort,
+ { "geqo_effort", PGC_USERSET }, &Geqo_effort,
1, 1, INT_MAX, NULL, NULL
},
{
- "geqo_generations", PGC_USERSET, PGC_S_DEFAULT, &Geqo_generations,
+ { "geqo_generations", PGC_USERSET }, &Geqo_generations,
0, 0, INT_MAX, NULL, NULL
},
{
- "geqo_random_seed", PGC_USERSET, PGC_S_DEFAULT, &Geqo_random_seed,
+ { "geqo_random_seed", PGC_USERSET }, &Geqo_random_seed,
-1, INT_MIN, INT_MAX, NULL, NULL
},
{
- "deadlock_timeout", PGC_POSTMASTER, PGC_S_DEFAULT, &DeadlockTimeout,
+ { "deadlock_timeout", PGC_POSTMASTER }, &DeadlockTimeout,
1000, 0, INT_MAX, NULL, NULL
},
#ifdef HAVE_SYSLOG
{
- "syslog", PGC_SIGHUP, PGC_S_DEFAULT, &Use_syslog,
+ { "syslog", PGC_SIGHUP }, &Use_syslog,
0, 0, 2, NULL, NULL
},
#endif
@@ -407,116 +531,116 @@ static struct config_int
* constraints here are partially unused.
*/
{
- "max_connections", PGC_POSTMASTER, PGC_S_DEFAULT, &MaxBackends,
+ { "max_connections", PGC_POSTMASTER }, &MaxBackends,
DEF_MAXBACKENDS, 1, INT_MAX, NULL, NULL
},
{
- "shared_buffers", PGC_POSTMASTER, PGC_S_DEFAULT, &NBuffers,
+ { "shared_buffers", PGC_POSTMASTER }, &NBuffers,
DEF_NBUFFERS, 16, INT_MAX, NULL, NULL
},
{
- "port", PGC_POSTMASTER, PGC_S_DEFAULT, &PostPortNumber,
+ { "port", PGC_POSTMASTER }, &PostPortNumber,
DEF_PGPORT, 1, 65535, NULL, NULL
},
{
- "unix_socket_permissions", PGC_POSTMASTER, PGC_S_DEFAULT, &Unix_socket_permissions,
+ { "unix_socket_permissions", PGC_POSTMASTER }, &Unix_socket_permissions,
0777, 0000, 0777, NULL, NULL
},
{
- "sort_mem", PGC_USERSET, PGC_S_DEFAULT, &SortMem,
+ { "sort_mem", PGC_USERSET }, &SortMem,
512, 4 * BLCKSZ / 1024, INT_MAX, NULL, NULL
},
{
- "vacuum_mem", PGC_USERSET, PGC_S_DEFAULT, &VacuumMem,
+ { "vacuum_mem", PGC_USERSET }, &VacuumMem,
8192, 1024, INT_MAX, NULL, NULL
},
{
- "max_files_per_process", PGC_BACKEND, PGC_S_DEFAULT, &max_files_per_process,
+ { "max_files_per_process", PGC_BACKEND }, &max_files_per_process,
1000, 25, INT_MAX, NULL, NULL
},
#ifdef LOCK_DEBUG
{
- "trace_lock_oidmin", PGC_SUSET, PGC_S_DEFAULT, &Trace_lock_oidmin,
+ { "trace_lock_oidmin", PGC_SUSET }, &Trace_lock_oidmin,
BootstrapObjectIdData, 1, INT_MAX, NULL, NULL
},
{
- "trace_lock_table", PGC_SUSET, PGC_S_DEFAULT, &Trace_lock_table,
+ { "trace_lock_table", PGC_SUSET }, &Trace_lock_table,
0, 0, INT_MAX, NULL, NULL
},
#endif
{
- "max_expr_depth", PGC_USERSET, PGC_S_DEFAULT, &max_expr_depth,
+ { "max_expr_depth", PGC_USERSET }, &max_expr_depth,
DEFAULT_MAX_EXPR_DEPTH, 10, INT_MAX, NULL, NULL
},
{
- "max_fsm_relations", PGC_POSTMASTER, PGC_S_DEFAULT, &MaxFSMRelations,
+ { "max_fsm_relations", PGC_POSTMASTER }, &MaxFSMRelations,
100, 10, INT_MAX, NULL, NULL
},
{
- "max_fsm_pages", PGC_POSTMASTER, PGC_S_DEFAULT, &MaxFSMPages,
+ { "max_fsm_pages", PGC_POSTMASTER }, &MaxFSMPages,
10000, 1000, INT_MAX, NULL, NULL
},
{
- "max_locks_per_transaction", PGC_POSTMASTER, PGC_S_DEFAULT, &max_locks_per_xact,
+ { "max_locks_per_transaction", PGC_POSTMASTER }, &max_locks_per_xact,
64, 10, INT_MAX, NULL, NULL
},
{
- "authentication_timeout", PGC_SIGHUP, PGC_S_DEFAULT, &AuthenticationTimeout,
+ { "authentication_timeout", PGC_SIGHUP }, &AuthenticationTimeout,
60, 1, 600, NULL, NULL
},
{
- "pre_auth_delay", PGC_SIGHUP, PGC_S_DEFAULT, &PreAuthDelay,
+ { "pre_auth_delay", PGC_SIGHUP }, &PreAuthDelay,
0, 0, 60, NULL, NULL
},
{
- "checkpoint_segments", PGC_SIGHUP, PGC_S_DEFAULT, &CheckPointSegments,
+ { "checkpoint_segments", PGC_SIGHUP }, &CheckPointSegments,
3, 1, INT_MAX, NULL, NULL
},
{
- "checkpoint_timeout", PGC_SIGHUP, PGC_S_DEFAULT, &CheckPointTimeout,
+ { "checkpoint_timeout", PGC_SIGHUP }, &CheckPointTimeout,
300, 30, 3600, NULL, NULL
},
{
- "wal_buffers", PGC_POSTMASTER, PGC_S_DEFAULT, &XLOGbuffers,
+ { "wal_buffers", PGC_POSTMASTER }, &XLOGbuffers,
8, 4, INT_MAX, NULL, NULL
},
{
- "wal_files", PGC_SIGHUP, PGC_S_DEFAULT, &XLOGfiles,
+ { "wal_files", PGC_SIGHUP }, &XLOGfiles,
0, 0, 64, NULL, NULL
},
{
- "wal_debug", PGC_SUSET, PGC_S_DEFAULT, &XLOG_DEBUG,
+ { "wal_debug", PGC_SUSET }, &XLOG_DEBUG,
0, 0, 16, NULL, NULL
},
{
- "commit_delay", PGC_USERSET, PGC_S_DEFAULT, &CommitDelay,
+ { "commit_delay", PGC_USERSET }, &CommitDelay,
0, 0, 100000, NULL, NULL
},
{
- "commit_siblings", PGC_USERSET, PGC_S_DEFAULT, &CommitSiblings,
+ { "commit_siblings", PGC_USERSET }, &CommitSiblings,
5, 1, 1000, NULL, NULL
},
{
- NULL, 0, 0, NULL, 0, 0, 0, NULL, NULL
+ { NULL, 0 }, NULL, 0, 0, 0, NULL, NULL
}
};
@@ -525,34 +649,40 @@ static struct config_real
ConfigureNamesReal[] =
{
{
- "effective_cache_size", PGC_USERSET, PGC_S_DEFAULT, &effective_cache_size,
+ { "effective_cache_size", PGC_USERSET }, &effective_cache_size,
DEFAULT_EFFECTIVE_CACHE_SIZE, 0, DBL_MAX, NULL, NULL
},
{
- "random_page_cost", PGC_USERSET, PGC_S_DEFAULT, &random_page_cost,
+ { "random_page_cost", PGC_USERSET }, &random_page_cost,
DEFAULT_RANDOM_PAGE_COST, 0, DBL_MAX, NULL, NULL
},
{
- "cpu_tuple_cost", PGC_USERSET, PGC_S_DEFAULT, &cpu_tuple_cost,
+ { "cpu_tuple_cost", PGC_USERSET }, &cpu_tuple_cost,
DEFAULT_CPU_TUPLE_COST, 0, DBL_MAX, NULL, NULL
},
{
- "cpu_index_tuple_cost", PGC_USERSET, PGC_S_DEFAULT, &cpu_index_tuple_cost,
+ { "cpu_index_tuple_cost", PGC_USERSET }, &cpu_index_tuple_cost,
DEFAULT_CPU_INDEX_TUPLE_COST, 0, DBL_MAX, NULL, NULL
},
{
- "cpu_operator_cost", PGC_USERSET, PGC_S_DEFAULT, &cpu_operator_cost,
+ { "cpu_operator_cost", PGC_USERSET }, &cpu_operator_cost,
DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX, NULL, NULL
},
{
- "geqo_selection_bias", PGC_USERSET, PGC_S_DEFAULT, &Geqo_selection_bias,
+ { "geqo_selection_bias", PGC_USERSET }, &Geqo_selection_bias,
DEFAULT_GEQO_SELECTION_BIAS, MIN_GEQO_SELECTION_BIAS,
MAX_GEQO_SELECTION_BIAS, NULL, NULL
},
{
- NULL, 0, 0, NULL, 0.0, 0.0, 0.0, NULL, NULL
+ { "seed", PGC_USERSET, GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL },
+ &phony_random_seed,
+ 0.5, 0.0, 1.0, assign_random_seed, show_random_seed
+ },
+
+ {
+ { NULL, 0 }, NULL, 0.0, 0.0, 0.0, NULL, NULL
}
};
@@ -561,228 +691,709 @@ static struct config_string
ConfigureNamesString[] =
{
{
- "client_min_messages", PGC_USERSET, PGC_S_DEFAULT, &client_min_messages_str,
- client_min_messages_str_default, check_client_min_messages,
- assign_client_min_messages
+ { "client_encoding", PGC_USERSET }, &client_encoding_string,
+ "SQL_ASCII", assign_client_encoding, NULL
},
{
- "default_transaction_isolation", PGC_USERSET, PGC_S_DEFAULT, &default_iso_level_string,
- "read committed", check_defaultxactisolevel, assign_defaultxactisolevel
+ { "client_min_messages", PGC_USERSET }, &client_min_messages_str,
+ client_min_messages_str_default, assign_client_min_messages, NULL
},
{
- "dynamic_library_path", PGC_SUSET, PGC_S_DEFAULT, &Dynamic_library_path,
- "$libdir", NULL, NULL
+ { "DateStyle", PGC_USERSET, GUC_LIST_INPUT }, &datestyle_string,
+ "ISO, US", assign_datestyle, show_datestyle
},
{
- "search_path", PGC_USERSET, PGC_S_DEFAULT, &namespace_search_path,
- "$user,public", check_search_path, assign_search_path
+ { "default_transaction_isolation", PGC_USERSET }, &default_iso_level_string,
+ "read committed", assign_defaultxactisolevel, NULL
},
{
- "krb_server_keyfile", PGC_POSTMASTER, PGC_S_DEFAULT, &pg_krb_server_keyfile,
+ { "dynamic_library_path", PGC_SUSET }, &Dynamic_library_path,
+ "$libdir", NULL, NULL
+ },
+
+ {
+ { "krb_server_keyfile", PGC_POSTMASTER }, &pg_krb_server_keyfile,
PG_KRB_SRVTAB, NULL, NULL
},
{
- "lc_messages", PGC_SUSET, PGC_S_DEFAULT, &locale_messages,
- "", locale_messages_check, locale_messages_assign
+ { "lc_messages", PGC_SUSET }, &locale_messages,
+ "", locale_messages_assign, NULL
+ },
+
+ {
+ { "lc_monetary", PGC_USERSET }, &locale_monetary,
+ "", locale_monetary_assign, NULL
+ },
+
+ {
+ { "lc_numeric", PGC_USERSET }, &locale_numeric,
+ "", locale_numeric_assign, NULL
},
{
- "lc_monetary", PGC_USERSET, PGC_S_DEFAULT, &locale_monetary,
- "", locale_monetary_check, locale_monetary_assign
+ { "lc_time", PGC_USERSET }, &locale_time,
+ "", locale_time_assign, NULL
},
{
- "lc_numeric", PGC_USERSET, PGC_S_DEFAULT, &locale_numeric,
- "", locale_numeric_check, locale_numeric_assign
+ { "search_path", PGC_USERSET, GUC_LIST_INPUT | GUC_LIST_QUOTE },
+ &namespace_search_path,
+ "$user,public", assign_search_path, NULL
},
{
- "lc_time", PGC_USERSET, PGC_S_DEFAULT, &locale_time,
- "", locale_time_check, locale_time_assign
+ { "server_encoding", PGC_USERSET }, &server_encoding_string,
+ "SQL_ASCII", assign_server_encoding, show_server_encoding
},
{
- "server_min_messages", PGC_USERSET, PGC_S_DEFAULT, &server_min_messages_str,
- server_min_messages_str_default, check_server_min_messages,
- assign_server_min_messages
+ { "server_min_messages", PGC_USERSET }, &server_min_messages_str,
+ server_min_messages_str_default, assign_server_min_messages, NULL
+ },
+
+ {
+ { "session_authorization", PGC_USERSET, GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL },
+ &session_authorization_string,
+ NULL, assign_session_authorization, show_session_authorization
},
#ifdef HAVE_SYSLOG
{
- "syslog_facility", PGC_POSTMASTER, PGC_S_DEFAULT, &Syslog_facility,
- "LOCAL0", check_facility, NULL
+ { "syslog_facility", PGC_POSTMASTER }, &Syslog_facility,
+ "LOCAL0", assign_facility, NULL
},
{
- "syslog_ident", PGC_POSTMASTER, PGC_S_DEFAULT, &Syslog_ident,
+ { "syslog_ident", PGC_POSTMASTER }, &Syslog_ident,
"postgres", NULL, NULL
},
#endif
{
- "unix_socket_group", PGC_POSTMASTER, PGC_S_DEFAULT, &Unix_socket_group,
+ { "TimeZone", PGC_USERSET }, &timezone_string,
+ "UNKNOWN", assign_timezone, show_timezone
+ },
+
+ {
+ { "TRANSACTION ISOLATION LEVEL", PGC_USERSET, GUC_NO_RESET_ALL },
+ &XactIsoLevel_string,
+ NULL, assign_XactIsoLevel, show_XactIsoLevel
+ },
+
+ {
+ { "unix_socket_group", PGC_POSTMASTER }, &Unix_socket_group,
"", NULL, NULL
},
{
- "unix_socket_directory", PGC_POSTMASTER, PGC_S_DEFAULT, &UnixSocketDir,
+ { "unix_socket_directory", PGC_POSTMASTER }, &UnixSocketDir,
"", NULL, NULL
},
{
- "virtual_host", PGC_POSTMASTER, PGC_S_DEFAULT, &VirtualHost,
+ { "virtual_host", PGC_POSTMASTER }, &VirtualHost,
"", NULL, NULL
},
{
- "wal_sync_method", PGC_SIGHUP, PGC_S_DEFAULT, &XLOG_sync_method,
- XLOG_sync_method_default, check_xlog_sync_method,
- assign_xlog_sync_method
+ { "wal_sync_method", PGC_SIGHUP }, &XLOG_sync_method,
+ XLOG_sync_method_default, assign_xlog_sync_method, NULL
},
{
- NULL, 0, 0, NULL, NULL, NULL, NULL
+ { NULL, 0 }, NULL, NULL, NULL, NULL
}
};
/******** end of options list ********/
+/*
+ * Actual lookup of variables is done through this single, sorted array.
+ */
+static struct config_generic **guc_variables;
+static int num_guc_variables;
+
+static bool guc_dirty; /* TRUE if need to do commit/abort work */
+
+static char *guc_string_workspace; /* for avoiding memory leaks */
+
+
+static int guc_var_compare(const void *a, const void *b);
+static void _ShowOption(struct config_generic *record);
+
/*
- * 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.
+ * Build the sorted array. This is split out so that it could be
+ * re-executed after startup (eg, we could allow loadable modules to
+ * add vars, and then we'd need to re-sort).
*/
-static enum config_type
-find_option(const char *name, struct config_generic ** record)
+static void
+build_guc_variables(void)
{
+ int num_vars = 0;
+ struct config_generic **guc_vars;
int i;
- Assert(name);
+ for (i = 0; ConfigureNamesBool[i].gen.name; i++)
+ {
+ struct config_bool *conf = &ConfigureNamesBool[i];
- 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;
- }
+ /* Rather than requiring vartype to be filled in by hand, do this: */
+ conf->gen.vartype = PGC_BOOL;
+ num_vars++;
+ }
- 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; ConfigureNamesInt[i].gen.name; i++)
+ {
+ struct config_int *conf = &ConfigureNamesInt[i];
- 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;
- }
+ conf->gen.vartype = PGC_INT;
+ num_vars++;
+ }
- for (i = 0; ConfigureNamesString[i].name; i++)
- if (strcasecmp(ConfigureNamesString[i].name, name) == 0)
- {
- if (record)
- *record = (struct config_generic *) & ConfigureNamesString[i];
- return PGC_STRING;
- }
+ for (i = 0; ConfigureNamesReal[i].gen.name; i++)
+ {
+ struct config_real *conf = &ConfigureNamesReal[i];
- return PGC_NONE;
+ conf->gen.vartype = PGC_REAL;
+ num_vars++;
+ }
+
+ for (i = 0; ConfigureNamesString[i].gen.name; i++)
+ {
+ struct config_string *conf = &ConfigureNamesString[i];
+
+ conf->gen.vartype = PGC_STRING;
+ num_vars++;
+ }
+
+ guc_vars = (struct config_generic **)
+ malloc(num_vars * sizeof(struct config_generic *));
+ if (!guc_vars)
+ elog(PANIC, "out of memory");
+
+ num_vars = 0;
+
+ for (i = 0; ConfigureNamesBool[i].gen.name; i++)
+ guc_vars[num_vars++] = & ConfigureNamesBool[i].gen;
+
+ for (i = 0; ConfigureNamesInt[i].gen.name; i++)
+ guc_vars[num_vars++] = & ConfigureNamesInt[i].gen;
+
+ for (i = 0; ConfigureNamesReal[i].gen.name; i++)
+ guc_vars[num_vars++] = & ConfigureNamesReal[i].gen;
+
+ for (i = 0; ConfigureNamesString[i].gen.name; i++)
+ guc_vars[num_vars++] = & ConfigureNamesString[i].gen;
+
+ qsort((void *) guc_vars, num_vars, sizeof(struct config_generic *),
+ guc_var_compare);
+
+ if (guc_variables)
+ free(guc_variables);
+ guc_variables = guc_vars;
+ num_guc_variables = num_vars;
}
+/*
+ * Look up option NAME. If it exists, return a pointer to its record,
+ * else return NULL.
+ */
+static struct config_generic *
+find_option(const char *name)
+{
+ const char **key = &name;
+ struct config_generic **res;
+
+ Assert(name);
+
+ /*
+ * by equating const char ** with struct config_generic *, we are
+ * assuming the name field is first in config_generic.
+ */
+ res = (struct config_generic**) bsearch((void *) &key,
+ (void *) guc_variables,
+ num_guc_variables,
+ sizeof(struct config_generic *),
+ guc_var_compare);
+ if (res)
+ return *res;
+ return NULL;
+}
+
/*
- * Reset all options to their specified default values. Must be called
- * with isStartup = true at program startup. May be called later with
- * isStartup = false to reset all resettable options.
+ * comparator for qsorting and bsearching guc_variables array
+ */
+static int
+guc_var_compare(const void *a, const void *b)
+{
+ struct config_generic *confa = *(struct config_generic **) a;
+ struct config_generic *confb = *(struct config_generic **) b;
+ char namea[NAMEDATALEN];
+ char nameb[NAMEDATALEN];
+ int len,
+ i;
+
+ /*
+ * The temptation to use strcasecmp() here must be resisted, because
+ * the array ordering has to remain stable across setlocale() calls.
+ * So, apply an ASCII-only downcasing to both names and use strcmp().
+ */
+ len = strlen(confa->name);
+ if (len >= NAMEDATALEN)
+ len = NAMEDATALEN-1;
+ for (i = 0; i < len; i++)
+ {
+ char ch = confa->name[i];
+
+ if (ch >= 'A' && ch <= 'Z')
+ ch += 'a' - 'A';
+ namea[i] = ch;
+ }
+ namea[len] = '\0';
+
+ len = strlen(confb->name);
+ if (len >= NAMEDATALEN)
+ len = NAMEDATALEN-1;
+ for (i = 0; i < len; i++)
+ {
+ char ch = confb->name[i];
+
+ if (ch >= 'A' && ch <= 'Z')
+ ch += 'a' - 'A';
+ nameb[i] = ch;
+ }
+ nameb[len] = '\0';
+
+ return strcmp(namea, nameb);
+}
+
+
+/*
+ * Initialize GUC options during program startup.
*/
void
-ResetAllOptions(bool isStartup)
+InitializeGUCOptions(void)
{
int i;
+ char *env;
- for (i = 0; ConfigureNamesBool[i].name; i++)
+ /*
+ * Build sorted array of all GUC variables.
+ */
+ build_guc_variables();
+
+ /*
+ * Load all variables with their compiled-in defaults, and initialize
+ * status fields as needed.
+ *
+ * Note: any errors here are reported with plain ol' printf, since we
+ * shouldn't assume that elog will work before we've initialized its
+ * config variables. An error here would be unexpected anyway...
+ */
+ for (i = 0; i < num_guc_variables; i++)
{
- struct config_bool *conf = &ConfigureNamesBool[i];
+ struct config_generic *gconf = guc_variables[i];
+
+ gconf->status = 0;
+ gconf->reset_source = PGC_S_DEFAULT;
+ gconf->session_source = PGC_S_DEFAULT;
+ gconf->tentative_source = PGC_S_DEFAULT;
+ gconf->source = PGC_S_DEFAULT;
- if (isStartup ||
- conf->context == PGC_SUSET || conf->context == PGC_USERSET)
+ switch (gconf->vartype)
{
- if (conf->assign_hook)
- (conf->assign_hook) (conf->default_val);
- *conf->variable = conf->default_val;
+ case PGC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) gconf;
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->reset_val, true, false))
+ fprintf(stderr, "Failed to initialize %s",
+ conf->gen.name);
+ *conf->variable = conf->reset_val;
+ conf->session_val = conf->reset_val;
+ break;
+ }
+ case PGC_INT:
+ {
+ struct config_int *conf = (struct config_int *) gconf;
+
+ Assert(conf->reset_val >= conf->min);
+ Assert(conf->reset_val <= conf->max);
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->reset_val, true, false))
+ fprintf(stderr, "Failed to initialize %s",
+ conf->gen.name);
+ *conf->variable = conf->reset_val;
+ conf->session_val = conf->reset_val;
+ break;
+ }
+ case PGC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) gconf;
+
+ Assert(conf->reset_val >= conf->min);
+ Assert(conf->reset_val <= conf->max);
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->reset_val, true, false))
+ fprintf(stderr, "Failed to initialize %s",
+ conf->gen.name);
+ *conf->variable = conf->reset_val;
+ conf->session_val = conf->reset_val;
+ break;
+ }
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) gconf;
+ char *str;
+
+ *conf->variable = NULL;
+ conf->reset_val = NULL;
+ conf->session_val = NULL;
+ conf->tentative_val = NULL;
+
+ if (conf->boot_val == NULL)
+ {
+ /* Cannot set value yet */
+ break;
+ }
+
+ str = strdup(conf->boot_val);
+ if (str == NULL)
+ elog(PANIC, "out of memory");
+ conf->reset_val = str;
+
+ if (conf->assign_hook)
+ {
+ const char *newstr;
+
+ newstr = (*conf->assign_hook) (str, true, false);
+ if (newstr == NULL)
+ {
+ fprintf(stderr, "Failed to initialize %s",
+ conf->gen.name);
+ }
+ else if (newstr != str)
+ {
+ free(str);
+ /* See notes in set_config_option about casting */
+ str = (char *) newstr;
+ conf->reset_val = str;
+ }
+ }
+ *conf->variable = str;
+ conf->session_val = str;
+ break;
+ }
}
}
- for (i = 0; ConfigureNamesInt[i].name; i++)
+ guc_dirty = false;
+
+ guc_string_workspace = NULL;
+
+ /*
+ * Prevent any attempt to override TRANSACTION ISOLATION LEVEL from
+ * non-interactive sources.
+ */
+ SetConfigOption("TRANSACTION ISOLATION LEVEL", "default",
+ PGC_POSTMASTER, PGC_S_OVERRIDE);
+
+ /*
+ * For historical reasons, some GUC parameters can receive defaults
+ * from environment variables. Process those settings.
+ */
+
+ env = getenv("PGPORT");
+ if (env != NULL)
+ SetConfigOption("port", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
+
+ env = getenv("PGDATESTYLE");
+ if (env != NULL)
+ SetConfigOption("datestyle", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
+
+ env = getenv("TZ");
+ if (env != NULL)
+ SetConfigOption("timezone", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
+
+#ifdef MULTIBYTE
+ env = getenv("PGCLIENTENCODING");
+ if (env != NULL)
+ SetConfigOption("client_encoding", env, PGC_POSTMASTER, PGC_S_ENV_VAR);
+#endif
+}
+
+
+/*
+ * Reset all options to their saved default values (implements RESET ALL)
+ */
+void
+ResetAllOptions(void)
+{
+ int i;
+
+ for (i = 0; i < num_guc_variables; i++)
{
- struct config_int *conf = &ConfigureNamesInt[i];
+ struct config_generic *gconf = guc_variables[i];
+
+ /* Don't reset non-SET-able values */
+ if (gconf->context != PGC_SUSET && gconf->context != PGC_USERSET)
+ continue;
+ /* Don't reset if special exclusion from RESET ALL */
+ if (gconf->flags & GUC_NO_RESET_ALL)
+ continue;
+ /* No need to reset if wasn't SET */
+ if (gconf->source <= PGC_S_OVERRIDE)
+ continue;
- if (isStartup ||
- conf->context == PGC_SUSET || conf->context == PGC_USERSET)
+ switch (gconf->vartype)
{
- if (conf->assign_hook)
- (conf->assign_hook) (conf->default_val);
- *conf->variable = conf->default_val;
+ case PGC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) gconf;
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->reset_val, true, true))
+ elog(ERROR, "Failed to reset %s", conf->gen.name);
+ *conf->variable = conf->reset_val;
+ conf->tentative_val = conf->reset_val;
+ conf->gen.source = conf->gen.reset_source;
+ conf->gen.tentative_source = conf->gen.reset_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
+ break;
+ }
+ case PGC_INT:
+ {
+ struct config_int *conf = (struct config_int *) gconf;
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->reset_val, true, true))
+ elog(ERROR, "Failed to reset %s", conf->gen.name);
+ *conf->variable = conf->reset_val;
+ conf->tentative_val = conf->reset_val;
+ conf->gen.source = conf->gen.reset_source;
+ conf->gen.tentative_source = conf->gen.reset_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
+ break;
+ }
+ case PGC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) gconf;
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->reset_val, true, true))
+ elog(ERROR, "Failed to reset %s", conf->gen.name);
+ *conf->variable = conf->reset_val;
+ conf->tentative_val = conf->reset_val;
+ conf->gen.source = conf->gen.reset_source;
+ conf->gen.tentative_source = conf->gen.reset_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
+ break;
+ }
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) gconf;
+ char *str;
+
+ if (conf->reset_val == NULL)
+ {
+ /* Nothing to reset to, as yet; so do nothing */
+ break;
+ }
+
+ str = strdup(conf->reset_val);
+ if (str == NULL)
+ elog(ERROR, "out of memory");
+
+ /*
+ * Remember string in workspace, so that we can free it
+ * and avoid a permanent memory leak if hook elogs.
+ */
+ if (guc_string_workspace)
+ free(guc_string_workspace);
+ guc_string_workspace = str;
+
+ if (conf->assign_hook)
+ {
+ const char *newstr;
+
+ newstr = (*conf->assign_hook) (str, true, true);
+ if (newstr == NULL)
+ elog(ERROR, "Failed to reset %s", conf->gen.name);
+ else if (newstr != str)
+ {
+ free(str);
+ /* See notes in set_config_option about casting */
+ str = (char *) newstr;
+ }
+ }
+
+ guc_string_workspace = NULL;
+
+ SET_STRING_VARIABLE(conf, str);
+ SET_STRING_TENTATIVE_VAL(conf, str);
+ conf->gen.source = conf->gen.reset_source;
+ conf->gen.tentative_source = conf->gen.reset_source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
+ break;
+ }
}
}
+}
- for (i = 0; ConfigureNamesReal[i].name; i++)
- {
- struct config_real *conf = &ConfigureNamesReal[i];
- if (isStartup ||
- conf->context == PGC_SUSET || conf->context == PGC_USERSET)
- {
- if (conf->assign_hook)
- (conf->assign_hook) (conf->default_val);
- *conf->variable = conf->default_val;
- }
+/*
+ * Do GUC processing at transaction commit or abort.
+ */
+void
+AtEOXact_GUC(bool isCommit)
+{
+ int i;
+
+ /* Quick exit if nothing's changed in this transaction */
+ if (!guc_dirty)
+ return;
+
+ /* Prevent memory leak if elog during an assign_hook */
+ if (guc_string_workspace)
+ {
+ free(guc_string_workspace);
+ guc_string_workspace = NULL;
}
- for (i = 0; ConfigureNamesString[i].name; i++)
+ for (i = 0; i < num_guc_variables; i++)
{
- struct config_string *conf = &ConfigureNamesString[i];
+ struct config_generic *gconf = guc_variables[i];
+
+ /* Skip if nothing's happened to this var in this transaction */
+ if (gconf->status == 0)
+ continue;
- if (isStartup ||
- conf->context == PGC_SUSET || conf->context == PGC_USERSET)
+ switch (gconf->vartype)
{
- char *str = NULL;
+ case PGC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) gconf;
- if (conf->default_val == NULL &&
- conf->boot_default_val)
+ if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ {
+ conf->session_val = conf->tentative_val;
+ conf->gen.session_source = conf->gen.tentative_source;
+ }
+
+ if (*conf->variable != conf->session_val)
+ {
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->session_val,
+ true, false))
+ elog(LOG, "Failed to commit %s", conf->gen.name);
+ *conf->variable = conf->session_val;
+ }
+ conf->gen.source = conf->gen.session_source;
+ conf->gen.status = 0;
+ break;
+ }
+ case PGC_INT:
{
- str = strdup(conf->boot_default_val);
- if (str == NULL)
- elog(ERROR, "out of memory");
- conf->default_val = str;
+ struct config_int *conf = (struct config_int *) gconf;
+
+ if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ {
+ conf->session_val = conf->tentative_val;
+ conf->gen.session_source = conf->gen.tentative_source;
+ }
+
+ if (*conf->variable != conf->session_val)
+ {
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->session_val,
+ true, false))
+ elog(LOG, "Failed to commit %s", conf->gen.name);
+ *conf->variable = conf->session_val;
+ }
+ conf->gen.source = conf->gen.session_source;
+ conf->gen.status = 0;
+ break;
}
- if (conf->default_val)
+ case PGC_REAL:
{
- str = strdup(conf->default_val);
- if (str == NULL)
- elog(ERROR, "out of memory");
+ struct config_real *conf = (struct config_real *) gconf;
+
+ if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ {
+ conf->session_val = conf->tentative_val;
+ conf->gen.session_source = conf->gen.tentative_source;
+ }
+
+ if (*conf->variable != conf->session_val)
+ {
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (conf->session_val,
+ true, false))
+ elog(LOG, "Failed to commit %s", conf->gen.name);
+ *conf->variable = conf->session_val;
+ }
+ conf->gen.source = conf->gen.session_source;
+ conf->gen.status = 0;
+ break;
+ }
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) gconf;
+
+ if (isCommit && (conf->gen.status & GUC_HAVE_TENTATIVE))
+ {
+ SET_STRING_SESSION_VAL(conf, conf->tentative_val);
+ conf->gen.session_source = conf->gen.tentative_source;
+ conf->tentative_val = NULL; /* transfer ownership */
+ }
+ else
+ {
+ SET_STRING_TENTATIVE_VAL(conf, NULL);
+ }
+
+ if (*conf->variable != conf->session_val)
+ {
+ char *str = conf->session_val;
+
+ if (conf->assign_hook)
+ {
+ const char *newstr;
+
+ newstr = (*conf->assign_hook) (str, true, false);
+ if (newstr == NULL)
+ elog(LOG, "Failed to commit %s", conf->gen.name);
+ else if (newstr != str)
+ {
+ /* See notes in set_config_option about casting */
+ str = (char *) newstr;
+ SET_STRING_SESSION_VAL(conf, str);
+ }
+ }
+
+ SET_STRING_VARIABLE(conf, str);
+ }
+ conf->gen.source = conf->gen.session_source;
+ conf->gen.status = 0;
+ break;
}
- if (conf->assign_hook)
- (conf->assign_hook) (str);
- if (*conf->variable)
- free(*conf->variable);
- *conf->variable = str;
}
}
-}
+ guc_dirty = false;
+}
/*
@@ -900,7 +1511,7 @@ parse_real(const char *value, double *result)
/*
* 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
+ * type. The context and source parameters indicate in which context this
* function is being called so it can apply the access restrictions
* properly.
*
@@ -909,49 +1520,45 @@ parse_real(const char *value, double *result)
* 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.
+ * elog(ERROR) is thrown *unless* this is called in a context where we
+ * don't want to elog (currently, startup or SIGHUP config file reread).
+ * In that case we write a suitable error message via elog(DEBUG) and
+ * return false. This is working around the deficiencies in the elog
+ * mechanism, so don't blame me. In all other cases, the function
+ * returns true, including cases where the input is valid but we chose
+ * not to apply it because of context or source-priority considerations.
*
* See also SetConfigOption for an external interface.
*/
bool
set_config_option(const char *name, const char *value,
- GucContext context, bool DoIt, GucSource source)
+ GucContext context, GucSource source,
+ bool isLocal, bool DoIt)
{
struct config_generic *record;
- enum config_type type;
int elevel;
+ bool interactive;
bool makeDefault;
if (context == PGC_SIGHUP || source == PGC_S_DEFAULT)
elevel = DEBUG1;
- else if (guc_session_init)
+ else if (source == PGC_S_DATABASE || source == PGC_S_USER)
elevel = INFO;
else
elevel = ERROR;
- type = find_option(name, &record);
- if (type == PGC_NONE)
+ record = find_option(name);
+ if (record == NULL)
{
elog(elevel, "'%s' is not a valid option name", name);
return false;
}
- if (record->source > source)
- {
- elog(DEBUG2, "setting %s refused because previous source is higher",
- name);
- return false;
- }
- makeDefault = source < PGC_S_SESSION;
-
/*
* Check if the option can be set at this time. See guc.h for the
* precise rules. Note that we don't want to throw errors if we're in
- * the SIGHUP context. In that case we just ignore the attempt.
+ * the SIGHUP context. In that case we just ignore the attempt and
+ * return true.
*/
switch (record->context)
{
@@ -959,11 +1566,18 @@ set_config_option(const char *name, const char *value,
if (context == PGC_SIGHUP)
return true;
if (context != PGC_POSTMASTER)
- elog(ERROR, "'%s' cannot be changed after server start", name);
+ {
+ elog(elevel, "'%s' cannot be changed after server start",
+ name);
+ return false;
+ }
break;
case PGC_SIGHUP:
if (context != PGC_SIGHUP && context != PGC_POSTMASTER)
- elog(ERROR, "'%s' cannot be changed now", name);
+ {
+ elog(elevel, "'%s' cannot be changed now", name);
+ return false;
+ }
/*
* Hmm, the idea of the SIGHUP context is "ought to be global,
@@ -987,51 +1601,110 @@ set_config_option(const char *name, const char *value,
return true;
}
else if (context != PGC_BACKEND && context != PGC_POSTMASTER)
- elog(ERROR, "'%s' cannot be set after connection start", name);
+ {
+ elog(elevel, "'%s' cannot be set after connection start",
+ name);
+ return false;
+ }
break;
case PGC_SUSET:
if (context == PGC_USERSET || context == PGC_BACKEND)
- elog(ERROR, "permission denied");
+ {
+ elog(elevel, "'%s': permission denied", name);
+ return false;
+ }
break;
case PGC_USERSET:
/* always okay */
break;
}
+ interactive = (source >= PGC_S_SESSION);
+ makeDefault = (source <= PGC_S_OVERRIDE) && (value != NULL);
+
+ /*
+ * Ignore attempted set if overridden by previously processed setting.
+ * However, if DoIt is false then plow ahead anyway since we are trying
+ * to find out if the value is potentially good, not actually use it.
+ * Also keep going if makeDefault is true, since we may want to set
+ * the reset/session values even if we can't set the variable itself.
+ */
+ if (record->source > source)
+ {
+ if (DoIt && !makeDefault)
+ {
+ elog(DEBUG2, "setting %s ignored because previous source is higher",
+ name);
+ return true;
+ }
+ DoIt = false; /* we won't change the variable itself */
+ }
+
/*
* Evaluate value and set variable
*/
- switch (type)
+ switch (record->vartype)
{
case PGC_BOOL:
{
struct config_bool *conf = (struct config_bool *) record;
+ bool newval;
if (value)
{
- bool boolval;
+ if (!parse_bool(value, &newval))
+ {
+ elog(elevel, "option '%s' requires a boolean value",
+ name);
+ return false;
+ }
+ }
+ else
+ {
+ newval = conf->reset_val;
+ source = conf->gen.reset_source;
+ }
- if (!parse_bool(value, &boolval))
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval, DoIt, interactive))
{
- elog(elevel, "option '%s' requires a boolean value", name);
+ elog(elevel, "invalid value for option '%s': %d",
+ name, (int) newval);
return false;
}
- /* no parse_hook needed for booleans */
+
+ if (DoIt || makeDefault)
+ {
if (DoIt)
{
- if (conf->assign_hook)
- (conf->assign_hook) (boolval);
- *conf->variable = boolval;
- if (makeDefault)
- conf->default_val = boolval;
- conf->source = source;
+ *conf->variable = newval;
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ if (conf->gen.reset_source <= source)
+ {
+ conf->reset_val = newval;
+ conf->gen.reset_source = source;
+ }
+ if (conf->gen.session_source <= source)
+ {
+ conf->session_val = newval;
+ conf->gen.session_source = source;
+ }
+ }
+ else if (isLocal)
+ {
+ conf->gen.status |= GUC_HAVE_LOCAL;
+ guc_dirty = true;
+ }
+ else
+ {
+ conf->tentative_val = newval;
+ conf->gen.tentative_source = source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
}
- }
- else if (DoIt)
- {
- if (conf->assign_hook)
- (conf->assign_hook) (conf->default_val);
- *conf->variable = conf->default_val;
}
break;
}
@@ -1039,44 +1712,70 @@ set_config_option(const char *name, const char *value,
case PGC_INT:
{
struct config_int *conf = (struct config_int *) record;
+ int newval;
if (value)
{
- int intval;
-
- if (!parse_int(value, &intval))
+ if (!parse_int(value, &newval))
{
- elog(elevel, "option '%s' expects an integer value", name);
+ elog(elevel, "option '%s' expects an integer value",
+ name);
return false;
}
- if (intval < conf->min || intval > conf->max)
+ if (newval < conf->min || newval > conf->max)
{
elog(elevel, "option '%s' value %d is outside"
" of permissible range [%d .. %d]",
- name, intval, conf->min, conf->max);
+ name, newval, conf->min, conf->max);
return false;
}
- if (conf->parse_hook && !(conf->parse_hook) (intval))
+ }
+ else
+ {
+ newval = conf->reset_val;
+ source = conf->gen.reset_source;
+ }
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval, DoIt, interactive))
{
elog(elevel, "invalid value for option '%s': %d",
- name, intval);
+ name, newval);
return false;
}
+
+ if (DoIt || makeDefault)
+ {
if (DoIt)
{
- if (conf->assign_hook)
- (conf->assign_hook) (intval);
- *conf->variable = intval;
- if (makeDefault)
- conf->default_val = intval;
- conf->source = source;
+ *conf->variable = newval;
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ if (conf->gen.reset_source <= source)
+ {
+ conf->reset_val = newval;
+ conf->gen.reset_source = source;
+ }
+ if (conf->gen.session_source <= source)
+ {
+ conf->session_val = newval;
+ conf->gen.session_source = source;
+ }
+ }
+ else if (isLocal)
+ {
+ conf->gen.status |= GUC_HAVE_LOCAL;
+ guc_dirty = true;
+ }
+ else
+ {
+ conf->tentative_val = newval;
+ conf->gen.tentative_source = source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
}
- }
- else if (DoIt)
- {
- if (conf->assign_hook)
- (conf->assign_hook) (conf->default_val);
- *conf->variable = conf->default_val;
}
break;
}
@@ -1084,44 +1783,70 @@ set_config_option(const char *name, const char *value,
case PGC_REAL:
{
struct config_real *conf = (struct config_real *) record;
+ double newval;
if (value)
{
- double dval;
-
- if (!parse_real(value, &dval))
+ if (!parse_real(value, &newval))
{
- elog(elevel, "option '%s' expects a real number", name);
+ elog(elevel, "option '%s' expects a real number",
+ name);
return false;
}
- if (dval < conf->min || dval > conf->max)
+ if (newval < conf->min || newval > conf->max)
{
elog(elevel, "option '%s' value %g is outside"
" of permissible range [%g .. %g]",
- name, dval, conf->min, conf->max);
+ name, newval, conf->min, conf->max);
return false;
}
- if (conf->parse_hook && !(conf->parse_hook) (dval))
+ }
+ else
+ {
+ newval = conf->reset_val;
+ source = conf->gen.reset_source;
+ }
+
+ if (conf->assign_hook)
+ if (!(*conf->assign_hook) (newval, DoIt, interactive))
{
elog(elevel, "invalid value for option '%s': %g",
- name, dval);
+ name, newval);
return false;
}
+
+ if (DoIt || makeDefault)
+ {
if (DoIt)
{
- if (conf->assign_hook)
- (conf->assign_hook) (dval);
- *conf->variable = dval;
- if (makeDefault)
- conf->default_val = dval;
- conf->source = source;
+ *conf->variable = newval;
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ if (conf->gen.reset_source <= source)
+ {
+ conf->reset_val = newval;
+ conf->gen.reset_source = source;
+ }
+ if (conf->gen.session_source <= source)
+ {
+ conf->session_val = newval;
+ conf->gen.session_source = source;
+ }
+ }
+ else if (isLocal)
+ {
+ conf->gen.status |= GUC_HAVE_LOCAL;
+ guc_dirty = true;
+ }
+ else
+ {
+ conf->tentative_val = newval;
+ conf->gen.tentative_source = source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
}
- }
- else if (DoIt)
- {
- if (conf->assign_hook)
- (conf->assign_hook) (conf->default_val);
- *conf->variable = conf->default_val;
}
break;
}
@@ -1129,76 +1854,118 @@ set_config_option(const char *name, const char *value,
case PGC_STRING:
{
struct config_string *conf = (struct config_string *) record;
+ char *newval;
if (value)
{
- if (conf->parse_hook && !(conf->parse_hook) (value))
+ newval = strdup(value);
+ if (newval == NULL)
+ {
+ elog(elevel, "out of memory");
+ return false;
+ }
+ }
+ else if (conf->reset_val)
+ {
+ newval = strdup(conf->reset_val);
+ if (newval == NULL)
{
+ elog(elevel, "out of memory");
+ return false;
+ }
+ source = conf->gen.reset_source;
+ }
+ else
+ {
+ /* Nothing to reset to, as yet; so do nothing */
+ break;
+ }
+
+ /*
+ * Remember string in workspace, so that we can free it
+ * and avoid a permanent memory leak if hook elogs.
+ */
+ if (guc_string_workspace)
+ free(guc_string_workspace);
+ guc_string_workspace = newval;
+
+ if (conf->assign_hook)
+ {
+ const char *hookresult;
+
+ hookresult = (*conf->assign_hook) (newval,
+ DoIt, interactive);
+ guc_string_workspace = NULL;
+ if (hookresult == NULL)
+ {
+ free(newval);
elog(elevel, "invalid value for option '%s': '%s'",
- name, value);
+ name, value ? value : "");
return false;
}
- if (DoIt)
+ else if (hookresult != newval)
{
- char *str;
+ free(newval);
+ /*
+ * Having to cast away const here is annoying, but the
+ * alternative is to declare assign_hooks as returning
+ * char*, which would mean they'd have to cast away
+ * const, or as both taking and returning char*, which
+ * doesn't seem attractive either --- we don't want
+ * them to scribble on the passed str.
+ */
+ newval = (char *) hookresult;
+ }
+ }
- str = strdup(value);
- if (str == NULL)
+ guc_string_workspace = NULL;
+
+ if (DoIt || makeDefault)
+ {
+ if (DoIt)
+ {
+ SET_STRING_VARIABLE(conf, newval);
+ conf->gen.source = source;
+ }
+ if (makeDefault)
+ {
+ if (conf->gen.reset_source <= source)
{
- elog(elevel, "out of memory");
- return false;
+ SET_STRING_RESET_VAL(conf, newval);
+ conf->gen.reset_source = source;
}
- if (conf->assign_hook)
- (conf->assign_hook) (str);
- if (*conf->variable)
- free(*conf->variable);
- *conf->variable = str;
- if (makeDefault)
+ if (conf->gen.session_source <= source)
{
- str = strdup(value);
- if (str == NULL)
- {
- elog(elevel, "out of memory");
- return false;
- }
- if (conf->default_val)
- free(conf->default_val);
- conf->default_val = str;
+ SET_STRING_SESSION_VAL(conf, newval);
+ conf->gen.session_source = source;
}
- conf->source = source;
+ /* Perhaps we didn't install newval anywhere */
+ if (newval != *conf->variable &&
+ newval != conf->session_val &&
+ newval != conf->reset_val)
+ free(newval);
}
- }
- else if (DoIt)
- {
- char *str;
-
- if (!conf->default_val && conf->boot_default_val)
+ else if (isLocal)
{
- str = strdup(conf->boot_default_val);
- if (str == NULL)
- {
- elog(elevel, "out of memory");
- return false;
- }
- conf->default_val = str;
+ conf->gen.status |= GUC_HAVE_LOCAL;
+ guc_dirty = true;
}
- str = strdup(conf->default_val);
- if (str == NULL)
+ else
{
- elog(elevel, "out of memory");
- return false;
+ SET_STRING_TENTATIVE_VAL(conf, newval);
+ conf->gen.tentative_source = source;
+ conf->gen.status |= GUC_HAVE_TENTATIVE;
+ guc_dirty = true;
}
- if (conf->assign_hook)
- (conf->assign_hook) (str);
- if (*conf->variable)
- free(*conf->variable);
- *conf->variable = str;
+ }
+ else
+ {
+ free(newval);
}
break;
}
-
- default:;
}
+
return true;
}
@@ -1206,21 +1973,21 @@ set_config_option(const char *name, const char *value,
/*
* Set a config option to the given value. See also set_config_option,
- * this is just the wrapper to be called from the outside.
+ * this is just the wrapper to be called from outside GUC. NB: this
+ * is used only for non-transactional operations.
*/
void
SetConfigOption(const char *name, const char *value,
GucContext context, GucSource source)
{
- (void) set_config_option(name, value, context, true, source);
+ (void) set_config_option(name, value, context, source, false, 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.
+ * Fetch the current value of the option `name'. If the option doesn't exist,
+ * throw an elog and don't return.
*
* The string is *not* allocated for modification and is really only
* valid until the next call to configuration related functions.
@@ -1230,13 +1997,12 @@ GetConfigOption(const char *name)
{
struct config_generic *record;
static char buffer[256];
- enum config_type opttype;
- opttype = find_option(name, &record);
- if (opttype == PGC_NONE)
+ record = find_option(name);
+ if (record == NULL)
elog(ERROR, "Option '%s' is not recognized", name);
- switch (opttype)
+ switch (record->vartype)
{
case PGC_BOOL:
return *((struct config_bool *) record)->variable ? "on" : "off";
@@ -1253,68 +2019,298 @@ GetConfigOption(const char *name)
case PGC_STRING:
return *((struct config_string *) record)->variable;
-
- default:
- ;
}
return NULL;
}
-static void
-_ShowOption(enum config_type opttype, struct config_generic * record)
+/*
+ * Get the RESET value associated with the given option.
+ */
+const char *
+GetConfigOptionResetString(const char *name)
{
- char buffer[256];
- char *val;
+ struct config_generic *record;
+ static char buffer[256];
+
+ record = find_option(name);
+ if (record == NULL)
+ elog(ERROR, "Option '%s' is not recognized", name);
- switch (opttype)
+ switch (record->vartype)
{
case PGC_BOOL:
- val = *((struct config_bool *) record)->variable ? "on" : "off";
- break;
+ return ((struct config_bool *) record)->reset_val ? "on" : "off";
case PGC_INT:
snprintf(buffer, sizeof(buffer), "%d",
- *((struct config_int *) record)->variable);
- val = buffer;
- break;
+ ((struct config_int *) record)->reset_val);
+ return buffer;
case PGC_REAL:
snprintf(buffer, sizeof(buffer), "%g",
- *((struct config_real *) record)->variable);
- val = buffer;
- break;
+ ((struct config_real *) record)->reset_val);
+ return buffer;
case PGC_STRING:
- val = strlen(*((struct config_string *) record)->variable) != 0 ?
- *((struct config_string *) record)->variable : "unset";
- break;
+ return ((struct config_string *) record)->reset_val;
+ }
+ return NULL;
+}
- default:
- val = "???";
+
+
+/*
+ * flatten_set_variable_args
+ * Given a parsenode List as emitted by the grammar for SET,
+ * convert to the flat string representation used by GUC.
+ *
+ * We need to be told the name of the variable the args are for, because
+ * the flattening rules vary (ugh).
+ *
+ * The result is NULL if input is NIL (ie, SET ... TO DEFAULT), otherwise
+ * a palloc'd string.
+ */
+char *
+flatten_set_variable_args(const char *name, List *args)
+{
+ struct config_generic *record;
+ int flags;
+ StringInfoData buf;
+ List *l;
+
+ /* Fast path if just DEFAULT */
+ if (args == NIL)
+ return NULL;
+
+ record = find_option(name);
+ if (record == NULL)
+ flags = 0; /* default assumptions */
+ else
+ flags = record->flags;
+
+ /* Complain if list input and non-list variable */
+ if ((flags & GUC_LIST_INPUT) == 0 &&
+ lnext(args) != NIL)
+ elog(ERROR, "SET %s takes only one argument", name);
+
+ initStringInfo(&buf);
+
+ foreach(l, args)
+ {
+ A_Const *arg = (A_Const *) lfirst(l);
+ char *val;
+
+ if (l != args)
+ appendStringInfo(&buf, ", ");
+
+ if (!IsA(arg, A_Const))
+ elog(ERROR, "flatten_set_variable_args: unexpected input");
+
+ switch (nodeTag(&arg->val))
+ {
+ case T_Integer:
+ appendStringInfo(&buf, "%ld", intVal(&arg->val));
+ break;
+ case T_Float:
+ /* represented as a string, so just copy it */
+ appendStringInfo(&buf, "%s", strVal(&arg->val));
+ break;
+ case T_String:
+ val = strVal(&arg->val);
+ if (arg->typename != NULL)
+ {
+ /*
+ * Must be a ConstInterval argument for TIME ZONE.
+ * Coerce to interval and back to normalize the value
+ * and account for any typmod.
+ */
+ Datum interval;
+ char *intervalout;
+
+ interval =
+ DirectFunctionCall3(interval_in,
+ CStringGetDatum(val),
+ ObjectIdGetDatum(InvalidOid),
+ Int32GetDatum(arg->typename->typmod));
+
+ intervalout =
+ DatumGetCString(DirectFunctionCall3(interval_out,
+ interval,
+ ObjectIdGetDatum(InvalidOid),
+ Int32GetDatum(-1)));
+ appendStringInfo(&buf, "INTERVAL '%s'", intervalout);
+ }
+ else
+ {
+ /*
+ * Plain string literal or identifier. For quote mode,
+ * quote it if it's not a vanilla identifier.
+ */
+ if (flags & GUC_LIST_QUOTE)
+ appendStringInfo(&buf, "%s", quote_identifier(val));
+ else
+ appendStringInfo(&buf, "%s", val);
+ }
+ break;
+ default:
+ elog(ERROR, "flatten_set_variable_args: unexpected input");
+ break;
+ }
}
- elog(INFO, "%s is %s", record->name, val);
+
+ return buf.data;
+}
+
+
+/*
+ * SET command
+ */
+void
+SetPGVariable(const char *name, List *args, bool is_local)
+{
+ char *argstring = flatten_set_variable_args(name, args);
+
+ /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */
+ set_config_option(name,
+ argstring,
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ is_local,
+ true);
+}
+
+/*
+ * SHOW command
+ */
+void
+GetPGVariable(const char *name)
+{
+ if (strcasecmp(name, "all") == 0)
+ ShowAllGUCConfig();
+ else
+ ShowGUCConfigOption(name);
+}
+
+/*
+ * RESET command
+ */
+void
+ResetPGVariable(const char *name)
+{
+ if (strcasecmp(name, "all") == 0)
+ ResetAllOptions();
+ else
+ set_config_option(name,
+ NULL,
+ (superuser() ? PGC_SUSET : PGC_USERSET),
+ PGC_S_SESSION,
+ false,
+ true);
+}
+
+
+/*
+ * SHOW command
+ */
+void
+ShowGUCConfigOption(const char *name)
+{
+ struct config_generic *record;
+
+ record = find_option(name);
+ if (record == NULL)
+ elog(ERROR, "Option '%s' is not recognized", name);
+
+ _ShowOption(record);
}
+/*
+ * SHOW ALL command
+ */
void
ShowAllGUCConfig(void)
{
int i;
- for (i = 0; ConfigureNamesBool[i].name; i++)
- _ShowOption(PGC_BOOL, (struct config_generic *) & ConfigureNamesBool[i]);
+ for (i = 0; i < num_guc_variables; i++)
+ {
+ struct config_generic *conf = guc_variables[i];
- for (i = 0; ConfigureNamesInt[i].name; i++)
- _ShowOption(PGC_INT, (struct config_generic *) & ConfigureNamesInt[i]);
+ if ((conf->flags & GUC_NO_SHOW_ALL) == 0)
+ _ShowOption(conf);
+ }
+}
- for (i = 0; ConfigureNamesReal[i].name; i++)
- _ShowOption(PGC_REAL, (struct config_generic *) & ConfigureNamesReal[i]);
+static void
+_ShowOption(struct config_generic *record)
+{
+ char buffer[256];
+ const char *val;
- for (i = 0; ConfigureNamesString[i].name; i++)
- _ShowOption(PGC_STRING, (struct config_generic *) & ConfigureNamesString[i]);
-}
+ switch (record->vartype)
+ {
+ case PGC_BOOL:
+ {
+ struct config_bool *conf = (struct config_bool *) record;
+ if (conf->show_hook)
+ val = (*conf->show_hook) ();
+ else
+ val = *conf->variable ? "on" : "off";
+ }
+ break;
+ case PGC_INT:
+ {
+ struct config_int *conf = (struct config_int *) record;
+ if (conf->show_hook)
+ val = (*conf->show_hook) ();
+ else
+ {
+ snprintf(buffer, sizeof(buffer), "%d",
+ *conf->variable);
+ val = buffer;
+ }
+ }
+ break;
+
+ case PGC_REAL:
+ {
+ struct config_real *conf = (struct config_real *) record;
+
+ if (conf->show_hook)
+ val = (*conf->show_hook) ();
+ else
+ {
+ snprintf(buffer, sizeof(buffer), "%g",
+ *conf->variable);
+ val = buffer;
+ }
+ }
+ break;
+
+ case PGC_STRING:
+ {
+ struct config_string *conf = (struct config_string *) record;
+
+ if (conf->show_hook)
+ val = (*conf->show_hook) ();
+ else if (*conf->variable && **conf->variable)
+ val = *conf->variable;
+ else
+ val = "unset";
+ }
+ break;
+
+ default:
+ /* just to keep compiler quiet */
+ val = "???";
+ break;
+ }
+
+ elog(INFO, "%s is %s", record->name, val);
+}
/*
@@ -1366,59 +2362,54 @@ ParseLongOption(const char *string, char **name, char **value)
#ifdef HAVE_SYSLOG
-static bool
-check_facility(const char *facility)
+static const char *
+assign_facility(const char *facility, bool doit, bool interactive)
{
if (strcasecmp(facility, "LOCAL0") == 0)
- return true;
+ return facility;
if (strcasecmp(facility, "LOCAL1") == 0)
- return true;
+ return facility;
if (strcasecmp(facility, "LOCAL2") == 0)
- return true;
+ return facility;
if (strcasecmp(facility, "LOCAL3") == 0)
- return true;
+ return facility;
if (strcasecmp(facility, "LOCAL4") == 0)
- return true;
+ return facility;
if (strcasecmp(facility, "LOCAL5") == 0)
- return true;
+ return facility;
if (strcasecmp(facility, "LOCAL6") == 0)
- return true;
+ return facility;
if (strcasecmp(facility, "LOCAL7") == 0)
- return true;
- return false;
+ return facility;
+ return NULL;
}
-#endif
-
-
-static bool
-check_defaultxactisolevel(const char *value)
-{
- return (strcasecmp(value, "read committed") == 0
- || strcasecmp(value, "serializable") == 0)
- ? true : false;
-}
+#endif
-static void
-assign_defaultxactisolevel(const char *value)
+static const char *
+assign_defaultxactisolevel(const char *newval, bool doit, bool interactive)
{
- if (strcasecmp(value, "serializable") == 0)
- DefaultXactIsoLevel = XACT_SERIALIZABLE;
- else if (strcasecmp(value, "read committed") == 0)
- DefaultXactIsoLevel = XACT_READ_COMMITTED;
+ if (strcasecmp(newval, "serializable") == 0)
+ { if (doit) DefaultXactIsoLevel = XACT_SERIALIZABLE; }
+ else if (strcasecmp(newval, "read committed") == 0)
+ { if (doit) DefaultXactIsoLevel = XACT_READ_COMMITTED; }
else
- elog(ERROR, "bogus transaction isolation level");
+ return NULL;
+ return newval;
}
-
+/*
+ * Handle options fetched from pg_database.datconfig or pg_shadow.useconfig.
+ */
void
ProcessGUCArray(ArrayType *array, GucSource source)
{
int i;
- Assert(array);
+ Assert(array != NULL);
+ Assert(source == PGC_S_DATABASE || source == PGC_S_USER);
for (i = 1; i <= ARR_DIMS(array)[0]; i++)
{
@@ -1445,12 +2436,12 @@ ProcessGUCArray(ArrayType *array, GucSource source)
continue;
}
- /* prevent errors from incorrect options */
- guc_session_init = true;
-
+ /*
+ * We process all these options at SUSET level. We assume that the
+ * right to insert an option into pg_database or pg_shadow was
+ * checked when it was inserted.
+ */
SetConfigOption(name, value, PGC_SUSET, source);
-
- guc_session_init = false;
}
}
@@ -1469,7 +2460,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
/* test if the option is valid */
set_config_option(name, value,
superuser() ? PGC_SUSET : PGC_USERSET,
- false, PGC_S_INFINITY);
+ PGC_S_SESSION, false, false);
newval = palloc(strlen(name) + 1 + strlen(value) + 1);
sprintf(newval, "%s=%s", name, value);
@@ -1525,7 +2516,7 @@ GUCArrayDelete(ArrayType *array, const char *name)
/* test if the option is valid */
set_config_option(name, NULL,
superuser() ? PGC_SUSET : PGC_USERSET,
- false, PGC_S_INFINITY);
+ PGC_S_SESSION, false, false);
newarray = construct_array(NULL, 0, false, -1, 'i');
index = 1;
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 843ad5ce158..3fae561d7c6 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -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'