Skip to content

Commit 0e21ff8

Browse files
committed
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.
1 parent 491e2b1 commit 0e21ff8

File tree

7 files changed

+116
-1
lines changed

7 files changed

+116
-1
lines changed

mysql-test/main/create.result

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,3 +2070,50 @@ SHOW CREATE DATABASE `#testone#■■■■■■■■■■■■■■■■
20702070
ERROR 42000: Incorrect database name '#testone#■■■■■■■■■■■■■■■■■■■■■■■■■■■■■...'
20712071
SET NAMES DEFAULT;
20722072
# End of 10.5 Test
2073+
#
2074+
# MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables on 2nd execution of PS
2075+
#
2076+
CREATE TABLE t (a INT);
2077+
INSERT INTO t VALUES (1),(2);
2078+
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';
2079+
EXECUTE stmt;
2080+
ERROR 22007: Truncated incorrect DECIMAL value: 'x'
2081+
EXECUTE stmt;
2082+
ERROR 22007: Truncated incorrect DECIMAL value: 'x'
2083+
DEALLOCATE PREPARE stmt;
2084+
# Now, run the same in SP:
2085+
create procedure sp1()
2086+
begin
2087+
declare i int unsigned default 1;
2088+
declare continue handler for SQLEXCEPTION
2089+
begin
2090+
select 'In Continue handler' as printf;
2091+
end;
2092+
while i <= 3 do
2093+
begin
2094+
select concat('Iteration ', i) as printf;
2095+
CREATE temporary TABLE tmp AS
2096+
SELECT * FROM (select t1.* from t t1 join t t2 on(t1.a = t2.a)) sq WHERE "x"=0 ;
2097+
drop temporary table if exists tmp;
2098+
set i=i+1;
2099+
end;
2100+
end while;
2101+
end|
2102+
call sp1();
2103+
printf
2104+
Iteration 1
2105+
printf
2106+
In Continue handler
2107+
printf
2108+
Iteration 2
2109+
printf
2110+
In Continue handler
2111+
printf
2112+
Iteration 3
2113+
printf
2114+
In Continue handler
2115+
Warnings:
2116+
Note 1051 Unknown table 'test.tmp'
2117+
DROP PROCEDURE sp1;
2118+
DROP TABLE t;
2119+
# End of 11.7 Test

mysql-test/main/create.test

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,3 +1945,46 @@ SHOW CREATE DATABASE `#testone#■■■■■■■■■■■■■■■■
19451945
SET NAMES DEFAULT;
19461946

19471947
--echo # End of 10.5 Test
1948+
1949+
--echo #
1950+
--echo # MDEV-35318 Assertion `tl->jtbm_subselect' failed in JOIN::calc_allowed_top_level_tables on 2nd execution of PS
1951+
--echo #
1952+
1953+
CREATE TABLE t (a INT);
1954+
INSERT INTO t VALUES (1),(2); # Optional, fails either way
1955+
1956+
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';
1957+
--error ER_TRUNCATED_WRONG_VALUE
1958+
EXECUTE stmt;
1959+
--error ER_TRUNCATED_WRONG_VALUE
1960+
EXECUTE stmt;
1961+
DEALLOCATE PREPARE stmt;
1962+
1963+
--echo # Now, run the same in SP:
1964+
delimiter |;
1965+
1966+
create procedure sp1()
1967+
begin
1968+
declare i int unsigned default 1;
1969+
declare continue handler for SQLEXCEPTION
1970+
begin
1971+
select 'In Continue handler' as printf;
1972+
end;
1973+
while i <= 3 do
1974+
begin
1975+
select concat('Iteration ', i) as printf;
1976+
CREATE temporary TABLE tmp AS
1977+
SELECT * FROM (select t1.* from t t1 join t t2 on(t1.a = t2.a)) sq WHERE "x"=0 ;
1978+
drop temporary table if exists tmp;
1979+
set i=i+1;
1980+
end;
1981+
end while;
1982+
end|
1983+
1984+
delimiter ;|
1985+
call sp1();
1986+
1987+
DROP PROCEDURE sp1;
1988+
DROP TABLE t;
1989+
1990+
--echo # End of 11.7 Test

sql/sp_instr.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ int sp_lex_keeper::validate_lex_and_exec_core(THD *thd, uint *nextp,
459459

460460
while (true)
461461
{
462-
if (instr->is_invalid())
462+
if (instr->is_invalid() || m_lex->needs_reprepare)
463463
{
464464
thd->clear_error();
465465
free_lex(thd);

sql/sql_lex.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,7 @@ void LEX::start(THD *thd_arg)
13361336
exchange= 0;
13371337

13381338
table_count_update= 0;
1339+
needs_reprepare= false;
13391340

13401341
memset(&trg_chistics, 0, sizeof(trg_chistics));
13411342
DBUG_VOID_RETURN;

sql/sql_lex.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,6 +3136,13 @@ struct LEX: public Query_tables_list
31363136
/* Query Plan Footprint of a currently running select */
31373137
Explain_query *explain;
31383138

3139+
/*
3140+
If true, query optimizer has encountered an unrecoverable error when doing
3141+
once-per-statement optimization and it is not safe to re-execute this
3142+
statement.
3143+
*/
3144+
bool needs_reprepare{false};
3145+
31393146
/*
31403147
LEX which represents current statement (conventional, SP or PS)
31413148

sql/sql_prepare.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4401,6 +4401,16 @@ Prepared_statement::execute_loop(String *expanded_query,
44014401
*/
44024402
DBUG_ASSERT(thd->free_list == NULL);
44034403

4404+
if (lex->needs_reprepare)
4405+
{
4406+
/*
4407+
Something has happened on previous execution that requires us to
4408+
re-prepare before we try to execute.
4409+
*/
4410+
lex->needs_reprepare= false;
4411+
goto start_with_reprepare;
4412+
}
4413+
44044414
/* Check if we got an error when sending long data */
44054415
if (unlikely(state == Query_arena::STMT_ERROR))
44064416
{
@@ -4448,6 +4458,7 @@ Prepared_statement::execute_loop(String *expanded_query,
44484458
DBUG_ASSERT(thd->get_stmt_da()->sql_errno() == ER_NEED_REPREPARE);
44494459
thd->clear_error();
44504460

4461+
start_with_reprepare:
44514462
error= reprepare();
44524463

44534464
if (likely(!error)) /* Success */

sql/sql_select.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2316,6 +2316,12 @@ JOIN::optimize_inner()
23162316
if (thd->is_error() ||
23172317
(!select_lex->leaf_tables_saved && select_lex->save_leaf_tables(thd)))
23182318
{
2319+
/*
2320+
If there was an error above, the data structures may have been left in
2321+
some undefined state. If this is a PS/SP statement, it might not be
2322+
safe to run it again. Note that it needs to be re-prepared.
2323+
*/
2324+
thd->lex->needs_reprepare= true;
23192325
if (arena)
23202326
thd->restore_active_arena(arena, &backup);
23212327
DBUG_RETURN(1);

0 commit comments

Comments
 (0)