diff --git a/dbutils.c b/dbutils.c index 69de5dd2..f0761902 100644 --- a/dbutils.c +++ b/dbutils.c @@ -1852,6 +1852,51 @@ get_wal_receiver_pid(PGconn *conn) /* =============================== */ +/* + * Determine if the user associated with the current connection can execute CHECKPOINT command. + * User must be a supersuer or a member of the pg_checkpoint default role (available from PostgreSQL 15). + */ +bool +can_execute_checkpoint(PGconn *conn) +{ + PQExpBufferData query; + PGresult *res; + bool has_pg_checkpoint_role = false; + + /* superusers can do anything, no role check needed */ + if (is_superuser_connection(conn, NULL) == true) + return true; + + /* pg_checkpoint available from PostgreSQL 15 */ + if (PQserverVersion(conn) < 150000) + return false; + + initPQExpBuffer(&query); + appendPQExpBufferStr(&query, + " SELECT pg_catalog.pg_has_role('pg_checkpoint','USAGE') "); + + res = PQexec(conn, query.data); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + { + log_db_error(conn, query.data, + _("can_execute_checkpoint(): unable to query user roles")); + } + else + { + has_pg_checkpoint_role = atobool(PQgetvalue(res, 0, 0)); + } + termPQExpBuffer(&query); + PQclear(res); + + return has_pg_checkpoint_role; +} + + +/* + * Determine if the user associated with the current connection + * has sufficient permissions to use pg_promote function + */ bool can_execute_pg_promote(PGconn *conn) { @@ -2492,7 +2537,10 @@ get_repmgr_extension_status(PGconn *conn, t_extension_versions *extversions) /* node management functions */ /* ========================= */ -/* assumes superuser connection */ +/* + * Assumes the connection can execute CHECKPOINT command. + * A check can be executed via 'can_execute_checkpoint' function. + */ void checkpoint(PGconn *conn) { diff --git a/dbutils.h b/dbutils.h index 23bfff29..5b0d3c58 100644 --- a/dbutils.h +++ b/dbutils.h @@ -453,6 +453,7 @@ TimeLineHistoryEntry *get_timeline_history(PGconn *repl_conn, TimeLineID tli); pid_t get_wal_receiver_pid(PGconn *conn); /* user/role information functions */ +bool can_execute_checkpoint(PGconn *conn); bool can_execute_pg_promote(PGconn *conn); bool can_disable_walsender(PGconn *conn); bool connection_has_pg_monitor_role(PGconn *conn, const char *subrole); diff --git a/doc/configuration-permissions.xml b/doc/configuration-permissions.xml index f84e3fcf..f27d1807 100644 --- a/doc/configuration-permissions.xml +++ b/doc/configuration-permissions.xml @@ -79,6 +79,10 @@ Alternatively the meta-role pg_monitor can be granted, which includes membership of the above predefined roles. + + PostgreSQL 15 introduced the pg_checkpoint predefined role which allows a + non-superuser &repmgr; database user to perform a CHECKPOINT command. + Membership of these roles can be granted with e.g. GRANT pg_read_all_stats TO repmgr. @@ -148,6 +152,8 @@ repmgr standby switchover. This can only be executed by a superuser; if the &repmgr; user is not a superuser, the / should be used. + From PostgreSQL 15 the pg_checkpoint predefined role removes the need of + superuser permissions to perform CHECKPOINT command. If &repmgr; is not able to execute CHECKPOINT, diff --git a/doc/repmgr-node-service.xml b/doc/repmgr-node-service.xml index 1a5618f2..9a561bda 100644 --- a/doc/repmgr-node-service.xml +++ b/doc/repmgr-node-service.xml @@ -77,7 +77,8 @@ Note that a superuser connection is required to be able to execute the - CHECKPOINT command. + CHECKPOINT command. From PostgreSQL 15 the pg_checkpoint + predefined role removes the need for superuser permissions to perform CHECKPOINT command. diff --git a/doc/repmgr-standby-switchover.xml b/doc/repmgr-standby-switchover.xml index ef0ce2a4..f70bd221 100644 --- a/doc/repmgr-standby-switchover.xml +++ b/doc/repmgr-standby-switchover.xml @@ -79,7 +79,8 @@ Note that CHECKPOINT requires database superuser permissions to execute. If the repmgr user is not a superuser, the name of a superuser should be - provided with the / option. + provided with the / option. From PostgreSQL 15 the pg_checkpoint + predefined role removes the need for superuser permissions to perform CHECKPOINT command. If &repmgr; is unable to execute the CHECKPOINT command, the switchover diff --git a/repmgr-action-node.c b/repmgr-action-node.c index ac476172..ebb6dc4e 100644 --- a/repmgr-action-node.c +++ b/repmgr-action-node.c @@ -2365,18 +2365,25 @@ do_node_service(void) conn = establish_db_connection_by_params(&source_conninfo, true); } - if (is_superuser_connection(conn, NULL) == false) + if (can_execute_checkpoint(conn) == false) { if (runtime_options.dry_run == true) { - log_warning(_("a CHECKPOINT would be issued here but no superuser connection is available")); + log_warning(_("a CHECKPOINT would be issued here but no authorized connection is available")); } else { - log_warning(_("a superuser connection is required to issue a CHECKPOINT")); + log_warning(_("an authorized connection is required to issue a CHECKPOINT")); } - log_hint(_("provide a superuser with -S/--superuser")); + if (PQserverVersion(conn) >= 150000) + { + log_hint(_("provide a superuser with -S/--superuser or grant pg_checkpoint role to repmgr user")); + } + else + { + log_hint(_("provide a superuser with -S/--superuser")); + } } else { diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index bc9e4a2e..c222c7f8 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -5288,7 +5288,7 @@ do_standby_switchover(void) checkpoint_conn = superuser_conn; } - if (is_superuser_connection(checkpoint_conn, NULL) == true) + if (can_execute_checkpoint(checkpoint_conn) == true) { log_notice(_("issuing CHECKPOINT on node \"%s\" (ID: %i) "), config_file_options.node_name, @@ -5297,7 +5297,16 @@ do_standby_switchover(void) } else { - log_warning(_("no superuser connection available, unable to issue CHECKPOINT")); + log_warning(_("no authorized connection available, unable to issue CHECKPOINT")); + + if (PQserverVersion(conn) >= 150000) + { + log_hint(_("provide a superuser with -S/--superuser or grant pg_checkpoint role to repmgr user")); + } + else + { + log_hint(_("provide a superuser with -S/--superuser")); + } } }