diff --git a/HISTORY b/HISTORY index 2c594d27..8931c5be 100644 --- a/HISTORY +++ b/HISTORY @@ -1,3 +1,6 @@ +4.2.1 2018-??-?? + repmgr: add sanity check for correct extension version (Ian) + 4.2 2018-10-24 repmgr: add parameter "shutdown_check_timeout" for use by "standby switchover"; GitHub #504 (Ian) diff --git a/dbutils.c b/dbutils.c index ef21f06c..70f4a036 100644 --- a/dbutils.c +++ b/dbutils.c @@ -1834,7 +1834,7 @@ atobool(const char *value) /* =================== */ ExtensionStatus -get_repmgr_extension_status(PGconn *conn) +get_repmgr_extension_status(PGconn *conn, t_extension_versions *extversions) { PQExpBufferData query; PGresult *res = NULL; @@ -1845,7 +1845,11 @@ get_repmgr_extension_status(PGconn *conn) initPQExpBuffer(&query); appendPQExpBufferStr(&query, - " SELECT ae.name, e.extname " + " SELECT ae.name, e.extname, " + " ae.default_version, " + " (ae.default_version::numeric * 10)::INT AS available, " + " ae.installed_version, " + " (ae.installed_version::numeric * 10)::INT AS installed " " FROM pg_catalog.pg_available_extensions ae " "LEFT JOIN pg_catalog.pg_extension e " " ON e.extname=ae.name " @@ -1868,7 +1872,24 @@ get_repmgr_extension_status(PGconn *conn) /* 2. Check if extension installed */ else if (PQgetisnull(res, 0, 1) == 0) { - status = REPMGR_INSTALLED; + int available_version = atoi(PQgetvalue(res, 0, 3)); + int installed_version = atoi(PQgetvalue(res, 0, 5)); + + /* caller wants to know which versions are installed/available */ + if (extversions != NULL) + { + strncpy(extversions->default_version, PQgetvalue(res, 0, 2), 7); + strncpy(extversions->installed_version, PQgetvalue(res, 0, 4), 7); + } + + if (available_version > installed_version) + { + status = REPMGR_OLD_VERSION_INSTALLED; + } + else + { + status = REPMGR_INSTALLED; + } } else { diff --git a/dbutils.h b/dbutils.h index 1976512e..a5b50d7a 100644 --- a/dbutils.h +++ b/dbutils.h @@ -47,6 +47,7 @@ typedef enum typedef enum { REPMGR_INSTALLED = 0, + REPMGR_OLD_VERSION_INSTALLED, REPMGR_AVAILABLE, REPMGR_UNAVAILABLE, REPMGR_UNKNOWN @@ -104,6 +105,20 @@ typedef enum } BackupState; +/* + * Struct to store extension version information + */ + +typedef struct s_extension_versions { + char default_version[8]; + char installed_version[8]; +} t_extension_versions; + +#define T_EXTENSION_VERSIONS_INITIALIZER { \ + "", \ + "", \ +} + /* * Struct to store node information */ @@ -419,7 +434,7 @@ bool repmgrd_is_paused(PGconn *conn); bool repmgrd_pause(PGconn *conn, bool pause); /* extension functions */ -ExtensionStatus get_repmgr_extension_status(PGconn *conn); +ExtensionStatus get_repmgr_extension_status(PGconn *conn, t_extension_versions *extversions); /* node management functions */ void checkpoint(PGconn *conn); diff --git a/repmgr-action-bdr.c b/repmgr-action-bdr.c index be6cc4a8..ff0e76c0 100644 --- a/repmgr-action-bdr.c +++ b/repmgr-action-bdr.c @@ -126,7 +126,7 @@ do_bdr_register(void) } /* check whether repmgr extension exists, and there are no non-BDR nodes registered */ - extension_status = get_repmgr_extension_status(conn); + extension_status = get_repmgr_extension_status(conn, NULL); if (extension_status == REPMGR_UNKNOWN) { @@ -232,7 +232,7 @@ do_bdr_register(void) } /* check repmgr schema exists, skip if not */ - other_node_extension_status = get_repmgr_extension_status(bdr_node_conn); + other_node_extension_status = get_repmgr_extension_status(bdr_node_conn, NULL); if (other_node_extension_status != REPMGR_INSTALLED) { @@ -442,7 +442,7 @@ do_bdr_unregister(void) exit(ERR_BAD_CONFIG); } - extension_status = get_repmgr_extension_status(conn); + extension_status = get_repmgr_extension_status(conn, NULL); if (extension_status != REPMGR_INSTALLED) { log_error(_("repmgr is not installed on database \"%s\""), dbname); diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index 68c5fee0..8add98bd 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -4425,7 +4425,7 @@ check_source_server() * to be used as a standalone clone tool) */ - extension_status = get_repmgr_extension_status(primary_conn); + extension_status = get_repmgr_extension_status(primary_conn, NULL); if (extension_status != REPMGR_INSTALLED) { diff --git a/repmgr-client.c b/repmgr-client.c index 4e7278a8..ac1d5937 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -2080,8 +2080,9 @@ create_repmgr_extension(PGconn *conn) bool is_superuser = false; PGconn *superuser_conn = NULL; PGconn *schema_create_conn = NULL; + t_extension_versions extversions = T_EXTENSION_VERSIONS_INITIALIZER; - extension_status = get_repmgr_extension_status(conn); + extension_status = get_repmgr_extension_status(conn, &extversions); switch (extension_status) { @@ -2093,8 +2094,15 @@ create_repmgr_extension(PGconn *conn) log_error(_("\"repmgr\" extension is not available")); return false; + case REPMGR_OLD_VERSION_INSTALLED: + log_error(_("an older version of the \"repmgr\" extension is installed")); + log_detail(_("version %s is installed but newer version %s is available"), + extversions.installed_version, + extversions.default_version); + log_hint(_("execute \"ALTER EXTENSION repmgr UPGRADE\"")); + return false; + case REPMGR_INSTALLED: - /* TODO: check version */ log_info(_("\"repmgr\" extension is already installed")); return true; diff --git a/repmgrd.c b/repmgrd.c index 4e4776e9..180507ab 100644 --- a/repmgrd.c +++ b/repmgrd.c @@ -88,6 +88,7 @@ main(int argc, char **argv) RecordStatus record_status; ExtensionStatus extension_status = REPMGR_UNKNOWN; + t_extension_versions extversions = T_EXTENSION_VERSIONS_INITIALIZER; FILE *fd; @@ -389,7 +390,7 @@ main(int argc, char **argv) */ /* Check "repmgr" the extension is installed */ - extension_status = get_repmgr_extension_status(local_conn); + extension_status = get_repmgr_extension_status(local_conn, &extversions); if (extension_status != REPMGR_INSTALLED) { @@ -402,19 +403,32 @@ main(int argc, char **argv) exit(ERR_DB_QUERY); } - log_error(_("repmgr extension not found on this node")); + if (extension_status == REPMGR_OLD_VERSION_INSTALLED) + { + log_error(_("an older version of the \"repmgr\" extension is installed")); + log_detail(_("version %s is installed but newer version %s is available"), + extversions.installed_version, + extversions.default_version); + log_hint(_("verify the repmgr installation is updated properly before continuing")); - if (extension_status == REPMGR_AVAILABLE) - { - log_detail(_("repmgr extension is available but not installed in database \"%s\""), - PQdb(local_conn)); } - else if (extension_status == REPMGR_UNAVAILABLE) + else { - log_detail(_("repmgr extension is not available on this node")); + log_error(_("repmgr extension not found on this node")); + + if (extension_status == REPMGR_AVAILABLE) + { + log_detail(_("repmgr extension is available but not installed in database \"%s\""), + PQdb(local_conn)); + } + else if (extension_status == REPMGR_UNAVAILABLE) + { + log_detail(_("repmgr extension is not available on this node")); + } + + log_hint(_("check that this node is part of a repmgr cluster")); } - log_hint(_("check that this node is part of a repmgr cluster")); close_connection(&local_conn); exit(ERR_BAD_CONFIG); }