Tom Lane <t...@sss.pgh.pa.us> wrote: > The main thing I would be worried about is whether you're sure > that you have separated the RESET-as-a-command case from the cases > where we actually are rolling back to a previous state. It looks good to me. I added a few regression tests for that. Robert Haas <robertmh...@gmail.com> wrote: > +1 for such a comment. Added. The attached patch includes these. If it seems close, I'd be happy to come up with a version for the 9.1 branch. Basically it looks like that means omitting the changes to variable.c (which only changed message text and made the order of steps on related GUCs more consistent), and capturing a different out file for the expected directory. -Kevin
*** a/src/backend/commands/variable.c --- b/src/backend/commands/variable.c *************** *** 600,616 **** check_XactIsoLevel(char **newval, void **extra, GucSource source) if (newXactIsoLevel != XactIsoLevel) { ! if (FirstSnapshotSet) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query"); return false; } ! /* We ignore a subtransaction setting it to the existing value. */ ! if (IsSubTransaction()) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction"); return false; } /* Can't go to serializable mode while recovery is still active */ --- 600,616 ---- if (newXactIsoLevel != XactIsoLevel) { ! /* We ignore a subtransaction setting it to the existing value. */ ! if (IsSubTransaction()) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("cannot set transaction isolation level in a subtransaction"); return false; } ! if (FirstSnapshotSet) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("transaction isolation level must be set before any query"); return false; } /* Can't go to serializable mode while recovery is still active */ *************** *** 665,677 **** check_transaction_deferrable(bool *newval, void **extra, GucSource source) if (IsSubTransaction()) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE cannot be called within a subtransaction"); return false; } if (FirstSnapshotSet) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("SET TRANSACTION [NOT] DEFERRABLE must be called before any query"); return false; } --- 665,677 ---- if (IsSubTransaction()) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("cannot set transaction deferrable state within a subtransaction"); return false; } if (FirstSnapshotSet) { GUC_check_errcode(ERRCODE_ACTIVE_SQL_TRANSACTION); ! GUC_check_errmsg("transaction deferrable state must be set before any query"); return false; } *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 5042,5047 **** config_enum_get_options(struct config_enum * record, const char *prefix, --- 5042,5051 ---- * * If value is NULL, set the option to its default value (normally the * reset_val, but if source == PGC_S_DEFAULT we instead use the boot_val). + * When we reset a value we can't assume that the old value is valid, but must + * call the check hook if present. This is because the validity of a change + * might depend on context. For example, transaction isolation may not be + * changed after the transaction has run a query, even by a RESET command. * * action indicates whether to set the value globally in the session, locally * to the current top transaction, or just for the duration of a function call. *************** *** 5287,5302 **** set_config_option(const char *name, const char *value, name))); return 0; } - if (!call_bool_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else if (source == PGC_S_DEFAULT) { newval = conf->boot_val; - if (!call_bool_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else { --- 5291,5300 ---- *************** *** 5306,5311 **** set_config_option(const char *name, const char *value, --- 5304,5313 ---- context = conf->gen.reset_scontext; } + if (!call_bool_check_hook(conf, &newval, &newextra, + source, elevel)) + return 0; + if (prohibitValueChange) { if (*conf->variable != newval) *************** *** 5391,5406 **** set_config_option(const char *name, const char *value, newval, name, conf->min, conf->max))); return 0; } - if (!call_int_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else if (source == PGC_S_DEFAULT) { newval = conf->boot_val; - if (!call_int_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else { --- 5393,5402 ---- *************** *** 5410,5415 **** set_config_option(const char *name, const char *value, --- 5406,5415 ---- context = conf->gen.reset_scontext; } + if (!call_int_check_hook(conf, &newval, &newextra, + source, elevel)) + return 0; + if (prohibitValueChange) { if (*conf->variable != newval) *************** *** 5492,5507 **** set_config_option(const char *name, const char *value, newval, name, conf->min, conf->max))); return 0; } - if (!call_real_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else if (source == PGC_S_DEFAULT) { newval = conf->boot_val; - if (!call_real_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else { --- 5492,5501 ---- *************** *** 5511,5516 **** set_config_option(const char *name, const char *value, --- 5505,5514 ---- context = conf->gen.reset_scontext; } + if (!call_real_check_hook(conf, &newval, &newextra, + source, elevel)) + return 0; + if (prohibitValueChange) { if (*conf->variable != newval) *************** *** 5591,5603 **** set_config_option(const char *name, const char *value, */ if (conf->gen.flags & GUC_IS_NAME) truncate_identifier(newval, strlen(newval), true); - - if (!call_string_check_hook(conf, &newval, &newextra, - source, elevel)) - { - free(newval); - return 0; - } } else if (source == PGC_S_DEFAULT) { --- 5589,5594 ---- *************** *** 5610,5635 **** set_config_option(const char *name, const char *value, } else newval = NULL; - - if (!call_string_check_hook(conf, &newval, &newextra, - source, elevel)) - { - free(newval); - return 0; - } } else { ! /* ! * strdup not needed, since reset_val is already under ! * guc.c's control ! */ ! newval = conf->reset_val; newextra = conf->reset_extra; source = conf->gen.reset_source; context = conf->gen.reset_scontext; } if (prohibitValueChange) { /* newval shouldn't be NULL, so we're a bit sloppy here */ --- 5601,5630 ---- } else newval = NULL; } else { ! /* non-NULL reset_val must always get strdup'd */ ! if (conf->reset_val != NULL) ! { ! newval = guc_strdup(elevel, conf->reset_val); ! if (newval == NULL) ! return 0; ! } ! else ! newval = NULL; newextra = conf->reset_extra; source = conf->gen.reset_source; context = conf->gen.reset_scontext; } + if (!call_string_check_hook(conf, &newval, &newextra, + source, elevel)) + { + free(newval); + return 0; + } + if (prohibitValueChange) { /* newval shouldn't be NULL, so we're a bit sloppy here */ *************** *** 5721,5736 **** set_config_option(const char *name, const char *value, pfree(hintmsg); return 0; } - if (!call_enum_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else if (source == PGC_S_DEFAULT) { newval = conf->boot_val; - if (!call_enum_check_hook(conf, &newval, &newextra, - source, elevel)) - return 0; } else { --- 5716,5725 ---- *************** *** 5740,5745 **** set_config_option(const char *name, const char *value, --- 5729,5738 ---- context = conf->gen.reset_scontext; } + if (!call_enum_check_hook(conf, &newval, &newextra, + source, elevel)) + return 0; + if (prohibitValueChange) { if (*conf->variable != newval) *** a/src/test/regress/expected/transactions.out --- b/src/test/regress/expected/transactions.out *************** *** 53,58 **** SELECT * FROM writetest; -- ok --- 53,90 ---- SET TRANSACTION READ WRITE; --fail ERROR: transaction read-write mode must be set before any query COMMIT; + SET default_transaction_read_only = FALSE; + SET default_transaction_isolation = 'read committed'; + BEGIN; + SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok + SELECT * FROM writetest; -- ok + a + --- + (0 rows) + + RESET transaction_read_only; --fail + ERROR: transaction read-write mode must be set before any query + COMMIT; + BEGIN; + SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok + SELECT * FROM writetest; -- ok + a + --- + (0 rows) + + RESET transaction_isolation; --fail + ERROR: transaction isolation level must be set before any query + COMMIT; + BEGIN; + SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok + SELECT * FROM writetest; -- ok + a + --- + (0 rows) + + ROLLBACK; -- ok + RESET default_transaction_read_only; + RESET default_transaction_isolation; BEGIN; SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; -- ok *************** *** 391,396 **** SELECT 1; -- this should work --- 423,480 ---- 1 (1 row) + -- test rollbacks and resets of GUC in transactions + CREATE SCHEMA albion; + SET search_path = "$user",public,albion; + DROP SCHEMA albion; + SHOW search_path; + search_path + ------------------------- + "$user", public, albion + (1 row) + + BEGIN; + CREATE SCHEMA camelot; + SET search_path = "$user",public,camelot; + DROP SCHEMA camelot; + SAVEPOINT bedivere; + CREATE SCHEMA avalon; + SET search_path = "$user",public,avalon; + DROP SCHEMA avalon; + SHOW search_path; + search_path + ------------------------- + "$user", public, avalon + (1 row) + + ROLLBACK TO SAVEPOINT bedivere; + SHOW search_path; + search_path + -------------------------- + "$user", public, camelot + (1 row) + + RESET search_path; + SHOW search_path; + search_path + ---------------- + "$user",public + (1 row) + + ROLLBACK; + SHOW search_path; + search_path + ------------------------- + "$user", public, albion + (1 row) + + RESET search_path; + SHOW search_path; + search_path + ---------------- + "$user",public + (1 row) + -- check non-transactional behavior of cursors BEGIN; DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2; *** a/src/test/regress/sql/transactions.sql --- b/src/test/regress/sql/transactions.sql *************** *** 45,50 **** SELECT * FROM writetest; -- ok --- 45,73 ---- SET TRANSACTION READ WRITE; --fail COMMIT; + SET default_transaction_read_only = FALSE; + SET default_transaction_isolation = 'read committed'; + + BEGIN; + SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok + SELECT * FROM writetest; -- ok + RESET transaction_read_only; --fail + COMMIT; + + BEGIN; + SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok + SELECT * FROM writetest; -- ok + RESET transaction_isolation; --fail + COMMIT; + + BEGIN; + SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, READ ONLY; -- ok + SELECT * FROM writetest; -- ok + ROLLBACK; -- ok + + RESET default_transaction_read_only; + RESET default_transaction_isolation; + BEGIN; SET TRANSACTION READ ONLY; -- ok SET TRANSACTION READ WRITE; -- ok *************** *** 252,257 **** BEGIN; --- 275,303 ---- COMMIT; SELECT 1; -- this should work + -- test rollbacks and resets of GUC in transactions + CREATE SCHEMA albion; + SET search_path = "$user",public,albion; + DROP SCHEMA albion; + SHOW search_path; + BEGIN; + CREATE SCHEMA camelot; + SET search_path = "$user",public,camelot; + DROP SCHEMA camelot; + SAVEPOINT bedivere; + CREATE SCHEMA avalon; + SET search_path = "$user",public,avalon; + DROP SCHEMA avalon; + SHOW search_path; + ROLLBACK TO SAVEPOINT bedivere; + SHOW search_path; + RESET search_path; + SHOW search_path; + ROLLBACK; + SHOW search_path; + RESET search_path; + SHOW search_path; + -- check non-transactional behavior of cursors BEGIN; DECLARE c CURSOR FOR SELECT unique2 FROM tenk1 ORDER BY unique2;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers