1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

MDEV-24823 Crash with invalid multi-table update of view in 2nd execution of SP

Before this patch mergeable derived tables / view used in a multi-table
update / delete were merged before the preparation stage.
When the merge of a derived table / view is performed the on expression
attached to it is fixed and ANDed with the where condition of the select S
containing this derived table / view. It happens after the specification of
the derived table / view has been merged into S. If the ON expression refers
to a non existing field an error is reported and some other mergeable derived
tables / views remain unmerged. It's not a problem if the multi-table
update / delete statement is standalone. Yet if it is used in a stored
procedure the select with incompletely merged derived tables / views may
cause a problem for the second call of the procedure. This does not happen
for select queries using derived tables / views, because in this case their
specifications are merged after the preparation stage at which all ON
expressions are fixed.
This patch makes sure that merging of the derived tables / views used in a
multi-table update / delete statement is performed after the preparation
stage.

Approved by Oleksandr Byelkin <sanja@mariadb.com>
This commit is contained in:
Igor Babaev
2021-04-22 20:02:08 -07:00
parent 6f271302b6
commit e3a25793be
7 changed files with 147 additions and 25 deletions

View File

@ -1571,15 +1571,8 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
call in setup_tables()).
*/
if (setup_tables_and_check_access(thd, &select_lex->context,
&select_lex->top_join_list, table_list, select_lex->leaf_tables,
FALSE, UPDATE_ACL, SELECT_ACL, FALSE))
DBUG_RETURN(1);
if (select_lex->handle_derived(thd->lex, DT_MERGE))
DBUG_RETURN(1);
if (thd->lex->save_prep_leaf_tables())
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
table_list, select_lex->leaf_tables, FALSE, TRUE))
DBUG_RETURN(1);
List<Item> *fields= &lex->select_lex.item_list;
@ -1755,6 +1748,7 @@ int mysql_multi_update_prepare(THD *thd)
skip all tables of UPDATE SELECT itself
*/
lex->select_lex.exclude_from_table_unique_test= TRUE;
/* We only need SELECT privilege for columns in the values list */
List_iterator<TABLE_LIST> ti(lex->select_lex.leaf_tables);
while ((tl= ti++))
@ -1805,9 +1799,16 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields,
DBUG_RETURN(TRUE);
}
if ((*result)->init(thd))
DBUG_RETURN(1);
thd->abort_on_warning= !ignore && thd->is_strict_mode();
List<Item> total_list;
if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
table_list, select_lex->leaf_tables, FALSE, FALSE))
DBUG_RETURN(1);
if (select_lex->vers_setup_conds(thd, table_list))
DBUG_RETURN(1);
@ -1849,6 +1850,24 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list,
}
bool multi_update::init(THD *thd)
{
table_map tables_to_update= get_table_map(fields);
List_iterator_fast<TABLE_LIST> li(*leaves);
TABLE_LIST *tbl;
while ((tbl =li++))
{
if (tbl->is_jtbm())
continue;
if (!(tbl->table->map & tables_to_update))
continue;
if (updated_leaves.push_back(tbl, thd->mem_root))
return true;
}
return false;
}
/*
Connect fields with tables and create list of tables that are updated
*/
@ -1865,7 +1884,7 @@ int multi_update::prepare(List<Item> &not_used_values,
List_iterator_fast<Item> value_it(*values);
uint i, max_fields;
uint leaf_table_count= 0;
List_iterator<TABLE_LIST> ti(*leaves);
List_iterator<TABLE_LIST> ti(updated_leaves);
DBUG_ENTER("multi_update::prepare");
if (prepared)