mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-36462: Crash on DECLARE spvar1 ROW TYPE OF cursor1
after a table recreation
After a cursor's statement is re-parsed by the reason of metadata changes for tables the statement depends on, following memory allocations taken place on cursor execution is performed on a memory root already marked as read only despite the fact that a new memory root has been allocated for this goal. To fix the issue, bind the cursor lex with a new memory root created for re-parsing cursor's statement, clean up items stored on cursor's free list and nullify the data member sp_lex_cursor::free_list to avoid dangling pointer problem.
This commit is contained in:
@@ -923,4 +923,67 @@ SELECT pkg.f1();
|
|||||||
pkg.f1()
|
pkg.f1()
|
||||||
1 2
|
1 2
|
||||||
DROP PACKAGE pkg;
|
DROP PACKAGE pkg;
|
||||||
|
#
|
||||||
|
# MDEV-36462: Crash on `DECLARE spvar1 ROW TYPE OF cursor1` after a table recreation
|
||||||
|
#
|
||||||
|
CREATE PROCEDURE p1()
|
||||||
|
BEGIN
|
||||||
|
DECLARE c CURSOR FOR SELECT a FROM t1;
|
||||||
|
BEGIN
|
||||||
|
DECLARE va ROW TYPE OF c; -- the crash happens here
|
||||||
|
END;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
CREATE PROCEDURE p2()
|
||||||
|
BEGIN
|
||||||
|
FOR i IN 1..10 DO -- usually it crashes on the third iteration, but not always
|
||||||
|
SELECT i;
|
||||||
|
CREATE OR REPLACE TABLE t1 (a INT);
|
||||||
|
CALL p1;
|
||||||
|
CALL p1;
|
||||||
|
END FOR;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
CALL p2;
|
||||||
|
i
|
||||||
|
1
|
||||||
|
i
|
||||||
|
2
|
||||||
|
i
|
||||||
|
3
|
||||||
|
i
|
||||||
|
4
|
||||||
|
i
|
||||||
|
5
|
||||||
|
i
|
||||||
|
6
|
||||||
|
i
|
||||||
|
7
|
||||||
|
i
|
||||||
|
8
|
||||||
|
i
|
||||||
|
9
|
||||||
|
i
|
||||||
|
10
|
||||||
|
# Clean up
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
# The following test is taken from the task MDEV-36114 which is
|
||||||
|
# partially a duplicate of the task MDEV-36462
|
||||||
|
CREATE PROCEDURE p()
|
||||||
|
BEGIN
|
||||||
|
DECLARE cur1 CURSOR FOR SELECT * FROM t;
|
||||||
|
BEGIN
|
||||||
|
DECLARE rec1 ROW TYPE OF cur1;
|
||||||
|
END;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
CREATE TABLE t (id INT);
|
||||||
|
CALL p();
|
||||||
|
CREATE OR REPLACE TABLE t (id INT);
|
||||||
|
CALL p();
|
||||||
|
# Clean up
|
||||||
|
DROP PROCEDURE p;
|
||||||
|
DROP TABLE t;
|
||||||
# End of 11.4 tests
|
# End of 11.4 tests
|
||||||
|
@@ -891,4 +891,60 @@ DROP PACKAGE pkg;
|
|||||||
SELECT pkg.f1();
|
SELECT pkg.f1();
|
||||||
DROP PACKAGE pkg;
|
DROP PACKAGE pkg;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-36462: Crash on `DECLARE spvar1 ROW TYPE OF cursor1` after a table recreation
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
--delimiter /
|
||||||
|
|
||||||
|
CREATE PROCEDURE p1()
|
||||||
|
BEGIN
|
||||||
|
DECLARE c CURSOR FOR SELECT a FROM t1;
|
||||||
|
BEGIN
|
||||||
|
DECLARE va ROW TYPE OF c; -- the crash happens here
|
||||||
|
END;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
|
||||||
|
CREATE PROCEDURE p2()
|
||||||
|
BEGIN
|
||||||
|
FOR i IN 1..10 DO -- usually it crashes on the third iteration, but not always
|
||||||
|
SELECT i;
|
||||||
|
CREATE OR REPLACE TABLE t1 (a INT);
|
||||||
|
CALL p1;
|
||||||
|
CALL p1;
|
||||||
|
END FOR;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
|
||||||
|
--delimiter ;
|
||||||
|
|
||||||
|
CALL p2;
|
||||||
|
|
||||||
|
--echo # Clean up
|
||||||
|
DROP PROCEDURE p1;
|
||||||
|
DROP PROCEDURE p2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo # The following test is taken from the task MDEV-36114 which is
|
||||||
|
--echo # partially a duplicate of the task MDEV-36462
|
||||||
|
--delimiter /
|
||||||
|
CREATE PROCEDURE p()
|
||||||
|
BEGIN
|
||||||
|
DECLARE cur1 CURSOR FOR SELECT * FROM t;
|
||||||
|
BEGIN
|
||||||
|
DECLARE rec1 ROW TYPE OF cur1;
|
||||||
|
END;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
--delimiter ;
|
||||||
|
CREATE TABLE t (id INT);
|
||||||
|
CALL p();
|
||||||
|
CREATE OR REPLACE TABLE t (id INT);
|
||||||
|
CALL p();
|
||||||
|
|
||||||
|
--echo # Clean up
|
||||||
|
DROP PROCEDURE p;
|
||||||
|
DROP TABLE t;
|
||||||
|
|
||||||
--echo # End of 11.4 tests
|
--echo # End of 11.4 tests
|
||||||
|
@@ -662,11 +662,16 @@ bool sp_lex_instr::setup_table_fields_for_trigger(
|
|||||||
re-parsing.
|
re-parsing.
|
||||||
|
|
||||||
@param sphead The stored program.
|
@param sphead The stored program.
|
||||||
|
@param[out] new_memroot_allocated true in case a new memory root for
|
||||||
|
re-parsing was created, else false meaning
|
||||||
|
that already allocated memory root is
|
||||||
|
reused
|
||||||
|
|
||||||
@return false on success, true on error (OOM)
|
@return false on success, true on error (OOM)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool sp_lex_instr::setup_memroot_for_reparsing(sp_head *sphead)
|
bool sp_lex_instr::setup_memroot_for_reparsing(sp_head *sphead,
|
||||||
|
bool *new_memroot_allocated)
|
||||||
{
|
{
|
||||||
if (!m_mem_root_for_reparsing)
|
if (!m_mem_root_for_reparsing)
|
||||||
{
|
{
|
||||||
@@ -711,6 +716,8 @@ bool sp_lex_instr::setup_memroot_for_reparsing(sp_head *sphead)
|
|||||||
|
|
||||||
if (!m_mem_root_for_reparsing)
|
if (!m_mem_root_for_reparsing)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
*new_memroot_allocated= true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -721,6 +728,7 @@ bool sp_lex_instr::setup_memroot_for_reparsing(sp_head *sphead)
|
|||||||
statement.
|
statement.
|
||||||
*/
|
*/
|
||||||
free_root(m_mem_root_for_reparsing, MYF(0));
|
free_root(m_mem_root_for_reparsing, MYF(0));
|
||||||
|
*new_memroot_allocated= false;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_sql_alloc(key_memory_sp_head_main_root, m_mem_root_for_reparsing,
|
init_sql_alloc(key_memory_sp_head_main_root, m_mem_root_for_reparsing,
|
||||||
@@ -787,7 +795,8 @@ LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp, LEX *sp_instr_lex)
|
|||||||
/*
|
/*
|
||||||
First, set up a men_root for the statement is going to re-compile.
|
First, set up a men_root for the statement is going to re-compile.
|
||||||
*/
|
*/
|
||||||
if (setup_memroot_for_reparsing(sp))
|
bool mem_root_allocated;
|
||||||
|
if (setup_memroot_for_reparsing(sp, &mem_root_allocated))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -842,8 +851,22 @@ LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp, LEX *sp_instr_lex)
|
|||||||
data member. So, for the sp_instr_cpush instruction by the time we reach
|
data member. So, for the sp_instr_cpush instruction by the time we reach
|
||||||
this block cursor_lex->free_list is already empty.
|
this block cursor_lex->free_list is already empty.
|
||||||
*/
|
*/
|
||||||
cleanup_items(cursor_lex->free_list);
|
if (mem_root_allocated)
|
||||||
|
/*
|
||||||
|
If the new memory root for re-parsing has been just created,
|
||||||
|
then delete every item from the free item list of sp_lex_cursor.
|
||||||
|
In case the memory root for re-parsing is re-used from previous
|
||||||
|
re-parsing of failed instruction, don't do anything since all memory
|
||||||
|
allocated for items were already released on calling free_root
|
||||||
|
inside the method sp_lex_instr::setup_memroot_for_reparsing
|
||||||
|
*/
|
||||||
|
cursor_lex->free_items();
|
||||||
|
|
||||||
|
/* Nullify free_list to don't have a dangling pointer */
|
||||||
|
cursor_lex->free_list= nullptr;
|
||||||
|
|
||||||
cursor_free_list= &cursor_lex->free_list;
|
cursor_free_list= &cursor_lex->free_list;
|
||||||
|
cursor_lex->mem_root= m_mem_root_for_reparsing;
|
||||||
DBUG_ASSERT(thd->lex == sp_instr_lex);
|
DBUG_ASSERT(thd->lex == sp_instr_lex);
|
||||||
lex_start(thd);
|
lex_start(thd);
|
||||||
}
|
}
|
||||||
|
@@ -522,7 +522,8 @@ private:
|
|||||||
THD *thd, sp_head *sp,
|
THD *thd, sp_head *sp,
|
||||||
SQL_I_List<Item_trigger_field> *next_trig_items_list);
|
SQL_I_List<Item_trigger_field> *next_trig_items_list);
|
||||||
|
|
||||||
bool setup_memroot_for_reparsing(sp_head *sphead);
|
bool setup_memroot_for_reparsing(sp_head *sphead,
|
||||||
|
bool *new_memroot_allocated);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user