From 35f2a0c8971d31ec63b7752292b011b90b1ca004 Mon Sep 17 00:00:00 2001 From: Jim Jones Date: Thu, 30 Oct 2025 09:27:24 +0100 Subject: [PATCH v6] Add %i prompt escape to indicate server read-only status This patch introduces a new prompt escape %i for psql, which shows whether the connected server is operating in read-only or read/write mode. It expands to "read-only" if any of the following are true: - The server is in hot standby mode (in_hot_standby = on) - The session's default transaction mode is read-only (default_transaction_read_only = on) - The current transaction is explicitly set to read-only via SET TRANSACTION READ ONLY (transaction_read_only = on) Otherwise, it displays "read/write". To minimize overhead, the implementation checks session-level parameters (in_hot_standby and default_transaction_read_only) first via PQparameterStatus(). If both indicate read/write mode, it queries the server for the transaction-level transaction_read_only setting. This means no extra queries are issued on hot standby systems or sessions with default_transaction_read_only = on. This is useful for distinguishing read-only sessions (e.g. connected to a standby, using a default read-only transaction mode, or in an explicit read-only transaction) from "read/write" ones at a glance, especially when working with multiple connections in replicated or restricted environments. --- doc/src/sgml/ref/psql-ref.sgml | 16 ++++++++++++++++ src/bin/psql/prompt.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 1a339600bc..3b45514f34 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -5044,6 +5044,22 @@ testdb=> INSERT INTO my_table VALUES (:'content'); + + %i + + + Displays the session's read-only status as read-only + if the current transaction is read-only (transaction_read_only + is on), the server is in hot standby + (in_hot_standby is on), or the + default transaction mode is read-only (default_transaction_read_only + is on); otherwise displays read-write. + Useful for identifying sessions that cannot perform writes, such as in + replication setups or when explicit read-only transactions are in use. + + + + %x diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index b08d7328fb..5d43d5bbcb 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -43,6 +43,8 @@ * or a ! if session is not connected to a database; * in prompt2 -, *, ', or "; * in prompt3 nothing + * %i - displays "read-only" if in hot standby, or if default_transaction_read_only + * or transaction_read_only are on, "read/write" otherwise. * %x - transaction status: empty, *, !, ? (unknown or no connection) * %l - The line number inside the current statement, starting from 1. * %? - the error code of the last query (not yet implemented) @@ -247,7 +249,39 @@ get_prompt(promptStatus_t status, ConditionalStack cstack) break; } break; + case 'i': + if (pset.db) + { + const char *hs = PQparameterStatus(pset.db, "in_hot_standby"); + const char *ro = PQparameterStatus(pset.db, "default_transaction_read_only"); + + if (!hs || !ro) + strlcpy(buf, _("unknown"), sizeof(buf)); + else if (strcmp(hs, "on") == 0 || strcmp(ro, "on") == 0) + strlcpy(buf, _("read-only"), sizeof(buf)); + else + { + const char *tr = NULL; + PGresult *res; + + res = PQexec(pset.db, "SHOW transaction_read_only"); + if (PQresultStatus(res) == PGRES_TUPLES_OK && + PQntuples(res) == 1) + tr = PQgetvalue(res, 0, 0); + + if (!tr) + strlcpy(buf, _("unknown"), sizeof(buf)); + else if (strcmp(tr, "on") == 0) + strlcpy(buf, _("read-only"), sizeof(buf)); + else + strlcpy(buf, _("read/write"), sizeof(buf)); + PQclear(res); + } + } + else + buf[0] = '\0'; + break; case 'x': if (!pset.db) buf[0] = '?'; -- 2.43.0