summaryrefslogtreecommitdiff
path: root/sql/sql_update.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r--sql/sql_update.cc134
1 files changed, 94 insertions, 40 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 528e76d8d32..e9e8033c4fd 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -502,7 +502,7 @@ int mysql_update(THD *thd,
if (used_key_is_modified || order ||
partition_key_modified(table, table->write_set))
{
- if (order && (need_sort || used_key_is_modified))
+ if (order && need_sort)
query_plan.using_filesort= true;
else
query_plan.using_io_buffer= true;
@@ -703,16 +703,8 @@ int mysql_update(THD *thd,
transactional_table= table->file->has_transactions();
thd->abort_on_warning= !ignore && thd->is_strict_mode();
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
+ if (table->prepare_triggers_for_update_stmt_or_event())
{
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
will_batch= FALSE;
}
else
@@ -1229,6 +1221,87 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
return false;
}
+/**
+ Check if there is enough privilege on specific table used by the
+ main select list of multi-update directly or indirectly (through
+ a view).
+
+ @param[in] thd Thread context.
+ @param[in] table Table list element for the table.
+ @param[in] tables_for_update Bitmap with tables being updated.
+ @param[in/out] updated_arg Set to true if table in question is
+ updated, also set to true if it is
+ a view and one of its underlying
+ tables is updated. Should be
+ initialized to false by the caller
+ before a sequence of calls to this
+ function.
+
+ @note To determine which tables/views are updated we have to go from
+ leaves to root since tables_for_update contains map of leaf
+ tables being updated and doesn't include non-leaf tables
+ (fields are already resolved to leaf tables).
+
+ @retval false - Success, all necessary privileges on all tables are
+ present or might be present on column-level.
+ @retval true - Failure, some necessary privilege on some table is
+ missing.
+*/
+
+static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table,
+ table_map tables_for_update,
+ bool *updated_arg)
+{
+ if (table->view)
+ {
+ bool updated= false;
+ /*
+ If it is a mergeable view then we need to check privileges on its
+ underlying tables being merged (including views). We also need to
+ check if any of them is updated in order to find if this view is
+ updated.
+ If it is a non-mergeable view then it can't be updated.
+ */
+ DBUG_ASSERT(table->merge_underlying_list ||
+ (!table->updatable &&
+ !(table->table->map & tables_for_update)));
+
+ for (TABLE_LIST *tbl= table->merge_underlying_list; tbl;
+ tbl= tbl->next_local)
+ {
+ if (multi_update_check_table_access(thd, tbl, tables_for_update,
+ &updated))
+ {
+ tbl->hide_view_error(thd);
+ return true;
+ }
+ }
+ if (check_table_access(thd, updated ? UPDATE_ACL: SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return true;
+ *updated_arg|= updated;
+ /* We only need SELECT privilege for columns in the values list. */
+ table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege;
+ }
+ else
+ {
+ /* Must be a base or derived table. */
+ const bool updated= table->table->map & tables_for_update;
+ if (check_table_access(thd, updated ? UPDATE_ACL : SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return true;
+ *updated_arg|= updated;
+ /* We only need SELECT privilege for columns in the values list. */
+ if (!table->derived)
+ {
+ table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege;
+ table->table->grant.want_privilege= (SELECT_ACL &
+ ~table->table->grant.privilege);
+ }
+ }
+ return false;
+}
+
/*
make update specific preparation and checks after opening tables
@@ -1363,19 +1436,17 @@ int mysql_multi_update_prepare(THD *thd)
tl->table->reginfo.lock_type= tl->lock_type;
}
}
+
+ /*
+ Check access privileges for tables being updated or read.
+ Note that unlike in the above loop we need to iterate here not only
+ through all leaf tables but also through all view hierarchy.
+ */
for (tl= table_list; tl; tl= tl->next_local)
{
- /* Check access privileges for table */
- if (!tl->is_derived())
- {
- uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL;
- if (check_access(thd, want_privilege, tl->db,
- &tl->grant.privilege,
- &tl->grant.m_internal,
- 0, 0) ||
- check_grant(thd, want_privilege, tl, FALSE, 1, FALSE))
- DBUG_RETURN(TRUE);
- }
+ bool not_used= false;
+ if (multi_update_check_table_access(thd, tl, tables_for_update, &not_used))
+ DBUG_RETURN(TRUE);
}
/* check single table update for view compound from several tables */
@@ -1614,17 +1685,7 @@ int multi_update::prepare(List<Item> &not_used_values,
table->no_keyread=1;
table->covering_keys.clear_all();
table->pos_in_table_list= tl;
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
- }
+ table->prepare_triggers_for_update_stmt_or_event();
}
}
@@ -2099,13 +2160,6 @@ int multi_update::send_data(List<Item> &not_used_values)
}
-void multi_update::send_error(uint errcode,const char *err)
-{
- /* First send error what ever it is ... */
- my_error(errcode, MYF(0), err);
-}
-
-
void multi_update::abort_result_set()
{
/* the error was handled or nothing deleted and no side effects return */
@@ -2436,7 +2490,7 @@ bool multi_update::send_eof()
thd->transaction.stmt.modified_non_trans_table);
if (local_error != 0)
- error_handled= TRUE; // to force early leave from ::send_error()
+ error_handled= TRUE; // to force early leave from ::abort_result_set()
if (local_error > 0) // if the above log write did not fail ...
{