mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
WL#3984 (Revise locking of mysql.general_log and mysql.slow_log)
Bug#25422 (Hang with log tables) Bug 17876 (Truncating mysql.slow_log in a SP after using cursor locks the thread) Bug 23044 (Warnings on flush of a log table) Bug 29129 (Resetting general_log while the GLOBAL READ LOCK is set causes a deadlock) Prior to this fix, the server would hang when performing concurrent ALTER TABLE or TRUNCATE TABLE statements against the LOG TABLES, which are mysql.general_log and mysql.slow_log. The root cause traces to the following code: in sql_base.cc, open_table() if (table->in_use != thd) { /* wait_for_condition will unlock LOCK_open for us */ wait_for_condition(thd, &LOCK_open, &COND_refresh); } The problem with this code is that the current implementation of the LOGGER creates 'fake' THD objects, like - Log_to_csv_event_handler::general_log_thd - Log_to_csv_event_handler::slow_log_thd which are not associated to a real thread running in the server, so that waiting for these non-existing threads to release table locks cause the dead lock. In general, the design of Log_to_csv_event_handler does not fit into the general architecture of the server, so that the concept of general_log_thd and slow_log_thd has to be abandoned: - this implementation does not work with table locking - it will not work with commands like SHOW PROCESSLIST - having the log tables always opened does not integrate well with DDL operations / FLUSH TABLES / SET GLOBAL READ_ONLY With this patch, the fundamental design of the LOGGER has been changed to: - always open and close a log table when writing a log - remove totally the usage of fake THD objects - clarify how locking of log tables is implemented in general. See WL#3984 for details related to the new locking design. Additional changes (misc bugs exposed and fixed): 1) mysqldump which would ignore some tables in dump_all_tables_in_db(), but forget to ignore the same in dump_all_views_in_db(). 2) mysqldump would also issue an empty "LOCK TABLE" command when all the tables to lock are to be ignored (numrows == 0), instead of not issuing the query. 3) Internal errors handlers could intercept errors but not warnings (see sql_error.cc). 4) Implementing a nested call to open tables, for the performance schema tables, exposed an existing bug in remove_table_from_cache(), which would perform: in_use->some_tables_deleted=1; against another thread, without any consideration about thread locking. This call inside remove_table_from_cache() was not required anyway, since calling mysql_lock_abort() takes care of aborting -- cleanly -- threads that might hold a lock on a table. This line (in_use->some_tables_deleted=1) has been removed.
This commit is contained in:
80
sql/table.cc
80
sql/table.cc
@ -21,6 +21,18 @@
|
||||
#include <m_ctype.h>
|
||||
#include "md5.h"
|
||||
|
||||
/* INFORMATION_SCHEMA name */
|
||||
LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
|
||||
|
||||
/* MYSQL_SCHEMA name */
|
||||
LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")};
|
||||
|
||||
/* GENERAL_LOG name */
|
||||
LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")};
|
||||
|
||||
/* SLOW_LOG name */
|
||||
LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")};
|
||||
|
||||
/* Functions defined in this file */
|
||||
|
||||
void open_table_error(TABLE_SHARE *share, int error, int db_errno,
|
||||
@ -31,6 +43,7 @@ static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
|
||||
uint types, char **names);
|
||||
static uint find_field(Field **fields, uchar *record, uint start, uint length);
|
||||
|
||||
inline bool is_system_table_name(const char *name, uint length);
|
||||
|
||||
/**************************************************************************
|
||||
Object_creation_ctx implementation.
|
||||
@ -192,6 +205,49 @@ char *fn_rext(char *name)
|
||||
return name + strlen(name);
|
||||
}
|
||||
|
||||
TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
|
||||
{
|
||||
DBUG_ASSERT(db != NULL);
|
||||
DBUG_ASSERT(name != NULL);
|
||||
|
||||
if ((db->length == INFORMATION_SCHEMA_NAME.length) &&
|
||||
(my_strcasecmp(system_charset_info,
|
||||
INFORMATION_SCHEMA_NAME.str,
|
||||
db->str) == 0))
|
||||
{
|
||||
return TABLE_CATEGORY_INFORMATION;
|
||||
}
|
||||
|
||||
if ((db->length == MYSQL_SCHEMA_NAME.length) &&
|
||||
(my_strcasecmp(system_charset_info,
|
||||
MYSQL_SCHEMA_NAME.str,
|
||||
db->str) == 0))
|
||||
{
|
||||
if (is_system_table_name(name->str, name->length))
|
||||
{
|
||||
return TABLE_CATEGORY_SYSTEM;
|
||||
}
|
||||
|
||||
if ((name->length == GENERAL_LOG_NAME.length) &&
|
||||
(my_strcasecmp(system_charset_info,
|
||||
GENERAL_LOG_NAME.str,
|
||||
name->str) == 0))
|
||||
{
|
||||
return TABLE_CATEGORY_PERFORMANCE;
|
||||
}
|
||||
|
||||
if ((name->length == SLOW_LOG_NAME.length) &&
|
||||
(my_strcasecmp(system_charset_info,
|
||||
SLOW_LOG_NAME.str,
|
||||
name->str) == 0))
|
||||
{
|
||||
return TABLE_CATEGORY_PERFORMANCE;
|
||||
}
|
||||
}
|
||||
|
||||
return TABLE_CATEGORY_USER;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Allocate a setup TABLE_SHARE structure
|
||||
@ -297,7 +353,8 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key,
|
||||
|
||||
bzero((char*) share, sizeof(*share));
|
||||
init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
|
||||
share->tmp_table= INTERNAL_TMP_TABLE;
|
||||
share->table_category= TABLE_CATEGORY_TEMPORARY;
|
||||
share->tmp_table= INTERNAL_TMP_TABLE;
|
||||
share->db.str= (char*) key;
|
||||
share->db.length= strlen(key);
|
||||
share->table_cache_key.str= (char*) key;
|
||||
@ -544,28 +601,11 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
|
||||
*root_ptr= &share->mem_root;
|
||||
error= open_binary_frm(thd, share, head, file);
|
||||
*root_ptr= old_root;
|
||||
|
||||
if (share->db.length == 5 && !(lower_case_table_names ?
|
||||
my_strcasecmp(system_charset_info, share->db.str, "mysql") :
|
||||
strcmp(share->db.str, "mysql")))
|
||||
{
|
||||
/*
|
||||
We can't mark all tables in 'mysql' database as system since we don't
|
||||
allow to lock such tables for writing with any other tables (even with
|
||||
other system tables) and some privilege tables need this.
|
||||
*/
|
||||
share->system_table= is_system_table_name(share->table_name.str,
|
||||
share->table_name.length);
|
||||
if (!share->system_table)
|
||||
{
|
||||
share->log_table= check_if_log_table(share->db.length, share->db.str,
|
||||
share->table_name.length,
|
||||
share->table_name.str, 0);
|
||||
}
|
||||
}
|
||||
error_given= 1;
|
||||
}
|
||||
|
||||
share->table_category= get_table_category(& share->db, & share->table_name);
|
||||
|
||||
if (!error)
|
||||
thd->status_var.opened_shares++;
|
||||
|
||||
|
Reference in New Issue
Block a user