Browse Source

MDEV-37312 ASAN errors or assertion failure upon attempt to UPDATE FOR PORTION violating long unique under READ COMMITTED

in case of a long unique conflict ha_write_row() used delete_row()
to remove the newly inserted row, and it used rnd_pos() to position
the cursor before deletion. This rnd_pos() was freeing and reallocating
blobs in record[0]. So when the code for FOR PORTION OF did
  store_record(record[2]);
  ha_write_row()
  restore_record(record[2]);
it ended up with blob pointers to a freed memory.

Let's use lookup_handler for deletion.
bb-10.6-release
Sergei Golubchik 2 months ago
parent
commit
633417308f
  1. 17
      mysql-test/suite/period/r/long_unique.result
  2. 21
      mysql-test/suite/period/t/long_unique.test
  3. 13
      sql/handler.cc

17
mysql-test/suite/period/r/long_unique.result

@ -15,3 +15,20 @@ INSERT INTO t1 VALUES (1,'foo','2022-01-01', '2025-01-01');
DELETE FROM t1 FOR PORTION OF app FROM '2023-01-01' TO '2024-01-01';
ERROR 23000: Duplicate entry 'foo' for key 'b'
DROP TABLE t1;
# End of 10.5 tests
#
# MDEV-37312 ASAN errors or assertion failure upon attempt to UPDATE FOR PORTION violating long unique under READ COMMITTED
#
create table t1 (a int, f text unique, s datetime, e datetime, period for p(s,e)) engine=innodb;
insert into t1 values (1,'foo','1900-01-01','2000-01-01'),(2,'bar','1900-01-01','2000-01-01');
set transaction isolation level read committed;
update t1 for portion of p from '1980-01-01' to '1980-01-02' set a = 1;
ERROR 23000: Duplicate entry 'foo' for key 'f'
drop table t1;
create table t1 (a int, f text unique, s datetime, e datetime, period for p(s,e)) engine=innodb partition by hash (a);
insert into t1 values (1,'foo','1900-01-01','2000-01-01'),(2,'bar','1900-01-01','2000-01-01');
set transaction isolation level read committed;
update t1 for portion of p from '1980-01-01' to '1980-01-02' set a = 1;
ERROR 23000: Duplicate entry 'foo' for key 'f'
drop table t1;
# End of 10.6 tests

21
mysql-test/suite/period/t/long_unique.test

@ -1,3 +1,4 @@
--source include/have_innodb.inc
--source include/have_partition.inc
--echo #
@ -21,3 +22,23 @@ INSERT INTO t1 VALUES (1,'foo','2022-01-01', '2025-01-01');
DELETE FROM t1 FOR PORTION OF app FROM '2023-01-01' TO '2024-01-01';
DROP TABLE t1;
--echo # End of 10.5 tests
--echo #
--echo # MDEV-37312 ASAN errors or assertion failure upon attempt to UPDATE FOR PORTION violating long unique under READ COMMITTED
--echo #
create table t1 (a int, f text unique, s datetime, e datetime, period for p(s,e)) engine=innodb;
insert into t1 values (1,'foo','1900-01-01','2000-01-01'),(2,'bar','1900-01-01','2000-01-01');
set transaction isolation level read committed;
--error ER_DUP_ENTRY
update t1 for portion of p from '1980-01-01' to '1980-01-02' set a = 1;
drop table t1;
create table t1 (a int, f text unique, s datetime, e datetime, period for p(s,e)) engine=innodb partition by hash (a);
insert into t1 values (1,'foo','1900-01-01','2000-01-01'),(2,'bar','1900-01-01','2000-01-01');
set transaction isolation level read committed;
--error ER_DUP_ENTRY
update t1 for portion of p from '1980-01-01' to '1980-01-02' set a = 1;
drop table t1;
--echo # End of 10.6 tests

13
sql/handler.cc

@ -3367,7 +3367,7 @@ int handler::create_lookup_handler()
if (!(tmp= clone(table->s->normalized_path.str, table->in_use->mem_root)))
return 1;
lookup_handler= tmp;
return lookup_handler->ha_external_lock(table->in_use, F_RDLCK);
return lookup_handler->ha_external_lock(table->in_use, F_WRLCK);
}
LEX_CSTRING *handler::engine_name()
@ -7774,16 +7774,16 @@ int handler::ha_write_row(const uchar *buf)
if (lookup_handler != this) // INSERT IGNORE or REPLACE or ODKU
{
int olderror= error;
if ((error= rnd_init(0)))
if ((error= lookup_handler->rnd_init(0)))
goto err;
position(buf);
if ((error= rnd_pos(lookup_buffer, ref)))
if ((error= lookup_handler->rnd_pos(lookup_buffer, ref)))
goto err;
increment_statistics(&SSV::ha_delete_count);
TABLE_IO_WAIT(tracker, PSI_TABLE_DELETE_ROW, MAX_KEY, error,
{ error= delete_row(buf);})
rnd_end();
{ error= lookup_handler->delete_row(buf);})
lookup_handler->rnd_end();
if (!error)
error= olderror;
}
@ -7916,8 +7916,7 @@ int handler::ha_delete_row(const uchar *buf)
/*
Normally table->record[0] is used, but sometimes table->record[1] is used.
*/
DBUG_ASSERT(buf == table->record[0] ||
buf == table->record[1]);
DBUG_ASSERT(buf == table->record[0] || buf == table->record[1]);
MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();

Loading…
Cancel
Save