commit a3b8853d79498d9e0b6b8596abf6650237065a11
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date:   Fri Mar 22 13:02:59 2013 +0200

    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.

diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index 578541a..9fe440a 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.
   </para>
+
+  <para>
+   <application>pg_basebackup</application> 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.
+  </para>
  </refsect1>
 
  <refsect1>
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index eacb592..4558506 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
@@ -1239,6 +1243,31 @@ BaseBackup(void)
 		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
 	 */
 	if (writerecoveryconf)
@@ -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 e68f8ea..34869b8 100644
--- a/src/bin/pg_basebackup/pg_receivexlog.c
+++ b/src/bin/pg_basebackup/pg_receivexlog.c
@@ -220,6 +220,9 @@ StreamLog(void)
 	uint32		servertli;
 	uint32		hi,
 				lo;
+	int			minServerMajor,
+				maxServerMajor;
+	int			serverMajor;
 
 	/*
 	 * Connect in replication mode to the server
@@ -229,6 +232,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 1f7611f..2bf4df9 100644
--- a/src/bin/pg_basebackup/receivelog.c
+++ b/src/bin/pg_basebackup/receivelog.c
@@ -437,6 +437,40 @@ sendFeedback(PGconn *conn, XLogRecPtr blockpos, int64 now, bool replyRequested)
 }
 
 /*
+ * 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.
  *
  * If sysidentifier is specified, validate that both the system
@@ -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 53f31a7..7c983cd 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,
