diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 02884ba..d263aab 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1500,13 +1500,23 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
If this parameter is set to read-write, only a
connection in which read-write transactions are accepted by default
- is considered acceptable. The query
+ is considered acceptable. The query
SHOW transaction_read_only will be sent upon any
successful connection; if it returns on, 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,
- any, regards all connections as acceptable.
+ attempt had failed.
+
+
+ If this paramete is set to prefer_read
+ the libpq will try to connect to a read-only transactions supported server
+ firstly. If failed to connect to a read-only transactions supported server
+ then the libpq will try to connect to a read-write transactions supported
+ server.
+
+
+ The default value of this parameter,any, regards all
+ connections as acceptable.
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 77eebb0..3fd4c0f 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -327,7 +327,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 */
@@ -1184,7 +1184,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, "prefer-read") != 0)
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
@@ -2086,8 +2087,22 @@ keep_going: /* We will come back to here until there is
{
if (++conn->whichhost >= conn->nconnhost)
{
- conn->whichhost = 0;
- break;
+ if (conn->primary_host_index > 0 &&
+ strcmp(conn->target_session_attrs,"prefer-read") ==0 )
+ {
+ /*
+ * Go to here means failed to connect to read-only servers
+ * and now try connect to read-write server again.
+ * Only under the 'prefer-read' scenario will go to here.
+ */
+ conn->addr_cur = conn->connhost[conn->primary_host_index].addrlist;
+ conn->whichhost = conn->primary_host_index;
+ }
+ else
+ {
+ conn->whichhost = 0;
+ break;
+ }
}
conn->addr_cur =
conn->connhost[conn->whichhost].addrlist;
@@ -2341,6 +2356,14 @@ keep_going: /* We will come back to here until there is
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
+ else if (conn->primary_host_index >= 0 &&
+ strcmp(conn->target_session_attrs, "prefer-read") == 0)
+ {
+ conn->addr_cur = conn->connhost[conn->primary_host_index].addrlist;
+ conn->whichhost = conn->primary_host_index;
+ conn->status = CONNECTION_NEEDED;
+ goto keep_going;
+ }
goto error_return;
}
@@ -2978,10 +3001,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 read-write or prefer-read 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, "read-write") == 0 ||
+ strcmp(conn->target_session_attrs, "prefer-read") == 0))
{
/*
* We are yet to make a connection. Save all existing
@@ -3042,10 +3067,12 @@ keep_going: /* We will come back to here until there is
}
/*
- * If a read-write connection is requested check for same.
+ * If a read-write or prefer-read connection is requested
+ * check for same.
*/
if (conn->target_session_attrs != NULL &&
- strcmp(conn->target_session_attrs, "read-write") == 0)
+ (strcmp(conn->target_session_attrs, "read-write") == 0 ||
+ strcmp(conn->target_session_attrs, "prefer-read") == 0))
{
if (!saveErrorMessage(conn, &savedMessage))
goto error_return;
@@ -3124,57 +3151,130 @@ keep_going: /* We will come back to here until there is
PQntuples(res) == 1)
{
char *val;
+ bool readonly_server = false;
val = PQgetvalue(res, 0, 0);
if (strncmp(val, "on", 2) == 0)
+ readonly_server = true;
+
+ if (strcmp(conn->target_session_attrs, "read-write") == 0)
{
- const char *displayed_host;
- const char *displayed_port;
+ if(readonly_server)
+ {
+ 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->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;
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
- PQclear(res);
- restoreErrorMessage(conn, &savedMessage);
+ /* Not writable; close connection. */
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not make a writable "
+ "connection to server "
+ "\"%s:%s\"\n"),
+ displayed_host, displayed_port);
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+ pqDropConnection(conn, true);
- /* Not writable; close connection. */
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not make a writable "
- "connection to server "
- "\"%s:%s\"\n"),
- displayed_host, displayed_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;
+ }
- /* Skip any remaining addresses for this host. */
- conn->addr_cur = NULL;
- if (conn->whichhost + 1 < conn->nconnhost)
+ /* No more addresses to try. So we fail. */
+ goto error_return;
+ }
+ else /* server support read-write */
{
- conn->status = CONNECTION_NEEDED;
+ PQclear(res);
+ termPQExpBuffer(&savedMessage);
+
+ /* We can release the address lists now. */
+ release_all_addrinfo(conn);
+
+ /*
+ * Finish reading any remaining messages before being
+ * considered as ready.
+ */
+ conn->status = CONNECTION_CONSUME;
goto keep_going;
}
-
- /* No more addresses to try. So we fail. */
- goto error_return;
}
- PQclear(res);
- termPQExpBuffer(&savedMessage);
+ else /* conn->target_session_attrs is prefer-read */
+ {
+ if(readonly_server)
+ {
+ PQclear(res);
+ termPQExpBuffer(&savedMessage);
- /* We can release the address lists now. */
- release_all_addrinfo(conn);
+ /* We can release the address lists now. */
+ release_all_addrinfo(conn);
- /*
- * Finish reading any remaining messages before being
- * considered as ready.
- */
- conn->status = CONNECTION_CONSUME;
- goto keep_going;
+ /*
+ * Finish reading any remaining messages before being
+ * considered as ready.
+ */
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+ else /* server support read-write */
+ {
+ if ((conn->primary_host_index < 0) && (conn->whichhost + 1 < conn->nconnhost))
+ {
+ 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;
+
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
+
+ /*
+ * Connecting to a writable server, close it
+ * and try to connect to another one.
+ */
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+ pqDropConnection(conn, true);
+
+ /* Skip any remaining addresses for this host. */
+ conn->addr_cur = NULL;
+
+ conn->status = CONNECTION_NEEDED;
+
+ /* Record primary host index */
+ conn->primary_host_index = conn->whichhost;
+ goto keep_going;
+ }
+ else /* No more host to connect, keep this connection */
+ {
+ PQclear(res);
+ termPQExpBuffer(&savedMessage);
+
+ /* We can release the address lists now. */
+ release_all_addrinfo(conn);
+
+ /*
+ * Finish reading any remaining messages before being
+ * considered as ready.
+ */
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+ }
+ }
}
/*
@@ -3393,6 +3493,8 @@ makeEmptyPGconn(void)
conn = NULL;
}
+ conn->primary_host_index = -1;
+
return conn;
}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4e35409..a3e582d 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -362,7 +362,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, perfer-read. */
char *target_session_attrs;
/* Optional file to write trace info to */
@@ -396,6 +396,7 @@ struct pg_conn
int nconnhost; /* # of possible hosts */
int whichhost; /* host we're currently considering */
pg_conn_host *connhost; /* details about each possible host */
+ int primary_host_index; /* index for primary host in connhost */
/* 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 fb27925..09816e0 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 => 28;
+use Test::More tests => 31;
# Initialize master node
my $node_master = get_new_node('master');
@@ -115,6 +115,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