On Fri, May 24, 2024 at 01:15:13PM -0400, Robert Haas wrote: > + /* Note that filename was already canonicalized */ > > I see that this comment is copied from load_libraries(), but I don't > immediately see where the canonicalization actually happens. Do you > know, or can you find out? Because that's crucial here, else stat() > might not target the real filename. I wonder if it will anyway. Like, > couldn't the library be versioned, and might not dlopen() try a few > possibilities?
This comment made me realize that we've been fixated on the warning. But the patch was broken, and would've always warned. I think almost all of the previous patch versions had this issue - oops. I added a call to expand_dynamic_library_name(), which seems to answer your question. And added a preparatory patch to distinguish ALTER USER/DATABASE SET from SET in a function, to avoid warning in that case. -- Justin
>From 29610d4e417a1a0cf099e10736b5c14aec90f641 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Sun, 21 Jul 2024 09:49:17 -0500 Subject: [PATCH 1/4] change function SET to use a separate GUC source This allows distinguishing it from ALTER ROLE SET and ALTER DATABASE SET, as needed for the following patch. See also: 2abae34a2e8fde42be731b4e18d44cd08901464d - added function SET 0c66a223774dec62edb5281a47e72fe480a8f7aa - (partially) updated comments about PGC_S_TEST --- src/backend/access/table/tableamapi.c | 2 +- src/backend/catalog/pg_db_role_setting.c | 8 ++++---- src/backend/commands/functioncmds.c | 4 ++-- src/backend/commands/tablespace.c | 4 ++-- src/backend/commands/variable.c | 8 ++++---- src/backend/storage/buffer/localbuf.c | 2 +- src/backend/utils/cache/ts_cache.c | 2 +- src/backend/utils/misc/guc.c | 18 +++++++++--------- src/include/utils/guc.h | 7 ++++--- 9 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/access/table/tableamapi.c b/src/backend/access/table/tableamapi.c index e9b598256fb..7c0f08ed46d 100644 --- a/src/backend/access/table/tableamapi.c +++ b/src/backend/access/table/tableamapi.c @@ -132,7 +132,7 @@ check_default_table_access_method(char **newval, void **extra, GucSource source) * nonexistent table access method, only a NOTICE. See comments in * guc.h. */ - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) { ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), diff --git a/src/backend/catalog/pg_db_role_setting.c b/src/backend/catalog/pg_db_role_setting.c index 8c20f519fc0..abfcc5655b3 100644 --- a/src/backend/catalog/pg_db_role_setting.c +++ b/src/backend/catalog/pg_db_role_setting.c @@ -70,7 +70,7 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) RelationGetDescr(rel), &isnull); if (!isnull) - new = GUCArrayReset(DatumGetArrayTypeP(datum)); + new = GUCArrayReset(DatumGetArrayTypeP(datum), PGC_S_TEST); if (new) { @@ -115,9 +115,9 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) /* Update (valuestr is NULL in RESET cases) */ if (valuestr) - a = GUCArrayAdd(a, setstmt->name, valuestr); + a = GUCArrayAdd(a, setstmt->name, valuestr, PGC_S_TEST); else - a = GUCArrayDelete(a, setstmt->name); + a = GUCArrayDelete(a, setstmt->name, PGC_S_TEST); if (a) { @@ -141,7 +141,7 @@ AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt) memset(nulls, false, sizeof(nulls)); - a = GUCArrayAdd(NULL, setstmt->name, valuestr); + a = GUCArrayAdd(NULL, setstmt->name, valuestr, PGC_S_TEST); values[Anum_pg_db_role_setting_setdatabase - 1] = ObjectIdGetDatum(databaseid); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 6593fd7d811..30a58cf027c 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -657,9 +657,9 @@ update_proconfig_value(ArrayType *a, List *set_items) char *valuestr = ExtractSetVariableArgs(sstmt); if (valuestr) - a = GUCArrayAdd(a, sstmt->name, valuestr); + a = GUCArrayAdd(a, sstmt->name, valuestr, PGC_S_TEST_FUNCTION); else /* RESET */ - a = GUCArrayDelete(a, sstmt->name); + a = GUCArrayDelete(a, sstmt->name, PGC_S_TEST_FUNCTION); } } diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 113b4807315..b0a460c1622 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -1104,7 +1104,7 @@ check_default_tablespace(char **newval, void **extra, GucSource source) * When source == PGC_S_TEST, don't throw a hard error for a * nonexistent tablespace, only a NOTICE. See comments in guc.h. */ - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) { ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -1251,7 +1251,7 @@ check_temp_tablespaces(char **newval, void **extra, GucSource source) curoid = get_tablespace_oid(curname, source <= PGC_S_TEST); if (curoid == InvalidOid) { - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 9345131711e..c9421b17952 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -831,7 +831,7 @@ check_session_authorization(char **newval, void **extra, GucSource source) roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); if (!HeapTupleIsValid(roleTup)) { - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) { ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -856,7 +856,7 @@ check_session_authorization(char **newval, void **extra, GucSource source) if (roleid != GetAuthenticatedUserId() && !superuser_arg(GetAuthenticatedUserId())) { - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) { ereport(NOTICE, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -940,7 +940,7 @@ check_role(char **newval, void **extra, GucSource source) roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(*newval)); if (!HeapTupleIsValid(roleTup)) { - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) { ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -965,7 +965,7 @@ check_role(char **newval, void **extra, GucSource source) if (!InitializingParallelWorker && !member_can_set_role(GetSessionUserId(), roleid)) { - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) { ereport(NOTICE, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 8da7dd6c98a..0e92f604061 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -707,7 +707,7 @@ check_temp_buffers(int *newval, void **extra, GucSource source) * Once local buffers have been initialized, it's too late to change this. * However, if this is only a test call, allow it. */ - if (source != PGC_S_TEST && NLocBuffer && NLocBuffer != *newval) + if ((source != PGC_S_TEST && source != PGC_S_TEST_FUNCTION) && NLocBuffer && NLocBuffer != *newval) { GUC_check_errdetail("\"temp_buffers\" cannot be changed after any temporary tables have been accessed in the session."); return false; diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c index 54de33eadd2..9d7e8b66763 100644 --- a/src/backend/utils/cache/ts_cache.c +++ b/src/backend/utils/cache/ts_cache.c @@ -628,7 +628,7 @@ check_default_text_search_config(char **newval, void **extra, GucSource source) */ if (!OidIsValid(cfgId)) { - if (source == PGC_S_TEST) + if (source == PGC_S_TEST || source == PGC_S_TEST_FUNCTION) { ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a043d529efa..e2734475189 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -252,7 +252,7 @@ static void reapply_stacked_values(struct config_generic *variable, GucContext curscontext, GucSource cursource, Oid cursrole); static bool validate_option_array_item(const char *name, const char *value, - bool skipIfNoPermissions); + bool skipIfNoPermissions, int source); static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head); static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p, const char *name, const char *value); @@ -6444,7 +6444,7 @@ ProcessGUCArray(ArrayType *array, * to indicate the current table entry is NULL. */ ArrayType * -GUCArrayAdd(ArrayType *array, const char *name, const char *value) +GUCArrayAdd(ArrayType *array, const char *name, const char *value, int source) { struct config_generic *record; Datum datum; @@ -6455,7 +6455,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value) Assert(value); /* test if the option is valid and we're allowed to set it */ - (void) validate_option_array_item(name, value, false); + (void) validate_option_array_item(name, value, false, source); /* normalize name (converts obsolete GUC names to modern spellings) */ record = find_option(name, false, true, WARNING); @@ -6522,7 +6522,7 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value) * is NULL then a null should be stored. */ ArrayType * -GUCArrayDelete(ArrayType *array, const char *name) +GUCArrayDelete(ArrayType *array, const char *name, int source) { struct config_generic *record; ArrayType *newarray; @@ -6532,7 +6532,7 @@ GUCArrayDelete(ArrayType *array, const char *name) Assert(name); /* test if the option is valid and we're allowed to set it */ - (void) validate_option_array_item(name, NULL, false); + (void) validate_option_array_item(name, NULL, false, source); /* normalize name (converts obsolete GUC names to modern spellings) */ record = find_option(name, false, true, WARNING); @@ -6592,7 +6592,7 @@ GUCArrayDelete(ArrayType *array, const char *name) * those that are PGC_USERSET or we have permission to set */ ArrayType * -GUCArrayReset(ArrayType *array) +GUCArrayReset(ArrayType *array, int source) { ArrayType *newarray; int i; @@ -6630,7 +6630,7 @@ GUCArrayReset(ArrayType *array) *eqsgn = '\0'; /* skip if we have permission to delete it */ - if (validate_option_array_item(val, NULL, true)) + if (validate_option_array_item(val, NULL, true, source)) continue; /* else add it to the output array */ @@ -6665,7 +6665,7 @@ GUCArrayReset(ArrayType *array) */ static bool validate_option_array_item(const char *name, const char *value, - bool skipIfNoPermissions) + bool skipIfNoPermissions, int source) { struct config_generic *gconf; @@ -6726,7 +6726,7 @@ validate_option_array_item(const char *name, const char *value, /* test for permissions and valid option value */ (void) set_config_option(name, value, superuser() ? PGC_SUSET : PGC_USERSET, - PGC_S_TEST, GUC_ACTION_SET, false, 0, false); + source, GUC_ACTION_SET, false, 0, false); return true; } diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index ff506bf48d9..84203109af7 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -119,6 +119,7 @@ typedef enum PGC_S_OVERRIDE, /* special case to forcibly set default */ PGC_S_INTERACTIVE, /* dividing line for error reporting */ PGC_S_TEST, /* test per-database or per-user setting */ + PGC_S_TEST_FUNCTION, /* function SET */ PGC_S_SESSION, /* SET command */ } GucSource; @@ -406,9 +407,9 @@ extern void TransformGUCArray(ArrayType *array, List **names, List **values); extern void ProcessGUCArray(ArrayType *array, GucContext context, GucSource source, GucAction action); -extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value); -extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name); -extern ArrayType *GUCArrayReset(ArrayType *array); +extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value, int source); +extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name, int source); +extern ArrayType *GUCArrayReset(ArrayType *array, int source); extern void *guc_malloc(int elevel, size_t size); extern pg_nodiscard void *guc_realloc(int elevel, void *old, size_t size); -- 2.42.0
>From 47eb2ab393e050ac98a6197a8302c83c23ca0df3 Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Mon, 13 Dec 2021 08:42:38 -0600 Subject: [PATCH 2/4] warn when setting GUC to a nonextant library Note that warnings shouldn't be issued during startup (only fatals): $ ./tmp_install/usr/local/pgsql/bin/postgres -D ./testrun/regress/regress/tmp_check/data -c shared_preload_libraries=ab 2023-07-06 14:47:03.817 CDT postmaster[2608106] FATAL: could not access file "ab": No existe el archivo o el directorio 2023-07-06 14:47:03.817 CDT postmaster[2608106] CONTEXT: while loading shared libraries for setting "shared_preload_libraries" --- src/backend/commands/variable.c | 101 ++++++++++++++++++ src/backend/utils/fmgr/dfmgr.c | 3 +- src/backend/utils/misc/guc.c | 6 ++ src/backend/utils/misc/guc_tables.c | 6 +- src/include/fmgr.h | 1 + src/include/utils/guc_hooks.h | 7 ++ .../unsafe_tests/expected/rolenames.out | 19 ++++ .../modules/unsafe_tests/sql/rolenames.sql | 13 +++ 8 files changed, 151 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index c9421b17952..0714b4421dc 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -40,6 +40,7 @@ #include "utils/timestamp.h" #include "utils/tzparser.h" #include "utils/varlena.h" +#include <unistd.h> /* * DATESTYLE @@ -1234,3 +1235,103 @@ check_ssl(bool *newval, void **extra, GucSource source) #endif return true; } + + +/* + * See also load_libraries() and internal_load_library(). + */ +static bool +check_preload_libraries(char **newval, void **extra, GucSource source, + bool restricted) +{ + char *rawstring; + List *elemlist; + ListCell *l; + + /* nothing to do if empty */ + if (newval == NULL || *newval[0] == '\0') + return true; + + /* + * Issue warnings only during an interactive SET from ALTER + * ROLE/DATABASE/SYSTEM, and not while applying GUCs during startup, + * when relevant errors would be FATAL, and the warnings would be redundant. + * + * IsUnderPostmaster is a hack to allow warning under ALTER SYSTEM but + * not during startup. Both of which use PGC_S_FILE. + */ + if (!IsUnderPostmaster) + return true; + + /* Need a modifiable copy of string */ + rawstring = pstrdup(*newval); + + /* Parse string into list of filename paths */ + if (!SplitDirectoriesString(rawstring, ',', &elemlist)) + { + /* Should not happen ? */ + return false; + } + + foreach(l, elemlist) + { + /* Note that filename was already canonicalized */ + char *filename = (char *) lfirst(l); + char *expanded = NULL; + + /* If restricting, insert $libdir/plugins if not mentioned already */ + if (restricted && first_dir_separator(filename) == NULL) + { + expanded = psprintf("$libdir/plugins/%s", filename); + filename = expanded; + } + + filename = expand_dynamic_library_name(filename); + + /* + * stat()/access() only check that the library exists, not that it has + * the correct magic number or even a library. But error messages + * from dlopen() are not portable, so it'd be hard to report any + * problem other than "does not exist". + */ + if (access(filename, R_OK) == 0) + continue; + + if (source == PGC_S_FILE) + /* ALTER SYSTEM */ + ereport(WARNING, + errcode_for_file_access(), + errmsg("could not access file \"%s\"", filename), + errdetail("The server might fail to start with this setting."), + errhint("If the server is shut down, it will be necessary to manually edit the %s file to allow it to start again.", + "postgresql.auto.conf")); + else if (source == PGC_S_TEST) + /* ALTER ROLE/DATABASE */ + ereport(WARNING, + errcode_for_file_access(), + errmsg("could not access file \"%s\"", filename), + errdetail("New sessions will fail to connect with the new setting.")); + } + + list_free_deep(elemlist); + pfree(rawstring); + return true; +} + +bool +check_shared_preload_libraries(char **newval, void **extra, GucSource source) +{ + return check_preload_libraries(newval, extra, source, false); +} + +bool +check_local_preload_libraries(char **newval, void **extra, GucSource source) +{ + return check_preload_libraries(newval, extra, source, true); +} + +bool +check_session_preload_libraries(char **newval, void **extra, GucSource source) +{ + return check_preload_libraries(newval, extra, source, false); +} diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c index 092004dcf3b..b03017f6275 100644 --- a/src/backend/utils/fmgr/dfmgr.c +++ b/src/backend/utils/fmgr/dfmgr.c @@ -79,7 +79,6 @@ char *Dynamic_library_path; static void *internal_load_library(const char *libname); static void incompatible_module_error(const char *libname, const Pg_magic_struct *module_magic_data) pg_attribute_noreturn(); -static char *expand_dynamic_library_name(const char *name); static void check_restricted_library_name(const char *name); static char *substitute_libpath_macro(const char *name); static char *find_in_dynamic_libpath(const char *basename); @@ -410,7 +409,7 @@ incompatible_module_error(const char *libname, * * The result will always be freshly palloc'd. */ -static char * +char * expand_dynamic_library_name(const char *name) { bool have_slash; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index e2734475189..4cd40ca9678 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -4654,6 +4654,12 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt) union config_var_val newval; void *newextra = NULL; + /* + * PGC_S_FILE is used even though we should use PGC_S_TEST, + * since various GUC validation functions use TEST to give a + * NOTICE rather than an ERROR, and we don't want to allow + * setting values that would always return an error later. + */ if (!parse_and_validate_value(record, name, value, PGC_S_FILE, ERROR, &newval, &newextra)) diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 630ed0f1629..6c424be4c47 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -4237,7 +4237,7 @@ struct config_string ConfigureNamesString[] = }, &session_preload_libraries_string, "", - NULL, NULL, NULL + check_session_preload_libraries, NULL, NULL }, { @@ -4248,7 +4248,7 @@ struct config_string ConfigureNamesString[] = }, &shared_preload_libraries_string, "", - NULL, NULL, NULL + check_shared_preload_libraries, NULL, NULL }, { @@ -4259,7 +4259,7 @@ struct config_string ConfigureNamesString[] = }, &local_preload_libraries_string, "", - NULL, NULL, NULL + check_local_preload_libraries, NULL, NULL }, { diff --git a/src/include/fmgr.h b/src/include/fmgr.h index ccb4070a251..c6319782a99 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -749,6 +749,7 @@ extern void **find_rendezvous_variable(const char *varName); extern Size EstimateLibraryStateSpace(void); extern void SerializeLibraryState(Size maxsize, char *start_address); extern void RestoreLibraryState(char *start_address); +char *expand_dynamic_library_name(const char *name); /* * Support for aggregate functions diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h index 153c652c93e..a11c5488041 100644 --- a/src/include/utils/guc_hooks.h +++ b/src/include/utils/guc_hooks.h @@ -172,4 +172,11 @@ extern bool check_synchronized_standby_slots(char **newval, void **extra, GucSource source); extern void assign_synchronized_standby_slots(const char *newval, void *extra); +extern bool check_local_preload_libraries(char **newval, void **extra, + GucSource source); +extern bool check_session_preload_libraries(char **newval, void **extra, + GucSource source); +extern bool check_shared_preload_libraries(char **newval, void **extra, + GucSource source); + #endif /* GUC_HOOKS_H */ diff --git a/src/test/modules/unsafe_tests/expected/rolenames.out b/src/test/modules/unsafe_tests/expected/rolenames.out index 61396b2a805..de707a4803e 100644 --- a/src/test/modules/unsafe_tests/expected/rolenames.out +++ b/src/test/modules/unsafe_tests/expected/rolenames.out @@ -1083,6 +1083,25 @@ RESET SESSION AUTHORIZATION; ERROR: current transaction is aborted, commands ignored until end of transaction block ROLLBACK; REVOKE pg_read_all_settings FROM regress_role_haspriv; +-- test some warnings +ALTER ROLE regress_testrol0 SET session_preload_libraries="DoesNotExist"; +WARNING: could not access file "DoesNotExist" +DETAIL: New sessions will fail to connect with the new setting. +ALTER ROLE regress_testrol0 SET local_preload_libraries="DoesNotExist"; +WARNING: could not access file "$libdir/plugins/DoesNotExist" +DETAIL: New sessions will fail to connect with the new setting. +CREATE DATABASE regression_nosuch_db; +ALTER DATABASE regression_nosuch_db SET session_preload_libraries="DoesNotExist"; +WARNING: could not access file "DoesNotExist" +DETAIL: New sessions will fail to connect with the new setting. +ALTER DATABASE regression_nosuch_db SET local_preload_libraries="DoesNotExist"; +WARNING: could not access file "$libdir/plugins/DoesNotExist" +DETAIL: New sessions will fail to connect with the new setting. +DROP DATABASE regression_nosuch_db; +-- SET doesn't do anything, but should not warn, either +SET session_preload_libraries="DoesNotExist"; +SET SESSION session_preload_libraries="DoesNotExist"; +begin; SET LOCAL session_preload_libraries="DoesNotExist"; rollback; -- clean up \c DROP SCHEMA test_roles_schema; diff --git a/src/test/modules/unsafe_tests/sql/rolenames.sql b/src/test/modules/unsafe_tests/sql/rolenames.sql index adac36536db..56fb52d4e08 100644 --- a/src/test/modules/unsafe_tests/sql/rolenames.sql +++ b/src/test/modules/unsafe_tests/sql/rolenames.sql @@ -494,6 +494,19 @@ RESET SESSION AUTHORIZATION; ROLLBACK; REVOKE pg_read_all_settings FROM regress_role_haspriv; +-- test some warnings +ALTER ROLE regress_testrol0 SET session_preload_libraries="DoesNotExist"; +ALTER ROLE regress_testrol0 SET local_preload_libraries="DoesNotExist"; +CREATE DATABASE regression_nosuch_db; +ALTER DATABASE regression_nosuch_db SET session_preload_libraries="DoesNotExist"; +ALTER DATABASE regression_nosuch_db SET local_preload_libraries="DoesNotExist"; +DROP DATABASE regression_nosuch_db; + +-- SET doesn't do anything, but should not warn, either +SET session_preload_libraries="DoesNotExist"; +SET SESSION session_preload_libraries="DoesNotExist"; +begin; SET LOCAL session_preload_libraries="DoesNotExist"; rollback; + -- clean up \c -- 2.42.0
>From 71d670eec45d0c7f9bfa6684e112b8bf5d6552fe Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Sat, 18 Dec 2021 22:51:01 -0600 Subject: [PATCH 3/4] errcontext if server fails to start due to library GUCs --- src/backend/utils/fmgr/dfmgr.c | 21 ++++++++++++++++----- src/backend/utils/init/miscinit.c | 2 +- src/include/fmgr.h | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c index b03017f6275..fd8371cb582 100644 --- a/src/backend/utils/fmgr/dfmgr.c +++ b/src/backend/utils/fmgr/dfmgr.c @@ -76,7 +76,7 @@ static DynamicFileList *file_tail = NULL; char *Dynamic_library_path; -static void *internal_load_library(const char *libname); +static void *internal_load_library(const char *libname, const char *gucname); static void incompatible_module_error(const char *libname, const Pg_magic_struct *module_magic_data) pg_attribute_noreturn(); static void check_restricted_library_name(const char *name); @@ -112,7 +112,7 @@ load_external_function(const char *filename, const char *funcname, fullname = expand_dynamic_library_name(filename); /* Load the shared library, unless we already did */ - lib_handle = internal_load_library(fullname); + lib_handle = internal_load_library(fullname, NULL); /* Return handle if caller wants it */ if (filehandle) @@ -141,6 +141,15 @@ load_external_function(const char *filename, const char *funcname, */ void load_file(const char *filename, bool restricted) +{ + load_file_guc(filename, restricted, NULL); +} + +/* + * Also accepts a GUC arg, for error reports + */ +void +load_file_guc(const char *filename, bool restricted, const char *gucname) { char *fullname; @@ -152,7 +161,7 @@ load_file(const char *filename, bool restricted) fullname = expand_dynamic_library_name(filename); /* Load the shared library */ - (void) internal_load_library(fullname); + (void) internal_load_library(fullname, gucname); pfree(fullname); } @@ -171,6 +180,7 @@ lookup_external_function(void *filehandle, const char *funcname) /* * Load the specified dynamic-link library file, unless it already is * loaded. Return the pg_dl* handle for the file. + * gucname may be passed for error reports. * * Note: libname is expected to be an exact name for the library file. * @@ -180,7 +190,7 @@ lookup_external_function(void *filehandle, const char *funcname) * perhaps other things that are definitely unsafe currently. */ static void * -internal_load_library(const char *libname) +internal_load_library(const char *libname, const char *gucname) { DynamicFileList *file_scanner; PGModuleMagicFunction magic_func; @@ -205,6 +215,7 @@ internal_load_library(const char *libname) if (stat(libname, &stat_buf) == -1) ereport(ERROR, (errcode_for_file_access(), + gucname ? errcontext("while loading shared libraries for setting \"%s\"", gucname) : 0, errmsg("could not access file \"%s\": %m", libname))); @@ -676,7 +687,7 @@ RestoreLibraryState(char *start_address) { while (*start_address != '\0') { - internal_load_library(start_address); + internal_load_library(start_address, NULL); start_address += strlen(start_address) + 1; } } diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 537d92c0cfd..8f11f718135 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -1825,7 +1825,7 @@ load_libraries(const char *libraries, const char *gucname, bool restricted) expanded = psprintf("$libdir/plugins/%s", filename); filename = expanded; } - load_file(filename, restricted); + load_file_guc(filename, restricted, gucname); ereport(DEBUG1, (errmsg_internal("loaded library \"%s\"", filename))); if (expanded) diff --git a/src/include/fmgr.h b/src/include/fmgr.h index c6319782a99..22e70dc43ea 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -745,6 +745,7 @@ extern void *load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle); extern void *lookup_external_function(void *filehandle, const char *funcname); extern void load_file(const char *filename, bool restricted); +extern void load_file_guc(const char *filename, bool restricted, const char *gucname); extern void **find_rendezvous_variable(const char *varName); extern Size EstimateLibraryStateSpace(void); extern void SerializeLibraryState(Size maxsize, char *start_address); -- 2.42.0
>From b3aa9009491b2421f5c99dcd3911d1d17740d6ab Mon Sep 17 00:00:00 2001 From: Justin Pryzby <pryz...@telsasoft.com> Date: Wed, 2 Feb 2022 20:54:49 -0600 Subject: [PATCH 4/4] show the GUC source in errcontext //-os-only: freebsd --- src/backend/utils/fmgr/dfmgr.c | 14 +++++++++++--- src/backend/utils/misc/guc_funcs.c | 26 ++++++++++++++++++++++++++ src/include/utils/guc.h | 1 + 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c index fd8371cb582..7666919d78d 100644 --- a/src/backend/utils/fmgr/dfmgr.c +++ b/src/backend/utils/fmgr/dfmgr.c @@ -35,6 +35,7 @@ #include "miscadmin.h" #include "storage/fd.h" #include "storage/shmem.h" +#include "utils/guc_tables.h" #include "utils/hsearch.h" @@ -213,11 +214,18 @@ internal_load_library(const char *libname, const char *gucname) * Check for same files - different paths (ie, symlink or link) */ if (stat(libname, &stat_buf) == -1) + { + char *errstr = strerror(errno); + int linenum; + char *sourcefile = gucname ? GetConfigSourceFile(gucname, &linenum) : NULL; + ereport(ERROR, (errcode_for_file_access(), gucname ? errcontext("while loading shared libraries for setting \"%s\"", gucname) : 0, - errmsg("could not access file \"%s\": %m", - libname))); + sourcefile ? errcontext("from %s:%d", sourcefile, linenum) : 0, + errmsg("could not access file \"%s\": %s", + libname, errstr))); + } for (file_scanner = file_list; file_scanner != NULL && @@ -281,7 +289,7 @@ internal_load_library(const char *libname, const char *gucname) } else { - /* try to close library */ + /* try to close library */ // Not needed due to ERROR ? // dlclose(file_scanner->handle); free(file_scanner); /* complain */ diff --git a/src/backend/utils/misc/guc_funcs.c b/src/backend/utils/misc/guc_funcs.c index 9c9edd3d2f5..eb7c3d726b2 100644 --- a/src/backend/utils/misc/guc_funcs.c +++ b/src/backend/utils/misc/guc_funcs.c @@ -177,6 +177,32 @@ ExtractSetVariableArgs(VariableSetStmt *stmt) } } + +/* + * Get the source file and line associated with the given option, if it was set + * by a file. + * + * If the option doesn't exist, throw an ereport and don't return. + */ +char * +GetConfigSourceFile(const char *name, int *linenum) +{ + struct config_generic *record; + + record = find_option(name, false, false, ERROR); + + /* Should not happen */ + if (record == NULL) + return NULL; + + if (record->source != PGC_S_FILE) + return NULL; + + *linenum = record->sourceline; + return record->sourcefile; +} + + /* * flatten_set_variable_args * Given a parsenode List as emitted by the grammar for SET, diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 84203109af7..6aaff5768ba 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -365,6 +365,7 @@ extern const char *GetConfigOption(const char *name, bool missing_ok, bool restrict_privileged); extern const char *GetConfigOptionResetString(const char *name); extern int GetConfigOptionFlags(const char *name, bool missing_ok); +extern char *GetConfigSourceFile(const char *name, int *linenum); extern void ProcessConfigFile(GucContext context); extern char *convert_GUC_name_for_parameter_acl(const char *name); extern void check_GUC_name_for_parameter_acl(const char *name); -- 2.42.0