From 9cbaf7217747d6b5c88ba9b500a37b8372f185c9 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 25 Apr 2003 19:45:10 +0000 Subject: [PATCH] In the continuing saga of FE/BE protocol revisions, add reporting of initial values and runtime changes in selected parameters. This gets rid of the need for an initial 'select pg_client_encoding()' query in libpq, bringing us back to one message transmitted in each direction for a standard connection startup. To allow server version to be sent using the same GUC mechanism that handles other parameters, invent the concept of a never-settable GUC parameter: you can 'show server_version' but it's not settable by any GUC input source. Create 'lc_collate' and 'lc_ctype' never-settable parameters so that people can find out these settings without need for pg_controldata. (These side ideas were all discussed some time ago in pgsql-hackers, but not yet implemented.) --- doc/src/sgml/protocol.sgml | 17 ++- doc/src/sgml/ref/set.sgml | 30 ++-- doc/src/sgml/ref/show.sgml | 73 +++++++++- src/backend/access/transam/xlog.c | 9 +- src/backend/commands/variable.c | 18 +-- src/backend/utils/init/postinit.c | 11 +- src/backend/utils/misc/guc.c | 269 +++++++++++++++++++++++++---------- src/include/commands/variable.h | 5 +- src/include/libpq/pqcomm.h | 4 +- src/include/utils/guc.h | 97 +++++++------ src/interfaces/libpq/fe-connect.c | 289 ++------------------------------------ src/interfaces/libpq/fe-exec.c | 53 ++++++- src/interfaces/libpq/libpq-int.h | 15 +- 13 files changed, 412 insertions(+), 478 deletions(-) diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 7b5f9593a9..70c255f00c 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1,4 +1,4 @@ - + Frontend/Backend Protocol @@ -328,7 +328,7 @@ is being started, and the frontend is just an interested bystander. It is still possible for the startup attempt to fail (ErrorResponse), but in the normal case the backend will send - BackendKeyData, some ParameterStatus messages, and finally ReadyForQuery. + some ParameterStatus messages, BackendKeyData, and finally ReadyForQuery. @@ -900,9 +900,9 @@ At present there is a hard-wired set of parameters for which ParameterStatus will be generated: they are - version (backend version, - a pseudo-parameter that cannot change after startup); - database_encoding (also not presently changeable after start); + server_version (a pseudo-parameter that cannot change after + startup); + server_encoding (also not presently changeable after start); client_encoding, and DateStyle. This set might change in the future, or even become configurable. @@ -3882,6 +3882,13 @@ whether the COPY operation is text or binary. +The backend sends ParameterStatus ('S') messages during connection +startup for all parameters it considers interesting to the client library. +Subsequently, a ParameterStatus message is sent whenever the active value +changes for any of these parameters. + + + The CursorResponse ('P') message is no longer generated by the backend. diff --git a/doc/src/sgml/ref/set.sgml b/doc/src/sgml/ref/set.sgml index 23b77dff5c..75dc366c3f 100644 --- a/doc/src/sgml/ref/set.sgml +++ b/doc/src/sgml/ref/set.sgml @@ -1,5 +1,5 @@ @@ -52,7 +52,7 @@ SET [ SESSION | LOCAL ] TIME ZONE { timezonevariable - A settable run-time parameter. + Name of a settable run-time parameter. @@ -79,8 +79,9 @@ SET [ SESSION | LOCAL ] TIME ZONE { timezone The SET command changes run-time configuration - parameters. Many of the run-time parameters listed in the - can be changed on-the-fly with SET. + parameters. Many of the run-time parameters listed in + 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 @@ -123,7 +124,7 @@ SET [ SESSION | LOCAL ] TIME ZONE { timezone Choose the date/time representation style. Two separate - settings are involved: the default date/time output and the + settings are involved: the default date/time output format and the interpretation of ambiguous input. @@ -147,7 +148,7 @@ SET [ SESSION | LOCAL ] TIME ZONE { timezone Use Oracle/Ingres-style dates and times. Note that this style has nothing to do with SQL (which mandates ISO 8601 - style), the naming of this option is a historical accident. + style); the naming of this option is a historical accident. @@ -284,17 +285,6 @@ SELECT setseed(value); - SERVER_ENCODING - - - Shows the server-side multibyte encoding. (At present, this - parameter can be shown but not set, because the encoding is - determined at initdb time.) - - - - - TIME ZONE TIMEZONE @@ -410,7 +400,7 @@ SELECT setseed(value); - ERROR: 'name is not a + ERROR: 'name' is not a valid option name @@ -447,7 +437,7 @@ SELECT setseed(value); Notes - The function set_config provides the equivalent + The function set_config provides equivalent capability. See . @@ -517,6 +507,8 @@ SELECT CURRENT_TIMESTAMP AS today; See Also + , + , , , diff --git a/doc/src/sgml/ref/show.sgml b/doc/src/sgml/ref/show.sgml index 832c4392cb..0fec5fcce5 100644 --- a/doc/src/sgml/ref/show.sgml +++ b/doc/src/sgml/ref/show.sgml @@ -1,5 +1,5 @@ @@ -52,12 +52,13 @@ SHOW ALL Description - SHOW will display the current setting of a - run-time parameter. These variables can be set using the + SHOW will display the current setting of + run-time parameters. These variables can be set using the SET statement, by editing the - postgresql.conf, through the - PGOPTIONS environmental variable, or through a - command-line flag when starting the + postgresql.conf configuration file, through the + PGOPTIONS environmental variable (when using libpq + or a libpq-based application), or through + command-line flags when starting the postmaster. @@ -66,6 +67,64 @@ SHOW ALL does not start a new transaction block. See the autocommit section in for details. + + + Available parameters are documented in + and on the + reference page. + In addition, there are a few parameters that can be shown but not set: + + + + + SERVER_VERSION + + + Shows the server's version number. + + + + + + SERVER_ENCODING + + + Shows the server-side multibyte encoding. At present, this + parameter can be shown but not set, because the encoding is + determined at database creation time. + + + + + + LC_COLLATE + + + Shows the database's locale setting for collation (text ordering). + At present, this parameter can be shown but not set, because the + setting is determined at initdb time. + + + + + + LC_CTYPE + + + Shows the database's locale setting for character set considerations. + At present, this parameter can be shown but not set, because the + setting is determined at initdb time. + + + + + + + + + Use to set the value + of settable parameters. + @@ -79,7 +138,7 @@ SHOW ALL Message returned if name does - not stand for an existing parameter. + not stand for a known parameter. diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 4030a11a52..d1523dc89b 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, 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.113 2003/04/18 01:03:41 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.114 2003/04/25 19:45:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,6 +36,7 @@ #include "storage/sinval.h" #include "storage/spin.h" #include "utils/builtins.h" +#include "utils/guc.h" #include "utils/relcache.h" #include "miscadmin.h" @@ -2260,6 +2261,12 @@ ReadControlFile(void) "\twhich is not recognized by setlocale().\n" "\tIt looks like you need to initdb.", ControlFile->lc_ctype); + + /* Make the fixed locale settings visible as GUC variables, too */ + SetConfigOption("lc_collate", ControlFile->lc_collate, + PGC_INTERNAL, PGC_S_OVERRIDE); + SetConfigOption("lc_ctype", ControlFile->lc_ctype, + PGC_INTERNAL, PGC_S_OVERRIDE); } void diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 6ce1487e86..7b23cc80d0 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.73 2003/02/01 18:31:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/variable.c,v 1.74 2003/04/25 19:45:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -500,22 +500,6 @@ assign_client_encoding(const char *value, bool doit, bool interactive) } -const char * -assign_server_encoding(const char *value, bool doit, bool interactive) -{ - if (interactive) - elog(ERROR, "SET SERVER_ENCODING is not supported"); - /* Pretend never to fail in noninteractive case */ - return value; -} - -const char * -show_server_encoding(void) -{ - return GetDatabaseEncodingName(); -} - - /* * SET SESSION AUTHORIZATION * diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 36dd3c7b5c..4d76e69ac9 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.119 2003/02/19 14:31:26 ishii Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.120 2003/04/25 19:45:08 tgl Exp $ * * *------------------------------------------------------------------------- @@ -128,6 +128,9 @@ ReverifyMyDatabase(const char *name) * info out of the pg_database tuple. */ SetDatabaseEncoding(dbform->encoding); + /* Record it as a GUC internal option, too */ + SetConfigOption("server_encoding", GetDatabaseEncodingName(), + PGC_INTERNAL, PGC_S_OVERRIDE); /* If we have no other source of client_encoding, use server encoding */ SetConfigOption("client_encoding", GetDatabaseEncodingName(), PGC_BACKEND, PGC_S_DEFAULT); @@ -401,6 +404,12 @@ InitPostgres(const char *dbname, const char *username) InitializeClientEncoding(); /* + * Now all default states are fully set up. Report them to client + * if appropriate. + */ + BeginReportingGUCOptions(); + + /* * 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 callbacks that are registered by buffer manager, diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 89b6252779..f18c74c472 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -5,10 +5,13 @@ * 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.118 2003/03/28 20:17:13 tgl Exp $ * - * Copyright 2000 by PostgreSQL Global Development Group + * Copyright 2000-2003 by PostgreSQL Global Development Group * Written by Peter Eisentraut . + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.119 2003/04/25 19:45:09 tgl Exp $ + * *-------------------------------------------------------------------- */ @@ -32,6 +35,7 @@ #include "funcapi.h" #include "libpq/auth.h" #include "libpq/pqcomm.h" +#include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "miscadmin.h" #include "optimizer/cost.h" @@ -52,7 +56,12 @@ #include "pgstat.h" -/* XXX these should be in other modules' header files */ +#ifndef PG_KRB_SRVTAB +#define PG_KRB_SRVTAB "" +#endif + + +/* XXX these should appear in other modules' header files */ extern bool Log_connections; extern int PreAuthDelay; extern int AuthenticationTimeout; @@ -70,9 +79,18 @@ static const char *assign_facility(const char *facility, bool doit, bool interactive); #endif +static const char *assign_defaultxactisolevel(const char *newval, + bool doit, bool interactive); +static const char *assign_log_min_messages(const char *newval, + bool doit, bool interactive); +static const char *assign_client_min_messages(const char *newval, + bool doit, bool interactive); +static const char *assign_min_error_statement(const char *newval, bool doit, + bool interactive); static const char *assign_msglvl(int *var, const char *newval, bool doit, bool interactive); + /* * Debugging options */ @@ -85,6 +103,7 @@ bool Debug_print_plan = false; bool Debug_print_parse = false; bool Debug_print_rewritten = false; bool Debug_pretty_print = false; +bool Explain_pretty_print = true; bool log_parser_stats = false; bool log_planner_stats = false; @@ -93,8 +112,6 @@ bool log_statement_stats = false; /* this is sort of all * three above together */ bool log_btree_build_stats = false; -bool Explain_pretty_print = true; - bool SQL_inheritance = true; bool Australian_timezones = false; @@ -102,40 +119,31 @@ bool Australian_timezones = false; bool Password_encryption = true; int log_min_error_statement = PANIC; -char *log_min_error_statement_str = NULL; -const char log_min_error_statement_str_default[] = "panic"; - int log_min_messages = NOTICE; -char *log_min_messages_str = NULL; -const char log_min_messages_str_default[] = "notice"; - int client_min_messages = NOTICE; -char *client_min_messages_str = NULL; -const char client_min_messages_str_default[] = "notice"; -#ifndef PG_KRB_SRVTAB -#define PG_KRB_SRVTAB "" -#endif - /* * 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 char *log_min_error_statement_str; +static char *log_min_messages_str; +static char *client_min_messages_str; static double phony_random_seed; static char *client_encoding_string; static char *datestyle_string; static char *default_iso_level_string; +static char *locale_collate; +static char *locale_ctype; static char *regex_flavor_string; static char *server_encoding_string; +static char *server_version_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 @@ -171,6 +179,7 @@ struct config_generic #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 */ +#define GUC_REPORT 0x0010 /* auto-report changes to client */ /* bit values in status field */ #define GUC_HAVE_TENTATIVE 0x0001 /* tentative value is defined */ @@ -763,22 +772,24 @@ static struct config_string ConfigureNamesString[] = { { - {"client_encoding", PGC_USERSET}, &client_encoding_string, + {"client_encoding", PGC_USERSET, GUC_REPORT}, + &client_encoding_string, "SQL_ASCII", assign_client_encoding, NULL }, { {"client_min_messages", PGC_USERSET}, &client_min_messages_str, - client_min_messages_str_default, assign_client_min_messages, NULL + "notice", assign_client_min_messages, NULL }, { {"log_min_error_statement", PGC_USERSET}, &log_min_error_statement_str, - log_min_error_statement_str_default, assign_min_error_statement, NULL + "panic", assign_min_error_statement, NULL }, { - {"DateStyle", PGC_USERSET, GUC_LIST_INPUT}, &datestyle_string, + {"DateStyle", PGC_USERSET, GUC_LIST_INPUT | GUC_REPORT}, + &datestyle_string, "ISO, US", assign_datestyle, show_datestyle }, @@ -800,6 +811,16 @@ static struct config_string /* See main.c about why defaults for LC_foo are not all alike */ { + {"lc_collate", PGC_INTERNAL}, &locale_collate, + "C", NULL, NULL + }, + + { + {"lc_ctype", PGC_INTERNAL}, &locale_ctype, + "C", NULL, NULL + }, + + { {"lc_messages", PGC_SUSET}, &locale_messages, "", locale_messages_assign, NULL }, @@ -837,13 +858,20 @@ static struct config_string }, { - {"server_encoding", PGC_USERSET}, &server_encoding_string, - "SQL_ASCII", assign_server_encoding, show_server_encoding + {"server_encoding", PGC_INTERNAL, GUC_REPORT}, + &server_encoding_string, + "SQL_ASCII", NULL, NULL + }, + + { + {"server_version", PGC_INTERNAL, GUC_REPORT}, + &server_version_string, + PG_VERSION, NULL, NULL }, { {"log_min_messages", PGC_USERSET}, &log_min_messages_str, - log_min_messages_str_default, assign_log_min_messages, NULL + "notice", assign_log_min_messages, NULL }, { @@ -910,10 +938,13 @@ static int num_guc_variables; static bool guc_dirty; /* TRUE if need to do commit/abort work */ +static bool reporting_enabled; /* TRUE to enable GUC_REPORT */ + static char *guc_string_workspace; /* for avoiding memory leaks */ static int guc_var_compare(const void *a, const void *b); +static void ReportGUCOption(struct config_generic *record); static char *_ShowOption(struct config_generic * record); @@ -1182,6 +1213,8 @@ InitializeGUCOptions(void) guc_dirty = false; + reporting_enabled = false; + guc_string_workspace = NULL; /* @@ -1325,6 +1358,9 @@ ResetAllOptions(void) break; } } + + if (gconf->flags & GUC_REPORT) + ReportGUCOption(gconf); } } @@ -1351,11 +1387,14 @@ AtEOXact_GUC(bool isCommit) for (i = 0; i < num_guc_variables; i++) { struct config_generic *gconf = guc_variables[i]; + bool changed; /* Skip if nothing's happened to this var in this transaction */ if (gconf->status == 0) continue; + changed = false; + switch (gconf->vartype) { case PGC_BOOL: @@ -1375,6 +1414,7 @@ AtEOXact_GUC(bool isCommit) true, false)) elog(LOG, "Failed to commit %s", conf->gen.name); *conf->variable = conf->session_val; + changed = true; } conf->gen.source = conf->gen.session_source; conf->gen.status = 0; @@ -1397,6 +1437,7 @@ AtEOXact_GUC(bool isCommit) true, false)) elog(LOG, "Failed to commit %s", conf->gen.name); *conf->variable = conf->session_val; + changed = true; } conf->gen.source = conf->gen.session_source; conf->gen.status = 0; @@ -1419,6 +1460,7 @@ AtEOXact_GUC(bool isCommit) true, false)) elog(LOG, "Failed to commit %s", conf->gen.name); *conf->variable = conf->session_val; + changed = true; } conf->gen.source = conf->gen.session_source; conf->gen.status = 0; @@ -1460,12 +1502,16 @@ AtEOXact_GUC(bool isCommit) } SET_STRING_VARIABLE(conf, str); + changed = true; } conf->gen.source = conf->gen.session_source; conf->gen.status = 0; break; } } + + if (changed && (gconf->flags & GUC_REPORT)) + ReportGUCOption(gconf); } guc_dirty = false; @@ -1473,6 +1519,56 @@ AtEOXact_GUC(bool isCommit) /* + * Start up automatic reporting of changes to variables marked GUC_REPORT. + * This is executed at completion of backend startup. + */ +void +BeginReportingGUCOptions(void) +{ + int i; + + /* + * Don't do anything unless talking to an interactive frontend of + * protocol 3.0 or later. + */ + if (whereToSendOutput != Remote || + PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) + return; + + reporting_enabled = true; + + /* Transmit initial values of interesting variables */ + for (i = 0; i < num_guc_variables; i++) + { + struct config_generic *conf = guc_variables[i]; + + if (conf->flags & GUC_REPORT) + ReportGUCOption(conf); + } +} + +/* + * ReportGUCOption: if appropriate, transmit option value to frontend + */ +static void +ReportGUCOption(struct config_generic *record) +{ + if (reporting_enabled && (record->flags & GUC_REPORT)) + { + char *val = _ShowOption(record); + StringInfoData msgbuf; + + pq_beginmessage(&msgbuf, 'S'); + pq_sendstring(&msgbuf, record->name); + pq_sendstring(&msgbuf, val); + pq_endmessage(&msgbuf); + + pfree(val); + } +} + + +/* * Try to interpret value as boolean value. Valid values are: true, * false, yes, no, on, off, 1, 0. If the string parses okay, return * true, else false. If result is not NULL, return the parsing result @@ -1638,6 +1734,16 @@ set_config_option(const char *name, const char *value, */ switch (record->context) { + case PGC_INTERNAL: + if (context == PGC_SIGHUP) + return true; + if (context != PGC_INTERNAL) + { + elog(elevel, "'%s' cannot be changed", + name); + return false; + } + break; case PGC_POSTMASTER: if (context == PGC_SIGHUP) return true; @@ -2054,6 +2160,9 @@ set_config_option(const char *name, const char *value, } } + if (DoIt && (record->flags & GUC_REPORT)) + ReportGUCOption(record); + return true; } @@ -2614,8 +2723,10 @@ show_all_settings(PG_FUNCTION_ARGS) SRF_RETURN_NEXT(funcctx, result); } else -/* do when there is no more left */ + { + /* do when there is no more left */ SRF_RETURN_DONE(funcctx); + } } static char * @@ -2736,52 +2847,6 @@ ParseLongOption(const char *string, char **name, char **value) } - -#ifdef HAVE_SYSLOG - -static const char * -assign_facility(const char *facility, bool doit, bool interactive) -{ - if (strcasecmp(facility, "LOCAL0") == 0) - return facility; - if (strcasecmp(facility, "LOCAL1") == 0) - return facility; - if (strcasecmp(facility, "LOCAL2") == 0) - return facility; - if (strcasecmp(facility, "LOCAL3") == 0) - return facility; - if (strcasecmp(facility, "LOCAL4") == 0) - return facility; - if (strcasecmp(facility, "LOCAL5") == 0) - return facility; - if (strcasecmp(facility, "LOCAL6") == 0) - return facility; - if (strcasecmp(facility, "LOCAL7") == 0) - return facility; - return NULL; -} -#endif - - -static const char * -assign_defaultxactisolevel(const char *newval, bool doit, bool interactive) -{ - if (strcasecmp(newval, "serializable") == 0) - { - if (doit) - DefaultXactIsoLevel = XACT_SERIALIZABLE; - } - else if (strcasecmp(newval, "read committed") == 0) - { - if (doit) - DefaultXactIsoLevel = XACT_READ_COMMITTED; - } - else - return NULL; - return newval; -} - - /* * Handle options fetched from pg_database.datconfig or pg_shadow.useconfig. * The array parameter must be an array of TEXT (it must not be NULL). @@ -2993,21 +3058,70 @@ GUCArrayDelete(ArrayType *array, const char *name) return newarray; } -const char * + +/* + * assign_hook subroutines + */ + +#ifdef HAVE_SYSLOG + +static const char * +assign_facility(const char *facility, bool doit, bool interactive) +{ + if (strcasecmp(facility, "LOCAL0") == 0) + return facility; + if (strcasecmp(facility, "LOCAL1") == 0) + return facility; + if (strcasecmp(facility, "LOCAL2") == 0) + return facility; + if (strcasecmp(facility, "LOCAL3") == 0) + return facility; + if (strcasecmp(facility, "LOCAL4") == 0) + return facility; + if (strcasecmp(facility, "LOCAL5") == 0) + return facility; + if (strcasecmp(facility, "LOCAL6") == 0) + return facility; + if (strcasecmp(facility, "LOCAL7") == 0) + return facility; + return NULL; +} +#endif + + +static const char * +assign_defaultxactisolevel(const char *newval, bool doit, bool interactive) +{ + if (strcasecmp(newval, "serializable") == 0) + { + if (doit) + DefaultXactIsoLevel = XACT_SERIALIZABLE; + } + else if (strcasecmp(newval, "read committed") == 0) + { + if (doit) + DefaultXactIsoLevel = XACT_READ_COMMITTED; + } + else + return NULL; + return newval; +} + +static const char * assign_log_min_messages(const char *newval, bool doit, bool interactive) { return (assign_msglvl(&log_min_messages, newval, doit, interactive)); } -const char * +static const char * assign_client_min_messages(const char *newval, bool doit, bool interactive) { return (assign_msglvl(&client_min_messages, newval, doit, interactive)); } -const char * +static const char * assign_min_error_statement(const char *newval, bool doit, bool interactive) { return (assign_msglvl(&log_min_error_statement, newval, doit, interactive)); @@ -3087,4 +3201,5 @@ assign_msglvl(int *var, const char *newval, bool doit, bool interactive) return newval; /* OK */ } + #include "guc-file.c" diff --git a/src/include/commands/variable.h b/src/include/commands/variable.h index 73687178fe..68a0ebf745 100644 --- a/src/include/commands/variable.h +++ b/src/include/commands/variable.h @@ -2,7 +2,7 @@ * variable.h * Routines for handling specialized SET variables. * - * $Id: variable.h,v 1.19 2002/09/04 20:31:42 momjian Exp $ + * $Id: variable.h,v 1.20 2003/04/25 19:45:09 tgl Exp $ * */ #ifndef VARIABLE_H @@ -22,9 +22,6 @@ extern bool assign_random_seed(double value, 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); diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h index 4f458c51c2..6a871c7c6f 100644 --- a/src/include/libpq/pqcomm.h +++ b/src/include/libpq/pqcomm.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pqcomm.h,v 1.79 2003/04/24 21:16:44 tgl Exp $ + * $Id: pqcomm.h,v 1.80 2003/04/25 19:45:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -106,7 +106,7 @@ typedef union SockAddr /* The earliest and latest frontend/backend protocol version supported. */ #define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0) -#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,103) /* XXX temporary value */ +#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,104) /* XXX temporary value */ typedef uint32 ProtocolVersion; /* FE/BE protocol version number */ diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index f39f0a1854..47a5eeab18 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -1,10 +1,14 @@ -/* +/*-------------------------------------------------------------------- * guc.h * * External declarations pertaining to backend/utils/misc/guc.c and * backend/utils/misc/guc-file.l * - * $Id: guc.h,v 1.26 2002/11/15 00:47:22 momjian Exp $ + * Copyright 2000-2003 by PostgreSQL Global Development Group + * Written by Peter Eisentraut . + * + * $Id: guc.h,v 1.27 2003/04/25 19:45:09 tgl Exp $ + *-------------------------------------------------------------------- */ #ifndef GUC_H #define GUC_H @@ -17,32 +21,37 @@ * Certain options can only be set at certain times. The rules are * like this: * + * INTERNAL options cannot be set by the user at all, but only through + * internal processes ("server_version" is an example). These are GUC + * variables only so they can be shown by SHOW, etc. + * * POSTMASTER options can only be set when the postmaster starts, * either from the configuration file or the command line. * * SIGHUP options can only be set at postmaster startup or by changing * the configuration file and sending the HUP signal to the postmaster * or a backend process. (Notice that the signal receipt will not be - * evaluated immediately. The postmaster and the backend block at a + * evaluated immediately. The postmaster and the backend check it at a * certain point in their main loop. It's safer to wait than to read a * file asynchronously.) * * BACKEND options can only be set at postmaster startup, from the - * configuration file, or with the PGOPTIONS variable from the client - * when the connection is initiated. Furthermore, an already-started - * backend will ignore changes to such an option in the configuration - * file. The idea is that these options are fixed for a given backend - * once it's started, but they can vary across backends. + * configuration file, or by client request in the connection startup + * packet (e.g., from libpq's PGOPTIONS variable). Furthermore, an + * already-started backend will ignore changes to such an option in the + * configuration file. The idea is that these options are fixed for a + * given backend once it's started, but they can vary across backends. * * SUSET options can be set at postmaster startup, with the SIGHUP * mechanism, or from SQL if you're a superuser. These options cannot - * be set using the PGOPTIONS mechanism, because there is not check as - * to who does this. + * be set in the connection startup packet, because when it is processed + * we don't yet know if the user is a superuser. * * USERSET options can be set by anyone any time. */ typedef enum { + PGC_INTERNAL, PGC_POSTMASTER, PGC_SIGHUP, PGC_BACKEND, @@ -57,7 +66,8 @@ typedef enum * 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. + * as the current value. Note that source == PGC_S_OVERRIDE should be + * used when setting a PGC_INTERNAL option. */ typedef enum { @@ -67,11 +77,35 @@ typedef enum 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_CLIENT = 6, /* from client connection request */ PGC_S_OVERRIDE = 7, /* special case to forcibly set default */ PGC_S_SESSION = 8 /* SET command */ } GucSource; + +/* GUC vars that are actually declared in guc.c, rather than elsewhere */ +extern bool log_statement; +extern bool log_duration; +extern bool Debug_print_plan; +extern bool Debug_print_parse; +extern bool Debug_print_rewritten; +extern bool Debug_pretty_print; +extern bool Explain_pretty_print; + +extern bool log_parser_stats; +extern bool log_planner_stats; +extern bool log_executor_stats; +extern bool log_statement_stats; +extern bool log_btree_build_stats; + +extern bool SQL_inheritance; +extern bool Australian_timezones; + +extern int log_min_error_statement; +extern int log_min_messages; +extern int client_min_messages; + + extern void SetConfigOption(const char *name, const char *value, GucContext context, GucSource source); extern const char *GetConfigOption(const char *name); @@ -80,6 +114,7 @@ extern void ProcessConfigFile(GucContext context); extern void InitializeGUCOptions(void); extern void ResetAllOptions(void); extern void AtEOXact_GUC(bool isCommit); +extern void BeginReportingGUCOptions(void); extern void ParseLongOption(const char *string, char **name, char **value); extern bool set_config_option(const char *name, const char *value, GucContext context, GucSource source, @@ -100,42 +135,4 @@ 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); -extern const char *assign_min_error_statement(const char *newval, bool doit, - bool interactive); - -extern const char *assign_log_min_messages(const char *newval, - bool doit, bool interactive); -extern const char *assign_client_min_messages(const char *newval, - bool doit, bool interactive); -extern bool log_statement; -extern bool log_duration; -extern bool Debug_print_plan; -extern bool Debug_print_parse; -extern bool Debug_print_rewritten; -extern bool Debug_pretty_print; - -extern bool log_parser_stats; -extern bool log_planner_stats; -extern bool log_executor_stats; -extern bool log_statement_stats; -extern bool log_btree_build_stats; - -extern bool Explain_pretty_print; - -extern bool SQL_inheritance; -extern bool Australian_timezones; - -extern int log_min_error_statement; -extern char *log_min_error_statement_str; -extern const char log_min_error_statement_str_default[]; - -extern int log_min_messages; -extern char *log_min_messages_str; -extern const char log_min_messages_str_default[]; - -extern int client_min_messages; -extern char *client_min_messages_str; - -extern const char client_min_messages_str_default[]; - #endif /* GUC_H */ diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index a31f34d7a9..e2df4de859 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.236 2003/04/25 01:24:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.237 2003/04/25 19:45:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -174,8 +174,6 @@ static const struct EnvironmentOptions static int connectDBStart(PGconn *conn); static int connectDBComplete(PGconn *conn); -static bool PQsetenvStart(PGconn *conn); -static PostgresPollingStatusType PQsetenvPoll(PGconn *conn); static PGconn *makeEmptyPGconn(void); static void freePGconn(PGconn *conn); static void closePGconn(PGconn *conn); @@ -1207,10 +1205,6 @@ PQconnectPoll(PGconn *conn) case CONNECTION_MADE: break; - case CONNECTION_SETENV: - /* We allow PQsetenvPoll to decide whether to proceed */ - break; - default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( @@ -1517,10 +1511,10 @@ keep_going: /* We will come back to here until there * message indicates that startup is successful, but we * might also get an Error message indicating failure. * (Notice messages indicating nonfatal warnings are also - * allowed by the protocol, as is a BackendKeyData - * message.) Easiest way to handle this is to let - * PQgetResult() read the messages. We just have to fake - * it out about the state of the connection, by setting + * allowed by the protocol, as are ParameterStatus and + * BackendKeyData messages.) Easiest way to handle this is + * to let PQgetResult() read the messages. We just have to + * fake it out about the state of the connection, by setting * asyncStatus = PGASYNC_BUSY (done above). */ @@ -1554,44 +1548,11 @@ keep_going: /* We will come back to here until there } /* - * Post-connection housekeeping. Prepare to send - * environment variables to server. + * We are open for business! */ - if (!PQsetenvStart(conn)) - goto error_return; - - conn->status = CONNECTION_SETENV; - - goto keep_going; - } - - case CONNECTION_SETENV: - - /* - * We pretend that the connection is OK for the duration of - * these queries. - */ - conn->status = CONNECTION_OK; - - switch (PQsetenvPoll(conn)) - { - case PGRES_POLLING_OK: /* Success */ - conn->status = CONNECTION_OK; - return PGRES_POLLING_OK; - - case PGRES_POLLING_READING: /* Still going */ - conn->status = CONNECTION_SETENV; - return PGRES_POLLING_READING; - - case PGRES_POLLING_WRITING: /* Still going */ - conn->status = CONNECTION_SETENV; - return PGRES_POLLING_WRITING; - - default: - conn->status = CONNECTION_SETENV; - goto error_return; + conn->status = CONNECTION_OK; + return PGRES_POLLING_OK; } - /* Unreachable */ default: printfPQExpBuffer(&conn->errorMessage, @@ -1619,239 +1580,6 @@ error_return: /* - * PQsetenvStart - * - * Starts the process of passing the values of a standard set of environment - * variables to the backend. - */ -static bool -PQsetenvStart(PGconn *conn) -{ - if (conn == NULL || - conn->status == CONNECTION_BAD || - conn->setenv_state != SETENV_STATE_IDLE) - return false; - - conn->setenv_state = SETENV_STATE_ENCODINGS_SEND; - - return true; -} - -/* - * PQsetenvPoll - * - * Polls the process of passing the values of a standard set of environment - * variables to the backend. - */ -static PostgresPollingStatusType -PQsetenvPoll(PGconn *conn) -{ - PGresult *res; - - if (conn == NULL || conn->status == CONNECTION_BAD) - return PGRES_POLLING_FAILED; - - /* Check whether there are any data for us */ - switch (conn->setenv_state) - { - /* These are reading states */ - case SETENV_STATE_ENCODINGS_WAIT: - { - /* Load waiting data */ - int n = pqReadData(conn); - - if (n < 0) - goto error_return; - if (n == 0) - return PGRES_POLLING_READING; - - break; - } - - /* These are writing states, so we just proceed. */ - case SETENV_STATE_ENCODINGS_SEND: - break; - - /* Should we raise an error if called when not active? */ - case SETENV_STATE_IDLE: - return PGRES_POLLING_OK; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "invalid setenv state %c, " - "probably indicative of memory corruption\n" - ), - conn->setenv_state); - goto error_return; - } - - /* We will loop here until there is nothing left to do in this call. */ - for (;;) - { - switch (conn->setenv_state) - { - case SETENV_STATE_ENCODINGS_SEND: - { - const char *env = getenv("PGCLIENTENCODING"); - - if (!env || *env == '\0') - { - /* - * PGCLIENTENCODING is not specified, so query - * server for it. We must use begin/commit in - * case autocommit is off by default. - */ - if (!PQsendQuery(conn, "begin; select pg_client_encoding(); commit")) - goto error_return; - - conn->setenv_state = SETENV_STATE_ENCODINGS_WAIT; - return PGRES_POLLING_READING; - } - else - { - /* otherwise set client encoding in pg_conn struct */ - int encoding = pg_char_to_encoding(env); - - if (encoding < 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid encoding name in PGCLIENTENCODING: %s\n"), - env); - goto error_return; - } - conn->client_encoding = encoding; - - /* Move on to setting the environment options */ - conn->setenv_state = SETENV_STATE_IDLE; - } - break; - } - - case SETENV_STATE_ENCODINGS_WAIT: - { - if (PQisBusy(conn)) - return PGRES_POLLING_READING; - - res = PQgetResult(conn); - - if (res) - { - if (PQresultStatus(res) == PGRES_TUPLES_OK) - { - /* set client encoding in pg_conn struct */ - char *encoding; - - encoding = PQgetvalue(res, 0, 0); - if (!encoding) /* this should not happen */ - conn->client_encoding = PG_SQL_ASCII; - else - conn->client_encoding = pg_char_to_encoding(encoding); - } - else if (PQresultStatus(res) != PGRES_COMMAND_OK) - { - PQclear(res); - goto error_return; - } - PQclear(res); - /* Keep reading until PQgetResult returns NULL */ - } - else - { - /* - * NULL result indicates that the query is - * finished - */ - conn->setenv_state = SETENV_STATE_IDLE; - } - break; - } - - case SETENV_STATE_IDLE: - return PGRES_POLLING_OK; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid state %c, " - "probably indicative of memory corruption\n"), - conn->setenv_state); - goto error_return; - } - } - - /* Unreachable */ - -error_return: - conn->setenv_state = SETENV_STATE_IDLE; - return PGRES_POLLING_FAILED; -} - - -#ifdef NOT_USED - -/* - * PQsetenv - * - * Passes the values of a standard set of environment variables to the - * backend. - * - * Returns true on success, false on failure. - * - * This function used to be exported for no particularly good reason. - * Since it's no longer used by libpq itself, let's try #ifdef'ing it out - * and see if anyone complains. - */ -static bool -PQsetenv(PGconn *conn) -{ - PostgresPollingStatusType flag = PGRES_POLLING_WRITING; - - if (!PQsetenvStart(conn)) - return false; - - for (;;) - { - /* - * Wait, if necessary. Note that the initial state (just after - * PQsetenvStart) is to wait for the socket to select for writing. - */ - switch (flag) - { - case PGRES_POLLING_OK: - return true; /* success! */ - - case PGRES_POLLING_READING: - if (pqWait(1, 0, conn)) - { - conn->status = CONNECTION_BAD; - return false; - } - break; - - case PGRES_POLLING_WRITING: - if (pqWait(0, 1, conn)) - { - conn->status = CONNECTION_BAD; - return false; - } - break; - - default: - /* Just in case we failed to set it in PQsetenvPoll */ - conn->status = CONNECTION_BAD; - return false; - } - - /* - * Now try to advance the state machine. - */ - flag = PQsetenvPoll(conn); - } -} -#endif /* NOT_USED */ - - -/* * makeEmptyPGconn * - create a PGconn data structure with (as yet) no interesting data */ @@ -1869,7 +1597,6 @@ makeEmptyPGconn(void) conn->noticeHook = defaultNoticeProcessor; conn->status = CONNECTION_BAD; conn->asyncStatus = PGASYNC_IDLE; - conn->setenv_state = SETENV_STATE_IDLE; conn->notifyList = DLNewList(); conn->sock = -1; #ifdef USE_SSL diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 8452098781..53c4e28862 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.132 2003/04/25 01:24:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.133 2003/04/25 19:45:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,8 @@ #include "libpq-fe.h" #include "libpq-int.h" +#include "mb/pg_wchar.h" + #ifdef WIN32 #include "win32.h" #else @@ -54,6 +56,7 @@ static void handleSendFailure(PGconn *conn); static void handleSyncLoss(PGconn *conn, char id, int msgLength); static int getRowDescriptions(PGconn *conn); static int getAnotherTuple(PGconn *conn, int binary); +static int getParameterStatus(PGconn *conn); static int getNotify(PGconn *conn); /* --------------- @@ -950,6 +953,11 @@ parseInput(PGconn *conn) * * However, if the state is IDLE then we got trouble; we need to deal * with the unexpected message somehow. + * + * ParameterStatus ('S') messages are a special case: in IDLE state + * we must process 'em (this case could happen if a new value was + * adopted from config file due to SIGHUP), but otherwise we hold + * off until BUSY state. */ if (id == 'A') { @@ -970,6 +978,7 @@ parseInput(PGconn *conn) /* * Unexpected message in IDLE state; need to recover somehow. * ERROR messages are displayed using the notice processor; + * ParameterStatus is handled normally; * anything else is just dropped on the floor after displaying * a suitable warning notice. (An ERROR is very possibly the * backend telling us why it is about to close the connection, @@ -980,6 +989,11 @@ parseInput(PGconn *conn) if (pqGetErrorNotice(conn, false /* treat as notice */)) return; } + else if (id == 'S') + { + if (getParameterStatus(conn)) + return; + } else { snprintf(noticeWorkspace, sizeof(noticeWorkspace), @@ -1021,6 +1035,10 @@ parseInput(PGconn *conn) PGRES_EMPTY_QUERY); conn->asyncStatus = PGASYNC_READY; break; + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + return; + break; case 'K': /* secret key data from the backend */ /* @@ -1672,6 +1690,35 @@ fail: } /* + * Attempt to read a ParameterStatus message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'S' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +static int +getParameterStatus(PGconn *conn) +{ + /* Get the parameter name */ + if (pqGets(&conn->workBuffer, conn)) + return EOF; + /* Is it one we care about? */ + if (strcmp(conn->workBuffer.data, "client_encoding") == 0) + { + if (pqGets(&conn->workBuffer, conn)) + return EOF; + conn->client_encoding = pg_char_to_encoding(conn->workBuffer.data); + } + else + { + /* Uninteresting parameter, ignore it */ + if (pqGets(&conn->workBuffer, conn)) + return EOF; + } + return 0; +} + +/* * Attempt to read a Notify response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'A' message type and length have already been consumed. @@ -2249,6 +2296,10 @@ PQfn(PGconn *conn, if (conn->result) return prepareAsyncResult(conn); return PQmakeEmptyPGresult(conn, status); + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + continue; + break; default: /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 4688fbf8f5..12670f3a0a 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.64 2003/04/24 21:16:44 tgl Exp $ + * $Id: libpq-int.h,v 1.65 2003/04/25 19:45:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast * pqcomm.h describe what the backend knows, not what libpq knows. */ -#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,103) /* XXX temporary value */ +#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,104) /* XXX temporary value */ /* * POSTGRES backend dependent Constants. @@ -194,14 +194,6 @@ typedef enum PGASYNC_COPY_OUT /* Copy Out data transfer in progress */ } PGAsyncStatusType; -/* PGSetenvStatusType defines the state of the PQSetenv state machine */ -typedef enum -{ - SETENV_STATE_ENCODINGS_SEND, /* About to send an "encodings" query */ - SETENV_STATE_ENCODINGS_WAIT, /* Waiting for query to complete */ - SETENV_STATE_IDLE -} PGSetenvStatusType; - /* large-object-access data ... allocated only if large-object code is used. */ typedef struct pgLobjfuncs { @@ -293,9 +285,6 @@ struct pg_conn PGresult *result; /* result being constructed */ PGresAttValue *curTuple; /* tuple currently being read */ - /* Status for sending environment info. Used during PQSetenv only. */ - PGSetenvStatusType setenv_state; - #ifdef USE_SSL bool allow_ssl_try; /* Allowed to try SSL negotiation */ bool require_ssl; /* Require SSL to make connection */ -- 2.11.0