diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-05-17 01:19:19 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-05-17 01:19:19 +0000 |
commit | f0811a74b37427d7ee5eee56b00f7f2ea323d7d6 (patch) | |
tree | 51a596c44fd21144383062aa7d2ce852ae270268 /src/backend/commands/variable.c | |
parent | fa613fa1eafd8fd80272a31e8477ad9368c95dbb (diff) | |
download | postgresql-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.c | 1099 |
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()); } |