I wrote: > Tom Lane <t...@sss.pgh.pa.us> wrote: > >> I agree that letting it be changed back to read/write after that >> is surprising and unnecessary. Perhaps locking down the setting >> at the time of first grabbing a snapshot would be appropriate. >> IIRC that's how it works for transaction isolation level, and >> this seems like it ought to work the same. > > Agreed. I can create a patch today to implement this. Attached. Accomplished more through mimicry (based on setting transaction isolation level) than profound understanding of the code involved; but it passes all regression tests on both `make check` and `make installcheck-world`. This includes a new regression test that an attempt to change it after a query fails. I've poked at it with various ad hoc tests, and it is behaving as expected in those. I wasn't too confident how to word the new failure messages. -Kevin
*** a/src/backend/commands/variable.c --- b/src/backend/commands/variable.c *************** *** 544,549 **** show_log_timezone(void) --- 544,580 ---- /* + * SET TRANSACTION READ ONLY and SET TRANSACTION READ WRITE + * + * These should be transaction properties which can be set in exactly the + * same points in time that transaction isolation may be set. + */ + bool + assign_transaction_read_only(bool value, bool doit, GucSource source) + { + if (FirstSnapshotSet) + { + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), + errmsg("read-only property must be set before any query"))); + /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ + if (source != PGC_S_OVERRIDE) + return false; + } + else if (IsSubTransaction()) + { + ereport(GUC_complaint_elevel(source), + (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), + errmsg("read-only propery may not be changed in a subtransaction"))); + /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ + if (source != PGC_S_OVERRIDE) + return false; + } + + return true; + } + + /* * SET TRANSACTION ISOLATION LEVEL */ *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 169,175 **** static bool assign_bonjour(bool newval, bool doit, GucSource source); static bool assign_ssl(bool newval, bool doit, GucSource source); static bool assign_stage_log_stats(bool newval, bool doit, GucSource source); static bool assign_log_stats(bool newval, bool doit, GucSource source); - static bool assign_transaction_read_only(bool newval, bool doit, GucSource source); static const char *assign_canonical_path(const char *newval, bool doit, GucSource source); static const char *assign_timezone_abbreviations(const char *newval, bool doit, GucSource source); static const char *show_archive_command(void); --- 169,174 ---- *************** *** 7837,7870 **** assign_log_stats(bool newval, bool doit, GucSource source) return true; } - static bool - assign_transaction_read_only(bool newval, bool doit, GucSource source) - { - /* Can't go to r/w mode inside a r/o transaction */ - if (newval == false && XactReadOnly && IsSubTransaction()) - { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot set transaction read-write mode inside a read-only transaction"))); - /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - if (source != PGC_S_OVERRIDE) - return false; - } - - /* Can't go to r/w mode while recovery is still active */ - if (newval == false && XactReadOnly && RecoveryInProgress()) - { - ereport(GUC_complaint_elevel(source), - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot set transaction read-write mode during recovery"))); - /* source == PGC_S_OVERRIDE means do it anyway, eg at xact abort */ - if (source != PGC_S_OVERRIDE) - return false; - } - - return true; - } - static const char * assign_canonical_path(const char *newval, bool doit, GucSource source) { --- 7836,7841 ---- *** a/src/include/commands/variable.h --- b/src/include/commands/variable.h *************** *** 21,26 **** extern const char *show_timezone(void); --- 21,28 ---- extern const char *assign_log_timezone(const char *value, bool doit, GucSource source); extern const char *show_log_timezone(void); + extern bool assign_transaction_read_only(bool value, + bool doit, GucSource source); extern const char *assign_XactIsoLevel(const char *value, bool doit, GucSource source); extern const char *show_XactIsoLevel(void); *** a/src/test/regress/expected/transactions.out --- b/src/test/regress/expected/transactions.out *************** *** 43,48 **** SELECT * FROM aggtest; --- 43,58 ---- -- Read-only tests CREATE TABLE writetest (a int); CREATE TEMPORARY TABLE temptest (a int); + BEGIN; + SET TRANSACTION READ ONLY; -- ok + SELECT * FROM writetest; -- ok + a + --- + (0 rows) + + SET TRANSACTION READ WRITE; --fail + ERROR: read-only property must be set before any query + COMMIT; SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY; DROP TABLE writetest; -- fail ERROR: cannot execute DROP TABLE in a read-only transaction *** a/src/test/regress/sql/transactions.sql --- b/src/test/regress/sql/transactions.sql *************** *** 39,44 **** SELECT * FROM aggtest; --- 39,50 ---- CREATE TABLE writetest (a int); CREATE TEMPORARY TABLE temptest (a int); + BEGIN; + SET TRANSACTION READ ONLY; -- ok + SELECT * FROM writetest; -- ok + SET TRANSACTION READ WRITE; --fail + COMMIT; + SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY; DROP TABLE writetest; -- fail
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers