From d298b50a3b469c088bb40a4d36d38111b4cd574d Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Fri, 22 Mar 2013 13:02:59 +0200 Subject: [PATCH] Make pg_basebackup work with pre-9.3 servers, and add server version check. A new 'starttli' field was added to the response of BASE_BACKUP command. Make pg_basebackup tolerate the case that it's missing, so that it still works with older servers. Add an explicit check for the server version, so that you get a nicer error message if you try to use it with a pre-9.1 server. The streaming protocol message format changed in 9.3, so -X stream still won't work with pre-9.3 servers. I added a version check to ReceiveXLogStream() earlier, but write that slightly differently, so that in 9.4, it will still work with a 9.3 server. (In 9.4, the error message needs to be adjusted to "9.3 or above", though). Also, if the version check fails, don't retry. --- doc/src/sgml/ref/pg_basebackup.sgml | 6 ++++ src/bin/pg_basebackup/pg_basebackup.c | 42 ++++++++++++++++++++-- src/bin/pg_basebackup/pg_receivexlog.c | 10 ++++++ src/bin/pg_basebackup/receivelog.c | 48 ++++++++++++++++++++------ src/bin/pg_basebackup/receivelog.h | 1 + 5 files changed, 93 insertions(+), 14 deletions(-) diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml index 578541a1e2..9fe440a66d 100644 --- a/doc/src/sgml/ref/pg_basebackup.sgml +++ b/doc/src/sgml/ref/pg_basebackup.sgml @@ -520,6 +520,12 @@ PostgreSQL documentation for all additional tablespaces must be identical whenever a backup is restored. The main data directory, however, is relocatable to any location. + + + pg_basebackup works with servers of the same + or an older major version, down to 9.1. However, WAL streaming mode (-X + stream) only works with server version 9.3. + diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index eacb5922a2..45585069a5 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -1223,12 +1223,16 @@ BaseBackup(void) { PGresult *res; char *sysidentifier; + uint32 latesttli; uint32 starttli; char current_path[MAXPGPATH]; char escaped_label[MAXPGPATH]; int i; char xlogstart[64]; char xlogend[64]; + int minServerMajor, + maxServerMajor; + int serverMajor; /* * Connect in replication mode to the server @@ -1238,6 +1242,31 @@ BaseBackup(void) /* Error message already written in GetConnection() */ exit(1); + /* + * Check server version. BASE_BACKUP command was introduced in 9.1, so + * we can't work with servers older than 9.1. + */ + minServerMajor = 901; + maxServerMajor = PG_VERSION_NUM / 100; + serverMajor = PQserverVersion(conn) / 100; + if (serverMajor < minServerMajor || serverMajor > maxServerMajor) + { + const char *serverver = PQparameterStatus(conn, "server_version"); + fprintf(stderr, _("%s: incompatible server version %s\n"), + progname, serverver ? serverver : "'unknown'"); + disconnect_and_exit(1); + } + + /* + * If WAL streaming was requested, also check that the server is new + * enough for that. + */ + if (streamwal && !CheckServerVersionForStreaming(conn)) + { + /* Error message already written in CheckServerVersionForStreaming() */ + disconnect_and_exit(1); + } + /* * Build contents of recovery.conf if requested */ @@ -1262,6 +1291,7 @@ BaseBackup(void) disconnect_and_exit(1); } sysidentifier = pg_strdup(PQgetvalue(res, 0, 0)); + latesttli = atoi(PQgetvalue(res, 0, 1)); PQclear(res); /* @@ -1293,7 +1323,7 @@ BaseBackup(void) progname, PQerrorMessage(conn)); disconnect_and_exit(1); } - if (PQntuples(res) != 1 || PQnfields(res) < 2) + if (PQntuples(res) != 1) { fprintf(stderr, _("%s: server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d rows and %d fields\n"), @@ -1302,8 +1332,14 @@ BaseBackup(void) } strcpy(xlogstart, PQgetvalue(res, 0, 0)); - starttli = atoi(PQgetvalue(res, 0, 1)); - + /* + * 9.3 and later sends the TLI of the starting point. With older servers, + * assume it's the same as the latest timeline reported by IDENTIFY_SYSTEM. + */ + if (PQnfields(res) >= 2) + starttli = atoi(PQgetvalue(res, 0, 1)); + else + starttli = latesttli; PQclear(res); MemSet(xlogend, 0, sizeof(xlogend)); diff --git a/src/bin/pg_basebackup/pg_receivexlog.c b/src/bin/pg_basebackup/pg_receivexlog.c index e68f8ea707..e4da799d1f 100644 --- a/src/bin/pg_basebackup/pg_receivexlog.c +++ b/src/bin/pg_basebackup/pg_receivexlog.c @@ -229,6 +229,16 @@ StreamLog(void) /* Error message already written in GetConnection() */ return; + if (!CheckServerVersionForStreaming(conn)) + { + /* + * Error message already written in CheckServerVersionForStreaming(). + * There's no hope of recovering from a version mismatch, so don't + * retry. + */ + disconnect_and_exit(1); + } + /* * Run IDENTIFY_SYSTEM so we can get the timeline and current xlog * position. diff --git a/src/bin/pg_basebackup/receivelog.c b/src/bin/pg_basebackup/receivelog.c index 1f7611f444..2bf4df961e 100644 --- a/src/bin/pg_basebackup/receivelog.c +++ b/src/bin/pg_basebackup/receivelog.c @@ -436,6 +436,40 @@ sendFeedback(PGconn *conn, XLogRecPtr blockpos, int64 now, bool replyRequested) return true; } +/* + * Check that the server version we're connected to is supported by + * ReceiveXlogStream(). + * + * If it's not, an error message is printed to stderr, and false is returned. + */ +bool +CheckServerVersionForStreaming(PGconn *conn) +{ + int minServerMajor, + maxServerMajor; + int serverMajor; + + /* + * The message format used in streaming replication changed in 9.3, so we + * cannot stream from older servers. And we don't support servers newer + * than the client; it might work, but we don't know, so err on the safe + * side. + */ + minServerMajor = 903; + maxServerMajor = PG_VERSION_NUM / 100; + serverMajor = PQserverVersion(conn) / 100; + if (serverMajor < minServerMajor || serverMajor > maxServerMajor) + { + const char *serverver = PQparameterStatus(conn, "server_version"); + fprintf(stderr, _("%s: incompatible server version %s; streaming is only supported with server version %s\n"), + progname, + serverver ? serverver : "'unknown'", + "9.3"); + return false; + } + return true; +} + /* * Receive a log stream starting at the specified position. * @@ -476,19 +510,11 @@ ReceiveXlogStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, XLogRecPtr stoppos; /* - * The message format used in streaming replication changed in 9.3, so we - * cannot stream from older servers. Don't know if we would work with - * newer versions, but let's not take the risk. + * The caller should've checked the server version already, but doesn't do + * any harm to check it here too. */ - if (PQserverVersion(conn) / 100 != PG_VERSION_NUM / 100) - { - const char *serverver = PQparameterStatus(conn, "server_version"); - fprintf(stderr, _("%s: incompatible server version %s; streaming is only supported with server version %s\n"), - progname, - serverver ? serverver : "'unknown'", - PG_MAJORVERSION); + if (!CheckServerVersionForStreaming(conn)) return false; - } if (sysidentifier != NULL) { diff --git a/src/bin/pg_basebackup/receivelog.h b/src/bin/pg_basebackup/receivelog.h index 53f31a78ec..7c983cd604 100644 --- a/src/bin/pg_basebackup/receivelog.h +++ b/src/bin/pg_basebackup/receivelog.h @@ -6,6 +6,7 @@ */ typedef bool (*stream_stop_callback) (XLogRecPtr segendpos, uint32 timeline, bool segment_finished); +extern bool CheckServerVersionForStreaming(PGconn *conn); extern bool ReceiveXlogStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, -- 2.40.0