Skip to content

Commit

Permalink
MDEV-15890 Strange error message if you try to FLUSH TABLES <view> af…
Browse files Browse the repository at this point in the history
…ter LOCK TABLES <view>.

Check if the argument of the FLUSH TABLE is a VIEW and handle it
accordingly.
  • Loading branch information
Alexey Botchkov committed Sep 2, 2018
1 parent 288212f commit 63ad6a9
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 61 deletions.
24 changes: 24 additions & 0 deletions mysql-test/r/flush.result
Original file line number Diff line number Diff line change
Expand Up @@ -496,3 +496,27 @@ flush relay logs,relay logs;
ERROR HY000: Incorrect usage of FLUSH and RELAY LOGS
flush slave,slave;
ERROR HY000: Incorrect usage of FLUSH and SLAVE
#
# MDEV-15890 Strange error message if you try to
# FLUSH TABLES <view> after LOCK TABLES <view>.
#
CREATE TABLE t1 (qty INT, price INT);
CREATE VIEW v1 AS SELECT qty, price, qty*price AS value FROM t1;
LOCK TABLES v1 READ;
FLUSH TABLES v1;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
UNLOCK TABLES;
LOCK TABLES v1 WRITE;
FLUSH TABLES v1;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
UNLOCK TABLES;
LOCK TABLES v1 READ;
FLUSH TABLES t1;
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
UNLOCK TABLES;
LOCK TABLES t1 READ;
FLUSH TABLES v1;
ERROR HY000: Table 'v1' was not locked with LOCK TABLES
UNLOCK TABLES;
DROP VIEW v1;
DROP TABLE t1;
32 changes: 32 additions & 0 deletions mysql-test/t/flush.test
Original file line number Diff line number Diff line change
Expand Up @@ -709,3 +709,35 @@ DROP TABLE t1;
flush relay logs,relay logs;
--error ER_WRONG_USAGE
flush slave,slave;

--echo #
--echo # MDEV-15890 Strange error message if you try to
--echo # FLUSH TABLES <view> after LOCK TABLES <view>.
--echo #

CREATE TABLE t1 (qty INT, price INT);
CREATE VIEW v1 AS SELECT qty, price, qty*price AS value FROM t1;

LOCK TABLES v1 READ;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
FLUSH TABLES v1;
UNLOCK TABLES;

LOCK TABLES v1 WRITE;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
FLUSH TABLES v1;
UNLOCK TABLES;

LOCK TABLES v1 READ;
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
FLUSH TABLES t1;
UNLOCK TABLES;

LOCK TABLES t1 READ;
--error ER_TABLE_NOT_LOCKED
FLUSH TABLES v1;
UNLOCK TABLES;

DROP VIEW v1;
DROP TABLE t1;

142 changes: 87 additions & 55 deletions sql/sql_base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -522,9 +522,10 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
for (TABLE_LIST *table_list= tables_to_reopen; table_list;
table_list= table_list->next_global)
{
int err;
/* A check that the table was locked for write is done by the caller. */
TABLE *table= find_table_for_mdl_upgrade(thd, table_list->db,
table_list->table_name, TRUE);
table_list->table_name, &err);

/* May return NULL if this table has already been closed via an alias. */
if (! table)
Expand Down Expand Up @@ -2121,6 +2122,66 @@ open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
}


/**
Check if the given table is actually a VIEW that was LOCK-ed
@param thd Thread context.
@param t Table to check.
@retval TRUE The 't'-table is a locked view
needed to remedy problem before retrying again.
@retval FALSE 't' was not locked, not a VIEW or an error happened.
*/
bool is_locked_view(THD *thd, TABLE_LIST *t)
{
DBUG_ENTER("check_locked_view");
/*
Is this table a view and not a base table?
(it is work around to allow to open view with locked tables,
real fix will be made after definition cache will be made)
Since opening of view which was not explicitly locked by LOCK
TABLES breaks metadata locking protocol (potentially can lead
to deadlocks) it should be disallowed.
*/
if (thd->mdl_context.is_lock_owner(MDL_key::TABLE,
t->db, t->table_name,
MDL_SHARED))
{
char path[FN_REFLEN + 1];
build_table_filename(path, sizeof(path) - 1,
t->db, t->table_name, reg_ext, 0);
/*
Note that we can't be 100% sure that it is a view since it's
possible that we either simply have not found unused TABLE
instance in THD::open_tables list or were unable to open table
during prelocking process (in this case in theory we still
should hold shared metadata lock on it).
*/
if (dd_frm_is_view(thd, path))
{
/*
If parent_l of the table_list is non null then a merge table
has this view as child table, which is not supported.
*/
if (t->parent_l)
{
my_error(ER_WRONG_MRG_TABLE, MYF(0));
DBUG_RETURN(FALSE);
}

if (!tdc_open_view(thd, t, t->alias, CHECK_METADATA_VERSION))
{
DBUG_ASSERT(t->view != 0);
DBUG_RETURN(TRUE); // VIEW
}
}
}

DBUG_RETURN(FALSE);
}


/**
Open a base table.
Expand Down Expand Up @@ -2263,50 +2324,10 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx)
DBUG_PRINT("info",("Using locked table"));
goto reset;
}
/*
Is this table a view and not a base table?
(it is work around to allow to open view with locked tables,
real fix will be made after definition cache will be made)

Since opening of view which was not explicitly locked by LOCK
TABLES breaks metadata locking protocol (potentially can lead
to deadlocks) it should be disallowed.
*/
if (thd->mdl_context.is_lock_owner(MDL_key::TABLE,
table_list->db,
table_list->table_name,
MDL_SHARED))
{
char path[FN_REFLEN + 1];
build_table_filename(path, sizeof(path) - 1,
table_list->db, table_list->table_name, reg_ext, 0);
/*
Note that we can't be 100% sure that it is a view since it's
possible that we either simply have not found unused TABLE
instance in THD::open_tables list or were unable to open table
during prelocking process (in this case in theory we still
should hold shared metadata lock on it).
*/
if (dd_frm_is_view(thd, path))
{
/*
If parent_l of the table_list is non null then a merge table
has this view as child table, which is not supported.
*/
if (table_list->parent_l)
{
my_error(ER_WRONG_MRG_TABLE, MYF(0));
DBUG_RETURN(true);
}
if (is_locked_view(thd, table_list))
DBUG_RETURN(FALSE); // VIEW

if (!tdc_open_view(thd, table_list, alias, key, key_length,
CHECK_METADATA_VERSION))
{
DBUG_ASSERT(table_list->view != 0);
DBUG_RETURN(FALSE); // VIEW
}
}
}
/*
No table in the locked tables list. In case of explicit LOCK TABLES
this can happen if a user did not include the table into the list.
Expand Down Expand Up @@ -2666,8 +2687,9 @@ TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name)
@param thd Thread context
@param db Database name.
@param table_name Name of table.
@param no_error Don't emit error if no suitable TABLE
instance were found.
@param p_error In the case of an error (when the function returns NULL)
the error number is stored there.
If the p_error is NULL, function launches the error itself.
@note This function checks if the connection holds a global IX
metadata lock. If no such lock is found, it is not safe to
Expand All @@ -2680,15 +2702,15 @@ TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name)
*/

TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
const char *table_name, bool no_error)
const char *table_name, int *p_error)
{
TABLE *tab= find_locked_table(thd->open_tables, db, table_name);
int error;

if (!tab)
{
if (!no_error)
my_error(ER_TABLE_NOT_LOCKED, MYF(0), table_name);
return NULL;
error= ER_TABLE_NOT_LOCKED;
goto err_exit;
}

/*
Expand All @@ -2700,20 +2722,30 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
if (!thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
MDL_INTENTION_EXCLUSIVE))
{
if (!no_error)
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_name);
return NULL;
error= ER_TABLE_NOT_LOCKED_FOR_WRITE;
goto err_exit;
}

while (tab->mdl_ticket != NULL &&
!tab->mdl_ticket->is_upgradable_or_exclusive() &&
(tab= find_locked_table(tab->next, db, table_name)))
continue;

if (!tab && !no_error)
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_name);
if (unlikely(!tab))
{
error= ER_TABLE_NOT_LOCKED_FOR_WRITE;
goto err_exit;
}

return tab;

err_exit:
if (p_error)
*p_error= error;
else
my_error(error, MYF(0), table_name);

return NULL;
}


Expand Down Expand Up @@ -4446,7 +4478,7 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
Note that find_table_for_mdl_upgrade() will report an error if
no suitable ticket is found.
*/
if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false))
if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, NULL))
return TRUE;
}

Expand Down
3 changes: 2 additions & 1 deletion sql/sql_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
MYSQL_OPEN_GET_NEW_TABLE |\
MYSQL_OPEN_HAS_MDL_LOCK)

bool is_locked_view(THD *thd, TABLE_LIST *t);
bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx);

bool get_key_map_from_key_list(key_map *map, TABLE *table,
Expand Down Expand Up @@ -329,7 +330,7 @@ static inline bool tdc_open_view(THD *thd, TABLE_LIST *table_list,

TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
const char *table_name,
bool no_error);
int *p_error);
void mark_tmp_table_for_reuse(TABLE *table);

int update_virtual_fields(THD *thd, TABLE *table,
Expand Down
13 changes: 11 additions & 2 deletions sql/sql_reload.cc
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,18 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
*/
if (tables)
{
int err;
for (TABLE_LIST *t= tables; t; t= t->next_local)
if (!find_table_for_mdl_upgrade(thd, t->db, t->table_name, false))
return 1;
if (!find_table_for_mdl_upgrade(thd, t->db, t->table_name, &err))
{
if (is_locked_view(thd, t))
t->next_local= t->next_global;
else
{
my_error(err, MYF(0), t->table_name);
return 1;
}
}
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion sql/sql_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2070,7 +2070,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
in its elements.
*/
table->table= find_table_for_mdl_upgrade(thd, table->db,
table->table_name, false);
table->table_name, NULL);
if (!table->table)
DBUG_RETURN(true);
table->mdl_request.ticket= table->table->mdl_ticket;
Expand Down
2 changes: 1 addition & 1 deletion sql/sql_trigger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
/* Under LOCK TABLES we must only accept write locked tables. */
if (!(tables->table= find_table_for_mdl_upgrade(thd, tables->db,
tables->table_name,
FALSE)))
NULL)))
goto end;
}
else
Expand Down
2 changes: 1 addition & 1 deletion sql/sql_truncate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
if (thd->locked_tables_mode)
{
if (!(table= find_table_for_mdl_upgrade(thd, table_ref->db,
table_ref->table_name, FALSE)))
table_ref->table_name, NULL)))
DBUG_RETURN(TRUE);

*hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
Expand Down

0 comments on commit 63ad6a9

Please sign in to comment.