Bug #91080 | sql_safe_updates behaves inconsistently between delete and select | ||
---|---|---|---|
Submitted: | 30 May 2018 17:37 | Modified: | 27 Sep 2018 17:55 |
Reporter: | Nakoa Mccullough | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: DML | Severity: | S2 (Serious) |
Version: | 5.7.22, 8.0.11 | OS: | Oracle Linux |
Assigned to: | CPU Architecture: | Any |
[30 May 2018 17:37]
Nakoa Mccullough
[7 Jun 2018 5:39]
MySQL Verification Team
Hello Nakoa, Thank you for the report and test case. Observed the issue on 5.7.22 when --safe-updates on. As you noted, for DELETE - issue occurring when 37136 values in the 'IN' clause(up-to 37135 no issues observed). Thanks, Umesh
[8 Jun 2018 7:06]
MySQL Verification Team
-- 5.7.22 - default settings mysql> create database if not exists test; mysql> use test; mysql> drop table if exists del_test; mysql> CREATE TABLE `del_test` (`id` int(11) NOT NULL, UNIQUE KEY `id` (`id`))ENGINE=InnoDB DEFAULT CHARSET=latin1; mysql> set @id:=0; mysql> insert into `del_test` values -> (@id:=@id+1) -> , (@id:=@id+1) -> , (@id:=@id+1) -> , (@id:=@id+1); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> insert into `del_test`(`id`) select @id:=@id+1 from `del_test` k1, `del_test` k2, `del_test` k3, `del_test` k4,`del_test` k5,`del_test` k6, `del_test` k7, `del_test` k8, `del_test` k9,`del_test` k0,`del_test` ka, `del_test` kb, `del_test` kc, `del_test` kd limit 10000000; Query OK, 10000000 rows affected (1 min 13.07 sec) Records: 10000000 Duplicates: 0 Warnings: 0 # 91080_DELETE.sql - DELETE FROM test.del_test WHERE id IN (1,2....37135); - this works bin/mysql -uroot -S /tmp/mysql_ushastry57.sock -e "SELECT COUNT(*) from test.del_test;" +----------+ | COUNT(*) | +----------+ | 10000004 | +----------+ bin/mysql -uroot -S /tmp/mysql_ushastry57.sock --safe-updates=1 < /tmp/91080_DELETE.sql bin/mysql -uroot -S /tmp/mysql_ushastry57.sock -e "SELECT COUNT(*) from test.del_test;" +----------+ | COUNT(*) | +----------+ | 9962869 | +----------+ # 91080_DELETE.sql - DELETE FROM test.del_test WHERE id IN (1,2....37136); - this complains bin/mysql -uroot -S /tmp/mysql_ushastry57.sock -e "SELECT COUNT(*) from test.del_test;" +----------+ | COUNT(*) | +----------+ | 10000004 | +----------+ bin/mysql -uroot -S /tmp/mysql_ushastry57.sock --safe-updates=1 < /tmp/91080_DELETE.sql ERROR 1175 (HY000) at line 1: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column bin/mysql -uroot -S /tmp/mysql_ushastry57.sock -e "SELECT COUNT(*) from test.del_test;" +----------+ | COUNT(*) | +----------+ | 10000004 | +----------+
[8 Jun 2018 10:06]
Sreeharsha Ramanavarapu
Posted by developer: DELETE FROM test.del_test WHERE id IN (1,372....37136); g++ -g -std=c++0x genereate_delete.cpp b sql_delete.cc:141 b sql_delete.cc:328 mysql> source /home/sreeharsha/changes/17668844/code-5.7/del_explain.txt; ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column +---------+------+-------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------------------------------------------------------------------------------------+ | Warning | 3170 | Memory capacity of 8388608 bytes for 'range_optimizer_max_mem_size' exceeded. Range optimization was not done for this query. | | Error | 1175 | You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column | +---------+------+-------------------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec) mysql> So range optimizer couldn't be used because it exceeded the max memory size. The below code from "Sql_cmd_delete::mysql_delete" will only check whether range optimizer is being used. If not it assumes all tables will be deleted. Hence the error. This fails even for EXPLAIN DELETE... /* If running in safe sql mode, don't allow updates without keys */ if (table->quick_keys.is_clear_all()) { thd->server_status|=SERVER_QUERY_NO_INDEX_USED; if (safe_update && !using_limit) { free_underlaid_joins(thd, select_lex); my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0)); DBUG_RETURN(TRUE); } }
[20 Jun 2018 5:26]
Sreeharsha Ramanavarapu
Posted by developer: This problem can happen even with data truncation: CREATE TABLE del_test (c1 INT NOT NULL, c2 VARCHAR(200) NOT NULL, UNIQUE KEY idx1 (c1), UNIQUE KEY idx2 (c2)); Query OK, 0 rows affected (0.18 sec) mysql> INSERT INTO del_test VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'); Query OK, 4 rows affected (0.02 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> set sql_mode=''; Query OK, 0 rows affected (0.00 sec) mysql> EXPLAIN DELETE FROM del_test WHERE c2 IN(1,2222); +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | DELETE | del_test | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 100.00 | Using where | +----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 2 warnings (0.00 sec) mysql> DELETE FROM del_test WHERE c2 IN(1,2222); Query OK, 0 rows affected, 4 warnings (0.01 sec) mysql> show warnings; +---------+------+---------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: 'a' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'b' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'c' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'd' | +---------+------+---------------------------------------+ 4 rows in set (0.00 sec) mysql> mysql> SET SESSION sql_safe_updates=1; Query OK, 0 rows affected (0.00 sec) mysql> mysql> DELETE FROM del_test WHERE c2 IN(1,2222); ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
[25 Jun 2018 6:05]
Sreeharsha Ramanavarapu
Posted by developer: Our documentation states: https://dev.mysql.com/doc/refman/8.0/en/mysql-command-options.html#option_mysql_safe-updat... "Permit only those UPDATE and DELETE statements that specify which rows to modify by using key values. If you have set this option in an option file, you can override it by using --safe-updates on the command line. See Section 4.5.1.6, “mysql Tips”, for more information about this option. " https://dev.mysql.com/doc/refman/8.0/en/mysql-tips.html "It is helpful for cases when you might have issued a DELETE FROM tbl_name statement but forgotten the WHERE clause. Normally, such a statement deletes all rows from the table. With --safe-updates, you can delete rows only by specifying the key values that identify them. This helps prevent accidents. " Notice the difference. One place mentions " which rows to modify by using key values" and another says "you might have issued a DELETE FROM tbl_name statement but forgotten the WHERE clause" This is interpreted in the code for single table UPDATE/DELETE as does table have any "quick_keys" set. For multi-table UPDATE/DELETE this becomes a check for "tab->quick()". Safe updates error is returned when range optimization or index lookup isn't possible. This can happen with: 1) RightNow's DML exceeds 'range_optimizer_max_mem_size' and because of this quick_keys isn't set. UPDATE/DELETE code interprets this as no "key values" 2) Data truncation when column with index is a varchar and value is an integer. Example posted on bug page. Possible fix approaches: --------------------------------- 1) Ignore the requirement for "key values" or index usage that is present now. Just add an condition to check if the WHERE conditions are present and they aren't constant before returning the safe updates error. Patch for this approach can be seen on the bug page. Chaithra has commented on some of her concerns to my patch on mail. Pros: a) Simplifies the conditions for the user. Cons: a) Approach to deal with multi-table UPDATE/DELETE isn't clear. (See error_if_full_join()) b) Documentation will need to be modified. Will it be interpreted as change of behavior in GA version? 2) Append the warning (data truncation / 'range_optimizer_max_mem_size' exceeded) to the safe update error. Pros: a) Informs user clearly why safe updates error is thrown. Cons: a) User might say that table scan is a perfectly legitimate way delete/update a subset of the rows.
[25 Jun 2018 19:30]
Nakoa Mccullough
This is just very inconsistent behavior. For instance, the query will successfully if a limit of the size of the 'IN' clause is added. I tweaked the above test case to demonstrate this: [koa.mccullough@bastionhg01 ~]$ let x=37130; while [ 0==0 ]; do echo -n '.'; seq 1 $x|awk 'BEGIN{printf("DELETE FROM `del_test` WHERE id IN(")}{if(NR>1){printf(",")}{printf("%d", $1)}}END{printf(");\n")}'| mysql; if [ $? -ne 0 ]; then echo; echo $x; seq 1 $x|awk 'BEGIN{printf("SELECT * FROM `del_test` WHERE id IN (")}{if(NR>1){printf(",")}{printf("%d",$1)}}END{printf(");\n")}'|mysql; seq 1 $x|awk 'BEGIN{printf("DELETE FROM `del_test` WHERE id IN (")}{if(NR>1){printf(",")}{printf("%d",$1)}; last_x=$1}END{printf(") LIMIT %d;\n", last_x)}'|mysql; break; fi; let x=$x+1; done .......ERROR 1175 (HY000) at line 1: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column 37136 The problem with adding limits every where is that this will trigger RBR when running in MIXED mode. Not to mention that it makes the code that generates these statement much more complicated. MySQL has this information and should be able to apply an implicit limit if the index is unique once it hits the optimizer. --Koa McCullough
[26 Jun 2018 4:29]
Sreeharsha Ramanavarapu
Posted by developer: Koa, The workaround for this problem would be to increase the value of range_optimizer_max_mem_size (https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_range_optimize...). Alternately, you can turn off the safe-updates option altogether: https://dev.mysql.com/doc/refman/8.0/en/mysql-command-options.html#option_mysql_safe-updat... . We are working on clarifying the documentation and returning a better error message.
[26 Jun 2018 15:57]
Nakoa Mccullough
Turning off safe updates has always been an option but, that defeats the purpose of having the feature in the first place. The purpose of safe updates is to make sure that you are using a key to delete data. We hand the server a list of primary keys (the best possible choice for a key to delete off of) and get an error saying we are not deleting off of a key is a very poor user experience. Changing the value of range_optimizer_mem_size is a bandaid not a fix. We don't want to be in the position of having to change server configuration when ever a dev decides to write a test that pushes the bounds of what the system can handle. We want to encourage that kind of testing not make it painful. --Koa McCullough
[27 Sep 2018 17:55]
Paul DuBois
Posted by developer: Fixed in 5.7.24, 8.0.13. For DELETE and UPDATE that produced an error due to sql_safe_updates being enabled, the error message was insufficiently informative. The message now indicates that data truncation occurred or the range_optimizer_max_mem_size value was exceeded. Additionally: (1) Using EXPLAIN for such statements does not produce an error, enabling users to see from EXPLAIN output why an index is not used; (2) For multiple-table deletes and updates, an error is produced with safe updates enabled only if the target table or tables use a table scan.