Commit 7865c8c9 authored by Rucha Deodhar's avatar Rucha Deodhar

Crash in INSERT...SELECT..RETURNING with subquery

Underlying causes of all bugs mentioned below are same. This patch fixes
all of them:
1) MDEV-25028: ASAN use-after-poison in
base_list_iterator::next or Assertion `sl->join == 0' upon
INSERT .. RETURNING via PS
2) MDEV-25187: Assertion `inited == NONE || table->open_by_handler'
failed or Direct leak in init_dynamic_array2 upon INSERT .. RETURNING
and memory leak in init_dynamic_array2
3) MDEV-28740: crash in INSERT RETURNING subquery in prepared statements
4) MDEV-27165: crash in base_list_iterator::next
5) MDEV-29686: Assertion `slave == 0' failed in
st_select_lex_node::attach_single

Analysis:
consider this statement:
INSERT(1)...SELECT(2)...(SELECT(3)...) RETURNING (SELECT(4)...)

When RETURNING is encountered, add_slave() changes how selects are linked.
It makes the builtin_select(1) slave of SELECT(2). This causes
losing of already existing slave(3) (which is nested select of SELECT of
INSERT...SELECT). When really, builtin_select (1) shouldn't be slave to
SELECT(2) because it is not nested within it. Also, push_select() to use
correct context also changed how select are linked.
During reinit_stmt_before_use(), we expect the selects to
be cleaned-up and have join=0. Since these selects are not linked correctly,
clean-up doesn't happen correctly so join is not NULL. Hence the crash.

Fix:
IF we are parsing RETURNING, make is_parsing_returning= true for
current select. get rid of add_slave(). In place of push_select(), used
push_context() to have correct context (the context of builtin_select)
to resolve items in item_list. And add these items to item_list of
builtin_select.
parent 3a211624
...@@ -400,10 +400,10 @@ f(id2) ...@@ -400,10 +400,10 @@ f(id2)
14 14
EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE id1=8 RETURNING id2; EXPLAIN INSERT INTO t2 SELECT * FROM t1 WHERE id1=8 RETURNING id2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 const PRIMARY PRIMARY 4 const 1 1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1
EXPLAIN EXTENDED INSERT INTO t1 SELECT * FROM t1 WHERE id1=9 RETURNING val1; EXPLAIN EXTENDED INSERT INTO t1 SELECT * FROM t1 WHERE id1=9 RETURNING val1;
id select_type table type possible_keys key key_len ref rows filtered Extra id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
EXPLAIN FORMAT="json" INSERT INTO t1 SELECT * FROM t1 WHERE id1=10 RETURNING val1; EXPLAIN FORMAT="json" INSERT INTO t1 SELECT * FROM t1 WHERE id1=10 RETURNING val1;
EXPLAIN EXPLAIN
{ {
...@@ -433,7 +433,7 @@ Warning 1062 Duplicate entry '7' for key 'PRIMARY' ...@@ -433,7 +433,7 @@ Warning 1062 Duplicate entry '7' for key 'PRIMARY'
Warning 1062 Duplicate entry '8' for key 'PRIMARY' Warning 1062 Duplicate entry '8' for key 'PRIMARY'
ANALYZE INSERT INTO t2 SELECT * FROM t1 WHERE id1=11 RETURNING *; ANALYZE INSERT INTO t2 SELECT * FROM t1 WHERE id1=11 RETURNING *;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL no matching row in const table 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL no matching row in const table
SELECT * FROM t2; SELECT * FROM t2;
id2 val2 id2 val2
1 a 1 a
...@@ -615,3 +615,61 @@ DROP TABLE ins_duplicate; ...@@ -615,3 +615,61 @@ DROP TABLE ins_duplicate;
DROP VIEW v1; DROP VIEW v1;
DROP VIEW v2; DROP VIEW v2;
DROP FUNCTION f; DROP FUNCTION f;
#
# MDEV-25028: ASAN use-after-poison in base_list_iterator::next or
# Assertion `sl->join == 0' upon INSERT .. RETURNING via PS
#
CREATE TABLE t1 (a INT);
CREATE TABLE t2 (b INT);
PREPARE stmt1 FROM "INSERT INTO t1 SELECT * FROM t1 WHERE a IN (SELECT b FROM t2) RETURNING a";
EXECUTE stmt1;
a
PREPARE stmt2 FROM "INSERT INTO t1 SELECT * FROM t1 WHERE a IN (SELECT b FROM t2) RETURNING (SELECT b FROM t2)";
EXECUTE stmt2;
(SELECT b FROM t2)
DROP TABLE t1, t2;
#
# MDEV-25187: Assertion `inited == NONE || table->open_by_handler'
# failed or Direct leak in init_dynamic_array2 upon INSERT .. RETURNING
# and memory leak in init_dynamic_array2
#
CREATE TABLE t (a INT, KEY (a));
CREATE TABLE t1 (f INT);
INSERT INTO t VALUES (1),(2);
INSERT INTO t1 SELECT a FROM t WHERE 1 NOT IN (SELECT a FROM t) RETURNING f;
f
DROP TABLE t, t1;
#
# MDEV-28740: crash in INSERT RETURNING subquery in prepared statements
#
CREATE TABLE t1 (
id INTEGER NOT NULL,
data VARCHAR(30),
PRIMARY KEY (id)
)ENGINE=MyISAM;
EXECUTE IMMEDIATE 'INSERT INTO t1 (id, data) VALUES ((SELECT CAST(1 AS SIGNED INTEGER) AS anon_1), ?) RETURNING t1.id' using 'hi';
id
1
DROP TABLE t1;
#
# MDEV-27165: crash in base_list_iterator::next
#
CREATE TABLE t1 ( id int, a int);
CREATE TABLE t2 ( id int);
INSERT INTO t1 VALUES (( SELECT 1 from t2),999999999999) RETURNING id;
ERROR 22003: Out of range value for column 'a' at row 1
EXECUTE immediate "INSERT INTO t1 VALUES (( SELECT 1 from t2),999999999999) RETURNING id ";
ERROR 22003: Out of range value for column 'a' at row 1
EXECUTE immediate "INSERT INTO t1 VALUES (( SELECT 1 from t2),9) RETURNING id ";
id
NULL
DROP TABLE t1, t2;
#
# MDEV-29686: Assertion `slave == 0' failed in
# st_select_lex_node::attach_single
#
CREATE TABLE t (a INT);
INSERT t WITH cte AS (SELECT 1) SELECT * FROM cte RETURNING *;
a
1
DROP TABLE t;
...@@ -326,3 +326,73 @@ DROP TABLE ins_duplicate; ...@@ -326,3 +326,73 @@ DROP TABLE ins_duplicate;
DROP VIEW v1; DROP VIEW v1;
DROP VIEW v2; DROP VIEW v2;
DROP FUNCTION f; DROP FUNCTION f;
--echo #
--echo # MDEV-25028: ASAN use-after-poison in base_list_iterator::next or
--echo # Assertion `sl->join == 0' upon INSERT .. RETURNING via PS
--echo #
CREATE TABLE t1 (a INT);
CREATE TABLE t2 (b INT);
PREPARE stmt1 FROM "INSERT INTO t1 SELECT * FROM t1 WHERE a IN (SELECT b FROM t2) RETURNING a";
EXECUTE stmt1;
PREPARE stmt2 FROM "INSERT INTO t1 SELECT * FROM t1 WHERE a IN (SELECT b FROM t2) RETURNING (SELECT b FROM t2)";
EXECUTE stmt2;
DROP TABLE t1, t2;
--echo #
--echo # MDEV-25187: Assertion `inited == NONE || table->open_by_handler'
--echo # failed or Direct leak in init_dynamic_array2 upon INSERT .. RETURNING
--echo # and memory leak in init_dynamic_array2
--echo #
CREATE TABLE t (a INT, KEY (a));
CREATE TABLE t1 (f INT);
INSERT INTO t VALUES (1),(2);
INSERT INTO t1 SELECT a FROM t WHERE 1 NOT IN (SELECT a FROM t) RETURNING f;
# Cleanup
DROP TABLE t, t1;
--echo #
--echo # MDEV-28740: crash in INSERT RETURNING subquery in prepared statements
--echo #
CREATE TABLE t1 (
id INTEGER NOT NULL,
data VARCHAR(30),
PRIMARY KEY (id)
)ENGINE=MyISAM;
EXECUTE IMMEDIATE 'INSERT INTO t1 (id, data) VALUES ((SELECT CAST(1 AS SIGNED INTEGER) AS anon_1), ?) RETURNING t1.id' using 'hi';
DROP TABLE t1;
--echo #
--echo # MDEV-27165: crash in base_list_iterator::next
--echo #
CREATE TABLE t1 ( id int, a int);
CREATE TABLE t2 ( id int);
--error ER_WARN_DATA_OUT_OF_RANGE
INSERT INTO t1 VALUES (( SELECT 1 from t2),999999999999) RETURNING id;
--error ER_WARN_DATA_OUT_OF_RANGE
EXECUTE immediate "INSERT INTO t1 VALUES (( SELECT 1 from t2),999999999999) RETURNING id ";
EXECUTE immediate "INSERT INTO t1 VALUES (( SELECT 1 from t2),9) RETURNING id ";
DROP TABLE t1, t2;
--echo #
--echo # MDEV-29686: Assertion `slave == 0' failed in
--echo # st_select_lex_node::attach_single
--echo #
CREATE TABLE t (a INT);
INSERT t WITH cte AS (SELECT 1) SELECT * FROM cte RETURNING *;
DROP TABLE t;
...@@ -7484,10 +7484,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ...@@ -7484,10 +7484,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
Item *item; Item *item;
List_iterator<Item> it(fields); List_iterator<Item> it(fields);
Query_arena *arena, backup; Query_arena *arena, backup;
uint *with_wild= returning_field ? &(thd->lex->returning()->with_wild) :
&(select_lex->with_wild);
DBUG_ENTER("setup_wild"); DBUG_ENTER("setup_wild");
if (!select_lex->with_wild) if (!(*with_wild))
DBUG_RETURN(0); DBUG_RETURN(0);
/* /*
Don't use arena if we are not in prepared statements or stored procedures Don't use arena if we are not in prepared statements or stored procedures
...@@ -7496,7 +7498,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ...@@ -7496,7 +7498,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
arena= thd->activate_stmt_arena_if_needed(&backup); arena= thd->activate_stmt_arena_if_needed(&backup);
thd->lex->current_select->cur_pos_in_select_list= 0; thd->lex->current_select->cur_pos_in_select_list= 0;
while (select_lex->with_wild && (item= it++)) while (*with_wild && (item= it++))
{ {
if (item->type() == Item::FIELD_ITEM && if (item->type() == Item::FIELD_ITEM &&
((Item_field*) item)->field_name.str == star_clex_str.str && ((Item_field*) item)->field_name.str == star_clex_str.str &&
...@@ -7534,12 +7536,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ...@@ -7534,12 +7536,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
*/ */
sum_func_list->elements+= fields.elements - elem; sum_func_list->elements+= fields.elements - elem;
} }
select_lex->with_wild--; (*with_wild)--;
} }
else else
thd->lex->current_select->cur_pos_in_select_list++; thd->lex->current_select->cur_pos_in_select_list++;
} }
DBUG_ASSERT(!select_lex->with_wild); DBUG_ASSERT(!(*with_wild));
thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS; thd->lex->current_select->cur_pos_in_select_list= UNDEF_POS;
if (arena) if (arena)
thd->restore_active_arena(arena, &backup); thd->restore_active_arena(arena, &backup);
......
...@@ -7039,7 +7039,12 @@ class select_dumpvar :public select_result_interceptor { ...@@ -7039,7 +7039,12 @@ class select_dumpvar :public select_result_interceptor {
inline bool add_item_to_list(THD *thd, Item *item) inline bool add_item_to_list(THD *thd, Item *item)
{ {
bool res= thd->lex->current_select->add_item_to_list(thd, item); bool res;
LEX *lex= thd->lex;
if (lex->current_select->parsing_place == IN_RETURNING)
res= lex->returning()->add_item_to_list(thd, item);
else
res= lex->current_select->add_item_to_list(thd, item);
return res; return res;
} }
......
...@@ -9522,7 +9522,8 @@ Item *LEX::create_item_qualified_asterisk(THD *thd, ...@@ -9522,7 +9522,8 @@ Item *LEX::create_item_qualified_asterisk(THD *thd,
null_clex_str, *name, null_clex_str, *name,
star_clex_str))) star_clex_str)))
return NULL; return NULL;
current_select->with_wild++; current_select->parsing_place == IN_RETURNING ?
thd->lex->returning()->with_wild++ : current_select->with_wild++;
return item; return item;
} }
...@@ -9537,7 +9538,8 @@ Item *LEX::create_item_qualified_asterisk(THD *thd, ...@@ -9537,7 +9538,8 @@ Item *LEX::create_item_qualified_asterisk(THD *thd,
if (!(item= new (thd->mem_root) Item_field(thd, current_context(), if (!(item= new (thd->mem_root) Item_field(thd, current_context(),
schema, *b, star_clex_str))) schema, *b, star_clex_str)))
return NULL; return NULL;
current_select->with_wild++; current_select->parsing_place == IN_RETURNING ?
thd->lex->returning()->with_wild++ : current_select->with_wild++;
return item; return item;
} }
......
...@@ -374,6 +374,7 @@ enum enum_parsing_place ...@@ -374,6 +374,7 @@ enum enum_parsing_place
BEFORE_OPT_LIST, BEFORE_OPT_LIST,
AFTER_LIST, AFTER_LIST,
FOR_LOOP_BOUND, FOR_LOOP_BOUND,
IN_RETURNING,
PARSING_PLACE_SIZE /* always should be the last */ PARSING_PLACE_SIZE /* always should be the last */
}; };
......
...@@ -9175,14 +9175,20 @@ select_item_list: ...@@ -9175,14 +9175,20 @@ select_item_list:
| select_item | select_item
| '*' | '*'
{ {
bool is_parsing_returning=
thd->lex->current_select->parsing_place ==
IN_RETURNING;
SELECT_LEX *correct_select= is_parsing_returning ?
thd->lex->returning() :
thd->lex->current_select;
Item *item= new (thd->mem_root) Item *item= new (thd->mem_root)
Item_field(thd, &thd->lex->current_select->context, Item_field(thd, &correct_select->context,
star_clex_str); star_clex_str);
if (unlikely(item == NULL)) if (unlikely(item == NULL))
MYSQL_YYABORT; MYSQL_YYABORT;
if (unlikely(add_item_to_list(thd, item))) if (unlikely(add_item_to_list(thd, item)))
MYSQL_YYABORT; MYSQL_YYABORT;
(thd->lex->current_select->with_wild)++; correct_select->with_wild++;
} }
; ;
...@@ -13360,19 +13366,33 @@ opt_returning: ...@@ -13360,19 +13366,33 @@ opt_returning:
| RETURNING_SYM | RETURNING_SYM
{ {
DBUG_ASSERT(!Lex->has_returning()); DBUG_ASSERT(!Lex->has_returning());
if (($<num>$= (Select != Lex->returning()))) /*
{ When parsing_place is IN_RETURNING, we push select items to
SELECT_LEX *sl= Lex->returning(); item_list of builtin_select instead of current_select.
sl->set_master_unit(0); But set parsing_place of current_select to true.
Select->attach_single(Lex->create_unit(sl));
sl->include_global((st_select_lex_node**)&Lex->all_selects_list); Because parsing_place for builtin_select will be IN_RETURNING,
Lex->push_select(sl); regardless there is SELECT in RETURNING. Example, if
} there is RETURNING (SELECT...), then when we parse
SELECT inside RETURNING, builtin_select->parsing_place
will still be true. So the select items of SELECT inside
RETURNING will be added to item_list of builtin_select which
is incorrect. We want to prevent this from happening.
Since for every new select, a new SELECT_LEX
object is created and pushed to select stack, current_select
will point to SELECT inside RETURNING, and also has
parsing_place not set to IN_RETURNING by default.
So items are correctly added to item_list of SELECT inside
RETURNING instead of builtin_select.
*/
thd->lex->current_select->parsing_place= IN_RETURNING;
thd->lex->push_context(&thd->lex->returning()->context);
} }
select_item_list select_item_list
{ {
if ($<num>2) thd->lex->pop_context();
Lex->pop_select(); thd->lex->current_select->parsing_place= NO_MATTER;
} }
; ;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment