From 10fb48d66de76e7dc1e36ef18af502ed9600352f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 2 Jul 2015 16:40:55 -0400 Subject: [PATCH] Add an optional missing_ok argument to SQL function current_setting(). This allows convenient checking for existence of a GUC from SQL, which is particularly useful when dealing with custom variables. David Christensen, reviewed by Jeevan Chalke --- contrib/tsearch2/tsearch2.c | 2 +- doc/src/sgml/func.sgml | 7 +++- src/backend/utils/misc/guc.c | 53 ++++++++++++++++++++++++------- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 6 ++-- src/include/utils/builtins.h | 1 + src/include/utils/guc.h | 3 +- src/test/regress/expected/guc.out | 31 ++++++++++++++++++ src/test/regress/sql/guc.sql | 13 ++++++++ 9 files changed, 101 insertions(+), 17 deletions(-) diff --git a/contrib/tsearch2/tsearch2.c b/contrib/tsearch2/tsearch2.c index 143dabba40..4354c5b090 100644 --- a/contrib/tsearch2/tsearch2.c +++ b/contrib/tsearch2/tsearch2.c @@ -363,7 +363,7 @@ tsa_tsearch2(PG_FUNCTION_ARGS) tgargs[i + 1] = trigger->tgargs[i]; tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config", - NULL)); + NULL, false)); tgargs_old = trigger->tgargs; trigger->tgargs = tgargs; trigger->tgnargs++; diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 99923f46bc..76f77cb0fc 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -16444,7 +16444,7 @@ SELECT collation for ('foo' COLLATE "de_DE"); current_setting - current_setting(setting_name) + current_setting(setting_name [, missing_ok ]) text get current value of setting @@ -16492,6 +16492,11 @@ SELECT current_setting('datestyle'); ISO, MDY (1 row) + + If there is no setting named setting_name, + current_setting throws an error + unless missing_ok is supplied and is + true. diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 0356ecb482..595a609989 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -7131,7 +7131,7 @@ ExtractSetVariableArgs(VariableSetStmt *stmt) case VAR_SET_VALUE: return flatten_set_variable_args(stmt->name, stmt->args); case VAR_SET_CURRENT: - return GetConfigOptionByName(stmt->name, NULL); + return GetConfigOptionByName(stmt->name, NULL, false); default: return NULL; } @@ -7200,7 +7200,7 @@ set_config_by_name(PG_FUNCTION_ARGS) true, 0, false); /* get the new current value */ - new_value = GetConfigOptionByName(name, NULL); + new_value = GetConfigOptionByName(name, NULL, false); /* Convert return string to text */ PG_RETURN_TEXT_P(cstring_to_text(new_value)); @@ -7627,7 +7627,7 @@ GetPGVariableResultDesc(const char *name) const char *varname; /* Get the canonical spelling of name */ - (void) GetConfigOptionByName(name, &varname); + (void) GetConfigOptionByName(name, &varname, false); /* need a tuple descriptor representing a single TEXT column */ tupdesc = CreateTemplateTupleDesc(1, false); @@ -7650,7 +7650,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) char *value; /* Get the value and canonical spelling of name */ - value = GetConfigOptionByName(name, &varname); + value = GetConfigOptionByName(name, &varname, false); /* need a tuple descriptor representing a single TEXT column */ tupdesc = CreateTemplateTupleDesc(1, false); @@ -7734,19 +7734,30 @@ ShowAllGUCConfig(DestReceiver *dest) } /* - * Return GUC variable value by name; optionally return canonical - * form of name. Return value is palloc'd. + * Return GUC variable value by name; optionally return canonical form of + * name. If the GUC is unset, then throw an error unless missing_ok is true, + * in which case return NULL. Return value is palloc'd (but *varname isn't). */ char * -GetConfigOptionByName(const char *name, const char **varname) +GetConfigOptionByName(const char *name, const char **varname, bool missing_ok) { struct config_generic *record; record = find_option(name, false, ERROR); if (record == NULL) + { + if (missing_ok) + { + if (varname) + *varname = NULL; + return NULL; + } + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized configuration parameter \"%s\"", name))); + } + if ((record->flags & GUC_SUPERUSER_ONLY) && !superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -8033,14 +8044,34 @@ GetNumConfigOptions(void) Datum show_config_by_name(PG_FUNCTION_ARGS) { - char *varname; + char *varname = TextDatumGetCString(PG_GETARG_DATUM(0)); char *varval; - /* Get the GUC variable name */ - varname = TextDatumGetCString(PG_GETARG_DATUM(0)); + /* Get the value */ + varval = GetConfigOptionByName(varname, NULL, false); + + /* Convert to text */ + PG_RETURN_TEXT_P(cstring_to_text(varval)); +} + +/* + * show_config_by_name_missing_ok - equiv to SHOW X command but implemented as + * a function. If X does not exist, suppress the error and just return NULL + * if missing_ok is TRUE. + */ +Datum +show_config_by_name_missing_ok(PG_FUNCTION_ARGS) +{ + char *varname = TextDatumGetCString(PG_GETARG_DATUM(0)); + bool missing_ok = PG_GETARG_BOOL(1); + char *varval; /* Get the value */ - varval = GetConfigOptionByName(varname, NULL); + varval = GetConfigOptionByName(varname, NULL, missing_ok); + + /* return NULL if no such variable */ + if (varval == NULL) + PG_RETURN_NULL(); /* Convert to text */ PG_RETURN_TEXT_P(cstring_to_text(varval)); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 965a53cbfd..44ce2b3a62 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201506282 +#define CATALOG_VERSION_NO 201507021 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index be3a8fba1b..6fd1278d1b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3067,6 +3067,8 @@ DESCR("convert bitstring to int8"); DATA(insert OID = 2077 ( current_setting PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ show_config_by_name _null_ _null_ _null_ )); DESCR("SHOW X as a function"); +DATA(insert OID = 3294 ( current_setting PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 25 "25 16" _null_ _null_ _null_ _null_ _null_ show_config_by_name_missing_ok _null_ _null_ _null_ )); +DESCR("SHOW X as a function, optionally no error for missing variable"); DATA(insert OID = 2078 ( set_config PGNSP PGUID 12 1 0 0 0 f f f f f f v 3 0 25 "25 25 16" _null_ _null_ _null_ _null_ _null_ set_config_by_name _null_ _null_ _null_ )); DESCR("SET X as a function"); DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 1 1000 0 0 f f f f t t s 0 0 2249 "" "{25,25,25,25,25,25,25,25,25,25,25,1009,25,25,25,23,16}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{name,setting,unit,category,short_desc,extra_desc,context,vartype,source,min_val,max_val,enumvals,boot_val,reset_val,sourcefile,sourceline,pending_restart}" _null_ _null_ show_all_settings _null_ _null_ _null_ )); @@ -4866,8 +4868,8 @@ DESCR("GIN support"); DATA(insert OID = 3301 ( jsonb_concat PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 3802" _null_ _null_ _null_ _null_ _null_ jsonb_concat _null_ _null_ _null_ )); DATA(insert OID = 3302 ( jsonb_delete PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ _null_ _null_ _null_ jsonb_delete _null_ _null_ _null_ )); DATA(insert OID = 3303 ( jsonb_delete PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ _null_ _null_ _null_ jsonb_delete_idx _null_ _null_ _null_ )); -DATA(insert OID = 3304 ( jsonb_delete_path PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ _null_ _null_ _null_ jsonb_delete_path _null_ _null_ _null_ )); -DATA(insert OID = 3305 ( jsonb_set PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 3802 "3802 1009 3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_set _null_ _null_ _null_ )); +DATA(insert OID = 3304 ( jsonb_delete_path PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ _null_ _null_ _null_ jsonb_delete_path _null_ _null_ _null_ )); +DATA(insert OID = 3305 ( jsonb_set PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 3802 "3802 1009 3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_set _null_ _null_ _null_ )); DESCR("Set part of a jsonb"); DATA(insert OID = 3306 ( jsonb_pretty PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_pretty _null_ _null_ _null_ )); DESCR("Indented text from jsonb"); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 98556725c8..fcb0bf0ce8 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1114,6 +1114,7 @@ extern Datum quote_nullable(PG_FUNCTION_ARGS); /* guc.c */ extern Datum show_config_by_name(PG_FUNCTION_ARGS); +extern Datum show_config_by_name_missing_ok(PG_FUNCTION_ARGS); extern Datum set_config_by_name(PG_FUNCTION_ARGS); extern Datum show_all_settings(PG_FUNCTION_ARGS); extern Datum show_all_file_settings(PG_FUNCTION_ARGS); diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 49ec3840af..dc167f9bb5 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -365,7 +365,8 @@ extern int set_config_option(const char *name, const char *value, GucAction action, bool changeVal, int elevel, bool is_reload); extern void AlterSystemSetConfigFile(AlterSystemStmt *setstmt); -extern char *GetConfigOptionByName(const char *name, const char **varname); +extern char *GetConfigOptionByName(const char *name, const char **varname, + bool missing_ok); extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow); extern int GetNumConfigOptions(void); diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 4f0065cb7e..fdb9b5cdda 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -720,6 +720,37 @@ select myfunc(1), current_setting('work_mem'); 2MB | 2MB (1 row) +-- check current_setting()'s behavior with invalid setting name +select current_setting('nosuch.setting'); -- FAIL +ERROR: unrecognized configuration parameter "nosuch.setting" +select current_setting('nosuch.setting', false); -- FAIL +ERROR: unrecognized configuration parameter "nosuch.setting" +select current_setting('nosuch.setting', true) is null; + ?column? +---------- + t +(1 row) + +-- after this, all three cases should yield 'nada' +set nosuch.setting = 'nada'; +select current_setting('nosuch.setting'); + current_setting +----------------- + nada +(1 row) + +select current_setting('nosuch.setting', false); + current_setting +----------------- + nada +(1 row) + +select current_setting('nosuch.setting', true); + current_setting +----------------- + nada +(1 row) + -- Normally, CREATE FUNCTION should complain about invalid values in -- function SET options; but not if check_function_bodies is off, -- because that creates ordering hazards for pg_dump diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql index 3de8a6b55d..6a062a0a5b 100644 --- a/src/test/regress/sql/guc.sql +++ b/src/test/regress/sql/guc.sql @@ -258,6 +258,19 @@ select myfunc(0); select current_setting('work_mem'); select myfunc(1), current_setting('work_mem'); +-- check current_setting()'s behavior with invalid setting name + +select current_setting('nosuch.setting'); -- FAIL +select current_setting('nosuch.setting', false); -- FAIL +select current_setting('nosuch.setting', true) is null; + +-- after this, all three cases should yield 'nada' +set nosuch.setting = 'nada'; + +select current_setting('nosuch.setting'); +select current_setting('nosuch.setting', false); +select current_setting('nosuch.setting', true); + -- Normally, CREATE FUNCTION should complain about invalid values in -- function SET options; but not if check_function_bodies is off, -- because that creates ordering hazards for pg_dump