Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3853664

Browse files
committed
Introduce GUC_NO_RESET flag.
Previously, the transaction-property GUCs such as transaction_isolation could be reset after starting a transaction, because we marked them as GUC_NO_RESET_ALL but still allowed a targeted RESET. That leads to assertion failures or worse, because those properties aren't supposed to change after we've acquired a transaction snapshot. There are some NO_RESET_ALL variables for which RESET is okay, so we can't just redefine the semantics of that flag. Instead introduce a separate GUC_NO_RESET flag. Mark "seed", as well as the transaction property GUCs, as GUC_NO_RESET. We have to disallow GUC_ACTION_SAVE as well as straight RESET, because otherwise a function having a "SET transaction_isolation" clause can still break things: the end-of-function restore action is equivalent to a RESET. No back-patch, as it's conceivable that someone is doing something this patch will forbid (like resetting one of these GUCs at transaction start, or "CREATE FUNCTION ... SET transaction_read_only = 1") and not running into problems with it today. Given how long we've had this issue and not noticed, the side effects in non-assert builds can't be too serious. Per bug #17385 from Andrew Bille. Masahiko Sawada Discussion: https://postgr.es/m/17385-9ee529fb091f0ce5@postgresql.org
1 parent 4148c8b commit 3853664

File tree

11 files changed

+103
-13
lines changed

11 files changed

+103
-13
lines changed

doc/src/sgml/func.sgml

+6
Original file line numberDiff line numberDiff line change
@@ -24353,6 +24353,12 @@ SELECT collation for ('foo' COLLATE "de_DE");
2435324353
<command>SHOW ALL</command> commands.
2435424354
</entry>
2435524355
</row>
24356+
<row>
24357+
<entry><literal>NO_RESET</literal></entry>
24358+
<entry>Parameters with this flag do not support
24359+
<command>RESET</command> commands.
24360+
</entry>
24361+
</row>
2435624362
<row>
2435724363
<entry><literal>NO_RESET_ALL</literal></entry>
2435824364
<entry>Parameters with this flag are excluded from

src/backend/utils/misc/guc.c

+20
Original file line numberDiff line numberDiff line change
@@ -3243,6 +3243,26 @@ set_config_option_ext(const char *name, const char *value,
32433243
}
32443244
}
32453245

3246+
/* Disallow resetting and saving GUC_NO_RESET values */
3247+
if (record->flags & GUC_NO_RESET)
3248+
{
3249+
if (value == NULL)
3250+
{
3251+
ereport(elevel,
3252+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3253+
errmsg("parameter \"%s\" cannot be reset", name)));
3254+
return 0;
3255+
}
3256+
if (action == GUC_ACTION_SAVE)
3257+
{
3258+
ereport(elevel,
3259+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3260+
errmsg("parameter \"%s\" cannot be set locally in functions",
3261+
name)));
3262+
return 0;
3263+
}
3264+
}
3265+
32463266
/*
32473267
* Should we set reset/stacked values? (If so, the behavior is not
32483268
* transactional.) This is done either when we get a default value from

src/backend/utils/misc/guc_funcs.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,6 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
141141
WarnNoTransactionBlock(isTopLevel, "SET LOCAL");
142142
/* fall through */
143143
case VAR_RESET:
144-
if (strcmp(stmt->name, "transaction_isolation") == 0)
145-
WarnNoTransactionBlock(isTopLevel, "RESET TRANSACTION");
146-
147144
(void) set_config_option(stmt->name,
148145
NULL,
149146
(superuser() ? PGC_SUSET : PGC_USERSET),
@@ -539,7 +536,7 @@ ShowAllGUCConfig(DestReceiver *dest)
539536
Datum
540537
pg_settings_get_flags(PG_FUNCTION_ARGS)
541538
{
542-
#define MAX_GUC_FLAGS 5
539+
#define MAX_GUC_FLAGS 6
543540
char *varname = TextDatumGetCString(PG_GETARG_DATUM(0));
544541
struct config_generic *record;
545542
int cnt = 0;
@@ -554,6 +551,8 @@ pg_settings_get_flags(PG_FUNCTION_ARGS)
554551

555552
if (record->flags & GUC_EXPLAIN)
556553
flags[cnt++] = CStringGetTextDatum("EXPLAIN");
554+
if (record->flags & GUC_NO_RESET)
555+
flags[cnt++] = CStringGetTextDatum("NO_RESET");
557556
if (record->flags & GUC_NO_RESET_ALL)
558557
flags[cnt++] = CStringGetTextDatum("NO_RESET_ALL");
559558
if (record->flags & GUC_NO_SHOW_ALL)

src/backend/utils/misc/guc_tables.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,7 @@ struct config_bool ConfigureNamesBool[] =
15051505
{"transaction_read_only", PGC_USERSET, CLIENT_CONN_STATEMENT,
15061506
gettext_noop("Sets the current transaction's read-only status."),
15071507
NULL,
1508-
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
1508+
GUC_NO_RESET | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
15091509
},
15101510
&XactReadOnly,
15111511
false,
@@ -1524,7 +1524,7 @@ struct config_bool ConfigureNamesBool[] =
15241524
{"transaction_deferrable", PGC_USERSET, CLIENT_CONN_STATEMENT,
15251525
gettext_noop("Whether to defer a read-only serializable transaction until it can be executed with no possible serialization failures."),
15261526
NULL,
1527-
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
1527+
GUC_NO_RESET | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
15281528
},
15291529
&XactDeferrable,
15301530
false,
@@ -3606,7 +3606,7 @@ struct config_real ConfigureNamesReal[] =
36063606
{"seed", PGC_USERSET, UNGROUPED,
36073607
gettext_noop("Sets the seed for random-number generation."),
36083608
NULL,
3609-
GUC_NO_SHOW_ALL | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
3609+
GUC_NO_SHOW_ALL | GUC_NO_RESET | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
36103610
},
36113611
&phony_random_seed,
36123612
0.0, -1.0, 1.0,
@@ -4557,7 +4557,7 @@ struct config_enum ConfigureNamesEnum[] =
45574557
{"transaction_isolation", PGC_USERSET, CLIENT_CONN_STATEMENT,
45584558
gettext_noop("Sets the current transaction's isolation level."),
45594559
NULL,
4560-
GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
4560+
GUC_NO_RESET | GUC_NO_RESET_ALL | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
45614561
},
45624562
&XactIsoLevel,
45634563
XACT_READ_COMMITTED, isolation_level_options,

src/include/utils/guc.h

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ typedef enum
207207
#define GUC_LIST_INPUT 0x0001 /* input can be list format */
208208
#define GUC_LIST_QUOTE 0x0002 /* double-quote list elements */
209209
#define GUC_NO_SHOW_ALL 0x0004 /* exclude from SHOW ALL */
210+
#define GUC_NO_RESET 0x400000 /* disallow RESET and SAVE */
210211
#define GUC_NO_RESET_ALL 0x0008 /* exclude from RESET ALL */
211212
#define GUC_REPORT 0x0010 /* auto-report changes to client */
212213
#define GUC_NOT_IN_SAMPLE 0x0020 /* not in postgresql.conf.sample */

src/pl/plpgsql/src/expected/plpgsql_transaction.out

+2-3
Original file line numberDiff line numberDiff line change
@@ -576,16 +576,15 @@ BEGIN
576576
PERFORM 1;
577577
RAISE INFO '%', current_setting('transaction_isolation');
578578
COMMIT;
579-
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
580-
RESET TRANSACTION ISOLATION LEVEL;
579+
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
581580
PERFORM 1;
582581
RAISE INFO '%', current_setting('transaction_isolation');
583582
COMMIT;
584583
END;
585584
$$;
586585
INFO: read committed
587586
INFO: repeatable read
588-
INFO: read committed
587+
INFO: serializable
589588
-- error cases
590589
DO LANGUAGE plpgsql $$
591590
BEGIN

src/pl/plpgsql/src/sql/plpgsql_transaction.sql

+1-2
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,7 @@ BEGIN
481481
PERFORM 1;
482482
RAISE INFO '%', current_setting('transaction_isolation');
483483
COMMIT;
484-
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
485-
RESET TRANSACTION ISOLATION LEVEL;
484+
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
486485
PERFORM 1;
487486
RAISE INFO '%', current_setting('transaction_isolation');
488487
COMMIT;

src/test/regress/expected/guc.out

+9
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@ SELECT pg_settings_get_flags('does_not_exist');
839839

840840
CREATE TABLE tab_settings_flags AS SELECT name, category,
841841
'EXPLAIN' = ANY(flags) AS explain,
842+
'NO_RESET' = ANY(flags) AS no_reset,
842843
'NO_RESET_ALL' = ANY(flags) AS no_reset_all,
843844
'NO_SHOW_ALL' = ANY(flags) AS no_show_all,
844845
'NOT_IN_SAMPLE' = ANY(flags) AS not_in_sample,
@@ -906,4 +907,12 @@ SELECT name FROM tab_settings_flags
906907
------
907908
(0 rows)
908909

910+
-- NO_RESET implies NO_RESET_ALL.
911+
SELECT name FROM tab_settings_flags
912+
WHERE no_reset AND NOT no_reset_all
913+
ORDER BY 1;
914+
name
915+
------
916+
(0 rows)
917+
909918
DROP TABLE tab_settings_flags;

src/test/regress/expected/transactions.out

+34
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,40 @@ SELECT * FROM xacttest;
4444
777 | 777.777
4545
(5 rows)
4646

47+
-- Test that transaction characteristics cannot be reset.
48+
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
49+
SELECT COUNT(*) FROM xacttest;
50+
count
51+
-------
52+
5
53+
(1 row)
54+
55+
RESET transaction_isolation; -- error
56+
ERROR: parameter "transaction_isolation" cannot be reset
57+
END;
58+
BEGIN TRANSACTION READ ONLY;
59+
SELECT COUNT(*) FROM xacttest;
60+
count
61+
-------
62+
5
63+
(1 row)
64+
65+
RESET transaction_read_only; -- error
66+
ERROR: parameter "transaction_read_only" cannot be reset
67+
END;
68+
BEGIN TRANSACTION DEFERRABLE;
69+
SELECT COUNT(*) FROM xacttest;
70+
count
71+
-------
72+
5
73+
(1 row)
74+
75+
RESET transaction_deferrable; -- error
76+
ERROR: parameter "transaction_deferrable" cannot be reset
77+
END;
78+
CREATE FUNCTION errfunc() RETURNS int LANGUAGE SQL AS 'SELECT 1'
79+
SET transaction_read_only = on; -- error
80+
ERROR: parameter "transaction_read_only" cannot be set locally in functions
4781
-- Read-only tests
4882
CREATE TABLE writetest (a int);
4983
CREATE TEMPORARY TABLE temptest (a int);

src/test/regress/sql/guc.sql

+5
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ SELECT pg_settings_get_flags(NULL);
324324
SELECT pg_settings_get_flags('does_not_exist');
325325
CREATE TABLE tab_settings_flags AS SELECT name, category,
326326
'EXPLAIN' = ANY(flags) AS explain,
327+
'NO_RESET' = ANY(flags) AS no_reset,
327328
'NO_RESET_ALL' = ANY(flags) AS no_reset_all,
328329
'NO_SHOW_ALL' = ANY(flags) AS no_show_all,
329330
'NOT_IN_SAMPLE' = ANY(flags) AS not_in_sample,
@@ -360,4 +361,8 @@ SELECT name FROM tab_settings_flags
360361
SELECT name FROM tab_settings_flags
361362
WHERE no_show_all AND NOT not_in_sample
362363
ORDER BY 1;
364+
-- NO_RESET implies NO_RESET_ALL.
365+
SELECT name FROM tab_settings_flags
366+
WHERE no_reset AND NOT no_reset_all
367+
ORDER BY 1;
363368
DROP TABLE tab_settings_flags;

src/test/regress/sql/transactions.sql

+18
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,24 @@ SELECT oid FROM pg_class WHERE relname = 'disappear';
3535
-- should have members again
3636
SELECT * FROM xacttest;
3737

38+
-- Test that transaction characteristics cannot be reset.
39+
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
40+
SELECT COUNT(*) FROM xacttest;
41+
RESET transaction_isolation; -- error
42+
END;
43+
44+
BEGIN TRANSACTION READ ONLY;
45+
SELECT COUNT(*) FROM xacttest;
46+
RESET transaction_read_only; -- error
47+
END;
48+
49+
BEGIN TRANSACTION DEFERRABLE;
50+
SELECT COUNT(*) FROM xacttest;
51+
RESET transaction_deferrable; -- error
52+
END;
53+
54+
CREATE FUNCTION errfunc() RETURNS int LANGUAGE SQL AS 'SELECT 1'
55+
SET transaction_read_only = on; -- error
3856

3957
-- Read-only tests
4058

0 commit comments

Comments
 (0)