diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 493cc12..b359333 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1425,10 +1425,14 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
        <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.
+        If this parameter is set to <literal>read-only</literal>, only a
+        connection in which read-write transactions are rejected 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</>, the connection
-        will be closed.  If multiple hosts were specified in the connection
+        successful connection if the server is prior to version 10.
+        If the session attribute does not match the requested one, 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</>, regards all connections as acceptable.
@@ -1709,6 +1713,7 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName);
        <varname>application_name</>,
        <varname>is_superuser</>,
        <varname>session_authorization</>,
+       <varname>session_read_only</>,
        <varname>DateStyle</>,
        <varname>IntervalStyle</>,
        <varname>TimeZone</>,
@@ -1719,7 +1724,8 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName);
        <varname>standard_conforming_strings</> was not reported by releases
        before 8.1;
        <varname>IntervalStyle</> was not reported by releases before 8.4;
-       <varname>application_name</> was not reported by releases before 9.0.)
+       <varname>application_name</> was not reported by releases before 9.0;
+       <varname>session_read_only</> was not reported by releases before 10.0.)
        Note that
        <varname>server_version</>,
        <varname>server_encoding</> and
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index d23df02..291d564 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1136,6 +1136,7 @@
     <varname>application_name</>,
     <varname>is_superuser</>,
     <varname>session_authorization</>,
+    <varname>session_read_only</>,
     <varname>DateStyle</>,
     <varname>IntervalStyle</>,
     <varname>TimeZone</>,
@@ -1146,7 +1147,8 @@
     <varname>standard_conforming_strings</> was not reported by releases
     before 8.1;
     <varname>IntervalStyle</> was not reported by releases before 8.4;
-    <varname>application_name</> was not reported by releases before 9.0.)
+    <varname>application_name</> was not reported by releases before 9.0;
+    <varname>session_read_only</> was not reported by releases before 10.0.)
     Note that
     <varname>server_version</>,
     <varname>server_encoding</> and
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 399822d..bf0bae8 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7904,6 +7904,10 @@ RecoveryInProgress(void)
 			 */
 			pg_memory_barrier();
 			InitXLOGAccess();
+
+			/* Update session read-only status. */
+			SetConfigOption("session_read_only", DefaultXactReadOnly,
+							PGC_INTERNAL, PGC_S_OVERRIDE);
 		}
 
 		/*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 75c2d9a..8933f4f 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3722,6 +3722,14 @@ PostgresMain(int argc, char *argv[],
 	InitPostgres(dbname, InvalidOid, username, InvalidOid, NULL);
 
 	/*
+	 * Update session read-only status if in recovery.
+	 */
+	if (IsUnderPostmaster && !DefaultXactReadOnly &&
+		RecoveryInProgress())
+		SetConfigOption("session_read_only", "on",
+						PGC_INTERNAL, PGC_S_OVERRIDE);
+
+	/*
 	 * If the PostmasterContext is still around, recycle the space; we don't
 	 * need it anymore after InitPostgres completes.  Note this does not trash
 	 * *MyProcPort, because ConnCreate() allocated that space with malloc()
diff --git a/src/backend/utils/misc/check_guc b/src/backend/utils/misc/check_guc
index d228bbe..da96df2 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 session_read_only"
 
 ### 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 92e1d63..2aff993 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -147,6 +147,8 @@ static bool call_string_check_hook(struct config_string * conf, char **newval,
 static bool call_enum_check_hook(struct config_enum * conf, int *newval,
 					 void **extra, GucSource source, int elevel);
 
+static void assign_default_transaction_read_only(bool newval, void *extra);
+
 static bool check_log_destination(char **newval, void **extra, GucSource source);
 static void assign_log_destination(const char *newval, void *extra);
 
@@ -493,6 +495,7 @@ int			huge_pages;
  */
 static char *syslog_ident_str;
 static bool session_auth_is_superuser;
+static bool session_read_only;
 static double phony_random_seed;
 static char *client_encoding_string;
 static char *datestyle_string;
@@ -933,6 +936,16 @@ static struct config_bool ConfigureNamesBool[] =
 		NULL, NULL, NULL
 	},
 	{
+		{"session_read_only", PGC_INTERNAL, UNGROUPED,
+			gettext_noop("Shows whether the session is read-only by default."),
+			NULL,
+			GUC_REPORT | GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+		},
+		&session_read_only,
+		false,
+		NULL, NULL, NULL
+	},
+	{
 		{"bonjour", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
 			gettext_noop("Enables advertising the server via Bonjour."),
 			NULL
@@ -1366,7 +1379,7 @@ static struct config_bool ConfigureNamesBool[] =
 		},
 		&DefaultXactReadOnly,
 		false,
-		NULL, NULL, NULL
+		NULL, assign_default_transaction_read_only, NULL
 	},
 	{
 		{"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
@@ -10038,6 +10051,30 @@ assign_wal_consistency_checking(const char *newval, void *extra)
 	wal_consistency_checking = (bool *) extra;
 }
 
+static void
+assign_default_transaction_read_only(bool newval, void *extra)
+{
+	if (newval == DefaultXactReadOnly)
+		return;
+
+	/*
+	 * We clamp manually-set values to at least 1MB.  Since
+	 * Also set the session read-only parameter.  We only need
+	 * to set the correct value in processes that have database
+	 * sessions, but there's no mechanism to know that there's
+	 * a session.  Instead, we use the shared memory segment
+	 * pointer because the processes with database sessions are
+	 * attached to the shared memory.  Without this check,
+	 * RecoveryInProgress() would crash the processes which
+	 * are not attached to the shared memory.
+	 */
+	if (IsUnderPostmaster && UsedShmemSegAddr != NULL &&
+		RecoveryInProgress())
+		newval = true;
+	SetConfigOption("session_read_only", newval ? "on" : "off",
+					PGC_INTERNAL, PGC_S_OVERRIDE);
+}
+
 static bool
 check_log_destination(char **newval, void **extra, GucSource source)
 {
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index f2c9bf7..e63e16b 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1066,7 +1066,8 @@ connectOptions2(PGconn *conn)
 	if (conn->target_session_attrs)
 	{
 		if (strcmp(conn->target_session_attrs, "any") != 0
-			&& strcmp(conn->target_session_attrs, "read-write") != 0)
+			&& strcmp(conn->target_session_attrs, "read-write") != 0
+			&& strcmp(conn->target_session_attrs, "read-only") != 0)
 		{
 			conn->status = CONNECTION_BAD;
 			printfPQExpBuffer(&conn->errorMessage,
@@ -2848,10 +2849,12 @@ keep_going:						/* We will come back to here until there is
 				}
 
 				/*
-				 * If a read-write connection is required, see if we have one.
+				 * If a specific type of connection is required, see if we have one.
 				 */
-				if (conn->target_session_attrs != NULL &&
-					strcmp(conn->target_session_attrs, "read-write") == 0)
+				/* If the server version is before 10.0, issue an SQL query. */
+				if (conn->sversion < 100000 &&
+					conn->target_session_attrs != NULL &&
+					strcmp(conn->target_session_attrs, "any") != 0)
 				{
 					/*
 					 * We are yet to make a connection. Save all existing
@@ -2876,6 +2879,34 @@ keep_going:						/* We will come back to here until there is
 					return PGRES_POLLING_READING;
 				}
 
+				/* Otherwise, check parameter status sent by backend to avoid round-trip for a query. */
+				if (conn->target_session_attrs != NULL &&
+					((strcmp(conn->target_session_attrs, "read-write") == 0 && !conn->session_read_only) ||
+					 (strcmp(conn->target_session_attrs, "read-only") == 0 && conn->session_read_only)))
+				{
+					/* Not suitable; close connection. */
+					appendPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("could not make a suitable "
+													"connection to server "
+													"\"%s:%s\"\n"),
+									  conn->connhost[conn->whichhost].host,
+									  conn->connhost[conn->whichhost].port);
+					conn->status = CONNECTION_OK;
+					sendTerminateConn(conn);
+					pqDropConnection(conn, true);
+
+					/* Skip any remaining addresses for this host. */
+					conn->addr_cur = NULL;
+					if (conn->whichhost + 1 < conn->nconnhost)
+					{
+						conn->status = CONNECTION_NEEDED;
+						goto keep_going;
+					}
+
+					/* No more addresses to try. So we fail. */
+					goto error_return;
+				}
+
 				/* We can release the address lists now. */
 				release_all_addrinfo(conn);
 
@@ -2912,10 +2943,10 @@ keep_going:						/* We will come back to here until there is
 			}
 
 			/*
-			 * If a read-write connection is requested check for same.
+			 * If a specific type of connection is required, see if we have one.
 			 */
 			if (conn->target_session_attrs != NULL &&
-				strcmp(conn->target_session_attrs, "read-write") == 0)
+				strcmp(conn->target_session_attrs, "any") != 0)
 			{
 				if (!saveErrorMessage(conn, &savedMessage))
 					goto error_return;
@@ -2991,16 +3022,29 @@ keep_going:						/* We will come back to here until there is
 					PQntuples(res) == 1)
 				{
 					char	   *val;
+					char	   *expected_val;
+					int			expected_len;
+
+					if (strcmp(conn->target_session_attrs, "read-write") == 0)
+					{
+						expected_val = "on";
+						expected_len = 2;
+					}
+					else
+					{
+						expected_val = "off";
+						expected_len = 3;
+					}
 
 					val = PQgetvalue(res, 0, 0);
-					if (strncmp(val, "on", 2) == 0)
+					if (strncmp(val, expected_val, expected_len) == 0)
 					{
 						PQclear(res);
 						restoreErrorMessage(conn, &savedMessage);
 
-						/* Not writable; close connection. */
+						/* Not suitable; close connection. */
 						appendPQExpBuffer(&conn->errorMessage,
-								   libpq_gettext("could not make a writable "
+								   libpq_gettext("could not make a suitable "
 												 "connection to server "
 												 "\"%s:%s\"\n"),
 										conn->connhost[conn->whichhost].host,
@@ -3200,6 +3244,7 @@ makeEmptyPGconn(void)
 	conn->setenv_state = SETENV_STATE_IDLE;
 	conn->client_encoding = PG_SQL_ASCII;
 	conn->std_strings = false;	/* unless server says differently */
+	conn->session_read_only = false;	/* unless server says differently */
 	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 9decd53..9b034aa 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -956,8 +956,8 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
 	}
 
 	/*
-	 * Special hacks: remember client_encoding and
-	 * standard_conforming_strings, and convert server version to a numeric
+	 * Special hacks: remember client_encoding/
+	 * standard_conforming_strings/session_read_only, 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
 	 * least in single-connection-using programs).
@@ -975,6 +975,8 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
 		conn->std_strings = (strcmp(value, "on") == 0);
 		static_std_strings = conn->std_strings;
 	}
+	else if (strcmp(name, "session_read_only") == 0)
+		conn->session_read_only = (strcmp(value, "on") == 0);
 	else if (strcmp(name, "server_version") == 0)
 	{
 		int			cnt;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 335568b..2430982 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -360,7 +360,7 @@ 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, read-only. */
 	char	   *target_session_attrs;
 
 	/* Optional file to write trace info to */
@@ -422,6 +422,7 @@ struct pg_conn
 	pgParameterStatus *pstatus; /* ParameterStatus data */
 	int			client_encoding;	/* encoding id */
 	bool		std_strings;	/* standard_conforming_strings */
+	bool		session_read_only;	/* session_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 */
