diff --git a/mysql-test/main/insert_returning.result b/mysql-test/main/insert_returning.result index a3cde7b1270..1976c1ca02e 100644 --- a/mysql-test/main/insert_returning.result +++ b/mysql-test/main/insert_returning.result @@ -400,10 +400,10 @@ f(id2) 14 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 -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; 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 { @@ -433,7 +433,7 @@ Warning 1062 Duplicate entry '7' for key 'PRIMARY' Warning 1062 Duplicate entry '8' for key 'PRIMARY' 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 -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; id2 val2 1 a @@ -615,3 +615,61 @@ DROP TABLE ins_duplicate; DROP VIEW v1; DROP VIEW v2; 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; diff --git a/mysql-test/main/insert_returning.test b/mysql-test/main/insert_returning.test index 6c8e71a4617..837d61d2c16 100644 --- a/mysql-test/main/insert_returning.test +++ b/mysql-test/main/insert_returning.test @@ -326,3 +326,73 @@ DROP TABLE ins_duplicate; DROP VIEW v1; DROP VIEW v2; 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; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b10f09d67a5..607d03d3450 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7484,10 +7484,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, Item *item; List_iterator it(fields); Query_arena *arena, backup; + uint *with_wild= returning_field ? &(thd->lex->returning()->with_wild) : + &(select_lex->with_wild); DBUG_ENTER("setup_wild"); - if (!select_lex->with_wild) - DBUG_RETURN(0); + if (!(*with_wild)) + DBUG_RETURN(0); /* 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 &fields, arena= thd->activate_stmt_arena_if_needed(&backup); 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 && ((Item_field*) item)->field_name.str == star_clex_str.str && @@ -7534,12 +7536,12 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, */ sum_func_list->elements+= fields.elements - elem; } - select_lex->with_wild--; + (*with_wild)--; } else 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; if (arena) thd->restore_active_arena(arena, &backup); diff --git a/sql/sql_class.h b/sql/sql_class.h index 880a3bc1d09..51c1bc984da 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -7039,7 +7039,12 @@ public: 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; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 4fe097a4067..35ed1af7bc1 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -9522,7 +9522,8 @@ Item *LEX::create_item_qualified_asterisk(THD *thd, null_clex_str, *name, star_clex_str))) return NULL; - current_select->with_wild++; + current_select->parsing_place == IN_RETURNING ? + thd->lex->returning()->with_wild++ : current_select->with_wild++; return item; } @@ -9537,7 +9538,8 @@ Item *LEX::create_item_qualified_asterisk(THD *thd, if (!(item= new (thd->mem_root) Item_field(thd, current_context(), schema, *b, star_clex_str))) return NULL; - current_select->with_wild++; + current_select->parsing_place == IN_RETURNING ? + thd->lex->returning()->with_wild++ : current_select->with_wild++; return item; } diff --git a/sql/sql_priv.h b/sql/sql_priv.h index b18a80ba1f2..a304cd39df7 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -374,6 +374,7 @@ enum enum_parsing_place BEFORE_OPT_LIST, AFTER_LIST, FOR_LOOP_BOUND, + IN_RETURNING, PARSING_PLACE_SIZE /* always should be the last */ }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cab60b2c266..9e172646af9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9175,14 +9175,20 @@ select_item_list: | 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_field(thd, &thd->lex->current_select->context, + Item_field(thd, &correct_select->context, star_clex_str); if (unlikely(item == NULL)) MYSQL_YYABORT; if (unlikely(add_item_to_list(thd, item))) MYSQL_YYABORT; - (thd->lex->current_select->with_wild)++; + correct_select->with_wild++; } ; @@ -13360,19 +13366,33 @@ opt_returning: | RETURNING_SYM { DBUG_ASSERT(!Lex->has_returning()); - if (($$= (Select != Lex->returning()))) - { - SELECT_LEX *sl= Lex->returning(); - sl->set_master_unit(0); - Select->attach_single(Lex->create_unit(sl)); - sl->include_global((st_select_lex_node**)&Lex->all_selects_list); - Lex->push_select(sl); - } + /* + When parsing_place is IN_RETURNING, we push select items to + item_list of builtin_select instead of current_select. + But set parsing_place of current_select to true. + + Because parsing_place for builtin_select will be IN_RETURNING, + 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 { - if ($2) - Lex->pop_select(); + thd->lex->pop_context(); + thd->lex->current_select->parsing_place= NO_MATTER; } ;