On Wed, Mar 27, 2019 at 5:17 PM Tsunakawa, Takayuki <
tsunakawa.ta...@jp.fujitsu.com> wrote:

> I've looked through 0004-0007.  I've only found the following:
>
> (5) 0005
> With this read-only option type, application can connect to
> connecting to a read-only server in the list of hosts, in case
> if there is any read-only servers available, the connection
> attempt fails.
>
> "connecting to" can be removed.
>
> in case if there is any read-only servers
> -> If There's no read only server
>

Thanks for the review.

I corrected all the comments that are raised by you and attached updated
version of patches
along with implementation of WIP in_recovery GUC_REPORT variable. This
patch has used some
of the implementations that are provided earlier in thread [1].

During the implementation of in_recovery configuration variable, I see a
lot of code addition just
for this variable, is this worth it?

[1] -
https://www.postgresql.org/message-id/2239254.dtfY1H9x0t%40hammer.magicstack.net

Regards,
Haribabu Kommi
Fujitsu Australia
From aa812716104b3fbd787dae4483341894c9e5ed9a Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Thu, 21 Feb 2019 23:11:55 +1100
Subject: [PATCH 1/8] Restructure the code to remove duplicate code

The duplicate code logic of checking for the server version
before issuing the SHOW transaction_readonly to find out whether
the server is read-write or not is restructured under a new
connection status, so that duplicate code is removed. This is
required for the next set of patches
---
 doc/src/sgml/libpq.sgml           | 26 +++++---
 src/interfaces/libpq/fe-connect.c | 99 ++++++++++++-------------------
 src/interfaces/libpq/libpq-fe.h   |  3 +-
 3 files changed, 57 insertions(+), 71 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index c1d1b6b2db..6221e359f0 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1576,17 +1576,27 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
      <varlistentry id="libpq-connect-target-session-attrs" xreflabel="target_session_attrs">
       <term><literal>target_session_attrs</literal></term>
       <listitem>
+       <para>
+        The supported options for this parameter are, <literal>any</literal> and
+        <literal>read-write</literal>. The default value of this parameter,
+        <literal>any</literal>, regards all connections as acceptable.
+        If multiple hosts were specified in the connection string, based on the
+        specified value, any remaining servers will be tried before confirming
+        succesful connection or failure.
+       </para>
+
        <para>
         If this parameter is set to <literal>read-write</literal>, only a
         connection in which read-write transactions are accepted by default
-        is considered acceptable.  The query
-        <literal>SHOW transaction_read_only</literal> will be sent upon any
-        successful connection; if it returns <literal>on</literal>, the connection
-        will be closed.  If multiple hosts were specified in the connection
-        string, any remaining servers will be tried just as if the connection
-        attempt had failed.  The default value of this parameter,
-        <literal>any</literal>, regards all connections as acceptable.
-      </para>
+        is considered acceptable.
+       </para>
+
+       <para>
+        To find out whether the server supports read-write transactions are not,
+        query <literal>SHOW transaction_read_only</literal> will be sent upon any
+        successful connection; if it returns <literal>on</literal>, it means server
+        doesn't support read-write transactions.
+       </para>
       </listitem>
     </varlistentry>
     </variablelist>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index e3bf6a7449..c68448786d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3188,6 +3188,43 @@ keep_going:						/* We will come back to here until there is
 					return PGRES_POLLING_WRITING;
 				}
 
+				conn->status = CONNECTION_CHECK_TARGET;
+				goto keep_going;
+			}
+
+		case CONNECTION_SETENV:
+			{
+
+				/*
+				 * Do post-connection housekeeping (only needed in protocol 2.0).
+				 *
+				 * We pretend that the connection is OK for the duration of these
+				 * queries.
+				 */
+				conn->status = CONNECTION_OK;
+
+				switch (pqSetenvPoll(conn))
+				{
+					case PGRES_POLLING_OK:	/* Success */
+						break;
+
+					case PGRES_POLLING_READING: /* Still going */
+						conn->status = CONNECTION_SETENV;
+						return PGRES_POLLING_READING;
+
+					case PGRES_POLLING_WRITING: /* Still going */
+						conn->status = CONNECTION_SETENV;
+						return PGRES_POLLING_WRITING;
+
+					default:
+						goto error_return;
+				}
+			}
+
+			/* Intentional fall through */
+
+		case CONNECTION_CHECK_TARGET:
+			{
 				/*
 				 * If a read-write connection is required, see if we have one.
 				 *
@@ -3229,68 +3266,6 @@ keep_going:						/* We will come back to here until there is
 				return PGRES_POLLING_OK;
 			}
 
-		case CONNECTION_SETENV:
-
-			/*
-			 * Do post-connection housekeeping (only needed in protocol 2.0).
-			 *
-			 * We pretend that the connection is OK for the duration of these
-			 * queries.
-			 */
-			conn->status = CONNECTION_OK;
-
-			switch (pqSetenvPoll(conn))
-			{
-				case PGRES_POLLING_OK:	/* Success */
-					break;
-
-				case PGRES_POLLING_READING: /* Still going */
-					conn->status = CONNECTION_SETENV;
-					return PGRES_POLLING_READING;
-
-				case PGRES_POLLING_WRITING: /* Still going */
-					conn->status = CONNECTION_SETENV;
-					return PGRES_POLLING_WRITING;
-
-				default:
-					goto error_return;
-			}
-
-			/*
-			 * If a read-write connection is required, see if we have one.
-			 * (This should match the stanza in the CONNECTION_AUTH_OK case
-			 * above.)
-			 *
-			 * Servers before 7.4 lack the transaction_read_only GUC, but by
-			 * the same token they don't have any read-only mode, so we may
-			 * just skip the test in that case.
-			 */
-			if (conn->sversion >= 70400 &&
-				conn->target_session_attrs != NULL &&
-				strcmp(conn->target_session_attrs, "read-write") == 0)
-			{
-				if (!saveErrorMessage(conn, &savedMessage))
-					goto error_return;
-
-				conn->status = CONNECTION_OK;
-				if (!PQsendQuery(conn,
-								 "SHOW transaction_read_only"))
-				{
-					restoreErrorMessage(conn, &savedMessage);
-					goto error_return;
-				}
-				conn->status = CONNECTION_CHECK_WRITABLE;
-				restoreErrorMessage(conn, &savedMessage);
-				return PGRES_POLLING_READING;
-			}
-
-			/* We can release the address list now. */
-			release_conn_addrinfo(conn);
-
-			/* We are open for business! */
-			conn->status = CONNECTION_OK;
-			return PGRES_POLLING_OK;
-
 		case CONNECTION_CONSUME:
 			{
 				conn->status = CONNECTION_OK;
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 97bc98b1f3..50cfe266c6 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -65,8 +65,9 @@ typedef enum
 	CONNECTION_NEEDED,			/* Internal state: connect() needed */
 	CONNECTION_CHECK_WRITABLE,	/* Check if we could make a writable
 								 * connection. */
-	CONNECTION_CONSUME			/* Wait for any pending message and consume
+	CONNECTION_CONSUME,			/* Wait for any pending message and consume
 								 * them. */
+	CONNECTION_CHECK_TARGET		/* Check if we have a proper target connection */
 } ConnStatusType;
 
 typedef enum
-- 
2.20.1.windows.1

From ebfa1b1e253aa661261760095d1a75293118161e Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Wed, 27 Feb 2019 11:50:33 +1100
Subject: [PATCH 2/8] New TargetSessionAttrsType enum

This new enum is useful to compare the requested session type
instead of comparing it with string always. This may not show
much improvement with current code, but it will be useful with
further patches
---
 src/interfaces/libpq/fe-connect.c | 12 ++++++++----
 src/interfaces/libpq/libpq-fe.h   |  6 ++++++
 src/interfaces/libpq/libpq-int.h  |  1 +
 3 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index c68448786d..1dc22e6bdf 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1243,8 +1243,11 @@ connectOptions2(PGconn *conn)
 	 */
 	if (conn->target_session_attrs)
 	{
-		if (strcmp(conn->target_session_attrs, "any") != 0
-			&& strcmp(conn->target_session_attrs, "read-write") != 0)
+		if (strcmp(conn->target_session_attrs, "any") == 0)
+			conn->requested_session_type = SESSION_TYPE_ANY;
+		else if (strcmp(conn->target_session_attrs, "read-write") == 0)
+			conn->requested_session_type = SESSION_TYPE_READ_WRITE;
+		else
 		{
 			conn->status = CONNECTION_BAD;
 			printfPQExpBuffer(&conn->errorMessage,
@@ -3233,8 +3236,7 @@ keep_going:						/* We will come back to here until there is
 				 * may just skip the test in that case.
 				 */
 				if (conn->sversion >= 70400 &&
-					conn->target_session_attrs != NULL &&
-					strcmp(conn->target_session_attrs, "read-write") == 0)
+					conn->requested_session_type != SESSION_TYPE_ANY)
 				{
 					/*
 					 * Save existing error messages across the PQsendQuery
@@ -3540,6 +3542,8 @@ makeEmptyPGconn(void)
 	conn->show_context = PQSHOW_CONTEXT_ERRORS;
 	conn->sock = PGINVALID_SOCKET;
 
+	conn->requested_session_type = SESSION_TYPE_ANY;
+
 	/*
 	 * We try to send at least 8K at a time, which is the usual size of pipe
 	 * buffers on Unix systems.  That way, when we are sending a large amount
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 50cfe266c6..6961d7ce8e 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -70,6 +70,12 @@ typedef enum
 	CONNECTION_CHECK_TARGET		/* Check if we have a proper target connection */
 } ConnStatusType;
 
+typedef enum
+{
+	SESSION_TYPE_ANY = 0,		/* Any session (default) */
+	SESSION_TYPE_READ_WRITE		/* Read-write session */
+}			TargetSessionAttrsType;
+
 typedef enum
 {
 	PGRES_POLLING_FAILED = 0,
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index dbe0f7e5c0..551120660c 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -365,6 +365,7 @@ struct pg_conn
 
 	/* Type of connection to make.  Possible values: any, read-write. */
 	char	   *target_session_attrs;
+	TargetSessionAttrsType requested_session_type;
 
 	/* Optional file to write trace info to */
 	FILE	   *Pfdebug;
-- 
2.20.1.windows.1

From 45015c7271300e2a607d8a1211de361e8895f555 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Wed, 27 Mar 2019 17:05:24 +1100
Subject: [PATCH 3/8] Make transaction_read_only as GUC_REPORT varaible

transaction_read_only GUC variable value is used in multi host
connection to identify the required host of read-write, but currently
this carried out by executing a command to find out whether the host
is a read-write or not? Instead of that, Reporting the GUC to the client
upon connection reduces the time to make the connection.
---
 doc/src/sgml/libpq.sgml           | 14 ++++---
 doc/src/sgml/protocol.sgml        |  8 ++--
 src/backend/utils/misc/guc.c      |  2 +-
 src/interfaces/libpq/fe-connect.c | 70 +++++++++++++++++++++++--------
 src/interfaces/libpq/fe-exec.c    |  6 ++-
 src/interfaces/libpq/libpq-int.h  |  1 +
 6 files changed, 74 insertions(+), 27 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 6221e359f0..fc5eec8450 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1594,8 +1594,10 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
        <para>
         To find out whether the server supports read-write transactions are not,
         query <literal>SHOW transaction_read_only</literal> will be sent upon any
-        successful connection; if it returns <literal>on</literal>, it means server
-        doesn't support read-write transactions.
+        successful connection if the server is prior to version 12; if it returns
+        <literal>on</literal>, it means server doesn't support read-write transactions.
+        But for server version 12 or greater uses the value of <varname>transaction_read_only</varname>
+        configuration parameter that is reported by the server upon successful connection.
        </para>
       </listitem>
     </varlistentry>
@@ -1961,14 +1963,16 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName);
        <varname>DateStyle</varname>,
        <varname>IntervalStyle</varname>,
        <varname>TimeZone</varname>,
-       <varname>integer_datetimes</varname>, and
-       <varname>standard_conforming_strings</varname>.
+       <varname>integer_datetimes</varname>,
+       <varname>standard_conforming_strings</varname>, and
+       <varname>transaction_read_only</varname>.
        (<varname>server_encoding</varname>, <varname>TimeZone</varname>, and
        <varname>integer_datetimes</varname> were not reported by releases before 8.0;
        <varname>standard_conforming_strings</varname> was not reported by releases
        before 8.1;
        <varname>IntervalStyle</varname> was not reported by releases before 8.4;
-       <varname>application_name</varname> was not reported by releases before 9.0.)
+       <varname>application_name</varname> was not reported by releases before 9.0;
+       <varname>transaction_read_only</varname> was not reported by release before 12.0.)
        Note that
        <varname>server_version</varname>,
        <varname>server_encoding</varname> and
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index d66b860cbd..f3357b5c59 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1283,14 +1283,16 @@ SELCT 1/0;
     <varname>DateStyle</varname>,
     <varname>IntervalStyle</varname>,
     <varname>TimeZone</varname>,
-    <varname>integer_datetimes</varname>, and
-    <varname>standard_conforming_strings</varname>.
+    <varname>integer_datetimes</varname>,
+    <varname>standard_conforming_strings</varname>, and
+    <varname>transaction_read_only</varname>.
     (<varname>server_encoding</varname>, <varname>TimeZone</varname>, and
     <varname>integer_datetimes</varname> were not reported by releases before 8.0;
     <varname>standard_conforming_strings</varname> was not reported by releases
     before 8.1;
     <varname>IntervalStyle</varname> was not reported by releases before 8.4;
-    <varname>application_name</varname> was not reported by releases before 9.0.)
+    <varname>application_name</varname> was not reported by releases before 9.0;
+    <varname>transaction_read_only</varname> was not reported by releases before 12.0.)
     Note that
     <varname>server_version</varname>,
     <varname>server_encoding</varname> and
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index aa564d153a..d464be2c5b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1503,7 +1503,7 @@ static struct config_bool ConfigureNamesBool[] =
 		{"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
 			gettext_noop("Sets the current transaction's read-only status."),
 			NULL,
-			GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+			GUC_REPORT | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
 		},
 		&XactReadOnly,
 		false,
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 1dc22e6bdf..0387d4fd09 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3238,26 +3238,61 @@ keep_going:						/* We will come back to here until there is
 				if (conn->sversion >= 70400 &&
 					conn->requested_session_type != SESSION_TYPE_ANY)
 				{
-					/*
-					 * Save existing error messages across the PQsendQuery
-					 * attempt.  This is necessary because PQsendQuery is
-					 * going to reset conn->errorMessage, so we would lose
-					 * error messages related to previous hosts we have tried
-					 * and failed to connect to.
-					 */
-					if (!saveErrorMessage(conn, &savedMessage))
-						goto error_return;
-
-					conn->status = CONNECTION_OK;
-					if (!PQsendQuery(conn,
-									 "SHOW transaction_read_only"))
+					if (conn->sversion < 120000)
 					{
+						/*
+						 * Save existing error messages across the PQsendQuery
+						 * attempt.  This is necessary because PQsendQuery is
+						 * going to reset conn->errorMessage, so we would lose
+						 * error messages related to previous hosts we have tried
+						 * and failed to connect to.
+						 */
+						if (!saveErrorMessage(conn, &savedMessage))
+							goto error_return;
+
+						conn->status = CONNECTION_OK;
+						if (!PQsendQuery(conn,
+										 "SHOW transaction_read_only"))
+						{
+							restoreErrorMessage(conn, &savedMessage);
+							goto error_return;
+						}
+						conn->status = CONNECTION_CHECK_WRITABLE;
 						restoreErrorMessage(conn, &savedMessage);
-						goto error_return;
+						return PGRES_POLLING_READING;
+					}
+					else if (conn->transaction_read_only)
+					{
+						/* Not writable; fail this connection. */
+						const char *displayed_host;
+						const char *displayed_port;
+
+						/* Append error report to conn->errorMessage. */
+						if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+							displayed_host = conn->connhost[conn->whichhost].hostaddr;
+						else
+							displayed_host = conn->connhost[conn->whichhost].host;
+						displayed_port = conn->connhost[conn->whichhost].port;
+						if (displayed_port == NULL || displayed_port[0] == '\0')
+							displayed_port = DEF_PGPORT_STR;
+
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not make a writable "
+														"connection to server "
+														"\"%s:%s\"\n"),
+										  displayed_host, displayed_port);
+
+						/* Close connection politely. */
+						conn->status = CONNECTION_OK;
+						sendTerminateConn(conn);
+
+						/*
+						 * Try next host if any, but we don't want to consider
+						 * additional addresses for this host.
+						 */
+						conn->try_next_host = true;
+						goto keep_going;
 					}
-					conn->status = CONNECTION_CHECK_WRITABLE;
-					restoreErrorMessage(conn, &savedMessage);
-					return PGRES_POLLING_READING;
 				}
 
 				/* We can release the address list now. */
@@ -3538,6 +3573,7 @@ makeEmptyPGconn(void)
 	conn->setenv_state = SETENV_STATE_IDLE;
 	conn->client_encoding = PG_SQL_ASCII;
 	conn->std_strings = false;	/* unless server says differently */
+	conn->transaction_read_only = false;
 	conn->verbosity = PQERRORS_DEFAULT;
 	conn->show_context = PQSHOW_CONTEXT_ERRORS;
 	conn->sock = PGINVALID_SOCKET;
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 6202653826..d2658efba5 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1059,7 +1059,7 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
 	}
 
 	/*
-	 * Special hacks: remember client_encoding and
+	 * Special hacks: remember client_encoding, transaction_read_only and
 	 * standard_conforming_strings, and convert server version to a numeric
 	 * form.  We keep the first two of these in static variables as well, so
 	 * that PQescapeString and PQescapeBytea can behave somewhat sanely (at
@@ -1113,6 +1113,10 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
 		else
 			conn->sversion = 0; /* unknown */
 	}
+	else if (strcmp(name, "transaction_read_only") == 0)
+	{
+		conn->transaction_read_only = (strcmp(value, "on") == 0);
+	}
 }
 
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 551120660c..1154926391 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -430,6 +430,7 @@ struct pg_conn
 	pgParameterStatus *pstatus; /* ParameterStatus data */
 	int			client_encoding;	/* encoding id */
 	bool		std_strings;	/* standard_conforming_strings */
+	bool		transaction_read_only;	/* transaction_read_only */
 	PGVerbosity verbosity;		/* error/notice message verbosity */
 	PGContextVisibility show_context;	/* whether to show CONTEXT field */
 	PGlobjfuncs *lobjfuncs;		/* private state for large-object access fns */
-- 
2.20.1.windows.1

From f7d15841528091b266da0ef43b47d1f4da21ed8d Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Wed, 27 Mar 2019 17:56:48 +1100
Subject: [PATCH 4/8] New prefer-read target_session_attrs type

With this prefer-read option type, application can prefer
connecting to a read-only server if available from the list
of hosts, otherwise connect it to read-write server
---
 doc/src/sgml/libpq.sgml               |  21 ++--
 src/interfaces/libpq/fe-connect.c     | 161 ++++++++++++++++++++++----
 src/interfaces/libpq/libpq-fe.h       |   3 +-
 src/interfaces/libpq/libpq-int.h      |  13 ++-
 src/test/recovery/t/001_stream_rep.pl |  14 ++-
 5 files changed, 177 insertions(+), 35 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index fc5eec8450..51b3e5a6f0 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1577,12 +1577,12 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       <term><literal>target_session_attrs</literal></term>
       <listitem>
        <para>
-        The supported options for this parameter are, <literal>any</literal> and
-        <literal>read-write</literal>. The default value of this parameter,
-        <literal>any</literal>, regards all connections as acceptable.
-        If multiple hosts were specified in the connection string, based on the
-        specified value, any remaining servers will be tried before confirming
-        succesful connection or failure.
+        The supported options for this parameter are, <literal>any</literal>,
+        <literal>read-write</literal> and <literal>prefer-read</literal>.
+        The default value of this parameter, <literal>any</literal>, regards
+        all connections as acceptable. If multiple hosts were specified in the
+        connection string, based on the specified value, any remaining servers
+        will be tried before confirming succesful connection or failure.
        </para>
 
        <para>
@@ -1591,6 +1591,13 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         is considered acceptable.
        </para>
 
+       <para>
+        If this parameter is set to <literal>prefer-read</literal>, only a
+        connection in which read-only transactions are accepted by default
+        is preferred. If no such connections can be found, then a connection
+        in which read-write transactions accepted will be considered.
+       </para>
+
        <para>
         To find out whether the server supports read-write transactions are not,
         query <literal>SHOW transaction_read_only</literal> will be sent upon any
@@ -1600,7 +1607,7 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         configuration parameter that is reported by the server upon successful connection.
        </para>
       </listitem>
-    </varlistentry>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 0387d4fd09..4359a3e152 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -322,7 +322,7 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 
 	{"target_session_attrs", "PGTARGETSESSIONATTRS",
 		DefaultTargetSessionAttrs, NULL,
-		"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
+		"Target-Session-Attrs", "", 12, /* sizeof("prefer-read") = 12 */
 	offsetof(struct pg_conn, target_session_attrs)},
 
 	/* Terminating entry --- MUST BE LAST */
@@ -1247,6 +1247,8 @@ connectOptions2(PGconn *conn)
 			conn->requested_session_type = SESSION_TYPE_ANY;
 		else if (strcmp(conn->target_session_attrs, "read-write") == 0)
 			conn->requested_session_type = SESSION_TYPE_READ_WRITE;
+		else if (strcmp(conn->target_session_attrs, "prefer-read") == 0)
+			conn->requested_session_type = SESSION_TYPE_PREFER_READ;
 		else
 		{
 			conn->status = CONNECTION_BAD;
@@ -2141,13 +2143,31 @@ keep_going:						/* We will come back to here until there is
 
 		if (conn->whichhost + 1 >= conn->nconnhost)
 		{
-			/*
-			 * Oops, no more hosts.  An appropriate error message is already
-			 * set up, so just set the right status.
-			 */
-			goto error_return;
+			if (conn->read_write_host_index >= 0)
+			{
+				/*
+				 * Getting here means, failed to connect to read-only servers
+				 * and now try connect to read-write server again.
+				 */
+				conn->whichhost = conn->read_write_host_index;
+
+				/*
+				 * Reset the host index value to avoid recursion during the
+				 * second connection attempt.
+				 */
+				conn->read_write_host_index = -2;
+			}
+			else
+			{
+				/*
+				 * Oops, no more hosts.  An appropriate error message is
+				 * already set up, so just set the right status.
+				 */
+				goto error_return;
+			}
 		}
-		conn->whichhost++;
+		else
+			conn->whichhost++;
 
 		/* Drop any address info for previous host */
 		release_conn_addrinfo(conn);
@@ -3229,7 +3249,8 @@ keep_going:						/* We will come back to here until there is
 		case CONNECTION_CHECK_TARGET:
 			{
 				/*
-				 * If a read-write connection is required, see if we have one.
+				 * If a read-write or prefer-read connection is required, see
+				 * if we have one.
 				 *
 				 * Servers before 7.4 lack the transaction_read_only GUC, but
 				 * by the same token they don't have any read-only mode, so we
@@ -3244,8 +3265,8 @@ keep_going:						/* We will come back to here until there is
 						 * Save existing error messages across the PQsendQuery
 						 * attempt.  This is necessary because PQsendQuery is
 						 * going to reset conn->errorMessage, so we would lose
-						 * error messages related to previous hosts we have tried
-						 * and failed to connect to.
+						 * error messages related to previous hosts we have
+						 * tried and failed to connect to.
 						 */
 						if (!saveErrorMessage(conn, &savedMessage))
 							goto error_return;
@@ -3257,16 +3278,30 @@ keep_going:						/* We will come back to here until there is
 							restoreErrorMessage(conn, &savedMessage);
 							goto error_return;
 						}
+
 						conn->status = CONNECTION_CHECK_WRITABLE;
+
 						restoreErrorMessage(conn, &savedMessage);
 						return PGRES_POLLING_READING;
 					}
-					else if (conn->transaction_read_only)
+					else if ((conn->transaction_read_only &&
+							  conn->requested_session_type == SESSION_TYPE_READ_WRITE) ||
+							 (!conn->transaction_read_only &&
+							  conn->requested_session_type == SESSION_TYPE_PREFER_READ))
 					{
-						/* Not writable; fail this connection. */
+						/* Not a requested type; fail this connection. */
 						const char *displayed_host;
 						const char *displayed_port;
 
+						/*
+						 * The following scenario is possible only for the
+						 * prefer-read mode for the next pass of the list of
+						 * connections as it couldn't find any servers that
+						 * are default read-only.
+						 */
+						if (conn->read_write_host_index == -2)
+							goto consume_checked_target_connection;
+
 						/* Append error report to conn->errorMessage. */
 						if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
 							displayed_host = conn->connhost[conn->whichhost].hostaddr;
@@ -3276,16 +3311,28 @@ keep_going:						/* We will come back to here until there is
 						if (displayed_port == NULL || displayed_port[0] == '\0')
 							displayed_port = DEF_PGPORT_STR;
 
-						appendPQExpBuffer(&conn->errorMessage,
-										  libpq_gettext("could not make a writable "
-														"connection to server "
-														"\"%s:%s\"\n"),
-										  displayed_host, displayed_port);
+						if (conn->requested_session_type == SESSION_TYPE_READ_WRITE)
+							appendPQExpBuffer(&conn->errorMessage,
+											  libpq_gettext("could not make a writable "
+															"connection to server "
+															"\"%s:%s\"\n"),
+											  displayed_host, displayed_port);
+						else
+							appendPQExpBuffer(&conn->errorMessage,
+											  libpq_gettext("could not make a readonly "
+															"connection to server "
+															"\"%s:%s\"\n"),
+											  displayed_host, displayed_port);
 
 						/* Close connection politely. */
 						conn->status = CONNECTION_OK;
 						sendTerminateConn(conn);
 
+						/* Record read-write host index */
+						if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
+							conn->read_write_host_index == -1)
+							conn->read_write_host_index = conn->whichhost;
+
 						/*
 						 * Try next host if any, but we don't want to consider
 						 * additional addresses for this host.
@@ -3293,8 +3340,36 @@ keep_going:						/* We will come back to here until there is
 						conn->try_next_host = true;
 						goto keep_going;
 					}
+
+					/* obtained the requested type, consume it */
+					goto consume_checked_target_connection;
+				}
+
+				/*
+				 * Requested type is prefer-read, then record this host index
+				 * and try the other before considering it later
+				 */
+				if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
+					conn->read_write_host_index != -2)
+				{
+					/* Close connection politely. */
+					conn->status = CONNECTION_OK;
+					sendTerminateConn(conn);
+
+					/* Record read-write host index */
+					if (conn->read_write_host_index == -1)
+						conn->read_write_host_index = conn->whichhost;
+
+					/*
+					 * Try next host if any, but we don't want to consider
+					 * additional addresses for this host.
+					 */
+					conn->try_next_host = true;
+					goto keep_going;
 				}
 
+		consume_checked_target_connection:
+
 				/* We can release the address list now. */
 				release_conn_addrinfo(conn);
 
@@ -3360,11 +3435,33 @@ keep_going:						/* We will come back to here until there is
 					PQntuples(res) == 1)
 				{
 					char	   *val;
+					bool		readonly_server;
 
 					val = PQgetvalue(res, 0, 0);
-					if (strncmp(val, "on", 2) == 0)
+					readonly_server = (strncmp(val, "on", 2) == 0);
+
+					/*
+					 * Server is read-only and requested mode is read-write,
+					 * ignore it. Server is read-write and requested mode is
+					 * prefer-read, record it for the first time and try to
+					 * consume in the next scan (it means no read-only server
+					 * is found in the first scan).
+					 */
+					if ((readonly_server &&
+						 conn->requested_session_type == SESSION_TYPE_READ_WRITE) ||
+						(!readonly_server &&
+						 conn->requested_session_type == SESSION_TYPE_PREFER_READ))
 					{
-						/* Not writable; fail this connection. */
+						/*
+						 * The following scenario is possible only for the
+						 * prefer-read mode for the next pass of the list of
+						 * connections as it couldn't find any servers that
+						 * are default read-only.
+						 */
+						if (conn->read_write_host_index == -2)
+							goto consume_checked_write_connection;
+
+						/* Not a requested type; fail this connection. */
 						PQclear(res);
 						restoreErrorMessage(conn, &savedMessage);
 
@@ -3377,16 +3474,28 @@ keep_going:						/* We will come back to here until there is
 						if (displayed_port == NULL || displayed_port[0] == '\0')
 							displayed_port = DEF_PGPORT_STR;
 
-						appendPQExpBuffer(&conn->errorMessage,
-										  libpq_gettext("could not make a writable "
-														"connection to server "
-														"\"%s:%s\"\n"),
-										  displayed_host, displayed_port);
+						if (conn->requested_session_type == SESSION_TYPE_READ_WRITE)
+							appendPQExpBuffer(&conn->errorMessage,
+											  libpq_gettext("could not make a writable "
+															"connection to server "
+															"\"%s:%s\"\n"),
+											  displayed_host, displayed_port);
+						else
+							appendPQExpBuffer(&conn->errorMessage,
+											  libpq_gettext("could not make a readonly "
+															"connection to server "
+															"\"%s:%s\"\n"),
+											  displayed_host, displayed_port);
 
 						/* Close connection politely. */
 						conn->status = CONNECTION_OK;
 						sendTerminateConn(conn);
 
+						/* Record read-write host index */
+						if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
+							conn->read_write_host_index == -1)
+							conn->read_write_host_index = conn->whichhost;
+
 						/*
 						 * Try next host if any, but we don't want to consider
 						 * additional addresses for this host.
@@ -3395,7 +3504,8 @@ keep_going:						/* We will come back to here until there is
 						goto keep_going;
 					}
 
-					/* Session is read-write, so we're good. */
+			consume_checked_write_connection:
+					/* Session is requested type, so we're good. */
 					PQclear(res);
 					termPQExpBuffer(&savedMessage);
 
@@ -3579,6 +3689,7 @@ makeEmptyPGconn(void)
 	conn->sock = PGINVALID_SOCKET;
 
 	conn->requested_session_type = SESSION_TYPE_ANY;
+	conn->read_write_host_index = -1;
 
 	/*
 	 * We try to send at least 8K at a time, which is the usual size of pipe
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 6961d7ce8e..4b0fc80df2 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -73,7 +73,8 @@ typedef enum
 typedef enum
 {
 	SESSION_TYPE_ANY = 0,		/* Any session (default) */
-	SESSION_TYPE_READ_WRITE		/* Read-write session */
+	SESSION_TYPE_READ_WRITE,	/* Read-write session */
+	SESSION_TYPE_PREFER_READ	/* Prefer read only session */
 }			TargetSessionAttrsType;
 
 typedef enum
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 1154926391..9fbce9888b 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -363,7 +363,10 @@ struct pg_conn
 	char	   *krbsrvname;		/* Kerberos service name */
 #endif
 
-	/* Type of connection to make.  Possible values: any, read-write. */
+	/*
+	 * Type of connection to make.  Possible values: any, read-write,
+	 * prefer-read.
+	 */
 	char	   *target_session_attrs;
 	TargetSessionAttrsType requested_session_type;
 
@@ -400,6 +403,14 @@ struct pg_conn
 	pg_conn_host *connhost;		/* details about each named host */
 	char	   *connip;			/* IP address for current network connection */
 
+	/*
+	 * First read-write host index in the connection string.
+	 *
+	 * Initial value is -1, then the index of the first read-write host, -2
+	 * during the second attempt of connection to avoid recursion.
+	 */
+	int			read_write_host_index;
+
 	/* Connection data */
 	pgsocket	sock;			/* FD for socket, PGINVALID_SOCKET if
 								 * unconnected */
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index beb45551a2..0e398136a5 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 26;
+use Test::More tests => 29;
 
 # Initialize master node
 my $node_master = get_new_node('master');
@@ -117,6 +117,18 @@ test_target_session_attrs($node_master, $node_standby_1, $node_master, "any",
 test_target_session_attrs($node_standby_1, $node_master, $node_standby_1,
 	"any", 0);
 
+# Connect to standby1 in "prefer-read" mode with master,standby1 list.
+test_target_session_attrs($node_master, $node_standby_1, $node_standby_1, "prefer-read",
+	0);
+
+# Connect to standby1 in "prefer-read" mode with standby1,master list.
+test_target_session_attrs($node_standby_1, $node_master, $node_standby_1,
+	"prefer-read", 0);
+
+# Connect to node_master in "prefer-read" mode with only master list.
+test_target_session_attrs($node_master, $node_master, $node_master,
+	"prefer-read", 0);
+
 note "switching to physical replication slot";
 
 # Switch to using a physical replication slot. We can do this without a new
-- 
2.20.1.windows.1

From 536bf1d55e8c49ac02136503a8eab15c796f2226 Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Wed, 27 Mar 2019 18:02:59 +1100
Subject: [PATCH 5/8] New read-only target_session_attrs type

With this read-only option type, application can connect to
to a read-only server in the list of hosts, in case
if there is no read-only server available, the connection
attempt fails.
---
 doc/src/sgml/libpq.sgml               |  7 +++++-
 src/interfaces/libpq/fe-connect.c     | 34 ++++++++++++++++++++-------
 src/interfaces/libpq/libpq-fe.h       |  3 ++-
 src/interfaces/libpq/libpq-int.h      |  2 +-
 src/test/recovery/t/001_stream_rep.pl | 10 +++++++-
 5 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 51b3e5a6f0..a47081a265 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1578,7 +1578,7 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       <listitem>
        <para>
         The supported options for this parameter are, <literal>any</literal>,
-        <literal>read-write</literal> and <literal>prefer-read</literal>.
+        <literal>read-write</literal>, <literal>prefer-read</literal> and <literal>read-only</literal>.
         The default value of this parameter, <literal>any</literal>, regards
         all connections as acceptable. If multiple hosts were specified in the
         connection string, based on the specified value, any remaining servers
@@ -1606,6 +1606,11 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         But for server version 12 or greater uses the value of <varname>transaction_read_only</varname>
         configuration parameter that is reported by the server upon successful connection.
        </para>
+
+       <para>
+        If this parameter is set to <literal>read-only</literal>, only a connection
+        in which read-only transactions are accepted by default.
+       </para>
       </listitem>
      </varlistentry>
     </variablelist>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 4359a3e152..668bb9712d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1249,6 +1249,8 @@ connectOptions2(PGconn *conn)
 			conn->requested_session_type = SESSION_TYPE_READ_WRITE;
 		else if (strcmp(conn->target_session_attrs, "prefer-read") == 0)
 			conn->requested_session_type = SESSION_TYPE_PREFER_READ;
+		else if (strcmp(conn->target_session_attrs, "read-only") == 0)
+			conn->requested_session_type = SESSION_TYPE_READ_ONLY;
 		else
 		{
 			conn->status = CONNECTION_BAD;
@@ -3249,8 +3251,8 @@ keep_going:						/* We will come back to here until there is
 		case CONNECTION_CHECK_TARGET:
 			{
 				/*
-				 * If a read-write or prefer-read connection is required, see
-				 * if we have one.
+				 * If a read-write, prefer-read or read-only connection is
+				 * required, see if we have one.
 				 *
 				 * Servers before 7.4 lack the transaction_read_only GUC, but
 				 * by the same token they don't have any read-only mode, so we
@@ -3287,7 +3289,8 @@ keep_going:						/* We will come back to here until there is
 					else if ((conn->transaction_read_only &&
 							  conn->requested_session_type == SESSION_TYPE_READ_WRITE) ||
 							 (!conn->transaction_read_only &&
-							  conn->requested_session_type == SESSION_TYPE_PREFER_READ))
+							  (conn->requested_session_type == SESSION_TYPE_PREFER_READ ||
+							   conn->requested_session_type == SESSION_TYPE_READ_ONLY)))
 					{
 						/* Not a requested type; fail this connection. */
 						const char *displayed_host;
@@ -3347,17 +3350,28 @@ keep_going:						/* We will come back to here until there is
 
 				/*
 				 * Requested type is prefer-read, then record this host index
-				 * and try the other before considering it later
+				 * and try the other before considering it later. If requested
+				 * type of connection is read-only, ignore this connection.
 				 */
-				if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
-					conn->read_write_host_index != -2)
+				if (conn->requested_session_type == SESSION_TYPE_PREFER_READ ||
+					conn->requested_session_type == SESSION_TYPE_READ_ONLY)
 				{
+					/*
+					 * The following scenario is possible only for the
+					 * prefer-read mode for the next pass of the list of
+					 * connections as it couldn't find any servers that are
+					 * default read-only.
+					 */
+					if (conn->read_write_host_index == -2)
+						goto target_accept_connection;
+
 					/* Close connection politely. */
 					conn->status = CONNECTION_OK;
 					sendTerminateConn(conn);
 
 					/* Record read-write host index */
-					if (conn->read_write_host_index == -1)
+					if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
+						conn->read_write_host_index == -1)
 						conn->read_write_host_index = conn->whichhost;
 
 					/*
@@ -3445,12 +3459,14 @@ keep_going:						/* We will come back to here until there is
 					 * ignore it. Server is read-write and requested mode is
 					 * prefer-read, record it for the first time and try to
 					 * consume in the next scan (it means no read-only server
-					 * is found in the first scan).
+					 * is found in the first scan). Server is read-write and
+					 * requested mode is read-only, ignore this connection.
 					 */
 					if ((readonly_server &&
 						 conn->requested_session_type == SESSION_TYPE_READ_WRITE) ||
 						(!readonly_server &&
-						 conn->requested_session_type == SESSION_TYPE_PREFER_READ))
+						 (conn->requested_session_type == SESSION_TYPE_PREFER_READ ||
+						  conn->requested_session_type == SESSION_TYPE_READ_ONLY)))
 					{
 						/*
 						 * The following scenario is possible only for the
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 4b0fc80df2..5d0b885dae 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -74,7 +74,8 @@ typedef enum
 {
 	SESSION_TYPE_ANY = 0,		/* Any session (default) */
 	SESSION_TYPE_READ_WRITE,	/* Read-write session */
-	SESSION_TYPE_PREFER_READ	/* Prefer read only session */
+	SESSION_TYPE_PREFER_READ,	/* Prefer read only session */
+	SESSION_TYPE_READ_ONLY		/* Read only session */
 }			TargetSessionAttrsType;
 
 typedef enum
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 9fbce9888b..79ddd4d886 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -365,7 +365,7 @@ struct pg_conn
 
 	/*
 	 * Type of connection to make.  Possible values: any, read-write,
-	 * prefer-read.
+	 * prefer-read and read-only.
 	 */
 	char	   *target_session_attrs;
 	TargetSessionAttrsType requested_session_type;
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index 0e398136a5..651107f49b 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 29;
+use Test::More tests => 31;
 
 # Initialize master node
 my $node_master = get_new_node('master');
@@ -129,6 +129,14 @@ test_target_session_attrs($node_standby_1, $node_master, $node_standby_1,
 test_target_session_attrs($node_master, $node_master, $node_master,
 	"prefer-read", 0);
 
+# Connect to standby1 in "read-only" mode with master,standby1 list.
+test_target_session_attrs($node_master, $node_standby_1, $node_standby_1,
+	"read-only", 0);
+
+# Connect to standby1 in "read-only" mode with standby1,master list.
+test_target_session_attrs($node_standby_1, $node_master, $node_standby_1,
+	"read-only", 0);
+
 note "switching to physical replication slot";
 
 # Switch to using a physical replication slot. We can do this without a new
-- 
2.20.1.windows.1

From 6802a581641d3082e6a15dbe06c838e64a6a8c2b Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Wed, 27 Mar 2019 18:06:46 +1100
Subject: [PATCH 6/8] Primary, prefer-standby and standby options

New options to check whether the server is in recovery mode
or not, before considering them to connect. To confirm whether
the server is running in recovery mode or not, it sends the query
to server as 'SELECT pg_is_in_recovery()'.
---
 doc/src/sgml/libpq.sgml               |  26 ++-
 src/interfaces/libpq/fe-connect.c     | 236 +++++++++++++++++++++++---
 src/interfaces/libpq/libpq-fe.h       |   8 +-
 src/interfaces/libpq/libpq-int.h      |   4 +-
 src/test/recovery/t/001_stream_rep.pl |  18 +-
 5 files changed, 262 insertions(+), 30 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index a47081a265..328b632e16 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1578,7 +1578,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       <listitem>
        <para>
         The supported options for this parameter are, <literal>any</literal>,
-        <literal>read-write</literal>, <literal>prefer-read</literal> and <literal>read-only</literal>.
+        <literal>read-write</literal>, <literal>prefer-read</literal>, <literal>read-only</literal>,
+        <literal>primary</literal>, <literal>prefer-standby</literal> and <literal>standby</literal>.
         The default value of this parameter, <literal>any</literal>, regards
         all connections as acceptable. If multiple hosts were specified in the
         connection string, based on the specified value, any remaining servers
@@ -1611,6 +1612,29 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         If this parameter is set to <literal>read-only</literal>, only a connection
         in which read-only transactions are accepted by default.
        </para>
+
+       <para>
+        If this parameter is set to <literal>primary</literal>, only a connection in which
+        where the server is not in recovery mode.
+       </para>
+
+       <para>
+        If this parameter is set to <literal>prefer-standby</literal>, only a connection in which
+        where the server is in recovery mode is preferred. If no such connections can be found,
+        then a connection in which server is not in recovery mode will be considered.
+       </para>
+
+       <para>
+        If this parameter is set to <literal>standby</literal>, only a connection in which
+        where the server is in recovery mode.
+       </para>
+
+       <para>
+        To find out whether the server is in recovery mode or not, query <literal>SELECT pg_is_in_recovery()</literal>
+        will be sent upon any successful connection; if it returns <literal>t</literal>, means server
+        is in recovery mode.
+       </para>
+
       </listitem>
      </varlistentry>
     </variablelist>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 668bb9712d..7a75b692c8 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -124,6 +124,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
 #define DefaultOption	""
 #define DefaultAuthtype		  ""
 #define DefaultTargetSessionAttrs	"any"
+#define DefaultTargetServerType	"any"
 #ifdef USE_SSL
 #define DefaultSSLMode "prefer"
 #else
@@ -322,7 +323,7 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 
 	{"target_session_attrs", "PGTARGETSESSIONATTRS",
 		DefaultTargetSessionAttrs, NULL,
-		"Target-Session-Attrs", "", 12, /* sizeof("prefer-read") = 12 */
+		"Target-Session-Attrs", "", 15, /* sizeof("prefer-standby") = 15 */
 	offsetof(struct pg_conn, target_session_attrs)},
 
 	/* Terminating entry --- MUST BE LAST */
@@ -1251,6 +1252,12 @@ connectOptions2(PGconn *conn)
 			conn->requested_session_type = SESSION_TYPE_PREFER_READ;
 		else if (strcmp(conn->target_session_attrs, "read-only") == 0)
 			conn->requested_session_type = SESSION_TYPE_READ_ONLY;
+		else if (strcmp(conn->target_session_attrs, "primary") == 0)
+			conn->requested_session_type = SESSION_TYPE_PRIMARY;
+		else if (strcmp(conn->target_session_attrs, "prefer-standby") == 0)
+			conn->requested_session_type = SESSION_TYPE_PREFER_STANDBY;
+		else if (strcmp(conn->target_session_attrs, "standby") == 0)
+			conn->requested_session_type = SESSION_TYPE_STANDBY;
 		else
 		{
 			conn->status = CONNECTION_BAD;
@@ -2106,6 +2113,7 @@ PQconnectPoll(PGconn *conn)
 		case CONNECTION_NEEDED:
 		case CONNECTION_CHECK_WRITABLE:
 		case CONNECTION_CONSUME:
+		case CONNECTION_CHECK_RECOVERY:
 			break;
 
 		default:
@@ -2145,19 +2153,19 @@ keep_going:						/* We will come back to here until there is
 
 		if (conn->whichhost + 1 >= conn->nconnhost)
 		{
-			if (conn->read_write_host_index >= 0)
+			if (conn->read_write_or_primary_host_index >= 0)
 			{
 				/*
 				 * Getting here means, failed to connect to read-only servers
 				 * and now try connect to read-write server again.
 				 */
-				conn->whichhost = conn->read_write_host_index;
+				conn->whichhost = conn->read_write_or_primary_host_index;
 
 				/*
 				 * Reset the host index value to avoid recursion during the
 				 * second connection attempt.
 				 */
-				conn->read_write_host_index = -2;
+				conn->read_write_or_primary_host_index = -2;
 			}
 			else
 			{
@@ -3259,7 +3267,9 @@ keep_going:						/* We will come back to here until there is
 				 * may just skip the test in that case.
 				 */
 				if (conn->sversion >= 70400 &&
-					conn->requested_session_type != SESSION_TYPE_ANY)
+					(conn->requested_session_type == SESSION_TYPE_READ_WRITE ||
+					 conn->requested_session_type == SESSION_TYPE_PREFER_READ ||
+					 conn->requested_session_type == SESSION_TYPE_READ_ONLY))
 				{
 					if (conn->sversion < 120000)
 					{
@@ -3302,7 +3312,7 @@ keep_going:						/* We will come back to here until there is
 						 * connections as it couldn't find any servers that
 						 * are default read-only.
 						 */
-						if (conn->read_write_host_index == -2)
+						if (conn->read_write_or_primary_host_index == -2)
 							goto consume_checked_target_connection;
 
 						/* Append error report to conn->errorMessage. */
@@ -3333,8 +3343,8 @@ keep_going:						/* We will come back to here until there is
 
 						/* Record read-write host index */
 						if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
-							conn->read_write_host_index == -1)
-							conn->read_write_host_index = conn->whichhost;
+							conn->read_write_or_primary_host_index == -1)
+							conn->read_write_or_primary_host_index = conn->whichhost;
 
 						/*
 						 * Try next host if any, but we don't want to consider
@@ -3349,30 +3359,70 @@ keep_going:						/* We will come back to here until there is
 				}
 
 				/*
-				 * Requested type is prefer-read, then record this host index
-				 * and try the other before considering it later. If requested
-				 * type of connection is read-only, ignore this connection.
+				 * severs before 9.0 don't support recovery, skip the check
+				 * when the requested type of connection is primary,
+				 * prefer-standby or standby.
 				 */
+				else if ((conn->sversion >= 90000 &&
+						  (conn->requested_session_type == SESSION_TYPE_PRIMARY ||
+						   conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY ||
+						   conn->requested_session_type == SESSION_TYPE_STANDBY)))
+				{
+					/*
+					 * Save existing error messages across the PQsendQuery
+					 * attempt.  This is necessary because PQsendQuery is
+					 * going to reset conn->errorMessage, so we would lose
+					 * error messages related to previous hosts we have tried
+					 * and failed to connect to.
+					 */
+					if (!saveErrorMessage(conn, &savedMessage))
+						goto error_return;
+
+					conn->status = CONNECTION_OK;
+					if (!PQsendQuery(conn, "SELECT pg_is_in_recovery()"))
+					{
+						restoreErrorMessage(conn, &savedMessage);
+						goto error_return;
+					}
+
+					conn->status = CONNECTION_CHECK_RECOVERY;
+
+					restoreErrorMessage(conn, &savedMessage);
+					return PGRES_POLLING_READING;
+				}
+
+				/*
+				 * Requested type is prefer-read or prefer-standby, then
+				 * record this host index and try the other before considering
+				 * it later. If requested type of connection is read-only or
+				 * standby, ignore this connection.
+				 */
+
 				if (conn->requested_session_type == SESSION_TYPE_PREFER_READ ||
-					conn->requested_session_type == SESSION_TYPE_READ_ONLY)
+					conn->requested_session_type == SESSION_TYPE_READ_ONLY ||
+					conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY ||
+					conn->requested_session_type == SESSION_TYPE_STANDBY)
 				{
 					/*
 					 * The following scenario is possible only for the
-					 * prefer-read mode for the next pass of the list of
-					 * connections as it couldn't find any servers that are
-					 * default read-only.
+					 * prefer-read or prefer-standby mode for the next pass of
+					 * the list of connections as it couldn't find any servers
+					 * that are default read-only or in recovery mode.
 					 */
-					if (conn->read_write_host_index == -2)
-						goto target_accept_connection;
+					if (conn->read_write_or_primary_host_index == -2)
+						goto consume_checked_target_connection;
 
 					/* Close connection politely. */
 					conn->status = CONNECTION_OK;
 					sendTerminateConn(conn);
 
 					/* Record read-write host index */
-					if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
-						conn->read_write_host_index == -1)
-						conn->read_write_host_index = conn->whichhost;
+					if (conn->requested_session_type == SESSION_TYPE_PREFER_READ ||
+						conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY)
+					{
+						if (conn->read_write_or_primary_host_index == -1)
+							conn->read_write_or_primary_host_index = conn->whichhost;
+					}
 
 					/*
 					 * Try next host if any, but we don't want to consider
@@ -3474,7 +3524,7 @@ keep_going:						/* We will come back to here until there is
 						 * connections as it couldn't find any servers that
 						 * are default read-only.
 						 */
-						if (conn->read_write_host_index == -2)
+						if (conn->read_write_or_primary_host_index == -2)
 							goto consume_checked_write_connection;
 
 						/* Not a requested type; fail this connection. */
@@ -3509,8 +3559,8 @@ keep_going:						/* We will come back to here until there is
 
 						/* Record read-write host index */
 						if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
-							conn->read_write_host_index == -1)
-							conn->read_write_host_index = conn->whichhost;
+							conn->read_write_or_primary_host_index == -1)
+							conn->read_write_or_primary_host_index = conn->whichhost;
 
 						/*
 						 * Try next host if any, but we don't want to consider
@@ -3563,6 +3613,144 @@ keep_going:						/* We will come back to here until there is
 				goto keep_going;
 			}
 
+		case CONNECTION_CHECK_RECOVERY:
+			{
+				const char *displayed_host;
+				const char *displayed_port;
+
+				if (!saveErrorMessage(conn, &savedMessage))
+					goto error_return;
+
+				conn->status = CONNECTION_OK;
+				if (!PQconsumeInput(conn))
+				{
+					restoreErrorMessage(conn, &savedMessage);
+					goto error_return;
+				}
+
+				if (PQisBusy(conn))
+				{
+					conn->status = CONNECTION_CHECK_RECOVERY;
+					restoreErrorMessage(conn, &savedMessage);
+					return PGRES_POLLING_READING;
+				}
+
+				res = PQgetResult(conn);
+				if (res && (PQresultStatus(res) == PGRES_TUPLES_OK) &&
+					PQntuples(res) == 1)
+				{
+					char	   *val;
+					bool		standby_server;
+
+					val = PQgetvalue(res, 0, 0);
+					standby_server = (strncmp(val, "t", 1) == 0);
+
+					/*
+					 * Server is in recovery mode and requested mode is
+					 * primary, ignore it. Server is not in recovery mode and
+					 * requested mode is prefer-standby, record it for the
+					 * first time and try to consume in the next scan (it
+					 * means no standby server is found in the first scan).
+					 */
+					if ((standby_server &&
+						 conn->requested_session_type == SESSION_TYPE_PRIMARY) ||
+						(!standby_server &&
+						 (conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY ||
+						  conn->requested_session_type == SESSION_TYPE_STANDBY)))
+					{
+
+						/*
+						 * The following scenario is possible only for the
+						 * prefer-standby mode for the next pass of the list
+						 * of connections as it couldn't find any servers that
+						 * are in recovery.
+						 */
+						if (conn->read_write_or_primary_host_index == -2)
+							goto consume_checked_recovery_connection;
+
+						/* Not a requested type; fail this connection. */
+						PQclear(res);
+						restoreErrorMessage(conn, &savedMessage);
+
+						/* Append error report to conn->errorMessage. */
+						if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+							displayed_host = conn->connhost[conn->whichhost].hostaddr;
+						else
+							displayed_host = conn->connhost[conn->whichhost].host;
+						displayed_port = conn->connhost[conn->whichhost].port;
+						if (displayed_port == NULL || displayed_port[0] == '\0')
+							displayed_port = DEF_PGPORT_STR;
+
+						if (conn->requested_session_type == SESSION_TYPE_PRIMARY)
+							appendPQExpBuffer(&conn->errorMessage,
+											  libpq_gettext("server is in recovery mode "
+															"\"%s:%s\"\n"),
+											  displayed_host, displayed_port);
+						else
+							appendPQExpBuffer(&conn->errorMessage,
+											  libpq_gettext("server is not in recovery mode "
+															"\"%s:%s\"\n"),
+											  displayed_host, displayed_port);
+
+						/* Close connection politely. */
+						conn->status = CONNECTION_OK;
+						sendTerminateConn(conn);
+
+						/* Record primary host index */
+						if (conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY &&
+							conn->read_write_or_primary_host_index == -1)
+							conn->read_write_or_primary_host_index = conn->whichhost;
+
+						/*
+						 * Try next host if any, but we don't want to consider
+						 * additional addresses for this host.
+						 */
+						conn->try_next_host = true;
+						goto keep_going;
+					}
+
+			consume_checked_recovery_connection:
+					/* Session is requested type, so we're good. */
+					PQclear(res);
+					termPQExpBuffer(&savedMessage);
+
+					/*
+					 * Finish reading any remaining messages before being
+					 * considered as ready.
+					 */
+					conn->status = CONNECTION_CONSUME;
+					goto keep_going;
+				}
+
+				/*
+				 * Something went wrong with "SELECT pg_is_in_recovery()". We
+				 * should try next addresses.
+				 */
+				if (res)
+					PQclear(res);
+				restoreErrorMessage(conn, &savedMessage);
+
+				/* Append error report to conn->errorMessage. */
+				if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+					displayed_host = conn->connhost[conn->whichhost].hostaddr;
+				else
+					displayed_host = conn->connhost[conn->whichhost].host;
+				displayed_port = conn->connhost[conn->whichhost].port;
+				if (displayed_port == NULL || displayed_port[0] == '\0')
+					displayed_port = DEF_PGPORT_STR;
+				appendPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("test \"SELECT pg_is_in_recovery()\" failed "
+												"on server \"%s:%s\"\n"),
+								  displayed_host, displayed_port);
+
+				/* Close connection politely. */
+				conn->status = CONNECTION_OK;
+				sendTerminateConn(conn);
+
+				/* Try next address */
+				conn->try_next_addr = true;
+				goto keep_going;
+			}
 		default:
 			appendPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("invalid connection state %d, "
@@ -3705,7 +3893,7 @@ makeEmptyPGconn(void)
 	conn->sock = PGINVALID_SOCKET;
 
 	conn->requested_session_type = SESSION_TYPE_ANY;
-	conn->read_write_host_index = -1;
+	conn->read_write_or_primary_host_index = -1;
 
 	/*
 	 * We try to send at least 8K at a time, which is the usual size of pipe
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 5d0b885dae..4c1b849019 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -67,7 +67,8 @@ typedef enum
 								 * connection. */
 	CONNECTION_CONSUME,			/* Wait for any pending message and consume
 								 * them. */
-	CONNECTION_CHECK_TARGET		/* Check if we have a proper target connection */
+	CONNECTION_CHECK_TARGET,	/* Check if we have a proper target connection */
+	CONNECTION_CHECK_RECOVERY	/* Check whether server is in recovery */
 } ConnStatusType;
 
 typedef enum
@@ -75,7 +76,10 @@ typedef enum
 	SESSION_TYPE_ANY = 0,		/* Any session (default) */
 	SESSION_TYPE_READ_WRITE,	/* Read-write session */
 	SESSION_TYPE_PREFER_READ,	/* Prefer read only session */
-	SESSION_TYPE_READ_ONLY		/* Read only session */
+	SESSION_TYPE_READ_ONLY,		/* Read only session */
+	SESSION_TYPE_PRIMARY,		/* Primary server */
+	SESSION_TYPE_PREFER_STANDBY,	/* Prefer Standby server */
+	SESSION_TYPE_STANDBY		/* Standby server */
 }			TargetSessionAttrsType;
 
 typedef enum
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 79ddd4d886..d60ee385a6 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -365,7 +365,7 @@ struct pg_conn
 
 	/*
 	 * Type of connection to make.  Possible values: any, read-write,
-	 * prefer-read and read-only.
+	 * prefer-read, read-only, primary, prefer-standby and standby.
 	 */
 	char	   *target_session_attrs;
 	TargetSessionAttrsType requested_session_type;
@@ -409,7 +409,7 @@ struct pg_conn
 	 * Initial value is -1, then the index of the first read-write host, -2
 	 * during the second attempt of connection to avoid recursion.
 	 */
-	int			read_write_host_index;
+	int			read_write_or_primary_host_index;
 
 	/* Connection data */
 	pgsocket	sock;			/* FD for socket, PGINVALID_SOCKET if
diff --git a/src/test/recovery/t/001_stream_rep.pl b/src/test/recovery/t/001_stream_rep.pl
index 651107f49b..f18fef4445 100644
--- a/src/test/recovery/t/001_stream_rep.pl
+++ b/src/test/recovery/t/001_stream_rep.pl
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 31;
+use Test::More tests => 35;
 
 # Initialize master node
 my $node_master = get_new_node('master');
@@ -137,6 +137,22 @@ test_target_session_attrs($node_master, $node_standby_1, $node_standby_1,
 test_target_session_attrs($node_standby_1, $node_master, $node_standby_1,
 	"read-only", 0);
 
+# Connect to master in "primary" mode with standby1,master list.
+test_target_session_attrs($node_standby_1, $node_master, $node_master,
+	"primary", 0);
+
+# Connect to master in "prefer-standby" mode with master,master list.
+test_target_session_attrs($node_master, $node_master, $node_master,
+	"prefer-standby", 0);
+
+# Connect to standby1 in "prefer-standby" mode with master,standby1 list.
+test_target_session_attrs($node_master, $node_standby_1, $node_standby_1,
+	"prefer-standby", 0);
+
+# Connect to standby1 in "standby" mode with master,standby1 list.
+test_target_session_attrs($node_master, $node_standby_1, $node_standby_1,
+	"standby", 0);
+
 note "switching to physical replication slot";
 
 # Switch to using a physical replication slot. We can do this without a new
-- 
2.20.1.windows.1

From cbe3b9461a6fb4fcde4168f5a45ae0e6af0d900a Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Mon, 25 Mar 2019 18:11:18 +1100
Subject: [PATCH 7/8] New function to rejecting the checked write connection

When the connection is checked for write or not and based
on the result, if we decide to reject it, call the newly
added function to reject it.
---
 src/interfaces/libpq/fe-connect.c | 123 ++++++++++++------------------
 1 file changed, 47 insertions(+), 76 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 7a75b692c8..82b80385d1 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2032,6 +2032,51 @@ restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
 	termPQExpBuffer(savedMessage);
 }
 
+static void
+reject_checked_write_connection(PGconn *conn)
+{
+	/* Not a requested type; fail this connection. */
+	const char *displayed_host;
+	const char *displayed_port;
+
+	/* Append error report to conn->errorMessage. */
+	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+		displayed_host = conn->connhost[conn->whichhost].hostaddr;
+	else
+		displayed_host = conn->connhost[conn->whichhost].host;
+	displayed_port = conn->connhost[conn->whichhost].port;
+	if (displayed_port == NULL || displayed_port[0] == '\0')
+		displayed_port = DEF_PGPORT_STR;
+
+	if (conn->requested_session_type == SESSION_TYPE_READ_WRITE)
+		appendPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not make a writable "
+										"connection to server "
+										"\"%s:%s\"\n"),
+						  displayed_host, displayed_port);
+	else
+		appendPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not make a readonly "
+										"connection to server "
+										"\"%s:%s\"\n"),
+						  displayed_host, displayed_port);
+
+	/* Close connection politely. */
+	conn->status = CONNECTION_OK;
+	sendTerminateConn(conn);
+
+	/* Record read-write host index */
+	if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
+		conn->read_write_or_primary_host_index == -1)
+		conn->read_write_or_primary_host_index = conn->whichhost;
+
+	/*
+	 * Try next host if any, but we don't want to consider additional
+	 * addresses for this host.
+	 */
+	conn->try_next_host = true;
+}
+
 /* ----------------
  *		PQconnectPoll
  *
@@ -3302,10 +3347,6 @@ keep_going:						/* We will come back to here until there is
 							  (conn->requested_session_type == SESSION_TYPE_PREFER_READ ||
 							   conn->requested_session_type == SESSION_TYPE_READ_ONLY)))
 					{
-						/* Not a requested type; fail this connection. */
-						const char *displayed_host;
-						const char *displayed_port;
-
 						/*
 						 * The following scenario is possible only for the
 						 * prefer-read mode for the next pass of the list of
@@ -3315,42 +3356,7 @@ keep_going:						/* We will come back to here until there is
 						if (conn->read_write_or_primary_host_index == -2)
 							goto consume_checked_target_connection;
 
-						/* Append error report to conn->errorMessage. */
-						if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-							displayed_host = conn->connhost[conn->whichhost].hostaddr;
-						else
-							displayed_host = conn->connhost[conn->whichhost].host;
-						displayed_port = conn->connhost[conn->whichhost].port;
-						if (displayed_port == NULL || displayed_port[0] == '\0')
-							displayed_port = DEF_PGPORT_STR;
-
-						if (conn->requested_session_type == SESSION_TYPE_READ_WRITE)
-							appendPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("could not make a writable "
-															"connection to server "
-															"\"%s:%s\"\n"),
-											  displayed_host, displayed_port);
-						else
-							appendPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("could not make a readonly "
-															"connection to server "
-															"\"%s:%s\"\n"),
-											  displayed_host, displayed_port);
-
-						/* Close connection politely. */
-						conn->status = CONNECTION_OK;
-						sendTerminateConn(conn);
-
-						/* Record read-write host index */
-						if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
-							conn->read_write_or_primary_host_index == -1)
-							conn->read_write_or_primary_host_index = conn->whichhost;
-
-						/*
-						 * Try next host if any, but we don't want to consider
-						 * additional addresses for this host.
-						 */
-						conn->try_next_host = true;
+						reject_checked_write_connection(conn);
 						goto keep_going;
 					}
 
@@ -3531,42 +3537,7 @@ keep_going:						/* We will come back to here until there is
 						PQclear(res);
 						restoreErrorMessage(conn, &savedMessage);
 
-						/* Append error report to conn->errorMessage. */
-						if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-							displayed_host = conn->connhost[conn->whichhost].hostaddr;
-						else
-							displayed_host = conn->connhost[conn->whichhost].host;
-						displayed_port = conn->connhost[conn->whichhost].port;
-						if (displayed_port == NULL || displayed_port[0] == '\0')
-							displayed_port = DEF_PGPORT_STR;
-
-						if (conn->requested_session_type == SESSION_TYPE_READ_WRITE)
-							appendPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("could not make a writable "
-															"connection to server "
-															"\"%s:%s\"\n"),
-											  displayed_host, displayed_port);
-						else
-							appendPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("could not make a readonly "
-															"connection to server "
-															"\"%s:%s\"\n"),
-											  displayed_host, displayed_port);
-
-						/* Close connection politely. */
-						conn->status = CONNECTION_OK;
-						sendTerminateConn(conn);
-
-						/* Record read-write host index */
-						if (conn->requested_session_type == SESSION_TYPE_PREFER_READ &&
-							conn->read_write_or_primary_host_index == -1)
-							conn->read_write_or_primary_host_index = conn->whichhost;
-
-						/*
-						 * Try next host if any, but we don't want to consider
-						 * additional addresses for this host.
-						 */
-						conn->try_next_host = true;
+						reject_checked_write_connection(conn);
 						goto keep_going;
 					}
 
-- 
2.20.1.windows.1

From b4c362af111487617f560df54fd0f2971628642d Mon Sep 17 00:00:00 2001
From: Hari Babu <kommi.haribabu@gmail.com>
Date: Thu, 28 Mar 2019 15:30:01 +1100
Subject: [PATCH 8/8] Server recovery mode handling

in_recovery GUC_REPORT is added to update the clients when the
server is recovery mode, this is useful for the client connections
to conenct to a standby server with a faster check instead of
executing a command.

New SIGUSR1 handling interrupt is added to support reporting
of recovery mode exit to all backends and their respective
clients.

Some parts of the code is taken from earlier development by
Elvis Pranskevichus and Tsunakawa Takayuki.
---
 doc/src/sgml/libpq.sgml              |  14 ++-
 doc/src/sgml/protocol.sgml           |   8 +-
 src/backend/access/transam/xlog.c    |   9 ++
 src/backend/commands/async.c         |  53 +++++++++++
 src/backend/storage/ipc/procarray.c  |  30 ++++++
 src/backend/storage/ipc/procsignal.c |   3 +
 src/backend/storage/ipc/standby.c    |   9 ++
 src/backend/tcop/postgres.c          |   4 +
 src/backend/utils/misc/check_guc     |   2 +-
 src/backend/utils/misc/guc.c         |  17 +++-
 src/include/commands/async.h         |   5 +
 src/include/storage/procarray.h      |   1 +
 src/include/storage/procsignal.h     |   2 +
 src/include/storage/standby.h        |   1 +
 src/interfaces/libpq/fe-connect.c    | 133 +++++++++++++++++----------
 src/interfaces/libpq/fe-exec.c       |   4 +
 src/interfaces/libpq/libpq-int.h     |   1 +
 17 files changed, 237 insertions(+), 59 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 328b632e16..f3bbff9193 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1631,8 +1631,10 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
 
        <para>
         To find out whether the server is in recovery mode or not, query <literal>SELECT pg_is_in_recovery()</literal>
-        will be sent upon any successful connection; if it returns <literal>t</literal>, means server
-        is in recovery mode.
+        will be sent upon any successful connection if the server is prior to version 12; if it returns
+        <literal>t</literal>, it means server is in recovery mode. But for server version 12 or greater
+        uses the value of <varname>in_recovery</varname> configuration parameter that is reported by the
+        server upon successful connection.
        </para>
 
       </listitem>
@@ -2000,15 +2002,17 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName);
        <varname>IntervalStyle</varname>,
        <varname>TimeZone</varname>,
        <varname>integer_datetimes</varname>,
-       <varname>standard_conforming_strings</varname>, and
-       <varname>transaction_read_only</varname>.
+       <varname>standard_conforming_strings</varname>,
+       <varname>transaction_read_only</varname> and
+       <varname>in_recovery</varname>.
        (<varname>server_encoding</varname>, <varname>TimeZone</varname>, and
        <varname>integer_datetimes</varname> were not reported by releases before 8.0;
        <varname>standard_conforming_strings</varname> was not reported by releases
        before 8.1;
        <varname>IntervalStyle</varname> was not reported by releases before 8.4;
        <varname>application_name</varname> was not reported by releases before 9.0;
-       <varname>transaction_read_only</varname> was not reported by release before 12.0.)
+       <varname>transaction_read_only</varname> and <varname>in_recovery</varname>
+       were not reported by release before 12.0.)
        Note that
        <varname>server_version</varname>,
        <varname>server_encoding</varname> and
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index f3357b5c59..0da02263ea 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1284,15 +1284,17 @@ SELCT 1/0;
     <varname>IntervalStyle</varname>,
     <varname>TimeZone</varname>,
     <varname>integer_datetimes</varname>,
-    <varname>standard_conforming_strings</varname>, and
-    <varname>transaction_read_only</varname>.
+    <varname>standard_conforming_strings</varname>,
+    <varname>transaction_read_only</varname> and
+    <varname>in_recovery</varname>.
     (<varname>server_encoding</varname>, <varname>TimeZone</varname>, and
     <varname>integer_datetimes</varname> were not reported by releases before 8.0;
     <varname>standard_conforming_strings</varname> was not reported by releases
     before 8.1;
     <varname>IntervalStyle</varname> was not reported by releases before 8.4;
     <varname>application_name</varname> was not reported by releases before 9.0;
-    <varname>transaction_read_only</varname> was not reported by releases before 12.0.)
+    <varname>transaction_read_only</varname> and <varname>in_recovery</varname>
+    were not reported by releases before 12.0.)
     Note that
     <varname>server_version</varname>,
     <varname>server_encoding</varname> and
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 19d7911ec5..fa51c736fe 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7670,7 +7670,10 @@ StartupXLOG(void)
 	 * RecoverPreparedTransactions(), see notes for lock_twophase_recover()
 	 */
 	if (standbyState != STANDBY_DISABLED)
+	{
 		ShutdownRecoveryTransactionEnvironment();
+		SendRecoveryExitSignal();
+	}
 
 	/* Shut down xlogreader */
 	if (readFile >= 0)
@@ -7879,6 +7882,12 @@ RecoveryInProgress(void)
 			InitXLOGAccess();
 		}
 
+		/* Update in_recovery status. */
+		if (LocalRecoveryInProgress)
+			SetConfigOption("in_recovery",
+							"on",
+							PGC_INTERNAL, PGC_S_OVERRIDE);
+
 		/*
 		 * Note: We don't need a memory barrier when we're still in recovery.
 		 * We might exit recovery immediately after return, so the caller
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 5a7ee0de4c..934844523c 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -356,6 +356,15 @@ static List *upperPendingNotifies = NIL;	/* list of upper-xact lists */
  */
 volatile sig_atomic_t notifyInterruptPending = false;
 
+/*
+ * Inbound recovery exit are initially processed by
+ * HandleRecoveryExitInterrupt(), called from inside a signal handler.
+ * That just sets the recoveryExitInterruptPending flag and sets the process
+ * latch. ProcessRecoveryExitInterrupt() will then be called whenever it's
+ * safe to actually deal with the interrupt.
+ */
+volatile sig_atomic_t recoveryExitInterruptPending = false;
+
 /* True if we've registered an on_shmem_exit cleanup */
 static bool unlistenExitRegistered = false;
 
@@ -1736,6 +1745,50 @@ ProcessNotifyInterrupt(void)
 		ProcessIncomingNotify();
 }
 
+/*
+ * HandleRecoveryExitInterrupt
+ *
+ *		Signal handler portion of interrupt handling. Let the backend know
+ *		that the server has exited the recovery mode.
+ */
+void
+HandleRecoveryExitInterrupt(void)
+{
+	/*
+	 * Note: this is called by a SIGNAL HANDLER. You must be very wary what
+	 * you do here.
+	 */
+
+	/* signal that work needs to be done */
+	recoveryExitInterruptPending = true;
+
+	/* make sure the event is processed in due course */
+	SetLatch(MyLatch);
+}
+
+/*
+ * ProcessRecoveryExitInterrupt
+ *
+ *		This is called just after waiting for a frontend command.  If a
+ *		interrupt arrives (via HandleRecoveryExitInterrupt()) while reading,
+ *		the read will be interrupted via the process's latch, and this routine
+ *		will get called.
+*/
+void
+ProcessRecoveryExitInterrupt(void)
+{
+	recoveryExitInterruptPending = false;
+
+	SetConfigOption("in_recovery",
+					"off",
+					PGC_INTERNAL, PGC_S_OVERRIDE);
+
+	/*
+	 * Flush output buffer so that clients receive the ParameterStatus message
+	 * as soon as possible.
+	 */
+	pq_flush();
+}
 
 /*
  * Read all pending notifications from the queue, and deliver appropriate
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 010cc061c8..a5f3568d09 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -2972,6 +2972,36 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
 	return true;				/* timed out, still conflicts */
 }
 
+/*
+ * SendSignalToAllBackends --- send a signal to all backends.
+ */
+void
+SendSignalToAllBackends(ProcSignalReason reason)
+{
+	ProcArrayStruct *arrayP = procArray;
+	int			index;
+	pid_t		pid = 0;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		int			pgprocno = arrayP->pgprocnos[index];
+		volatile PGPROC *proc = &allProcs[pgprocno];
+		VirtualTransactionId procvxid;
+
+		GET_VXID_FROM_PGPROC(procvxid, *proc);
+
+		pid = proc->pid;
+		if (pid != 0)
+		{
+			(void) SendProcSignal(pid, reason, procvxid.backendId);
+		}
+	}
+
+	LWLockRelease(ProcArrayLock);
+}
+
 /*
  * ProcArraySetReplicationSlotXmin
  *
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 7605b2c367..e4548dc323 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -292,6 +292,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
 
+	if (CheckProcSignal(PROCSIG_RECOVERY_EXIT))
+		HandleRecoveryExitInterrupt();
+
 	SetLatch(MyLatch);
 
 	latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index cd56dca3ae..65f6ec7ca2 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -138,6 +138,15 @@ ShutdownRecoveryTransactionEnvironment(void)
 	VirtualXactLockTableCleanup();
 }
 
+/*
+ * SendRecoveryExitSignal
+ *		Signal backends that the server has exited recovery mode.
+ */
+void
+SendRecoveryExitSignal(void)
+{
+	SendSignalToAllBackends(PROCSIG_RECOVERY_EXIT);
+}
 
 /*
  * -----------------------------------------------------
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index f9ce3d8f22..d8d13f5a75 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -543,6 +543,10 @@ ProcessClientReadInterrupt(bool blocked)
 		/* Process notify interrupts, if any */
 		if (notifyInterruptPending)
 			ProcessNotifyInterrupt();
+
+		/* Process recovery exit interrupts that happened while reading */
+		if (recoveryExitInterruptPending)
+			ProcessRecoveryExitInterrupt();
 	}
 	else if (ProcDiePending)
 	{
diff --git a/src/backend/utils/misc/check_guc b/src/backend/utils/misc/check_guc
index d228bbed68..1c0f51ac8b 100755
--- a/src/backend/utils/misc/check_guc
+++ b/src/backend/utils/misc/check_guc
@@ -21,7 +21,7 @@ is_superuser lc_collate lc_ctype lc_messages lc_monetary lc_numeric lc_time \
 pre_auth_delay role seed server_encoding server_version server_version_int \
 session_authorization trace_lock_oidmin trace_lock_table trace_locks trace_lwlocks \
 trace_notify trace_userlocks transaction_isolation transaction_read_only \
-zero_damaged_pages"
+zero_damaged_pages in_recovery"
 
 ### What options are listed in postgresql.conf.sample, but don't appear
 ### in guc.c?
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index d464be2c5b..98795126d0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -578,7 +578,7 @@ static char *recovery_target_xid_string;
 static char *recovery_target_time_string;
 static char *recovery_target_name_string;
 static char *recovery_target_lsn_string;
-
+static bool in_recovery;
 
 /* should be static, but commands/variable.c needs to get at this */
 char	   *role_string;
@@ -1728,6 +1728,21 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		/*
+		 * Not for general use --- used to indicate whether the instance is
+		 * recovery mode
+		 */
+		{"in_recovery", PGC_INTERNAL, UNGROUPED,
+			gettext_noop("Shows whether the instance is in recovery mode."),
+			NULL,
+			GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+		},
+		&in_recovery,
+		false,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"allow_system_table_mods", PGC_POSTMASTER, DEVELOPER_OPTIONS,
 			gettext_noop("Allows modifications of the structure of system tables."),
diff --git a/src/include/commands/async.h b/src/include/commands/async.h
index cfea78e039..7c0bbd3897 100644
--- a/src/include/commands/async.h
+++ b/src/include/commands/async.h
@@ -24,6 +24,7 @@
 
 extern bool Trace_notify;
 extern volatile sig_atomic_t notifyInterruptPending;
+extern volatile sig_atomic_t recoveryExitInterruptPending;
 
 extern Size AsyncShmemSize(void);
 extern void AsyncShmemInit(void);
@@ -54,4 +55,8 @@ extern void HandleNotifyInterrupt(void);
 /* process interrupts */
 extern void ProcessNotifyInterrupt(void);
 
+/* recovery exit interrupt handling functions */
+extern void HandleRecoveryExitInterrupt(void);
+extern void ProcessRecoveryExitInterrupt(void);
+
 #endif							/* ASYNC_H */
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index bd24850989..296d606e51 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -113,6 +113,7 @@ extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conf
 extern int	CountUserBackends(Oid roleid);
 extern bool CountOtherDBBackends(Oid databaseId,
 					 int *nbackends, int *nprepared);
+extern void SendSignalToAllBackends(ProcSignalReason reason);
 
 extern void XidCacheRemoveRunningXids(TransactionId xid,
 						  int nxids, const TransactionId *xids,
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 9f2f965d5c..722357c829 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -42,6 +42,8 @@ typedef enum
 	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
 
+	PROCSIG_RECOVERY_EXIT,		/* recovery exit interrupt */
+
 	NUM_PROCSIGNALS				/* Must be last! */
 } ProcSignalReason;
 
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 2361243514..3bb9023a6b 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -26,6 +26,7 @@ extern int	max_standby_streaming_delay;
 
 extern void InitRecoveryTransactionEnvironment(void);
 extern void ShutdownRecoveryTransactionEnvironment(void);
+extern void SendRecoveryExitSignal(void);
 
 extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid,
 									RelFileNode node);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 82b80385d1..15ac9866bd 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2077,6 +2077,49 @@ reject_checked_write_connection(PGconn *conn)
 	conn->try_next_host = true;
 }
 
+static void
+reject_checked_recovery_connection(PGconn *conn)
+{
+	/* Not a requested type; fail this connection. */
+	const char *displayed_host;
+	const char *displayed_port;
+
+	/* Append error report to conn->errorMessage. */
+	if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+		displayed_host = conn->connhost[conn->whichhost].hostaddr;
+	else
+		displayed_host = conn->connhost[conn->whichhost].host;
+	displayed_port = conn->connhost[conn->whichhost].port;
+	if (displayed_port == NULL || displayed_port[0] == '\0')
+		displayed_port = DEF_PGPORT_STR;
+
+	if (conn->requested_session_type == SESSION_TYPE_PRIMARY)
+		appendPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("server is in recovery mode "
+										"\"%s:%s\"\n"),
+						  displayed_host, displayed_port);
+	else
+		appendPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("server is not in recovery mode "
+										"\"%s:%s\"\n"),
+						  displayed_host, displayed_port);
+
+	/* Close connection politely. */
+	conn->status = CONNECTION_OK;
+	sendTerminateConn(conn);
+
+	/* Record primary host index */
+	if (conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY &&
+		conn->read_write_or_primary_host_index == -1)
+		conn->read_write_or_primary_host_index = conn->whichhost;
+
+	/*
+	 * Try next host if any, but we don't want to consider additional
+	 * addresses for this host.
+	 */
+	conn->try_next_host = true;
+}
+
 /* ----------------
  *		PQconnectPoll
  *
@@ -3374,27 +3417,52 @@ keep_going:						/* We will come back to here until there is
 						   conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY ||
 						   conn->requested_session_type == SESSION_TYPE_STANDBY)))
 				{
-					/*
-					 * Save existing error messages across the PQsendQuery
-					 * attempt.  This is necessary because PQsendQuery is
-					 * going to reset conn->errorMessage, so we would lose
-					 * error messages related to previous hosts we have tried
-					 * and failed to connect to.
-					 */
-					if (!saveErrorMessage(conn, &savedMessage))
-						goto error_return;
 
-					conn->status = CONNECTION_OK;
-					if (!PQsendQuery(conn, "SELECT pg_is_in_recovery()"))
+					if (conn->sversion < 120000)
 					{
+						/*
+						 * Save existing error messages across the PQsendQuery
+						 * attempt.  This is necessary because PQsendQuery is
+						 * going to reset conn->errorMessage, so we would lose
+						 * error messages related to previous hosts we have
+						 * tried and failed to connect to.
+						 */
+						if (!saveErrorMessage(conn, &savedMessage))
+							goto error_return;
+
+						conn->status = CONNECTION_OK;
+						if (!PQsendQuery(conn, "SELECT pg_is_in_recovery()"))
+						{
+							restoreErrorMessage(conn, &savedMessage);
+							goto error_return;
+						}
+
+						conn->status = CONNECTION_CHECK_RECOVERY;
+
 						restoreErrorMessage(conn, &savedMessage);
-						goto error_return;
+						return PGRES_POLLING_READING;
 					}
+					else if ((conn->in_recovery &&
+							  conn->requested_session_type == SESSION_TYPE_PRIMARY) ||
+							 (!conn->in_recovery &&
+							  (conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY ||
+							   conn->requested_session_type == SESSION_TYPE_STANDBY)))
+					{
+						/*
+						 * The following scenario is possible only for the
+						 * prefer-standby mode for the next pass of the list
+						 * of connections as it couldn't find any servers that
+						 * are in recovery.
+						 */
+						if (conn->read_write_or_primary_host_index == -2)
+							goto consume_checked_target_connection;
 
-					conn->status = CONNECTION_CHECK_RECOVERY;
+						reject_checked_recovery_connection(conn);
+						goto keep_going;
+					}
 
-					restoreErrorMessage(conn, &savedMessage);
-					return PGRES_POLLING_READING;
+					/* obtained the requested type, consume it */
+					goto consume_checked_target_connection;
 				}
 
 				/*
@@ -3643,40 +3711,7 @@ keep_going:						/* We will come back to here until there is
 						PQclear(res);
 						restoreErrorMessage(conn, &savedMessage);
 
-						/* Append error report to conn->errorMessage. */
-						if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
-							displayed_host = conn->connhost[conn->whichhost].hostaddr;
-						else
-							displayed_host = conn->connhost[conn->whichhost].host;
-						displayed_port = conn->connhost[conn->whichhost].port;
-						if (displayed_port == NULL || displayed_port[0] == '\0')
-							displayed_port = DEF_PGPORT_STR;
-
-						if (conn->requested_session_type == SESSION_TYPE_PRIMARY)
-							appendPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("server is in recovery mode "
-															"\"%s:%s\"\n"),
-											  displayed_host, displayed_port);
-						else
-							appendPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("server is not in recovery mode "
-															"\"%s:%s\"\n"),
-											  displayed_host, displayed_port);
-
-						/* Close connection politely. */
-						conn->status = CONNECTION_OK;
-						sendTerminateConn(conn);
-
-						/* Record primary host index */
-						if (conn->requested_session_type == SESSION_TYPE_PREFER_STANDBY &&
-							conn->read_write_or_primary_host_index == -1)
-							conn->read_write_or_primary_host_index = conn->whichhost;
-
-						/*
-						 * Try next host if any, but we don't want to consider
-						 * additional addresses for this host.
-						 */
-						conn->try_next_host = true;
+						reject_checked_recovery_connection(conn);
 						goto keep_going;
 					}
 
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index d2658efba5..38b8abcc44 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1117,6 +1117,10 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
 	{
 		conn->transaction_read_only = (strcmp(value, "on") == 0);
 	}
+	else if (strcmp(name, "in_recovery") == 0)
+	{
+		conn->in_recovery = (strcmp(value, "on") == 0);
+	}
 }
 
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d60ee385a6..d135f3d9a3 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -442,6 +442,7 @@ struct pg_conn
 	int			client_encoding;	/* encoding id */
 	bool		std_strings;	/* standard_conforming_strings */
 	bool		transaction_read_only;	/* transaction_read_only */
+	bool		in_recovery;	/* in_recovery */
 	PGVerbosity verbosity;		/* error/notice message verbosity */
 	PGContextVisibility show_context;	/* whether to show CONTEXT field */
 	PGlobjfuncs *lobjfuncs;		/* private state for large-object access fns */
-- 
2.20.1.windows.1

Reply via email to