aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/variable.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-05-17 01:19:19 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-05-17 01:19:19 +0000
commitf0811a74b37427d7ee5eee56b00f7f2ea323d7d6 (patch)
tree51a596c44fd21144383062aa7d2ce852ae270268 /src/backend/commands/variable.c
parentfa613fa1eafd8fd80272a31e8477ad9368c95dbb (diff)
downloadpostgresql-f0811a74b37427d7ee5eee56b00f7f2ea323d7d6.tar.gz
postgresql-f0811a74b37427d7ee5eee56b00f7f2ea323d7d6.zip
Merge the last few variable.c configuration variables into the generic
GUC support. It's now possible to set datestyle, timezone, and client_encoding from postgresql.conf and per-database or per-user settings. Also, implement rollback of SET commands that occur in a transaction that later fails. Create a SET LOCAL var = value syntax that sets the variable only for the duration of the current transaction. All per previous discussions in pghackers.
Diffstat (limited to 'src/backend/commands/variable.c')
-rw-r--r--src/backend/commands/variable.c1099
1 files changed, 360 insertions, 739 deletions
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());
}