Details
-
Bug
-
Status: Open (View Workflow)
-
Major
-
Resolution: Unresolved
-
10.11
-
None
-
None
-
Can result in unexpected behaviour
Description
With sql_safe_updates=1, a single-table DELETE without WHERE is rejected even when it has LIMIT (ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE). For a system-versioned table the same statement is accepted. The user wrote no WHERE, but the server adds a hidden row_end condition that selects current rows, and the safe-updates check counts that condition as a WHERE clause.
A system-versioned table must behave like a non-versioned one here: the implicit versioning condition is not a restriction written by the user.
Reproduce
set sql_safe_updates= 1; |
|
|
create table plain (x int); |
create table vers (x int) with system versioning; |
|
|
insert into plain values (1), (2); |
insert into vers values (1), (2); |
|
|
delete from plain limit 1; |
delete from vers limit 1; |
|
|
set sql_safe_updates= default; |
drop table plain, vers; |
Result
DELETE FROM plain LIMIT 1 fails, DELETE FROM vers LIMIT 1 succeeds.
For a table whose history is stored in SYSTEM_TIME partitions, MDEV-17613 replaces the row_end condition with the selection of the current partition, so after MDEV-17613 such a table already behaves like a non-versioned one. Only versioned tables without history partitions are affected.
Expected
Both DELETE statements fail with ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE.
Analysis
Sql_cmd_delete::delete_from_single_table() (sql/sql_delete.cc) rejects a DELETE under safe updates when its condition is missing or constant, regardless of LIMIT:
const_cond= (!conds || conds->const_item());
|
...
|
if (safe_update && const_cond) |
... ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
|
For a versioned table, SELECT_LEX::vers_setup_conds(), called from Sql_cmd_delete::prepare_inner(), ANDs the row_end condition into select_lex->where before the condition is fixed. So conds is never empty and const_cond is false. Only the later "no index used and no LIMIT" check applies, and LIMIT satisfies it.
Fix
Judge the WHERE written by the user, not conds:
- In Sql_cmd_delete::prepare_inner(), before vers_setup_conds(), remember whether the user wrote a WHERE clause. Do it only when !skip_setup_conds(thd): on prepared statement re-execution select_lex->where is restored from prep_where, which already contains the versioning condition.
- In delete_from_single_table(), use that flag in the safe-updates check only: if (safe_update && (const_cond || no_user_where)). Keep const_cond itself unchanged, since it also controls const_cond_result and delete_all_rows().
This does not cover a constant WHERE written by the user (e.g. WHERE 1), which is still combined with the versioning condition. Covering it requires the constness of the user's part after fix_fields.
Related case (to be verified)
With an index whose first keypart is row_end (e.g. KEY (row_end)), the hidden row_end condition may produce a range access on that index. That satisfies the "no index used and no LIMIT" check of single-table UPDATE and DELETE, and the full-join check of multi-table UPDATE/DELETE. So UPDATE or DELETE without WHERE and without LIMIT could be accepted for such a versioned table, while a non-versioned table with the same index is rejected. This follows from the code but has not been reproduced yet.
set sql_safe_updates= 1; |
create table t1 ( |
x int, |
ts timestamp(6) as row start invisible, |
te timestamp(6) as row end invisible, |
period for system_time (ts, te), |
key (te) |
) with system versioning; |
insert into t1 values (1), (2); |
update t1 set x= x + 1; |
delete from t1; |
set sql_safe_updates= default; |
drop table t1; |
Expected: both statements fail with ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE.
Attachments
Issue Links
- relates to
-
MDEV-17613 Read sole now-partition for non-historical versioned query
-
- Closed
-