diff --git a/config.h.in b/config.h.in index 69adebae..b977437d 100644 --- a/config.h.in +++ b/config.h.in @@ -2,4 +2,3 @@ /* Only build repmgr for BDR */ #undef BDR_ONLY - diff --git a/configfile.c b/configfile.c index c590309c..f6a17321 100644 --- a/configfile.c +++ b/configfile.c @@ -260,6 +260,7 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * options->tablespace_mapping.head = NULL; options->tablespace_mapping.tail = NULL; memset(options->recovery_min_apply_delay, 0, sizeof(options->recovery_min_apply_delay)); + options->recovery_min_apply_delay_provided = false; options->use_primary_conninfo_password = false; /*----------------- @@ -435,7 +436,10 @@ _parse_config(t_configuration_options *options, ItemList *error_list, ItemList * else if (strcmp(name, "restore_command") == 0) strncpy(options->restore_command, value, MAXLEN); else if (strcmp(name, "recovery_min_apply_delay") == 0) + { parse_time_unit_parameter(name, value, options->recovery_min_apply_delay, error_list); + options->recovery_min_apply_delay_provided = true; + } else if (strcmp(name, "use_primary_conninfo_password") == 0) options->use_primary_conninfo_password = parse_bool(value, name, error_list); diff --git a/configfile.h b/configfile.h index 7beb4961..13504e77 100644 --- a/configfile.h +++ b/configfile.h @@ -88,6 +88,7 @@ typedef struct char restore_command[MAXLEN]; TablespaceList tablespace_mapping; char recovery_min_apply_delay[MAXLEN]; + bool recovery_min_apply_delay_provided; bool use_primary_conninfo_password; /* node check settings */ @@ -152,7 +153,7 @@ typedef struct /* log settings */ \ "", "", "", DEFAULT_LOG_STATUS_INTERVAL, \ /* standby action settings */ \ - false, "", "", { NULL, NULL }, "", false, \ + false, "", "", { NULL, NULL }, "", false, false, \ /* node check settings */ \ DEFAULT_ARCHIVE_READY_WARNING, DEFAULT_ARCHIVE_READY_CRITICAL, \ DEFAULT_REPLICATION_LAG_WARNING, DEFAULT_REPLICATION_LAG_CRITICAL, \ diff --git a/configure b/configure index 71c256d4..08d3eca4 100755 --- a/configure +++ b/configure @@ -1841,7 +1841,7 @@ if test "$major_version_num" -lt '10'; then version_num_int=$(echo "$version_num"| $SED -e 's/^\([0-9]*\)\.\([0-9]*\)$/\1\2/') - if test "$version_num_int" -lt '94'; then + if test "$version_num_int" -lt '93'; then as_fn_error $? "repmgr is not compatible with detected PostgreSQL version: $version_num" "$LINENO" 5 fi else diff --git a/configure.in b/configure.in index 7f036c11..3fd4f30a 100644 --- a/configure.in +++ b/configure.in @@ -38,7 +38,7 @@ if test "$major_version_num" -lt '10'; then version_num_int=$(echo "$version_num"| $SED -e 's/^\([[0-9]]*\)\.\([[0-9]]*\)$/\1\2/') - if test "$version_num_int" -lt '94'; then + if test "$version_num_int" -lt '93'; then AC_MSG_ERROR([repmgr is not compatible with detected PostgreSQL version: $version_num]) fi else diff --git a/dbutils.c b/dbutils.c index 00e02ecb..1cc83377 100644 --- a/dbutils.c +++ b/dbutils.c @@ -3619,10 +3619,6 @@ request_vote(PGconn *conn, t_node_info *this_node, t_node_info *other_node, int "SELECT repmgr.request_vote(%i, %i)", this_node->node_id, electoral_term); -/* "SELECT repmgr.request_vote(%i, '%X/%X'::pg_lsn)", - this_node->node_id, - (uint32) (last_wal_receive_lsn >> 32), - (uint32) last_wal_receive_lsn);*/ res = PQexec(conn, query.data); termPQExpBuffer(&query); diff --git a/repmgr--4.0.sql b/repmgr--4.0.sql index 8621d921..b84c87f3 100644 --- a/repmgr--4.0.sql +++ b/repmgr--4.0.sql @@ -23,6 +23,16 @@ CREATE TABLE repmgr.events ( details TEXT NULL ); +DO $repmgr$ +DECLARE + DECLARE server_version_num INT; +BEGIN + SELECT setting + FROM pg_catalog.pg_settings + WHERE name = 'server_version_num' + INTO server_version_num; + IF server_version_num >= 90400 THEN + EXECUTE $repmgr_func$ CREATE TABLE repmgr.monitoring_history ( primary_node_id INTEGER NOT NULL, standby_node_id INTEGER NOT NULL, @@ -32,7 +42,26 @@ CREATE TABLE repmgr.monitoring_history ( last_wal_standby_location PG_LSN, replication_lag BIGINT NOT NULL, apply_lag BIGINT NOT NULL -); +) + $repmgr_func$; + ELSE + EXECUTE $repmgr_func$ +CREATE TABLE repmgr.monitoring_history ( + primary_node_id INTEGER NOT NULL, + standby_node_id INTEGER NOT NULL, + last_monitor_time TIMESTAMP WITH TIME ZONE NOT NULL, + last_apply_time TIMESTAMP WITH TIME ZONE, + last_wal_primary_location TEXT NOT NULL, + last_wal_standby_location TEXT, + replication_lag BIGINT NOT NULL, + apply_lag BIGINT NOT NULL +) + $repmgr_func$; + END IF; +END$repmgr$; + + + CREATE INDEX idx_monitoring_history_time ON repmgr.monitoring_history (last_monitor_time, standby_node_id); @@ -73,10 +102,33 @@ CREATE FUNCTION standby_get_last_updated() /* failover functions */ + +DO $repmgr$ +DECLARE + DECLARE server_version_num INT; +BEGIN + SELECT setting + FROM pg_catalog.pg_settings + WHERE name = 'server_version_num' + INTO server_version_num; + + IF server_version_num >= 90400 THEN + EXECUTE $repmgr_func$ CREATE FUNCTION request_vote(INT,INT) RETURNS pg_lsn AS 'MODULE_PATHNAME', 'request_vote' LANGUAGE C STRICT; + $repmgr_func$; + ELSE + EXECUTE $repmgr_func$ +CREATE FUNCTION request_vote(INT,INT) + RETURNS TEXT + AS 'MODULE_PATHNAME', 'request_vote' + LANGUAGE C STRICT; + $repmgr_func$; + END IF; +END$repmgr$; + CREATE FUNCTION get_voting_status() RETURNS INT diff --git a/repmgr-action-node.c b/repmgr-action-node.c index dc604dfb..84d41b7d 100644 --- a/repmgr-action-node.c +++ b/repmgr-action-node.c @@ -1598,6 +1598,7 @@ do_node_rejoin(void) t_node_info primary_node_record = T_NODE_INFO_INITIALIZER; bool success = true; + int server_version_num = UNKNOWN_SERVER_VERSION_NUM; /* check node is not actually running */ @@ -1646,6 +1647,12 @@ do_node_rejoin(void) /* check provided upstream connection */ upstream_conn = establish_db_connection_by_params(&source_conninfo, true); + /* sanity-checks for 9.3 */ + server_version_num = get_server_version(upstream_conn, NULL); + + if (server_version_num < 90400) + check_93_config(); + if (get_primary_node_record(upstream_conn, &primary_node_record) == false) { log_error(_("unable to retrieve primary node record")); diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index 8290b316..ecf6c76d 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -81,7 +81,6 @@ static bool check_upstream_config(PGconn *conn, int server_version_num, t_node_i static void check_primary_standby_version_match(PGconn *conn, PGconn *primary_conn); static void check_recovery_type(PGconn *conn); - static void initialise_direct_clone(t_node_info *node_record); static int run_basebackup(t_node_info *node_record); static int run_file_backup(t_node_info *node_record); @@ -96,6 +95,11 @@ static void get_barman_property(char *dst, char *name, char *local_repmgr_direct static int get_tablespace_data_barman(char *, TablespaceDataList *); static char *make_barman_ssh_command(char *buf); +static bool create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir); +static void write_primary_conninfo(char *line, t_conninfo_param_list *param_list); +static bool write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line); + + static NodeStatus parse_node_status_is_shutdown_cleanly(const char *node_status_output, XLogRecPtr *checkPoint); static CheckStatus parse_node_check_archiver(const char *node_check_output, int *files, int *threshold); static CheckStatus parse_node_check_replication_lag(const char *node_check_output, int *seconds, int *threshold); @@ -1539,6 +1543,7 @@ do_standby_follow(void) RecordStatus record_status = RECORD_NOT_FOUND; int timer = 0; + int server_version_num = UNKNOWN_SERVER_VERSION_NUM; PQExpBufferData follow_output; bool success = false; @@ -1557,6 +1562,12 @@ do_standby_follow(void) /* check this is a standby */ check_recovery_type(local_conn); + /* sanity-checks for 9.3 */ + server_version_num = get_server_version(local_conn, NULL); + + if (server_version_num < 90400) + check_93_config(); + if (runtime_options.upstream_node_id != NO_UPSTREAM_NODE) { /* check not self! */ @@ -1677,13 +1688,11 @@ do_standby_follow(void) initPQExpBuffer(&follow_output); - success = do_standby_follow_internal( - primary_conn, + success = do_standby_follow_internal(primary_conn, &primary_node_record, &follow_output); - create_event_notification( - primary_conn, + create_event_notification(primary_conn, &config_file_options, config_file_options.node_id, "standby_follow", @@ -1713,6 +1722,9 @@ do_standby_follow(void) /* * Perform the actuall "follow" operation; this is executed by * "node rejoin" too. + * + * For PostgreSQL 9.3, ensure check_93_config() was called before calling + * this function. */ bool do_standby_follow_internal(PGconn *primary_conn, t_node_info *primary_node_record, PQExpBufferData *output) @@ -3098,6 +3110,10 @@ check_source_server() } } + /* disable configuration file options incompatible with 9.3 */ + if (source_server_version_num < 90400) + check_93_config(); + check_upstream_config(source_conn, source_server_version_num, &node_record, true); } @@ -3198,6 +3214,8 @@ check_source_server_via_barman() * Perform sanity check on upstream server configuration before starting cloning * process * + * For PostreSQL 9.3, ensure check_93_config() is called before calling this. + * * TODO: * - check user is qualified to perform base backup */ @@ -3248,6 +3266,12 @@ check_upstream_config(PGconn *conn, int server_version_num, t_node_info *node_in xlog_stream = false; /* Check that WAL level is set correctly */ + if (server_version_num < 90400) + { + i = guc_set(conn, "wal_level", "=", "hot_standby"); + wal_error_message = _("parameter \"wal_level\" must be set to \"hot_standby\""); + } + else { char *levels_pre96[] = { "hot_standby", @@ -3272,12 +3296,12 @@ check_upstream_config(PGconn *conn, int server_version_num, t_node_info *node_in if (server_version_num < 90600) { levels = (char **) levels_pre96; - wal_error_message = _("parameter 'wal_level' must be set to 'hot_standby' or 'logical'"); + wal_error_message = _("parameter \"wal_level\" must be set to \"hot_standby\" or \"logical\""); } else { levels = (char **) levels_96plus; - wal_error_message = _("parameter 'wal_level' must be set to 'replica' or 'logical'"); + wal_error_message = _("parameter \"wal_level\" must be set to \"replica\" or \"logical\""); } do @@ -3596,57 +3620,64 @@ initialise_direct_clone(t_node_info *node_record) if (config_file_options.tablespace_mapping.head != NULL) { - TablespaceListCell *cell = false; - KeyValueList not_found = {NULL, NULL}; - int total = 0, - matched = 0; - bool success = false; - - for (cell = config_file_options.tablespace_mapping.head; cell; cell = cell->next) + if (server_version_num < 90400) { - char *old_dir_escaped = escape_string(source_conn, cell->old_dir); - char name[MAXLEN] = ""; - - success = get_tablespace_name_by_location(source_conn, old_dir_escaped, name); - pfree(old_dir_escaped); - - if (success == true) - { - matched++; - } - else - { - key_value_list_set( - ¬_found, - cell->old_dir, - ""); - } - - total++; + log_error(_("tablespace mapping not supported in PostgreSQL 9.3, ignoring")); } - - if (not_found.head != NULL) + else { - PQExpBufferData detail; - KeyValueListCell *kv_cell; + TablespaceListCell *cell = false; + KeyValueList not_found = {NULL, NULL}; + int total = 0, + matched = 0; + bool success = false; - log_error(_("%i of %i mapped tablespaces not found"), - total - matched, total); - initPQExpBuffer(&detail); - - for (kv_cell = not_found.head; kv_cell; kv_cell = kv_cell->next) + for (cell = config_file_options.tablespace_mapping.head; cell; cell = cell->next) { - appendPQExpBuffer( - &detail, - " %s\n", kv_cell->key); + char *old_dir_escaped = escape_string(source_conn, cell->old_dir); + char name[MAXLEN] = ""; + + success = get_tablespace_name_by_location(source_conn, old_dir_escaped, name); + pfree(old_dir_escaped); + + if (success == true) + { + matched++; + } + else + { + key_value_list_set(¬_found, + cell->old_dir, + ""); + } + + total++; } - log_detail(_("following tablespaces not found:\n%s"), - detail.data); - termPQExpBuffer(&detail); + if (not_found.head != NULL) + { + PQExpBufferData detail; + KeyValueListCell *kv_cell; - exit(ERR_BAD_CONFIG); + log_error(_("%i of %i mapped tablespaces not found"), + total - matched, total); + + initPQExpBuffer(&detail); + + for (kv_cell = not_found.head; kv_cell; kv_cell = kv_cell->next) + { + appendPQExpBuffer( + &detail, + " %s\n", kv_cell->key); + } + + log_detail(_("following tablespaces not found:\n%s"), + detail.data); + termPQExpBuffer(&detail); + + exit(ERR_BAD_CONFIG); + } } } @@ -4751,6 +4782,225 @@ drop_replication_slot_if_exists(PGconn *conn, int node_id, char *slot_name) } +/* + * Creates a recovery.conf file for a standby + * + * A database connection pointer is required for escaping primary_conninfo + * parameters. When cloning from Barman and --no-upstream-connection ) this + * might not be available. + */ +bool +create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir) +{ + FILE *recovery_file; + char recovery_file_path[MAXPGPATH] = ""; + char line[MAXLEN] = ""; + mode_t um; + + maxpath_snprintf(recovery_file_path, "%s/%s", data_dir, RECOVERY_COMMAND_FILE); + + /* Set umask to 0600 */ + um = umask((~(S_IRUSR | S_IWUSR)) & (S_IRWXG | S_IRWXO)); + recovery_file = fopen(recovery_file_path, "w"); + umask(um); + + if (recovery_file == NULL) + { + log_error(_("unable to create recovery.conf file at \"%s\""), + recovery_file_path); + log_detail("%s", strerror(errno)); + + return false; + } + + log_debug("create_recovery_file(): creating \"%s\"...", + recovery_file_path); + + /* standby_mode = 'on' */ + maxlen_snprintf(line, "standby_mode = 'on'\n"); + + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + + /* primary_conninfo = '...' */ + + /* + * the user specified --upstream-conninfo string - copy that + */ + if (strlen(runtime_options.upstream_conninfo)) + { + char *escaped = escape_recovery_conf_value(runtime_options.upstream_conninfo); + + maxlen_snprintf(line, "primary_conninfo = '%s'\n", + escaped); + free(escaped); + } + + /* + * otherwise use the conninfo inferred from the upstream connection and/or + * node record + */ + else + { + write_primary_conninfo(line, recovery_conninfo); + } + + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + + /* recovery_target_timeline = 'latest' */ + maxlen_snprintf(line, "recovery_target_timeline = 'latest'\n"); + + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + + /* recovery_min_apply_delay = ... (optional) */ + if (config_file_options.recovery_min_apply_delay_provided == true) + { + maxlen_snprintf(line, "recovery_min_apply_delay = %s\n", + config_file_options.recovery_min_apply_delay); + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + } + + /* primary_slot_name = '...' (optional, for 9.4 and later) */ + if (config_file_options.use_replication_slots) + { + maxlen_snprintf(line, "primary_slot_name = %s\n", + node_record->slot_name); + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + } + + /* + * If restore_command is set, we use it as restore_command in + * recovery.conf + */ + if (strcmp(config_file_options.restore_command, "") != 0) + { + maxlen_snprintf(line, "restore_command = '%s'\n", + config_file_options.restore_command); + if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) + return false; + + trim(line); + log_debug("recovery.conf: %s", line); + } + fclose(recovery_file); + + return true; +} + + +static bool +write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line) +{ + if (fputs(line, recovery_file) == EOF) + { + log_error(_("unable to write to recovery file at \"%s\""), recovery_file_path); + fclose(recovery_file); + return false; + } + + return true; +} + + +static void +write_primary_conninfo(char *line, t_conninfo_param_list *param_list) +{ + PQExpBufferData conninfo_buf; + bool application_name_provided = false; + bool password_provided = false; + int c; + char *escaped = NULL; + t_conninfo_param_list env_conninfo; + + initialize_conninfo_params(&env_conninfo, true); + + initPQExpBuffer(&conninfo_buf); + + for (c = 0; c < param_list->size && param_list->keywords[c] != NULL; c++) + { + /* + * Skip empty settings and ones which don't make any sense in + * recovery.conf + */ + if (strcmp(param_list->keywords[c], "dbname") == 0 || + strcmp(param_list->keywords[c], "replication") == 0 || + (param_list->values[c] == NULL) || + (param_list->values[c] != NULL && param_list->values[c][0] == '\0')) + continue; + + /* only include "password" if explicitly requested */ + if (strcmp(param_list->keywords[c], "password") == 0) + { + password_provided = true; + } + + if (conninfo_buf.len != 0) + appendPQExpBufferChar(&conninfo_buf, ' '); + + if (strcmp(param_list->keywords[c], "application_name") == 0) + application_name_provided = true; + + appendPQExpBuffer(&conninfo_buf, "%s=", param_list->keywords[c]); + appendConnStrVal(&conninfo_buf, param_list->values[c]); + } + + /* "application_name" not provided - default to repmgr node name */ + if (application_name_provided == false) + { + if (strlen(config_file_options.node_name)) + { + appendPQExpBuffer(&conninfo_buf, " application_name="); + appendConnStrVal(&conninfo_buf, config_file_options.node_name); + } + else + { + appendPQExpBuffer(&conninfo_buf, " application_name=repmgr"); + } + } + + /* no password provided explicitly */ + if (password_provided == false) + { + if (config_file_options.use_primary_conninfo_password == true) + { + const char *password = param_get(&env_conninfo, "password"); + + if (password != NULL) + { + appendPQExpBuffer(&conninfo_buf, " password="); + appendConnStrVal(&conninfo_buf, password); + } + } + } + + escaped = escape_recovery_conf_value(conninfo_buf.data); + maxlen_snprintf(line, "primary_conninfo = '%s'\n", escaped); + + free(escaped); + free_conninfo_params(&env_conninfo); + termPQExpBuffer(&conninfo_buf); +} + + /* TODO: consolidate code in below functions */ static NodeStatus parse_node_status_is_shutdown_cleanly(const char *node_status_output, XLogRecPtr *checkPoint) diff --git a/repmgr-client-global.h b/repmgr-client-global.h index 066dbec4..30018650 100644 --- a/repmgr-client-global.h +++ b/repmgr-client-global.h @@ -198,6 +198,7 @@ extern t_node_info target_node_info; extern int check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char *server_version_string); +extern void check_93_config(void); extern bool create_repmgr_extension(PGconn *conn); extern int test_ssh_connection(char *host, char *remote_user); extern bool local_command(const char *command, PQExpBufferData *outputbuf); @@ -211,8 +212,6 @@ extern void print_error_list(ItemList *error_list, int log_level); extern char *make_pg_path(const char *file); -extern bool create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir); - extern void get_superuser_connection(PGconn **conn, PGconn **superuser_conn, PGconn **privileged_conn); extern bool remote_command(const char *host, const char *user, const char *command, PQExpBufferData *outputbuf); diff --git a/repmgr-client.c b/repmgr-client.c index 0494ec51..4105758c 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -1998,6 +1998,30 @@ check_server_version(PGconn *conn, char *server_type, bool exit_on_error, char * } + +/* + * check_93_config() + * + * Disable options not compatible with PostgreSQL 9.3 + */ +void +check_93_config(void) +{ + if (config_file_options.recovery_min_apply_delay_provided == true) + { + config_file_options.recovery_min_apply_delay_provided = false; + log_warning(_("configuration file option \"recovery_min_apply_delay\" not compatible with PostgreSQL 9.3, ignoring")); + } + + if (config_file_options.use_replication_slots == true) + { + config_file_options.use_replication_slots = false; + log_warning(_("configuration file option \"use_replication_slots\" not compatible with PostgreSQL 9.3, ignoring")); + log_hint(_("replication slots are available from PostgreSQL 9.4")); + } +} + + int test_ssh_connection(char *host, char *remote_user) { @@ -2272,223 +2296,6 @@ copy_remote_files(char *host, char *remote_user, char *remote_path, -/* - * Creates a recovery.conf file for a standby - * - * A database connection pointer is required for escaping primary_conninfo - * parameters. When cloning from Barman and --no-upstream-connection ) this - * might not be available. - */ -bool -create_recovery_file(t_node_info *node_record, t_conninfo_param_list *recovery_conninfo, const char *data_dir) -{ - FILE *recovery_file; - char recovery_file_path[MAXPGPATH] = ""; - char line[MAXLEN] = ""; - mode_t um; - - maxpath_snprintf(recovery_file_path, "%s/%s", data_dir, RECOVERY_COMMAND_FILE); - - /* Set umask to 0600 */ - um = umask((~(S_IRUSR | S_IWUSR)) & (S_IRWXG | S_IRWXO)); - recovery_file = fopen(recovery_file_path, "w"); - umask(um); - - if (recovery_file == NULL) - { - log_error(_("unable to create recovery.conf file at \"%s\""), - recovery_file_path); - log_detail("%s", strerror(errno)); - - return false; - } - - log_debug("create_recovery_file(): creating \"%s\"...", - recovery_file_path); - - /* standby_mode = 'on' */ - maxlen_snprintf(line, "standby_mode = 'on'\n"); - - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - - /* primary_conninfo = '...' */ - - /* - * the user specified --upstream-conninfo string - copy that - */ - if (strlen(runtime_options.upstream_conninfo)) - { - char *escaped = escape_recovery_conf_value(runtime_options.upstream_conninfo); - - maxlen_snprintf(line, "primary_conninfo = '%s'\n", - escaped); - free(escaped); - } - - /* - * otherwise use the conninfo inferred from the upstream connection and/or - * node record - */ - else - { - write_primary_conninfo(line, recovery_conninfo); - } - - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - - /* recovery_target_timeline = 'latest' */ - maxlen_snprintf(line, "recovery_target_timeline = 'latest'\n"); - - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - - /* recovery_min_apply_delay = ... (optional) */ - if (*config_file_options.recovery_min_apply_delay) - { - maxlen_snprintf(line, "recovery_min_apply_delay = %s\n", - config_file_options.recovery_min_apply_delay); - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - } - - /* primary_slot_name = '...' (optional, for 9.4 and later) */ - if (config_file_options.use_replication_slots) - { - maxlen_snprintf(line, "primary_slot_name = %s\n", - node_record->slot_name); - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - } - - /* - * If restore_command is set, we use it as restore_command in - * recovery.conf - */ - if (strcmp(config_file_options.restore_command, "") != 0) - { - maxlen_snprintf(line, "restore_command = '%s'\n", - config_file_options.restore_command); - if (write_recovery_file_line(recovery_file, recovery_file_path, line) == false) - return false; - - trim(line); - log_debug("recovery.conf: %s", line); - } - fclose(recovery_file); - - return true; -} - - -static bool -write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line) -{ - if (fputs(line, recovery_file) == EOF) - { - log_error(_("unable to write to recovery file at \"%s\""), recovery_file_path); - fclose(recovery_file); - return false; - } - - return true; -} - - -static void -write_primary_conninfo(char *line, t_conninfo_param_list *param_list) -{ - PQExpBufferData conninfo_buf; - bool application_name_provided = false; - bool password_provided = false; - int c; - char *escaped = NULL; - t_conninfo_param_list env_conninfo; - - initialize_conninfo_params(&env_conninfo, true); - - initPQExpBuffer(&conninfo_buf); - - for (c = 0; c < param_list->size && param_list->keywords[c] != NULL; c++) - { - /* - * Skip empty settings and ones which don't make any sense in - * recovery.conf - */ - if (strcmp(param_list->keywords[c], "dbname") == 0 || - strcmp(param_list->keywords[c], "replication") == 0 || - (param_list->values[c] == NULL) || - (param_list->values[c] != NULL && param_list->values[c][0] == '\0')) - continue; - - /* only include "password" if explicitly requested */ - if (strcmp(param_list->keywords[c], "password") == 0) - { - password_provided = true; - } - - if (conninfo_buf.len != 0) - appendPQExpBufferChar(&conninfo_buf, ' '); - - if (strcmp(param_list->keywords[c], "application_name") == 0) - application_name_provided = true; - - appendPQExpBuffer(&conninfo_buf, "%s=", param_list->keywords[c]); - appendConnStrVal(&conninfo_buf, param_list->values[c]); - } - - /* "application_name" not provided - default to repmgr node name */ - if (application_name_provided == false) - { - if (strlen(config_file_options.node_name)) - { - appendPQExpBuffer(&conninfo_buf, " application_name="); - appendConnStrVal(&conninfo_buf, config_file_options.node_name); - } - else - { - appendPQExpBuffer(&conninfo_buf, " application_name=repmgr"); - } - } - - /* no password provided explicitly */ - if (password_provided == false) - { - if (config_file_options.use_primary_conninfo_password == true) - { - const char *password = param_get(&env_conninfo, "password"); - - if (password != NULL) - { - appendPQExpBuffer(&conninfo_buf, " password="); - appendConnStrVal(&conninfo_buf, password); - } - } - } - - escaped = escape_recovery_conf_value(conninfo_buf.data); - maxlen_snprintf(line, "primary_conninfo = '%s'\n", escaped); - - free(escaped); - free_conninfo_params(&env_conninfo); - termPQExpBuffer(&conninfo_buf); -} /* diff --git a/repmgr-client.h b/repmgr-client.h index f53b1efc..aeeef397 100644 --- a/repmgr-client.h +++ b/repmgr-client.h @@ -204,7 +204,4 @@ static const char *action_name(const int action); static void check_cli_parameters(const int action); -static void write_primary_conninfo(char *line, t_conninfo_param_list *param_list); -static bool write_recovery_file_line(FILE *recovery_file, char *recovery_file_path, char *line); - #endif /* _REPMGR_CLIENT_H_ */ diff --git a/repmgr.c b/repmgr.c index af54ecaf..eabfcc81 100644 --- a/repmgr.c +++ b/repmgr.c @@ -20,6 +20,7 @@ * along with this program. If not, see . */ + #include "postgres.h" #include "fmgr.h" #include "access/xlog.h" @@ -31,7 +32,11 @@ #include "storage/shmem.h" #include "storage/spin.h" #include "utils/builtins.h" + +#if (PG_VERSION_NUM >= 90400) #include "utils/pg_lsn.h" +#endif + #include "utils/timestamp.h" #include "executor/spi.h" @@ -280,14 +285,24 @@ request_vote(PG_FUNCTION_ARGS) { #ifndef BDR_ONLY StringInfoData query; + +#if (PG_VERSION_NUM >= 90400) XLogRecPtr our_lsn = InvalidXLogRecPtr; + bool isnull; +#else + char *value = NULL; + char lsn_text[64] = ""; +#endif /* node_id used for logging purposes */ int requesting_node_id = PG_GETARG_INT32(0); int current_electoral_term = PG_GETARG_INT32(1); int ret; - bool isnull; + + + + if (!shared_state) PG_RETURN_NULL(); @@ -326,17 +341,30 @@ request_vote(PG_FUNCTION_ARGS) { SPI_finish(); elog(WARNING, "unable to retrieve last received LSN"); + +#if (PG_VERSION_NUM >= 90400) PG_RETURN_LSN(InvalidOid); +#else + PG_RETURN_TEXT_P(cstring_to_text("0/0")); +#endif } +#if (PG_VERSION_NUM >= 90400) our_lsn = DatumGetLSN(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)); - elog(DEBUG1, "our LSN is %X/%X", (uint32) (our_lsn >> 32), (uint32) our_lsn); +#else + value = SPI_getvalue(SPI_tuptable->vals[0], + SPI_tuptable->tupdesc, + 1); + strncpy(lsn_text, value, 64); + pfree(value); + elog(DEBUG1, "our LSN is %s", lsn_text); +#endif /* indicate this node has responded to a vote request */ shared_state->voting_status = VS_VOTE_REQUEST_RECEIVED; @@ -347,7 +375,11 @@ request_vote(PG_FUNCTION_ARGS) /* should we free "query" here? */ SPI_finish(); +#if (PG_VERSION_NUM >= 90400) PG_RETURN_LSN(our_lsn); +#else + PG_RETURN_TEXT_P(cstring_to_text(lsn_text)); +#endif #else PG_RETURN(InvalidOid); #endif diff --git a/repmgr.h b/repmgr.h index 5b5ec855..ff8f4022 100644 --- a/repmgr.h +++ b/repmgr.h @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include @@ -39,8 +41,8 @@ #include "dbutils.h" #include "log.h" -#define MIN_SUPPORTED_VERSION "9.4" -#define MIN_SUPPORTED_VERSION_NUM 90400 +#define MIN_SUPPORTED_VERSION "9.3" +#define MIN_SUPPORTED_VERSION_NUM 90300 #define REPLICATION_TYPE_PHYSICAL 1 #define REPLICATION_TYPE_BDR 2