mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-40041 Assertion `fixed()' failed in Item_func_connection_id::val_int on INSERT w/ TRIGGER
Under LOCK TABLES close_thread_tables() cleans up session-dependent virtual column expressions (vcol_cleanup_expr()) of all tables in thd->open_tables, because they are not closed at the end of the statement. A trigger sub-statement also calls close_thread_tables() at its end, so a BEFORE INSERT trigger cleaned up the expressions of the table the outer INSERT was still writing to. Then TABLE::update_virtual_fields() evaluated the unfixed CONNECTION_ID() and failed the assertion. The fix skips vcol_cleanup_expr() in close_thread_tables() when in sub-statement. The outer statement cleans up the expressions when it closes its tables.
This commit is contained in:
@@ -248,3 +248,106 @@ select unix_timestamp(f), unix_timestamp(g) from t;
|
||||
unix_timestamp(f) unix_timestamp(g)
|
||||
1 2
|
||||
drop table t;
|
||||
#
|
||||
# MDEV-40041 Assertion `fixed()' failed in Item_func_connection_id::val_int on INSERT w/ TRIGGER
|
||||
#
|
||||
create table t (c int);
|
||||
lock table t write;
|
||||
create or replace table t (a int as (connection_id()));
|
||||
create trigger a before insert on t for each row set @a:=1;
|
||||
insert into t values ('');
|
||||
Warnings:
|
||||
Warning 1906 The value specified for generated column 'a' in table 't' has been ignored
|
||||
Warning 1366 Incorrect integer value: '' for column `test`.`t`.`a` at row 1
|
||||
select a = connection_id() from t;
|
||||
a = connection_id()
|
||||
1
|
||||
unlock tables;
|
||||
drop table t;
|
||||
# Simpler variant: table and trigger exist before LOCK TABLES
|
||||
create table t1 (a int as (connection_id()));
|
||||
create trigger tr1 before insert on t1 for each row set @a:=1;
|
||||
lock table t1 write;
|
||||
insert into t1 values ('');
|
||||
Warnings:
|
||||
Warning 1906 The value specified for generated column 'a' in table 't1' has been ignored
|
||||
Warning 1366 Incorrect integer value: '' for column `test`.`t1`.`a` at row 1
|
||||
select a = connection_id() from t1;
|
||||
a = connection_id()
|
||||
1
|
||||
unlock tables;
|
||||
drop table t1;
|
||||
# Trigger writes to another locked table with session vcol
|
||||
create table t1 (a int);
|
||||
create table t2 (a int primary key, b int as (connection_id()));
|
||||
create trigger tr1 before insert on t1 for each row insert into t2 values (new.a, '');
|
||||
lock table t1 write, t2 write;
|
||||
# Trigger fires for each row
|
||||
insert into t1 values (1), (2), (3);
|
||||
select a, b = connection_id() from t2;
|
||||
a b = connection_id()
|
||||
1 1
|
||||
2 1
|
||||
3 1
|
||||
# Duplicate key in trigger
|
||||
insert into t1 values (1);
|
||||
ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
|
||||
# Direct INSERT after failed trigger, then duplicate key in trigger
|
||||
insert into t2 (a) values (4);
|
||||
select a, b = connection_id() from t2;
|
||||
a b = connection_id()
|
||||
1 1
|
||||
2 1
|
||||
3 1
|
||||
4 1
|
||||
insert into t1 values (4);
|
||||
ERROR 23000: Duplicate entry '4' for key 'PRIMARY'
|
||||
select a, b = connection_id() from t2;
|
||||
a b = connection_id()
|
||||
1 1
|
||||
2 1
|
||||
3 1
|
||||
4 1
|
||||
unlock tables;
|
||||
drop tables t1, t2;
|
||||
# Trigger fails, later statements use the same locked table
|
||||
create table t1 (a int as (connection_id()), b int);
|
||||
create trigger tr1 before insert on t1 for each row
|
||||
if new.b = 0 then signal sqlstate '45000'; end if|
|
||||
create trigger tr2 after insert on t1 for each row
|
||||
if new.b = -1 then signal sqlstate '45000'; end if|
|
||||
lock table t1 write;
|
||||
# BEFORE trigger fails
|
||||
insert into t1 (b) values (0);
|
||||
ERROR 45000: Unhandled user-defined exception condition
|
||||
select a = connection_id(), b from t1;
|
||||
a = connection_id() b
|
||||
# AFTER trigger fails
|
||||
insert into t1 (b) values (-1);
|
||||
ERROR 45000: Unhandled user-defined exception condition
|
||||
select a = connection_id(), b from t1;
|
||||
a = connection_id() b
|
||||
1 -1
|
||||
# AFTER trigger of the first row, then the second row is written
|
||||
insert into t1 values ('', 1), ('', 2);
|
||||
Warnings:
|
||||
Warning 1906 The value specified for generated column 'a' in table 't1' has been ignored
|
||||
Warning 1366 Incorrect integer value: '' for column `test`.`t1`.`a` at row 1
|
||||
Warning 1906 The value specified for generated column 'a' in table 't1' has been ignored
|
||||
Warning 1366 Incorrect integer value: '' for column `test`.`t1`.`a` at row 2
|
||||
select a = connection_id(), b from t1;
|
||||
a = connection_id() b
|
||||
1 -1
|
||||
1 1
|
||||
1 2
|
||||
# BEFORE trigger fails after successful INSERT
|
||||
insert into t1 (b) values (0);
|
||||
ERROR 45000: Unhandled user-defined exception condition
|
||||
select a = connection_id(), b from t1;
|
||||
a = connection_id() b
|
||||
1 -1
|
||||
1 1
|
||||
1 2
|
||||
unlock tables;
|
||||
drop table t1;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -197,3 +197,73 @@ flush table t;
|
||||
select unix_timestamp(f), unix_timestamp(g) from t;
|
||||
# Cleanup
|
||||
drop table t;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-40041 Assertion `fixed()' failed in Item_func_connection_id::val_int on INSERT w/ TRIGGER
|
||||
--echo #
|
||||
create table t (c int);
|
||||
lock table t write;
|
||||
create or replace table t (a int as (connection_id()));
|
||||
create trigger a before insert on t for each row set @a:=1;
|
||||
insert into t values ('');
|
||||
select a = connection_id() from t;
|
||||
unlock tables;
|
||||
drop table t;
|
||||
|
||||
--echo # Simpler variant: table and trigger exist before LOCK TABLES
|
||||
create table t1 (a int as (connection_id()));
|
||||
create trigger tr1 before insert on t1 for each row set @a:=1;
|
||||
lock table t1 write;
|
||||
insert into t1 values ('');
|
||||
select a = connection_id() from t1;
|
||||
unlock tables;
|
||||
drop table t1;
|
||||
|
||||
--echo # Trigger writes to another locked table with session vcol
|
||||
create table t1 (a int);
|
||||
create table t2 (a int primary key, b int as (connection_id()));
|
||||
create trigger tr1 before insert on t1 for each row insert into t2 values (new.a, '');
|
||||
lock table t1 write, t2 write;
|
||||
--echo # Trigger fires for each row
|
||||
insert into t1 values (1), (2), (3);
|
||||
select a, b = connection_id() from t2;
|
||||
--echo # Duplicate key in trigger
|
||||
--error ER_DUP_ENTRY
|
||||
insert into t1 values (1);
|
||||
--echo # Direct INSERT after failed trigger, then duplicate key in trigger
|
||||
insert into t2 (a) values (4);
|
||||
select a, b = connection_id() from t2;
|
||||
--error ER_DUP_ENTRY
|
||||
insert into t1 values (4);
|
||||
select a, b = connection_id() from t2;
|
||||
unlock tables;
|
||||
drop tables t1, t2;
|
||||
|
||||
--echo # Trigger fails, later statements use the same locked table
|
||||
create table t1 (a int as (connection_id()), b int);
|
||||
--delimiter |
|
||||
create trigger tr1 before insert on t1 for each row
|
||||
if new.b = 0 then signal sqlstate '45000'; end if|
|
||||
create trigger tr2 after insert on t1 for each row
|
||||
if new.b = -1 then signal sqlstate '45000'; end if|
|
||||
--delimiter ;
|
||||
lock table t1 write;
|
||||
--echo # BEFORE trigger fails
|
||||
--error ER_SIGNAL_EXCEPTION
|
||||
insert into t1 (b) values (0);
|
||||
select a = connection_id(), b from t1;
|
||||
--echo # AFTER trigger fails
|
||||
--error ER_SIGNAL_EXCEPTION
|
||||
insert into t1 (b) values (-1);
|
||||
select a = connection_id(), b from t1;
|
||||
--echo # AFTER trigger of the first row, then the second row is written
|
||||
insert into t1 values ('', 1), ('', 2);
|
||||
select a = connection_id(), b from t1;
|
||||
--echo # BEFORE trigger fails after successful INSERT
|
||||
--error ER_SIGNAL_EXCEPTION
|
||||
insert into t1 (b) values (0);
|
||||
select a = connection_id(), b from t1;
|
||||
unlock tables;
|
||||
drop table t1;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
@@ -876,8 +876,11 @@ int close_thread_tables(THD *thd) noexcept
|
||||
/*
|
||||
For simple locking we cleanup it here because we don't close thread
|
||||
tables. For prelocking we close it when we do close thread tables.
|
||||
Sub-statement does not cleanup: the outer statement may still use
|
||||
the tables, and the top-level statement cleans up all locked tables
|
||||
when it ends.
|
||||
*/
|
||||
if (thd->locked_tables_mode != LTM_PRELOCKED)
|
||||
if (thd->locked_tables_mode != LTM_PRELOCKED && !thd->in_sub_stmt)
|
||||
table->vcol_cleanup_expr(thd);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user