From f0811a74b37427d7ee5eee56b00f7f2ea323d7d6 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 17 May 2002 01:19:19 +0000 Subject: [PATCH] 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. --- doc/src/sgml/ref/alter_database.sgml | 12 +- doc/src/sgml/ref/alter_user.sgml | 19 +- doc/src/sgml/ref/reset.sgml | 13 +- doc/src/sgml/ref/set.sgml | 199 +- doc/src/sgml/ref/set_session_auth.sgml | 12 +- doc/src/sgml/ref/show.sgml | 23 +- doc/src/sgml/release.sgml | 8 +- doc/src/sgml/runtime.sgml | 56 +- src/backend/access/transam/xact.c | 5 +- src/backend/access/transam/xlog.c | 39 +- src/backend/bootstrap/bootstrap.c | 13 +- src/backend/catalog/namespace.c | 66 +- src/backend/commands/dbcommands.c | 8 +- src/backend/commands/user.c | 8 +- src/backend/commands/variable.c | 1141 ++++------ src/backend/main/main.c | 4 +- src/backend/nodes/copyfuncs.c | 3 +- src/backend/nodes/equalfuncs.c | 4 +- src/backend/parser/gram.y | 137 +- src/backend/postmaster/postmaster.c | 13 +- src/backend/tcop/postgres.c | 17 +- src/backend/tcop/utility.c | 6 +- src/backend/utils/adt/datetime.c | 16 +- src/backend/utils/adt/pg_locale.c | 114 +- src/backend/utils/adt/ruleutils.c | 45 +- src/backend/utils/error/elog.c | 155 +- src/backend/utils/init/miscinit.c | 24 +- src/backend/utils/init/postinit.c | 11 +- src/backend/utils/misc/README | 136 ++ src/backend/utils/misc/guc-file.l | 33 +- src/backend/utils/misc/guc.c | 1961 +++++++++++++---- src/backend/utils/misc/postgresql.conf.sample | 3 + src/include/access/xlog.h | 6 +- src/include/catalog/namespace.h | 6 +- src/include/commands/variable.h | 32 +- src/include/miscadmin.h | 4 +- src/include/nodes/parsenodes.h | 3 +- src/include/utils/datetime.h | 4 +- src/include/utils/elog.h | 10 +- src/include/utils/guc.h | 39 +- src/include/utils/pg_locale.h | 31 +- .../jdbc/org/postgresql/Connection.java | 4 +- 42 files changed, 2641 insertions(+), 1802 deletions(-) create mode 100644 src/backend/utils/misc/README diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml index a7e81d22d9..76a4ac014a 100644 --- a/doc/src/sgml/ref/alter_database.sgml +++ b/doc/src/sgml/ref/alter_database.sgml @@ -1,5 +1,5 @@ @@ -28,10 +28,9 @@ ALTER DATABASE name RESET ALTER DATABASE is used to change the session default of a run-time configuration variable for a PostgreSQL database. Whenever a new - session is subsequently started in that database, SET - variable TO - value is effectively executed - before the start of the session. The database-specific default + session is subsequently started in that database, the specified + value becomes the session default value. + The database-specific default overrides whatever setting is present in postgresql.conf or has been received from the postmaster. @@ -64,7 +63,8 @@ ALTER DATABASE name RESET value is DEFAULT or, equivalently, RESET is used, the - database-specific variable setting is removed and the default + database-specific variable setting is removed and the system-wide + default setting will be inherited in new sessions. Use RESET ALL to clear all settings. diff --git a/doc/src/sgml/ref/alter_user.sgml b/doc/src/sgml/ref/alter_user.sgml index d8461c4f4f..3cc82371aa 100644 --- a/doc/src/sgml/ref/alter_user.sgml +++ b/doc/src/sgml/ref/alter_user.sgml @@ -1,5 +1,5 @@ @@ -48,14 +48,13 @@ ALTER USER username RESET - The second and the third variant change a user's session default of + The second and the third variant change a user's session default for a specified configuration variable. Whenever the user subsequently - starts a new session, SET - variable TO - value is effectively executed - before the start of the session. Ordinary users can change their - own session defaults. Superusers can change anyone's session - defaults. + starts a new session, the specified value becomes the session default, + overriding whatever setting is present in postgresql.conf + or has been received from the postmaster. + Ordinary users can change their own session defaults. + Superusers can change anyone's session defaults. @@ -135,12 +134,12 @@ ALTER USER username RESET value - Set this user's session default of the specified configuration + Set this user's session default for the specified configuration variable to the given value. If value is DEFAULT or, equivalently, RESET is used, the user-specific variable setting is removed and the user will - inherit the default setting in new sessions. Use + inherit the system-wide default setting in new sessions. Use RESET ALL to clear all settings. diff --git a/doc/src/sgml/ref/reset.sgml b/doc/src/sgml/ref/reset.sgml index 69c1f861ee..9f78d9df6c 100644 --- a/doc/src/sgml/ref/reset.sgml +++ b/doc/src/sgml/ref/reset.sgml @@ -1,5 +1,5 @@ @@ -37,7 +37,7 @@ RESET ALL ALL - Resets all run-time parameters to default values. + Resets all settable run-time parameters to default values. @@ -53,11 +53,18 @@ RESET ALL RESET restores run-time parameters to their default values. Refer to - for details. RESET is an alternate form for + for details. RESET is an alternate spelling for SET variable TO DEFAULT + + The default value is defined as the value that the variable would + have had, had no SET ever been issued for it in the + current session. The actual source of this value might be a + compiled-in default, the postmaster's configuration file or command-line + switches, or per-database or per-user default settings. See the + Administrator's Guide for details. diff --git a/doc/src/sgml/ref/set.sgml b/doc/src/sgml/ref/set.sgml index 0dd4c44db1..f54f70c197 100644 --- a/doc/src/sgml/ref/set.sgml +++ b/doc/src/sgml/ref/set.sgml @@ -1,5 +1,5 @@ @@ -14,14 +14,40 @@ PostgreSQL documentation -SET variable { TO | = } { value | 'value' | DEFAULT } -SET TIME ZONE { 'timezone' | LOCAL | DEFAULT } +SET [ SESSION | LOCAL ] variable { TO | = } { value | 'value' | DEFAULT } +SET [ SESSION | LOCAL ] TIME ZONE { timezone | LOCAL | DEFAULT } Inputs + + + + + + Specifies that the command takes effect for the current session. + (This is the default if neither + + + + + + + + Specifies that the command takes effect for only the current + transaction. After COMMIT or ROLLBACK, + the session-level setting takes effect again. Note that + SET LOCAL will appear to have no effect if it's + executed outside a BEGIN block, since the transaction + will end immediately. + + + + variable @@ -30,6 +56,7 @@ SET TIME ZONE { 'timezone' | LOCAL + value @@ -49,34 +76,49 @@ SET TIME ZONE { 'timezone' | LOCAL Description + The SET command changes run-time configuration - parameters. The following parameters can be altered: + parameters. Many of the run-time parameters listed in the + Administrator's Guide can be changed on-the-fly + with SET. (But some require superuser privileges + to change, and others cannot be changed after server or session start.) + Note that SET only affects the value used by the + current session. + + + + If SET or SET SESSION is issued + within a transaction that is later aborted, the effects of the + SET command disappear when the transaction is rolled + back. (This behavior represents a change from + PostgreSQL versions prior to 7.3, where + the effects of SET would not roll back after a later + error.) Once the surrounding transaction is committed, the effects + will persist until the end of the session, unless overridden by another + SET. + + + + The effects of SET LOCAL last only till the end of + the current transaction, whether committed or not. A special case is + SET followed by SET LOCAL within + a single transaction: the SET LOCAL value will be + seen until the end of the transaction, but afterwards (if the transaction + is committed) the SET value will take effect. + + + + Here are additional details about a few of the parameters that can be set: - - CLIENT_ENCODING - NAMES - - - Sets the multibyte client encoding. The specified encoding - must be supported by the backend. - - - - This option is only available if - PostgreSQL is build with multibyte - support. - - - DATESTYLE Choose the date/time representation style. Two separate - settings are made: the default date/time output and the + settings are involved: the default date/time output and the interpretation of ambiguous input. @@ -158,22 +200,6 @@ SET TIME ZONE { 'timezone' | LOCAL (substyles), or one from each separated by a comma. - - Date format initialization may be done by: - - - Setting the PGDATESTYLE environment variable. - If PGDATESTYLE is set in the frontend environment of a client - based on libpq, libpq will automatically set DATESTYLE to the - value of PGDATESTYLE during connection start-up. - - - Running postmaster using the option to - set dates to the European convention. - - - - SET DATESTYLE affects interpretation of input and provides several standard output formats. For @@ -182,6 +208,41 @@ SET TIME ZONE { 'timezone' | LOCAL the to_char family of functions. + + + There are several now-deprecated means for setting the datestyle + in addition to the normal methods of setting it via SET or + a configuration-file entry: + + + Setting the postmaster's PGDATESTYLE environment + variable. (This will be overridden by any of the other methods.) + + + Running postmaster using the option to + select the European conventions. + (This overrides environment variables and configuration-file + entries.) + + + Setting the client's PGDATESTYLE environment variable. + If PGDATESTYLE is set in the frontend environment of a client + based on libpq, libpq will automatically set DATESTYLE to the + value of PGDATESTYLE during connection start-up. This is + equivalent to a manually issued SET. + + + + + + + + + NAMES + + + SET NAMES is an alias for SET CLIENT_ENCODING. + @@ -199,23 +260,22 @@ SET TIME ZONE { 'timezone' | LOCAL The value for the seed to be used by the random function. Allowed values are floating-point numbers between 0 and 1, which - are then multiplied by 231-1. This product will - silently overflow if a number outside the range is used. - - - - The seed can also be set by invoking the - setseed SQL function: - - -SELECT setseed(value); - + are then multiplied by 231-1. + + The seed can also be set by invoking the + setseed SQL function: + + +SELECT setseed(value); + + + @@ -223,13 +283,9 @@ SELECT setseed(value); SERVER_ENCODING - Sets the multibyte server encoding. - - - - This option is only available if - PostgreSQL was built with multibyte - support. + Shows the server-side multibyte encoding. (At present, this + parameter can be shown but not set, because the encoding is + determined at initdb time.) @@ -241,18 +297,18 @@ SELECT setseed(value); Sets the default time zone for your session. Arguments can be an SQL time interval constant, an integer or double precision - constant, or a string representing a time zone supported by - the host operating system. + constant, or a string representing a time zone name recognized + by the host operating system. - The possible values for time zone depends on your operating + The available time zone names depend on your operating system. For example, on Linux /usr/share/zoneinfo contains the database of time zones. - Here are some valid values for time zone: + Here are some typical values for time zone names: @@ -279,6 +335,14 @@ SELECT setseed(value); + + + + + In addition to time zone names, PostgreSQL + accepts these other methods of specifying a time zone: + + 7 @@ -310,7 +374,7 @@ SELECT setseed(value); - If an invalid time zone is specified, the time zone + If an invalid time zone name is specified, the time zone becomes GMT (on most systems anyway). @@ -324,14 +388,9 @@ SELECT setseed(value); - - An extended list of other run-time parameters can be found in the - Administrator's Guide. - - Use to show the - current setting of a parameters. + current setting of a parameter. @@ -363,7 +422,7 @@ SELECT setseed(value); ERROR: permission denied - You must be a superuser to have access to certain settings. + You must be a superuser to alter certain settings. @@ -394,7 +453,7 @@ SET DATESTYLE TO PostgreSQL,European; Set the time zone for Berkeley, California, using quotes to - preserve the uppercase attributes of the time zone specifier (note + preserve the uppercase spelling of the time zone name (note that the date style is PostgreSQL for this example): @@ -437,8 +496,8 @@ SELECT CURRENT_TIMESTAMP AS today; only numeric time zone offsets while PostgreSQL allows full time zone specifier strings as well. All other SET - features are a - PostgreSQL extension. + features are + PostgreSQL extensions. diff --git a/doc/src/sgml/ref/set_session_auth.sgml b/doc/src/sgml/ref/set_session_auth.sgml index 7cd0d7d1ec..dfb2035700 100644 --- a/doc/src/sgml/ref/set_session_auth.sgml +++ b/doc/src/sgml/ref/set_session_auth.sgml @@ -1,4 +1,4 @@ - + 2001-04-21 @@ -16,8 +16,8 @@ -SET SESSION AUTHORIZATION username -SET SESSION AUTHORIZATION DEFAULT +SET [ SESSION | LOCAL ] SESSION AUTHORIZATION username +SET [ SESSION | LOCAL ] SESSION AUTHORIZATION DEFAULT RESET SESSION AUTHORIZATION @@ -51,6 +51,12 @@ RESET SESSION AUTHORIZATION specifies the authenticated username. + + The + The DEFAULT and RESET forms reset the session and current user identifiers to be the originally authenticated user diff --git a/doc/src/sgml/ref/show.sgml b/doc/src/sgml/ref/show.sgml index 37e7e85652..b752f86ee6 100644 --- a/doc/src/sgml/ref/show.sgml +++ b/doc/src/sgml/ref/show.sgml @@ -1,5 +1,5 @@ @@ -54,7 +54,7 @@ SHOW ALL SHOW will display the current setting of a run-time parameter. These variables can be set using the - SET statement or are determined at server start. + SET statement or are determined at session start. @@ -72,25 +72,6 @@ SHOW ALL - - - ERROR: permission denied - - - You must be a superuser to be allowed to see certain settings. - - - - - - WARNING: Time zone is unknown - - - If the TZ or PGTZ environment - variable is not set. - - - diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index 72157ff271..889f2203f6 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -1,5 +1,5 @@ @@ -24,6 +24,9 @@ CDATA means the content is "SGML-free", so you can write without worries about funny characters. --> diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 70131be3a2..78d6dec0aa 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ @@ -488,11 +488,13 @@ psql: could not connect to server: Connection refused # This is a comment log_connections = yes syslog = 2 +search_path = '$user, public' As you see, options are one per line. The equal sign between name and value is optional. Whitespace is insignificant and blank lines are ignored. Hash marks (#) introduce comments - anywhere. + anywhere. Parameter values that are not simple identifiers or + numbers should be single-quoted. @@ -526,7 +528,7 @@ postmaster -c log_connections=yes -c syslog=2 env PGOPTIONS='-c geqo=off' psql - (This works for any client application, not just + (This works for any libpq-based client application, not just psql.) Note that this won't work for options that are fixed when the server is started, such as the port number. @@ -539,11 +541,17 @@ env PGOPTIONS='-c geqo=off' psql => SET ENABLE_SEQSCAN TO OFF; See the SQL command language reference for details on the syntax. + + + Furthermore, it is possible to assign a set of option settings to a user or a database. Whenever a session is started, the default settings for the user and database involved are loaded. The commands ALTER DATABASE and ALTER - USER, respectively, are used to configure these. + USER, respectively, are used to configure these settings. + Such per-database settings override anything received from the postmaster + or the configuration file, and in turn are overridden by per-user + settings. @@ -1091,6 +1099,34 @@ env PGOPTIONS='-c geqo=off' psql + + CLIENT_ENCODING (string) + character set encoding + + + Sets the client-side encoding for multibyte character sets. + The default is to use the database encoding. + + + This option is only available if + PostgreSQL was built with multibyte + support. + + + + + + DATESTYLE (string) + date style + + + Sets the display format for dates, as well as the rules for + interpreting ambiguous input dates. + The default is ISO, US. + + + + deadlock @@ -1586,6 +1622,18 @@ dynamic_library_path = '/usr/local/lib/postgresql:/home/my_project/lib:$libdir' + + TIMEZONE (string) + time zone + + + Sets the time zone for displaying and interpreting timestamps. + The default is to use whatever the system environment + specifies as the timezone. + + + + TRANSFORM_NULL_EQUALS (boolean) IS NULL diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index ca66b0afaf..d6176ec5c4 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 eca4a5de5f..77ad566ba8 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 51f432b1a0..1a13786a04 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 9e5002e37f..92c9306f18 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()) + if (interactive && 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) - { - 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 41f122767a..bb53ad8b3b 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 405a7ceaa2..5398d718b6 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 de42538bd6..03d7a66457 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. - */ -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! */ -/* These get initialized from the "master" values in init/globals.c */ -static int DefaultDateStyle; -static bool DefaultEuroDates; - -static bool -parse_datestyle_internal(char *value) +/* + * assign_datestyle: GUC assign_hook for datestyle + */ +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"); + ok = false; - return TRUE; -} + pfree(rawstring); + freeList(elemlist); -static bool -parse_datestyle(List *args) -{ - int rstat = FALSE; - List *arg; - char *value; - - if (args == NULL) - return reset_datestyle(); - - Assert(IsA(args, List)); - - 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); - } - - value = v->val.str; - } - else - { - elog(ERROR, "SET DATESTYLE argument is not valid"); - value = NULL; - } - - rstat = parse_datestyle_internal(value); - - if (rstat != TRUE) - return rstat; + if (interactive) + elog(ERROR, "SET DATESTYLE: conflicting specifications"); + return NULL; } - return rstat; + /* + * 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(32); + if (!result) + return NULL; + + 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 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 - * + * assign_timezone: GUC assign_hook for 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 - */ -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; + const char *valueptr = value; + char *val; + Interval *interval; - Assert(IsA(arg, List)); - p = lfirst(arg); - Assert(IsA(p, A_Const)); - - type = p->typename; - if (type != NULL) + 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) - { - float8 time; - - time = DatumGetFloat8(DirectFunctionCall1(float8in, CStringGetDatum(p->val.val.str))); - CTimeZone = time * 3600; - } - - /* - * We do not actually generate an integer constant in gram.y - * so this is not used... - */ - else if (typeOid == INT4OID) - { - int32 time; - - time = p->val.val.ival; - CTimeZone = time * 3600; - } - else - { - elog(ERROR, "Unable to process SET TIME ZONE command; internal coding error"); - } - + 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) + { + CTimeZone = hours * 3600; + HasCTZSet = true; + } + } + else if (strcasecmp(value, "UNKNOWN") == 0) + { + /* + * 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 + */ + if (doit) + { + if (tzbuf[0] == 'T') + { + strcpy(tzbuf, "="); + if (putenv(tzbuf) != 0) + elog(ERROR, "Unable to clear TZ environment variable"); + tzset(); + } + HasCTZSet = false; + } + } 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; -static bool + /* + * 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; +} + +/* + * 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) + +bool +assign_random_seed(double value, bool doit, bool interactive) { - 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); + /* Can't really roll back on error, so ignore non-interactive setting */ + if (doit && interactive) + DirectFunctionCall1(setseed, Float8GetDatum(value)); + return true; } -static bool +const char * show_random_seed(void) { - elog(INFO, "Seed for random number generator is unavailable"); - return (TRUE); -} - -static bool -reset_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 + int old_encoding = 0; - 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; - -#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); + if (strcasecmp(value, pg_get_client_encoding_name()) != 0) + return NULL; #endif - return TRUE; + + return value; } -static bool -show_client_encoding(void) + +const char * +assign_server_encoding(const char *value, bool doit, bool interactive) { - elog(INFO, "Current client encoding is '%s'", - pg_get_client_encoding_name()); - return TRUE; + if (interactive) + elog(ERROR, "SET SERVER_ENCODING is not supported"); + /* Pretend never to fail in noninteractive case */ + return value; } -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); -#endif - return TRUE; -} - -/* Called during MULTIBYTE backend startup ... */ -void -set_default_client_encoding(void) -{ - reset_client_encoding(); -} - - -static bool -parse_server_encoding(List *args) -{ - elog(INFO, "SET SERVER_ENCODING is not supported"); - return TRUE; -} - -static bool +const char * show_server_encoding(void) { - elog(INFO, "Current server encoding is '%s'", GetDatabaseEncodingName()); - return TRUE; + return GetDatabaseEncodingName(); } -static bool -reset_server_encoding(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, "RESET SERVER_ENCODING is not supported"); - return TRUE; + Oid usesysid; + char *endptr; + char *result; + + usesysid = (Oid) strtoul(value, &endptr, 10); + + if (endptr != value && *endptr == '\0' && OidIsValid(usesysid)) + { + /* use the numeric user ID */ + } + else + { + HeapTuple userTup; + + userTup = SearchSysCache(SHADOWNAME, + PointerGetDatum(value), + 0, 0, 0); + if (!HeapTupleIsValid(userTup)) + { + if (interactive) + elog(ERROR, "user \"%s\" does not exist", value); + return NULL; + } + + usesysid = ((Form_pg_shadow) GETSTRUCT(userTup))->usesysid; + + ReleaseSysCache(userTup); + } + + if (doit) + SetSessionAuthorization(usesysid); + + result = (char *) malloc(32); + if (!result) + return NULL; + + snprintf(result, 32, "%lu", (unsigned long) usesysid); + + return result; } - -static bool +const char * show_session_authorization(void) { - elog(INFO, "Current session authorization is '%s'", - GetUserName(GetSessionUserId())); - return TRUE; -} - - - -/* 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); - 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; - - if (args != NULL) - { - 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 (strcasecmp(name, "session_authorization") == 0) - SetSessionAuthorization(value); - else - SetConfigOption(name, - value, - (superuser() ? PGC_SUSET : PGC_USERSET), - PGC_S_SESSION); - } - return; -} - -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(); - } - else - { - const char *val = GetConfigOption(name); - - elog(INFO, "%s is %s", name, val); - } -} - -void -ResetPGVariable(const char *name) -{ - 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 9c79e7b78d..227fcc35e5 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 a3751d3698..909847dc85 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 cf203243f1..1f0d175326 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 bd3a7a0832..0009b9df2f 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 stmt, schema_stmt, @@ -246,6 +247,8 @@ static void doNegateFloat(Value *v); %type insert_rest +%type set_rest + %type OptTableElement, ConstraintElem %type columnDef %type 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 = "TRANSACTION ISOLATION LEVEL"; + n->args = makeList1(makeStringConst($4, NULL)); + $$ = 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($4, NULL)); - $$ = (Node *) n; + 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"; - $$ = (Node *) n; - } - | SHOW ALL - { - VariableShowStmt *n = makeNode(VariableShowStmt); - n->name = "all"; + n->name = "timezone"; $$ = (Node *) n; } | SHOW TRANSACTION ISOLATION LEVEL { VariableShowStmt *n = makeNode(VariableShowStmt); - n->name = "XactIsoLevel"; + n->name = "TRANSACTION ISOLATION LEVEL"; + $$ = (Node *) n; + } + | SHOW SESSION AUTHORIZATION + { + VariableShowStmt *n = makeNode(VariableShowStmt); + n->name = "session_authorization"; + $$ = (Node *) n; + } + | SHOW ALL + { + VariableShowStmt *n = makeNode(VariableShowStmt); + 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 a3a8f2521e..1513f13878 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 eaf626ace9..53bf45dce9 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 9a9c062559..e72d8dfccf 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 1a908d9d6b..d6e0358e81 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 c5c8d312d3..ba962ac8b1 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 +#include "utils/pg_locale.h" + /* GUC storage area */ -char * locale_messages; -char * locale_monetary; -char * locale_numeric; -char * locale_time; +char *locale_messages; +char *locale_monetary; +char *locale_numeric; +char *locale_time; -/* GUC parse hooks */ - -bool locale_messages_check(const char *proposed) -{ -#ifdef LC_MESSAGES - return chklocale(LC_MESSAGES, proposed); -#else - /* We return true here so LC_MESSAGES can be set in the - configuration file on every system. */ - return true; -#endif -} - -bool locale_monetary_check(const char *proposed) -{ - return chklocale(LC_MONETARY, proposed); -} - -bool locale_numeric_check(const char *proposed) -{ - return chklocale(LC_NUMERIC, proposed); -} - -bool locale_time_check(const char *proposed) -{ - return chklocale(LC_TIME, proposed); -} /* GUC assign hooks */ -void locale_messages_assign(const char *value) +static const char * +locale_xxx_assign(int category, const char *value, bool doit, bool interactive) { + if (doit) + { + if (!setlocale(category, value)) + return NULL; + } + else + { + char *save; + + save = setlocale(category, NULL); + if (!save) + return NULL; + + if (!setlocale(category, value)) + return NULL; + + setlocale(category, save); + } + return value; +} + +const char * +locale_messages_assign(const char *value, bool doit, bool interactive) +{ + /* LC_MESSAGES category does not exist everywhere, but accept it anyway */ #ifdef LC_MESSAGES - setlocale(LC_MESSAGES, value); + return locale_xxx_assign(LC_MESSAGES, value, doit, interactive); +#else + return value; #endif } -void locale_monetary_assign(const char *value) +const char * +locale_monetary_assign(const char *value, bool doit, bool interactive) { - setlocale(LC_MONETARY, value); + return locale_xxx_assign(LC_MONETARY, value, doit, interactive); } -void locale_numeric_assign(const char *value) +const char * +locale_numeric_assign(const char *value, bool doit, bool interactive) { - setlocale(LC_NUMERIC, value); + return locale_xxx_assign(LC_NUMERIC, value, doit, interactive); } -void locale_time_assign(const char *value) +const char * +locale_time_assign(const char *value, bool doit, bool interactive) { - setlocale(LC_TIME, value); -} - - -/* - * Returns true if the proposed string represents a valid locale of - * the given category. This is probably pretty slow, but it's not - * called in critical places. - */ -bool -chklocale(int category, const char *proposed) -{ - char *save; - - save = setlocale(category, NULL); - if (!save) - return false; - - if (!setlocale(category, proposed)) - return false; - - setlocale(category, save); - return true; + return locale_xxx_assign(LC_TIME, value, doit, interactive); } @@ -123,7 +107,6 @@ lc_collate_is_c(void) } - /* * Return the POSIX lconv struct (contains number/money formatting * information) with locale information for all categories. @@ -131,10 +114,11 @@ lc_collate_is_c(void) struct lconv * PGLC_localeconv(void) { - struct lconv *extlconv; static bool CurrentLocaleConvValid = false; static struct lconv CurrentLocaleConv; + struct lconv *extlconv; + /* Did we do it already? */ if (CurrentLocaleConvValid) return &CurrentLocaleConv; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 78cb6306e0..88528b9a06 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 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 572e21ba83..a1813b67b8 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) +const char * +assign_server_min_messages(const char *newval, + bool doit, bool interactive) { - if (strcasecmp(lev, "debug") == 0 || - strcasecmp(lev, "debug5") == 0 || - strcasecmp(lev, "debug4") == 0 || - strcasecmp(lev, "debug3") == 0 || - strcasecmp(lev, "debug2") == 0 || - strcasecmp(lev, "debug1") == 0 || - strcasecmp(lev, "info") == 0 || - strcasecmp(lev, "notice") == 0 || - strcasecmp(lev, "warning") == 0 || - strcasecmp(lev, "error") == 0 || - strcasecmp(lev, "log") == 0 || - strcasecmp(lev, "fatal") == 0 || - strcasecmp(lev, "panic") == 0) - return true; - return false; -} - -void -assign_server_min_messages(const char *lev) -{ - if (strcasecmp(lev, "debug") == 0) - server_min_messages = DEBUG5; - else if (strcasecmp(lev, "debug5") == 0) - server_min_messages = DEBUG5; - else if (strcasecmp(lev, "debug4") == 0) - server_min_messages = DEBUG4; - else if (strcasecmp(lev, "debug3") == 0) - server_min_messages = DEBUG3; - else if (strcasecmp(lev, "debug2") == 0) - server_min_messages = DEBUG2; - else if (strcasecmp(lev, "debug1") == 0) - server_min_messages = DEBUG1; - else if (strcasecmp(lev, "info") == 0) - server_min_messages = INFO; - else if (strcasecmp(lev, "notice") == 0) - server_min_messages = NOTICE; - else if (strcasecmp(lev, "warning") == 0) - server_min_messages = WARNING; - else if (strcasecmp(lev, "error") == 0) - server_min_messages = ERROR; - else if (strcasecmp(lev, "log") == 0) - server_min_messages = LOG; - else if (strcasecmp(lev, "fatal") == 0) - server_min_messages = FATAL; - else if (strcasecmp(lev, "panic") == 0) - server_min_messages = PANIC; + if (strcasecmp(newval, "debug") == 0) + { if (doit) server_min_messages = DEBUG1; } + else if (strcasecmp(newval, "debug5") == 0) + { if (doit) server_min_messages = DEBUG5; } + else if (strcasecmp(newval, "debug4") == 0) + { if (doit) server_min_messages = DEBUG4; } + else if (strcasecmp(newval, "debug3") == 0) + { if (doit) server_min_messages = DEBUG3; } + else if (strcasecmp(newval, "debug2") == 0) + { if (doit) server_min_messages = DEBUG2; } + else if (strcasecmp(newval, "debug1") == 0) + { if (doit) server_min_messages = DEBUG1; } + else if (strcasecmp(newval, "info") == 0) + { if (doit) server_min_messages = INFO; } + else if (strcasecmp(newval, "notice") == 0) + { if (doit) server_min_messages = NOTICE; } + else if (strcasecmp(newval, "warning") == 0) + { if (doit) server_min_messages = WARNING; } + else if (strcasecmp(newval, "error") == 0) + { if (doit) server_min_messages = ERROR; } + else if (strcasecmp(newval, "log") == 0) + { if (doit) server_min_messages = LOG; } + else if (strcasecmp(newval, "fatal") == 0) + { if (doit) server_min_messages = FATAL; } + else if (strcasecmp(newval, "panic") == 0) + { if (doit) server_min_messages = PANIC; } else - /* Can't get here unless guc.c screwed up */ - elog(ERROR, "bogus server_min_messages %s", lev); + return NULL; /* fail */ + return newval; /* OK */ } -bool -check_client_min_messages(const char *lev) +const char * +assign_client_min_messages(const char *newval, + bool doit, bool interactive) { - if (strcasecmp(lev, "debug") == 0 || - strcasecmp(lev, "debug5") == 0 || - strcasecmp(lev, "debug4") == 0 || - strcasecmp(lev, "debug3") == 0 || - strcasecmp(lev, "debug2") == 0 || - strcasecmp(lev, "debug1") == 0 || - strcasecmp(lev, "log") == 0 || - strcasecmp(lev, "info") == 0 || - strcasecmp(lev, "notice") == 0 || - strcasecmp(lev, "warning") == 0 || - strcasecmp(lev, "error") == 0) - return true; - return false; -} - -void -assign_client_min_messages(const char *lev) -{ - if (strcasecmp(lev, "debug") == 0) - client_min_messages = DEBUG5; - else if (strcasecmp(lev, "debug5") == 0) - client_min_messages = DEBUG5; - else if (strcasecmp(lev, "debug4") == 0) - client_min_messages = DEBUG4; - else if (strcasecmp(lev, "debug3") == 0) - client_min_messages = DEBUG3; - else if (strcasecmp(lev, "debug2") == 0) - client_min_messages = DEBUG2; - else if (strcasecmp(lev, "debug1") == 0) - client_min_messages = DEBUG1; - else if (strcasecmp(lev, "log") == 0) - client_min_messages = LOG; - else if (strcasecmp(lev, "info") == 0) - client_min_messages = INFO; - else if (strcasecmp(lev, "notice") == 0) - client_min_messages = NOTICE; - else if (strcasecmp(lev, "warning") == 0) - client_min_messages = WARNING; - else if (strcasecmp(lev, "error") == 0) - client_min_messages = ERROR; + if (strcasecmp(newval, "debug") == 0) + { if (doit) client_min_messages = DEBUG1; } + else if (strcasecmp(newval, "debug5") == 0) + { if (doit) client_min_messages = DEBUG5; } + else if (strcasecmp(newval, "debug4") == 0) + { if (doit) client_min_messages = DEBUG4; } + else if (strcasecmp(newval, "debug3") == 0) + { if (doit) client_min_messages = DEBUG3; } + else if (strcasecmp(newval, "debug2") == 0) + { if (doit) client_min_messages = DEBUG2; } + else if (strcasecmp(newval, "debug1") == 0) + { if (doit) client_min_messages = DEBUG1; } + else if (strcasecmp(newval, "log") == 0) + { if (doit) client_min_messages = LOG; } + else if (strcasecmp(newval, "info") == 0) + { if (doit) client_min_messages = INFO; } + else if (strcasecmp(newval, "notice") == 0) + { if (doit) client_min_messages = NOTICE; } + else if (strcasecmp(newval, "warning") == 0) + { if (doit) client_min_messages = WARNING; } + else if (strcasecmp(newval, "error") == 0) + { if (doit) client_min_messages = ERROR; } else - /* Can't get here unless guc.c screwed up */ - elog(ERROR, "bogus client_min_messages %s", lev); + return NULL; /* fail */ + return newval; /* OK */ } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 4cc9d396c7..fd3f191d5c 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 95974c6d6a..817c87c80a 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 0000000000..fe252d534d --- /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 fe6cf89ac0..2f50b4b55c 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 f917af9a42..f1d4e8ee29 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 . @@ -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,14 +59,11 @@ extern bool FixBTree; #ifdef HAVE_SYSLOG extern char *Syslog_facility; extern char *Syslog_ident; -static bool check_facility(const char *facility); + +static const char *assign_facility(const char *facility, + bool doit, bool interactive); #endif -static char *default_iso_level_string; - -static bool check_defaultxactisolevel(const char *value); -static void assign_defaultxactisolevel(const char *value); - /* * 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,229 +691,710 @@ 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, + { "DateStyle", PGC_USERSET, GUC_LIST_INPUT }, &datestyle_string, + "ISO, US", assign_datestyle, show_datestyle + }, + + { + { "default_transaction_isolation", PGC_USERSET }, &default_iso_level_string, + "read committed", assign_defaultxactisolevel, NULL + }, + + { + { "dynamic_library_path", PGC_SUSET }, &Dynamic_library_path, "$libdir", NULL, NULL }, { - "search_path", PGC_USERSET, PGC_S_DEFAULT, &namespace_search_path, - "$user,public", check_search_path, assign_search_path - }, - - { - "krb_server_keyfile", PGC_POSTMASTER, PGC_S_DEFAULT, &pg_krb_server_keyfile, + { "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, PGC_S_DEFAULT, &locale_monetary, - "", locale_monetary_check, locale_monetary_assign + { "lc_monetary", PGC_USERSET }, &locale_monetary, + "", locale_monetary_assign, NULL }, { - "lc_numeric", PGC_USERSET, PGC_S_DEFAULT, &locale_numeric, - "", locale_numeric_check, locale_numeric_assign + { "lc_numeric", PGC_USERSET }, &locale_numeric, + "", locale_numeric_assign, NULL }, { - "lc_time", PGC_USERSET, PGC_S_DEFAULT, &locale_time, - "", locale_time_check, locale_time_assign + { "lc_time", PGC_USERSET }, &locale_time, + "", locale_time_assign, NULL }, { - "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 + { "search_path", PGC_USERSET, GUC_LIST_INPUT | GUC_LIST_QUOTE }, + &namespace_search_path, + "$user,public", assign_search_path, NULL + }, + + { + { "server_encoding", PGC_USERSET }, &server_encoding_string, + "SQL_ASCII", assign_server_encoding, show_server_encoding + }, + + { + { "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 ********/ - /* - * 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. + * Actual lookup of variables is done through this single, sorted array. */ -static enum config_type -find_option(const char *name, struct config_generic ** record) -{ - int i; +static struct config_generic **guc_variables; +static int num_guc_variables; - Assert(name); +static bool guc_dirty; /* TRUE if need to do commit/abort work */ - 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; - } +static char *guc_string_workspace; /* for avoiding memory leaks */ - 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; ConfigureNamesReal[i].name; i++) - if (strcasecmp(ConfigureNamesReal[i].name, name) == 0) - { - if (record) - *record = (struct config_generic *) & ConfigureNamesReal[i]; - return PGC_REAL; - } - - 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; - } - - return PGC_NONE; -} +static int guc_var_compare(const void *a, const void *b); +static void _ShowOption(struct config_generic *record); /* - * 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. + * 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). */ -void -ResetAllOptions(bool isStartup) +static void +build_guc_variables(void) { + int num_vars = 0; + struct config_generic **guc_vars; int i; - for (i = 0; ConfigureNamesBool[i].name; i++) + for (i = 0; ConfigureNamesBool[i].gen.name; i++) { struct config_bool *conf = &ConfigureNamesBool[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; - } + /* 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++) + for (i = 0; ConfigureNamesInt[i].gen.name; i++) { struct config_int *conf = &ConfigureNamesInt[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; - } + conf->gen.vartype = PGC_INT; + num_vars++; } - for (i = 0; ConfigureNamesReal[i].name; i++) + for (i = 0; ConfigureNamesReal[i].gen.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; - } + conf->gen.vartype = PGC_REAL; + num_vars++; } - for (i = 0; ConfigureNamesString[i].name; i++) + for (i = 0; ConfigureNamesString[i].gen.name; i++) { struct config_string *conf = &ConfigureNamesString[i]; - if (isStartup || - conf->context == PGC_SUSET || conf->context == PGC_USERSET) - { - char *str = NULL; + conf->gen.vartype = PGC_STRING; + num_vars++; + } - if (conf->default_val == NULL && - conf->boot_default_val) + 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; +} + + +/* + * 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 +InitializeGUCOptions(void) +{ + int i; + char *env; + + /* + * 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_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; + + switch (gconf->vartype) + { + case PGC_BOOL: { - str = strdup(conf->boot_default_val); + 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; + } + } + } + + 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_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; + + switch (gconf->vartype) + { + 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"); - conf->default_val = str; + + /* + * 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; } - if (conf->default_val) - { - str = strdup(conf->default_val); - if (str == NULL) - elog(ERROR, "out of memory"); - } - if (conf->assign_hook) - (conf->assign_hook) (str); - if (*conf->variable) - free(*conf->variable); - *conf->variable = str; } } } +/* + * 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; i < num_guc_variables; i++) + { + struct config_generic *gconf = guc_variables[i]; + + /* Skip if nothing's happened to this var in this transaction */ + if (gconf->status == 0) + continue; + + switch (gconf->vartype) + { + case PGC_BOOL: + { + struct config_bool *conf = (struct config_bool *) 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_INT: + { + 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; + } + case PGC_REAL: + { + 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; + } + } + } + + guc_dirty = false; +} + /* * Try to interpret value as boolean value. Valid values are: true, @@ -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, &boolval)) + if (!parse_bool(value, &newval)) { - elog(elevel, "option '%s' requires a boolean value", name); + elog(elevel, "option '%s' requires a boolean value", + name); return false; } - /* no parse_hook needed for booleans */ + } + 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, (int) newval); + return false; + } + + 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)) - { - elog(elevel, "invalid value for option '%s': %d", - name, intval); - return false; - } - if (DoIt) - { - if (conf->assign_hook) - (conf->assign_hook) (intval); - *conf->variable = intval; - if (makeDefault) - conf->default_val = intval; - conf->source = source; - } } - else if (DoIt) + else { - if (conf->assign_hook) - (conf->assign_hook) (conf->default_val); - *conf->variable = conf->default_val; + 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, newval); + return false; + } + + if (DoIt || makeDefault) + { + if (DoIt) + { + *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; + } } 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)) - { - elog(elevel, "invalid value for option '%s': %g", - name, dval); - return false; - } - if (DoIt) - { - if (conf->assign_hook) - (conf->assign_hook) (dval); - *conf->variable = dval; - if (makeDefault) - conf->default_val = dval; - conf->source = source; - } } - else if (DoIt) + else { - if (conf->assign_hook) - (conf->assign_hook) (conf->default_val); - *conf->variable = conf->default_val; + 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, newval); + return false; + } + + if (DoIt || makeDefault) + { + if (DoIt) + { + *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; + } } 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)) - { - elog(elevel, "invalid value for option '%s': '%s'", - name, value); - return false; - } - if (DoIt) - { - char *str; - - str = strdup(value); - if (str == NULL) - { - elog(elevel, "out of memory"); - return false; - } - if (conf->assign_hook) - (conf->assign_hook) (str); - if (*conf->variable) - free(*conf->variable); - *conf->variable = str; - if (makeDefault) - { - 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; - } - conf->source = source; - } - } - else if (DoIt) - { - char *str; - - if (!conf->default_val && conf->boot_default_val) - { - str = strdup(conf->boot_default_val); - if (str == NULL) - { - elog(elevel, "out of memory"); - return false; - } - conf->default_val = str; - } - str = strdup(conf->default_val); - if (str == NULL) + newval = strdup(value); + if (newval == NULL) { elog(elevel, "out of memory"); return false; } - if (conf->assign_hook) - (conf->assign_hook) (str); - if (*conf->variable) - free(*conf->variable); - *conf->variable = str; + } + 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 ? value : ""); + return false; + } + else if (hookresult != newval) + { + 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; + } + } + + 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) + { + SET_STRING_RESET_VAL(conf, newval); + conf->gen.reset_source = source; + } + if (conf->gen.session_source <= source) + { + SET_STRING_SESSION_VAL(conf, newval); + conf->gen.session_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 (isLocal) + { + conf->gen.status |= GUC_HAVE_LOCAL; + guc_dirty = true; + } + else + { + SET_STRING_TENTATIVE_VAL(conf, newval); + conf->gen.tentative_source = source; + conf->gen.status |= GUC_HAVE_TENTATIVE; + guc_dirty = true; + } + } + 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]; - switch (opttype) + record = find_option(name); + if (record == NULL) + elog(ERROR, "Option '%s' is not recognized", name); + + 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; - - default: - val = "???"; + return ((struct config_string *) record)->reset_val; } - elog(INFO, "%s is %s", record->name, val); + return NULL; } + + +/* + * 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; + } + } + + 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]); - - for (i = 0; ConfigureNamesReal[i].name; i++) - _ShowOption(PGC_REAL, (struct config_generic *) & ConfigureNamesReal[i]); - - for (i = 0; ConfigureNamesString[i].name; i++) - _ShowOption(PGC_STRING, (struct config_generic *) & ConfigureNamesString[i]); + if ((conf->flags & GUC_NO_SHOW_ALL) == 0) + _ShowOption(conf); + } } +static void +_ShowOption(struct config_generic *record) +{ + char buffer[256]; + const char *val; + 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) +static const char * +assign_defaultxactisolevel(const char *newval, bool doit, bool interactive) { - return (strcasecmp(value, "read committed") == 0 - || strcasecmp(value, "serializable") == 0) - ? true : false; -} - - -static void -assign_defaultxactisolevel(const char *value) -{ - 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 843ad5ce15..3fae561d7c 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' diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 9252a8d324..92e74b88d7 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: xlog.h,v 1.29 2002/03/15 19:20:36 tgl Exp $ + * $Id: xlog.h,v 1.30 2002/05/17 01:19:19 tgl Exp $ */ #ifndef XLOG_H #define XLOG_H @@ -216,7 +216,7 @@ extern XLogRecPtr GetRedoRecPtr(void); */ extern XLogRecPtr GetUndoRecPtr(void); -extern bool check_xlog_sync_method(const char *method); -extern void assign_xlog_sync_method(const char *method); +extern const char *assign_xlog_sync_method(const char *method, + bool doit, bool interactive); #endif /* XLOG_H */ diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index 545b1a1383..8fe62acb0e 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: namespace.h,v 1.12 2002/05/01 23:06:41 tgl Exp $ + * $Id: namespace.h,v 1.13 2002/05/17 01:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -75,8 +75,8 @@ extern bool isTempNamespace(Oid namespaceId); /* stuff for search_path GUC variable */ extern char *namespace_search_path; -extern bool check_search_path(const char *proposed); -extern void assign_search_path(const char *newval); +extern const char *assign_search_path(const char *newval, + bool doit, bool interactive); extern void InitializeSearchPath(void); extern List *fetch_search_path(void); diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h index f4f12c9712..bdca5c88a9 100644 --- a/src/include/commands/variable.h +++ b/src/include/commands/variable.h @@ -1,18 +1,32 @@ /* - * Headers for handling of 'SET var TO', 'SHOW var' and 'RESET var' - * statements + * variable.h + * Routines for handling specialized SET variables. * - * $Id: variable.h,v 1.17 2001/11/05 17:46:33 momjian Exp $ + * $Id: variable.h,v 1.18 2002/05/17 01:19:19 tgl Exp $ * */ #ifndef VARIABLE_H #define VARIABLE_H -extern void SetPGVariable(const char *name, List *args); -extern void GetPGVariable(const char *name); -extern void ResetPGVariable(const char *name); - -extern void set_default_datestyle(void); -extern void set_default_client_encoding(void); +extern const char *assign_datestyle(const char *value, + bool doit, bool interactive); +extern const char *show_datestyle(void); +extern const char *assign_timezone(const char *value, + bool doit, bool interactive); +extern const char *show_timezone(void); +extern const char *assign_XactIsoLevel(const char *value, + bool doit, bool interactive); +extern const char *show_XactIsoLevel(void); +extern bool assign_random_seed(double value, + bool doit, bool interactive); +extern const char *show_random_seed(void); +extern const char *assign_client_encoding(const char *value, + bool doit, bool interactive); +extern const char *assign_server_encoding(const char *value, + bool doit, bool interactive); +extern const char *show_server_encoding(void); +extern const char *assign_session_authorization(const char *value, + bool doit, bool interactive); +extern const char *show_session_authorization(void); #endif /* VARIABLE_H */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 09e1c0fe63..2e5fc76bb7 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: miscadmin.h,v 1.103 2002/05/05 00:03:29 tgl Exp $ + * $Id: miscadmin.h,v 1.104 2002/05/17 01:19:19 tgl Exp $ * * NOTES * some of the information in this file should be moved to @@ -211,7 +211,7 @@ extern Oid GetSessionUserId(void); extern void SetSessionUserId(Oid userid); extern void InitializeSessionUserId(const char *username); extern void InitializeSessionUserIdStandalone(void); -extern void SetSessionAuthorization(const char *username); +extern void SetSessionAuthorization(Oid userid); extern void SetDataDir(const char *dir); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 0c5672dd67..a0bf47d7ed 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.176 2002/05/12 20:10:04 tgl Exp $ + * $Id: parsenodes.h,v 1.177 2002/05/17 01:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1439,6 +1439,7 @@ typedef struct VariableSetStmt NodeTag type; char *name; List *args; + bool is_local; /* SET LOCAL */ } VariableSetStmt; /* ---------------------- diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index a889bd6c7b..46d8753ff4 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: datetime.h,v 1.29 2002/04/21 19:48:31 thomas Exp $ + * $Id: datetime.h,v 1.30 2002/05/17 01:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -289,7 +289,7 @@ extern int EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str); extern int DecodeSpecial(int field, char *lowtoken, int *val); extern int DecodeUnits(int field, char *lowtoken, int *val); -extern void ClearDateCache(bool); +extern bool ClearDateCache(bool, bool, bool); extern int j2day(int jd); diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 6e0d580828..cc3ebd9220 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: elog.h,v 1.36 2002/04/21 00:22:52 ishii Exp $ + * $Id: elog.h,v 1.37 2002/05/17 01:19:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -60,9 +60,9 @@ __attribute__((format(printf, 2, 3))); extern int DebugFileOpen(void); -extern bool check_server_min_messages(const char *lev); -extern void assign_server_min_messages(const char *lev); -extern bool check_client_min_messages(const char *lev); -extern void assign_client_min_messages(const char *lev); +extern const char *assign_server_min_messages(const char *newval, + bool doit, bool interactive); +extern const char *assign_client_min_messages(const char *newval, + bool doit, bool interactive); #endif /* ELOG_H */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index a6eb17f6cf..ce1b10be83 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -4,13 +4,15 @@ * External declarations pertaining to backend/utils/misc/guc.c and * backend/utils/misc/guc-file.l * - * $Id: guc.h,v 1.16 2002/03/24 04:31:09 tgl Exp $ + * $Id: guc.h,v 1.17 2002/05/17 01:19:19 tgl Exp $ */ #ifndef GUC_H #define GUC_H +#include "nodes/pg_list.h" #include "utils/array.h" + /* * Certain options can only be set at certain times. The rules are * like this: @@ -52,30 +54,45 @@ typedef enum * The following type records the source of the current setting. A * new setting can only take effect if the previous setting had the * same or lower level. (E.g, changing the config file doesn't - * override the postmaster command line.) + * override the postmaster command line.) Tracking the source allows us + * to process sources in any convenient order without affecting results. + * Sources <= PGC_S_OVERRIDE will set the default used by RESET, as well + * as the current value. */ typedef enum { PGC_S_DEFAULT = 0, /* wired-in default */ - PGC_S_FILE = 1, /* postgresql.conf */ - PGC_S_ARGV = 2, /* postmaster command line */ - PGC_S_DATABASE = 3, /* per-database setting */ - PGC_S_USER = 4, /* per-user setting */ - PGC_S_CLIENT = 5, /* from client (PGOPTIONS) */ - PGC_S_SESSION = 6, /* SET command */ - PGC_S_INFINITY = 100 /* can be used to avoid checks */ + PGC_S_ENV_VAR = 1, /* postmaster environment variable */ + PGC_S_FILE = 2, /* postgresql.conf */ + PGC_S_ARGV = 3, /* postmaster command line */ + PGC_S_DATABASE = 4, /* per-database setting */ + PGC_S_USER = 5, /* per-user setting */ + PGC_S_CLIENT = 6, /* from client (PGOPTIONS) */ + PGC_S_OVERRIDE = 7, /* special case to forcibly set default */ + PGC_S_SESSION = 8 /* SET command */ } GucSource; extern void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source); extern const char *GetConfigOption(const char *name); +extern const char *GetConfigOptionResetString(const char *name); extern void ProcessConfigFile(GucContext context); -extern void ResetAllOptions(bool isStartup); +extern void InitializeGUCOptions(void); +extern void ResetAllOptions(void); +extern void AtEOXact_GUC(bool isCommit); extern void ParseLongOption(const char *string, char **name, char **value); extern bool set_config_option(const char *name, const char *value, - GucContext context, bool DoIt, GucSource source); + GucContext context, GucSource source, + bool isLocal, bool DoIt); +extern void ShowGUCConfigOption(const char *name); extern void ShowAllGUCConfig(void); +extern void SetPGVariable(const char *name, List *args, bool is_local); +extern void GetPGVariable(const char *name); +extern void ResetPGVariable(const char *name); + +extern char *flatten_set_variable_args(const char *name, List *args); + extern void ProcessGUCArray(ArrayType *array, GucSource source); extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value); extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name); diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index b5056e6ca8..a1ba131a92 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -2,7 +2,7 @@ * * PostgreSQL locale utilities * - * $Header: /cvsroot/pgsql/src/include/utils/pg_locale.h,v 1.12 2002/04/03 05:39:33 petere Exp $ + * $Id: pg_locale.h,v 1.13 2002/05/17 01:19:19 tgl Exp $ * * Copyright (c) 2002, PostgreSQL Global Development Group * @@ -12,26 +12,23 @@ #ifndef _PG_LOCALE_ #define _PG_LOCALE_ -#include "postgres.h" #include -extern char * locale_messages; -extern char * locale_monetary; -extern char * locale_numeric; -extern char * locale_time; +extern char *locale_messages; +extern char *locale_monetary; +extern char *locale_numeric; +extern char *locale_time; -bool locale_messages_check(const char *proposed); -bool locale_monetary_check(const char *proposed); -bool locale_numeric_check(const char *proposed); -bool locale_time_check(const char *proposed); +extern const char *locale_messages_assign(const char *value, + bool doit, bool interactive); +extern const char *locale_monetary_assign(const char *value, + bool doit, bool interactive); +extern const char *locale_numeric_assign(const char *value, + bool doit, bool interactive); +extern const char *locale_time_assign(const char *value, + bool doit, bool interactive); -void locale_messages_assign(const char *value); -void locale_monetary_assign(const char *value); -void locale_numeric_assign(const char *value); -void locale_time_assign(const char *value); - -bool chklocale(int category, const char *proposed); -bool lc_collate_is_c(void); +extern bool lc_collate_is_c(void); /* * Return the POSIX lconv struct (contains number/money formatting diff --git a/src/interfaces/jdbc/org/postgresql/Connection.java b/src/interfaces/jdbc/org/postgresql/Connection.java index 21f6c60ff3..96b85c78ce 100644 --- a/src/interfaces/jdbc/org/postgresql/Connection.java +++ b/src/interfaces/jdbc/org/postgresql/Connection.java @@ -12,7 +12,7 @@ import org.postgresql.util.*; import org.postgresql.core.*; /* - * $Id: Connection.java,v 1.46 2002/05/14 03:00:35 barry Exp $ + * $Id: Connection.java,v 1.47 2002/05/17 01:19:19 tgl Exp $ * * This abstract class is used by org.postgresql.Driver to open either the JDBC1 or * JDBC2 versions of the Connection class. @@ -951,7 +951,7 @@ public abstract class Connection public int getTransactionIsolation() throws SQLException { clearWarnings(); - ExecSQL("show xactisolevel"); + ExecSQL("show transaction isolation level"); SQLWarning warning = getWarnings(); if (warning != null)