diff --git a/mysql-test/main/cte_update_delete.result b/mysql-test/main/cte_update_delete.result index fac0c7e2a68a7..6cda90fb9e2cd 100644 --- a/mysql-test/main/cte_update_delete.result +++ b/mysql-test/main/cte_update_delete.result @@ -1325,5 +1325,54 @@ insert into t3 values (1, 1, 'iron'),(2,2,'wood'),(0,NULL, 'gold'), (3, 3, 'silver'), (4, 4, 'lead'), (5, 5, 'tin'), (6, 6, 'platinum'), (7, 7, 'aluminium'); drop prepare s; +# +# MDEV-38258 No error thrown when CTE columns updated in updates set clause +# +with cte as (select * from t1 where c < 1) +update cte set cte.a =(select a from cte); +ERROR HY000: The target table cte of the UPDATE is not updatable +with cte as (select a from t1) +update cte set cte.a=(select a from cte limit 1); +ERROR HY000: The target table cte of the UPDATE is not updatable +with cte as (select a from t1), +cte2 as (select a from t3) +update t2, cte set cte.a=(select a from cte limit 1); +ERROR HY000: The target table cte of the UPDATE is not updatable +with cte as (select a from t1), +cte2 as (select a from t3) +update cte +set cte.a=(select a from cte where cte.a in (select a from cte2) limit 1); +ERROR HY000: The target table cte of the UPDATE is not updatable drop table t1, t2, t3, t4, t5; +# +# MDEV-38272 Sig11 in LEX::resolve_references_to_cte at sql/sql_cte.cc +# +create table t1(a int); +insert into t1 values (1), (5), (10), (15), (20), (25); +create view v1 as select * from t1 where a < 10; +with cte as (select * from t1) delete from t1,cte where t1.a=cte.a; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'where t1.a=cte.a' at line 1 +with cte as (select * from t1) delete from t1 using t1,cte where t1.a=cte.a; +# +# MDEV-38272 Recursive CTE usage leaks memory when not used in a top level select +# +with recursive cte as (select a from t1 union select a from cte) +update t1, cte set t1.o = 1; +ERROR 42S22: Unknown column 't1.o' in 'SET' +with recursive cte as (select a from t1 union select a from cte where a < 2) +delete from cte where a = (select a from cte); +ERROR HY000: The target table cte of the DELETE is not updatable +with recursive cte as (select a from t1 union select a from cte where a < 2) +delete from cte where a = 10; +ERROR HY000: The target table cte of the DELETE is not updatable +insert into t1 values (1), (5), (10), (15), (20), (25); +with recursive cte as (select a from t1 union select a from cte where a < 2) +delete from v1 using v1, cte where v1.a = cte.a; +select * from t1 order by 1; +a +10 +15 +20 +25 +drop table t1; # End of 12.2 tests diff --git a/mysql-test/main/cte_update_delete.test b/mysql-test/main/cte_update_delete.test index 2fef3b6722a1e..8ec964153c489 100644 --- a/mysql-test/main/cte_update_delete.test +++ b/mysql-test/main/cte_update_delete.test @@ -1,3 +1,4 @@ +--source include/not_embedded.inc --echo # --echo # MDEV-37220 Allow UPDATE/DELETE to read from a CTE --echo # @@ -511,7 +512,87 @@ eval $empty_t3; eval $fill_t3; drop prepare s; +--echo # +--echo # MDEV-38258 No error thrown when CTE columns updated in updates set clause +--echo # + +--error ER_NON_UPDATABLE_TABLE +with cte as (select * from t1 where c < 1) + update cte set cte.a =(select a from cte); + +--error ER_NON_UPDATABLE_TABLE +with cte as (select a from t1) + update cte set cte.a=(select a from cte limit 1); + +--error ER_NON_UPDATABLE_TABLE +with cte as (select a from t1), + cte2 as (select a from t3) + update t2, cte set cte.a=(select a from cte limit 1); + +--error ER_NON_UPDATABLE_TABLE +with cte as (select a from t1), + cte2 as (select a from t3) + update cte + set cte.a=(select a from cte where cte.a in (select a from cte2) limit 1); drop table t1, t2, t3, t4, t5; +--echo # +--echo # MDEV-38272 Sig11 in LEX::resolve_references_to_cte at sql/sql_cte.cc +--echo # + +create table t1(a int); +insert into t1 values (1), (5), (10), (15), (20), (25); +create view v1 as select * from t1 where a < 10; +--error ER_PARSE_ERROR +with cte as (select * from t1) delete from t1,cte where t1.a=cte.a; +with cte as (select * from t1) delete from t1 using t1,cte where t1.a=cte.a; + +--echo # +--echo # MDEV-38272 Recursive CTE usage leaks memory when not used in a top level select +--echo # + +--error ER_BAD_FIELD_ERROR +with recursive cte as (select a from t1 union select a from cte) + update t1, cte set t1.o = 1; + +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +--write_line wait $restart_file +--shutdown_server +--source include/wait_until_disconnected.inc +--write_line "restart: " $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc + +--error ER_NON_UPDATABLE_TABLE +with recursive cte as (select a from t1 union select a from cte where a < 2) + delete from cte where a = (select a from cte); + +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +--write_line wait $restart_file +--shutdown_server +--source include/wait_until_disconnected.inc +--write_line "restart: " $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc + +--error ER_NON_UPDATABLE_TABLE +with recursive cte as (select a from t1 union select a from cte where a < 2) + delete from cte where a = 10; + +let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; +--write_line wait $restart_file +--shutdown_server +--source include/wait_until_disconnected.inc +--write_line "restart: " $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc + +insert into t1 values (1), (5), (10), (15), (20), (25); +with recursive cte as (select a from t1 union select a from cte where a < 2) + delete from v1 using v1, cte where v1.a = cte.a; +select * from t1 order by 1; + +drop table t1; + --echo # End of 12.2 tests diff --git a/sql/sql_base.h b/sql/sql_base.h index 0af7b44001793..32985f319fa07 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -456,6 +456,14 @@ class Multiupdate_prelocking_strategy : public DML_prelocking_strategy bool handle_end(THD *thd) override; }; +class Multidelete_prelocking_strategy : public DML_prelocking_strategy +{ + bool done; +public: + void reset(THD *thd) override; + bool handle_end(THD *thd) override; +}; + /** A strategy for prelocking algorithm to be used for LOCK TABLES diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index f533a8acfc9e5..293d57c7071fa 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -2202,3 +2202,46 @@ bool Sql_cmd_delete::execute_inner(THD *thd) status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count()); return res; } + + +void Multidelete_prelocking_strategy::reset(THD *thd) +{ + done= false; +} + + +/** + Call setup_tables() to populate lex->first_select_lex()->leaf_tables. + This is needed to properly free memory when calling the THD destructor. +*/ + +bool Multidelete_prelocking_strategy::handle_end(THD *thd) +{ + DBUG_ENTER("Multidelete_prelocking_strategy::handle_end"); + + if (done) + DBUG_RETURN(0); + + LEX *lex= thd->lex; + SELECT_LEX *select_lex= lex->first_select_lex(); + TABLE_LIST *table_list= lex->query_tables; + + done= true; + + /* + This is done to resolve other base tables within derived tables in the + outermost select. + */ + if (mysql_handle_derived(lex, DT_INIT) || + mysql_handle_derived(lex, DT_MERGE_FOR_INSERT) || + mysql_handle_derived(lex, DT_PREPARE)) + DBUG_RETURN(1); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, true)) + DBUG_RETURN(1); + + DBUG_RETURN(0); +} + + diff --git a/sql/sql_delete.h b/sql/sql_delete.h index c3afbe8bc6cb3..7b6401a7965f5 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -56,7 +56,7 @@ class Sql_cmd_delete final : public Sql_cmd_dml DML_prelocking_strategy *get_dml_prelocking_strategy() override { - return &dml_prelocking_strategy; + return &multidelete_prelocking_strategy; } bool processing_as_multitable_delete_prohibited(THD *thd); @@ -106,7 +106,7 @@ class Sql_cmd_delete final : public Sql_cmd_dml bool multitable; /* The prelocking strategy used when opening the used tables */ - DML_prelocking_strategy dml_prelocking_strategy; + Multidelete_prelocking_strategy multidelete_prelocking_strategy; List empty_list; /**< auxiliary empty list used by prepare_inner() */ Protocol *save_protocol; /**< needed for ANALYZE .. DELETE .. RETURNING */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7b9a822924a8b..8c4d90d6cdcf9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1281,6 +1281,7 @@ void LEX::start(THD *thd_arg) spcont= NULL; proc_list.first= 0; query_tables= 0; + save_list.empty(); reset_query_tables_list(FALSE); clause_that_disallows_subselect= NULL; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e833e6de8b936..dbe9a18792d76 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -34830,6 +34830,7 @@ bool Sql_cmd_dml::execute(THD *thd) DBUG_ASSERT(thd->is_error() || thd->killed); MYSQL_DML_DONE(thd, 1, 0, 0); THD_STAGE_INFO(thd, stage_end); + free_underlaid_joins(thd, select_lex); // tmp tables allocated in prepare (void)unit->cleanup(); if (is_prepared()) unprepare(thd); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index db54c21072ca2..0db790d2b13b5 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -2954,7 +2954,7 @@ bool st_select_lex::cleanup() cleanup_window_funcs(window_funcs); - if (join) + if (leaf_tables.elements) { List_iterator ti(leaf_tables); TABLE_LIST *tbl; @@ -2971,8 +2971,12 @@ bool st_select_lex::cleanup() error|= (bool) error | (uint) unit->cleanup(); } } + } + + if (join) + { DBUG_ASSERT((st_select_lex*)join->select_lex == this); - error= join->destroy(); + error|= (bool)join->destroy(); delete join; join= 0; } diff --git a/sql/table.cc b/sql/table.cc index 8f9c6398f07af..e8d4588e3de75 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -10163,6 +10163,9 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view) belong_to_view ? belong_to_view->updating : !unit->outer_select()->outer_select(); + if (with && updating) + set_materialized_derived(); + /* In the case where a table merge operation moves a derived table from one select to another, table hints may be adjusted already.