Skip to content

Commit 4a45b4e

Browse files
zongzhi.czzclaude
andcommitted
Add global support for sql_safe_updates system variable
Add command-line option --sql-safe-updates to allow setting the global default value for sql_safe_updates from the configuration file (my.cnf) or command line. Changes: - Add opt_sql_safe_updates variable and source_sql_safe_updates for tracking the command-line option source - Add --sql-safe-updates option to my_long_options array - Synchronize the command-line value to global_system_variables.option_bits during server startup This follows the same pattern used for --autocommit option, as Sys_var_bit does not support command-line options directly. Usage: # In my.cnf [mysqld] sql-safe-updates = 1 # Or via command line mysqld --sql-safe-updates=1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 8e432df commit 4a45b4e

File tree

4 files changed

+199
-0
lines changed

4 files changed

+199
-0
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#
2+
# Test 1: Verify global and session default values are ON
3+
# when server is started with --sql-safe-updates=1
4+
#
5+
SELECT @@global.sql_safe_updates AS global_value;
6+
global_value
7+
1
8+
SELECT @@session.sql_safe_updates AS session_value;
9+
session_value
10+
1
11+
#
12+
# Test 2: Verify that sql_safe_updates is enforced
13+
# (UPDATE without WHERE using KEY should fail)
14+
#
15+
CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50));
16+
INSERT INTO t1 VALUES (1, 'test1'), (2, 'test2'), (3, 'test3');
17+
# This should fail with sql_safe_updates enabled
18+
UPDATE t1 SET name = 'updated';
19+
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column.
20+
# This should fail with sql_safe_updates enabled
21+
DELETE FROM t1;
22+
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column.
23+
# UPDATE with WHERE using KEY column should succeed
24+
UPDATE t1 SET name = 'updated1' WHERE id = 1;
25+
SELECT * FROM t1 WHERE id = 1;
26+
id name
27+
1 updated1
28+
#
29+
# Test 3: Verify session can override the global value
30+
#
31+
SET SESSION sql_safe_updates = OFF;
32+
SELECT @@session.sql_safe_updates AS session_after_off;
33+
session_after_off
34+
0
35+
SELECT @@global.sql_safe_updates AS global_unchanged;
36+
global_unchanged
37+
1
38+
# Now UPDATE without WHERE should succeed
39+
UPDATE t1 SET name = 'all_updated';
40+
SELECT * FROM t1 ORDER BY id;
41+
id name
42+
1 all_updated
43+
2 all_updated
44+
3 all_updated
45+
#
46+
# Test 4: Verify new session inherits global value
47+
#
48+
# Reconnect to get a new session
49+
SELECT @@session.sql_safe_updates AS new_session_value;
50+
new_session_value
51+
1
52+
SELECT @@global.sql_safe_updates AS global_value;
53+
global_value
54+
1
55+
# UPDATE without WHERE should fail in new session
56+
UPDATE t1 SET name = 'fail';
57+
ERROR HY000: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column.
58+
#
59+
# Test 5: Verify SET GLOBAL works
60+
#
61+
SET GLOBAL sql_safe_updates = OFF;
62+
SELECT @@global.sql_safe_updates AS global_after_set_off;
63+
global_after_set_off
64+
0
65+
# Reconnect to verify new session gets the new global value
66+
SELECT @@session.sql_safe_updates AS new_session_inherits_global;
67+
new_session_inherits_global
68+
0
69+
# UPDATE without WHERE should now succeed in new session
70+
UPDATE t1 SET name = 'success';
71+
SELECT * FROM t1 ORDER BY id;
72+
id name
73+
1 success
74+
2 success
75+
3 success
76+
#
77+
# Cleanup
78+
#
79+
SET GLOBAL sql_safe_updates = OFF;
80+
DROP TABLE t1;
81+
#
82+
# Test completed successfully
83+
#
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--sql-safe-updates=1
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
###############################################################################
2+
# Test for --sql-safe-updates command line option #
3+
# #
4+
# This test verifies that the --sql-safe-updates command line option works #
5+
# correctly to set the global default value for sql_safe_updates. #
6+
# #
7+
# The server is started with --sql-safe-updates=1 (see .opt file) #
8+
###############################################################################
9+
10+
# Force restart after test because we modify global sql_safe_updates
11+
--source include/force_restart.inc
12+
13+
--echo #
14+
--echo # Test 1: Verify global and session default values are ON
15+
--echo # when server is started with --sql-safe-updates=1
16+
--echo #
17+
18+
SELECT @@global.sql_safe_updates AS global_value;
19+
SELECT @@session.sql_safe_updates AS session_value;
20+
21+
--echo #
22+
--echo # Test 2: Verify that sql_safe_updates is enforced
23+
--echo # (UPDATE without WHERE using KEY should fail)
24+
--echo #
25+
26+
CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50));
27+
INSERT INTO t1 VALUES (1, 'test1'), (2, 'test2'), (3, 'test3');
28+
29+
--echo # This should fail with sql_safe_updates enabled
30+
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
31+
UPDATE t1 SET name = 'updated';
32+
33+
--echo # This should fail with sql_safe_updates enabled
34+
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
35+
DELETE FROM t1;
36+
37+
--echo # UPDATE with WHERE using KEY column should succeed
38+
UPDATE t1 SET name = 'updated1' WHERE id = 1;
39+
SELECT * FROM t1 WHERE id = 1;
40+
41+
--echo #
42+
--echo # Test 3: Verify session can override the global value
43+
--echo #
44+
45+
SET SESSION sql_safe_updates = OFF;
46+
SELECT @@session.sql_safe_updates AS session_after_off;
47+
SELECT @@global.sql_safe_updates AS global_unchanged;
48+
49+
--echo # Now UPDATE without WHERE should succeed
50+
UPDATE t1 SET name = 'all_updated';
51+
SELECT * FROM t1 ORDER BY id;
52+
53+
--echo #
54+
--echo # Test 4: Verify new session inherits global value
55+
--echo #
56+
57+
--echo # Reconnect to get a new session
58+
--connect (con1, localhost, root,,)
59+
60+
SELECT @@session.sql_safe_updates AS new_session_value;
61+
SELECT @@global.sql_safe_updates AS global_value;
62+
63+
--echo # UPDATE without WHERE should fail in new session
64+
--error ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
65+
UPDATE t1 SET name = 'fail';
66+
67+
--connection default
68+
--disconnect con1
69+
70+
--echo #
71+
--echo # Test 5: Verify SET GLOBAL works
72+
--echo #
73+
74+
SET GLOBAL sql_safe_updates = OFF;
75+
SELECT @@global.sql_safe_updates AS global_after_set_off;
76+
77+
--echo # Reconnect to verify new session gets the new global value
78+
--connect (con2, localhost, root,,)
79+
80+
SELECT @@session.sql_safe_updates AS new_session_inherits_global;
81+
82+
--echo # UPDATE without WHERE should now succeed in new session
83+
UPDATE t1 SET name = 'success';
84+
SELECT * FROM t1 ORDER BY id;
85+
86+
--connection default
87+
--disconnect con2
88+
89+
--echo #
90+
--echo # Cleanup
91+
--echo #
92+
93+
# Reset to default OFF to avoid affecting MTR check_warnings
94+
SET GLOBAL sql_safe_updates = OFF;
95+
DROP TABLE t1;
96+
97+
--echo #
98+
--echo # Test completed successfully
99+
--echo #

sql/mysqld.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,8 @@ const char *my_localhost = "localhost";
10481048
bool opt_large_files = sizeof(my_off_t) > 4;
10491049
static bool opt_autocommit; ///< for --autocommit command-line option
10501050
static get_opt_arg_source source_autocommit;
1051+
static bool opt_sql_safe_updates; ///< for --sql-safe-updates command-line option
1052+
static get_opt_arg_source source_sql_safe_updates;
10511053

10521054
/*
10531055
Used with --help for detailed option
@@ -9283,6 +9285,14 @@ struct my_option my_long_options[] = {
92839285
"Option used by mysql-test for debugging and testing of replication.",
92849286
&opt_sporadic_binlog_dump_fail, &opt_sporadic_binlog_dump_fail, nullptr,
92859287
GET_BOOL, NO_ARG, 0, 0, 0, nullptr, 0, nullptr},
9288+
/*
9289+
Because Sys_var_bit does not support command-line options, we need to
9290+
explicitly add one for --sql-safe-updates
9291+
*/
9292+
{"sql-safe-updates", 0, "Set default value for sql_safe_updates (0 or 1)",
9293+
&opt_sql_safe_updates, &opt_sql_safe_updates, nullptr, GET_BOOL, OPT_ARG,
9294+
0, 0, 0, &source_sql_safe_updates, /* arg_source, to be copied to Sys_var */
9295+
0, nullptr},
92869296
{"ssl", OPT_USE_SSL,
92879297
"Enable SSL for connection (automatically enabled with other flags).",
92889298
&opt_use_ssl, &opt_use_ssl, nullptr, GET_BOOL, OPT_ARG, 1, 0, 0, nullptr,
@@ -11270,6 +11280,12 @@ static int get_options(int *argc_ptr, char ***argv_ptr) {
1127011280
Sys_autocommit_ptr->set_source_name(opt->arg_source->m_path_name);
1127111281
Sys_autocommit_ptr->set_source(opt->arg_source->m_source);
1127211282

11283+
// Synchronize @@global.sql_safe_updates value on --sql-safe-updates
11284+
if (opt_sql_safe_updates)
11285+
global_system_variables.option_bits |= OPTION_SAFE_UPDATES;
11286+
else
11287+
global_system_variables.option_bits &= ~OPTION_SAFE_UPDATES;
11288+
1127311289
global_system_variables.sql_mode =
1127411290
expand_sql_mode(global_system_variables.sql_mode, nullptr);
1127511291

0 commit comments

Comments
 (0)