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"));
+ }
}
}