On Thursday, March 1, 2018 4:25:16 AM EDT Andres Freund wrote:
> A CF entry for this patch has been created yesterday, without any
> changes having happend since the above status update. Given that
> there's been no progress for multiple commitfests, and this is the
> last CF, this doesn't seem appropriate. Therefore marked as returned
> with feedback.
Sorry for the long silence on this. I'm attaching a rebased and
cleaned-up patch with the earlier review issues addressed. I've checked
all relevant tests and verified manually.
Elvis
>From b4b23556552d1fddd3a58ec4db43cbcc62230a87 Mon Sep 17 00:00:00 2001
From: Elvis Pranskevichus <el...@magic.io>
Date: Tue, 12 Sep 2017 11:15:01 -0400
Subject: [PATCH v2] Add and report the new "session_read_only" GUC
pseudo-variable.
Currently, clients wishing to know whether the server is in hot standby
have to resort to polling, which is often suboptimal.
This adds the new "session_read_only" GUC variable that is reported
via a ParameterStatus message. This allows the clients to:
(a) know right away that they are connected to a server in hot
standby; and
(b) know immediately when a server exits hot standby.
This is immediately useful for target_session_attrs=read-write, as
libpq will not need to make a `SHOW transaction_read_only` roundtrip.
It is also beneficial to various connection pooling systems
(pgpool, pgbouncer etc.)
This also adds support for "target_session_attrs=read-only".
Parts of the patch are based on the work by Tsunakawa Takayuki.
---
doc/src/sgml/libpq.sgml | 4 ++
doc/src/sgml/protocol.sgml | 3 +-
src/backend/access/transam/xlog.c | 9 +++-
src/backend/commands/async.c | 49 ++++++++++++++++++
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 | 14 ++++-
src/backend/utils/misc/check_guc | 2 +-
src/backend/utils/misc/guc.c | 42 ++++++++++++++-
src/include/commands/async.h | 7 +++
src/include/storage/procarray.h | 2 +
src/include/storage/procsignal.h | 2 +
src/include/storage/standby.h | 1 +
src/interfaces/libpq/fe-connect.c | 76 ++++++++++++++++++++++++----
src/interfaces/libpq/fe-exec.c | 6 ++-
src/interfaces/libpq/libpq-int.h | 3 +-
17 files changed, 243 insertions(+), 19 deletions(-)
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 06d909e804..0cd55bff97 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1579,6 +1579,9 @@ 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</literal>, the connection
@@ -1929,6 +1932,7 @@ const char *PQparameterStatus(const PGconn *conn, const char *paramName);
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>session_read_only</> was not reported by releases 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 f0b2145208..ab8db9dc89 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1291,6 +1291,7 @@ SELCT 1/0;
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>session_read_only</> was not reported by releases before 12.0.)
Note that
<varname>server_version</varname>,
<varname>server_encoding</varname> and
@@ -1586,7 +1587,7 @@ PostgreSQL is <literal>tls-server-end-point</literal>.
user-supplied password in the transmitted password hash. While this
prevents the password hash from being successfully retransmitted in
a later session, it does not prevent a fake server between the real
- server and client from passing through the server's random value
+ server and client from passing through the server's random value
and successfully authenticating.
</para>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 4754e75436..8d29fd6aa1 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7904,8 +7904,10 @@ StartupXLOG(void)
* Shutdown the recovery environment. This must occur after
* RecoverPreparedTransactions(), see notes for lock_twophase_recover()
*/
- if (standbyState != STANDBY_DISABLED)
+ if (standbyState != STANDBY_DISABLED) {
ShutdownRecoveryTransactionEnvironment();
+ SendHotStandbyExitSignal();
+ }
/* Shut down xlogreader */
if (readFile >= 0)
@@ -8112,6 +8114,11 @@ RecoveryInProgress(void)
*/
pg_memory_barrier();
InitXLOGAccess();
+
+ /* Update session read-only status. */
+ SetConfigOption("session_read_only",
+ DefaultXactReadOnly ? "on" : "off",
+ PGC_INTERNAL, PGC_S_OVERRIDE);
}
/*
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index ee7c6d41b4..7635794040 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -357,6 +357,8 @@ static List *upperPendingNotifies = NIL; /* list of upper-xact lists */
*/
volatile sig_atomic_t notifyInterruptPending = false;
+volatile sig_atomic_t hotStandbyExitInterruptPending = false;
+
/* True if we've registered an on_shmem_exit cleanup */
static bool unlistenExitRegistered = false;
@@ -1738,6 +1740,53 @@ ProcessNotifyInterrupt(void)
}
+/*
+ * HandleHotStandbyExitInterrupt
+ *
+ * Signal handler portion of interrupt handling. Let the backend know
+ * that the server has exited the recovery mode.
+ */
+void
+HandleHotStandbyExitInterrupt(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 */
+ hotStandbyExitInterruptPending = true;
+
+ /* make sure the event is processed in due course */
+ SetLatch(MyLatch);
+}
+
+
+/*
+ * ProcessHotStandbyExitInterrupt
+ *
+ * This is called just after waiting for a frontend command. If a
+ * interrupt arrives (via HandleHotStandbyExitInterrupt()) while reading,
+ * the read will be interrupted via the process's latch, and this routine
+ * will get called.
+ */
+void
+ProcessHotStandbyExitInterrupt(void)
+{
+ hotStandbyExitInterruptPending = false;
+
+ SetConfigOption("session_read_only",
+ DefaultXactReadOnly ? "on" : "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
* ones to my frontend. Stop when we reach queue head or an uncommitted
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index bf2f4dbed2..cb27b598bd 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -2955,6 +2955,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 b0dd7d1b37..71b78ac462 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_HOTSTANDBY_EXIT))
+ HandleHotStandbyExitInterrupt();
+
SetLatch(MyLatch);
latch_sigusr1_handler();
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index c9bb3e987d..e7f18058ab 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -138,6 +138,15 @@ ShutdownRecoveryTransactionEnvironment(void)
VirtualXactLockTableCleanup();
}
+/*
+ * SendHotStandbyExitSignal
+ * Signal backends that the server has exited Hot Standby.
+ */
+void
+SendHotStandbyExitSignal(void)
+{
+ SendSignalToAllBackends(PROCSIG_HOTSTANDBY_EXIT);
+}
/*
* -----------------------------------------------------
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e4c6e3d406..e090349f25 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -539,9 +539,13 @@ ProcessClientReadInterrupt(bool blocked)
if (catchupInterruptPending)
ProcessCatchupInterrupt();
- /* Process sinval catchup interrupts that happened while reading */
+ /* Process NOTIFY interrupts that happened while reading */
if (notifyInterruptPending)
ProcessNotifyInterrupt();
+
+ /* Process recovery exit interrupts that happened while reading */
+ if (hotStandbyExitInterruptPending)
+ ProcessHotStandbyExitInterrupt();
}
else if (ProcDiePending && blocked)
{
@@ -3847,6 +3851,14 @@ PostgresMain(int argc, char *argv[],
*/
InitPostgres(dbname, InvalidOid, username, InvalidOid, NULL, false);
+ /*
+ * 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
diff --git a/src/backend/utils/misc/check_guc b/src/backend/utils/misc/check_guc
index d228bbed68..da96df2298 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 e9f542cfed..973b246e51 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -149,6 +149,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);
@@ -503,6 +505,7 @@ int huge_pages;
* and is kept in sync by assign_hooks.
*/
static char *syslog_ident_str;
+static bool session_read_only;
static double phony_random_seed;
static char *client_encoding_string;
static char *datestyle_string;
@@ -1001,6 +1004,20 @@ static struct config_bool ConfigureNamesBool[] =
false,
NULL, NULL, NULL
},
+ {
+ /*
+ * Not for general use --- used to indicate whether
+ * the session is read-only by default.
+ */
+ {"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."),
@@ -1444,7 +1461,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,
@@ -10344,6 +10361,29 @@ 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;
+
+ /*
+ * 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/include/commands/async.h b/src/include/commands/async.h
index d5868c42a0..0d1565a3d9 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 hotStandbyExitInterruptPending;
extern Size AsyncShmemSize(void);
extern void AsyncShmemInit(void);
@@ -54,4 +55,10 @@ extern void HandleNotifyInterrupt(void);
/* process interrupts */
extern void ProcessNotifyInterrupt(void);
+/* signal handler for inbound notifies (PROCSIG_HOTSTANDBY_EXIT) */
+extern void HandleHotStandbyExitInterrupt(void);
+
+/* process recovery exit event */
+extern void ProcessHotStandbyExitInterrupt(void);
+
#endif /* ASYNC_H */
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 75bab2985f..a996043f92 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -114,6 +114,8 @@ 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,
TransactionId latestXid);
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index 6db0d69b71..6c1b07198f 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_HOTSTANDBY_EXIT, /* postmaster has exited hot standby */
+
NUM_PROCSIGNALS /* Must be last! */
} ProcSignalReason;
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 1fcd8cf1b5..22a40435b2 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 SendHotStandbyExitSignal(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 45bc067921..a498e315ee 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -1240,7 +1240,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,
@@ -3158,15 +3159,19 @@ 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 a 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
- * may just skip the test in that case.
+ * may just skip the test in that case. Servers starting
+ * with 12.0 report the session_read_only value via a
+ * parameter status message.
*/
- if (conn->sversion >= 70400 &&
+ if (conn->sversion >= 70400 && conn->sversion < 120000 &&
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, "read-only") == 0))
{
/*
* Save existing error messages across the PQsendQuery
@@ -3190,6 +3195,36 @@ keep_going: /* We will come back to here until there is
return PGRES_POLLING_READING;
}
+ if (conn->sversion >= 120000 &&
+ 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 list now. */
release_conn_addrinfo(conn);
@@ -3226,9 +3261,9 @@ keep_going: /* We will come back to here until there is
}
/*
- * If a read-write connection is required, see if we have one.
- * (This should match the stanza in the CONNECTION_AUTH_OK case
- * above.)
+ * If a read-write or a read-only 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
@@ -3236,7 +3271,8 @@ keep_going: /* We will come back to here until there is
*/
if (conn->sversion >= 70400 &&
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, "read-only") == 0))
{
if (!saveErrorMessage(conn, &savedMessage))
goto error_return;
@@ -3317,9 +3353,22 @@ 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)
{
/* Not writable; fail this connection. */
const char *displayed_host;
@@ -3337,8 +3386,12 @@ keep_going: /* We will come back to here until there is
if (displayed_port == NULL || displayed_port[0] == '\0')
displayed_port = DEF_PGPORT_STR;
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
+
+ /* Not suitable; close connection. */
appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not make a writable "
+ libpq_gettext("could not make a suiteable "
"connection to server "
"\"%s:%s\"\n"),
displayed_host, displayed_port);
@@ -3533,6 +3586,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 e8b28d9ccf..5e30c6b3fc 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -1033,8 +1033,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).
@@ -1052,6 +1052,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 bdd8f9d9b2..891d6e3279 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -363,7 +363,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 */
@@ -426,6 +426,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 */
--
2.19.0