On Mon, Oct 02, 2023 at 10:53:22AM +0200, Drouvot, Bertrand wrote: > On 10/2/23 10:17 AM, Michael Paquier wrote: >> On Mon, Oct 02, 2023 at 10:01:04AM +0200, Drouvot, Bertrand wrote: >>> I think that would make sense to have more flexibility in the worker_spi >>> module. I think that could be done in a dedicated patch though. I >>> think it makes more sense to have the current patch "focusing" on >>> this new flag (while adding a test about it without too much >>> refactoring). What about doing the worker_spi module re-factoring >>> as a follow up of this one? >> >> I would do that first, as that's what I usually do, > > The reason I was thinking not doing that first is that there is no real use > case in the current worker_spi module test.
As a template, improving and extending it seems worth it to me as long as it can also improve tests. > > but I see also > > your point that this is not mandatory. If you want, I could give it a > > shot tomorrow to see where it leads. > > Oh yeah that would be great (and maybe you already see a use case in the > current test). Thanks! Yes, while it shows a bit more what one can achieve with bgw_extra, it also helps in improving the test coverage with starting dynamic workers across defined databases and roles through a SQL function. It took me a bit longer than I expected, but here is what I finish with: - 0001 extends worker_spi to be able to pass down database and role IDs for the workers to start, through MyBgworkerEntry->bgw_extra. Perhaps the two new arguments of worker_spi_launch() should use InvalidOid as default, actually, to fall back to the database and roles defined by the GUCs in these cases. That would be two lines to change in worker_spi--1.0.sql. - 0002 is your patch, on top of which I have added handling for the flags in the launch() function with a text[]. The tests get much simpler, and don't need restarts. By the way, I am pretty sure that we are going to need a wait phase after the startup of the worker in the case where "nologrole" is not allowed to log in even with the original patch: the worker may not have started at the point where we check the logs. That's true as well when involving worker_spi_launch(). What do you think? -- Michael
From b37dd684da3ff6f9c00719c27b89f1e39f6961a4 Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Wed, 4 Oct 2023 14:28:20 +0900 Subject: [PATCH v3 1/2] Redesign database and role handling in worker_spi worker_spi_launch gains two arguments for a database ID and a role ID. If these are InvalidOid, the dynamic worker falls back to the GUCs defined to launch the workers. Tests are refactored in consequence. bgw_extra is used to pass down these two OIDs to the main bgworker routine. Workers loaded with shared_preload_libraries just rely on the GUCs. --- .../modules/worker_spi/t/001_worker_spi.pl | 21 +++++--- .../modules/worker_spi/worker_spi--1.0.sql | 2 +- src/test/modules/worker_spi/worker_spi.c | 54 ++++++++++++++++++- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/test/modules/worker_spi/t/001_worker_spi.pl b/src/test/modules/worker_spi/t/001_worker_spi.pl index 2965acd789..a026042c65 100644 --- a/src/test/modules/worker_spi/t/001_worker_spi.pl +++ b/src/test/modules/worker_spi/t/001_worker_spi.pl @@ -20,9 +20,11 @@ $node->safe_psql('postgres', 'CREATE EXTENSION worker_spi;'); # This consists in making sure that a table name "counted" is created # on a new schema whose name includes the index defined in input argument # of worker_spi_launch(). -# By default, dynamic bgworkers connect to the "postgres" database. +# By default, dynamic bgworkers connect to the "postgres" database with +# an undefined role, falling back to the GUC defaults (InvalidOid for +# worker_spi_launch). my $result = - $node->safe_psql('postgres', 'SELECT worker_spi_launch(4) IS NOT NULL;'); + $node->safe_psql('postgres', 'SELECT worker_spi_launch(4, 0, 0) IS NOT NULL;'); is($result, 't', "dynamic bgworker launched"); $node->poll_query_until( 'postgres', @@ -58,6 +60,7 @@ note "testing bgworkers loaded with shared_preload_libraries"; # Create the database first so as the workers can connect to it when # the library is loaded. $node->safe_psql('postgres', q(CREATE DATABASE mydb;)); +$node->safe_psql('postgres', q(CREATE ROLE myrole SUPERUSER LOGIN;)); $node->safe_psql('mydb', 'CREATE EXTENSION worker_spi;'); # Now load the module as a shared library. @@ -80,16 +83,18 @@ ok( $node->poll_query_until( # Ask worker_spi to launch dynamic bgworkers with the library loaded, then # check their existence. Use IDs that do not overlap with the schemas created -# by the previous workers. -my $worker1_pid = $node->safe_psql('mydb', 'SELECT worker_spi_launch(10);'); -my $worker2_pid = $node->safe_psql('mydb', 'SELECT worker_spi_launch(11);'); +# by the previous workers. These use a specific role. +my $myrole_id = $node->safe_psql('mydb', "SELECT oid FROM pg_roles where rolname = 'myrole';"); +my $mydb_id = $node->safe_psql('mydb', "SELECT oid FROM pg_database where datname = 'mydb';"); +my $worker1_pid = $node->safe_psql('mydb', "SELECT worker_spi_launch(10, $mydb_id, $myrole_id);"); +my $worker2_pid = $node->safe_psql('mydb', "SELECT worker_spi_launch(11, $mydb_id, $myrole_id);"); ok( $node->poll_query_until( 'mydb', - qq[SELECT datname, count(datname), wait_event FROM pg_stat_activity + qq[SELECT datname, usename, count(datname), wait_event FROM pg_stat_activity WHERE backend_type = 'worker_spi dynamic' AND - pid IN ($worker1_pid, $worker2_pid) GROUP BY datname, wait_event;], - 'mydb|2|worker_spi_main'), + pid IN ($worker1_pid, $worker2_pid) GROUP BY datname, usename, wait_event;], + 'mydb|myrole|2|worker_spi_main'), 'dynamic bgworkers all launched' ) or die "Timed out while waiting for dynamic bgworkers to be launched"; diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql index e9d5b07373..f5b91128ff 100644 --- a/src/test/modules/worker_spi/worker_spi--1.0.sql +++ b/src/test/modules/worker_spi/worker_spi--1.0.sql @@ -3,7 +3,7 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION worker_spi" to load this file. \quit -CREATE FUNCTION worker_spi_launch(pg_catalog.int4) +CREATE FUNCTION worker_spi_launch(pg_catalog.int4, pg_catalog.oid, pg_catalog.oid) RETURNS pg_catalog.int4 STRICT AS 'MODULE_PATHNAME' LANGUAGE C; diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c index 98f8d4194b..f35897ddac 100644 --- a/src/test/modules/worker_spi/worker_spi.c +++ b/src/test/modules/worker_spi/worker_spi.c @@ -34,10 +34,12 @@ /* these headers are used by this particular worker's code */ #include "access/xact.h" +#include "commands/dbcommands.h" #include "executor/spi.h" #include "fmgr.h" #include "lib/stringinfo.h" #include "pgstat.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/snapmgr.h" #include "tcop/utility.h" @@ -52,6 +54,7 @@ PGDLLEXPORT void worker_spi_main(Datum main_arg) pg_attribute_noreturn(); static int worker_spi_naptime = 10; static int worker_spi_total_workers = 2; static char *worker_spi_database = NULL; +static char *worker_spi_role = NULL; /* value cached, fetched from shared memory */ static uint32 worker_spi_wait_event_main = 0; @@ -138,12 +141,21 @@ worker_spi_main(Datum main_arg) worktable *table; StringInfoData buf; char name[20]; + Oid dboid; + Oid roleoid; + char *p; table = palloc(sizeof(worktable)); sprintf(name, "schema%d", index); table->schema = pstrdup(name); table->name = pstrdup("counted"); + /* fetch database and role OIDs, these are set for a dynamic worker */ + p = MyBgworkerEntry->bgw_extra; + memcpy(&dboid, p, sizeof(Oid)); + p += sizeof(Oid); + memcpy(&roleoid, p, sizeof(Oid)); + /* Establish signal handlers before unblocking signals. */ pqsignal(SIGHUP, SignalHandlerForConfigReload); pqsignal(SIGTERM, die); @@ -152,7 +164,10 @@ worker_spi_main(Datum main_arg) BackgroundWorkerUnblockSignals(); /* Connect to our database */ - BackgroundWorkerInitializeConnection(worker_spi_database, NULL, 0); + if (OidIsValid(dboid)) + BackgroundWorkerInitializeConnectionByOid(dboid, roleoid, 0); + else + BackgroundWorkerInitializeConnection(worker_spi_database, worker_spi_role, 0); elog(LOG, "%s initialized with %s.%s", MyBgworkerEntry->bgw_name, table->schema, table->name); @@ -316,6 +331,15 @@ _PG_init(void) 0, NULL, NULL, NULL); + DefineCustomStringVariable("worker_spi.role", + "Role to connect with.", + NULL, + &worker_spi_role, + NULL, + PGC_SIGHUP, + 0, + NULL, NULL, NULL); + if (!process_shared_preload_libraries_in_progress) return; @@ -346,6 +370,9 @@ _PG_init(void) /* * Now fill in worker-specific data, and do the actual registrations. + * + * bgw_extra can optionally include a dabatase OID and a role OID. This + * is left empty here to fallback to the related GUCs at startup. */ for (int i = 1; i <= worker_spi_total_workers; i++) { @@ -364,10 +391,13 @@ Datum worker_spi_launch(PG_FUNCTION_ARGS) { int32 i = PG_GETARG_INT32(0); + Oid dboid = PG_GETARG_OID(1); + Oid roleoid = PG_GETARG_OID(2); BackgroundWorker worker; BackgroundWorkerHandle *handle; BgwHandleStatus status; pid_t pid; + char *p; memset(&worker, 0, sizeof(worker)); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | @@ -382,6 +412,28 @@ worker_spi_launch(PG_FUNCTION_ARGS) /* set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */ worker.bgw_notify_pid = MyProcPid; + /* + * Register database and role to use for the worker started in bgw_extra. + * If none have been provided, fallback to the GUCs. + */ + if (!OidIsValid(dboid)) + dboid = get_database_oid(worker_spi_database, false); + if (!OidIsValid(roleoid)) + { + /* + * worker_spi_role is NULL by default, so just pass down an invalid + * OID to let the main() routine do its connection work. + */ + if (worker_spi_role) + roleoid = get_role_oid(worker_spi_role, false); + else + roleoid = InvalidOid; + } + p = worker.bgw_extra; + memcpy(p, &dboid, sizeof(Oid)); + p += sizeof(Oid); + memcpy(p, &roleoid, sizeof(Oid)); + if (!RegisterDynamicBackgroundWorker(&worker, &handle)) PG_RETURN_NULL(); -- 2.42.0
From 2fb2ae74f24cd14f3f08f612f1869517b145c61c Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Wed, 4 Oct 2023 15:06:25 +0900 Subject: [PATCH v3 2/2] Allow background workers to bypass login check This adds a new flag value (BGWORKER_BYPASS_ROLELOGINCHECK) to the flags being used in BackgroundWorkerInitializeConnection() and BackgroundWorkerInitializeConnectionByOid(). This flag allows the background workers to bypass the login check done in InitializeSessionUserId(). --- src/include/miscadmin.h | 4 +- src/include/postmaster/bgworker.h | 10 ++-- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/postmaster/autovacuum.c | 5 +- src/backend/postmaster/postmaster.c | 6 ++- src/backend/tcop/postgres.c | 1 + src/backend/utils/init/miscinit.c | 4 +- src/backend/utils/init/postinit.c | 5 +- .../modules/worker_spi/t/001_worker_spi.pl | 38 ++++++++++++++ .../modules/worker_spi/worker_spi--1.0.sql | 3 +- src/test/modules/worker_spi/worker_spi.c | 49 +++++++++++++++++-- doc/src/sgml/bgworker.sgml | 3 +- 12 files changed, 109 insertions(+), 21 deletions(-) diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 14bd574fc2..f1d03f2df8 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -364,7 +364,8 @@ extern bool InSecurityRestrictedOperation(void); extern bool InNoForceRLSOperation(void); extern void GetUserIdAndContext(Oid *userid, bool *sec_def_context); extern void SetUserIdAndContext(Oid userid, bool sec_def_context); -extern void InitializeSessionUserId(const char *rolename, Oid roleid); +extern void InitializeSessionUserId(const char *rolename, Oid roleid, + bool bypass_login_check); extern void InitializeSessionUserIdStandalone(void); extern void SetSessionAuthorization(Oid userid, bool is_superuser); extern Oid GetCurrentRoleId(void); @@ -469,6 +470,7 @@ extern void InitPostgres(const char *in_dbname, Oid dboid, const char *username, Oid useroid, bool load_session_libraries, bool override_allow_connections, + bool bypass_login_check, char *out_dbname); extern void BaseInit(void); diff --git a/src/include/postmaster/bgworker.h b/src/include/postmaster/bgworker.h index 7815507e3d..0fdbfaf431 100644 --- a/src/include/postmaster/bgworker.h +++ b/src/include/postmaster/bgworker.h @@ -141,18 +141,20 @@ extern PGDLLIMPORT BackgroundWorker *MyBgworkerEntry; * If dbname is NULL, connection is made to no specific database; * only shared catalogs can be accessed. */ -extern void BackgroundWorkerInitializeConnection(const char *dbname, const char *username, uint32 flags); +extern void BackgroundWorkerInitializeConnection(const char *dbname, const char *username, bits32 flags); /* Just like the above, but specifying database and user by OID. */ -extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags); +extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, bits32 flags); /* * Flags to BackgroundWorkerInitializeConnection et al * * - * Allow bypassing datallowconn restrictions when connecting to database + * Allow bypassing datallowconn restrictions and login check when connecting + * to database */ -#define BGWORKER_BYPASS_ALLOWCONN 1 +#define BGWORKER_BYPASS_ALLOWCONN 0x0001 +#define BGWORKER_BYPASS_ROLELOGINCHECK 0x0002 /* Block/unblock signals in a background worker process */ diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 5810f8825e..bdd005b66a 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -345,7 +345,7 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) if (pg_link_canary_is_frontend()) elog(ERROR, "backend is incorrectly linked to frontend functions"); - InitPostgres(NULL, InvalidOid, NULL, InvalidOid, false, false, NULL); + InitPostgres(NULL, InvalidOid, NULL, InvalidOid, false, false, false, NULL); /* Initialize stuff for bootstrap-file processing */ for (i = 0; i < MAXATTR; i++) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index ae9be9b911..4686b81f68 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -488,7 +488,7 @@ AutoVacLauncherMain(int argc, char *argv[]) /* Early initialization */ BaseInit(); - InitPostgres(NULL, InvalidOid, NULL, InvalidOid, false, false, NULL); + InitPostgres(NULL, InvalidOid, NULL, InvalidOid, false, false, false, NULL); SetProcessingMode(NormalProcessing); @@ -1706,8 +1706,7 @@ AutoVacWorkerMain(int argc, char *argv[]) * Note: if we have selected a just-deleted database (due to using * stale stats info), we'll fail and exit here. */ - InitPostgres(NULL, dbid, NULL, InvalidOid, false, false, - dbname); + InitPostgres(NULL, dbid, NULL, InvalidOid, false, false, false, dbname); SetProcessingMode(NormalProcessing); set_ps_display(dbname); ereport(DEBUG1, diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 54e9bfb8c4..37f5b94469 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -5575,7 +5575,7 @@ MaxLivePostmasterChildren(void) * Connect background worker to a database. */ void -BackgroundWorkerInitializeConnection(const char *dbname, const char *username, uint32 flags) +BackgroundWorkerInitializeConnection(const char *dbname, const char *username, bits32 flags) { BackgroundWorker *worker = MyBgworkerEntry; @@ -5589,6 +5589,7 @@ BackgroundWorkerInitializeConnection(const char *dbname, const char *username, u username, InvalidOid, /* role to connect as */ false, /* never honor session_preload_libraries */ (flags & BGWORKER_BYPASS_ALLOWCONN) != 0, /* ignore datallowconn? */ + (flags & BGWORKER_BYPASS_ROLELOGINCHECK) != 0, /* bypass login check? */ NULL); /* no out_dbname */ /* it had better not gotten out of "init" mode yet */ @@ -5602,7 +5603,7 @@ BackgroundWorkerInitializeConnection(const char *dbname, const char *username, u * Connect background worker to a database using OIDs. */ void -BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags) +BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, bits32 flags) { BackgroundWorker *worker = MyBgworkerEntry; @@ -5616,6 +5617,7 @@ BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags) NULL, useroid, /* role to connect as */ false, /* never honor session_preload_libraries */ (flags & BGWORKER_BYPASS_ALLOWCONN) != 0, /* ignore datallowconn? */ + (flags & BGWORKER_BYPASS_ROLELOGINCHECK) != 0, /* bypass login check? */ NULL); /* no out_dbname */ /* it had better not gotten out of "init" mode yet */ diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 21b9763183..5c1e21b99d 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -4211,6 +4211,7 @@ PostgresMain(const char *dbname, const char *username) username, InvalidOid, /* role to connect as */ !am_walsender, /* honor session_preload_libraries? */ false, /* don't ignore datallowconn */ + false, /* don't bypass login check */ NULL); /* no out_dbname */ /* diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 1e671c560c..182d666852 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -725,7 +725,7 @@ has_rolreplication(Oid roleid) * Initialize user identity during normal backend startup */ void -InitializeSessionUserId(const char *rolename, Oid roleid) +InitializeSessionUserId(const char *rolename, Oid roleid, bool bypass_login_check) { HeapTuple roleTup; Form_pg_authid rform; @@ -789,7 +789,7 @@ InitializeSessionUserId(const char *rolename, Oid roleid) /* * Is role allowed to login at all? */ - if (!rform->rolcanlogin) + if (!bypass_login_check && !rform->rolcanlogin) ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role \"%s\" is not permitted to log in", diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index df4d15a50f..dcfeb30832 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -719,6 +719,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, Oid useroid, bool load_session_libraries, bool override_allow_connections, + bool bypass_login_check, char *out_dbname) { bool bootstrap = IsBootstrapProcessingMode(); @@ -901,7 +902,7 @@ InitPostgres(const char *in_dbname, Oid dboid, } else { - InitializeSessionUserId(username, useroid); + InitializeSessionUserId(username, useroid, bypass_login_check); am_superuser = superuser(); } } @@ -910,7 +911,7 @@ InitPostgres(const char *in_dbname, Oid dboid, /* normal multiuser case */ Assert(MyProcPort != NULL); PerformAuthentication(MyProcPort); - InitializeSessionUserId(username, useroid); + InitializeSessionUserId(username, useroid, false); /* ensure that auth_method is actually valid, aka authn_id is not NULL */ if (MyClientConnectionInfo.authn_id) InitializeSystemUser(MyClientConnectionInfo.authn_id, diff --git a/src/test/modules/worker_spi/t/001_worker_spi.pl b/src/test/modules/worker_spi/t/001_worker_spi.pl index a026042c65..81f47e09d9 100644 --- a/src/test/modules/worker_spi/t/001_worker_spi.pl +++ b/src/test/modules/worker_spi/t/001_worker_spi.pl @@ -98,4 +98,42 @@ ok( $node->poll_query_until( 'dynamic bgworkers all launched' ) or die "Timed out while waiting for dynamic bgworkers to be launched"; +# Check BGWORKER_BYPASS_ROLELOGINCHECK. +# First create a role without login access. +$node->safe_psql( + 'postgres', qq[ + CREATE ROLE nologrole with nologin; + ALTER ROLE nologrole with superuser; +]); +my $nologrole_id = $node->safe_psql('mydb', "SELECT oid FROM pg_roles where rolname = 'nologrole';"); + +my $log_start = -s $node->logfile; + +# Ask the background workers to connect with this role, failing. +$node->safe_psql('mydb', "SELECT worker_spi_launch(12, $mydb_id, $nologrole_id);"); + +# An error message should be issued. +ok( $node->log_contains( + "role \"nologrole\" is not permitted to log in", $log_start), + "nologrole not allowed to connect if ROLELOGINCHECK is not set" +); + +$log_start = -s $node->logfile; + +my $worker3_pid = $node->safe_psql('mydb', qq(SELECT worker_spi_launch(13, $mydb_id, $nologrole_id, '{"ROLELOGINCHECK"}');)); + +ok( $node->poll_query_until( + 'mydb', + qq[SELECT datname, usename, wait_event FROM pg_stat_activity + WHERE backend_type = 'worker_spi dynamic' AND + pid = $worker3_pid;], + 'mydb|nologrole|worker_spi_main'), + 'dynamic bgworkers ' +) or die "Timed out while waiting for dynamic bgworkers to be launched"; + +# An error message should not be issued. +ok( !$node->log_contains( + "role \"nologrole\" is not permitted to log in", $log_start), + "nologrole allowed to connect if ROLELOGINCHECK is set"); + done_testing(); diff --git a/src/test/modules/worker_spi/worker_spi--1.0.sql b/src/test/modules/worker_spi/worker_spi--1.0.sql index f5b91128ff..59c0c686f3 100644 --- a/src/test/modules/worker_spi/worker_spi--1.0.sql +++ b/src/test/modules/worker_spi/worker_spi--1.0.sql @@ -3,7 +3,8 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION worker_spi" to load this file. \quit -CREATE FUNCTION worker_spi_launch(pg_catalog.int4, pg_catalog.oid, pg_catalog.oid) +CREATE FUNCTION worker_spi_launch(pg_catalog.int4, pg_catalog.oid, + pg_catalog.oid, pg_catalog.text[] DEFAULT '{}') RETURNS pg_catalog.int4 STRICT AS 'MODULE_PATHNAME' LANGUAGE C; diff --git a/src/test/modules/worker_spi/worker_spi.c b/src/test/modules/worker_spi/worker_spi.c index f35897ddac..07d8bab721 100644 --- a/src/test/modules/worker_spi/worker_spi.c +++ b/src/test/modules/worker_spi/worker_spi.c @@ -144,6 +144,7 @@ worker_spi_main(Datum main_arg) Oid dboid; Oid roleoid; char *p; + bits32 flags = 0; table = palloc(sizeof(worktable)); sprintf(name, "schema%d", index); @@ -155,6 +156,8 @@ worker_spi_main(Datum main_arg) memcpy(&dboid, p, sizeof(Oid)); p += sizeof(Oid); memcpy(&roleoid, p, sizeof(Oid)); + p += sizeof(bits32); + memcpy(&flags, p, sizeof(bits32)); /* Establish signal handlers before unblocking signals. */ pqsignal(SIGHUP, SignalHandlerForConfigReload); @@ -165,9 +168,10 @@ worker_spi_main(Datum main_arg) /* Connect to our database */ if (OidIsValid(dboid)) - BackgroundWorkerInitializeConnectionByOid(dboid, roleoid, 0); + BackgroundWorkerInitializeConnectionByOid(dboid, roleoid, flags); else - BackgroundWorkerInitializeConnection(worker_spi_database, worker_spi_role, 0); + BackgroundWorkerInitializeConnection(worker_spi_database, + worker_spi_role, flags); elog(LOG, "%s initialized with %s.%s", MyBgworkerEntry->bgw_name, table->schema, table->name); @@ -371,8 +375,9 @@ _PG_init(void) /* * Now fill in worker-specific data, and do the actual registrations. * - * bgw_extra can optionally include a dabatase OID and a role OID. This - * is left empty here to fallback to the related GUCs at startup. + * bgw_extra can optionally include a dabatase OID, a role OID and a set + * of flags. This is left empty here to fallback to the related GUCs at + * startup (or 0 for the flags). */ for (int i = 1; i <= worker_spi_total_workers; i++) { @@ -398,6 +403,11 @@ worker_spi_launch(PG_FUNCTION_ARGS) BgwHandleStatus status; pid_t pid; char *p; + bits32 flags = 0; + ArrayType *arr = PG_GETARG_ARRAYTYPE_P(3); + Size ndim; + int nelems; + Datum *datum_flags; memset(&worker, 0, sizeof(worker)); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | @@ -412,6 +422,35 @@ worker_spi_launch(PG_FUNCTION_ARGS) /* set bgw_notify_pid so that we can use WaitForBackgroundWorkerStartup */ worker.bgw_notify_pid = MyProcPid; + /* extract flags, if any */ + ndim = ARR_NDIM(arr); + if (ndim > 1) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("flags array must be one-dimensional"))); + + if (array_contains_nulls(arr)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("flags array must not contain nulls"))); + + Assert(ARR_ELEMTYPE(arr) == TEXTOID); + deconstruct_array_builtin(arr, TEXTOID, &datum_flags, NULL, &nelems); + + for (i = 0; i < nelems; i++) + { + char *optname = TextDatumGetCString(datum_flags[i]); + + if (strcmp(optname, "ROLELOGINCHECK") == 0) + flags |= BGWORKER_BYPASS_ROLELOGINCHECK; + else if (strcmp(optname, "ALLOWCONN") == 0) + flags |= BGWORKER_BYPASS_ALLOWCONN; + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("incorrect flag value found in array"))); + } + /* * Register database and role to use for the worker started in bgw_extra. * If none have been provided, fallback to the GUCs. @@ -433,6 +472,8 @@ worker_spi_launch(PG_FUNCTION_ARGS) memcpy(p, &dboid, sizeof(Oid)); p += sizeof(Oid); memcpy(p, &roleoid, sizeof(Oid)); + p += sizeof(bits32); + memcpy(p, &flags, sizeof(bits32)); if (!RegisterDynamicBackgroundWorker(&worker, &handle)) PG_RETURN_NULL(); diff --git a/doc/src/sgml/bgworker.sgml b/doc/src/sgml/bgworker.sgml index 9ad1146ba0..7335c02ed8 100644 --- a/doc/src/sgml/bgworker.sgml +++ b/doc/src/sgml/bgworker.sgml @@ -200,7 +200,8 @@ typedef struct BackgroundWorker <literal>InvalidOid</literal>, the process will run as the superuser created during <command>initdb</command>. If <literal>BGWORKER_BYPASS_ALLOWCONN</literal> is specified as <varname>flags</varname> it is possible to bypass the restriction - to connect to databases not allowing user connections. + to connect to databases not allowing user connections. If <literal>BGWORKER_BYPASS_ROLELOGINCHECK</literal> + is specified as <varname>flags</varname> it is possible to bypass the login check to connect to databases. A background worker can only call one of these two functions, and only once. It is not possible to switch databases. </para> -- 2.42.0
signature.asc
Description: PGP signature