>From 5c0b6418971799e3d0b76a6ce55bff91a73fa371 Mon Sep 17 00:00:00 2001 From: David Christensen Date: Fri, 20 Mar 2015 12:03:47 -0500 Subject: [PATCH] Add two-arg form of current_setting() to optionally suppress errors The current_setting() function by default throws an error if the GUC is unset/unknown. Add a second argument "missing_ok" which will return NULL when an unset GUC is encountered and the parameter is true. Since GUCs cannot be set to NULL, this can be used to check for the existance of a particular GUC without throwing an error by testing whether the two-arg form returns NULL or not. This can also be combined with COALESCE() to provide support for a fallback/default value instead of throwing an error when an unknown GUC is encountered. This would come in most useful when using custom GUCs; e.g.: -- errors out if the 'foo.bar' setting is unset SELECT current_setting('foo.bar'); -- returns current setting of foo.bar, or NULL if not set SELECT current_setting('foo.bar', 'true') -- returns current setting of foo.bar, or 'default' if not set SELECT COALESCE(current_setting('foo.bar', 'true'), 'default') This saves you having to wrap the use of the function in an exception block just to catch and handle a default setting within a function. --- doc/src/sgml/func.sgml | 6 ++++-- src/backend/utils/misc/guc.c | 43 ++++++++++++++++++++++++++++++++++++--- src/include/catalog/pg_proc.h | 2 ++ src/include/utils/builtins.h | 1 + src/include/utils/guc.h | 4 +++- src/test/regress/expected/guc.out | 36 ++++++++++++++++++++++++++++++++ src/test/regress/sql/guc.sql | 22 ++++++++++++++++++++ 7 files changed, 108 insertions(+), 6 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index aa19e10..d36a0f1 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -16215,7 +16215,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 @@ -16254,7 +16254,9 @@ SELECT collation for ('foo' COLLATE "de_DE"); The function current_setting yields the current value of the setting setting_name. It corresponds to the SQL command - SHOW. An example: + SHOW. If setting does not exist, throws an error + unless missing_ok is true. An + example: SELECT current_setting('datestyle'); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 26275bd..5245ee8 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -7696,20 +7696,27 @@ ShowAllGUCConfig(DestReceiver *dest) end_tup_output(tstate); } + /* - * 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. */ char * -GetConfigOptionByName(const char *name, const char **varname) +GetConfigOptionByNameMissingOK(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) + 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), @@ -8008,6 +8015,36 @@ show_config_by_name(PG_FUNCTION_ARGS) } /* + * 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; + bool missing_ok; + char *varval; + + /* Get the GUC variable name */ + varname = TextDatumGetCString(PG_GETARG_DATUM(0)); + + /* Get the missing_ok value */ + missing_ok = PG_GETARG_BOOL(1); + + /* Get the value */ + varval = GetConfigOptionByNameMissingOK(varname, NULL, missing_ok); + + /* return NULL if this was NULL */ + if (!varval) + PG_RETURN_NULL(); + + /* Convert to text */ + PG_RETURN_TEXT_P(cstring_to_text(varval)); +} + + +/* * show_all_settings - equiv to SHOW ALL command but implemented as * a Table Function. */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 3c218a3..d1092a0 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3040,6 +3040,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_ show_config_by_name _null_ _null_ _null_ )); DESCR("SHOW X as a function"); +DATA(insert OID = 3280 ( 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_ show_config_by_name_missing_ok _null_ _null_ _null_ )); +DESCR("SHOW X as a function; if second param is true then don't thrown an error if unset"); 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_ 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}" "{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}" _null_ show_all_settings _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 6310641..4d672ae 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1095,6 +1095,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_fallback(PG_FUNCTION_ARGS); extern Datum set_config_by_name(PG_FUNCTION_ARGS); extern Datum show_all_settings(PG_FUNCTION_ARGS); diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index d3100d1..3f294f1 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -351,9 +351,11 @@ 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 *GetConfigOptionByNameMissingOK(const char *name, const char **varname, bool missing_ok); extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow); extern int GetNumConfigOptions(void); +#define GetConfigOptionByName(name,var) GetConfigOptionByNameMissingOK(name,var,false); extern void SetPGVariable(const char *name, List *args, bool is_local); extern void GetPGVariable(const char *name, DestReceiver *dest); diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out index 4f0065c..e20f6a9 100644 --- a/src/test/regress/expected/guc.out +++ b/src/test/regress/expected/guc.out @@ -736,3 +736,39 @@ NOTICE: text search configuration "no_such_config" does not exist select func_with_bad_set(); ERROR: invalid value for parameter "default_text_search_config": "no_such_config" reset check_function_bodies; +-- check multi-arg custom current_setting +-- this should error: +select current_setting('nosuch.setting'); +ERROR: unrecognized configuration parameter "nosuch.setting" +-- this should also error +select current_setting('nosuch.setting',false); +ERROR: unrecognized configuration parameter "nosuch.setting" +-- this should return NULL +select current_setting('nosuch.setting',true) is null; + ?column? +---------- + t +(1 row) + +-- this should now return 'nada' +set nosuch.setting = 'nada'; +select current_setting('nosuch.setting'); + current_setting +----------------- + nada +(1 row) + +-- as should this +select current_setting('nosuch.setting',false); + current_setting +----------------- + nada +(1 row) + +-- as should this +select current_setting('nosuch.setting',true); + current_setting +----------------- + nada +(1 row) + diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql index 3de8a6b..a1561f2 100644 --- a/src/test/regress/sql/guc.sql +++ b/src/test/regress/sql/guc.sql @@ -275,3 +275,25 @@ set default_text_search_config = no_such_config; select func_with_bad_set(); reset check_function_bodies; + +-- check multi-arg custom current_setting + +-- this should error: +select current_setting('nosuch.setting'); + +-- this should also error +select current_setting('nosuch.setting',false); + +-- this should return NULL +select current_setting('nosuch.setting',true) is null; + +-- this should now return 'nada' +set nosuch.setting = 'nada'; + +select current_setting('nosuch.setting'); + +-- as should this +select current_setting('nosuch.setting',false); + +-- as should this +select current_setting('nosuch.setting',true); -- 2.3.3