mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Merge branch '10.3' into 10.4
This commit is contained in:
@ -1622,121 +1622,81 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
make update specific preparation and checks after opening tables
|
||||
|
||||
SYNOPSIS
|
||||
mysql_multi_update_prepare()
|
||||
thd thread handler
|
||||
|
||||
RETURN
|
||||
FALSE OK
|
||||
TRUE Error
|
||||
*/
|
||||
|
||||
int mysql_multi_update_prepare(THD *thd)
|
||||
class Multiupdate_prelocking_strategy : public DML_prelocking_strategy
|
||||
{
|
||||
bool done;
|
||||
bool has_prelocking_list;
|
||||
public:
|
||||
void reset(THD *thd);
|
||||
bool handle_end(THD *thd);
|
||||
};
|
||||
|
||||
void Multiupdate_prelocking_strategy::reset(THD *thd)
|
||||
{
|
||||
done= false;
|
||||
has_prelocking_list= thd->lex->requires_prelocking();
|
||||
}
|
||||
|
||||
/**
|
||||
Determine what tables could be updated in the multi-update
|
||||
|
||||
For these tables we'll need to open triggers and continue prelocking
|
||||
until all is open.
|
||||
*/
|
||||
bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
|
||||
{
|
||||
DBUG_ENTER("Multiupdate_prelocking_strategy::handle_end");
|
||||
if (done)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
LEX *lex= thd->lex;
|
||||
TABLE_LIST *table_list= lex->query_tables;
|
||||
TABLE_LIST *tl;
|
||||
List<Item> *fields= &lex->first_select_lex()->item_list;
|
||||
table_map tables_for_update;
|
||||
bool update_view= 0;
|
||||
DML_prelocking_strategy prelocking_strategy;
|
||||
bool has_prelocking_list= thd->lex->requires_prelocking();
|
||||
SELECT_LEX *select_lex= lex->first_select_lex();
|
||||
TABLE_LIST *table_list= lex->query_tables, *tl;
|
||||
|
||||
/*
|
||||
if this multi-update was converted from usual update, here is table
|
||||
counter else junk will be assigned here, but then replaced with real
|
||||
count in open_tables()
|
||||
*/
|
||||
uint table_count= lex->table_count;
|
||||
const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE;
|
||||
bool original_multiupdate= (thd->lex->sql_command == SQLCOM_UPDATE_MULTI);
|
||||
DBUG_ENTER("mysql_multi_update_prepare");
|
||||
done= true;
|
||||
|
||||
/* following need for prepared statements, to run next time multi-update */
|
||||
thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
|
||||
if (mysql_handle_derived(lex, DT_INIT) ||
|
||||
mysql_handle_derived(lex, DT_MERGE_FOR_INSERT) ||
|
||||
mysql_handle_derived(lex, DT_PREPARE))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/*
|
||||
Open tables and create derived ones, but do not lock and fill them yet.
|
||||
|
||||
During prepare phase acquire only S metadata locks instead of SW locks to
|
||||
keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE
|
||||
and global read lock.
|
||||
*/
|
||||
if ((original_multiupdate && open_tables(thd, &table_list, &table_count,
|
||||
thd->stmt_arena->is_stmt_prepare()
|
||||
? MYSQL_OPEN_FORCE_SHARED_MDL : 0,
|
||||
&prelocking_strategy)) ||
|
||||
mysql_handle_derived(lex, DT_INIT))
|
||||
DBUG_RETURN(TRUE);
|
||||
/*
|
||||
setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables()
|
||||
second time, but this call will do nothing (there are check for second
|
||||
call in setup_tables()).
|
||||
*/
|
||||
|
||||
//We need to merge for insert prior to prepare.
|
||||
if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT))
|
||||
DBUG_RETURN(TRUE);
|
||||
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 (mysql_handle_derived(lex, DT_PREPARE))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (table_list->has_period())
|
||||
{
|
||||
/*
|
||||
Multi-table update is not supported on syntax lexel. However it's possible
|
||||
to get here through PREPARE with update of multi-table view.
|
||||
*/
|
||||
DBUG_ASSERT(table_list->is_view_or_derived());
|
||||
my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (setup_tables_and_check_access(thd,
|
||||
&lex->first_select_lex()->context,
|
||||
&lex->first_select_lex()->top_join_list,
|
||||
table_list,
|
||||
lex->first_select_lex()->leaf_tables,
|
||||
FALSE, UPDATE_ACL, SELECT_ACL, FALSE))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (lex->first_select_lex()->handle_derived(thd->lex, DT_MERGE))
|
||||
DBUG_RETURN(TRUE);
|
||||
if (select_lex->handle_derived(thd->lex, DT_MERGE))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
List<Item> *fields= &lex->first_select_lex()->item_list;
|
||||
if (setup_fields_with_no_wrap(thd, Ref_ptr_array(),
|
||||
*fields, MARK_COLUMNS_WRITE, 0, 0))
|
||||
DBUG_RETURN(TRUE);
|
||||
DBUG_RETURN(1);
|
||||
|
||||
// Check if we have a view in the list ...
|
||||
for (tl= table_list; tl ; tl= tl->next_local)
|
||||
{
|
||||
if (tl->view)
|
||||
{
|
||||
update_view= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ... and pass this knowlage in check_fields call
|
||||
if (check_fields(thd, table_list, *fields, tl != NULL ))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (check_fields(thd, table_list, *fields, update_view))
|
||||
{
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
table_map tables_for_update= thd->table_map_for_update= get_table_map(fields);
|
||||
|
||||
thd->table_map_for_update= tables_for_update= get_table_map(fields);
|
||||
|
||||
if (unsafe_key_update(lex->first_select_lex()->leaf_tables,
|
||||
tables_for_update))
|
||||
DBUG_RETURN(true);
|
||||
|
||||
TABLE_LIST **new_tables= lex->query_tables_last;
|
||||
DBUG_ASSERT(*new_tables== NULL);
|
||||
if (unsafe_key_update(select_lex->leaf_tables, tables_for_update))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/*
|
||||
Setup timestamp handling and locking mode
|
||||
*/
|
||||
List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables);
|
||||
const bool using_lock_tables= thd->locked_tables_mode != LTM_NONE;
|
||||
while ((tl= ti++))
|
||||
{
|
||||
TABLE *table= tl->table;
|
||||
@ -1751,7 +1711,7 @@ int mysql_multi_update_prepare(THD *thd)
|
||||
{
|
||||
my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
|
||||
tl->top_table()->alias.str, "UPDATE");
|
||||
DBUG_RETURN(TRUE);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
DBUG_PRINT("info",("setting table `%s` for update",
|
||||
@ -1763,8 +1723,8 @@ int mysql_multi_update_prepare(THD *thd)
|
||||
tl->updating= 1;
|
||||
if (tl->belong_to_view)
|
||||
tl->belong_to_view->updating= 1;
|
||||
if (extend_table_list(thd, tl, &prelocking_strategy, has_prelocking_list))
|
||||
DBUG_RETURN(TRUE);
|
||||
if (extend_table_list(thd, tl, this, has_prelocking_list))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1796,19 +1756,6 @@ int mysql_multi_update_prepare(THD *thd)
|
||||
through all leaf tables but also through all view hierarchy.
|
||||
*/
|
||||
|
||||
uint addon_table_count= 0;
|
||||
if (*new_tables)
|
||||
{
|
||||
Sroutine_hash_entry **new_routines= thd->lex->sroutines_list.next;
|
||||
DBUG_ASSERT(*new_routines == NULL);
|
||||
if (open_tables(thd, thd->lex->create_info, new_tables,
|
||||
&addon_table_count, new_routines,
|
||||
thd->stmt_arena->is_stmt_prepare()
|
||||
? MYSQL_OPEN_FORCE_SHARED_MDL : 0,
|
||||
&prelocking_strategy))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
for (tl= table_list; tl; tl= tl->next_local)
|
||||
{
|
||||
bool not_used= false;
|
||||
@ -1821,23 +1768,67 @@ int mysql_multi_update_prepare(THD *thd)
|
||||
/* check single table update for view compound from several tables */
|
||||
for (tl= table_list; tl; tl= tl->next_local)
|
||||
{
|
||||
TABLE_LIST *for_update= 0;
|
||||
if (tl->is_jtbm())
|
||||
continue;
|
||||
if (tl->is_merged_derived())
|
||||
if (tl->is_merged_derived() &&
|
||||
tl->check_single_table(&for_update, tables_for_update, tl))
|
||||
{
|
||||
TABLE_LIST *for_update= 0;
|
||||
if (tl->check_single_table(&for_update, tables_for_update, tl))
|
||||
{
|
||||
my_error(ER_VIEW_MULTIUPDATE, MYF(0),
|
||||
tl->view_db.str, tl->view_name.str);
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
my_error(ER_VIEW_MULTIUPDATE, MYF(0), tl->view_db.str, tl->view_name.str);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
make update specific preparation and checks after opening tables
|
||||
|
||||
SYNOPSIS
|
||||
mysql_multi_update_prepare()
|
||||
thd thread handler
|
||||
|
||||
RETURN
|
||||
FALSE OK
|
||||
TRUE Error
|
||||
*/
|
||||
|
||||
int mysql_multi_update_prepare(THD *thd)
|
||||
{
|
||||
LEX *lex= thd->lex;
|
||||
TABLE_LIST *table_list= lex->query_tables;
|
||||
TABLE_LIST *tl;
|
||||
Multiupdate_prelocking_strategy prelocking_strategy;
|
||||
uint table_count= lex->table_count;
|
||||
DBUG_ENTER("mysql_multi_update_prepare");
|
||||
|
||||
/*
|
||||
Open tables and create derived ones, but do not lock and fill them yet.
|
||||
|
||||
During prepare phase acquire only S metadata locks instead of SW locks to
|
||||
keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE
|
||||
and global read lock.
|
||||
*/
|
||||
if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI)
|
||||
{
|
||||
if (open_tables(thd, &table_list, &table_count,
|
||||
thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0,
|
||||
&prelocking_strategy))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* following need for prepared statements, to run next time multi-update */
|
||||
thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
|
||||
prelocking_strategy.reset(thd);
|
||||
if (prelocking_strategy.handle_end(thd))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/* now lock and fill tables */
|
||||
if (!thd->stmt_arena->is_stmt_prepare() &&
|
||||
lock_tables(thd, table_list, table_count + addon_table_count, 0))
|
||||
lock_tables(thd, table_list, table_count, 0))
|
||||
{
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
@ -1850,7 +1841,7 @@ int mysql_multi_update_prepare(THD *thd)
|
||||
*/
|
||||
lex->first_select_lex()->exclude_from_table_unique_test= TRUE;
|
||||
/* We only need SELECT privilege for columns in the values list */
|
||||
ti.rewind();
|
||||
List_iterator<TABLE_LIST> ti(lex->first_select_lex()->leaf_tables);
|
||||
while ((tl= ti++))
|
||||
{
|
||||
if (tl->is_jtbm())
|
||||
@ -1883,25 +1874,18 @@ int mysql_multi_update_prepare(THD *thd)
|
||||
Setup multi-update handling and call SELECT to do the join
|
||||
*/
|
||||
|
||||
bool mysql_multi_update(THD *thd,
|
||||
TABLE_LIST *table_list,
|
||||
List<Item> *fields,
|
||||
List<Item> *values,
|
||||
COND *conds,
|
||||
ulonglong options,
|
||||
bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields,
|
||||
List<Item> *values, COND *conds, ulonglong options,
|
||||
enum enum_duplicates handle_duplicates,
|
||||
bool ignore,
|
||||
SELECT_LEX_UNIT *unit,
|
||||
SELECT_LEX *select_lex,
|
||||
multi_update **result)
|
||||
bool ignore, SELECT_LEX_UNIT *unit,
|
||||
SELECT_LEX *select_lex, multi_update **result)
|
||||
{
|
||||
bool res;
|
||||
DBUG_ENTER("mysql_multi_update");
|
||||
|
||||
|
||||
if (!(*result= new (thd->mem_root) multi_update(thd, table_list,
|
||||
&thd->lex->first_select_lex()->leaf_tables,
|
||||
fields, values,
|
||||
handle_duplicates, ignore)))
|
||||
fields, values, handle_duplicates, ignore)))
|
||||
{
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
@ -1911,8 +1895,8 @@ bool mysql_multi_update(THD *thd,
|
||||
|
||||
res= mysql_select(thd,
|
||||
table_list, select_lex->with_wild, total_list, conds,
|
||||
select_lex->order_list.elements, select_lex->order_list.first,
|
||||
(ORDER *)NULL, (Item *) NULL, (ORDER *)NULL,
|
||||
select_lex->order_list.elements,
|
||||
select_lex->order_list.first, NULL, NULL, NULL,
|
||||
options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
|
||||
OPTION_SETUP_TABLES_DONE,
|
||||
*result, unit, select_lex);
|
||||
|
Reference in New Issue
Block a user