From c32416ebb748f9cc6ad4c0d3383d8593f151fa60 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Tue, 31 Aug 2004 19:28:51 +0000 Subject: [PATCH] Code review for various recent GUC hacking. Don't elog(ERROR) when not supposed to (fixes problem with postmaster aborting due to mistaken postgresql.conf change); don't call superuser() when not inside a transaction (fixes coredump when, eg, try to set log_statement from PGOPTIONS); some message style guidelines enforcement. --- src/backend/commands/variable.c | 15 ++- src/backend/utils/misc/guc.c | 219 ++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 97 deletions(-) diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index f262754a56e..e0ccd668e59 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.102 2004/08/30 02:54:38 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.103 2004/08/31 19:28:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -475,16 +475,23 @@ show_timezone(void) const char * assign_XactIsoLevel(const char *value, bool doit, GucSource source) { - if (doit && source >= PGC_S_INTERACTIVE) + if (SerializableSnapshot != NULL) { - if (SerializableSnapshot != NULL) + if (source >= PGC_S_INTERACTIVE) ereport(ERROR, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query"))); - if (IsSubTransaction()) + else + return NULL; + } + if (IsSubTransaction()) + { + if (source >= PGC_S_INTERACTIVE) ereport(ERROR, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction"))); + else + return NULL; } if (strcmp(value, "serializable") == 0) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index c5919c47fe1..b3b6ce0470d 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.236 2004/08/31 04:53:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.237 2004/08/31 19:28:51 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -1848,6 +1848,10 @@ static int guc_name_compare(const char *namea, const char *nameb); static void push_old_value(struct config_generic * gconf); static void ReportGUCOption(struct config_generic * record); static char *_ShowOption(struct config_generic * record); +static bool check_userlimit_privilege(struct config_generic *record, + GucSource source, int elevel); +static bool check_userlimit_override(struct config_generic *record, + GucSource source); /* @@ -2034,7 +2038,7 @@ static bool is_custom_class(const char *name, int dotPos) { /* - * The assign_custom_variable_classes has made sure no empty + * assign_custom_variable_classes() has made sure no empty * identifiers or whitespace exists in the variable */ bool result = false; @@ -3233,22 +3237,14 @@ set_config_option(const char *name, const char *value, if (newval < conf->reset_val) { /* Limit non-superuser changes */ - if (source > PGC_S_UNPRIVILEGED && !superuser()) - { - ereport(elevel, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set parameter \"%s\"", - name), - errhint("must be superuser to change this value to false"))); + if (!check_userlimit_privilege(record, source, + elevel)) return false; - } } if (newval > *conf->variable) { /* Allow change if admin should override */ - if (source < PGC_S_UNPRIVILEGED && - record->source > PGC_S_UNPRIVILEGED && - !superuser()) + if (check_userlimit_override(record, source)) changeVal = changeValOrig; } } @@ -3338,30 +3334,25 @@ set_config_option(const char *name, const char *value, } if (record->context == PGC_USERLIMIT) { - /* handle log_min_duration_statement, -1=disable */ - if ((newval != -1 && conf->reset_val != -1 && - newval > conf->reset_val) || /* increase duration */ - (newval == -1 && conf->reset_val != -1)) /* turn off */ + /* + * handle log_min_duration_statement: if it's enabled + * then either turning it off or increasing it + * requires privileges. + */ + if (conf->reset_val != -1 && + (newval == -1 || newval > conf->reset_val)) { /* Limit non-superuser changes */ - if (source > PGC_S_UNPRIVILEGED && !superuser()) - { - ereport(elevel, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set parameter \"%s\"", - name), - errhint("Must be superuser to increase this value or turn it off."))); + if (!check_userlimit_privilege(record, source, + elevel)) return false; - } } - /* Allow change if admin should override */ - if ((newval != -1 && *conf->variable != -1 && - newval < *conf->variable) || /* decrease duration */ - (newval != -1 && *conf->variable == -1)) /* turn on */ + /* Admin override includes turning on or decreasing */ + if (newval != -1 && + (*conf->variable == -1 || newval < *conf->variable)) { - if (source < PGC_S_UNPRIVILEGED && - record->source > PGC_S_UNPRIVILEGED && - !superuser()) + /* Allow change if admin should override */ + if (check_userlimit_override(record, source)) changeVal = changeValOrig; } } @@ -3450,23 +3441,21 @@ set_config_option(const char *name, const char *value, return false; } if (record->context == PGC_USERLIMIT) - /* No REAL PGC_USERLIMIT */ { - /* Limit non-superuser changes */ - if (source > PGC_S_UNPRIVILEGED && !superuser()) + /* No REAL PGC_USERLIMIT at present */ + if (newval < conf->reset_val) { - ereport(elevel, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set parameter \"%s\"", - name), - errhint("Must be superuser to increase this value or turn it off."))); - return false; + /* Limit non-superuser changes */ + if (!check_userlimit_privilege(record, source, + elevel)) + return false; + } + if (newval > *conf->variable) + { + /* Allow change if admin should override */ + if (check_userlimit_override(record, source)) + changeVal = changeValOrig; } - /* Allow change if admin should override */ - if (source < PGC_S_UNPRIVILEGED && - record->source > PGC_S_UNPRIVILEGED && - !superuser()) - changeVal = false; } } else @@ -3559,27 +3548,17 @@ set_config_option(const char *name, const char *value, (*var_hook) (&var_value, *conf->variable, true, source); - /* Limit non-superuser changes */ if (new_value > reset_value) { /* Limit non-superuser changes */ - if (source > PGC_S_UNPRIVILEGED && !superuser()) - { - ereport(elevel, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to set parameter \"%s\"", - name), - errhint("Must be superuser to increase this value."))); + if (!check_userlimit_privilege(record, source, + elevel)) return false; - } } - - /* Allow change if admin should override */ if (new_value < var_value) { - if (source < PGC_S_UNPRIVILEGED && - record->source > PGC_S_UNPRIVILEGED && - !superuser()) + /* Allow change if admin should override */ + if (check_userlimit_override(record, source)) changeVal = changeValOrig; } } @@ -3702,6 +3681,71 @@ set_config_option(const char *name, const char *value, return true; } +/* + * Check whether we should allow a USERLIMIT parameter to be set + * + * This is invoked only when the desired new setting is "less" than the + * old and so appropriate privileges are needed. If the setting should + * be disallowed, either throw an error (in interactive case) or return false. + */ +static bool +check_userlimit_privilege(struct config_generic *record, GucSource source, + int elevel) +{ + /* Allow if trusted source (e.g., config file) */ + if (source < PGC_S_UNPRIVILEGED) + return true; + /* + * Allow if superuser. We can only check this inside a transaction, + * though, so assume not-superuser otherwise. (In practice this means + * that settings coming from PGOPTIONS will be treated as non-superuser) + */ + if (IsTransactionState() && superuser()) + return true; + + ereport(elevel, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to set parameter \"%s\"", + record->name), + (record->vartype == PGC_BOOL) ? + errhint("Must be superuser to change this value to false.") + : ((record->vartype == PGC_INT) ? + errhint("Must be superuser to increase this value or turn it off.") + : errhint("Must be superuser to increase this value.")))); + return false; +} + +/* + * Check whether we should allow a USERLIMIT parameter to be overridden + * + * This is invoked when the desired new setting is "greater" than the + * old; if the old setting was unprivileged and the new one is privileged, + * we should apply it, even though the normal rule would be not to. + */ +static bool +check_userlimit_override(struct config_generic *record, GucSource source) +{ + /* Unprivileged source never gets to override this way */ + if (source > PGC_S_UNPRIVILEGED) + return false; + /* If existing setting is from privileged source, keep it */ + if (record->source < PGC_S_UNPRIVILEGED) + return false; + /* + * If user is a superuser, he gets to keep his setting. We can't check + * this unless inside a transaction, though. XXX in practice that + * restriction means this code is essentially worthless, because the + * result will depend on whether we happen to be inside a transaction + * block when SIGHUP arrives. Dike out until we can think of something + * that actually works. + */ +#ifdef NOT_USED + if (IsTransactionState() && superuser()) + return false; +#endif + /* Otherwise override */ + return true; +} /* @@ -5450,22 +5494,19 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) * Syntax error due to token following space after token or * non alpha numeric character */ - pfree(buf.data); - ereport(WARNING, + ereport(LOG, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("illegal syntax for custom_variable_classes \"%s\"", newval))); + errmsg("invalid syntax for custom_variable_classes: \"%s\"", newval))); + pfree(buf.data); return NULL; } symLen++; appendStringInfoChar(&buf, (char) c); } + /* Remove stray ',' at end */ if (symLen == 0 && buf.len > 0) - - /* - * Remove stray ',' at end - */ - buf.data[--buf.len] = 0; + buf.data[--buf.len] = '\0'; if (buf.len == 0) newval = NULL; @@ -5479,39 +5520,32 @@ assign_custom_variable_classes(const char *newval, bool doit, GucSource source) static bool assign_stage_log_stats(bool newval, bool doit, GucSource source) { - if (newval) + if (newval && log_statement_stats) { - if (log_statement_stats) - { - if (doit) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot enable parameter when \"log_statement_stats\" is true."))); - else - return false; - } - return true; + if (source >= PGC_S_INTERACTIVE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot enable parameter when \"log_statement_stats\" is true"))); + else + return false; } return true; } - static bool assign_log_stats(bool newval, bool doit, GucSource source) { - if (newval) + if (newval && + (log_parser_stats || log_planner_stats || log_executor_stats)) { - if (log_parser_stats || log_planner_stats || log_executor_stats) - { - if (doit) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot enable \"log_statement_stats\" when \"log_parser_stats\",\n" - "\"log_planner_stats\", or \"log_executor_stats\" is true."))); - else - return false; - } - return true; + if (source >= PGC_S_INTERACTIVE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot enable \"log_statement_stats\" when " + "\"log_parser_stats\", \"log_planner_stats\", " + "or \"log_executor_stats\" is true"))); + else + return false; } return true; } @@ -5534,7 +5568,6 @@ assign_transaction_read_only(bool newval, bool doit, GucSource source) static const char * assign_canonical_path(const char *newval, bool doit, GucSource source) { - if (doit) { /* We have to create a new pointer to force the change */ -- GitLab