summaryrefslogtreecommitdiff
path: root/sql/sql_update.cc
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2019-06-14 22:10:50 +0200
committerOleksandr Byelkin <sanja@mariadb.com>2019-06-14 22:10:50 +0200
commitf66d1850ac30922eaa49c0131d89efc8357155ff (patch)
treecb69c43694146bb7993078e76e203ded4ca48a5e /sql/sql_update.cc
parent772c5f3c78fcdaea2169ba435bac8d851c7945c7 (diff)
parent1135244a647e423f6a7b2122ad9c305253039399 (diff)
downloadmariadb-git-f66d1850ac30922eaa49c0131d89efc8357155ff.tar.gz
Merge branch '10.3' into 10.4
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r--sql/sql_update.cc238
1 files changed, 111 insertions, 127 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index fe021e27505..a193d501c1e 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -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
+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();
+}
- SYNOPSIS
- mysql_multi_update_prepare()
- thd thread handler
+/**
+ Determine what tables could be updated in the multi-update
- RETURN
- FALSE OK
- TRUE Error
+ For these tables we'll need to open triggers and continue prelocking
+ until all is open.
*/
-
-int mysql_multi_update_prepare(THD *thd)
+bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
{
- 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();
+ DBUG_ENTER("Multiupdate_prelocking_strategy::handle_end");
+ if (done)
+ DBUG_RETURN(0);
- /*
- 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");
+ LEX *lex= thd->lex;
+ SELECT_LEX *select_lex= lex->first_select_lex();
+ TABLE_LIST *table_list= lex->query_tables, *tl;
- /* following need for prepared statements, to run next time multi-update */
- thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
+ done= true;
- /*
- Open tables and create derived ones, but do not lock and fill them yet.
+ if (mysql_handle_derived(lex, DT_INIT) ||
+ mysql_handle_derived(lex, DT_MERGE_FOR_INSERT) ||
+ mysql_handle_derived(lex, DT_PREPARE))
+ DBUG_RETURN(1);
- 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 (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 (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 (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;
- }
- }
-
- if (check_fields(thd, table_list, *fields, update_view))
- {
- DBUG_RETURN(TRUE);
- }
-
- thd->table_map_for_update= tables_for_update= get_table_map(fields);
+ // ... and pass this knowlage in check_fields call
+ if (check_fields(thd, table_list, *fields, tl != NULL ))
+ DBUG_RETURN(1);
- if (unsafe_key_update(lex->first_select_lex()->leaf_tables,
- tables_for_update))
- DBUG_RETURN(true);
+ table_map tables_for_update= thd->table_map_for_update= get_table_map(fields);
- 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);