Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions mysql-test/main/cte_update_delete.result
Original file line number Diff line number Diff line change
Expand Up @@ -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
81 changes: 81 additions & 0 deletions mysql-test/main/cte_update_delete.test
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
--source include/not_embedded.inc
--echo #
--echo # MDEV-37220 Allow UPDATE/DELETE to read from a CTE
--echo #
Expand Down Expand Up @@ -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
8 changes: 8 additions & 0 deletions sql/sql_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions sql/sql_delete.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}


4 changes: 2 additions & 2 deletions sql/sql_delete.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<Item> empty_list; /**< auxiliary empty list used by prepare_inner() */
Protocol *save_protocol; /**< needed for ANALYZE .. DELETE .. RETURNING */
Expand Down
1 change: 1 addition & 0 deletions sql/sql_lex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions sql/sql_select.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 6 additions & 2 deletions sql/sql_union.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2954,7 +2954,7 @@ bool st_select_lex::cleanup()

cleanup_window_funcs(window_funcs);

if (join)
if (leaf_tables.elements)
{
List_iterator<TABLE_LIST> ti(leaf_tables);
TABLE_LIST *tbl;
Expand All @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions sql/table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down