MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables
Alternative, more general fix, Variant 2. The problem was as follows: Suppose we are running a PS/SP statement and we get an error while doing optimization that is done once per statement life. This may leave the statement data structures in an undefined state, where it is not safe to execute it again. The fix: introduce LEX::needs_reprepare and set it in such cases. Make PS and SP runtime check it and re-prepare the statement before executing it again. We do not use Reprepare_observer, because it turns out it is tightly tied to watching versions of statement's objects. For example, it must not be used when running the statement for the first time, exactly when the once-per-statement-lifetime optimizations are done.
This commit is contained in:
parent
491e2b17a9
commit
0e21ff8ca4
7 changed files with 116 additions and 1 deletions
|
@ -2070,3 +2070,50 @@ SHOW CREATE DATABASE `#testone#■■■■■■■■■■■■■■■■
|
|||
ERROR 42000: Incorrect database name '#testone#■■■■■■■■■■■■■■■■■■■■■■■■■■■■■...'
|
||||
SET NAMES DEFAULT;
|
||||
# End of 10.5 Test
|
||||
#
|
||||
# MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables on 2nd execution of PS
|
||||
#
|
||||
CREATE TABLE t (a INT);
|
||||
INSERT INTO t VALUES (1),(2);
|
||||
PREPARE stmt FROM 'CREATE TABLE tmp AS SELECT * FROM (select t1.* from t t1 join t t2 on(t1.a = t2.a)) sq WHERE "x"=0';
|
||||
EXECUTE stmt;
|
||||
ERROR 22007: Truncated incorrect DECIMAL value: 'x'
|
||||
EXECUTE stmt;
|
||||
ERROR 22007: Truncated incorrect DECIMAL value: 'x'
|
||||
DEALLOCATE PREPARE stmt;
|
||||
# Now, run the same in SP:
|
||||
create procedure sp1()
|
||||
begin
|
||||
declare i int unsigned default 1;
|
||||
declare continue handler for SQLEXCEPTION
|
||||
begin
|
||||
select 'In Continue handler' as printf;
|
||||
end;
|
||||
while i <= 3 do
|
||||
begin
|
||||
select concat('Iteration ', i) as printf;
|
||||
CREATE temporary TABLE tmp AS
|
||||
SELECT * FROM (select t1.* from t t1 join t t2 on(t1.a = t2.a)) sq WHERE "x"=0 ;
|
||||
drop temporary table if exists tmp;
|
||||
set i=i+1;
|
||||
end;
|
||||
end while;
|
||||
end|
|
||||
call sp1();
|
||||
printf
|
||||
Iteration 1
|
||||
printf
|
||||
In Continue handler
|
||||
printf
|
||||
Iteration 2
|
||||
printf
|
||||
In Continue handler
|
||||
printf
|
||||
Iteration 3
|
||||
printf
|
||||
In Continue handler
|
||||
Warnings:
|
||||
Note 1051 Unknown table 'test.tmp'
|
||||
DROP PROCEDURE sp1;
|
||||
DROP TABLE t;
|
||||
# End of 11.7 Test
|
||||
|
|
|
@ -1945,3 +1945,46 @@ SHOW CREATE DATABASE `#testone#■■■■■■■■■■■■■■■■
|
|||
SET NAMES DEFAULT;
|
||||
|
||||
--echo # End of 10.5 Test
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables on 2nd execution of PS
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t (a INT);
|
||||
INSERT INTO t VALUES (1),(2); # Optional, fails either way
|
||||
|
||||
PREPARE stmt FROM 'CREATE TABLE tmp AS SELECT * FROM (select t1.* from t t1 join t t2 on(t1.a = t2.a)) sq WHERE "x"=0';
|
||||
--error ER_TRUNCATED_WRONG_VALUE
|
||||
EXECUTE stmt;
|
||||
--error ER_TRUNCATED_WRONG_VALUE
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
--echo # Now, run the same in SP:
|
||||
delimiter |;
|
||||
|
||||
create procedure sp1()
|
||||
begin
|
||||
declare i int unsigned default 1;
|
||||
declare continue handler for SQLEXCEPTION
|
||||
begin
|
||||
select 'In Continue handler' as printf;
|
||||
end;
|
||||
while i <= 3 do
|
||||
begin
|
||||
select concat('Iteration ', i) as printf;
|
||||
CREATE temporary TABLE tmp AS
|
||||
SELECT * FROM (select t1.* from t t1 join t t2 on(t1.a = t2.a)) sq WHERE "x"=0 ;
|
||||
drop temporary table if exists tmp;
|
||||
set i=i+1;
|
||||
end;
|
||||
end while;
|
||||
end|
|
||||
|
||||
delimiter ;|
|
||||
call sp1();
|
||||
|
||||
DROP PROCEDURE sp1;
|
||||
DROP TABLE t;
|
||||
|
||||
--echo # End of 11.7 Test
|
||||
|
|
|
@ -459,7 +459,7 @@ int sp_lex_keeper::validate_lex_and_exec_core(THD *thd, uint *nextp,
|
|||
|
||||
while (true)
|
||||
{
|
||||
if (instr->is_invalid())
|
||||
if (instr->is_invalid() || m_lex->needs_reprepare)
|
||||
{
|
||||
thd->clear_error();
|
||||
free_lex(thd);
|
||||
|
|
|
@ -1336,6 +1336,7 @@ void LEX::start(THD *thd_arg)
|
|||
exchange= 0;
|
||||
|
||||
table_count_update= 0;
|
||||
needs_reprepare= false;
|
||||
|
||||
memset(&trg_chistics, 0, sizeof(trg_chistics));
|
||||
DBUG_VOID_RETURN;
|
||||
|
|
|
@ -3136,6 +3136,13 @@ public:
|
|||
/* Query Plan Footprint of a currently running select */
|
||||
Explain_query *explain;
|
||||
|
||||
/*
|
||||
If true, query optimizer has encountered an unrecoverable error when doing
|
||||
once-per-statement optimization and it is not safe to re-execute this
|
||||
statement.
|
||||
*/
|
||||
bool needs_reprepare{false};
|
||||
|
||||
/*
|
||||
LEX which represents current statement (conventional, SP or PS)
|
||||
|
||||
|
|
|
@ -4401,6 +4401,16 @@ Prepared_statement::execute_loop(String *expanded_query,
|
|||
*/
|
||||
DBUG_ASSERT(thd->free_list == NULL);
|
||||
|
||||
if (lex->needs_reprepare)
|
||||
{
|
||||
/*
|
||||
Something has happened on previous execution that requires us to
|
||||
re-prepare before we try to execute.
|
||||
*/
|
||||
lex->needs_reprepare= false;
|
||||
goto start_with_reprepare;
|
||||
}
|
||||
|
||||
/* Check if we got an error when sending long data */
|
||||
if (unlikely(state == Query_arena::STMT_ERROR))
|
||||
{
|
||||
|
@ -4448,6 +4458,7 @@ reexecute:
|
|||
DBUG_ASSERT(thd->get_stmt_da()->sql_errno() == ER_NEED_REPREPARE);
|
||||
thd->clear_error();
|
||||
|
||||
start_with_reprepare:
|
||||
error= reprepare();
|
||||
|
||||
if (likely(!error)) /* Success */
|
||||
|
|
|
@ -2316,6 +2316,12 @@ JOIN::optimize_inner()
|
|||
if (thd->is_error() ||
|
||||
(!select_lex->leaf_tables_saved && select_lex->save_leaf_tables(thd)))
|
||||
{
|
||||
/*
|
||||
If there was an error above, the data structures may have been left in
|
||||
some undefined state. If this is a PS/SP statement, it might not be
|
||||
safe to run it again. Note that it needs to be re-prepared.
|
||||
*/
|
||||
thd->lex->needs_reprepare= true;
|
||||
if (arena)
|
||||
thd->restore_active_arena(arena, &backup);
|
||||
DBUG_RETURN(1);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue